diff --git a/contrib/wpa/Makefile b/contrib/wpa/Makefile new file mode 100644 index 000000000000..c4b1498cff9f --- /dev/null +++ b/contrib/wpa/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=$(wildcard hostapd) $(wildcard wpa_supplicant) + +all: + for dir in $(SUBDIRS); do $(MAKE) -C $$dir; done + +clean distclean: + for dir in $(SUBDIRS); do $(MAKE) -C $$dir clean; done + +install: + for dir in $(SUBDIRS); do $(MAKE) -C $$dir install; done diff --git a/contrib/wpa/README b/contrib/wpa/README index 8392bb354fac..0addde360ee5 100644 --- a/contrib/wpa/README +++ b/contrib/wpa/README @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2024, Jouni Malinen and contributors +Copyright (c) 2002-2026, Jouni Malinen and contributors All Rights Reserved. These programs are licensed under the BSD license (the one with diff --git a/contrib/wpa/hostapd/Android.mk b/contrib/wpa/hostapd/Android.mk index 573564d5b0de..bff81cac4a31 100644 --- a/contrib/wpa/hostapd/Android.mk +++ b/contrib/wpa/hostapd/Android.mk @@ -127,7 +127,7 @@ HOBJS += src/utils/trace.c LDFLAGS += -rdynamic L_CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD -L_CFLAGS += -DWPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls LIBS += -lbfd LIBS_c += -lbfd LIBS_h += -lbfd @@ -259,9 +259,10 @@ L_CFLAGS += -DCONFIG_SAE OBJS += src/common/sae.c ifdef CONFIG_SAE_PK L_CFLAGS += -DCONFIG_SAE_PK -NEED_AES_SIV=y +NEED_BASE64=y OBJS += src/common/sae_pk.c endif +NEED_AES_SIV=y NEED_ECC=y NEED_DH_GROUPS=y NEED_HMAC_SHA256_KDF=y @@ -588,6 +589,21 @@ OBJS += src/ap/nan_usd_ap.c L_CFLAGS += -DCONFIG_NAN_USD endif +ifdef CONFIG_PMKSA_PRIVACY +CONFIG_ENC_ASSOC=y +L_CFLAGS += -DCONFIG_PMKSA_PRIVACY +endif + +ifdef CONFIG_IEEE8021X_AUTH +CONFIG_ENC_ASSOC=y +L_CFLAGS += -DCONFIG_IEEE8021X_AUTH +endif + +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +L_CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_PASN L_CFLAGS += -DCONFIG_PASN L_CFLAGS += -DCONFIG_PTKSA_CACHE @@ -936,6 +952,20 @@ endif endif endif +ifdef CONFIG_SAE +ifdef CONFIG_IEEE80211BE +NEED_SHA384=y +endif +ifdef NEED_SHA384 +# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled. +NEED_HMAC_SHA384_KDF=y +endif +ifdef NEED_SHA512 +# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled. +NEED_HMAC_SHA512_KDF=y +endif +endif + L_CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) ifneq ($(CONFIG_TLS), gnutls) @@ -1055,6 +1085,7 @@ OBJS += src/pasn/pasn_common.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c OBJS += src/ap/dfs.c +OBJS += src/ap/interference.c L_CFLAGS += -DNEED_AP_MLME endif OBJS += src/ap/ieee802_11_ht.c @@ -1125,6 +1156,11 @@ ifdef CONFIG_ANDROID_LOG L_CFLAGS += -DCONFIG_ANDROID_LOG endif +ifdef CONFIG_PROCESS_COORDINATION +L_CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += src/common/proc_coord.c +endif + OBJS_c = hostapd_cli.c OBJS_c += src/common/wpa_ctrl.c OBJS_c += src/utils/os_$(CONFIG_OS).c diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog index 1c8240d333c4..ec26207d8d9d 100644 --- a/contrib/wpa/hostapd/ChangeLog +++ b/contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,35 @@ ChangeLog for hostapd +2026-08-07 - v2.12 + * support RSN overriding (e.g., WPA3-Personal Compatibility Mode) + * EHT/IEEE 802.11be/Wi-Fi 7 + - more complete support + - fix message validation issues that could enable DoS attacks + - fix group key rekeying + * enable SAE group 20 by default if SAE-EXT-KEY is enabled + * reject unexpected SAE password identifier to avoid DoS attack against + a specific STA + * mandate use of SAE H2E when using password identifiers + * assign VLAN when using SAE with PMKSA caching + * support SPP A-MSDU negotiation + * support IEEE 802.11bi functionality + - changing SAE password identifiers + - EPPKE + - IEEE 802.1X/EAP in Authentication frames + - Association frame encryption + - PMKID privacy + * remove the driver interface for now obsolete Host AP driver + * remove the driver interface for now obsolete Atheros WEXT interface + * move supported, basic, and Beacon TX rate configuration to be at BSS + level instead of per-radio for all BSSs + * fix various issues in Multiple-BSSID functionality + * support OpenSSL 3.0 API changes + * EAP-TEAP: protocol changes based on RFC 9930; this is not compatible + with previous versions + * support Automated Frequency Coordination (AFC) on the 6 GHz band + * improve GAS/ANQP processing to support larger ANQP responses + * a large number of other fixes, cleanup, and extensions + 2024-07-20 - v2.11 * Wi-Fi Easy Connect - add support for DPP release 3 diff --git a/contrib/wpa/hostapd/Makefile b/contrib/wpa/hostapd/Makefile index ca4439234a11..f6fee0b46a33 100644 --- a/contrib/wpa/hostapd/Makefile +++ b/contrib/wpa/hostapd/Makefile @@ -116,7 +116,7 @@ HOBJS += ../src/utils/trace.o LDFLAGS += -rdynamic CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD -CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD +CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls LIBS += -lbfd -ldl -liberty -lz LIBS_c += -lbfd -ldl -liberty -lz LIBS_h += -lbfd -ldl -liberty -lz @@ -298,9 +298,10 @@ CFLAGS += -DCONFIG_SAE OBJS += ../src/common/sae.o ifdef CONFIG_SAE_PK CFLAGS += -DCONFIG_SAE_PK -NEED_AES_SIV=y +NEED_BASE64=y OBJS += ../src/common/sae_pk.o endif +NEED_AES_SIV=y NEED_ECC=y NEED_DH_GROUPS=y NEED_HMAC_SHA256_KDF=y @@ -354,6 +355,14 @@ CFLAGS += -DCONFIG_IEEE80211AX OBJS += ../src/ap/ieee802_11_he.o endif +ifdef CONFIG_IEEE80211AX +ifdef CONFIG_AFC +CFLAGS += -DCONFIG_AFC +OBJS += ../src/ap/afc.o +NEED_JSON=y +endif +endif + ifdef CONFIG_MBO CFLAGS += -DCONFIG_MBO OBJS += ../src/ap/mbo_ap.o @@ -614,6 +623,21 @@ OBJS += ../src/ap/nan_usd_ap.o CFLAGS += -DCONFIG_NAN_USD endif +ifdef CONFIG_PMKSA_PRIVACY +CONFIG_ENC_ASSOC=y +CFLAGS += -DCONFIG_PMKSA_PRIVACY +endif + +ifdef CONFIG_IEEE8021X_AUTH +CONFIG_ENC_ASSOC=y +CFLAGS += -DCONFIG_IEEE8021X_AUTH +endif + +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_PASN CFLAGS += -DCONFIG_PASN CFLAGS += -DCONFIG_PTKSA_CACHE @@ -708,6 +732,7 @@ CFLAGS += -DCONFIG_TLSV12 endif ifeq ($(CONFIG_TLS), wolfssl) +CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 CONFIG_CRYPTO=wolfssl ifdef TLS_FUNCS OBJS += ../src/crypto/tls_wolfssl.o @@ -1065,6 +1090,20 @@ endif endif endif +ifdef CONFIG_SAE +ifdef CONFIG_IEEE80211BE +NEED_SHA384=y +endif +ifdef NEED_SHA384 +# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled. +NEED_HMAC_SHA384_KDF=y +endif +ifdef NEED_SHA512 +# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled. +NEED_HMAC_SHA512_KDF=y +endif +endif + CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) ifneq ($(CONFIG_TLS), linux) @@ -1201,6 +1240,7 @@ OBJS += ../src/pasn/pasn_common.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o OBJS += ../src/ap/dfs.o +OBJS += ../src/ap/interference.o CFLAGS += -DNEED_AP_MLME endif OBJS += ../src/ap/ieee802_11_ht.o @@ -1298,6 +1338,11 @@ ifdef CONFIG_NO_TKIP CFLAGS += -DCONFIG_NO_TKIP endif +ifdef CONFIG_PROCESS_COORDINATION +CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += ../src/common/proc_coord.o +endif + $(DESTDIR)$(BINDIR)/%: % install -D $(<) $(@) @@ -1365,6 +1410,16 @@ SOBJS += ../src/common/sae.o SOBJS += ../src/common/sae_pk.o SOBJS += ../src/common/dragonfly.o SOBJS += $(AESOBJS) +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) +SOBJS += ../src/crypto/sha384.o +SOBJS += ../src/crypto/sha512.o +endif +endif +endif +endif SOBJS += ../src/crypto/sha256-prf.o SOBJS += ../src/crypto/sha384-prf.o SOBJS += ../src/crypto/sha512-prf.o @@ -1372,6 +1427,14 @@ SOBJS += ../src/crypto/dh_groups.o SOBJS += ../src/crypto/sha256-kdf.o SOBJS += ../src/crypto/sha384-kdf.o SOBJS += ../src/crypto/sha512-kdf.o +SOBJS += ../src/common/wpa_common.o +ifdef CONFIG_NO_RANDOM_POOL +CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +SOBJS += ../src/crypto/random.o +endif +SOBJS += ../src/crypto/sha1-prf.o +SOBJS += ../src/utils/eloop.o _OBJS_VAR := NOBJS include ../src/objs.mk diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README index 1a0248fce422..89f65d58eae6 100644 --- a/contrib/wpa/hostapd/README +++ b/contrib/wpa/hostapd/README @@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2024, Jouni Malinen and contributors +Copyright (c) 2002-2026, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -69,11 +69,6 @@ Requirements Current hardware/software requirements: - drivers: - Host AP driver for Prism2/2.5/3. - (http://w1.fi/hostap-driver.html) - Please note that station firmware version needs to be 1.7.0 or newer - to work in WPA mode. - mac80211-based drivers that support AP mode (with driver=nl80211). This includes drivers for Atheros (ath9k) and Broadcom (b43) chipsets. @@ -125,7 +120,7 @@ use an external Authentication Server and encapsulate EAP frame in the frames used by that server. RADIUS is suitable for this, but IEEE 802.1X would also allow other mechanisms. -Host AP driver includes PAE functionality in the kernel driver. It +Drivers include PAE functionality in the kernel driver. It is a relatively simple mechanism for denying normal frames going to or coming from an unauthorized port. PAE allows IEEE 802.1X related frames to be passed between the Supplicant and the Authenticator even @@ -156,7 +151,7 @@ with the Authentication Server. After this is completed successfully, the virtual port is set to Authorized state and frames from and to the station are accepted. -Host AP configuration for IEEE 802.1X +hostapd configuration for IEEE 802.1X ------------------------------------- The user space daemon has its own configuration file that can be used to diff --git a/contrib/wpa/hostapd/README-MULTI-AP b/contrib/wpa/hostapd/README-MULTI-AP index ccee69e13c08..6190cace4aa1 100644 --- a/contrib/wpa/hostapd/README-MULTI-AP +++ b/contrib/wpa/hostapd/README-MULTI-AP @@ -155,6 +155,6 @@ References ---------- [1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh -[2] https://github.com/prplfoundation/prplMesh +[2] https://gitlab.com/prpl-foundation/prplmesh/prplMesh [3] https://www.wi-fi.org/file/multi-ap-specification-v10 (requires registration) diff --git a/contrib/wpa/hostapd/android.config b/contrib/wpa/hostapd/android.config index 522de87266d5..133a8111286d 100644 --- a/contrib/wpa/hostapd/android.config +++ b/contrib/wpa/hostapd/android.config @@ -9,9 +9,6 @@ # be modified from here. In most cass, these lines should use += in order not # to override previous values of the variables. -# Driver interface for Host AP driver -#CONFIG_DRIVER_HOSTAP=y - # Driver interface for wired authenticator #CONFIG_DRIVER_WIRED=y diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c index 3fb059770d49..59684808c794 100644 --- a/contrib/wpa/hostapd/config_file.c +++ b/contrib/wpa/hostapd/config_file.c @@ -713,14 +713,14 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (os_strcmp(start, "DPP") == 0) val |= WPA_KEY_MGMT_DPP; #endif /* CONFIG_DPP */ -#ifdef CONFIG_HS20 - else if (os_strcmp(start, "OSEN") == 0) - val |= WPA_KEY_MGMT_OSEN; -#endif /* CONFIG_HS20 */ #ifdef CONFIG_PASN else if (os_strcmp(start, "PASN") == 0) val |= WPA_KEY_MGMT_PASN; #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + else if (os_strcmp(start, "EPPKE") == 0) + val |= WPA_KEY_MGMT_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -865,7 +865,7 @@ static int hostapd_parse_intlist(int **int_list, char *val) break; pos = end + 1; } - list[count] = -1; + list[count] = 0; *int_list = list; return 0; @@ -1269,7 +1269,7 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) *end = '\0'; bitpos = atoi(pos); - if (bitpos < 0 || bitpos > 64) + if (bitpos < 0 || bitpos > 63) return -1; bitmap[bitpos / 8] |= BIT(bitpos % 8); @@ -1281,6 +1281,140 @@ static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val) return 0; } + +#ifdef CONFIG_AFC + +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos) +{ + struct afc_cert_id *c = NULL, *tmp; + unsigned int i, count = 0; + + while (pos && pos[0]) { + char *p; + + tmp = os_realloc_array(c, count + 1, sizeof(*c)); + if (!tmp) + goto error; + c = tmp; + + i = count; + count++; + os_memset(&c[i], 0, sizeof(struct afc_cert_id)); + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + c[i].ruleset = os_strdup(pos); + if (!c[i].ruleset) + goto error; + + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + c[i].id = os_strdup(pos); + if (!c[i].id) + goto error; + + pos = p; + } + + hostapd_config_free_afc_cert_ids(conf); + + conf->afc.n_cert_ids = count; + conf->afc.cert_ids = c; + + return 0; + +error: + for (i = 0; i < count; i++) { + os_free(c[i].ruleset); + os_free(c[i].id); + } + os_free(c); + + return -ENOMEM; +} + + +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data, + unsigned int *n_linear_polygon_data, + char *pos) +{ + struct afc_linear_polygon *d = NULL, *tmp; + unsigned int count = 0; + + while (pos && pos[0]) { + char *p, *end; + + tmp = os_realloc_array(d, count + 1, sizeof(*d)); + if (!tmp) + goto error; + d = tmp; + + p = os_strchr(pos, ':'); + if (!p) + goto error; + + *p++ = '\0'; + if (!p || !p[0]) + goto error; + + d[count].longitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + d[count].latitude = strtod(pos, &end); + if (*end) + goto error; + + pos = p; + count++; + } + + os_free(*data); + *n_linear_polygon_data = count; + *data = d; + + return 0; + +error: + os_free(d); + return -ENOMEM; +} + + +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos) +{ + os_free(conf->afc.op_class); + conf->afc.op_class = NULL; + + while (pos && pos[0]) { + char *p; + + p = os_strchr(pos, ','); + if (p) + *p++ = '\0'; + + int_array_add_unique(&conf->afc.op_class, atoi(pos)); + pos = p; + } + + return 0; +} + +#endif /* CONFIG_AFC */ #endif /* CONFIG_IEEE80211AX */ @@ -1834,233 +1968,6 @@ static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, return 0; } - -static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos) -{ - struct hs20_icon *icon; - char *end; - - icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1, - sizeof(struct hs20_icon)); - if (icon == NULL) - return -1; - bss->hs20_icons = icon; - icon = &bss->hs20_icons[bss->hs20_icons_count]; - os_memset(icon, 0, sizeof(*icon)); - - icon->width = atoi(pos); - pos = os_strchr(pos, ':'); - if (pos == NULL) - return -1; - pos++; - - icon->height = atoi(pos); - pos = os_strchr(pos, ':'); - if (pos == NULL) - return -1; - pos++; - - end = os_strchr(pos, ':'); - if (end == NULL || end - pos > 3) - return -1; - os_memcpy(icon->language, pos, end - pos); - pos = end + 1; - - end = os_strchr(pos, ':'); - if (end == NULL || end - pos > 255) - return -1; - os_memcpy(icon->type, pos, end - pos); - pos = end + 1; - - end = os_strchr(pos, ':'); - if (end == NULL || end - pos > 255) - return -1; - os_memcpy(icon->name, pos, end - pos); - pos = end + 1; - - if (os_strlen(pos) > 255) - return -1; - os_memcpy(icon->file, pos, os_strlen(pos)); - - bss->hs20_icons_count++; - - return 0; -} - - -static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss, - char *pos, int line) -{ - size_t slen; - char *str; - - str = wpa_config_parse_string(pos, &slen); - if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { - wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos); - os_free(str); - return -1; - } - - os_memcpy(bss->osu_ssid, str, slen); - bss->osu_ssid_len = slen; - os_free(str); - - return 0; -} - - -static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss, - char *pos, int line) -{ - struct hs20_osu_provider *p; - - p = os_realloc_array(bss->hs20_osu_providers, - bss->hs20_osu_providers_count + 1, sizeof(*p)); - if (p == NULL) - return -1; - - bss->hs20_osu_providers = p; - bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count]; - bss->hs20_osu_providers_count++; - os_memset(bss->last_osu, 0, sizeof(*p)); - bss->last_osu->server_uri = os_strdup(pos); - - return 0; -} - - -static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss, - char *pos, int line) -{ - if (bss->last_osu == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - if (parse_lang_string(&bss->last_osu->friendly_name, - &bss->last_osu->friendly_name_count, pos)) { - wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'", - line, pos); - return -1; - } - - return 0; -} - - -static int hs20_parse_osu_nai(struct hostapd_bss_config *bss, - char *pos, int line) -{ - if (bss->last_osu == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - os_free(bss->last_osu->osu_nai); - bss->last_osu->osu_nai = os_strdup(pos); - if (bss->last_osu->osu_nai == NULL) - return -1; - - return 0; -} - - -static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, - char *pos, int line) -{ - if (bss->last_osu == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - os_free(bss->last_osu->osu_nai2); - bss->last_osu->osu_nai2 = os_strdup(pos); - if (bss->last_osu->osu_nai2 == NULL) - return -1; - bss->hs20_osu_providers_nai_count++; - - return 0; -} - - -static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, - int line) -{ - if (bss->last_osu == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) { - wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line); - return -1; - } - - return 0; -} - - -static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos, - int line) -{ - char **n; - struct hs20_osu_provider *p = bss->last_osu; - - if (p == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *)); - if (n == NULL) - return -1; - p->icons = n; - p->icons[p->icons_count] = os_strdup(pos); - if (p->icons[p->icons_count] == NULL) - return -1; - p->icons_count++; - - return 0; -} - - -static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss, - char *pos, int line) -{ - if (bss->last_osu == NULL) { - wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); - return -1; - } - - if (parse_lang_string(&bss->last_osu->service_desc, - &bss->last_osu->service_desc_count, pos)) { - wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'", - line, pos); - return -1; - } - - return 0; -} - - -static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, - int line) -{ - char **n; - - n = os_realloc_array(bss->hs20_operator_icon, - bss->hs20_operator_icon_count + 1, sizeof(char *)); - if (!n) - return -1; - bss->hs20_operator_icon = n; - bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); - if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) - return -1; - bss->hs20_operator_icon_count++; - - return 0; -} - #endif /* CONFIG_HS20 */ @@ -2436,6 +2343,31 @@ static int get_u16(const char *pos, int line, u16 *ret_val) #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_TESTING_OPTIONS +static bool get_hexstream(const char *val, struct wpabuf **var, + const char *name, int line) +{ + struct wpabuf *tmp; + size_t len = os_strlen(val) / 2; + + tmp = wpabuf_alloc(len); + if (!tmp) + return false; + + if (hexstr2bin(val, wpabuf_put(tmp, len), len)) { + wpabuf_free(tmp); + wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", + line, name, val); + return false; + } + + wpabuf_free(*var); + *var = tmp; + return true; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -2731,8 +2663,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->eap_teap_auth = val; - } else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) { - bss->eap_teap_pac_no_inner = atoi(pos); } else if (os_strcmp(buf, "eap_teap_separate_result") == 0) { bss->eap_teap_separate_result = atoi(pos); } else if (os_strcmp(buf, "eap_teap_id") == 0) { @@ -3073,6 +3003,14 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->extended_key_id = val; +#ifdef CONFIG_ENC_ASSOC + } else if (os_strcmp(buf, "assoc_frame_encryption") == 0) { + bss->assoc_frame_encryption = atoi(pos); + } else if (os_strcmp(buf, "pmksa_caching_privacy") == 0) { + bss->pmksa_caching_privacy = atoi(pos); + } else if (os_strcmp(buf, "eap_using_authentication_frames") == 0) { + bss->eap_using_authentication_frames = atoi(pos); +#endif /* CONFIG_ENC_ASSOC */ } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { bss->wpa_group_rekey = atoi(pos); bss->wpa_group_rekey_set = 1; @@ -3156,6 +3094,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos); if (bss->wpa_key_mgmt == -1) return 1; + } else if (os_strcmp(buf, "rsn_override_key_mgmt") == 0) { + bss->rsn_override_key_mgmt = + hostapd_config_parse_key_mgmt(line, pos); + if (bss->rsn_override_key_mgmt == -1) + return 1; + } else if (os_strcmp(buf, "rsn_override_key_mgmt_2") == 0) { + bss->rsn_override_key_mgmt_2 = + hostapd_config_parse_key_mgmt(line, pos); + if (bss->rsn_override_key_mgmt_2 == -1) + return 1; } else if (os_strcmp(buf, "wpa_psk_radius") == 0) { bss->wpa_psk_radius = atoi(pos); if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED && @@ -3187,6 +3135,32 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "rsn_override_pairwise") == 0) { + bss->rsn_override_pairwise = + hostapd_config_parse_cipher(line, pos); + if (bss->rsn_override_pairwise == -1 || + bss->rsn_override_pairwise == 0) + return 1; + if (bss->rsn_override_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, + "Line %d: unsupported pairwise cipher suite '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "rsn_override_pairwise_2") == 0) { + bss->rsn_override_pairwise_2 = + hostapd_config_parse_cipher(line, pos); + if (bss->rsn_override_pairwise_2 == -1 || + bss->rsn_override_pairwise_2 == 0) + return 1; + if (bss->rsn_override_pairwise_2 & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, + "Line %d: unsupported pairwise cipher suite '%s'", + line, pos); + return 1; + } } else if (os_strcmp(buf, "group_cipher") == 0) { bss->group_cipher = hostapd_config_parse_cipher(line, pos); if (bss->group_cipher == -1 || bss->group_cipher == 0) @@ -3208,6 +3182,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->rsn_preauth_interfaces); bss->rsn_preauth_interfaces = os_strdup(pos); #endif /* CONFIG_RSN_PREAUTH */ + } else if (os_strcmp(buf, "rsn_override_omit_rsnxe") == 0) { + bss->rsn_override_omit_rsnxe = atoi(pos); + } else if (os_strcmp(buf, "rsn_override_mlo_compat") == 0) { + bss->rsn_override_mlo_compat = atoi(pos); } else if (os_strcmp(buf, "peerkey") == 0) { wpa_printf(MSG_INFO, "Line %d: Obsolete peerkey parameter ignored", line); @@ -3319,6 +3297,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->radius_server_auth_port = atoi(pos); } else if (os_strcmp(buf, "radius_server_acct_port") == 0) { bss->radius_server_acct_port = atoi(pos); + } else if (os_strcmp(buf, "radius_server_acct_log") == 0) { + bss->radius_server_acct_log = atoi(pos); } else if (os_strcmp(buf, "radius_server_ipv6") == 0) { bss->radius_server_ipv6 = atoi(pos); #endif /* RADIUS_SERVER */ @@ -3494,18 +3474,19 @@ static int hostapd_config_fill(struct hostapd_config *conf, } bss->send_probe_response = val; } else if (os_strcmp(buf, "supported_rates") == 0) { - if (hostapd_parse_intlist(&conf->supported_rates, pos)) { + if (hostapd_parse_intlist(&bss->supported_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate list", line); return 1; } } else if (os_strcmp(buf, "basic_rates") == 0) { - if (hostapd_parse_intlist(&conf->basic_rates, pos)) { + if (hostapd_parse_intlist(&bss->basic_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate list", line); return 1; } } else if (os_strcmp(buf, "beacon_rate") == 0) { + enum beacon_rate_type rate_type; int val; if (os_strncmp(pos, "ht:", 3) == 0) { @@ -3516,8 +3497,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, val); return 1; } - conf->rate_type = BEACON_RATE_HT; - conf->beacon_rate = val; + rate_type = BEACON_RATE_HT; } else if (os_strncmp(pos, "vht:", 4) == 0) { val = atoi(pos + 4); if (val < 0 || val > 9) { @@ -3526,8 +3506,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, val); return 1; } - conf->rate_type = BEACON_RATE_VHT; - conf->beacon_rate = val; + rate_type = BEACON_RATE_VHT; } else if (os_strncmp(pos, "he:", 3) == 0) { val = atoi(pos + 3); if (val < 0 || val > 11) { @@ -3536,8 +3515,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, val); return 1; } - conf->rate_type = BEACON_RATE_HE; - conf->beacon_rate = val; + rate_type = BEACON_RATE_HE; } else { val = atoi(pos); if (val < 10 || val > 10000) { @@ -3546,9 +3524,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, val); return 1; } - conf->rate_type = BEACON_RATE_LEGACY; - conf->beacon_rate = val; + rate_type = BEACON_RATE_LEGACY; } + + bss->rate_type = rate_type; + bss->beacon_rate = val; } else if (os_strcmp(buf, "preamble") == 0) { if (atoi(pos)) conf->preamble = SHORT_PREAMBLE; @@ -3642,6 +3622,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->use_driver_iface_addr = atoi(pos); } else if (os_strcmp(buf, "ieee80211w") == 0) { bss->ieee80211w = atoi(pos); + } else if (os_strcmp(buf, "rsn_override_mfp") == 0) { + bss->rsn_override_mfp = atoi(pos); + } else if (os_strcmp(buf, "rsn_override_mfp_2") == 0) { + bss->rsn_override_mfp_2 = atoi(pos); + } else if (os_strcmp(buf, "spp_amsdu") == 0) { + bss->spp_amsdu = !!atoi(pos); } else if (os_strcmp(buf, "group_mgmt_cipher") == 0) { if (os_strcmp(pos, "AES-128-CMAC") == 0) { bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC; @@ -3888,6 +3874,89 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->unsol_bcast_probe_resp_interval = val; +#ifdef CONFIG_AFC + } else if (os_strcmp(buf, "afcd_sock") == 0) { + os_free(conf->afc.socket); + conf->afc.socket = os_strdup(pos); + } else if (os_strcmp(buf, "afc_request_version") == 0) { + os_free(conf->afc.request.version); + conf->afc.request.version = os_strdup((pos)); + } else if (os_strcmp(buf, "afc_serial_number") == 0) { + os_free(conf->afc.request.sn); + conf->afc.request.sn = os_strdup((pos)); + } else if (os_strcmp(buf, "afc_cert_ids") == 0) { + if (hostapd_afc_parse_cert_ids(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid afc_cert_ids", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_location_type") == 0) { + conf->afc.location.type = atoi(pos); + if (conf->afc.location.type != AFC_ELLIPSE && + conf->afc.location.type != AFC_LINEAR_POLYGON && + conf->afc.location.type != AFC_RADIAL_POLYGON) { + wpa_printf(MSG_ERROR, + "Line %d: invalid afc_location_type value", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) { + if (hostapd_afc_parse_position_data( + &conf->afc.location.linear_polygon_data, + &conf->afc.location.n_linear_polygon_data, + pos)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid afc_linear_polygon", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) { + if (hostapd_afc_parse_position_data( + (struct afc_linear_polygon **) + &conf->afc.location.radial_polygon_data, + &conf->afc.location.n_radial_polygon_data, + pos)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid afc_radial_polygon", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_major_axis") == 0) { + conf->afc.location.major_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_minor_axis") == 0) { + conf->afc.location.minor_axis = atoi(pos); + } else if (os_strcmp(buf, "afc_orientation") == 0) { + conf->afc.location.orientation = atoi(pos); + } else if (os_strcmp(buf, "afc_height") == 0) { + char *end; + + conf->afc.location.height = strtod(pos, &end); + if (*end) { + wpa_printf(MSG_ERROR, + "Line %d: invalid afc_height value", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_height_type") == 0) { + os_free(conf->afc.location.height_type); + conf->afc.location.height_type = os_strdup(pos); + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) { + conf->afc.location.vertical_tolerance = atoi(pos); + } else if (os_strcmp(buf, "afc_min_power") == 0) { + conf->afc.min_power = atoi(pos); + } else if (os_strcmp(buf, "afc_freq_range") == 0) { + if (freq_range_list_parse(&conf->afc.freqs, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid afc_freq_range", + line); + return 1; + } + } else if (os_strcmp(buf, "afc_op_class") == 0) { + if (hostapd_afc_parse_op_class(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid afc_op_class", + line); + return 1; + } +#endif /* CONFIG_AFC */ } else if (os_strcmp(buf, "mbssid") == 0) { int mbssid = atoi(pos); if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { @@ -3897,6 +3966,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } conf->mbssid = mbssid; + } else if (os_strcmp(buf, "mbssid_index") == 0) { + bss->mbssid_index = atoi(pos); + } else if (os_strcmp(buf, "mbssid_max") == 0) { + conf->mbssid_max = atoi(pos); #endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -4248,12 +4321,24 @@ static int hostapd_config_fill(struct hostapd_config *conf, tok_start = os_strchr(tok_prev, ','); if (tok_start) { domain_len = tok_start - tok_prev; + if (domain_len > 255) { + wpa_printf(MSG_ERROR, + "Line %d: domain_name is too long (max 255)", line); + os_free(domain_list); + return 1; + } *domain_ptr = domain_len; os_memcpy(domain_ptr + 1, tok_prev, domain_len); domain_ptr += domain_len + 1; tok_prev = ++tok_start; } else { domain_len = os_strlen(tok_prev); + if (domain_len > 255) { + wpa_printf(MSG_ERROR, + "Line %d: domain_name is too long (max 255)", line); + os_free(domain_list); + return 1; + } *domain_ptr = domain_len; os_memcpy(domain_ptr + 1, tok_prev, domain_len); domain_ptr += domain_len + 1; @@ -4314,8 +4399,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->disable_dgaf = atoi(pos); } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { bss->na_mcast_to_ucast = atoi(pos); - } else if (os_strcmp(buf, "osen") == 0) { - bss->osen = atoi(pos); } else if (os_strcmp(buf, "anqp_domain_id") == 0) { bss->anqp_domain_id = atoi(pos); } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) { @@ -4354,44 +4437,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->hs20_operating_class); bss->hs20_operating_class = oper_class; bss->hs20_operating_class_len = oper_class_len; - } else if (os_strcmp(buf, "hs20_icon") == 0) { - if (hs20_parse_icon(bss, pos) < 0) { - wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'", - line, pos); - return 1; - } - } else if (os_strcmp(buf, "osu_ssid") == 0) { - if (hs20_parse_osu_ssid(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_server_uri") == 0) { - if (hs20_parse_osu_server_uri(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_friendly_name") == 0) { - if (hs20_parse_osu_friendly_name(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_nai") == 0) { - if (hs20_parse_osu_nai(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_nai2") == 0) { - if (hs20_parse_osu_nai2(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_method_list") == 0) { - if (hs20_parse_osu_method_list(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_icon") == 0) { - if (hs20_parse_osu_icon(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "osu_service_desc") == 0) { - if (hs20_parse_osu_service_desc(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "operator_icon") == 0) { - if (hs20_parse_operator_icon(bss, pos, line) < 0) - return 1; - } else if (os_strcmp(buf, "subscr_remediation_url") == 0) { - os_free(bss->subscr_remediation_url); - bss->subscr_remediation_url = os_strdup(pos); - } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { - bss->subscr_remediation_method = atoi(pos); } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) { os_free(bss->t_c_filename); bss->t_c_filename = os_strdup(pos); @@ -4400,9 +4445,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { os_free(bss->t_c_server_url); bss->t_c_server_url = os_strdup(pos); - } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) { - os_free(bss->hs20_sim_provisioning_url); - bss->hs20_sim_provisioning_url = os_strdup(pos); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO } else if (os_strcmp(buf, "mbo") == 0) { @@ -4432,6 +4474,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) } else if (os_strcmp(buf, "ecsa_ie_only") == 0) { conf->ecsa_ie_only = atoi(pos); + } else if (os_strcmp(buf, "csa_ie_only") == 0) { + conf->csa_ie_only = atoi(pos); } else if (os_strcmp(buf, "bss_load_test") == 0) { WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); pos = os_strchr(pos, ':'); @@ -4462,23 +4506,29 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->radio_measurements[0] |= WLAN_RRM_CAPS_NEIGHBOR_REPORT; } else if (os_strcmp(buf, "own_ie_override") == 0) { - struct wpabuf *tmp; - size_t len = os_strlen(pos) / 2; - - tmp = wpabuf_alloc(len); - if (!tmp) + if (!get_hexstream(pos, &bss->own_ie_override, + "own_ie_override", line)) return 1; - - if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) { - wpabuf_free(tmp); - wpa_printf(MSG_ERROR, - "Line %d: Invalid own_ie_override '%s'", - line, pos); + } else if (os_strcmp(buf, "rsne_override") == 0) { + if (!get_hexstream(pos, &bss->rsne_override, + "rsne_override", line)) + return 1; + } else if (os_strcmp(buf, "rsnoe_override") == 0) { + if (!get_hexstream(pos, &bss->rsnoe_override, + "rsnoe_override", line)) + return 1; + } else if (os_strcmp(buf, "rsno2e_override") == 0) { + if (!get_hexstream(pos, &bss->rsno2e_override, + "rsno2e_override", line)) + return 1; + } else if (os_strcmp(buf, "rsnxe_override") == 0) { + if (!get_hexstream(pos, &bss->rsnxe_override, + "rsnxe_override", line)) + return 1; + } else if (os_strcmp(buf, "rsnxoe_override") == 0) { + if (!get_hexstream(pos, &bss->rsnxoe_override, + "rsnxoe_override", line)) return 1; - } - - wpabuf_free(bss->own_ie_override); - bss->own_ie_override = tmp; } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { bss->sae_reflection_attack = atoi(pos); } else if (os_strcmp(buf, "sae_commit_status") == 0) { @@ -4540,6 +4590,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } else if (os_strcmp(buf, "eapol_m3_no_encrypt") == 0) { bss->eapol_m3_no_encrypt = atoi(pos); + } else if (os_strcmp(buf, "eapol_key_reserved_random") == 0) { + bss->eapol_key_reserved_random = atoi(pos); } else if (os_strcmp(buf, "test_assoc_comeback_type") == 0) { bss->test_assoc_comeback_type = atoi(pos); } else if (os_strcmp(buf, "presp_elements") == 0) { @@ -4560,6 +4612,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; } + } else if (os_strcmp(buf, "sae_password_psk") == 0) { + bss->sae_password_psk = atoi(pos); + } else if (os_strcmp(buf, "sae_track_password") == 0) { + bss->sae_track_password = atoi(pos); #endif /* CONFIG_SAE */ } else if (os_strcmp(buf, "vendor_elements") == 0) { if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) @@ -4585,6 +4641,13 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->sae_confirm_immediate = atoi(pos); } else if (os_strcmp(buf, "sae_pwe") == 0) { bss->sae_pwe = atoi(pos); + } else if (os_strcmp(buf, "sae_accept_h2e_without_use") == 0) { + bss->sae_accept_h2e_without_use = atoi(pos); + } else if (os_strcmp(buf, "sae_pw_id_num") == 0) { + bss->sae_pw_id_num = atoi(pos); + } else if (os_strcmp(buf, "sae_pw_id_key") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->sae_pw_id_key, pos)) + return 1; } else if (os_strcmp(buf, "local_pwr_constraint") == 0) { int val = atoi(pos); if (val < 0 || val > 255) { @@ -4674,6 +4737,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, wpabuf_free(conf->lci); conf->lci = NULL; } + if (conf->lci) { + /* Enable LCI capability in RM Enabled Capabilities + * element */ + bss->radio_measurements[1] |= + WLAN_RRM_CAPS_LCI_MEASUREMENT; + } else { + bss->radio_measurements[1] &= + ~WLAN_RRM_CAPS_LCI_MEASUREMENT; + } } else if (os_strcmp(buf, "civic") == 0) { wpabuf_free(conf->civic); conf->civic = wpabuf_parse_bin(pos); @@ -4681,6 +4753,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, wpabuf_free(conf->civic); conf->civic = NULL; } + if (conf->civic) { + /* Enable civic location capability in RM Enabled + * Capabilities element */ + bss->radio_measurements[4] |= + WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT; + } else { + bss->radio_measurements[4] &= + ~WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT; + } } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { if (atoi(pos)) bss->radio_measurements[0] |= @@ -4980,6 +5061,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->macsec_csindex = macsec_csindex; + } else if (os_strcmp(buf, "macsec_icv_indicator") == 0) { + int macsec_icv_indicator = atoi(pos); + + if (macsec_icv_indicator < 0 || macsec_icv_indicator > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_icv_indicator (%d): '%s'.", + line, macsec_icv_indicator, pos); + return 1; + } + bss->macsec_icv_indicator = macsec_icv_indicator; } else if (os_strcmp(buf, "mka_cak") == 0) { size_t len = os_strlen(pos); @@ -5024,6 +5115,17 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->force_kdk_derivation = atoi(pos); } else if (os_strcmp(buf, "pasn_corrupt_mic") == 0) { bss->pasn_corrupt_mic = atoi(pos); + } else if (os_strcmp(buf, "pasn_test_groups") == 0) { + int *groups = NULL; + + if (hostapd_parse_intlist(&groups, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid pasn_test_groups value '%s'", + line, pos); + return 1; + } + os_free(bss->pasn_test_groups); + bss->pasn_test_groups = groups; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "pasn_groups") == 0) { if (hostapd_parse_intlist(&bss->pasn_groups, pos)) { @@ -5036,6 +5138,14 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->pasn_comeback_after = atoi(pos); } else if (os_strcmp(buf, "pasn_noauth") == 0) { bss->pasn_noauth = atoi(pos); +#ifdef CONFIG_ENC_ASSOC + } else if (os_strcmp(buf, "eppke_unauth") == 0) { + bss->eppke_unauth = atoi(pos); +#endif /* CONFIG_ENC_ASSOC */ + } else if (os_strcmp(buf, "urnm_mfpr") == 0) { + bss->urnm_mfpr = !!atoi(pos); + } else if (os_strcmp(buf, "urnm_mfpr_x20") == 0) { + bss->urnm_mfpr_x20 = !!atoi(pos); #endif /* CONFIG_PASN */ } else if (os_strcmp(buf, "ext_capa_mask") == 0) { if (get_hex_config(bss->ext_capa_mask, EXT_CAPA_MAX_LEN, @@ -5053,9 +5163,23 @@ static int hostapd_config_fill(struct hostapd_config *conf, if (val < 0 || val > 1) return 1; bss->ssid_protection = val; + } else if (os_strcmp(buf, "known_sta_identification") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) + return 1; + bss->known_sta_identification = val; + } else if (os_strcmp(buf, "channel_usage") == 0) { + conf->channel_usage = atoi(pos); + } else if (os_strcmp(buf, "peer_to_peer_twt") == 0) { + conf->peer_to_peer_twt = atoi(pos); #ifdef CONFIG_IEEE80211BE } else if (os_strcmp(buf, "ieee80211be") == 0) { conf->ieee80211be = atoi(pos); + } else if (os_strcmp(buf, "require_eht") == 0) { + conf->require_eht = atoi(pos); + } else if (os_strcmp(buf, "bss_require_eht") == 0) { + bss->bss_require_eht = atoi(pos); } else if (os_strcmp(buf, "eht_oper_chwidth") == 0) { conf->eht_oper_chwidth = atoi(pos); } else if (os_strcmp(buf, "eht_oper_centr_freq_seg0_idx") == 0) { @@ -5097,8 +5221,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } else if (os_strcmp(buf, "mld_indicate_disabled") == 0) { bss->mld_indicate_disabled = atoi(pos); + } else if (os_strcmp(buf, "disable_mcs15_rx") == 0) { + conf->disable_mcs15_rx = atoi(pos); #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_IEEE80211BE */ + } else if (os_strcmp(buf, "i2r_lmr_policy") == 0) { + conf->i2r_lmr_policy = atoi(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c index 39b9ef59dc57..e6ef61508325 100644 --- a/contrib/wpa/hostapd/ctrl_iface.c +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -30,6 +30,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/module_tests.h" +#include "utils/trace.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ctrl_iface_common.h" @@ -38,8 +39,8 @@ #endif /* CONFIG_DPP */ #include "common/wpa_ctrl.h" #include "common/ptksa_cache.h" -#include "common/hw_features_common.h" #include "common/nan_de.h" +#include "common/proc_coord.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -596,31 +597,8 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ + #ifdef CONFIG_HS20 - -static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd, - const char *cmd) -{ - u8 addr[ETH_ALEN]; - const char *url; - - if (hwaddr_aton(cmd, addr)) - return -1; - url = cmd + 17; - if (*url == '\0') { - url = NULL; - } else { - if (*url != ' ') - return -1; - url++; - if (*url == '\0') - url = NULL; - } - - return hs20_send_wnm_notification(hapd, addr, 1, url); -} - - static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd, const char *cmd) { @@ -669,7 +647,6 @@ static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd, wpabuf_free(req); return ret; } - #endif /* CONFIG_HS20 */ @@ -1240,6 +1217,16 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) hapd->ext_mgmt_frame_handling = atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); + } else if (os_strcasecmp(cmd, "association_response_status_code") == 0) + { + if (os_strcasecmp(value, "disable") == 0) + hapd->conf->association_response_status_code = -1; + else + hapd->conf->association_response_status_code = + atoi(value); + wpa_printf(MSG_DEBUG, + "TESTING: association_response_status_code=%d", + hapd->conf->association_response_status_code); } else if (os_strcasecmp(cmd, "force_backlog_bytes") == 0) { hapd->force_backlog_bytes = atoi(value); #ifdef CONFIG_DPP @@ -1313,7 +1300,6 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) hostapd_neighbor_sync_own_report(hapd); } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || os_strncmp(cmd, "wmm_ac_", 7) == 0) { - hapd->parameter_set_count++; if (ieee802_11_update_beacons(hapd->iface)) wpa_printf(MSG_DEBUG, "Failed to update beacons with WMM parameters"); @@ -2453,198 +2439,6 @@ static int hostapd_ctrl_register_frame(struct hostapd_data *hapd, #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef NEED_AP_MLME -static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params, - u16 punct_bitmap) -{ - u32 start_freq; - - if (is_6ghz_freq(params->freq)) { - const int bw_idx[] = { 20, 40, 80, 160, 320 }; - int idx, bw; - - /* The 6 GHz band requires HE to be enabled. */ - params->he_enabled = 1; - - if (params->center_freq1) { - if (params->freq == 5935) - idx = (params->center_freq1 - 5925) / 5; - else - idx = (params->center_freq1 - 5950) / 5; - - bw = center_idx_to_bw_6ghz(idx); - if (bw < 0 || bw > (int) ARRAY_SIZE(bw_idx) || - bw_idx[bw] != params->bandwidth) - return -1; - } - } else { /* Non-6 GHz channel */ - /* An EHT STA is also an HE STA as defined in - * IEEE P802.11be/D5.0, 4.3.16a. */ - if (params->he_enabled || params->eht_enabled) { - params->he_enabled = 1; - /* An HE STA is also a VHT STA if operating in the 5 GHz - * band and an HE STA is also an HT STA in the 2.4 GHz - * band as defined in IEEE Std 802.11ax-2021, 4.3.15a. - * A VHT STA is an HT STA as defined in IEEE - * Std 802.11, 4.3.15. */ - if (IS_5GHZ(params->freq)) - params->vht_enabled = 1; - - params->ht_enabled = 1; - } - } - - switch (params->bandwidth) { - case 0: - /* bandwidth not specified: use 20 MHz by default */ - /* fall-through */ - case 20: - if (params->center_freq1 && - params->center_freq1 != params->freq) - return -1; - - if (params->center_freq2 || params->sec_channel_offset) - return -1; - - if (punct_bitmap) - return -1; - break; - case 40: - if (params->center_freq2 || !params->sec_channel_offset) - return -1; - - if (punct_bitmap) - return -1; - - if (!params->center_freq1) - break; - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 10 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq - 10 != params->center_freq1) - return -1; - break; - default: - return -1; - } - break; - case 80: - if (!params->center_freq1 || !params->sec_channel_offset) - return 1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq - 10 != params->center_freq1 && - params->freq + 30 != params->center_freq1) - return 1; - break; - case -1: - if (params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1) - return -1; - break; - default: - return -1; - } - - if (params->center_freq2 && punct_bitmap) - return -1; - - /* Adjacent and overlapped are not allowed for 80+80 */ - if (params->center_freq2 && - params->center_freq1 - params->center_freq2 <= 80 && - params->center_freq2 - params->center_freq1 <= 80) - return 1; - break; - case 160: - if (!params->center_freq1 || params->center_freq2 || - !params->sec_channel_offset) - return -1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 70 != params->center_freq1 && - params->freq + 30 != params->center_freq1 && - params->freq - 10 != params->center_freq1 && - params->freq - 50 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq + 50 != params->center_freq1 && - params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1 && - params->freq - 70 != params->center_freq1) - return -1; - break; - default: - return -1; - } - break; - case 320: - if (!params->center_freq1 || params->center_freq2 || - !params->sec_channel_offset) - return -1; - - switch (params->sec_channel_offset) { - case 1: - if (params->freq + 150 != params->center_freq1 && - params->freq + 110 != params->center_freq1 && - params->freq + 70 != params->center_freq1 && - params->freq + 30 != params->center_freq1 && - params->freq - 10 != params->center_freq1 && - params->freq - 50 != params->center_freq1 && - params->freq - 90 != params->center_freq1 && - params->freq - 130 != params->center_freq1) - return -1; - break; - case -1: - if (params->freq + 130 != params->center_freq1 && - params->freq + 90 != params->center_freq1 && - params->freq + 50 != params->center_freq1 && - params->freq + 10 != params->center_freq1 && - params->freq - 30 != params->center_freq1 && - params->freq - 70 != params->center_freq1 && - params->freq - 110 != params->center_freq1 && - params->freq - 150 != params->center_freq1) - return -1; - break; - } - break; - default: - return -1; - } - - if (!punct_bitmap) - return 0; - - if (!params->eht_enabled) { - wpa_printf(MSG_ERROR, - "Preamble puncturing supported only in EHT"); - return -1; - } - - if (params->freq >= 2412 && params->freq <= 2484) { - wpa_printf(MSG_ERROR, - "Preamble puncturing is not supported in 2.4 GHz"); - return -1; - } - - start_freq = params->center_freq1 - (params->bandwidth / 2); - if (!is_punct_bitmap_valid(params->bandwidth, - (params->freq - start_freq) / 20, - punct_bitmap)) { - wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap"); - return -1; - } - - return 0; -} -#endif /* NEED_AP_MLME */ - - static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, char *pos) { @@ -2658,24 +2452,29 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, unsigned int num_err = 0; int err = 0; - ret = hostapd_parse_csa_settings(pos, &settings); + ret = hostapd_parse_csa_settings(iface, pos, &settings); if (ret) return ret; settings.link_id = -1; #ifdef CONFIG_IEEE80211BE + /* Reject if EHT is disabled in channel switch settings but the + * interface has a BSS affiliated with an AP MLD where EHT is mandatory + * to be enabled. */ + if (!settings.freq_params.eht_enabled) { + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->conf->mld_ap) { + wpa_printf(MSG_INFO, + "Do not allow EHT to be disabled when the interface has an ML BSS"); + return -1; + } + } + } + if (iface->num_bss && iface->bss[0]->conf->mld_ap) settings.link_id = iface->bss[0]->mld_link_id; #endif /* CONFIG_IEEE80211BE */ - ret = hostapd_ctrl_check_freq_params(&settings.freq_params, - settings.punct_bitmap); - if (ret) { - wpa_printf(MSG_INFO, - "chanswitch: invalid frequency settings provided"); - return ret; - } - switch (settings.freq_params.bandwidth) { case 40: bandwidth = CHAN_WIDTH_40; @@ -2735,6 +2534,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, return 0; } + if (iface->cac_started) { + wpa_printf(MSG_DEBUG, + "CAC is in progress - switching channel without CSA"); + return hostapd_force_channel_switch(iface, &settings); + } + for (i = 0; i < iface->num_bss; i++) { /* Save CHAN_SWITCH VHT, HE, and EHT config */ @@ -2761,7 +2566,6 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface, { #ifdef NEED_AP_MLME struct cca_settings settings; - struct hostapd_data *hapd = iface->bss[0]; int ret, color; unsigned int i; char *end; @@ -2807,13 +2611,16 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface, return 0; } - if (hapd->cca_in_progress) { + if (hostapd_is_cca_in_progress(iface)) { wpa_printf(MSG_ERROR, "color_change: CCA is already in progress"); return -1; } - iface->conf->he_op.he_bss_color_disabled = 0; + /* IEEE Std 802.11-2024, 26.17.3.4: BSS Color Disabled shall be 1 during + * the time leading up to the BSS color change TBTT. + */ + iface->conf->he_op.he_bss_color_disabled = 1; for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; @@ -2830,6 +2637,8 @@ static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface, hostapd_cleanup_cca_params(bss); continue; } + eloop_cancel_timeout(hostapd_switch_color_timeout_handler, + bss, NULL); wpa_printf(MSG_DEBUG, "Setting user selected color: %d", color); ret = hostapd_drv_switch_color(bss, &settings); @@ -2997,6 +2806,57 @@ static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd, } +#ifdef CONFIG_TESTING_OPTIONS +static int hostapd_ctrl_iface_set_bw(struct hostapd_iface *iface, char *pos) +{ +#ifdef NEED_AP_MLME + struct hostapd_freq_params freq_params; + int ret; + enum oper_chan_width chanwidth; + u8 chan, oper_class; + + if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE)) + return -1; + + ret = hostapd_parse_freq_params(pos, &freq_params, iface->freq); + if (ret) + return ret; + + chanwidth = hostapd_chan_width_from_freq_params(&freq_params); + + if (ieee80211_freq_to_channel_ext( + freq_params.freq, + freq_params.sec_channel_offset, + chanwidth, &oper_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "invalid channel: (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)", + freq_params.freq, + freq_params.sec_channel_offset, + freq_params.vht_enabled, + freq_params.he_enabled); + return -1; + } + + freq_params.channel = chan; + + /* FIXME: What if the newly extended channel overlaps radar ranges? */ + + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &freq_params, NULL); + if (ret) + return ret; + + ieee802_11_set_beacons(iface); + return 0; + +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} +#endif /* CONFIG_TESTING_OPTIONS */ + + static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply, int reply_size, const char *param) { @@ -3166,6 +3026,7 @@ static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd, #ifdef NEED_AP_MLME + static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -3199,6 +3060,35 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, return pos - buf; } + + +static int hostapd_ctrl_iface_dump_beacon(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + struct beacon_data beacon; + char *pos, *end; + int ret; + + if (hostapd_build_beacon_data(hapd, &beacon) < 0) + return -1; + + if (2 * (beacon.head_len + beacon.tail_len) > buflen) + return -1; + + pos = buf; + end = buf + buflen; + + ret = wpa_snprintf_hex(pos, end - pos, beacon.head, beacon.head_len); + pos += ret; + + ret = wpa_snprintf_hex(pos, end - pos, beacon.tail, beacon.tail_len); + pos += ret; + + free_beacon_data(&beacon); + + return pos - buf; +} + #endif /* NEED_AP_MLME */ @@ -3669,26 +3559,87 @@ static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface) #ifdef CONFIG_TESTING_OPTIONS -static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd, - char *buf, size_t buflen) + +static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd) { - int ret; u32 count = atoi(cmd); if (!count) count = 1; - ret = hostapd_link_remove(hapd, count); - if (ret == 0) { - ret = os_snprintf(buf, buflen, "%s\n", "OK"); - if (os_snprintf_error(buflen, ret)) - ret = -1; - else - ret = 0; + return hostapd_link_remove(hapd, count); +} + + +static int hostapd_ctrl_iface_link_enable(struct hostapd_data *hapd, char *cmd) +{ + struct hapd_interfaces *interfaces = hapd->iface->interfaces; + const char *conf_file, *phy, *ifname; + struct hostapd_iface *iface = NULL; + size_t len = os_strlen(cmd) + 1; + struct hostapd_config *conf; + char *pos, *cmd_buf; + int ret = -1; + size_t i; + + if (!hapd || !hapd->conf->mld_ap || !hapd->mld || + os_strncmp(cmd, "bss_config=", 11) != 0) + return -1; + + cmd_buf = os_malloc(len); + if (!cmd_buf) + return -1; + + os_snprintf(cmd_buf, len, "%s", cmd); + phy = cmd_buf + 11; + pos = os_strchr(phy, ':'); + if (!pos) + goto free_cmd_buffer; + + *pos++ = '\0'; + conf_file = pos; + if (*conf_file == '\0') + goto free_cmd_buffer; + + conf = interfaces->config_read_cb(conf_file); + if (!conf) + goto free_cmd_buffer; + + if (!conf->bss[0]->mld_ap) + goto free_config; + + ifname = conf->bss[0]->iface; + if (!ifname || ifname[0] == '\0') + goto free_config; + + for (i = 0; i < interfaces->count; i++) { + if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) { + iface = interfaces->iface[i]; + break; + } } + if (!iface || iface->state != HAPD_IFACE_DISABLED) + goto free_config; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *h = iface->bss[i]; + + if (os_strncmp(ifname, h->conf->iface, + sizeof(h->conf->iface)) == 0) { + ret = hostapd_enable_iface(iface); + break; + } + } + +free_config: + hostapd_config_free(conf); +free_cmd_buffer: + os_free(cmd_buf); + return ret; } + #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_IEEE80211BE */ @@ -3705,6 +3656,7 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd, struct wpabuf *ssi = NULL; int ret = -1; enum nan_service_protocol_type srv_proto_type = 0; + bool p2p = false; os_memset(¶ms, 0, sizeof(params)); /* USD shall use both solicited and unsolicited transmissions */ @@ -3738,6 +3690,11 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd, continue; } + if (os_strcmp(token, "p2p=1") == 0) { + p2p = true; + continue; + } + if (os_strcmp(token, "solicited=0") == 0) { params.solicited = false; continue; @@ -3759,7 +3716,7 @@ static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd, } publish_id = hostapd_nan_usd_publish(hapd, service_name, srv_proto_type, - ssi, ¶ms); + ssi, ¶ms, p2p); if (publish_id > 0) ret = os_snprintf(buf, buflen, "%d", publish_id); fail: @@ -3842,6 +3799,7 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd, struct wpabuf *ssi = NULL; int ret = -1; enum nan_service_protocol_type srv_proto_type = 0; + bool p2p = false; os_memset(¶ms, 0, sizeof(params)); @@ -3875,6 +3833,11 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd, continue; } + if (os_strcmp(token, "p2p=1") == 0) { + p2p = true; + continue; + } + wpa_printf(MSG_INFO, "CTRL: Invalid NAN_SUBSCRIBE parameter: %s", token); @@ -3883,7 +3846,7 @@ static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd, subscribe_id = hostapd_nan_usd_subscribe(hapd, service_name, srv_proto_type, ssi, - ¶ms); + ¶ms, p2p); if (subscribe_id > 0) ret = os_snprintf(buf, buflen, "%d", subscribe_id); fail: @@ -3970,7 +3933,7 @@ static int hostapd_ctrl_nan_transmit(struct hostapd_data *hapd, char *cmd) } ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr, - req_instance_id); + req_instance_id); fail: wpabuf_free(ssi); return ret; @@ -3979,6 +3942,126 @@ fail: #endif /* CONFIG_NAN_USD */ +#ifdef CONFIG_SAE +static int hostapd_ctrl_iface_sae_password_bind(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *password; + + if (hwaddr_aton(cmd, addr)) + return -1; + password = os_strchr(cmd, ' '); + if (!password) + return -1; + password++; + + return sae_password_bind(hapd, addr, password); +} +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_PROCESS_COORDINATION + +static bool hapd_ctrl_proc_coord_cb(void *ctx, int src, + enum proc_coord_message_types msg_type, + enum proc_coord_commands cmd, + u32 seq, const struct wpabuf *msg) +{ + struct hostapd_data *hapd = ctx; + + if (cmd != PROC_COORD_CMD_TEST) + return false; + + wpa_msg(hapd->msg_ctx, MSG_INFO, + "PROC-COORD-TEST RX src=%u msg_type=%d seq=%u msg_len=%zu", + src, msg_type, seq, wpabuf_len(msg)); + + if (msg_type == PROC_COORD_MSG_REQUEST) + proc_coord_send_response(hapd->iface->interfaces->pc, + src, cmd, seq, msg); + return false; +} + + +static void hapd_ctrl_proc_coord_test_cb(void *ctx, int pid, + const struct wpabuf *msg) +{ + struct hostapd_data *hapd = ctx; + + wpa_msg(hapd->msg_ctx, MSG_INFO, + "PROC-COORD-TEST RX-RESP src=%u msg_len=%d", + pid, msg ? (int) wpabuf_len(msg) : -1); + +} + + +static int hostapd_ctrl_iface_proc_coord_test(struct hostapd_data *hapd, + const char *cmd) +{ + int res, dst; + struct wpabuf *msg; + + if (!hapd->iface->interfaces->pc) + return -1; + + dst = atoi(cmd); + + msg = wpabuf_alloc(1); + if (!msg) + return -1; + wpabuf_put_u8(msg, 123); + + res = proc_coord_send_request(hapd->iface->interfaces->pc, + dst, PROC_COORD_CMD_TEST, msg, 1000, + hapd_ctrl_proc_coord_test_cb, hapd); + wpabuf_free(msg); + return res < 0 ? -1 : 0; +} + +#endif /* CONFIG_PROCESS_COORDINATION */ +#endif /* CONFIG_TESTING_OPTIONS */ + + +#ifdef CONFIG_AFC + +static int hostapd_ctrl_afc_get_request(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + size_t len; + + if (!hapd->iface->afc_request) + return -1; + + len = wpabuf_len(hapd->iface->afc_request); + if (buflen < len + 1) + return -1; + os_memcpy(buf, wpabuf_head(hapd->iface->afc_request), len); + buf[len] = '\n'; + return len + 1; +} + + +static int hostapd_ctrl_afc_get_response(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + if (!hapd->iface->afc_response) + return -1; + + return os_snprintf(buf, buflen, "%s\n", hapd->iface->afc_response); +} + + +static int hostapd_ctrl_afc_send_request(struct hostapd_data *hapd, char *cmd) +{ + hostapd_afc_send_request(hapd->iface); + return 0; +} + +#endif /* CONFIG_AFC */ + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, @@ -4000,6 +4083,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, wpa_debug_stop_log(); } else if (os_strncmp(buf, "NOTE ", 5) == 0) { wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + wpa_trace_set_context(buf + 5); } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = hostapd_ctrl_iface_status(hapd, reply, reply_size); @@ -4133,9 +4217,6 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 - } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) { - if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15)) - reply_len = -1; } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) { if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) reply_len = -1; @@ -4258,6 +4339,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) { if (hostapd_ctrl_register_frame(hapd, buf + 16) < 0) reply_len = -1; + } else if (os_strncmp(buf, "SET_BW ", 7) == 0) { + /* note: preserve the space for hostapd_parse_freq_params() */ + if (hostapd_ctrl_iface_set_bw(hapd->iface, buf + 6)) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -4291,6 +4376,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) { reply_len = hostapd_ctrl_iface_track_sta_list( hapd, reply, reply_size); + } else if (os_strcmp(buf, "DUMP_BEACON") == 0) { + reply_len = hostapd_ctrl_iface_dump_beacon(hapd, reply, + reply_size); #endif /* NEED_AP_MLME */ } else if (os_strcmp(buf, "PMKSA") == 0) { reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, @@ -4577,11 +4665,37 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; #ifdef CONFIG_TESTING_OPTIONS } else if (os_strncmp(buf, "LINK_REMOVE ", 12) == 0) { - if (hostapd_ctrl_iface_link_remove(hapd, buf + 12, - reply, reply_size)) + if (hostapd_ctrl_iface_link_remove(hapd, buf + 12)) + reply_len = -1; + } else if (os_strncmp(buf, "LINK_ENABLE ", 12) == 0) { + if (hostapd_ctrl_iface_link_enable(hapd, buf + 12)) reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_SAE + } else if (os_strncmp(buf, "SAE_PASSWORD_BIND ", 18) == 0) { + if (hostapd_ctrl_iface_sae_password_bind(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_PROCESS_COORDINATION + } else if (os_strncmp(buf, "PROC_COORD_TEST ", 16) == 0) { + if (hostapd_ctrl_iface_proc_coord_test(hapd, buf + 16)) + reply_len = -1; +#endif /* CONFIG_PROCESS_COORDINATION */ +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_AFC + } else if (os_strncmp(buf, "AFC_GET_REQUEST", 15) == 0) { + reply_len = hostapd_ctrl_afc_get_request(hapd, buf + 15, + reply, reply_size); + } else if (os_strncmp(buf, "AFC_GET_RESPONSE", 16) == 0) { + reply_len = hostapd_ctrl_afc_get_response(hapd, buf + 16, + reply, + reply_size); + } else if (os_strncmp(buf, "AFC_SEND_REQUEST", 16) == 0) { + if (hostapd_ctrl_afc_send_request(hapd, buf + 16) < 0) + reply_len = -1; +#endif /* CONFIG_AFC */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -4605,7 +4719,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply, *pos = buf; - const int reply_size = 4096; + const int reply_size = 8192; int reply_len; int level = MSG_DEBUG; #ifdef CONFIG_CTRL_IFACE_UDP @@ -4682,23 +4796,360 @@ done: } +#ifdef CONFIG_IEEE80211BE +#ifndef CONFIG_CTRL_IFACE_UDP + +static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld, + char *buf, char *reply, + size_t reply_size, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct hostapd_data *link_hapd, *link_itr; + int reply_len = -1, link_id = -1; + char *cmd; + bool found = false; + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + cmd = buf; + + /* Check whether the link ID is provided in the command */ + if (os_strncmp(cmd, "LINKID ", 7) == 0) { + cmd += 7; + link_id = atoi(cmd); + if (link_id < 0 || link_id >= 15) { + os_memcpy(reply, "INVALID LINK ID\n", 16); + reply_len = 16; + goto out; + } + + cmd = os_strchr(cmd, ' '); + if (!cmd) + goto out; + cmd++; + } + if (link_id >= 0) { + link_hapd = mld->fbss; + if (!link_hapd) { + os_memcpy(reply, "NO LINKS ACTIVE\n", 16); + reply_len = 16; + goto out; + } + + for_each_mld_link(link_itr, link_hapd) { + if (link_itr->mld_link_id == link_id) { + found = true; + break; + } + } + + if (!found) + goto out; + + link_hapd = link_itr; + } else { + link_hapd = mld->fbss; + } + + if (os_strcmp(cmd, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strcmp(cmd, "ATTACH") == 0) { + if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(cmd, "ATTACH ", 7) == 0) { + if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, cmd + 7)) + reply_len = -1; + } else if (os_strcmp(cmd, "DETACH") == 0) { + if (ctrl_iface_detach(&mld->ctrl_dst, from, fromlen)) + reply_len = -1; + } else { + if (link_id == -1) + wpa_printf(MSG_DEBUG, + "Link ID not provided, using the first link BSS (if available)"); + + if (!link_hapd) + reply_len = -1; + else + reply_len = + hostapd_ctrl_iface_receive_process( + link_hapd, cmd, reply, reply_size, + from, fromlen); + } + +out: + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + return reply_len; +} + + +static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_mld *mld = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply, *pos = buf; + const size_t reply_size = 8192; + int reply_len; + int level = MSG_DEBUG; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + + reply = os_malloc(reply_size); + if (!reply) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + + wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res); + + reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos, + reply, reply_size, + &from, fromlen); + + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + os_free(reply); +} + + +static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld) +{ + size_t len; + char *buf; + int ret; + + if (!mld->ctrl_interface) + return NULL; + + len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2; + + buf = os_malloc(len); + if (!buf) + return NULL; + + ret = os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name); + if (os_snprintf_error(len, ret)) { + os_free(buf); + return NULL; + } + + return buf; +} + +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (!mld) + return -1; + + if (mld->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!", + mld->name); + return 0; + } + + dl_list_init(&mld->ctrl_dst); + + if (!mld->ctrl_interface) + return 0; + + if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, + "Using existing control interface directory."); + } else { + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + } + + if (os_strlen(mld->ctrl_interface) + 1 + os_strlen(mld->name) >= + sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (!fname) + goto fail; + + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name); + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not allow connections - assuming it was left over from forced program termination"); + if (unlink(fname) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, + "hostapd-ctrl-iface: bind(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, + "Successfully replaced leftover ctrl_iface socket '%s'", + fname); + } else { + wpa_printf(MSG_INFO, + "ctrl_iface exists and seems to be in use - cannot override it"); + wpa_printf(MSG_INFO, + "Delete '%s' manually if it is not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + os_free(fname); + + mld->ctrl_sock = s; + + if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld, + NULL) < 0) + return -1; + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + return 0; +} + + +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct wpa_ctrl_dst *dst, *prev; + + if (mld->ctrl_sock > -1) { + char *fname; + + eloop_unregister_read_sock(mld->ctrl_sock); + close(mld->ctrl_sock); + mld->ctrl_sock = -1; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (mld->ctrl_interface && + rmdir(mld->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, + "MLD control interface directory not empty - leaving it behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + mld->ctrl_interface, + strerror(errno)); + } + } + } + + dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + os_free(mld->ctrl_interface); +} + +#endif /* CONFIG_IEEE80211BE */ + + #ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { char *buf; size_t len; + const char *ctrl_sock_iface; + +#ifdef CONFIG_IEEE80211BE + ctrl_sock_iface = hapd->ctrl_sock_iface; +#else /* CONFIG_IEEE80211BE */ + ctrl_sock_iface = hapd->conf->iface; +#endif /* CONFIG_IEEE80211BE */ if (hapd->conf->ctrl_interface == NULL) return NULL; len = os_strlen(hapd->conf->ctrl_interface) + - os_strlen(hapd->conf->iface) + 2; + os_strlen(ctrl_sock_iface) + 2; + buf = os_malloc(len); if (buf == NULL) return NULL; os_snprintf(buf, len, "%s/%s", - hapd->conf->ctrl_interface, hapd->conf->iface); + hapd->conf->ctrl_interface, ctrl_sock_iface); buf[len - 1] = '\0'; return buf; } @@ -4814,6 +5265,7 @@ fail: struct sockaddr_un addr; int s = -1; char *fname = NULL; + size_t iflen; if (hapd->ctrl_sock > -1) { wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); @@ -4868,8 +5320,13 @@ fail: } #endif /* ANDROID */ +#ifdef CONFIG_IEEE80211BE + iflen = os_strlen(hapd->ctrl_sock_iface); +#else /* CONFIG_IEEE80211BE */ + iflen = os_strlen(hapd->conf->iface); +#endif /* CONFIG_IEEE80211BE */ if (os_strlen(hapd->conf->ctrl_interface) + 1 + - os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) + iflen >= sizeof(addr.sun_path)) goto fail; s = socket(PF_UNIX, SOCK_DGRAM, 0); @@ -4951,6 +5408,14 @@ fail: hapd->msg_ctx = hapd; wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_PROCESS_COORDINATION + if (hapd->iface->interfaces->pc) + proc_coord_register_handler(hapd->iface->interfaces->pc, + hapd_ctrl_proc_coord_cb, hapd); +#endif /* CONFIG_PROCESS_COORDINATION */ +#endif /* CONFIG_TESTING_OPTIONS */ + return 0; fail: @@ -5006,6 +5471,14 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(hapd->l2_test); hapd->l2_test = NULL; +#ifdef CONFIG_PROCESS_COORDINATION + if (hapd->iface->interfaces->pc) { + proc_coord_unregister_handler(hapd->iface->interfaces->pc, + hapd_ctrl_proc_coord_cb, hapd); + proc_coord_cancel_wait(hapd->iface->interfaces->pc, + hapd_ctrl_proc_coord_test_cb, hapd); + } +#endif /* CONFIG_PROCESS_COORDINATION */ #endif /* CONFIG_TESTING_OPTIONS */ } @@ -5353,7 +5826,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, socklen_t fromlen = sizeof(from); char *reply; int reply_len; - const int reply_size = 4096; + const int reply_size = 8192; #ifdef CONFIG_CTRL_IFACE_UDP unsigned char lcookie[CTRL_IFACE_COOKIE_LEN]; #endif /* CONFIG_CTRL_IFACE_UDP */ @@ -5822,7 +6295,7 @@ static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst, &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; - if (sendmsg(sock, &msg, 0) < 0) { + if (sendmsg(sock, &msg, MSG_DONTWAIT) < 0) { int _errno = errno; wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " "%d - %s", diff --git a/contrib/wpa/hostapd/ctrl_iface.h b/contrib/wpa/hostapd/ctrl_iface.h index 3341a66bdc6c..6ce209d22c91 100644 --- a/contrib/wpa/hostapd/ctrl_iface.h +++ b/contrib/wpa/hostapd/ctrl_iface.h @@ -14,6 +14,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld); +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld); #else /* CONFIG_NO_CTRL_IFACE */ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig index 550db697bc0a..13bce7aaa88b 100644 --- a/contrib/wpa/hostapd/defconfig +++ b/contrib/wpa/hostapd/defconfig @@ -9,9 +9,6 @@ # be modified from here. In most cass, these lines should use += in order not # to override previous values of the variables. -# Driver interface for Host AP driver -CONFIG_DRIVER_HOSTAP=y - # Driver interface for wired authenticator #CONFIG_DRIVER_WIRED=y @@ -152,7 +149,6 @@ CONFIG_IPV6=y #CONFIG_DRIVER_RADIUS_ACL=y # Wireless Network Management (IEEE Std 802.11v-2011) -# Note: This is experimental and not complete implementation. #CONFIG_WNM=y # IEEE 802.11ac (Very High Throughput) support @@ -163,9 +159,6 @@ CONFIG_IPV6=y # IEEE 802.11be EHT support # CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE. -# Note: This is experimental and work in progress. The definitions are still -# subject to change and this should not be expected to interoperate with the -# final IEEE 802.11be version. #CONFIG_IEEE80211BE=y # Simultaneous Authentication of Equals (SAE), WPA3-Personal @@ -384,8 +377,7 @@ CONFIG_IPV6=y #CONFIG_WPA_CLI_EDIT=y # Opportunistic Wireless Encryption (OWE) -# Experimental implementation of draft-harkins-owe-07.txt -#CONFIG_OWE=y +CONFIG_OWE=y # Airtime policy support #CONFIG_AIRTIME_POLICY=y @@ -410,18 +402,32 @@ CONFIG_IPV6=y #CONFIG_NO_TKIP=y # Pre-Association Security Negotiation (PASN) -# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol -# design is still subject to change. As such, this should not yet be enabled in -# production use. #CONFIG_PASN=y # Device Provisioning Protocol (DPP) (also known as Wi-Fi Easy Connect) CONFIG_DPP=y # DPP version 2 support CONFIG_DPP2=y -# DPP version 3 support (experimental and still changing; do not enable for -# production use) +# DPP version 3 support #CONFIG_DPP3=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Enhanced Privacy Protection (IEEE P802.11bi) +# Note: These features are experimental and still changing; do not enable for +# production use. +# Encrypted association frame support (needed for EPPKE and EAP in +# Authentication frames) +#CONFIG_ENC_ASSOC=y +# +# PMKSA caching privacy support +#CONFIG_PMKSA_PRIVACY=y +# +# IEEE 802.1X (EAP) authentication in Authentication frames +# IEEE P802.11bi/D4.0, 12.16.5 (IEEE 802.1X authentication utilizing +# Authentication frames) +#CONFIG_IEEE8021X_AUTH=y + +# Automated Frequency Coordination for 6 GHz outdoor +#CONFIG_AFC=y diff --git a/contrib/wpa/hostapd/eap_register.c b/contrib/wpa/hostapd/eap_register.c index 3e870c7f0670..8bb25f2345a3 100644 --- a/contrib/wpa/hostapd/eap_register.c +++ b/contrib/wpa/hostapd/eap_register.c @@ -44,13 +44,6 @@ int eap_server_register_methods(void) ret = eap_server_unauth_tls_register(); #endif /* EAP_SERVER_TLS */ -#ifdef EAP_SERVER_TLS -#ifdef CONFIG_HS20 - if (ret == 0) - ret = eap_server_wfa_unauth_tls_register(); -#endif /* CONFIG_HS20 */ -#endif /* EAP_SERVER_TLS */ - #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c index 5caa779dd648..291acf9ffc14 100644 --- a/contrib/wpa/hostapd/hlr_auc_gw.c +++ b/contrib/wpa/hostapd/hlr_auc_gw.c @@ -1,6 +1,6 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen + * Copyright (c) 2005-2007, 2012-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -66,6 +66,7 @@ static int update_milenage = 0; static int sqn_changes = 0; static int ind_len = 5; static int stdout_debug = 1; +static bool stop = false; /* GSM triplets */ struct gsm_triplet { @@ -877,6 +878,12 @@ static int process_cmd(char *cmd, char *resp, size_t resp_len) if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0) return aka_auts(cmd + 9, resp, resp_len); + if (strncmp(cmd, "TERMINATE", 9) == 0) { + stop = true; + resp[0] = '\0'; + return 0; + } + printf("Unknown request: %s\n", cmd); return -1; } @@ -931,10 +938,13 @@ static void cleanup(void) struct gsm_triplet *g, *gprev; struct milenage_parameters *m, *prev; - if (update_milenage && milenage_file && sqn_changes) + if (update_milenage && milenage_file && sqn_changes) { + sqn_changes = 0; update_milenage_file(milenage_file); + } g = gsm_db; + gsm_db = NULL; while (g) { gprev = g; g = g->next; @@ -942,16 +952,21 @@ static void cleanup(void) } m = milenage_db; + milenage_db = NULL; while (m) { prev = m; m = m->next; os_free(prev); } - if (serv_sock >= 0) + if (serv_sock >= 0) { close(serv_sock); - if (socket_path) + serv_sock = -1; + } + if (socket_path) { unlink(socket_path); + socket_path = NULL; + } #ifdef CONFIG_SQLITE if (sqlite_db) { @@ -965,6 +980,7 @@ static void cleanup(void) static void handle_term(int sig) { printf("Signal %d - terminate\n", sig); + cleanup(); exit(0); } @@ -973,7 +989,7 @@ static void usage(void) { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2017, Jouni Malinen \n" + "Copyright (c) 2005-2024, Jouni Malinen \n" "\n" "usage:\n" "hlr_auc_gw [-hu] [-s] [-g] " @@ -1081,8 +1097,9 @@ int main(int argc, char *argv[]) signal(SIGTERM, handle_term); signal(SIGINT, handle_term); - for (;;) + while (!stop) process(serv_sock); + cleanup(); } else { char buf[1000]; socket_path = NULL; diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf index d875d5fc63b0..b81ce11b846c 100644 --- a/contrib/wpa/hostapd/hostapd.conf +++ b/contrib/wpa/hostapd/hostapd.conf @@ -20,11 +20,11 @@ interface=wlan0 # interface is also created. #bridge=br0 -# Driver interface type (hostap/wired/none/nl80211/bsd); -# default: hostap). nl80211 is used with all Linux mac80211 drivers. +# Driver interface type (wired/none/nl80211/bsd); +# default: nl80211). nl80211 is used with all Linux mac80211 drivers. # Use driver=none if building hostapd as a standalone RADIUS server that does # not control any wireless/wired driver. -# driver=hostap +# driver=nl80211 # Driver interface parameters (mainly for development testing use) # driver_params= @@ -313,7 +313,15 @@ fragm_threshold=-1 # Station MAC address -based authentication # Please note that this kind of access control requires a driver that uses # hostapd to take care of management frame processing and as such, this can be -# used with driver=hostap or driver=nl80211, but not with driver=atheros. +# used with driver=nl80211, but not with driver=bsd. +# +# Especially in cases where not all STAs with authentication credentials are +# fully trusted, unexpected MAC addresses should be marked to be rejected, e.g., +# by including them in the deny list (deny_mac_file). This should cover MAC +# addresses that are known to be used in the LAN, e.g., the MAC address of the +# default gateway and any server like RADIUS server or proxy. BSSIDs and MLD +# MAC addresses of other APs in the same network should also be disallowed. +# # 0 = accept unless in deny list # 1 = deny unless in accept list # 2 = use external RADIUS server (accept/deny lists are searched first) @@ -1030,6 +1038,62 @@ wmm_ac_vo_acm=0 # Valid range: 0..20 TUs; default is 0 (disabled) #unsol_bcast_probe_resp_interval=0 +#channel_usage: Whether Channel Usage procedures is supported by AP. +# 0 = Channel Usage support is disabled (default) +# 1 = Channel Usage support is enabled +#channel_usage=0 + +#peer_to_peer_twt: Indicates an HE AP supports negotiating a peer-to-peer +# TWT schedule that is requested by a non-AP STA to establish a +# channel-usage-aidable BSS or an off-channel TDLS direct link. +# 0 = Does not support Peer-to-peer TWT (default) +# 1 = Supports Peer-to-peer TWT +#peer_to_peer_twt=0 + +##### Automated Frequency Coordination for 6 GHz UNII-5 and UNII-7 bands ###### + +# AFC daemon connection socket +#afcd_sock=/var/run/afcd.sock + +# AFC request identification parameters +#afc_request_version=1.1 +#afc_serial_number=abcdefg +# Comma separated list of certification IDs in : format +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000 + +# AFC location type: +# 0 = ellipse +# 1 = linear polygon +# 2 = radial polygon +#afc_location_type=0 + +# AFC ellipse or linear polygon coordinations +#afc_linear_polygon=-122.984157:37.425056 + +# AFC radial polygon coordinations +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33 + +# AFC ellipse major/minor axis and orientation +#afc_major_axis=100 +#afc_minor_axis=50 +#afc_orientation=70 + +# AFC device elevation parameters +#afc_height=3.0 +#afc_height_type=AGL +#afc_vertical_tolerance=7 + +# AFC minimum desired TX power (dBm) +#afc_min_power=24 + +# AFC request frequency ranges +# Frequency list can be provided as range using hyphen ('-') or individual +# frequencies can be specified by comma (',') separated values +#afc_freq_range=5925-6425,6525-6875 + +# AFC request operation classes +#afc_op_class=131,132,133,134,136,137 + ##### IEEE 802.11be related configuration ##################################### #ieee80211be: Whether IEEE 802.11be (EHT) is enabled @@ -1037,6 +1101,14 @@ wmm_ac_vo_acm=0 # 1 = enabled #ieee80211be=1 +# Require stations to support EHT PHY (reject association if they do not). +# This is a radio-level parameter and applies to all BSSs in the radio. +#require_eht=1 + +# Require stations to support EHT PHY for a specific BSS only. +# This is a per-BSS parameter. +#bss_require_eht=1 + #disable_11be: Boolean (0/1) to disable EHT for a specific BSS #disable_11be=0 @@ -1102,6 +1174,13 @@ wmm_ac_vo_acm=0 # will be used as the AP MLD MAC address. #mld_addr=02:03:04:05:06:07 +# Disable EHT-MCS 15 RX +# If set to 1, the MCS15 Disable subfield in the EHT Operation element is set +# to 1, disabling EHT-MCS 15. If set to 0, the subfield is set to 0, enabling +# EHT-MCS 15. The default value is 1, indicating EHT-MCS 15 reception capability +# is disabled. +#disable_mcs15_rx=1 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -1207,6 +1286,10 @@ eapol_key_index_workaround=0 # mka_priority (Priority of MKA Actor) # Range: 0..255 (default: 255) # +# macsec_icv_indicator: Always include ICV indicator +# 0 = ICV Indicator is not included when ICV has default length (default) +# 1 = ICV Indicator is always included (compatibility mode) +# # macsec_csindex: IEEE 802.1X/MACsec cipher suite # 0 = GCM-AES-128 (default) # 1 = GCM-AES-256 (default) @@ -1786,6 +1869,9 @@ own_ip_addr=127.0.0.1 # accounting while still enabling RADIUS authentication. #radius_server_acct_port=1813 +# Log received RADIUS accounting data +#radius_server_acct_log=1 + # Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) #radius_server_ipv6=1 @@ -1869,8 +1955,8 @@ own_ip_addr=127.0.0.1 # FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384 # OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open) # DPP = Device Provisioning Protocol -# OSEN = Hotspot 2.0 online signup with encryption # (dot11RSNAConfigAuthenticationSuitesTable) +# EPPKE = Enhanced Privacy Protection Key Exchange #wpa_key_mgmt=WPA-PSK WPA-EAP # Set of accepted cipher suites (encryption algorithms) for pairwise keys @@ -2021,6 +2107,12 @@ own_ip_addr=127.0.0.1 # 1 = enabled #beacon_prot=0 +# SPP (Signaling and Payload Protected) A-MSDU. +# This depends on driver support and CCMP/GCMP cipher suite being used. +# 0 = disabled (default) +# 1 = enabled if driver indicates support for this +#spp_amsdu=1 + # Association SA Query maximum timeout (in TU = 1.024 ms; for MFP) # (maximum time to wait for a SA Query response) # dot11AssociationSAQueryMaximumTimeout, 1...4294967295 @@ -2110,6 +2202,29 @@ own_ip_addr=127.0.0.1 # contains and entry in the same format as sae_password uses. #sae_password_file=/tc/hostapd.sae_passwords +# Whether to use wpa_passphrase as an SAE password +# 0 = enabled if no matching sae_password is set (default) +# 1 = disabled in all cases +#sae_password_psk=0 + +# Tracking of SAE password use +# While SAE design does not allow the AP to determine the used password robustly +# if multiple password are configured without use of password identifiers, a +# small number of such passwords might be usable with minimal impact to STAs. +# This parameter can be used to enable such mechanism by tracking which password +# STAs have tried and either succeeded or failed to complete authentication +# with. Configured passwords are then tried one by one until success. This shows +# up as a potential attack to the STA, though, and as such, may result in the AP +# getting rejected after a couple of attempts. Only one password can be tested +# per attempt, so this limits this mechanism to only a small number (e.g., 2-3) +# passwords without showing significant usability issues with some STAs. This +# is meant as a workaround until SAE with password identifiers is deployed on +# STAs. +# This parameter sets the maximum number of STA MAC addresses to track per +# SAE password. This should be set sufficiently high to cover the expected +# number of active STAs. +#sae_track_password=0 + # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold) # This parameter defines how many open SAE instances can be in progress at the # same time before the anti-clogging mechanism is taken into use. @@ -2159,6 +2274,32 @@ own_ip_addr=127.0.0.1 # regardless of the sae_pwe parameter value. #sae_pwe=0 +# Accept SAE association when the STA advertises SAE H2E support in the RSNXE +# but authenticated using the hunting-and-pecking loop (i.e., did not use H2E). +# By default (0), hostapd rejects such an association as a potential downgrade +# when sae_pwe=2. This option is a workaround for WPA3-Personal Compatibility +# Mode and RSNXE-override interoperability with certain stations (e.g., some MLO +# STAs that add only the RSNXE Override element and do not use H2E on 2.4/5 +# GHz). +# +# Enabling this weakens SAE downgrade protection and should only be used when +# RSN overriding is enabled in the hostapd configuration and such +# interoperability is required. +#sae_accept_h2e_without_use=0 + +# Changing SAE password identifiers +# AP can send a set of unique SAE password identifiers to allow the long term +# password identifier to be changed for over the air exchanges to avoid tracking +# of devices using password identifiers. +# +# Key for generating and interpreting encrypted password identifiers. This must +# be same for each AP in the network. 32 octets as a hexdump +#sae_pw_id_key=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +# +# How many over the air password identifier values to provide to a STA each time +# SAE is used. +#sae_pw_id_num=2 + # FILS Cache Identifier (16-bit value in hexdump format) #fils_cache_id=0011 @@ -2279,6 +2420,17 @@ own_ip_addr=127.0.0.1 # (default: 1 = activated) #pasn_noauth=1 +# Unauthenticated EPPKE activated +# This indicates whether EPPKE without a base AKM (i.e., using +# WPA_KEY_MGMT_EPPKE as the selected AKMP in the RSNE without mutual +# authentication) is allowed. +# +# When enabled, the AP advertises support for unauthenticated EPPKE via the +# Unauthenticated EPPKE bit in the RSNXE (WLAN_RSNX_CAPAB_UNAUTH_EPPKE). +# See IEEE P802.11bi/D4.0, 12.16.9.1 for details. +# (default: 0 = disabled) +#eppke_unauth=0 + # SSID protection in 4-way handshake # The IEEE 802.11i-2004 RSN design did not provide means for protecting the # SSID in the general case. IEEE P802.11REVme/D6.0 added support for this in @@ -2298,6 +2450,129 @@ own_ip_addr=127.0.0.1 # #ssid_protection=0 +# Known STA Identification +# IEEE Std 802.11-2024 adds a mechanism that allows the SA Query procedure on +# (re)association to the previously used AP to be skipped when that AP still +# has a valid security association. This can speed up cases where a STA needs to +# reassociate back to the same AP to update some association parameters. +# +# 0 = Do not process Known STA Identification (default) +# 1 = Allow Known STA Identification to be used to skip SA Query procedure +#known_sta_identification=0 + +# RSNE/RSNXE override +# +# These parameters can be used to configure RSN parameters for STAs that support +# the override elements. The RSN parameters for STAs that do not support these +# mechanisms are configured in the referenced configuration parameters. The AP +# allows STAs to use either of the configured sets for negotiating RSN +# parameters. +# +# The main purpose of this mechanism is to make the AP look like it is using an +# older security mechanism (e.g., WPA2-Personal) to older STAs while allowing +# new stations use newer security mechanisms (e.g., WPA3-Personal) based on the +# override values. This might be needed to work around issues with deployed +# STAs that do not implement RSNE extensibility correctly and may fail to +# connect when the AP is using a transition mode like WPA3-Personal transition +# mode. +# +# Key management; see wpa_key_mgmt for RSNE configuration +#rsn_override_key_mgmt= +# +# Pairwise cipher suites; see rsn_pairwise for RSNE configuration +#rsn_override_pairwise= +# +# Second set of similar parameters. These are required to be used for +# Wi-Fi 7 (EHT/MLO) associations with RSN overriding and can optionally be used +# in cases that do not use Wi-Fi 7. +#rsn_override_key_mgmt_2 +#rsn_override_pairwise_2 +#rsn_override_mfp_2 +# +# The RSNXE is normally included if any of the extended RSN capabilities is +# enabled/supported. When using RSN overriding, a separate RSNXOE is included +# and it may be more interoperable to omit the RSNXE completely. This +# configuration parameter can be used to do that. +# 0 = Include the RSNXE if any extended RSN capability is enabled/supported +# (default). +# 1 = Do not include the RSNXE. +#rsn_override_omit_rsnxe=0 + +# Allow MLO partner link association from a non-RSNO-capable STA that +# negotiates an AKM from the override set (e.g., SAE on the 6 GHz primary +# link) but whose partner link per-STA profile RSNE would not match the base +# wpa_key_mgmt of that link (e.g., WPA-PSK on 2.4 GHz in WPA3-Personal +# Compatibility Mode). When enabled, the AP MLD falls back to +# rsn_override_key_mgmt for the partner link key_mgmt validation to allow the +# stronger AKM to be used for ML associations even if when RSNE does not +# advertise support for this on all links. +# 0 = Disabled (default) +# 1 = Enabled +#rsn_override_mlo_compat=0 +# +# Example configuration for WPA2-Personal/PMF-optional in RSNE and +# WPA3-Personal/PMF-required/MLO in override elements +#wpa_key_mgmt=WPA-PSK +#rsn_pairwise=CCMP +#ieee80211w=1 +#rsn_override_key_mgmt=SAE +#rsn_override_pairwise=GCMP-256 +#rsn_override_mfp=2 +#rsn_override_key_mgmt_2=SAE-EXT-KEY +#rsn_override_pairwise_2=GCMP-256 +#rsn_override_mfp_2=2 +#beacon_prot=1 +#sae_groups=19 20 +#sae_require_mfp=1 +#sae_pwe=2 + +##### IEEE 802.11az configuration ############################################# +# +# urnm_mfpr: +# urnm_mfpr mandates use of management frame protection and establishment of +# PTKSA across all supported bandwidths, regardless of the urnm_mfpr_x20 +# setting for unassociated negotiation and measurement operations. +# +# urnm_mfpr_x20: +# urnm_mfpr_x20 allows unassociated negotiation and measurement +# operations using 20 MHz bandwidth without requiring management frame +# protection and a PTKSA while mandating the security for higher bandwidth +# cases. This is only valid if urnm_mfpr is false. +# +# Behavior Summary: +# - urnm_mfpr = 1: +# - PTKSA is required for all supported bandwidths. +# - urnm_mfpr_x20 is ignored. +# +# - urnm_mfpr = 0 and urnm_mfpr_x20 = 1: +# - PTKSA is required only for bandwidths greater than 20 MHz. +# +# - urnm_mfpr = 0 and urnm_mfpr_x20 = 0: +# - PTKSA is not required for any bandwidths. +#urnm_mfpr=0 +#urnm_mfpr_x20=0 + +#i2r_lmr_policy: Policy for I2R LMR feedback +# This indicates whether initiating stations are required to support the +# capability to generate and transmit Initiator-to-Responder Location +# Measurement Reports (I2R LMRs). +# This parameter matches the dot11I2RLMRFeedbackPolicy MIB variable. +# +# When set to 1: +# Do not require initiating stations to support I2R LMR generation and +# transmission. +# +# When set to 0: +# Request initiating stations to negotiate and support the transmission of +# I2R LMRs during location measurement operations. +#i2r_lmr_policy=1 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) @@ -2402,6 +2677,34 @@ own_ip_addr=127.0.0.1 # 1 = enabled #ft_psk_generate_local=0 +##### IEEE 802.11bi related configuration ##################################### + +# IEEE Std 802.11bi introduces following Enhanced Privacy Protection (EPP) +# features. +# +# (Re)Association Request/Response frame encryption: Indicates if the AP +# supports encryption of (Re)Association Request and Response frames +# 0 = disabled (default) +# 1 = enabled +#assoc_frame_encryption=0 + +# PMKSA caching privacy: Indicates if the AP supports recomputation of the PMKID +# after the indicated PMKID in the RSNE identifies a cached PMKSA and a PTKSA +# was established using the identified PMKSA. The recomputed PMKID will be +# delivered to the non-AP STA in the Key Delivery element added in the encrypted +# (Re)Association Response frame. +# 0 = disabled (default) +# 1 = enabled +#pmksa_caching_privacy=0 + +# IEEE 802.1X (EAP) Authentication utilizing Authentication frames: Indicates +# if the AP supports encapsulation of EAP PDUs within Authentication frames +# in addition to the older design of EAPOL frames (Data frames after +# association). +# 0 = disabled (default) +# 1 = enabled +#eap_using_authentication_frames=0 + ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neighbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be @@ -2573,7 +2876,7 @@ own_ip_addr=127.0.0.1 # message when acting as a Registrar. If skip_cred_build=1, this data will also # be able to override the Credential attribute that would have otherwise been # automatically generated based on network configuration. This configuration -# option points to an external file that much contain the WPS Credential +# option points to an external file that contains the WPS Credential # attribute(s) as binary data. #extra_cred=hostapd.cred @@ -2970,9 +3273,6 @@ own_ip_addr=127.0.0.1 # forging such frames to other stations in the BSS. #disable_dgaf=1 -# OSU Server-Only Authenticated L2 Encryption Network -#osen=1 - # ANQP Domain ID (0..65535) # An identifier for a set of APs in an ESS that share the same common ANQP # information. 0 = Some of the ANQP information is unique to this AP (default). @@ -3053,42 +3353,6 @@ own_ip_addr=127.0.0.1 # @1@ = MAC address of the STA (colon separated hex octets) #hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 -# OSU and Operator icons -# ::::: -#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png -#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png - -# OSU SSID (see ssid2 for format description) -# This is the SSID used for all OSU connections to all the listed OSU Providers. -#osu_ssid="example" - -# OSU Providers -# One or more sets of following parameter. Each OSU provider is started by the -# mandatory osu_server_uri item. The other parameters add information for the -# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN -# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI -# value for OSEN authentication when using a shared BSS (Single SSID) for OSU. -# -#osu_server_uri=https://example.com/osu/ -#osu_friendly_name=eng:Example operator -#osu_friendly_name=fin:Esimerkkipalveluntarjoaja -#osu_nai=anonymous@example.com -#osu_nai2=anonymous@example.com -#osu_method_list=1 0 -#osu_icon=icon32 -#osu_icon=icon64 -#osu_service_desc=eng:Example services -#osu_service_desc=fin:Esimerkkipalveluja -# -#osu_server_uri=... - -# Operator Icons -# Operator icons are specified using references to the hs20_icon entries -# (Name subfield). This information, if present, is advertsised in the -# Operator Icon Metadata ANQO-element. -#operator_icon=icon32 -#operator_icon=icon64 - ##### Multiband Operation (MBO) ############################################### # # MBO enabled @@ -3267,6 +3531,10 @@ own_ip_addr=127.0.0.1 # (channel switch operating class is needed) #ecsa_ie_only=0 # +# Include only CSA IE without ECSA IE +# (the operating class is not mentioned) +#csa_ie_only=0 + # Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last # attempt (wpa_pairwise_update_count). This will trigger a timeout on all # previous attempts and thus delays the frame. (testing only) @@ -3350,6 +3618,18 @@ own_ip_addr=127.0.0.1 # 2 = Enhanced multiple BSSID advertisement enabled. #mbssid=0 # +# Maximum number of BSSs that can be added into a Multiple BSSID set +# This is a radio level parameter. If not set (or 0), the maximum is determined +# automatically based on the configured BSSs which may limit dynamic addition +# of new BSSs. +#mbssid_max=0 +# +# Multiple BSSID Index override +# This is a BSS level parameter. If not set (or 0), the BSSID index is +# determined automatically based on the configured BSSs which may limit dynamic +# addition of new BSSs. +#mbssid_index=0 +# # The transmitting interface should be added with the 'interface' option while # the non-transmitting interfaces should be added using the 'bss' option. # Security configuration should be added separately per interface, if required. diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c index eb8a38350bd1..94bc36d6a631 100644 --- a/contrib/wpa/hostapd/hostapd_cli.c +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -21,7 +21,7 @@ static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2024, Jouni Malinen and contributors"; +"Copyright (c) 2004-2026, Jouni Malinen and contributors"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; @@ -54,7 +54,11 @@ static void usage(void) fprintf(stderr, "%s\n", hostapd_cli_version); fprintf(stderr, "\n" - "usage: hostapd_cli [-p] [-i] [-hvBr] " + "usage: hostapd_cli [-p] [-i] " +#ifdef CONFIG_IEEE80211BE + "[-l] " +#endif /* CONFIG_IEEE80211BE */ + "[-hvBr] " "[-a] \\\n" " [-P] [-G] [command..]\n" "\n" @@ -74,7 +78,11 @@ static void usage(void) " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" - " socket path)\n\n"); + " socket path)\n" +#ifdef CONFIG_IEEE80211BE + " -l Link ID of the interface in case of Multi-Link Operation\n" +#endif /* CONFIG_IEEE80211BE */ + "\n"); print_help(stderr, NULL); } @@ -192,7 +200,7 @@ static void hostapd_cli_msg_cb(char *msg, size_t len) static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) { - char buf[4096]; + char buf[8192]; size_t len; int ret; @@ -213,6 +221,8 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) if (print) { buf[len] = '\0'; printf("%s", buf); + if (interactive && len > 0 && buf[len - 1] != '\n') + printf("\n"); } return 0; } @@ -1223,6 +1233,13 @@ static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl, } +static int hostapd_cli_cmd_set_bw(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "SET_BW", 0, argc, argv); +} + + static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1265,6 +1282,29 @@ static int hostapd_cli_cmd_enable_mld(struct wpa_ctrl *ctrl, int argc, } +#ifdef CONFIG_TESTING_OPTIONS + +static int hostapd_cli_cmd_remove_link(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + unsigned int count = (argc && atoi(argv[0]) > 0) ? atoi(argv[0]) : 1; + char buf[64]; + + snprintf(buf, sizeof(buf), "LINK_REMOVE %u", count); + + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_link_enable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "LINK_ENABLE", 1, argc, argv); +} + +#endif /* CONFIG_TESTING_OPTIONS */ + + static int hostapd_cli_cmd_disable_mld(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1654,6 +1694,31 @@ static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) #endif /* ANDROID */ +#ifdef CONFIG_AFC + +static int hostapd_cli_cmd_afc_get_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "AFC_GET_REQUEST", 0, 0, NULL); +} + + +static int hostapd_cli_cmd_afc_get_response(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "AFC_GET_RESPONSE", 0, 0, NULL); +} + + +static int hostapd_cli_cmd_afc_send_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "AFC_SEND_REQUEST", 0, 0, NULL); +} + +#endif /* CONFIG_AFC */ + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1762,6 +1827,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { #endif /* CONFIG_IEEE80211AX */ { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL, " = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" }, + { "set_bw", hostapd_cli_cmd_set_bw, NULL, + "[sec_channel_offset=] [center_freq1=]\n" + " [center_freq2=] [bandwidth=] [ht|vht]\n" + " = change channel bandwidth" }, { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, " \n" " = send WNM-Notification Subscription Remediation Request" }, @@ -1783,6 +1852,13 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { "= disable hostapd on current interface" }, { "enable_mld", hostapd_cli_cmd_enable_mld, NULL, "= enable AP MLD to which the interface is affiliated" }, +#ifdef CONFIG_TESTING_OPTIONS + { "remove_link", hostapd_cli_cmd_remove_link, NULL, + " = remove MLO link and send Reconfiguration MLE" }, + { "link_enable", hostapd_cli_cmd_link_enable, NULL, + " bss_config=:\n" + " = Enable a link previously removed from the MLD AP" }, +#endif /* CONFIG_TESTING_OPTIONS */ { "disable_mld", hostapd_cli_cmd_disable_mld, NULL, "= disable AP MLD to which the interface is affiliated" }, { "update_beacon", hostapd_cli_cmd_update_beacon, NULL, @@ -1882,6 +1958,14 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "driver", hostapd_cli_cmd_driver, NULL, " [] = send driver command data" }, #endif /* ANDROID */ +#ifdef CONFIG_AFC + { "afc_get_request", hostapd_cli_cmd_afc_get_request, NULL, + " = Show latest AFC request data"}, + { "afc_get_response", hostapd_cli_cmd_afc_get_response, NULL, + " = Show latest AFC response data"}, + { "afc_send_request", hostapd_cli_cmd_afc_send_request, NULL, + " = Send another AFC request and upadte the timeout"}, +#endif /* CONFIG_AFC */ { NULL, NULL, NULL, NULL } }; @@ -2174,9 +2258,13 @@ static void hostapd_cli_action_ping(void *eloop_ctx, void *timeout_ctx) if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, hostapd_cli_action_cb) < 0 || len < 4 || os_memcmp(buf, "PONG", 4) != 0) { - printf("hostapd did not reply to PING command - exiting\n"); - eloop_terminate(); - return; + printf("hostapd did not reply to PING command - open a new connection\n"); + hostapd_cli_close_connection(); + if (hostapd_cli_reconnect(ctrl_ifname)) { + printf("Failed to establish new connection - exit\n"); + eloop_terminate(); + return; + } } eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping, ctrl, NULL); @@ -2212,12 +2300,15 @@ int main(int argc, char *argv[]) int c; int daemonize = 0; int reconnect = 0; +#ifdef CONFIG_IEEE80211BE + int link_id = -1; +#endif /* CONFIG_IEEE80211BE */ if (os_program_init()) return -1; for (;;) { - c = getopt(argc, argv, "a:BhG:i:p:P:rs:v"); + c = getopt(argc, argv, "a:BhG:i:l:p:P:rs:v"); if (c < 0) break; switch (c) { @@ -2252,6 +2343,11 @@ int main(int argc, char *argv[]) case 's': client_socket_dir = optarg; break; +#ifdef CONFIG_IEEE80211BE + case 'l': + link_id = atoi(optarg); + break; +#endif /* CONFIG_IEEE80211BE */ default: usage(); return -1; @@ -2285,6 +2381,24 @@ int main(int argc, char *argv[]) closedir(dir); } } + +#ifdef CONFIG_IEEE80211BE + if (link_id >= 0 && ctrl_ifname) { + int ret; + char buf[300]; + + ret = os_snprintf(buf, sizeof(buf), "%s_%s%d", + ctrl_ifname, WPA_CTRL_IFACE_LINK_NAME, + link_id); + if (os_snprintf_error(sizeof(buf), ret)) + return -1; + + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(buf); + link_id = -1; + } +#endif /* CONFIG_IEEE80211BE */ + hostapd_cli_reconnect(ctrl_ifname); if (ctrl_conn) { if (warning_displayed) diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c index 00e02bb034c9..de78a046ca4f 100644 --- a/contrib/wpa/hostapd/main.c +++ b/contrib/wpa/hostapd/main.c @@ -20,6 +20,7 @@ #include "crypto/tls.h" #include "common/version.h" #include "common/dpp.h" +#include "common/proc_coord.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "eap_server/tncs.h" @@ -160,6 +161,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) struct wpa_driver_capa capa; #ifdef CONFIG_IEEE80211BE struct hostapd_data *h_hapd = NULL; + void *shared_hapd = NULL; #endif /* CONFIG_IEEE80211BE */ if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { @@ -168,8 +170,16 @@ static int hostapd_driver_init(struct hostapd_iface *iface) } #ifdef CONFIG_IEEE80211BE - if (conf->mld_ap) + if (conf->mld_ap) { + hostapd_bss_setup_multi_link(hapd, iface->interfaces); + if (!hapd->mld) { + wpa_printf(MSG_ERROR, + "MLD: Failed to initialize context for %s", + conf->iface); + return -1; + } h_hapd = hostapd_mld_get_first_bss(hapd); + } if (h_hapd) { hapd->drv_priv = h_hapd->drv_priv; @@ -191,7 +201,6 @@ static int hostapd_driver_init(struct hostapd_iface *iface) os_memcpy(hapd->own_addr, b, ETH_ALEN); } - hostapd_mld_add_link(hapd); wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s", hapd->mld_link_id, hapd->conf->iface); @@ -254,6 +263,42 @@ static int hostapd_driver_init(struct hostapd_iface *iface) params.own_addr = hapd->own_addr; +#ifdef CONFIG_IEEE80211BE + if (hapd->driver->can_share_drv && + hapd->driver->can_share_drv(hapd, ¶ms, &shared_hapd)) { + char force_ifname[IFNAMSIZ]; + const u8 *addr = params.bssid; + u8 if_addr[ETH_ALEN]; + + if (!shared_hapd) { + wpa_printf(MSG_ERROR, "Failed to get the shared drv"); + os_free(params.bridge); + return -1; + } + + /* Share an already initialized driver interface instance + * using an AP mode BSS in it instead of adding a new driver + * interface instance for the same driver. */ + if (hostapd_if_add(shared_hapd, WPA_IF_AP_BSS, + params.ifname, addr, hapd, + &hapd->drv_priv, force_ifname, if_addr, + params.num_bridge && params.bridge[0] ? + params.bridge[0] : NULL, + 1)) { + wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" + MACSTR ")", MAC2STR(hapd->own_addr)); + os_free(params.bridge); + return -1; + } + os_free(params.bridge); + + hapd->interface_added = 1; + os_memcpy(params.own_addr, addr ? addr : if_addr, ETH_ALEN); + + goto pre_setup_mld; + } +#endif /* CONFIG_IEEE80211BE */ + hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); os_free(params.bridge); if (hapd->drv_priv == NULL) { @@ -264,6 +309,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) } #ifdef CONFIG_IEEE80211BE +pre_setup_mld: /* * This is the first interface added to the AP MLD, so have the * interface hardware address be the MLD address, while the link address @@ -278,7 +324,6 @@ static int hostapd_driver_init(struct hostapd_iface *iface) else os_memcpy(hapd->own_addr, b, ETH_ALEN); - hostapd_mld_add_link(hapd); wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s", hapd->mld_link_id, hapd->conf->iface); } @@ -338,8 +383,14 @@ setup_mld: hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr), MAC2STR(hapd->own_addr)); - hostapd_drv_link_add(hapd, hapd->mld_link_id, - hapd->own_addr); + if (hostapd_drv_link_add(hapd, hapd->mld_link_id, + hapd->own_addr)) { + wpa_printf(MSG_ERROR, + "MLD: Failed to add link %d in MLD %s", + hapd->mld_link_id, hapd->conf->iface); + return -1; + } + hostapd_mld_add_link(hapd); } #endif /* CONFIG_IEEE80211BE */ @@ -555,7 +606,7 @@ static void show_version(void) "hostapd v%s\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2024, Jouni Malinen " + "Copyright (c) 2002-2026, Jouni Malinen " "and contributors\n", VERSION_STR); } @@ -570,6 +621,9 @@ static void usage(void) "\\\n" " [-g ] [-G ]\\\n" " [-i ]\\\n" +#ifdef CONFIG_PROCESS_COORDINATION + " [-z] \\\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " \n" "\n" "options:\n" @@ -595,6 +649,9 @@ static void usage(void) " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n" +#ifdef CONFIG_PROCESS_COORDINATION + " -z process coordination directory\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " -q show less debug messages (-qq for even less)\n"); exit(1); @@ -748,6 +805,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces) if (!interfaces->mld[i]) continue; + interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]); os_free(interfaces->mld[i]); interfaces->mld[i] = NULL; } @@ -779,6 +837,9 @@ int main(int argc, char *argv[]) #ifdef CONFIG_DPP struct dpp_global_config dpp_conf; #endif /* CONFIG_DPP */ +#ifdef CONFIG_PROCESS_COORDINATION + const char *proc_coord_dir = NULL; +#endif /* CONFIG_PROCESS_COORDINATION */ if (os_program_init()) return -1; @@ -793,6 +854,10 @@ int main(int argc, char *argv[]) interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; +#ifdef CONFIG_IEEE80211BE + interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init; + interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit; +#endif /* CONFIG_IEEE80211BE */ dl_list_init(&interfaces.global_ctrl_dst); #ifdef CONFIG_ETH_P_OUI dl_list_init(&interfaces.eth_p_oui); @@ -809,7 +874,7 @@ int main(int argc, char *argv[]) #endif /* CONFIG_DPP */ for (;;) { - c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:qz:"); if (c < 0) break; switch (c) { @@ -885,6 +950,11 @@ int main(int argc, char *argv[]) case 'q': wpa_debug_level++; break; +#ifdef CONFIG_PROCESS_COORDINATION + case 'z': + proc_coord_dir = optarg; + break; +#endif /* CONFIG_PROCESS_COORDINATION */ default: usage(); break; @@ -933,6 +1003,14 @@ int main(int argc, char *argv[]) eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, hostapd_periodic, &interfaces, NULL); +#ifdef CONFIG_PROCESS_COORDINATION + if (proc_coord_dir) { + interfaces.pc = proc_coord_init(proc_coord_dir); + if (!interfaces.pc) + goto out; + } +#endif /* CONFIG_PROCESS_COORDINATION */ + if (fst_global_init()) { wpa_printf(MSG_ERROR, "Failed to initialize global FST context"); @@ -1045,6 +1123,10 @@ int main(int argc, char *argv[]) dpp_global_deinit(interfaces.dpp); #endif /* CONFIG_DPP */ +#ifdef CONFIG_PROCESS_COORDINATION + proc_coord_deinit(interfaces.pc); +#endif /* CONFIG_PROCESS_COORDINATION */ + if (interfaces.eloop_initialized) eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); hostapd_global_deinit(pid_file, interfaces.eloop_initialized); diff --git a/contrib/wpa/hs20/client/Android.mk b/contrib/wpa/hs20/client/Android.mk index 8d208b25901b..1e1082be2e98 100644 --- a/contrib/wpa/hs20/client/Android.mk +++ b/contrib/wpa/hs20/client/Android.mk @@ -30,10 +30,7 @@ L_CFLAGS += -DCONFIG_CTRL_IFACE L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" -OBJS = spp_client.c -OBJS += oma_dm_client.c -OBJS += osu_client.c -OBJS += est.c +OBJS = osu_client.c OBJS += ../../src/common/wpa_ctrl.c OBJS += ../../src/common/wpa_helpers.c OBJS += ../../src/utils/xml-utils.c diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile index 4dcfe2d3bf2c..9e65dc63e8d5 100644 --- a/contrib/wpa/hs20/client/Makefile +++ b/contrib/wpa/hs20/client/Makefile @@ -24,10 +24,7 @@ LIBS += $(GTKLIBS) endif endif -OBJS=spp_client.o -OBJS += oma_dm_client.o -OBJS += osu_client.o -OBJS += est.o +OBJS = osu_client.o OBJS += ../../src/utils/xml-utils.o CFLAGS += -DCONFIG_CTRL_IFACE CFLAGS += -DCONFIG_CTRL_IFACE_UNIX diff --git a/contrib/wpa/hs20/client/devdetail.xml b/contrib/wpa/hs20/client/devdetail.xml deleted file mode 100644 index 6d0389e8a133..000000000000 --- a/contrib/wpa/hs20/client/devdetail.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - 13 - - - 21 - MS-CHAP-V2 - - - 18 - - - 23 - - - 50 - - - false - 020102030405 - 310026000000000 - imei:490123456789012 - http://localhost:12345/ - - - - - - - - - - 0 - 0 - 0 - - MobilePhone - Manufacturer - 1.0 - 1.0 - 1.0 - false - diff --git a/contrib/wpa/hs20/client/devinfo.xml b/contrib/wpa/hs20/client/devinfo.xml deleted file mode 100644 index d48a520a98a1..000000000000 --- a/contrib/wpa/hs20/client/devinfo.xml +++ /dev/null @@ -1,7 +0,0 @@ - - urn:Example:HS20-station:123456 - Manufacturer - HS20-station - 1.2 - en - diff --git a/contrib/wpa/hs20/client/est.c b/contrib/wpa/hs20/client/est.c deleted file mode 100644 index 425b72d18e6a..000000000000 --- a/contrib/wpa/hs20/client/est.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Hotspot 2.0 OSU client - EST client - * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "utils/base64.h" -#include "utils/xml-utils.h" -#include "utils/http-utils.h" -#include "osu_client.h" - - -static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7, - size_t len, char *pem_file, char *der_file) -{ -#ifdef OPENSSL_IS_BORINGSSL - CBS pkcs7_cbs; -#else /* OPENSSL_IS_BORINGSSL */ - PKCS7 *p7 = NULL; - const unsigned char *p = pkcs7; -#endif /* OPENSSL_IS_BORINGSSL */ - STACK_OF(X509) *certs; - int i, num, ret = -1; - BIO *out = NULL; - -#ifdef OPENSSL_IS_BORINGSSL - certs = sk_X509_new_null(); - if (!certs) - goto fail; - CBS_init(&pkcs7_cbs, pkcs7, len); - if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) { - wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", - ERR_error_string(ERR_get_error(), NULL)); - write_result(ctx, "Could not parse PKCS#7 object from EST"); - goto fail; - } -#else /* OPENSSL_IS_BORINGSSL */ - p7 = d2i_PKCS7(NULL, &p, len); - if (p7 == NULL) { - wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s", - ERR_error_string(ERR_get_error(), NULL)); - write_result(ctx, "Could not parse PKCS#7 object from EST"); - goto fail; - } - - switch (OBJ_obj2nid(p7->type)) { - case NID_pkcs7_signed: - certs = p7->d.sign->cert; - break; - case NID_pkcs7_signedAndEnveloped: - certs = p7->d.signed_and_enveloped->cert; - break; - default: - certs = NULL; - break; - } -#endif /* OPENSSL_IS_BORINGSSL */ - - if (!certs || ((num = sk_X509_num(certs)) == 0)) { - wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object"); - write_result(ctx, "No certificates found in PKCS#7 object"); - goto fail; - } - - if (der_file) { - FILE *f = fopen(der_file, "wb"); - if (f == NULL) - goto fail; - i2d_X509_fp(f, sk_X509_value(certs, 0)); - fclose(f); - } - - if (pem_file) { - out = BIO_new(BIO_s_file()); - if (out == NULL || - BIO_write_filename(out, pem_file) <= 0) - goto fail; - - for (i = 0; i < num; i++) { - X509 *cert = sk_X509_value(certs, i); - X509_print(out, cert); - PEM_write_bio_X509(out, cert); - BIO_puts(out, "\n"); - } - } - - ret = 0; - -fail: -#ifdef OPENSSL_IS_BORINGSSL - if (certs) - sk_X509_pop_free(certs, X509_free); -#else /* OPENSSL_IS_BORINGSSL */ - PKCS7_free(p7); -#endif /* OPENSSL_IS_BORINGSSL */ - if (out) - BIO_free_all(out); - - return ret; -} - - -int est_load_cacerts(struct hs20_osu_client *ctx, const char *url) -{ - char *buf, *resp; - size_t buflen; - unsigned char *pkcs7; - size_t pkcs7_len, resp_len; - int res; - - buflen = os_strlen(url) + 100; - buf = os_malloc(buflen); - if (buf == NULL) - return -1; - - os_snprintf(buf, buflen, "%s/cacerts", url); - wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf); - write_summary(ctx, "Download EST cacerts from %s", buf); - ctx->no_osu_cert_validation = 1; - http_ocsp_set(ctx->http, 1); - res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt", - ctx->ca_fname); - http_ocsp_set(ctx->http, - (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); - ctx->no_osu_cert_validation = 0; - if (res < 0) { - wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s", - buf); - write_result(ctx, "Failed to download EST cacerts from %s", - buf); - os_free(buf); - return -1; - } - os_free(buf); - - resp = os_readfile("Cert/est-cacerts.txt", &resp_len); - if (resp == NULL) { - wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt"); - write_result(ctx, "Could not read EST cacerts"); - return -1; - } - - pkcs7 = base64_decode(resp, resp_len, &pkcs7_len); - if (pkcs7 && pkcs7_len < resp_len / 2) { - wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary", - (unsigned int) pkcs7_len, (unsigned int) resp_len); - os_free(pkcs7); - pkcs7 = NULL; - } - if (pkcs7 == NULL) { - wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7"); - pkcs7 = os_malloc(resp_len); - if (pkcs7) { - os_memcpy(pkcs7, resp, resp_len); - pkcs7_len = resp_len; - } - } - os_free(resp); - - if (pkcs7 == NULL) { - wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts"); - write_result(ctx, "Could not fetch EST PKCS#7 cacerts"); - return -1; - } - - res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem", - NULL); - os_free(pkcs7); - if (res < 0) { - wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response"); - write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response"); - return -1; - } - unlink("Cert/est-cacerts.txt"); - - return 0; -} - - -/* - * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID - * - * AttrOrOID ::= CHOICE { - * oid OBJECT IDENTIFIER, - * attribute Attribute } - * - * Attribute ::= SEQUENCE { - * type OBJECT IDENTIFIER, - * values SET SIZE(1..MAX) OF OBJECT IDENTIFIER } - */ - -typedef struct { - ASN1_OBJECT *type; - STACK_OF(ASN1_OBJECT) *values; -} Attribute; - -typedef struct { - int type; - union { - ASN1_OBJECT *oid; - Attribute *attribute; - } d; -} AttrOrOID; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -DEFINE_STACK_OF(AttrOrOID) -#endif - -typedef struct { - int type; - STACK_OF(AttrOrOID) *attrs; -} CsrAttrs; - -ASN1_SEQUENCE(Attribute) = { - ASN1_SIMPLE(Attribute, type, ASN1_OBJECT), - ASN1_SET_OF(Attribute, values, ASN1_OBJECT) -} ASN1_SEQUENCE_END(Attribute); - -ASN1_CHOICE(AttrOrOID) = { - ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT), - ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute) -} ASN1_CHOICE_END(AttrOrOID); - -ASN1_CHOICE(CsrAttrs) = { - ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID) -} ASN1_CHOICE_END(CsrAttrs); - -IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs); - - -static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid, - STACK_OF(X509_EXTENSION) *exts) -{ - char txt[100]; - int res; - - if (!oid) - return; - - res = OBJ_obj2txt(txt, sizeof(txt), oid, 1); - if (res < 0 || res >= (int) sizeof(txt)) - return; - - if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) { - wpa_printf(MSG_INFO, "TODO: csrattr challengePassword"); - } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) { - wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption"); - } else { - wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt); - } -} - - -static void add_csrattrs_ext_req(struct hs20_osu_client *ctx, - STACK_OF(ASN1_OBJECT) *values, - STACK_OF(X509_EXTENSION) *exts) -{ - char txt[100]; - int i, num, res; - - num = sk_ASN1_OBJECT_num(values); - for (i = 0; i < num; i++) { - ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i); - - res = OBJ_obj2txt(txt, sizeof(txt), oid, 1); - if (res < 0 || res >= (int) sizeof(txt)) - continue; - - if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) { - wpa_printf(MSG_INFO, "TODO: extReq macAddress"); - } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) { - wpa_printf(MSG_INFO, "TODO: extReq imei"); - } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) { - wpa_printf(MSG_INFO, "TODO: extReq meid"); - } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) { - wpa_printf(MSG_INFO, "TODO: extReq DevId"); - } else { - wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s", - txt); - } - } -} - - -static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr, - STACK_OF(X509_EXTENSION) *exts) -{ - char txt[100], txt2[100]; - int i, num, res; - - if (!attr || !attr->type || !attr->values) - return; - - res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1); - if (res < 0 || res >= (int) sizeof(txt)) - return; - - if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) { - add_csrattrs_ext_req(ctx, attr->values, exts); - return; - } - - num = sk_ASN1_OBJECT_num(attr->values); - for (i = 0; i < num; i++) { - ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i); - - res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1); - if (res < 0 || res >= (int) sizeof(txt2)) - continue; - - wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s", - txt, txt2); - } -} - - -static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs, - STACK_OF(X509_EXTENSION) *exts) -{ - int i, num; - - if (!csrattrs || ! csrattrs->attrs) - return; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - num = sk_AttrOrOID_num(csrattrs->attrs); -#else - num = SKM_sk_num(AttrOrOID, csrattrs->attrs); -#endif - for (i = 0; i < num; i++) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i); -#else - AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); -#endif - switch (ao->type) { - case 0: - add_csrattrs_oid(ctx, ao->d.oid, exts); - break; - case 1: - add_csrattrs_attr(ctx, ao->d.attribute, exts); - break; - } - } -} - - -static int generate_csr(struct hs20_osu_client *ctx, char *key_pem, - char *csr_pem, char *est_req, char *old_cert, - CsrAttrs *csrattrs) -{ - EVP_PKEY_CTX *pctx = NULL; - EVP_PKEY *pkey = NULL; - X509_REQ *req = NULL; - int ret = -1; - unsigned int val; - X509_NAME *subj = NULL; - char name[100]; - STACK_OF(X509_EXTENSION) *exts = NULL; - X509_EXTENSION *ex; - BIO *out; - CONF *ctmp = NULL; - - wpa_printf(MSG_INFO, "Generate RSA private key"); - write_summary(ctx, "Generate RSA private key"); - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); - if (!pctx) - return -1; - - if (EVP_PKEY_keygen_init(pctx) <= 0) - goto fail; - - if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0) - goto fail; - - if (EVP_PKEY_keygen(pctx, &pkey) <= 0) - goto fail; - EVP_PKEY_CTX_free(pctx); - pctx = NULL; - - if (key_pem) { - FILE *f = fopen(key_pem, "wb"); - if (f == NULL) - goto fail; - if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) { - wpa_printf(MSG_INFO, "Could not write private key: %s", - ERR_error_string(ERR_get_error(), NULL)); - fclose(f); - goto fail; - } - fclose(f); - } - - wpa_printf(MSG_INFO, "Generate CSR"); - write_summary(ctx, "Generate CSR"); - req = X509_REQ_new(); - if (req == NULL) - goto fail; - - if (old_cert) { - FILE *f; - X509 *cert; - int res; - - f = fopen(old_cert, "r"); - if (f == NULL) - goto fail; - cert = PEM_read_X509(f, NULL, NULL, NULL); - fclose(f); - - if (cert == NULL) - goto fail; - res = X509_REQ_set_subject_name(req, - X509_get_subject_name(cert)); - X509_free(cert); - if (!res) - goto fail; - } else { - os_get_random((u8 *) &val, sizeof(val)); - os_snprintf(name, sizeof(name), "cert-user-%u", val); - subj = X509_NAME_new(); - if (subj == NULL || - !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC, - (unsigned char *) name, - -1, -1, 0) || - !X509_REQ_set_subject_name(req, subj)) - goto fail; - X509_NAME_free(subj); - subj = NULL; - } - - if (!X509_REQ_set_pubkey(req, pkey)) - goto fail; - - exts = sk_X509_EXTENSION_new_null(); - if (!exts) - goto fail; - - ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints, - "CA:FALSE"); - if (ex == NULL || - !sk_X509_EXTENSION_push(exts, ex)) - goto fail; - - ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage, - "nonRepudiation,digitalSignature,keyEncipherment"); - if (ex == NULL || - !sk_X509_EXTENSION_push(exts, ex)) - goto fail; - - ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage, - "1.3.6.1.4.1.40808.1.1.2"); - if (ex == NULL || - !sk_X509_EXTENSION_push(exts, ex)) - goto fail; - - add_csrattrs(ctx, csrattrs, exts); - - if (!X509_REQ_add_extensions(req, exts)) - goto fail; - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - exts = NULL; - - if (!X509_REQ_sign(req, pkey, EVP_sha256())) - goto fail; - - out = BIO_new(BIO_s_mem()); - if (out) { - char *txt; - size_t rlen; - -#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) - X509_REQ_print(out, req); -#endif - rlen = BIO_ctrl_pending(out); - txt = os_malloc(rlen + 1); - if (txt) { - int res = BIO_read(out, txt, rlen); - if (res > 0) { - txt[res] = '\0'; - wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s", - txt); - } - os_free(txt); - } - BIO_free(out); - } - - if (csr_pem) { - FILE *f = fopen(csr_pem, "w"); - if (f == NULL) - goto fail; -#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL) - X509_REQ_print_fp(f, req); -#endif - if (!PEM_write_X509_REQ(f, req)) { - fclose(f); - goto fail; - } - fclose(f); - } - - if (est_req) { - BIO *mem = BIO_new(BIO_s_mem()); - BUF_MEM *ptr; - char *pos, *end, *buf_end; - FILE *f; - - if (mem == NULL) - goto fail; - if (!PEM_write_bio_X509_REQ(mem, req)) { - BIO_free(mem); - goto fail; - } - - BIO_get_mem_ptr(mem, &ptr); - pos = ptr->data; - buf_end = pos + ptr->length; - - /* Remove START/END lines */ - while (pos < buf_end && *pos != '\n') - pos++; - if (pos == buf_end) { - BIO_free(mem); - goto fail; - } - pos++; - - end = pos; - while (end < buf_end && *end != '-') - end++; - - f = fopen(est_req, "w"); - if (f == NULL) { - BIO_free(mem); - goto fail; - } - fwrite(pos, end - pos, 1, f); - fclose(f); - - BIO_free(mem); - } - - ret = 0; -fail: - if (exts) - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - if (subj) - X509_NAME_free(subj); - if (req) - X509_REQ_free(req); - if (pkey) - EVP_PKEY_free(pkey); - if (pctx) - EVP_PKEY_CTX_free(pctx); - return ret; -} - - -int est_build_csr(struct hs20_osu_client *ctx, const char *url) -{ - char *buf; - size_t buflen; - int res; - char old_cert_buf[200]; - char *old_cert = NULL; - CsrAttrs *csrattrs = NULL; - - buflen = os_strlen(url) + 100; - buf = os_malloc(buflen); - if (buf == NULL) - return -1; - - os_snprintf(buf, buflen, "%s/csrattrs", url); - wpa_printf(MSG_INFO, "Download csrattrs from %s", buf); - write_summary(ctx, "Download EST csrattrs from %s", buf); - ctx->no_osu_cert_validation = 1; - http_ocsp_set(ctx->http, 1); - res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt", - ctx->ca_fname); - http_ocsp_set(ctx->http, - (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); - ctx->no_osu_cert_validation = 0; - os_free(buf); - if (res < 0) { - wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed"); - } else { - size_t resp_len; - char *resp; - unsigned char *attrs; - const unsigned char *pos; - size_t attrs_len; - - resp = os_readfile("Cert/est-csrattrs.txt", &resp_len); - if (resp == NULL) { - wpa_printf(MSG_INFO, "Could not read csrattrs"); - return -1; - } - - attrs = base64_decode(resp, resp_len, &attrs_len); - os_free(resp); - - if (attrs == NULL) { - wpa_printf(MSG_INFO, "Could not base64 decode csrattrs"); - return -1; - } - unlink("Cert/est-csrattrs.txt"); - - pos = attrs; - csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len); - os_free(attrs); - if (csrattrs == NULL) { - wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1"); - /* Continue assuming no additional requirements */ - } - } - - if (ctx->client_cert_present) { - os_snprintf(old_cert_buf, sizeof(old_cert_buf), - "SP/%s/client-cert.pem", ctx->fqdn); - old_cert = old_cert_buf; - } - - res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem", - "Cert/est-req.b64", old_cert, csrattrs); - if (csrattrs) - CsrAttrs_free(csrattrs); - - return res; -} - - -int est_simple_enroll(struct hs20_osu_client *ctx, const char *url, - const char *user, const char *pw) -{ - char *buf, *resp, *req, *req2; - size_t buflen, resp_len, len, pkcs7_len; - unsigned char *pkcs7; - char client_cert_buf[200]; - char client_key_buf[200]; - const char *client_cert = NULL, *client_key = NULL; - int res; - - req = os_readfile("Cert/est-req.b64", &len); - if (req == NULL) { - wpa_printf(MSG_INFO, "Could not read Cert/req.b64"); - return -1; - } - req2 = os_realloc(req, len + 1); - if (req2 == NULL) { - os_free(req); - return -1; - } - req2[len] = '\0'; - req = req2; - wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req); - - buflen = os_strlen(url) + 100; - buf = os_malloc(buflen); - if (buf == NULL) { - os_free(req); - return -1; - } - - if (ctx->client_cert_present) { - os_snprintf(buf, buflen, "%s/simplereenroll", url); - os_snprintf(client_cert_buf, sizeof(client_cert_buf), - "SP/%s/client-cert.pem", ctx->fqdn); - client_cert = client_cert_buf; - os_snprintf(client_key_buf, sizeof(client_key_buf), - "SP/%s/client-key.pem", ctx->fqdn); - client_key = client_key_buf; - } else - os_snprintf(buf, buflen, "%s/simpleenroll", url); - wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf); - write_summary(ctx, "EST simpleenroll URL: %s", buf); - ctx->no_osu_cert_validation = 1; - http_ocsp_set(ctx->http, 1); - resp = http_post(ctx->http, buf, req, "application/pkcs10", - "Content-Transfer-Encoding: base64", - ctx->ca_fname, user, pw, client_cert, client_key, - &resp_len); - http_ocsp_set(ctx->http, - (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); - ctx->no_osu_cert_validation = 0; - os_free(buf); - if (resp == NULL) { - wpa_printf(MSG_INFO, "EST certificate enrollment failed"); - write_result(ctx, "EST certificate enrollment failed"); - return -1; - } - wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp); - - pkcs7 = base64_decode(resp, resp_len, &pkcs7_len); - if (pkcs7 == NULL) { - wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7"); - pkcs7 = os_malloc(resp_len); - if (pkcs7) { - os_memcpy(pkcs7, resp, resp_len); - pkcs7_len = resp_len; - } - } - os_free(resp); - - if (pkcs7 == NULL) { - wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response"); - write_result(ctx, "Failed to parse EST simpleenroll base64 response"); - return -1; - } - - res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem", - "Cert/est_cert.der"); - os_free(pkcs7); - - if (res < 0) { - wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file"); - write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file"); - return -1; - } - - wpa_printf(MSG_INFO, "EST simple%senroll completed successfully", - ctx->client_cert_present ? "re" : ""); - write_summary(ctx, "EST simple%senroll completed successfully", - ctx->client_cert_present ? "re" : ""); - - return 0; -} diff --git a/contrib/wpa/hs20/client/oma_dm_client.c b/contrib/wpa/hs20/client/oma_dm_client.c deleted file mode 100644 index bcd68b8775d5..000000000000 --- a/contrib/wpa/hs20/client/oma_dm_client.c +++ /dev/null @@ -1,1398 +0,0 @@ -/* - * Hotspot 2.0 - OMA DM client - * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "wpa_helpers.h" -#include "xml-utils.h" -#include "http-utils.h" -#include "utils/browser.h" -#include "osu_client.h" - - -#define DM_SERVER_INITIATED_MGMT 1200 -#define DM_CLIENT_INITIATED_MGMT 1201 -#define DM_GENERIC_ALERT 1226 - -/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */ -#define DM_RESP_OK 200 -#define DM_RESP_AUTH_ACCEPTED 212 -#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213 -#define DM_RESP_NOT_EXECUTED 215 -#define DM_RESP_ATOMIC_ROLL_BACK_OK 216 -#define DM_RESP_NOT_MODIFIED 304 -#define DM_RESP_BAD_REQUEST 400 -#define DM_RESP_UNAUTHORIZED 401 -#define DM_RESP_FORBIDDEN 403 -#define DM_RESP_NOT_FOUND 404 -#define DM_RESP_COMMAND_NOT_ALLOWED 405 -#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406 -#define DM_RESP_MISSING_CREDENTIALS 407 -#define DM_RESP_CONFLICT 409 -#define DM_RESP_GONE 410 -#define DM_RESP_INCOMPLETE_COMMAND 412 -#define DM_RESP_REQ_ENTITY_TOO_LARGE 413 -#define DM_RESP_URI_TOO_LONG 414 -#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415 -#define DM_RESP_REQ_TOO_BIG 416 -#define DM_RESP_ALREADY_EXISTS 418 -#define DM_RESP_DEVICE_FULL 420 -#define DM_RESP_SIZE_MISMATCH 424 -#define DM_RESP_PERMISSION_DENIED 425 -#define DM_RESP_COMMAND_FAILED 500 -#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501 -#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516 - -#define DM_HS20_SUBSCRIPTION_CREATION \ - "org.wi-fi.hotspot2dot0.SubscriptionCreation" -#define DM_HS20_SUBSCRIPTION_PROVISIONING \ - "org.wi-fi.hotspot2dot0.SubscriptionProvisioning" -#define DM_HS20_SUBSCRIPTION_REMEDIATION \ - "org.wi-fi.hotspot2dot0.SubscriptionRemediation" -#define DM_HS20_POLICY_UPDATE \ - "org.wi-fi.hotspot2dot0.PolicyUpdate" - -#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription" -#define DM_URI_LAUNCH_BROWSER \ - "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI" - - -static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent, - const char *locuri, const char *data); - - -static const char * int2str(int val) -{ - static char buf[20]; - snprintf(buf, sizeof(buf), "%d", val); - return buf; -} - - -static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx, - xml_node_t *node) -{ - xml_node_t *locuri; - char *uri, *ret = NULL; - - locuri = get_node(ctx->xml, node, "Item/Target/LocURI"); - if (locuri == NULL) - return NULL; - - uri = xml_node_get_text(ctx->xml, locuri); - if (uri) - ret = os_strdup(uri); - xml_node_get_text_free(ctx->xml, uri); - return ret; -} - - -static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent, - const char *element, const char *uri) -{ - xml_node_t *node; - - node = xml_node_create(ctx->xml, parent, NULL, element); - if (node == NULL) - return; - xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri); -} - - -static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx, - const char *url, int msgid) -{ - xml_node_t *syncml, *synchdr; - xml_namespace_t *ns; - - if (!ctx->devid) { - wpa_printf(MSG_ERROR, - "DevId from devinfo.xml is not available - cannot use OMA DM"); - return NULL; - } - - syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns, - "SyncML"); - - synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr"); - xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2"); - xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2"); - xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1"); - xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid)); - - oma_dm_add_locuri(ctx, synchdr, "Target", url); - oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid); - - return syncml; -} - - -static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent, - int cmdid) -{ - xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid)); -} - - -static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent, - int cmdid, int data) -{ - xml_node_t *node; - - node = xml_node_create(ctx->xml, parent, NULL, "Alert"); - if (node == NULL) - return NULL; - oma_dm_add_cmdid(ctx, node, cmdid); - xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data)); - - return node; -} - - -static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent, - int msgref, int cmdref, int cmdid, - const char *cmd, int data, const char *targetref) -{ - xml_node_t *node; - - node = xml_node_create(ctx->xml, parent, NULL, "Status"); - if (node == NULL) - return NULL; - oma_dm_add_cmdid(ctx, node, cmdid); - xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref)); - if (cmdref) - xml_node_create_text(ctx->xml, node, NULL, "CmdRef", - int2str(cmdref)); - xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd); - xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data)); - if (targetref) { - xml_node_create_text(ctx->xml, node, NULL, "TargetRef", - targetref); - } - - return node; -} - - -static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent, - int msgref, int cmdref, int cmdid, - const char *locuri, const char *data) -{ - xml_node_t *node; - - node = xml_node_create(ctx->xml, parent, NULL, "Results"); - if (node == NULL) - return NULL; - - oma_dm_add_cmdid(ctx, node, cmdid); - xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref)); - xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref)); - add_item(ctx, node, locuri, data); - - return node; -} - - -static char * mo_str(struct hs20_osu_client *ctx, const char *urn, - const char *fname) -{ - xml_node_t *fnode, *tnds; - char *str; - - fnode = node_from_file(ctx->xml, fname); - if (!fnode) - return NULL; - tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2"); - xml_node_free(ctx->xml, fnode); - if (!tnds) - return NULL; - - str = xml_node_to_str(ctx->xml, tnds); - xml_node_free(ctx->xml, tnds); - if (str == NULL) - return NULL; - wpa_printf(MSG_INFO, "MgmtTree: %s", str); - - return str; -} - - -static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent, - const char *locuri, const char *data) -{ - xml_node_t *item, *node; - - item = xml_node_create(ctx->xml, parent, NULL, "Item"); - oma_dm_add_locuri(ctx, item, "Source", locuri); - node = xml_node_create(ctx->xml, item, NULL, "Meta"); - xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format", - "Chr"); - xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", - "text/plain"); - xml_node_create_text(ctx->xml, item, NULL, "Data", data); -} - - -static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent, - int cmdid) -{ - xml_node_t *info, *child, *replace; - const char *name; - char locuri[200], *txt; - - info = node_from_file(ctx->xml, "devinfo.xml"); - if (info == NULL) { - wpa_printf(MSG_INFO, "Could not read devinfo.xml"); - return; - } - - replace = xml_node_create(ctx->xml, parent, NULL, "Replace"); - if (replace == NULL) { - xml_node_free(ctx->xml, info); - return; - } - oma_dm_add_cmdid(ctx, replace, cmdid); - - xml_node_for_each_child(ctx->xml, child, info) { - xml_node_for_each_check(ctx->xml, child); - name = xml_node_get_localname(ctx->xml, child); - os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name); - txt = xml_node_get_text(ctx->xml, child); - if (txt) { - add_item(ctx, replace, locuri, txt); - xml_node_get_text_free(ctx->xml, txt); - } - } - - xml_node_free(ctx->xml, info); -} - - -static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx, - xml_node_t *syncbody, - int cmdid, const char *oper, - const char *data) -{ - xml_node_t *node, *item; - char buf[200]; - - node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT); - - item = xml_node_create(ctx->xml, node, NULL, "Item"); - oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS); - node = xml_node_create(ctx->xml, item, NULL, "Meta"); - snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper); - xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf); - xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format", - "xml"); - xml_node_create_text(ctx->xml, item, NULL, "Data", data); -} - - -static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx, - const char *url, int msgid, const char *oper) -{ - xml_node_t *syncml, *syncbody; - char *str; - int cmdid = 0; - - syncml = oma_dm_build_hdr(ctx, url, msgid); - if (syncml == NULL) - return NULL; - - syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody"); - if (syncbody == NULL) { - xml_node_free(ctx->xml, syncml); - return NULL; - } - - cmdid++; - add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT); - - str = mo_str(ctx, NULL, "devdetail.xml"); - if (str == NULL) { - xml_node_free(ctx->xml, syncml); - return NULL; - } - cmdid++; - oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str); - os_free(str); - - cmdid++; - add_replace_devinfo(ctx, syncbody, cmdid); - - xml_node_create(ctx->xml, syncbody, NULL, "Final"); - - return syncml; -} - - -static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx, - const char *url, int msgid) -{ - xml_node_t *syncml; - - syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION); - if (syncml) - debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml); - - return syncml; -} - - -static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx, - const char *url, int msgid) -{ - xml_node_t *syncml; - - syncml = build_oma_dm_1(ctx, url, msgid, - DM_HS20_SUBSCRIPTION_PROVISIONING); - if (syncml) - debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml); - - return syncml; -} - - -static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx, - const char *url, int msgid) -{ - xml_node_t *syncml; - - syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE); - if (syncml) - debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml); - - return syncml; -} - - -static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx, - const char *url, int msgid) -{ - xml_node_t *syncml; - - syncml = build_oma_dm_1(ctx, url, msgid, - DM_HS20_SUBSCRIPTION_REMEDIATION); - if (syncml) - debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml); - - return syncml; -} - - -static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec) -{ - xml_node_t *node; - char *data; - int res; - - node = get_node(ctx->xml, exec, "Item/Data"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Data node found"); - return DM_RESP_BAD_REQUEST; - } - - data = xml_node_get_text(ctx->xml, node); - if (data == NULL) { - wpa_printf(MSG_INFO, "Invalid data"); - return DM_RESP_BAD_REQUEST; - } - wpa_printf(MSG_INFO, "Data: %s", data); - wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data); - write_summary(ctx, "Launch browser to URI '%s'", data); - res = hs20_web_browser(data, 1); - xml_node_get_text_free(ctx->xml, data); - if (res > 0) { - wpa_printf(MSG_INFO, "User response in browser completed successfully"); - write_summary(ctx, "User response in browser completed successfully"); - return DM_RESP_OK; - } else { - wpa_printf(MSG_INFO, "Failed to receive user response"); - write_summary(ctx, "Failed to receive user response"); - return DM_RESP_COMMAND_FAILED; - } -} - - -static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec) -{ - xml_node_t *node, *getcert; - char *data; - const char *name; - int res; - - wpa_printf(MSG_INFO, "Client certificate enrollment"); - write_summary(ctx, "Client certificate enrollment"); - - node = get_node(ctx->xml, exec, "Item/Data"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Data node found"); - return DM_RESP_BAD_REQUEST; - } - - data = xml_node_get_text(ctx->xml, node); - if (data == NULL) { - wpa_printf(MSG_INFO, "Invalid data"); - return DM_RESP_BAD_REQUEST; - } - wpa_printf(MSG_INFO, "Data: %s", data); - getcert = xml_node_from_buf(ctx->xml, data); - xml_node_get_text_free(ctx->xml, data); - - if (getcert == NULL) { - wpa_printf(MSG_INFO, "Could not parse Item/Data node contents"); - return DM_RESP_BAD_REQUEST; - } - - debug_dump_node(ctx, "OMA-DM getCertificate", getcert); - - name = xml_node_get_localname(ctx->xml, getcert); - if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) { - wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'", - name); - return DM_RESP_BAD_REQUEST; - } - - res = osu_get_certificate(ctx, getcert); - - xml_node_free(ctx->xml, getcert); - - return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED; -} - - -static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec) -{ - char *locuri; - int ret; - - locuri = oma_dm_get_target_locuri(ctx, exec); - if (locuri == NULL) { - wpa_printf(MSG_INFO, "No Target LocURI node found"); - return DM_RESP_BAD_REQUEST; - } - - wpa_printf(MSG_INFO, "Target LocURI: %s", locuri); - - if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/" - "launchBrowserToURI") == 0) { - ret = oma_dm_exec_browser(ctx, exec); - } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/" - "getCertificate") == 0) { - ret = oma_dm_exec_get_cert(ctx, exec); - } else { - wpa_printf(MSG_INFO, "Unsupported exec Target LocURI"); - ret = DM_RESP_NOT_FOUND; - } - os_free(locuri); - - return ret; -} - - -static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri, - xml_node_t *add, xml_node_t *pps, - const char *pps_fname) -{ - const char *pos; - size_t fqdn_len; - xml_node_t *node, *tnds, *unode, *pps_node; - char *data, *uri, *upos, *end; - int use_tnds = 0; - size_t uri_len; - - wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri); - - if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi"); - return DM_RESP_PERMISSION_DENIED; - } - pos = locuri + 8; - - if (ctx->fqdn == NULL) - return DM_RESP_COMMAND_FAILED; - fqdn_len = os_strlen(ctx->fqdn); - if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || - pos[fqdn_len] != '/') { - wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s", - ctx->fqdn); - return DM_RESP_PERMISSION_DENIED; - } - pos += fqdn_len + 1; - - if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { - wpa_printf(MSG_INFO, - "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription", - ctx->fqdn); - return DM_RESP_PERMISSION_DENIED; - } - pos += 24; - - wpa_printf(MSG_INFO, "Add command for PPS node %s", pos); - - pps_node = get_node(ctx->xml, pps, pos); - if (pps_node) { - wpa_printf(MSG_INFO, "Specified PPS node exists already"); - return DM_RESP_ALREADY_EXISTS; - } - - uri = os_strdup(pos); - if (uri == NULL) - return DM_RESP_COMMAND_FAILED; - while (!pps_node) { - upos = os_strrchr(uri, '/'); - if (!upos) - break; - upos[0] = '\0'; - pps_node = get_node(ctx->xml, pps, uri); - wpa_printf(MSG_INFO, "Node %s %s", uri, - pps_node ? "exists" : "does not exist"); - } - - wpa_printf(MSG_INFO, "Parent URI: %s", uri); - - if (!pps_node) { - /* Add at root of PPS MO */ - pps_node = pps; - } - - uri_len = os_strlen(uri); - os_strlcpy(uri, pos + uri_len, os_strlen(pos)); - upos = uri; - while (*upos == '/') - upos++; - wpa_printf(MSG_INFO, "Nodes to add: %s", upos); - - for (;;) { - end = os_strchr(upos, '/'); - if (!end) - break; - *end = '\0'; - wpa_printf(MSG_INFO, "Adding interim node %s", upos); - pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos); - if (pps_node == NULL) { - os_free(uri); - return DM_RESP_COMMAND_FAILED; - } - upos = end + 1; - } - - wpa_printf(MSG_INFO, "Adding node %s", upos); - - node = get_node(ctx->xml, add, "Item/Meta/Type"); - if (node) { - char *type; - type = xml_node_get_text(ctx->xml, node); - if (type == NULL) { - wpa_printf(MSG_ERROR, "Could not find type text"); - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - use_tnds = node && - os_strstr(type, "application/vnd.syncml.dmtnds+xml"); - } - - node = get_node(ctx->xml, add, "Item/Data"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Add/Item/Data found"); - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - - data = xml_node_get_text(ctx->xml, node); - if (data == NULL) { - wpa_printf(MSG_INFO, "Could not get Add/Item/Data text"); - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - - wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data); - - if (use_tnds) { - tnds = xml_node_from_buf(ctx->xml, data); - xml_node_get_text_free(ctx->xml, data); - if (tnds == NULL) { - wpa_printf(MSG_INFO, - "Could not parse Add/Item/Data text"); - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - - unode = tnds_to_mo(ctx->xml, tnds); - xml_node_free(ctx->xml, tnds); - if (unode == NULL) { - wpa_printf(MSG_INFO, "Could not parse TNDS text"); - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - - debug_dump_node(ctx, "Parsed TNDS", unode); - - xml_node_add_child(ctx->xml, pps_node, unode); - } else { - /* TODO: What to do here? */ - os_free(uri); - return DM_RESP_BAD_REQUEST; - } - - os_free(uri); - - if (update_pps_file(ctx, pps_fname, pps) < 0) - return DM_RESP_COMMAND_FAILED; - - ctx->pps_updated = 1; - - return DM_RESP_OK; -} - - -static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add, - xml_node_t *pps, const char *pps_fname) -{ - xml_node_t *node; - char *locuri; - char fname[300]; - int ret; - - node = get_node(ctx->xml, add, "Item/Target/LocURI"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Target LocURI node found"); - return DM_RESP_BAD_REQUEST; - } - locuri = xml_node_get_text(ctx->xml, node); - if (locuri == NULL) { - wpa_printf(MSG_ERROR, "No LocURI node text found"); - return DM_RESP_BAD_REQUEST; - } - wpa_printf(MSG_INFO, "Target LocURI: %s", locuri); - if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Unsupported Add Target LocURI"); - xml_node_get_text_free(ctx->xml, locuri); - return DM_RESP_PERMISSION_DENIED; - } - - node = get_node(ctx->xml, add, "Item/Data"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Data node found"); - xml_node_get_text_free(ctx->xml, locuri); - return DM_RESP_BAD_REQUEST; - } - - if (pps_fname && os_file_exists(pps_fname)) { - ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname); - if (ret != DM_RESP_OK) { - xml_node_get_text_free(ctx->xml, locuri); - return ret; - } - ret = 0; - os_strlcpy(fname, pps_fname, sizeof(fname)); - } else - ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname)); - xml_node_get_text_free(ctx->xml, locuri); - if (ret < 0) - return ret == -2 ? DM_RESP_ALREADY_EXISTS : - DM_RESP_COMMAND_FAILED; - - if (ctx->no_reconnect == 2) { - os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s", - fname); - ctx->pps_cred_set = 1; - return DM_RESP_OK; - } - - wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); - cmd_set_pps(ctx, fname); - - if (ctx->no_reconnect) - return DM_RESP_OK; - - wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) - wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); - - return DM_RESP_OK; -} - - -static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace, - xml_node_t *pps, const char *pps_fname) -{ - char *locuri, *pos; - size_t fqdn_len; - xml_node_t *node, *tnds, *unode, *pps_node, *parent; - char *data; - int use_tnds = 0; - - locuri = oma_dm_get_target_locuri(ctx, replace); - if (locuri == NULL) - return DM_RESP_BAD_REQUEST; - - wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri); - if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi"); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos = locuri + 8; - - if (ctx->fqdn == NULL) { - os_free(locuri); - return DM_RESP_COMMAND_FAILED; - } - fqdn_len = os_strlen(ctx->fqdn); - if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || - pos[fqdn_len] != '/') { - wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s", - ctx->fqdn); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos += fqdn_len + 1; - - if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { - wpa_printf(MSG_INFO, - "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription", - ctx->fqdn); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos += 24; - - wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos); - - pps_node = get_node(ctx->xml, pps, pos); - if (pps_node == NULL) { - wpa_printf(MSG_INFO, "Specified PPS node not found"); - os_free(locuri); - return DM_RESP_NOT_FOUND; - } - - node = get_node(ctx->xml, replace, "Item/Meta/Type"); - if (node) { - char *type; - type = xml_node_get_text(ctx->xml, node); - if (type == NULL) { - wpa_printf(MSG_INFO, "Could not find type text"); - os_free(locuri); - return DM_RESP_BAD_REQUEST; - } - use_tnds = node && - os_strstr(type, "application/vnd.syncml.dmtnds+xml"); - } - - node = get_node(ctx->xml, replace, "Item/Data"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Replace/Item/Data found"); - os_free(locuri); - return DM_RESP_BAD_REQUEST; - } - - data = xml_node_get_text(ctx->xml, node); - if (data == NULL) { - wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text"); - os_free(locuri); - return DM_RESP_BAD_REQUEST; - } - - wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data); - - if (use_tnds) { - tnds = xml_node_from_buf(ctx->xml, data); - xml_node_get_text_free(ctx->xml, data); - if (tnds == NULL) { - wpa_printf(MSG_INFO, - "Could not parse Replace/Item/Data text"); - os_free(locuri); - return DM_RESP_BAD_REQUEST; - } - - unode = tnds_to_mo(ctx->xml, tnds); - xml_node_free(ctx->xml, tnds); - if (unode == NULL) { - wpa_printf(MSG_INFO, "Could not parse TNDS text"); - os_free(locuri); - return DM_RESP_BAD_REQUEST; - } - - debug_dump_node(ctx, "Parsed TNDS", unode); - - parent = xml_node_get_parent(ctx->xml, pps_node); - xml_node_detach(ctx->xml, pps_node); - xml_node_add_child(ctx->xml, parent, unode); - } else { - xml_node_set_text(ctx->xml, pps_node, data); - xml_node_get_text_free(ctx->xml, data); - } - - os_free(locuri); - - if (update_pps_file(ctx, pps_fname, pps) < 0) - return DM_RESP_COMMAND_FAILED; - - ctx->pps_updated = 1; - - return DM_RESP_OK; -} - - -static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get, - xml_node_t *pps, const char *pps_fname, char **value) -{ - char *locuri, *pos; - size_t fqdn_len; - xml_node_t *pps_node; - const char *name; - - *value = NULL; - - locuri = oma_dm_get_target_locuri(ctx, get); - if (locuri == NULL) - return DM_RESP_BAD_REQUEST; - - wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri); - if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi"); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos = locuri + 8; - - if (ctx->fqdn == NULL) - return DM_RESP_COMMAND_FAILED; - fqdn_len = os_strlen(ctx->fqdn); - if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || - pos[fqdn_len] != '/') { - wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s", - ctx->fqdn); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos += fqdn_len + 1; - - if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { - wpa_printf(MSG_INFO, - "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription", - ctx->fqdn); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - pos += 24; - - wpa_printf(MSG_INFO, "Get command for PPS node %s", pos); - - pps_node = get_node(ctx->xml, pps, pos); - if (pps_node == NULL) { - wpa_printf(MSG_INFO, "Specified PPS node not found"); - os_free(locuri); - return DM_RESP_NOT_FOUND; - } - - name = xml_node_get_localname(ctx->xml, pps_node); - wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name); - if (os_strcasecmp(name, "Password") == 0) { - wpa_printf(MSG_INFO, "Do not allow Get for Password node"); - os_free(locuri); - return DM_RESP_PERMISSION_DENIED; - } - - /* - * TODO: No support for DMTNDS, so if interior node, reply with a - * list of children node names in Results element. The child list type is - * defined in [DMTND]. - */ - - *value = xml_node_get_text(ctx->xml, pps_node); - if (*value == NULL) - return DM_RESP_COMMAND_FAILED; - - return DM_RESP_OK; -} - - -static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node) -{ - xml_node_t *cnode; - char *str; - int ret; - - cnode = get_node(ctx->xml, node, "CmdID"); - if (cnode == NULL) - return 0; - - str = xml_node_get_text(ctx->xml, cnode); - if (str == NULL) - return 0; - ret = atoi(str); - xml_node_get_text_free(ctx->xml, str); - return ret; -} - - -static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx, - const char *url, xml_node_t *syncml, - const char *ext_hdr, - const char *username, const char *password, - const char *client_cert, - const char *client_key) -{ - xml_node_t *resp; - char *str, *res; - char *resp_uri = NULL; - - str = xml_node_to_str(ctx->xml, syncml); - xml_node_free(ctx->xml, syncml); - if (str == NULL) - return NULL; - - wpa_printf(MSG_INFO, "Send OMA DM Package"); - write_summary(ctx, "Send OMA DM Package"); - os_free(ctx->server_url); - ctx->server_url = os_strdup(url); - res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml", - ext_hdr, ctx->ca_fname, username, password, - client_cert, client_key, NULL); - os_free(str); - os_free(resp_uri); - resp_uri = NULL; - - if (res == NULL) { - const char *err = http_get_err(ctx->http); - if (err) { - wpa_printf(MSG_INFO, "HTTP error: %s", err); - write_result(ctx, "HTTP error: %s", err); - } else { - write_summary(ctx, "Failed to send OMA DM Package"); - } - return NULL; - } - wpa_printf(MSG_DEBUG, "Server response: %s", res); - - wpa_printf(MSG_INFO, "Process OMA DM Package"); - write_summary(ctx, "Process received OMA DM Package"); - resp = xml_node_from_buf(ctx->xml, res); - os_free(res); - if (resp == NULL) { - wpa_printf(MSG_INFO, "Failed to parse OMA DM response"); - return NULL; - } - - debug_dump_node(ctx, "OMA DM Package", resp); - - return resp; -} - - -static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url, - xml_node_t *resp, int msgid, - char **ret_resp_uri, - xml_node_t *pps, const char *pps_fname) -{ - xml_node_t *syncml, *syncbody, *hdr, *body, *child; - const char *name; - char *resp_uri = NULL; - int server_msgid = 0; - int cmdid = 0; - int server_cmdid; - int resp_needed = 0; - char *tmp; - int final = 0; - char *locuri; - - *ret_resp_uri = NULL; - - name = xml_node_get_localname(ctx->xml, resp); - if (name == NULL || os_strcasecmp(name, "SyncML") != 0) { - wpa_printf(MSG_INFO, "SyncML node not found"); - return NULL; - } - - hdr = get_node(ctx->xml, resp, "SyncHdr"); - body = get_node(ctx->xml, resp, "SyncBody"); - if (hdr == NULL || body == NULL) { - wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody"); - return NULL; - } - - xml_node_for_each_child(ctx->xml, child, hdr) { - xml_node_for_each_check(ctx->xml, child); - name = xml_node_get_localname(ctx->xml, child); - wpa_printf(MSG_INFO, "SyncHdr %s", name); - if (os_strcasecmp(name, "RespURI") == 0) { - tmp = xml_node_get_text(ctx->xml, child); - if (tmp) - resp_uri = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); - } else if (os_strcasecmp(name, "MsgID") == 0) { - tmp = xml_node_get_text(ctx->xml, child); - if (tmp) - server_msgid = atoi(tmp); - xml_node_get_text_free(ctx->xml, tmp); - } - } - - wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid); - if (resp_uri) - wpa_printf(MSG_INFO, "RespURI: %s", resp_uri); - - syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid); - if (syncml == NULL) { - os_free(resp_uri); - return NULL; - } - - syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody"); - cmdid++; - add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr", - DM_RESP_AUTH_ACCEPTED, NULL); - - xml_node_for_each_child(ctx->xml, child, body) { - xml_node_for_each_check(ctx->xml, child); - server_cmdid = oma_dm_get_cmdid(ctx, child); - name = xml_node_get_localname(ctx->xml, child); - wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s", - server_cmdid, name); - if (os_strcasecmp(name, "Exec") == 0) { - int res = oma_dm_exec(ctx, child); - cmdid++; - locuri = oma_dm_get_target_locuri(ctx, child); - if (locuri == NULL) - res = DM_RESP_BAD_REQUEST; - add_status(ctx, syncbody, server_msgid, server_cmdid, - cmdid, name, res, locuri); - os_free(locuri); - resp_needed = 1; - } else if (os_strcasecmp(name, "Add") == 0) { - int res = oma_dm_add(ctx, child, pps, pps_fname); - cmdid++; - locuri = oma_dm_get_target_locuri(ctx, child); - if (locuri == NULL) - res = DM_RESP_BAD_REQUEST; - add_status(ctx, syncbody, server_msgid, server_cmdid, - cmdid, name, res, locuri); - os_free(locuri); - resp_needed = 1; - } else if (os_strcasecmp(name, "Replace") == 0) { - int res; - res = oma_dm_replace(ctx, child, pps, pps_fname); - cmdid++; - locuri = oma_dm_get_target_locuri(ctx, child); - if (locuri == NULL) - res = DM_RESP_BAD_REQUEST; - add_status(ctx, syncbody, server_msgid, server_cmdid, - cmdid, name, res, locuri); - os_free(locuri); - resp_needed = 1; - } else if (os_strcasecmp(name, "Status") == 0) { - /* TODO: Verify success */ - } else if (os_strcasecmp(name, "Get") == 0) { - int res; - char *value; - res = oma_dm_get(ctx, child, pps, pps_fname, &value); - cmdid++; - locuri = oma_dm_get_target_locuri(ctx, child); - if (locuri == NULL) - res = DM_RESP_BAD_REQUEST; - add_status(ctx, syncbody, server_msgid, server_cmdid, - cmdid, name, res, locuri); - if (res == DM_RESP_OK && value) { - cmdid++; - add_results(ctx, syncbody, server_msgid, - server_cmdid, cmdid, locuri, value); - } - os_free(locuri); - xml_node_get_text_free(ctx->xml, value); - resp_needed = 1; -#if 0 /* TODO: MUST support */ - } else if (os_strcasecmp(name, "Delete") == 0) { -#endif -#if 0 /* TODO: MUST support */ - } else if (os_strcasecmp(name, "Sequence") == 0) { -#endif - } else if (os_strcasecmp(name, "Final") == 0) { - final = 1; - break; - } else { - locuri = oma_dm_get_target_locuri(ctx, child); - add_status(ctx, syncbody, server_msgid, server_cmdid, - cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED, - locuri); - os_free(locuri); - resp_needed = 1; - } - } - - if (!final) { - wpa_printf(MSG_INFO, "Final node not found"); - xml_node_free(ctx->xml, syncml); - os_free(resp_uri); - return NULL; - } - - if (!resp_needed) { - wpa_printf(MSG_INFO, "Exchange completed - no response needed"); - xml_node_free(ctx->xml, syncml); - os_free(resp_uri); - return NULL; - } - - xml_node_create(ctx->xml, syncbody, NULL, "Final"); - - debug_dump_node(ctx, "OMA-DM Package 3", syncml); - - *ret_resp_uri = resp_uri; - return syncml; -} - - -int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url) -{ - xml_node_t *syncml, *resp; - char *resp_uri = NULL; - int msgid = 0; - - if (url == NULL) { - wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); - return -1; - } - - wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested"); - write_summary(ctx, "OMA-DM credential provisioning"); - - msgid++; - syncml = build_oma_dm_1_sub_reg(ctx, url, msgid); - if (syncml == NULL) - return -1; - - while (syncml) { - resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url, - syncml, NULL, NULL, NULL, NULL, NULL); - if (resp == NULL) - return -1; - - msgid++; - syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri, - NULL, NULL); - xml_node_free(ctx->xml, resp); - } - - os_free(resp_uri); - - return ctx->pps_cred_set ? 0 : -1; -} - - -int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url) -{ - xml_node_t *syncml, *resp; - char *resp_uri = NULL; - int msgid = 0; - - if (url == NULL) { - wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); - return -1; - } - - wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested"); - ctx->no_reconnect = 2; - - wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning"); - write_summary(ctx, "Wait for IP address before starting SIM provisioning"); - - if (wait_ip_addr(ctx->ifname, 15) < 0) { - wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); - } - write_summary(ctx, "OMA-DM SIM provisioning"); - - msgid++; - syncml = build_oma_dm_1_sub_prov(ctx, url, msgid); - if (syncml == NULL) - return -1; - - while (syncml) { - resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url, - syncml, NULL, NULL, NULL, NULL, NULL); - if (resp == NULL) - return -1; - - msgid++; - syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri, - NULL, NULL); - xml_node_free(ctx->xml, resp); - } - - os_free(resp_uri); - - if (ctx->pps_cred_set) { - wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); - cmd_set_pps(ctx, ctx->pps_fname); - - wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); - write_summary(ctx, "Requesting reconnection with updated configuration"); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { - wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); - write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); - return -1; - } - } - - return ctx->pps_cred_set ? 0 : -1; -} - - -void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps) -{ - xml_node_t *syncml, *resp; - char *resp_uri = NULL; - int msgid = 0; - - wpa_printf(MSG_INFO, "OMA-DM policy update"); - write_summary(ctx, "OMA-DM policy update"); - - msgid++; - syncml = build_oma_dm_1_pol_upd(ctx, address, msgid); - if (syncml == NULL) - return; - - while (syncml) { - resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address, - syncml, NULL, cred_username, - cred_password, client_cert, client_key); - if (resp == NULL) - return; - - msgid++; - syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri, - pps, pps_fname); - xml_node_free(ctx->xml, resp); - } - - os_free(resp_uri); - - if (ctx->pps_updated) { - wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO"); - write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection"); - cmd_set_pps(ctx, pps_fname); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { - wpa_printf(MSG_INFO, - "Failed to request wpa_supplicant to reconnect"); - write_summary(ctx, - "Failed to request wpa_supplicant to reconnect"); - } - } -} - - -void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps) -{ - xml_node_t *syncml, *resp; - char *resp_uri = NULL; - int msgid = 0; - - wpa_printf(MSG_INFO, "OMA-DM subscription remediation"); - write_summary(ctx, "OMA-DM subscription remediation"); - - msgid++; - syncml = build_oma_dm_1_sub_rem(ctx, address, msgid); - if (syncml == NULL) - return; - - while (syncml) { - resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address, - syncml, NULL, cred_username, - cred_password, client_cert, client_key); - if (resp == NULL) - return; - - msgid++; - syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri, - pps, pps_fname); - xml_node_free(ctx->xml, resp); - } - - os_free(resp_uri); - - wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection"); - write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection"); - cmd_set_pps(ctx, pps_fname); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { - wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); - write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); - } -} - - -void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname, - const char *add_fname) -{ - xml_node_t *pps, *add; - int res; - - ctx->fqdn = os_strdup("wi-fi.org"); - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "PPS file %s could not be parsed", - pps_fname); - return; - } - - add = node_from_file(ctx->xml, add_fname); - if (add == NULL) { - wpa_printf(MSG_INFO, "Add file %s could not be parsed", - add_fname); - xml_node_free(ctx->xml, pps); - return; - } - - res = oma_dm_add(ctx, add, pps, pps_fname); - wpa_printf(MSG_INFO, "oma_dm_add --> %d", res); - - xml_node_free(ctx->xml, pps); - xml_node_free(ctx->xml, add); -} - - -void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname, - const char *replace_fname) -{ - xml_node_t *pps, *replace; - int res; - - ctx->fqdn = os_strdup("wi-fi.org"); - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "PPS file %s could not be parsed", - pps_fname); - return; - } - - replace = node_from_file(ctx->xml, replace_fname); - if (replace == NULL) { - wpa_printf(MSG_INFO, "Replace file %s could not be parsed", - replace_fname); - xml_node_free(ctx->xml, pps); - return; - } - - res = oma_dm_replace(ctx, replace, pps, pps_fname); - wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res); - - xml_node_free(ctx->xml, pps); - xml_node_free(ctx->xml, replace); -} diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c index 2ca85f96ce3f..f679df4b18bf 100644 --- a/contrib/wpa/hs20/client/osu_client.c +++ b/contrib/wpa/hs20/client/osu_client.c @@ -9,9 +9,6 @@ #include "includes.h" #include #include -#ifdef ANDROID -#include "private/android_filesystem_config.h" -#endif /* ANDROID */ #include "common.h" #include "utils/browser.h" @@ -25,10 +22,9 @@ #include "crypto/sha256.h" #include "osu_client.h" -const char *spp_xsd_fname = "spp.xsd"; +static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...); - -void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) +static void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) { va_list ap; FILE *f; @@ -54,7 +50,7 @@ void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) } -void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) +static void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) { va_list ap; FILE *f; @@ -74,231 +70,6 @@ void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) } -void debug_dump_node(struct hs20_osu_client *ctx, const char *title, - xml_node_t *node) -{ - char *str = xml_node_to_str(ctx->xml, node); - wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str); - free(str); -} - - -static int valid_fqdn(const char *fqdn) -{ - const char *pos; - - /* TODO: could make this more complete.. */ - if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255) - return 0; - for (pos = fqdn; *pos; pos++) { - if (*pos >= 'a' && *pos <= 'z') - continue; - if (*pos >= 'A' && *pos <= 'Z') - continue; - if (*pos >= '0' && *pos <= '9') - continue; - if (*pos == '-' || *pos == '.' || *pos == '_') - continue; - return 0; - } - return 1; -} - - -static int android_update_permission(const char *path, mode_t mode) -{ -#ifdef ANDROID - /* we need to change file/folder permission for Android */ - - if (!path) { - wpa_printf(MSG_ERROR, "file path null"); - return -1; - } - - /* Allow processes running with Group ID as AID_WIFI, - * to read files from SP, SP/, Cert and osu-info directories */ - if (lchown(path, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", - strerror(errno)); - return -1; - } - - if (chmod(path, mode) < 0) { - wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", - strerror(errno)); - return -1; - } -#endif /* ANDROID */ - - return 0; -} - - -int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) -{ - xml_node_t *node; - char *url, *user = NULL, *pw = NULL; - char *proto; - int ret = -1; - - proto = xml_node_get_attr_value(ctx->xml, getcert, - "enrollmentProtocol"); - if (!proto) - return -1; - wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto); - write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto); - if (os_strcasecmp(proto, "EST") != 0) { - wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol"); - xml_node_get_attr_value_free(ctx->xml, proto); - return -1; - } - xml_node_get_attr_value_free(ctx->xml, proto); - - node = get_node(ctx->xml, getcert, "enrollmentServerURI"); - if (node == NULL) { - wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node"); - xml_node_get_attr_value_free(ctx->xml, proto); - return -1; - } - url = xml_node_get_text(ctx->xml, node); - if (url == NULL) { - wpa_printf(MSG_INFO, "Could not get URL text"); - return -1; - } - wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url); - write_summary(ctx, "enrollmentServerURI: %s", url); - - node = get_node(ctx->xml, getcert, "estUserID"); - if (node == NULL && !ctx->client_cert_present) { - wpa_printf(MSG_INFO, "Could not find estUserID node"); - goto fail; - } - if (node) { - user = xml_node_get_text(ctx->xml, node); - if (user == NULL) { - wpa_printf(MSG_INFO, "Could not get estUserID text"); - goto fail; - } - wpa_printf(MSG_INFO, "estUserID: %s", user); - write_summary(ctx, "estUserID: %s", user); - } - - node = get_node(ctx->xml, getcert, "estPassword"); - if (node == NULL && !ctx->client_cert_present) { - wpa_printf(MSG_INFO, "Could not find estPassword node"); - goto fail; - } - if (node) { - pw = xml_node_get_base64_text(ctx->xml, node, NULL); - if (pw == NULL) { - wpa_printf(MSG_INFO, "Could not get estPassword text"); - goto fail; - } - wpa_printf(MSG_INFO, "estPassword: %s", pw); - } - - mkdir("Cert", S_IRWXU); - android_update_permission("Cert", S_IRWXU | S_IRWXG); - - if (est_load_cacerts(ctx, url) < 0 || - est_build_csr(ctx, url) < 0 || - est_simple_enroll(ctx, url, user, pw) < 0) - goto fail; - - ret = 0; -fail: - xml_node_get_text_free(ctx->xml, url); - xml_node_get_text_free(ctx->xml, user); - xml_node_get_text_free(ctx->xml, pw); - - return ret; -} - - -static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert, - const char *fqdn) -{ - u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN]; - char *der, *pem; - size_t der_len, pem_len; - char *fingerprint; - char buf[200]; - - wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn); - - fingerprint = xml_node_get_text(ctx->xml, cert); - if (fingerprint == NULL) - return -1; - if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) { - wpa_printf(MSG_INFO, "Invalid SHA256 hash value"); - write_result(ctx, "Invalid client certificate SHA256 hash value in PPS"); - xml_node_get_text_free(ctx->xml, fingerprint); - return -1; - } - xml_node_get_text_free(ctx->xml, fingerprint); - - der = os_readfile("Cert/est_cert.der", &der_len); - if (der == NULL) { - wpa_printf(MSG_INFO, "Could not find client certificate from EST"); - write_result(ctx, "Could not find client certificate from EST"); - return -1; - } - - if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) { - os_free(der); - return -1; - } - os_free(der); - - if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) { - wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO"); - write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO"); - return -1; - } - - wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO"); - unlink("Cert/est_cert.der"); - - os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn); - if (rename("Cert/est-cacerts.pem", buf) < 0) { - wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s", - strerror(errno)); - return -1; - } - pem = os_readfile(buf, &pem_len); - - os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn); - if (rename("Cert/est_cert.pem", buf) < 0) { - wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s", - strerror(errno)); - os_free(pem); - return -1; - } - - if (pem) { - FILE *f = fopen(buf, "a"); - if (f) { - fwrite(pem, pem_len, 1, f); - fclose(f); - } - os_free(pem); - } - - os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn); - if (rename("Cert/privkey-plain.pem", buf) < 0) { - wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s", - strerror(errno)); - return -1; - } - - unlink("Cert/est-req.b64"); - unlink("Cert/est-req.pem"); - rmdir("Cert"); - - return 0; -} - - #define TMP_CERT_DL_FILE "tmp-cert-download" static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params, @@ -337,12 +108,10 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params, xml_node_get_text_free(ctx->xml, hash); write_summary(ctx, "Download certificate from %s", url); - ctx->no_osu_cert_validation = 1; http_ocsp_set(ctx->http, 1); res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL); http_ocsp_set(ctx->http, (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2); - ctx->no_osu_cert_validation = 0; xml_node_get_text_free(ctx->xml, url); if (res < 0) return -1; @@ -392,60 +161,6 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params, } -static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname, - const char *ca_fname) -{ - xml_node_t *pps, *node; - int ret; - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); - return -1; - } - - node = get_child_node(ctx->xml, pps, - "SubscriptionUpdate/TrustRoot"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS"); - xml_node_free(ctx->xml, pps); - return -1; - } - - ret = download_cert(ctx, node, ca_fname); - xml_node_free(ctx->xml, pps); - - return ret; -} - - -static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname, - const char *ca_fname) -{ - xml_node_t *pps, *node; - int ret; - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname); - return -1; - } - - node = get_child_node(ctx->xml, pps, - "Policy/PolicyUpdate/TrustRoot"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); - xml_node_free(ctx->xml, pps); - return -2; - } - - ret = download_cert(ctx, node, ca_fname); - xml_node_free(ctx->xml, pps); - - return ret; -} - - static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname, const char *ca_fname) { @@ -480,298 +195,6 @@ static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname, } -static int download_trust_roots(struct hs20_osu_client *ctx, - const char *pps_fname) -{ - char *dir, *pos; - char fname[300]; - int ret, ret1; - - dir = os_strdup(pps_fname); - if (dir == NULL) - return -1; - pos = os_strrchr(dir, '/'); - if (pos == NULL) { - os_free(dir); - return -1; - } - *pos = '\0'; - - snprintf(fname, sizeof(fname), "%s/ca.pem", dir); - ret = cmd_dl_osu_ca(ctx, pps_fname, fname); - snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); - ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); - if (ret == 0 && ret1 == -1) - ret = -1; - snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); - ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); - if (ret == 0 && ret1 == -1) - ret = -1; - - os_free(dir); - - return ret; -} - - -static int server_dnsname_suffix_match(struct hs20_osu_client *ctx, - const char *fqdn) -{ - size_t match_len, len, i; - const char *val; - - match_len = os_strlen(fqdn); - - for (i = 0; i < ctx->server_dnsname_count; i++) { - wpa_printf(MSG_INFO, - "Checking suffix match against server dNSName %s", - ctx->server_dnsname[i]); - val = ctx->server_dnsname[i]; - len = os_strlen(val); - - if (match_len > len) - continue; - - if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0) - continue; /* no match */ - - if (match_len == len) - return 1; /* exact match */ - - if (val[len - match_len - 1] == '.') - return 1; /* full label match completes suffix match */ - - /* Reject due to incomplete label match */ - } - - /* None of the dNSName(s) matched */ - return 0; -} - - -int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, - xml_node_t *add_mo, char *fname, size_t fname_len) -{ - char *str; - char *fqdn, *pos; - xml_node_t *tnds, *mo, *cert; - const char *name; - int ret; - - if (strncmp(uri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'", - uri); - write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'", - uri); - return -1; - } - - fqdn = strdup(uri + 8); - if (fqdn == NULL) - return -1; - pos = strchr(fqdn, '/'); - if (pos) { - if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) { - wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'", - uri); - write_result(ctx, "Unsupported location for addMO to " - "add PPS MO (extra directory): '%s'", uri); - free(fqdn); - return -1; - } - *pos = '\0'; /* remove trailing slash and PPS node name */ - } - wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn); - - if (!server_dnsname_suffix_match(ctx, fqdn)) { - wpa_printf(MSG_INFO, - "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d", - fqdn, (int) ctx->server_dnsname_count); - write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values", - fqdn); - free(fqdn); - return -1; - } - - if (!valid_fqdn(fqdn)) { - wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn); - write_result(ctx, "Invalid FQDN '%s'", fqdn); - free(fqdn); - return -1; - } - - mkdir("SP", S_IRWXU); - snprintf(fname, fname_len, "SP/%s", fqdn); - if (mkdir(fname, S_IRWXU) < 0) { - if (errno != EEXIST) { - int err = errno; - wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", - fname, strerror(err)); - free(fqdn); - return -1; - } - } - - android_update_permission("SP", S_IRWXU | S_IRWXG); - android_update_permission(fname, S_IRWXU | S_IRWXG); - - snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); - - if (os_file_exists(fname)) { - wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO", - fname); - write_result(ctx, "PPS file '%s' exists - reject addMO", - fname); - free(fqdn); - return -2; - } - wpa_printf(MSG_INFO, "Using PPS file: %s", fname); - - str = xml_node_get_text(ctx->xml, add_mo); - if (str == NULL) { - wpa_printf(MSG_INFO, "Could not extract MO text"); - free(fqdn); - return -1; - } - wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str); - - tnds = xml_node_from_buf(ctx->xml, str); - xml_node_get_text_free(ctx->xml, str); - if (tnds == NULL) { - wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text"); - free(fqdn); - return -1; - } - - mo = tnds_to_mo(ctx->xml, tnds); - if (mo == NULL) { - wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text"); - free(fqdn); - return -1; - } - - debug_dump_node(ctx, "Parsed TNDS", mo); - - name = xml_node_get_localname(ctx->xml, mo); - if (os_strcasecmp(name, "PerProviderSubscription") != 0) { - wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'", - name); - free(fqdn); - return -1; - } - - cert = get_child_node(ctx->xml, mo, - "Credential/DigitalCertificate/" - "CertSHA256Fingerprint"); - if (cert && process_est_cert(ctx, cert, fqdn) < 0) { - xml_node_free(ctx->xml, mo); - free(fqdn); - return -1; - } - free(fqdn); - - if (node_to_file(ctx->xml, fname, mo) < 0) { - wpa_printf(MSG_INFO, "Could not write MO to file"); - xml_node_free(ctx->xml, mo); - return -1; - } - xml_node_free(ctx->xml, mo); - - wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname); - write_summary(ctx, "A new PPS MO added as '%s'", fname); - - ret = download_trust_roots(ctx, fname); - if (ret < 0) { - wpa_printf(MSG_INFO, "Remove invalid PPS MO file"); - write_summary(ctx, "Remove invalid PPS MO file"); - unlink(fname); - } - - return ret; -} - - -int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname, - xml_node_t *pps) -{ - char *str; - FILE *f; - char backup[300]; - - if (ctx->client_cert_present) { - xml_node_t *cert; - cert = get_child_node(ctx->xml, pps, - "Credential/DigitalCertificate/" - "CertSHA256Fingerprint"); - if (cert && os_file_exists("Cert/est_cert.der") && - process_est_cert(ctx, cert, ctx->fqdn) < 0) { - wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update"); - return -1; - } - } - - wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname); - - str = xml_node_to_str(ctx->xml, pps); - if (str == NULL) { - wpa_printf(MSG_ERROR, "No node found"); - return -1; - } - wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str); - - snprintf(backup, sizeof(backup), "%s.bak", pps_fname); - rename(pps_fname, backup); - f = fopen(pps_fname, "w"); - if (f == NULL) { - wpa_printf(MSG_INFO, "Could not write PPS"); - rename(backup, pps_fname); - free(str); - return -1; - } - fprintf(f, "%s\n", str); - fclose(f); - - free(str); - - return 0; -} - - -void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps, - const char *alt_loc, char **user, char **pw) -{ - xml_node_t *node; - - node = get_child_node(ctx->xml, pps, - "Credential/UsernamePassword/Username"); - if (node) - *user = xml_node_get_text(ctx->xml, node); - - node = get_child_node(ctx->xml, pps, - "Credential/UsernamePassword/Password"); - if (node) - *pw = xml_node_get_base64_text(ctx->xml, node, NULL); - - node = get_child_node(ctx->xml, pps, alt_loc); - if (node) { - xml_node_t *a; - a = get_node(ctx->xml, node, "Username"); - if (a) { - xml_node_get_text_free(ctx->xml, *user); - *user = xml_node_get_text(ctx->xml, a); - wpa_printf(MSG_INFO, "Use OSU username '%s'", *user); - } - - a = get_node(ctx->xml, node, "Password"); - if (a) { - free(*pw); - *pw = xml_node_get_base64_text(ctx->xml, a, NULL); - wpa_printf(MSG_INFO, "Use OSU password"); - } - } -} - - /* Remove old credentials based on HomeSP/FQDN */ static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn) { @@ -1874,14 +1297,13 @@ static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps, wpa_printf(MSG_INFO, "Failed to set provisioning_sp"); wpa_printf(MSG_INFO, "credential localname: '%s'", name); set_pps_credential(ctx, id, child, fqdn); - ctx->pps_cred_set = 1; } xml_node_get_text_free(ctx->xml, update_identifier); } -void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname) +static void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname) { xml_node_t *pps; const char *fqdn; @@ -1988,1167 +1410,20 @@ static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname, } -struct osu_icon { - int id; - char lang[4]; - char mime_type[256]; - char filename[256]; -}; - -struct osu_data { - char bssid[20]; - char url[256]; - unsigned int methods; - char osu_ssid[33]; - char osu_ssid2[33]; - char osu_nai[256]; - char osu_nai2[256]; - struct osu_lang_text friendly_name[MAX_OSU_VALS]; - size_t friendly_name_count; - struct osu_lang_text serv_desc[MAX_OSU_VALS]; - size_t serv_desc_count; - struct osu_icon icon[MAX_OSU_VALS]; - size_t icon_count; -}; - - -static struct osu_data * parse_osu_providers(const char *fname, size_t *count) -{ - FILE *f; - char buf[1000]; - struct osu_data *osu = NULL, *last = NULL; - size_t osu_count = 0; - char *pos, *end; - int res; - - f = fopen(fname, "r"); - if (f == NULL) { - wpa_printf(MSG_ERROR, "Could not open %s", fname); - return NULL; - } - - while (fgets(buf, sizeof(buf), f)) { - pos = strchr(buf, '\n'); - if (pos) - *pos = '\0'; - - if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) { - last = realloc(osu, (osu_count + 1) * sizeof(*osu)); - if (last == NULL) - break; - osu = last; - last = &osu[osu_count++]; - memset(last, 0, sizeof(*last)); - res = os_snprintf(last->bssid, sizeof(last->bssid), - "%s", buf + 13); - if (os_snprintf_error(sizeof(last->bssid), res)) - break; - continue; - } - if (!last) - continue; - - if (strncmp(buf, "uri=", 4) == 0) { - res = os_snprintf(last->url, sizeof(last->url), - "%s", buf + 4); - if (os_snprintf_error(sizeof(last->url), res)) - break; - continue; - } - - if (strncmp(buf, "methods=", 8) == 0) { - last->methods = strtol(buf + 8, NULL, 16); - continue; - } - - if (strncmp(buf, "osu_ssid=", 9) == 0) { - res = os_snprintf(last->osu_ssid, - sizeof(last->osu_ssid), - "%s", buf + 9); - if (os_snprintf_error(sizeof(last->osu_ssid), res)) - break; - continue; - } - - if (strncmp(buf, "osu_ssid2=", 10) == 0) { - res = os_snprintf(last->osu_ssid2, - sizeof(last->osu_ssid2), - "%s", buf + 10); - if (os_snprintf_error(sizeof(last->osu_ssid2), res)) - break; - continue; - } - - if (os_strncmp(buf, "osu_nai=", 8) == 0) { - res = os_snprintf(last->osu_nai, sizeof(last->osu_nai), - "%s", buf + 8); - if (os_snprintf_error(sizeof(last->osu_nai), res)) - break; - continue; - } - - if (os_strncmp(buf, "osu_nai2=", 9) == 0) { - res = os_snprintf(last->osu_nai2, - sizeof(last->osu_nai2), - "%s", buf + 9); - if (os_snprintf_error(sizeof(last->osu_nai2), res)) - break; - continue; - } - - if (strncmp(buf, "friendly_name=", 14) == 0) { - struct osu_lang_text *txt; - if (last->friendly_name_count == MAX_OSU_VALS) - continue; - pos = strchr(buf + 14, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - txt = &last->friendly_name[last->friendly_name_count++]; - res = os_snprintf(txt->lang, sizeof(txt->lang), - "%s", buf + 14); - if (os_snprintf_error(sizeof(txt->lang), res)) - break; - res = os_snprintf(txt->text, sizeof(txt->text), - "%s", pos); - if (os_snprintf_error(sizeof(txt->text), res)) - break; - } - - if (strncmp(buf, "desc=", 5) == 0) { - struct osu_lang_text *txt; - if (last->serv_desc_count == MAX_OSU_VALS) - continue; - pos = strchr(buf + 5, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - txt = &last->serv_desc[last->serv_desc_count++]; - res = os_snprintf(txt->lang, sizeof(txt->lang), - "%s", buf + 5); - if (os_snprintf_error(sizeof(txt->lang), res)) - break; - res = os_snprintf(txt->text, sizeof(txt->text), - "%s", pos); - if (os_snprintf_error(sizeof(txt->text), res)) - break; - } - - if (strncmp(buf, "icon=", 5) == 0) { - struct osu_icon *icon; - if (last->icon_count == MAX_OSU_VALS) - continue; - icon = &last->icon[last->icon_count++]; - icon->id = atoi(buf + 5); - pos = strchr(buf, ':'); - if (pos == NULL) - continue; - pos = strchr(pos + 1, ':'); - if (pos == NULL) - continue; - pos = strchr(pos + 1, ':'); - if (pos == NULL) - continue; - pos++; - end = strchr(pos, ':'); - if (!end) - continue; - *end = '\0'; - res = os_snprintf(icon->lang, sizeof(icon->lang), - "%s", pos); - if (os_snprintf_error(sizeof(icon->lang), res)) - break; - pos = end + 1; - - end = strchr(pos, ':'); - if (end) - *end = '\0'; - res = os_snprintf(icon->mime_type, - sizeof(icon->mime_type), "%s", pos); - if (os_snprintf_error(sizeof(icon->mime_type), res)) - break; - if (!end) - continue; - pos = end + 1; - - end = strchr(pos, ':'); - if (end) - *end = '\0'; - res = os_snprintf(icon->filename, - sizeof(icon->filename), "%s", pos); - if (os_snprintf_error(sizeof(icon->filename), res)) - break; - continue; - } - } - - fclose(f); - - *count = osu_count; - return osu; -} - - -static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, - const char *ssid, const char *ssid2, const char *url, - unsigned int methods, int no_prod_assoc, - const char *osu_nai, const char *osu_nai2) -{ - int id; - const char *ifname = ctx->ifname; - char buf[200]; - struct wpa_ctrl *mon; - int res; - - if (ssid2 && ssid2[0] == '\0') - ssid2 = NULL; - - if (ctx->osu_ssid) { - if (os_strcmp(ssid, ctx->osu_ssid) == 0) { - wpa_printf(MSG_DEBUG, - "Enforced OSU SSID matches ANQP info"); - ssid2 = NULL; - } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { - wpa_printf(MSG_DEBUG, - "Enforced OSU SSID matches RSN[OSEN] info"); - ssid = ssid2; - } else { - wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); - write_summary(ctx, "Enforced OSU SSID did not match"); - return -1; - } - } - - id = add_network(ifname); - if (id < 0) - return -1; - if (set_network_quoted(ifname, id, "ssid", ssid) < 0) - return -1; - if (ssid2) - osu_nai = osu_nai2; - if (osu_nai && os_strlen(osu_nai) > 0) { - char dir[255], fname[300]; - if (getcwd(dir, sizeof(dir)) == NULL) - return -1; - os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); - - if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) - return -1; - - if (set_network(ifname, id, "proto", "OSEN") < 0 || - set_network(ifname, id, "key_mgmt", "OSEN") < 0 || - set_network(ifname, id, "pairwise", "CCMP") < 0 || - set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || - set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || - set_network(ifname, id, "ocsp", "2") < 0 || - set_network_quoted(ifname, id, "identity", osu_nai) < 0 || - set_network_quoted(ifname, id, "ca_cert", fname) < 0) - return -1; - } else if (ssid2) { - wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); - write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); - return -1; - } else { - if (set_network(ifname, id, "key_mgmt", "NONE") < 0) - return -1; - } - - mon = open_wpa_mon(ifname); - if (mon == NULL) - return -1; - - wpa_printf(MSG_INFO, "Associate with OSU SSID"); - write_summary(ctx, "Associate with OSU SSID"); - snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id); - if (wpa_command(ifname, buf) < 0) - return -1; - - res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED", - buf, sizeof(buf)); - - wpa_ctrl_detach(mon); - wpa_ctrl_close(mon); - - if (res < 0) { - wpa_printf(MSG_INFO, "Could not connect to OSU network"); - write_summary(ctx, "Could not connect to OSU network"); - wpa_printf(MSG_INFO, "Remove OSU network connection"); - snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); - wpa_command(ifname, buf); - return -1; - } - - write_summary(ctx, "Waiting for IP address for subscription registration"); - if (wait_ip_addr(ifname, 15) < 0) { - wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); - } - - if (no_prod_assoc) { - if (res < 0) - return -1; - wpa_printf(MSG_INFO, "No production connection used for testing purposes"); - write_summary(ctx, "No production connection used for testing purposes"); - return 0; - } - - ctx->no_reconnect = 1; - if (methods & 0x02) { - wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect"); - res = cmd_prov(ctx, url); - } else if (methods & 0x01) { - wpa_printf(MSG_DEBUG, - "Calling cmd_oma_dm_prov from osu_connect"); - res = cmd_oma_dm_prov(ctx, url); - } - - wpa_printf(MSG_INFO, "Remove OSU network connection"); - write_summary(ctx, "Remove OSU network connection"); - snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id); - wpa_command(ifname, buf); - - if (res < 0) - return -1; - - wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); - write_summary(ctx, "Requesting reconnection with updated configuration"); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) { - wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect"); - write_summary(ctx, "Failed to request wpa_supplicant to reconnect"); - return -1; - } - - return 0; -} - - -static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, - int connect, int no_prod_assoc, - const char *friendly_name) -{ - char fname[255]; - FILE *f; - struct osu_data *osu = NULL, *last = NULL; - size_t osu_count = 0, i, j; - int ret; - - write_summary(ctx, "OSU provider selection"); - - if (dir == NULL) { - wpa_printf(MSG_INFO, "Missing dir parameter to osu_select"); - return -1; - } - - snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir); - osu = parse_osu_providers(fname, &osu_count); - if (osu == NULL) { - wpa_printf(MSG_INFO, "Could not find any OSU providers from %s", - fname); - write_result(ctx, "No OSU providers available"); - return -1; - } - - if (friendly_name) { - for (i = 0; i < osu_count; i++) { - last = &osu[i]; - for (j = 0; j < last->friendly_name_count; j++) { - if (os_strcmp(last->friendly_name[j].text, - friendly_name) == 0) - break; - } - if (j < last->friendly_name_count) - break; - } - if (i == osu_count) { - wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers", - friendly_name); - write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers", - friendly_name); - free(osu); - return -1; - } - - wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'", - friendly_name); - write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'", - friendly_name); - ret = i + 1; - goto selected; - } - - snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir); - f = fopen(fname, "w"); - if (f == NULL) { - wpa_printf(MSG_INFO, "Could not open %s", fname); - free(osu); - return -1; - } - - fprintf(f, "" - "Select service operator" - "

Select service operator

\n"); - - if (osu_count == 0) - fprintf(f, "No online signup available\n"); - - for (i = 0; i < osu_count; i++) { - last = &osu[i]; -#ifdef ANDROID - fprintf(f, "

\n" - "" - "
", (int) i + 1); -#else /* ANDROID */ - fprintf(f, "

\n" - "" - "
", (int) i + 1); -#endif /* ANDROID */ - for (j = 0; j < last->icon_count; j++) { - fprintf(f, "\n", - last->icon[j].id, - strcasecmp(last->icon[j].mime_type, - "image/png") == 0 ? "png" : "icon"); - } - fprintf(f, ""); - for (j = 0; j < last->friendly_name_count; j++) { - fprintf(f, "[%s] %s
\n", - last->friendly_name[j].lang, - last->friendly_name[j].text); - } - fprintf(f, "
"); - for (j = 0; j < last->serv_desc_count; j++) { - fprintf(f, "[%s] %s
\n", - last->serv_desc[j].lang, - last->serv_desc[j].text); - } - fprintf(f, "

BSSID: %s
\n" - "SSID: %s
\n", - last->bssid, last->osu_ssid); - if (last->osu_ssid2[0]) - fprintf(f, "SSID2: %s
\n", last->osu_ssid2); - if (last->osu_nai[0]) - fprintf(f, "NAI: %s
\n", last->osu_nai); - if (last->osu_nai2[0]) - fprintf(f, "NAI2: %s
\n", last->osu_nai2); - fprintf(f, "URL: %s
\n" - "methods:%s%s
\n" - "

\n", - last->url, - last->methods & 0x01 ? " OMA-DM" : "", - last->methods & 0x02 ? " SOAP-XML-SPP" : ""); - } - - fprintf(f, "\n"); - - fclose(f); - - snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir); - write_summary(ctx, "Start web browser with OSU provider selection page"); - ret = hs20_web_browser(fname, 0); - -selected: - if (ret > 0 && (size_t) ret <= osu_count) { - char *data; - size_t data_len; - - wpa_printf(MSG_INFO, "Selected OSU id=%d", ret); - last = &osu[ret - 1]; - ret = 0; - wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); - wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); - if (last->osu_ssid2[0]) - wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); - wpa_printf(MSG_INFO, "URL: %s", last->url); - write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", - ret, last->bssid, last->osu_ssid, last->url); - - ctx->friendly_name_count = last->friendly_name_count; - for (j = 0; j < last->friendly_name_count; j++) { - wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s", - last->friendly_name[j].lang, - last->friendly_name[j].text); - os_strlcpy(ctx->friendly_name[j].lang, - last->friendly_name[j].lang, - sizeof(ctx->friendly_name[j].lang)); - os_strlcpy(ctx->friendly_name[j].text, - last->friendly_name[j].text, - sizeof(ctx->friendly_name[j].text)); - } - - ctx->icon_count = last->icon_count; - for (j = 0; j < last->icon_count; j++) { - char fname[256]; - - os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s", - dir, last->icon[j].id, - strcasecmp(last->icon[j].mime_type, - "image/png") == 0 ? - "png" : "icon"); - wpa_printf(MSG_INFO, "ICON: %s (%s)", - fname, last->icon[j].filename); - os_strlcpy(ctx->icon_filename[j], - last->icon[j].filename, - sizeof(ctx->icon_filename[j])); - - data = os_readfile(fname, &data_len); - if (data) { - sha256_vector(1, (const u8 **) &data, &data_len, - ctx->icon_hash[j]); - os_free(data); - } - } - - if (connect == 2) { - if (last->methods & 0x02) { - wpa_printf(MSG_DEBUG, - "Calling cmd_prov from cmd_osu_select"); - ret = cmd_prov(ctx, last->url); - } else if (last->methods & 0x01) { - wpa_printf(MSG_DEBUG, - "Calling cmd_oma_dm_prov from cmd_osu_select"); - ret = cmd_oma_dm_prov(ctx, last->url); - } else { - wpa_printf(MSG_DEBUG, - "No supported OSU provisioning method"); - ret = -1; - } - } else if (connect) { - ret = osu_connect(ctx, last->bssid, last->osu_ssid, - last->osu_ssid2, - last->url, last->methods, - no_prod_assoc, last->osu_nai, - last->osu_nai2); - } - } else - ret = -1; - - free(osu); - - return ret; -} - - -static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, - const char *friendly_name) -{ - char dir[255]; - char fname[300], buf[400]; - struct wpa_ctrl *mon; - const char *ifname; - int res; - - ifname = ctx->ifname; - - if (getcwd(dir, sizeof(dir)) == NULL) - return -1; - - snprintf(fname, sizeof(fname), "%s/osu-info", dir); - if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 && - errno != EEXIST) { - wpa_printf(MSG_INFO, "mkdir(%s) failed: %s", - fname, strerror(errno)); - return -1; - } - - android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); - if (wpa_command(ifname, buf) < 0) { - wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant"); - return -1; - } - - mon = open_wpa_mon(ifname); - if (mon == NULL) - return -1; - - wpa_printf(MSG_INFO, "Starting OSU fetch"); - write_summary(ctx, "Starting OSU provider information fetch"); - if (wpa_command(ifname, "FETCH_OSU") < 0) { - wpa_printf(MSG_INFO, "Could not start OSU fetch"); - wpa_ctrl_detach(mon); - wpa_ctrl_close(mon); - return -1; - } - res = get_wpa_cli_event(mon, "OSU provider fetch completed", - buf, sizeof(buf)); - - wpa_ctrl_detach(mon); - wpa_ctrl_close(mon); - - if (res < 0) { - wpa_printf(MSG_INFO, "OSU fetch did not complete"); - write_summary(ctx, "OSU fetch did not complete"); - return -1; - } - wpa_printf(MSG_INFO, "OSU provider fetch completed"); - - return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name); -} - - -static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, const char *ca_fname) -{ - xml_node_t *pps, *node; - char pps_fname_buf[300]; - char ca_fname_buf[200]; - char *cred_username = NULL; - char *cred_password = NULL; - char *sub_rem_uri = NULL; - char client_cert_buf[200]; - char *client_cert = NULL; - char client_key_buf[200]; - char *client_key = NULL; - int spp; - - wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s", - address); - - if (!pps_fname) { - char buf[256]; - wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); - if (os_strncmp(address, "fqdn=", 5) == 0) { - wpa_printf(MSG_INFO, "Use requested FQDN from command line"); - os_snprintf(buf, sizeof(buf), "%s", address + 5); - address = NULL; - } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, - sizeof(buf)) < 0) { - wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); - return -1; - } - os_free(ctx->fqdn); - ctx->fqdn = os_strdup(buf); - if (ctx->fqdn == NULL) - return -1; - wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", - buf); - os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), - "SP/%s/pps.xml", ctx->fqdn); - pps_fname = pps_fname_buf; - - os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem", - ctx->fqdn); - ca_fname = ca_fname_buf; - } - - if (!os_file_exists(pps_fname)) { - wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", - pps_fname); - return -1; - } - wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); - - if (ca_fname && !os_file_exists(ca_fname)) { - wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", - ca_fname); - return -1; - } - wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); - ctx->ca_fname = ca_fname; - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "Could not read PPS MO"); - return -1; - } - - if (!ctx->fqdn) { - char *tmp; - node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); - return -1; - } - tmp = xml_node_get_text(ctx->xml, node); - if (tmp == NULL) { - wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); - return -1; - } - ctx->fqdn = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); - if (!ctx->fqdn) { - wpa_printf(MSG_INFO, "No FQDN known"); - return -1; - } - } - - node = get_child_node(ctx->xml, pps, - "SubscriptionUpdate/UpdateMethod"); - if (node) { - char *tmp; - tmp = xml_node_get_text(ctx->xml, node); - if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) - spp = 0; - else - spp = 1; - } else { - wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); - spp = 1; - } - - get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword", - &cred_username, &cred_password); - if (cred_username) - wpa_printf(MSG_INFO, "Using username: %s", cred_username); - if (cred_password) - wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); - - if (cred_username == NULL && cred_password == NULL && - get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { - wpa_printf(MSG_INFO, "Using client certificate"); - os_snprintf(client_cert_buf, sizeof(client_cert_buf), - "SP/%s/client-cert.pem", ctx->fqdn); - client_cert = client_cert_buf; - os_snprintf(client_key_buf, sizeof(client_key_buf), - "SP/%s/client-key.pem", ctx->fqdn); - client_key = client_key_buf; - ctx->client_cert_present = 1; - } - - node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI"); - if (node) { - sub_rem_uri = xml_node_get_text(ctx->xml, node); - if (sub_rem_uri && - (!address || os_strcmp(address, sub_rem_uri) != 0)) { - wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s", - sub_rem_uri); - address = sub_rem_uri; - } - } - if (!address) { - wpa_printf(MSG_INFO, "Server URL not known"); - return -1; - } - - write_summary(ctx, "Wait for IP address for subscriptiom remediation"); - wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation"); - - if (wait_ip_addr(ctx->ifname, 15) < 0) { - wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); - } - - if (spp) - spp_sub_rem(ctx, address, pps_fname, - client_cert, client_key, - cred_username, cred_password, pps); - else - oma_dm_sub_rem(ctx, address, pps_fname, - client_cert, client_key, - cred_username, cred_password, pps); - - xml_node_get_text_free(ctx->xml, sub_rem_uri); - xml_node_get_text_free(ctx->xml, cred_username); - str_clear_free(cred_password); - xml_node_free(ctx->xml, pps); - return 0; -} - - -static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, const char *ca_fname) -{ - xml_node_t *pps; - xml_node_t *node; - char pps_fname_buf[300]; - char ca_fname_buf[200]; - char *uri = NULL; - char *cred_username = NULL; - char *cred_password = NULL; - char client_cert_buf[200]; - char *client_cert = NULL; - char client_key_buf[200]; - char *client_key = NULL; - int spp; - - wpa_printf(MSG_INFO, "Policy update requested"); - - if (!pps_fname) { - char buf[256]; - int res; - - wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); - if (address && os_strncmp(address, "fqdn=", 5) == 0) { - wpa_printf(MSG_INFO, "Use requested FQDN from command line"); - os_snprintf(buf, sizeof(buf), "%s", address + 5); - address = NULL; - } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf, - sizeof(buf)) < 0) { - wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant"); - return -1; - } - os_free(ctx->fqdn); - ctx->fqdn = os_strdup(buf); - if (ctx->fqdn == NULL) - return -1; - wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s", - buf); - os_snprintf(pps_fname_buf, sizeof(pps_fname_buf), - "SP/%s/pps.xml", ctx->fqdn); - pps_fname = pps_fname_buf; - - res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), - "SP/%s/ca.pem", buf); - if (os_snprintf_error(sizeof(ca_fname_buf), res)) { - os_free(ctx->fqdn); - ctx->fqdn = NULL; - return -1; - } - ca_fname = ca_fname_buf; - } - - if (!os_file_exists(pps_fname)) { - wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible", - pps_fname); - return -1; - } - wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname); - - if (ca_fname && !os_file_exists(ca_fname)) { - wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible", - ca_fname); - return -1; - } - wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname); - ctx->ca_fname = ca_fname; - - pps = node_from_file(ctx->xml, pps_fname); - if (pps == NULL) { - wpa_printf(MSG_INFO, "Could not read PPS MO"); - return -1; - } - - if (!ctx->fqdn) { - char *tmp; - node = get_child_node(ctx->xml, pps, "HomeSP/FQDN"); - if (node == NULL) { - wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS"); - return -1; - } - tmp = xml_node_get_text(ctx->xml, node); - if (tmp == NULL) { - wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS"); - return -1; - } - ctx->fqdn = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); - if (!ctx->fqdn) { - wpa_printf(MSG_INFO, "No FQDN known"); - return -1; - } - } - - node = get_child_node(ctx->xml, pps, - "Policy/PolicyUpdate/UpdateMethod"); - if (node) { - char *tmp; - tmp = xml_node_get_text(ctx->xml, node); - if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0) - spp = 0; - else - spp = 1; - } else { - wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP"); - spp = 1; - } - - get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword", - &cred_username, &cred_password); - if (cred_username) - wpa_printf(MSG_INFO, "Using username: %s", cred_username); - if (cred_password) - wpa_printf(MSG_DEBUG, "Using password: %s", cred_password); - - if (cred_username == NULL && cred_password == NULL && - get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) { - wpa_printf(MSG_INFO, "Using client certificate"); - os_snprintf(client_cert_buf, sizeof(client_cert_buf), - "SP/%s/client-cert.pem", ctx->fqdn); - client_cert = client_cert_buf; - os_snprintf(client_key_buf, sizeof(client_key_buf), - "SP/%s/client-key.pem", ctx->fqdn); - client_key = client_key_buf; - } - - if (!address) { - node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI"); - if (node) { - uri = xml_node_get_text(ctx->xml, node); - wpa_printf(MSG_INFO, "URI based on PPS: %s", uri); - address = uri; - } - } - if (!address) { - wpa_printf(MSG_INFO, "Server URL not known"); - return -1; - } - - if (spp) - spp_pol_upd(ctx, address, pps_fname, - client_cert, client_key, - cred_username, cred_password, pps); - else - oma_dm_pol_upd(ctx, address, pps_fname, - client_cert, client_key, - cred_username, cred_password, pps); - - xml_node_get_text_free(ctx->xml, uri); - xml_node_get_text_free(ctx->xml, cred_username); - str_clear_free(cred_password); - xml_node_free(ctx->xml, pps); - - return 0; -} - - -static char * get_hostname(const char *url) -{ - const char *pos, *end, *end2; - char *ret; - - if (url == NULL) - return NULL; - - pos = os_strchr(url, '/'); - if (pos == NULL) - return NULL; - pos++; - if (*pos != '/') - return NULL; - pos++; - - end = os_strchr(pos, '/'); - end2 = os_strchr(pos, ':'); - if ((end && end2 && end2 < end) || (!end && end2)) - end = end2; - if (end) - end--; - else { - end = pos; - while (*end) - end++; - if (end > pos) - end--; - } - - ret = os_malloc(end - pos + 2); - if (ret == NULL) - return NULL; - - os_memcpy(ret, pos, end - pos + 1); - ret[end - pos + 1] = '\0'; - - return ret; -} - - -static int osu_cert_cb(void *_ctx, struct http_cert *cert) -{ - struct hs20_osu_client *ctx = _ctx; - size_t i, j; - int found; - char *host = NULL; - - wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)", - !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A", - ctx->server_url); - - if (ctx->no_osu_cert_validation && cert->url) - host = get_hostname(cert->url); - else - host = get_hostname(ctx->server_url); - - if (!ctx->no_osu_cert_validation) { - for (i = 0; i < ctx->server_dnsname_count; i++) - os_free(ctx->server_dnsname[i]); - os_free(ctx->server_dnsname); - ctx->server_dnsname = os_calloc(cert->num_dnsname, - sizeof(char *)); - ctx->server_dnsname_count = 0; - } - - found = 0; - for (i = 0; i < cert->num_dnsname; i++) { - if (!ctx->no_osu_cert_validation && ctx->server_dnsname) { - ctx->server_dnsname[ctx->server_dnsname_count] = - os_strdup(cert->dnsname[i]); - if (ctx->server_dnsname[ctx->server_dnsname_count]) - ctx->server_dnsname_count++; - } - if (host && os_strcasecmp(host, cert->dnsname[i]) == 0) - found = 1; - wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]); - } - - if (host && !found) { - wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection", - host); - write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection", - host); - os_free(host); - return -1; - } - - os_free(host); - - for (i = 0; i < cert->num_othername; i++) { - if (os_strcmp(cert->othername[i].oid, - "1.3.6.1.4.1.40808.1.1.1") == 0) { - wpa_hexdump_ascii(MSG_INFO, - "id-wfa-hotspot-friendlyName", - cert->othername[i].data, - cert->othername[i].len); - } - } - - for (j = 0; !ctx->no_osu_cert_validation && - j < ctx->friendly_name_count; j++) { - int found = 0; - for (i = 0; i < cert->num_othername; i++) { - if (os_strcmp(cert->othername[i].oid, - "1.3.6.1.4.1.40808.1.1.1") != 0) - continue; - if (cert->othername[i].len < 3) - continue; - if (os_strncasecmp((char *) cert->othername[i].data, - ctx->friendly_name[j].lang, 3) != 0) - continue; - if (os_strncmp((char *) cert->othername[i].data + 3, - ctx->friendly_name[j].text, - cert->othername[i].len - 3) == 0) { - found = 1; - break; - } - } - - if (!found) { - wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'", - ctx->friendly_name[j].lang, - ctx->friendly_name[j].text); - write_result(ctx, "No friendly name match found for '[%s]%s'", - ctx->friendly_name[j].lang, - ctx->friendly_name[j].text); - return -1; - } - } - - for (i = 0; i < cert->num_logo; i++) { - struct http_logo *logo = &cert->logo[i]; - - wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'", - logo->alg_oid, logo->uri); - wpa_hexdump_ascii(MSG_INFO, "hashValue", - logo->hash, logo->hash_len); - } - - for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { - int found = 0; - char *name = ctx->icon_filename[j]; - size_t name_len = os_strlen(name); - - wpa_printf(MSG_INFO, - "[%zu] Looking for icon file name '%s' match", - j, name); - for (i = 0; i < cert->num_logo; i++) { - struct http_logo *logo = &cert->logo[i]; - size_t uri_len = os_strlen(logo->uri); - char *pos; - - wpa_printf(MSG_INFO, - "[%zu] Comparing to '%s' uri_len=%d name_len=%d", - i, logo->uri, (int) uri_len, (int) name_len); - if (uri_len < 1 + name_len) { - wpa_printf(MSG_INFO, "URI Length is too short"); - continue; - } - pos = &logo->uri[uri_len - name_len - 1]; - if (*pos != '/') - continue; - pos++; - if (os_strcmp(pos, name) == 0) { - found = 1; - break; - } - } - - if (!found) { - wpa_printf(MSG_INFO, "No icon filename match found for '%s'", - name); - write_result(ctx, - "No icon filename match found for '%s'", - name); - return -1; - } - } - - for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) { - int found = 0; - - for (i = 0; i < cert->num_logo; i++) { - struct http_logo *logo = &cert->logo[i]; - - if (logo->hash_len != 32) { - wpa_printf(MSG_INFO, - "[%zu][%zu] Icon hash length invalid (should be 32): %d", - j, i, (int) logo->hash_len); - continue; - } - if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) { - found = 1; - break; - } - - wpa_printf(MSG_DEBUG, - "[%zu][%zu] Icon hash did not match", j, i); - wpa_hexdump_ascii(MSG_DEBUG, "logo->hash", - logo->hash, 32); - wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]", - ctx->icon_hash[j], 32); - } - - if (!found) { - wpa_printf(MSG_INFO, - "No icon hash match (by hash) found"); - write_result(ctx, - "No icon hash match (by hash) found"); - return -1; - } - } - - return 0; -} - - static int init_ctx(struct hs20_osu_client *ctx) { - xml_node_t *devinfo, *devid; - os_memset(ctx, 0, sizeof(*ctx)); ctx->ifname = "wlan0"; ctx->xml = xml_node_init_ctx(ctx, NULL); if (ctx->xml == NULL) return -1; - devinfo = node_from_file(ctx->xml, "devinfo.xml"); - if (devinfo) { - devid = get_node(ctx->xml, devinfo, "DevId"); - if (devid) { - char *tmp = xml_node_get_text(ctx->xml, devid); - - if (tmp) { - ctx->devid = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); - } - } - xml_node_free(ctx->xml, devinfo); - } - ctx->http = http_init_ctx(ctx, ctx->xml); if (ctx->http == NULL) { xml_node_deinit_ctx(ctx->xml); return -1; } http_ocsp_set(ctx->http, 2); - http_set_cert_cb(ctx->http, osu_cert_cb, ctx); return 0; } @@ -3156,17 +1431,8 @@ static int init_ctx(struct hs20_osu_client *ctx) static void deinit_ctx(struct hs20_osu_client *ctx) { - size_t i; - http_deinit_ctx(ctx->http); xml_node_deinit_ctx(ctx->xml); - os_free(ctx->fqdn); - os_free(ctx->server_url); - os_free(ctx->devid); - - for (i = 0; i < ctx->server_dnsname_count; i++) - os_free(ctx->server_dnsname[i]); - os_free(ctx->server_dnsname); } @@ -3209,19 +1475,8 @@ static void usage(void) "- from_tnds \n" "- set_pps \n" "- get_fqdn \n" - "- pol_upd [Server URL] [PPS] [CA cert]\n" - "- sub_rem [PPS] [CA cert]\n" - "- prov [CA cert]\n" - "- oma_dm_prov [CA cert]\n" - "- sim_prov [CA cert]\n" - "- oma_dm_sim_prov [CA cert]\n" - "- signup [CA cert]\n" - "- dl_osu_ca \n" - "- dl_polupd_ca \n" "- dl_aaa_ca \n" - "- browser \n" - "- parse_cert \n" - "- osu_select [CA cert]\n"); + "- browser \n"); } @@ -3230,8 +1485,6 @@ int main(int argc, char *argv[]) struct hs20_osu_client ctx; int c; int ret = 0; - int no_prod_assoc = 0; - const char *friendly_name = NULL; const char *wpa_debug_file_path = NULL; extern char *wpas_ctrl_path; extern int wpa_debug_level; @@ -3242,7 +1495,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:"); + c = getopt(argc, argv, "df:hKqr:s:S:tTw:"); if (c < 0) break; switch (c) { @@ -3256,15 +1509,6 @@ int main(int argc, char *argv[]) case 'K': wpa_debug_show_keys++; break; - case 'N': - no_prod_assoc = 1; - break; - case 'o': - ctx.osu_ssid = optarg; - break; - case 'O': - friendly_name = optarg; - break; case 'q': wpa_debug_level++; break; @@ -3286,9 +1530,6 @@ int main(int argc, char *argv[]) case 'w': wpas_ctrl_path = optarg; break; - case 'x': - spp_xsd_fname = optarg; - break; case 'h': default: usage(); @@ -3335,62 +1576,12 @@ int main(int argc, char *argv[]) exit(0); } cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]); - } else if (strcmp(argv[optind], "sub_rem") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ret = cmd_sub_rem(&ctx, argv[optind + 1], - argc > optind + 2 ? argv[optind + 2] : NULL, - argc > optind + 3 ? argv[optind + 3] : NULL); - } else if (strcmp(argv[optind], "pol_upd") == 0) { - ret = cmd_pol_upd(&ctx, - argc > optind + 1 ? argv[optind + 1] : NULL, - argc > optind + 2 ? argv[optind + 2] : NULL, - argc > optind + 3 ? argv[optind + 3] : NULL); - } else if (strcmp(argv[optind], "prov") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ctx.ca_fname = argv[optind + 2]; - wpa_printf(MSG_DEBUG, "Calling cmd_prov from main"); - cmd_prov(&ctx, argv[optind + 1]); - } else if (strcmp(argv[optind], "sim_prov") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ctx.ca_fname = argv[optind + 2]; - cmd_sim_prov(&ctx, argv[optind + 1]); - } else if (strcmp(argv[optind], "dl_osu_ca") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]); - } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]); } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) { if (argc - optind < 2) { usage(); exit(0); } cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]); - } else if (strcmp(argv[optind], "osu_select") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL; - cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL); - } else if (strcmp(argv[optind], "signup") == 0) { - ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL; - ret = cmd_signup(&ctx, no_prod_assoc, friendly_name); } else if (strcmp(argv[optind], "set_pps") == 0) { if (argc - optind < 2) { usage(); @@ -3403,42 +1594,6 @@ int main(int argc, char *argv[]) exit(0); } ret = cmd_get_fqdn(&ctx, argv[optind + 1]); - } else if (strcmp(argv[optind], "oma_dm_prov") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ctx.ca_fname = argv[optind + 2]; - cmd_oma_dm_prov(&ctx, argv[optind + 1]); - } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - ctx.ca_fname = argv[optind + 2]; - if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) { - write_summary(&ctx, "Failed to complete OMA DM SIM provisioning"); - return -1; - } - } else if (strcmp(argv[optind], "oma_dm_add") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]); - } else if (strcmp(argv[optind], "oma_dm_replace") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]); - } else if (strcmp(argv[optind], "est_csr") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - mkdir("Cert", S_IRWXU); - est_build_csr(&ctx, argv[optind + 1]); } else if (strcmp(argv[optind], "browser") == 0) { int ret; @@ -3451,15 +1606,6 @@ int main(int argc, char *argv[]) argv[optind + 1]); ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls); wpa_printf(MSG_INFO, "Web browser result: %d", ret); - } else if (strcmp(argv[optind], "parse_cert") == 0) { - if (argc - optind < 2) { - usage(); - exit(0); - } - - wpa_debug_level = MSG_MSGDUMP; - http_parse_x509_certificate(ctx.http, argv[optind + 1]); - wpa_debug_level = MSG_INFO; } else { wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]); } diff --git a/contrib/wpa/hs20/client/osu_client.h b/contrib/wpa/hs20/client/osu_client.h index 9b45b03febe2..3bfbb0dd9e1b 100644 --- a/contrib/wpa/hs20/client/osu_client.h +++ b/contrib/wpa/hs20/client/osu_client.h @@ -9,113 +9,16 @@ #ifndef OSU_CLIENT_H #define OSU_CLIENT_H -#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp" - -#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0" -#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0" -#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0" -#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0" - - -#define MAX_OSU_VALS 10 - -struct osu_lang_text { - char lang[4]; - char text[253]; -}; - struct hs20_osu_client { struct xml_node_ctx *xml; struct http_ctx *http; - int no_reconnect; - char pps_fname[300]; - char *devid; const char *result_file; const char *summary_file; const char *ifname; - const char *ca_fname; - int no_osu_cert_validation; /* for EST operations */ - char *fqdn; - char *server_url; - struct osu_lang_text friendly_name[MAX_OSU_VALS]; - size_t friendly_name_count; - size_t icon_count; - char icon_filename[MAX_OSU_VALS][256]; - u8 icon_hash[MAX_OSU_VALS][32]; - int pps_cred_set; - int pps_updated; - int client_cert_present; - char **server_dnsname; - size_t server_dnsname_count; - const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */ #define WORKAROUND_OCSP_OPTIONAL 0x00000001 unsigned long int workarounds; int ignore_tls; /* whether to ignore TLS validation issues with HTTPS * server certificate */ }; - -/* osu_client.c */ - -void write_result(struct hs20_osu_client *ctx, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); -void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...) - __attribute__ ((format (printf, 2, 3))); - -void debug_dump_node(struct hs20_osu_client *ctx, const char *title, - xml_node_t *node); -int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert); -int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, - xml_node_t *add_mo, char *fname, size_t fname_len); -void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps, - const char *alt_loc, char **user, char **pw); -int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname, - xml_node_t *pps); -void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname); - - -/* spp_client.c */ - -void spp_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps); -void spp_pol_upd(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps); -int cmd_prov(struct hs20_osu_client *ctx, const char *url); -int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url); - - -/* oma_dm_client.c */ - -int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url); -int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url); -void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps); -void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps); -void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname); -void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname, - const char *add_fname); -void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname, - const char *replace_fname); - -/* est.c */ - -int est_load_cacerts(struct hs20_osu_client *ctx, const char *url); -int est_build_csr(struct hs20_osu_client *ctx, const char *url); -int est_simple_enroll(struct hs20_osu_client *ctx, const char *url, - const char *user, const char *pw); - #endif /* OSU_CLIENT_H */ diff --git a/contrib/wpa/hs20/client/spp_client.c b/contrib/wpa/hs20/client/spp_client.c deleted file mode 100644 index 194518e5aba7..000000000000 --- a/contrib/wpa/hs20/client/spp_client.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* - * Hotspot 2.0 SPP client - * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include - -#include "common.h" -#include "browser.h" -#include "wpa_ctrl.h" -#include "wpa_helpers.h" -#include "xml-utils.h" -#include "http-utils.h" -#include "utils/base64.h" -#include "crypto/crypto.h" -#include "crypto/sha256.h" -#include "osu_client.h" - - -extern const char *spp_xsd_fname; - -static int hs20_spp_update_response(struct hs20_osu_client *ctx, - const char *session_id, - const char *spp_status, - const char *error_code); -static void hs20_policy_update_complete( - struct hs20_osu_client *ctx, const char *pps_fname); - - -static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, - char *attr_name) -{ - return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name); -} - - -static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node, - const char *expected_name) -{ - struct xml_node_ctx *xctx = ctx->xml; - const char *name; - char *err; - int ret; - - if (!xml_node_is_element(xctx, node)) - return -1; - - name = xml_node_get_localname(xctx, node); - if (name == NULL) - return -1; - - if (strcmp(expected_name, name) != 0) { - wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')", - name, expected_name); - write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')", - name, expected_name); - return -1; - } - - ret = xml_validate(xctx, node, spp_xsd_fname, &err); - if (ret < 0) { - wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err); - write_summary(ctx, "SPP XML schema validation failed"); - os_free(err); - } - return ret; -} - - -static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns, - xml_node_t *parent, const char *urn, - const char *fname) -{ - xml_node_t *node; - xml_node_t *fnode, *tnds; - char *str; - - errno = 0; - fnode = node_from_file(ctx, fname); - if (!fnode) { - wpa_printf(MSG_ERROR, - "Failed to create XML node from file: %s, possible error: %s", - fname, strerror(errno)); - return; - } - tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2"); - xml_node_free(ctx, fnode); - if (!tnds) - return; - - str = xml_node_to_str(ctx, tnds); - xml_node_free(ctx, tnds); - if (str == NULL) - return; - - node = xml_node_create_text(ctx, parent, ns, "moContainer", str); - if (node) - xml_node_add_attr(ctx, node, ns, "moURN", urn); - os_free(str); -} - - -static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx, - xml_namespace_t **ret_ns, - const char *session_id, - const char *reason) -{ - xml_namespace_t *ns; - xml_node_t *spp_node; - - write_summary(ctx, "Building sppPostDevData requestReason='%s'", - reason); - spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, - "sppPostDevData"); - if (spp_node == NULL) - return NULL; - if (ret_ns) - *ret_ns = ns; - - xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); - xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason); - if (session_id) - xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", - session_id); - xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI", - "http://localhost:12345/"); - - xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions", - "1.0"); - xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList", - URN_HS20_PPS " " URN_OMA_DM_DEVINFO " " - URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT); - - add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO, - "devinfo.xml"); - add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL, - "devdetail.xml"); - - return spp_node; -} - - -static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps, - xml_node_t *update) -{ - xml_node_t *node, *parent, *tnds, *unode; - char *str; - const char *name; - char *uri, *pos; - char *cdata, *cdata_end; - size_t fqdn_len; - - wpa_printf(MSG_INFO, "Processing updateNode"); - debug_dump_node(ctx, "updateNode", update); - - uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI"); - if (uri == NULL) { - wpa_printf(MSG_INFO, "No managementTreeURI present"); - return -1; - } - wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri); - - name = os_strrchr(uri, '/'); - if (name == NULL) { - wpa_printf(MSG_INFO, "Unexpected URI"); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - name++; - wpa_printf(MSG_INFO, "Update interior node: '%s'", name); - - str = xml_node_get_text(ctx->xml, update); - if (str == NULL) { - wpa_printf(MSG_INFO, "Could not extract MO text"); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str); - cdata = strstr(str, ""); - if (cdata && cdata_end && cdata_end > cdata && - cdata < strstr(str, "MgmtTree") && - cdata_end > strstr(str, "/MgmtTree")) { - char *tmp; - wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container"); - tmp = strdup(cdata + 9); - if (tmp) { - cdata_end = strstr(tmp, "]]>"); - if (cdata_end) - *cdata_end = '\0'; - wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'", - tmp); - tnds = xml_node_from_buf(ctx->xml, tmp); - free(tmp); - } else - tnds = NULL; - } else - tnds = xml_node_from_buf(ctx->xml, str); - xml_node_get_text_free(ctx->xml, str); - if (tnds == NULL) { - wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text"); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - - unode = tnds_to_mo(ctx->xml, tnds); - xml_node_free(ctx->xml, tnds); - if (unode == NULL) { - wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text"); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - - debug_dump_node(ctx, "Parsed TNDS", unode); - - if (get_node_uri(ctx->xml, unode, name) == NULL) { - wpa_printf(MSG_INFO, "[hs20] %s node not found", name); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - - if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) { - wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi"); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - pos = uri + 8; - - if (ctx->fqdn == NULL) { - wpa_printf(MSG_INFO, "FQDN not known"); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - fqdn_len = os_strlen(ctx->fqdn); - if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 || - pos[fqdn_len] != '/') { - wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s", - ctx->fqdn); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - pos += fqdn_len + 1; - - if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) { - wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription", - ctx->fqdn); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - pos += 24; - - wpa_printf(MSG_INFO, "Update command for PPS node %s", pos); - - node = get_node(ctx->xml, pps, pos); - if (node) { - parent = xml_node_get_parent(ctx->xml, node); - xml_node_detach(ctx->xml, node); - wpa_printf(MSG_INFO, "Replace '%s' node", name); - } else { - char *pos2; - pos2 = os_strrchr(pos, '/'); - if (pos2 == NULL) { - parent = pps; - } else { - *pos2 = '\0'; - parent = get_node(ctx->xml, pps, pos); - } - if (parent == NULL) { - wpa_printf(MSG_INFO, "Could not find parent %s", pos); - xml_node_free(ctx->xml, unode); - xml_node_get_attr_value_free(ctx->xml, uri); - return -1; - } - wpa_printf(MSG_INFO, "Add '%s' node", name); - } - xml_node_add_child(ctx->xml, parent, unode); - - xml_node_get_attr_value_free(ctx->xml, uri); - - return 0; -} - - -static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update, - const char *pps_fname, xml_node_t *pps) -{ - wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)"); - xml_node_for_each_sibling(ctx->xml, update) { - xml_node_for_each_check(ctx->xml, update); - if (process_update_node(ctx, pps, update) < 0) - return -1; - } - - return update_pps_file(ctx, pps_fname, pps); -} - - -static void hs20_sub_rem_complete(struct hs20_osu_client *ctx, - const char *pps_fname) -{ - /* - * Update wpa_supplicant credentials and reconnect using updated - * information. - */ - wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); - cmd_set_pps(ctx, pps_fname); - - if (ctx->no_reconnect) - return; - - wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) - wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); -} - - -static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx, - xml_node_t *cmd, - const char *session_id, - const char *pps_fname) -{ - xml_namespace_t *ns; - xml_node_t *node, *ret_node; - char *urn; - - urn = get_spp_attr_value(ctx->xml, cmd, "moURN"); - if (!urn) { - wpa_printf(MSG_INFO, "No URN included"); - return NULL; - } - wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn); - if (strcasecmp(urn, URN_HS20_PPS) != 0) { - wpa_printf(MSG_INFO, "Unsupported moURN"); - xml_node_get_attr_value_free(ctx->xml, urn); - return NULL; - } - xml_node_get_attr_value_free(ctx->xml, urn); - - if (!pps_fname) { - wpa_printf(MSG_INFO, "PPS file name no known"); - return NULL; - } - - node = build_spp_post_dev_data(ctx, &ns, session_id, - "MO upload"); - if (node == NULL) - return NULL; - add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname); - - ret_node = soap_send_receive(ctx->http, node); - if (ret_node == NULL) - return NULL; - - debug_dump_node(ctx, "Received response to MO upload", ret_node); - - if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { - wpa_printf(MSG_INFO, "SPP validation failed"); - xml_node_free(ctx->xml, ret_node); - return NULL; - } - - return ret_node; -} - - -static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo, - char *fname, size_t fname_len) -{ - char *uri, *urn; - int ret; - - debug_dump_node(ctx, "Received addMO", add_mo); - - urn = get_spp_attr_value(ctx->xml, add_mo, "moURN"); - if (urn == NULL) { - wpa_printf(MSG_INFO, "[hs20] No moURN in addMO"); - return -1; - } - wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn); - if (strcasecmp(urn, URN_HS20_PPS) != 0) { - wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO"); - xml_node_get_attr_value_free(ctx->xml, urn); - return -1; - } - xml_node_get_attr_value_free(ctx->xml, urn); - - uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI"); - if (uri == NULL) { - wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO"); - return -1; - } - wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri); - - ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len); - xml_node_get_attr_value_free(ctx->xml, uri); - return ret; -} - - -static int process_spp_user_input_response(struct hs20_osu_client *ctx, - const char *session_id, - xml_node_t *add_mo) -{ - int ret; - char fname[300]; - - debug_dump_node(ctx, "addMO", add_mo); - - wpa_printf(MSG_INFO, "Subscription registration completed"); - - if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) { - wpa_printf(MSG_INFO, "Could not add MO"); - ret = hs20_spp_update_response( - ctx, session_id, - "Error occurred", - "MO addition or update failed"); - return 0; - } - - ret = hs20_spp_update_response(ctx, session_id, "OK", NULL); - if (ret == 0) - hs20_sub_rem_complete(ctx, fname); - - return 0; -} - - -static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx, - const char *session_id) -{ - xml_node_t *node, *ret_node; - - node = build_spp_post_dev_data(ctx, NULL, session_id, - "User input completed"); - if (node == NULL) - return NULL; - - ret_node = soap_send_receive(ctx->http, node); - if (!ret_node) { - if (soap_reinit_client(ctx->http) < 0) - return NULL; - wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); - node = build_spp_post_dev_data(ctx, NULL, session_id, - "User input completed"); - if (node == NULL) - return NULL; - ret_node = soap_send_receive(ctx->http, node); - if (ret_node == NULL) - return NULL; - wpa_printf(MSG_INFO, "Continue with new connection"); - } - - if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { - wpa_printf(MSG_INFO, "SPP validation failed"); - xml_node_free(ctx->xml, ret_node); - return NULL; - } - - return ret_node; -} - - -static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx, - xml_node_t *cmd, - const char *session_id, - const char *pps_fname) -{ - xml_namespace_t *ns; - xml_node_t *node, *ret_node; - int res; - - wpa_printf(MSG_INFO, "Client certificate enrollment"); - - res = osu_get_certificate(ctx, cmd); - if (res < 0) - wpa_printf(MSG_INFO, "EST simpleEnroll failed"); - - node = build_spp_post_dev_data(ctx, &ns, session_id, - res == 0 ? - "Certificate enrollment completed" : - "Certificate enrollment failed"); - if (node == NULL) - return NULL; - - ret_node = soap_send_receive(ctx->http, node); - if (ret_node == NULL) - return NULL; - - debug_dump_node(ctx, "Received response to certificate enrollment " - "completed", ret_node); - - if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { - wpa_printf(MSG_INFO, "SPP validation failed"); - xml_node_free(ctx->xml, ret_node); - return NULL; - } - - return ret_node; -} - - -static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec, - const char *session_id, const char *pps_fname, - xml_node_t *pps, xml_node_t **ret_node) -{ - xml_node_t *cmd; - const char *name; - char *uri; - char *id = strdup(session_id); - - if (id == NULL) - return -1; - - *ret_node = NULL; - - debug_dump_node(ctx, "exec", exec); - - xml_node_for_each_child(ctx->xml, cmd, exec) { - xml_node_for_each_check(ctx->xml, cmd); - break; - } - if (!cmd) { - wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)", - cmd); - free(id); - return -1; - } - - name = xml_node_get_localname(ctx->xml, cmd); - - if (strcasecmp(name, "launchBrowserToURI") == 0) { - int res; - uri = xml_node_get_text(ctx->xml, cmd); - if (!uri) { - wpa_printf(MSG_INFO, "No URI found"); - free(id); - return -1; - } - wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri); - write_summary(ctx, "Launch browser to URI '%s'", uri); - res = hs20_web_browser(uri, 1); - xml_node_get_text_free(ctx->xml, uri); - if (res > 0) { - wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'", - id); - write_summary(ctx, "User response in browser completed successfully"); - *ret_node = hs20_spp_user_input_completed(ctx, id); - free(id); - return *ret_node ? 0 : -1; - } else { - wpa_printf(MSG_INFO, "Failed to receive user response"); - write_summary(ctx, "Failed to receive user response"); - hs20_spp_update_response( - ctx, id, "Error occurred", "Other"); - free(id); - return -1; - } - } - - if (strcasecmp(name, "uploadMO") == 0) { - if (pps_fname == NULL) - return -1; - *ret_node = hs20_spp_upload_mo(ctx, cmd, id, - pps_fname); - free(id); - return *ret_node ? 0 : -1; - } - - if (strcasecmp(name, "getCertificate") == 0) { - *ret_node = hs20_spp_get_certificate(ctx, cmd, id, - pps_fname); - free(id); - return *ret_node ? 0 : -1; - } - - wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name); - free(id); - return -1; -} - - -enum spp_post_dev_data_use { - SPP_SUBSCRIPTION_REMEDIATION, - SPP_POLICY_UPDATE, - SPP_SUBSCRIPTION_REGISTRATION, -}; - -static void process_spp_post_dev_data_response( - struct hs20_osu_client *ctx, - enum spp_post_dev_data_use use, xml_node_t *node, - const char *pps_fname, xml_node_t *pps) -{ - xml_node_t *child; - char *status = NULL; - xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL; - char *session_id = NULL; - - debug_dump_node(ctx, "sppPostDevDataResponse node", node); - - status = get_spp_attr_value(ctx->xml, node, "sppStatus"); - if (status == NULL) { - wpa_printf(MSG_INFO, "No sppStatus attribute"); - goto out; - } - write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'", - status); - - session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); - if (session_id == NULL) { - wpa_printf(MSG_INFO, "No sessionID attribute"); - goto out; - } - - wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s' sessionID: '%s'", - status, session_id); - - xml_node_for_each_child(ctx->xml, child, node) { - const char *name; - xml_node_for_each_check(ctx->xml, child); - debug_dump_node(ctx, "child", child); - name = xml_node_get_localname(ctx->xml, child); - wpa_printf(MSG_INFO, "localname: '%s'", name); - if (!update && strcasecmp(name, "updateNode") == 0) - update = child; - if (!exec && strcasecmp(name, "exec") == 0) - exec = child; - if (!add_mo && strcasecmp(name, "addMO") == 0) - add_mo = child; - if (!no_mo && strcasecmp(name, "noMOUpdate") == 0) - no_mo = child; - } - - if (use == SPP_SUBSCRIPTION_REMEDIATION && - strcasecmp(status, - "Remediation complete, request sppUpdateResponse") == 0) - { - int res, ret; - if (!update && !no_mo) { - wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element"); - goto out; - } - wpa_printf(MSG_INFO, "Subscription remediation completed"); - res = update_pps(ctx, update, pps_fname, pps); - if (res < 0) - wpa_printf(MSG_INFO, "Failed to update PPS MO"); - ret = hs20_spp_update_response( - ctx, session_id, - res < 0 ? "Error occurred" : "OK", - res < 0 ? "MO addition or update failed" : NULL); - if (res == 0 && ret == 0) - hs20_sub_rem_complete(ctx, pps_fname); - goto out; - } - - if (use == SPP_SUBSCRIPTION_REMEDIATION && - strcasecmp(status, "Exchange complete, release TLS connection") == - 0) { - if (!no_mo) { - wpa_printf(MSG_INFO, "No noMOUpdate element"); - goto out; - } - wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)"); - goto out; - } - - if (use == SPP_POLICY_UPDATE && - strcasecmp(status, "Update complete, request sppUpdateResponse") == - 0) { - int res, ret; - wpa_printf(MSG_INFO, "Policy update received - update PPS"); - res = update_pps(ctx, update, pps_fname, pps); - ret = hs20_spp_update_response( - ctx, session_id, - res < 0 ? "Error occurred" : "OK", - res < 0 ? "MO addition or update failed" : NULL); - if (res == 0 && ret == 0) - hs20_policy_update_complete(ctx, pps_fname); - goto out; - } - - if (use == SPP_SUBSCRIPTION_REGISTRATION && - strcasecmp(status, "Provisioning complete, request " - "sppUpdateResponse") == 0) { - if (!add_mo) { - wpa_printf(MSG_INFO, "No addMO element - not sure what to do next"); - goto out; - } - process_spp_user_input_response(ctx, session_id, add_mo); - node = NULL; - goto out; - } - - if (strcasecmp(status, "No update available at this time") == 0) { - wpa_printf(MSG_INFO, "No update available at this time"); - goto out; - } - - if (strcasecmp(status, "OK") == 0) { - int res; - xml_node_t *ret; - - if (!exec) { - wpa_printf(MSG_INFO, "No exec element - not sure what to do next"); - goto out; - } - res = hs20_spp_exec(ctx, exec, session_id, - pps_fname, pps, &ret); - /* xml_node_free(ctx->xml, node); */ - node = NULL; - if (res == 0 && ret) - process_spp_post_dev_data_response(ctx, use, - ret, pps_fname, pps); - goto out; - } - - if (strcasecmp(status, "Error occurred") == 0) { - xml_node_t *err; - char *code = NULL; - err = get_node(ctx->xml, node, "sppError"); - if (err) - code = xml_node_get_attr_value(ctx->xml, err, - "errorCode"); - wpa_printf(MSG_INFO, "Error occurred - errorCode=%s", - code ? code : "N/A"); - xml_node_get_attr_value_free(ctx->xml, code); - goto out; - } - - wpa_printf(MSG_INFO, - "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'", - status); -out: - xml_node_get_attr_value_free(ctx->xml, status); - xml_node_get_attr_value_free(ctx->xml, session_id); - xml_node_free(ctx->xml, node); -} - - -static int spp_post_dev_data(struct hs20_osu_client *ctx, - enum spp_post_dev_data_use use, - const char *reason, - const char *pps_fname, xml_node_t *pps) -{ - xml_node_t *payload; - xml_node_t *ret_node; - - payload = build_spp_post_dev_data(ctx, NULL, NULL, reason); - if (payload == NULL) - return -1; - - ret_node = soap_send_receive(ctx->http, payload); - if (!ret_node) { - const char *err = http_get_err(ctx->http); - if (err) { - wpa_printf(MSG_INFO, "HTTP error: %s", err); - write_result(ctx, "HTTP error: %s", err); - } else { - write_summary(ctx, "Failed to send SOAP message"); - } - return -1; - } - - if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) { - wpa_printf(MSG_INFO, "SPP validation failed"); - xml_node_free(ctx->xml, ret_node); - return -1; - } - - process_spp_post_dev_data_response(ctx, use, ret_node, - pps_fname, pps); - return 0; -} - - -void spp_sub_rem(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps) -{ - wpa_printf(MSG_INFO, "SPP subscription remediation"); - write_summary(ctx, "SPP subscription remediation"); - - os_free(ctx->server_url); - ctx->server_url = os_strdup(address); - - if (soap_init_client(ctx->http, address, ctx->ca_fname, - cred_username, cred_password, client_cert, - client_key) == 0) { - spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION, - "Subscription remediation", pps_fname, pps); - } -} - - -static void hs20_policy_update_complete(struct hs20_osu_client *ctx, - const char *pps_fname) -{ - wpa_printf(MSG_INFO, "Policy update completed"); - - /* - * Update wpa_supplicant credentials and reconnect using updated - * information. - */ - wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials"); - cmd_set_pps(ctx, pps_fname); - - wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration"); - if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) - wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect"); -} - - -static int process_spp_exchange_complete(struct hs20_osu_client *ctx, - xml_node_t *node) -{ - char *status, *session_id; - - debug_dump_node(ctx, "sppExchangeComplete", node); - - status = get_spp_attr_value(ctx->xml, node, "sppStatus"); - if (status == NULL) { - wpa_printf(MSG_INFO, "No sppStatus attribute"); - return -1; - } - write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'", - status); - - session_id = get_spp_attr_value(ctx->xml, node, "sessionID"); - if (session_id == NULL) { - wpa_printf(MSG_INFO, "No sessionID attribute"); - xml_node_get_attr_value_free(ctx->xml, status); - return -1; - } - - wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s' sessionID: '%s'", - status, session_id); - xml_node_get_attr_value_free(ctx->xml, session_id); - - if (strcasecmp(status, "Exchange complete, release TLS connection") == - 0) { - xml_node_get_attr_value_free(ctx->xml, status); - return 0; - } - - wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status); - write_summary(ctx, "Unexpected sppStatus '%s'", status); - xml_node_get_attr_value_free(ctx->xml, status); - return -1; -} - - -static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx, - const char *session_id, - const char *spp_status, - const char *error_code) -{ - xml_namespace_t *ns; - xml_node_t *spp_node, *node; - - spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns, - "sppUpdateResponse"); - if (spp_node == NULL) - return NULL; - - xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0"); - xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id); - xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status); - - if (error_code) { - node = xml_node_create(ctx->xml, spp_node, ns, "sppError"); - if (node) - xml_node_add_attr(ctx->xml, node, NULL, "errorCode", - error_code); - } - - return spp_node; -} - - -static int hs20_spp_update_response(struct hs20_osu_client *ctx, - const char *session_id, - const char *spp_status, - const char *error_code) -{ - xml_node_t *node, *ret_node; - int ret; - - write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'", - spp_status, error_code); - node = build_spp_update_response(ctx, session_id, spp_status, - error_code); - if (node == NULL) - return -1; - ret_node = soap_send_receive(ctx->http, node); - if (!ret_node) { - if (soap_reinit_client(ctx->http) < 0) - return -1; - wpa_printf(MSG_INFO, "Try to finish with re-opened connection"); - node = build_spp_update_response(ctx, session_id, spp_status, - error_code); - if (node == NULL) - return -1; - ret_node = soap_send_receive(ctx->http, node); - if (ret_node == NULL) - return -1; - wpa_printf(MSG_INFO, "Continue with new connection"); - } - - if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) { - wpa_printf(MSG_INFO, "SPP validation failed"); - xml_node_free(ctx->xml, ret_node); - return -1; - } - - ret = process_spp_exchange_complete(ctx, ret_node); - xml_node_free(ctx->xml, ret_node); - return ret; -} - - -void spp_pol_upd(struct hs20_osu_client *ctx, const char *address, - const char *pps_fname, - const char *client_cert, const char *client_key, - const char *cred_username, const char *cred_password, - xml_node_t *pps) -{ - wpa_printf(MSG_INFO, "SPP policy update"); - write_summary(ctx, "SPP policy update"); - - os_free(ctx->server_url); - ctx->server_url = os_strdup(address); - - if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username, - cred_password, client_cert, client_key) == 0) { - spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update", - pps_fname, pps); - } -} - - -int cmd_prov(struct hs20_osu_client *ctx, const char *url) -{ - unlink("Cert/est_cert.der"); - unlink("Cert/est_cert.pem"); - - if (url == NULL) { - wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); - return -1; - } - - wpa_printf(MSG_INFO, - "Credential provisioning requested - URL: %s ca_fname: %s", - url, ctx->ca_fname ? ctx->ca_fname : "N/A"); - - os_free(ctx->server_url); - ctx->server_url = os_strdup(url); - - if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, - NULL) < 0) - return -1; - spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, - "Subscription registration", NULL, NULL); - - return ctx->pps_cred_set ? 0 : -1; -} - - -int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url) -{ - if (url == NULL) { - wpa_printf(MSG_INFO, "Invalid prov command (missing URL)"); - return -1; - } - - wpa_printf(MSG_INFO, "SIM provisioning requested"); - - os_free(ctx->server_url); - ctx->server_url = os_strdup(url); - - wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning"); - - if (wait_ip_addr(ctx->ifname, 15) < 0) { - wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway"); - } - - if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL, - NULL) < 0) - return -1; - spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION, - "Subscription provisioning", NULL, NULL); - - return ctx->pps_cred_set ? 0 : -1; -} diff --git a/contrib/wpa/src/ap/Makefile b/contrib/wpa/src/ap/Makefile index a1e9b7c44d2f..a7513ecfbe8a 100644 --- a/contrib/wpa/src/ap/Makefile +++ b/contrib/wpa/src/ap/Makefile @@ -3,6 +3,9 @@ CFLAGS += -DNEED_AP_MLME CFLAGS += -DCONFIG_ETH_P_OUI CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_INTERWORKING +CFLAGS += -DCONFIG_IEEE80211AC +CFLAGS += -DCONFIG_IEEE80211AX +CFLAGS += -DCONFIG_IEEE80211BE CFLAGS += -DCONFIG_IEEE80211R CFLAGS += -DCONFIG_IEEE80211R_AP CFLAGS += -DCONFIG_WPS @@ -32,10 +35,13 @@ LIB_OBJS= \ hw_features.o \ ieee802_11_auth.o \ ieee802_11.o \ + ieee802_11_eht.o \ + ieee802_11_he.o \ ieee802_11_ht.o \ ieee802_11_shared.o \ ieee802_11_vht.o \ ieee802_1x.o \ + interference.o \ neighbor_db.o \ ndisc_snoop.o \ p2p_hostapd.o \ diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c index f5b36d327a74..1966d8ec6c1a 100644 --- a/contrib/wpa/src/ap/acs.c +++ b/contrib/wpa/src/ap/acs.c @@ -2,6 +2,7 @@ * ACS - Automatic Channel Selection module * Copyright (c) 2011, Atheros Communications * Copyright (c) 2013, Qualcomm Atheros, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -54,7 +55,7 @@ * Todo / Ideas * ------------ * - implement other interference computation methods - * - BSS/RSSI based + * - RSSI based * - spectral scan based * (should be possibly to hook this up with current ACS scans) * - add wpa_supplicant support (for P2P) @@ -557,6 +558,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface) static int acs_usable_chan(struct hostapd_channel_data *chan) { + if (chan->interference_bss_based) + return 1; + return !dl_list_empty(&chan->survey_list) && !(chan->flag & HOSTAPD_CHAN_DISABLED) && acs_survey_list_is_sufficient(chan); @@ -828,7 +832,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, struct hostapd_channel_data **ideal_chan, long double *ideal_factor) { - struct hostapd_channel_data *chan, *adj_chan = NULL, *best; + struct hostapd_channel_data *chan, *adj_chan = NULL, + *chan2 = NULL, *best; long double factor; int i, j; int bw320_offset = 0, ideal_bw320_offset = 0; @@ -847,49 +852,11 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, chan = &mode->channels[i]; - /* Since in the current ACS implementation the first channel is - * always a primary channel, skip channels not available as - * primary until more sophisticated channel selection is - * implemented. - * - * If this implementation is changed to allow any channel in - * the bandwidth to be the primary one, the last parameter to - * acs_update_puncturing_bitmap() should be changed to the index - * of the primary channel - */ - if (!chan_pri_allowed(chan)) - continue; - - if ((chan->flag & HOSTAPD_CHAN_RADAR) && - iface->conf->acs_exclude_dfs) - continue; - - if (!is_in_chanlist(iface, chan)) - continue; - - if (!is_in_freqlist(iface, chan)) - continue; - - if (chan->max_tx_power < iface->conf->min_tx_power) - continue; - - if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) && - iface->conf->country[2] == 0x4f) - continue; - - if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) { - wpa_printf(MSG_DEBUG, - "ACS: Channel %d: BW %u is not supported", - chan->chan, bw); - continue; - } - /* HT40 on 5 GHz has a limited set of primary channels as per * 11n Annex J */ - if (mode->mode == HOSTAPD_MODE_IEEE80211A && - ((iface->conf->ieee80211n && - iface->conf->secondary_channel) || - is_6ghz_freq(chan->freq)) && + if (bw == 40 && + mode->mode == HOSTAPD_MODE_IEEE80211A && + iface->conf->ieee80211n && !acs_usable_bw_chan(chan, ACS_BW40)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth", @@ -900,18 +867,14 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, if (mode->mode == HOSTAPD_MODE_IEEE80211A && (iface->conf->ieee80211ac || iface->conf->ieee80211ax || iface->conf->ieee80211be)) { - if (hostapd_get_oper_chwidth(iface->conf) == - CONF_OPER_CHWIDTH_80MHZ && - !acs_usable_bw_chan(chan, ACS_BW80)) { + if (bw == 80 && !acs_usable_bw_chan(chan, ACS_BW80)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth", chan->chan); continue; } - if (hostapd_get_oper_chwidth(iface->conf) == - CONF_OPER_CHWIDTH_160MHZ && - !acs_usable_bw_chan(chan, ACS_BW160)) { + if (bw == 160 && !acs_usable_bw_chan(chan, ACS_BW160)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth", chan->chan); @@ -921,43 +884,67 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, if (mode->mode == HOSTAPD_MODE_IEEE80211A && iface->conf->ieee80211be) { - if (hostapd_get_oper_chwidth(iface->conf) == - CONF_OPER_CHWIDTH_320MHZ && - !acs_usable_bw320_chan(iface, chan, &bw320_offset)) + if (bw == 320 && + !acs_usable_bw320_chan(iface, chan, + &bw320_offset)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for 320 MHz bandwidth", + chan->chan); continue; + } } factor = 0; best = NULL; - if (acs_usable_chan(chan)) { - factor = chan->interference_factor; - total_weight = 1; - best = chan; - } - for (j = 1; j < n_chans; j++) { - adj_chan = acs_find_chan(iface, chan->freq + - j * secondary_channel * 20); - if (!adj_chan) + for (j = 0; j < n_chans; j++) { + chan2 = acs_find_chan(iface, chan->freq + + j * secondary_channel * 20); + if (!chan2) break; - if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { + if (!chan_bw_allowed(chan2, bw, secondary_channel != -1, + j == 0)) { wpa_printf(MSG_DEBUG, - "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", - chan->chan, adj_chan->chan, bw); + "ACS: Channel %d: BW %u is not supported", + chan2->chan, bw); break; } - if (!acs_usable_chan(adj_chan)) + if ((chan2->flag & HOSTAPD_CHAN_RADAR) && + iface->conf->acs_exclude_dfs) + break; + + if (chan2->max_tx_power < iface->conf->min_tx_power) + break; + + if ((chan2->flag & HOSTAPD_CHAN_INDOOR_ONLY) && + iface->conf->country[2] == 0x4f) + break; + + if (!acs_usable_chan(chan2)) continue; - factor += adj_chan->interference_factor; + factor += chan2->interference_factor; total_weight += 1; + if (!chan_pri_allowed(chan2)) + continue; + + if (!is_in_chanlist(iface, chan2)) + continue; + + if (!is_in_freqlist(iface, chan2)) + continue; + + if (iface->conf->acs_exclude_6ghz_non_psc && + !is_6ghz_psc_frequency(chan2->freq)) + continue; + /* find the best channel in this segment */ - if (!best || adj_chan->interference_factor < + if (!best || chan2->interference_factor < best->interference_factor) - best = adj_chan; + best = chan2; } if (j != n_chans) { @@ -966,18 +953,25 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, continue; } + if (!best) { + wpa_printf(MSG_DEBUG, + "ACS: No valid channel found in the segment starting with channel %d", + chan->chan); + continue; + } + /* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less * crowded primary channel if one was found in the segment */ if (iface->current_mode && iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && - best && chan != best) { + chan != best) { wpa_printf(MSG_DEBUG, "ACS: promoting channel %d over %d (less interference %Lg/%Lg)", best->chan, chan->chan, chan->interference_factor, best->interference_factor); #ifdef CONFIG_IEEE80211BE - index_primary = (chan->freq - best->freq) / 20; + index_primary = (best->freq - chan->freq) / 20; #endif /* CONFIG_IEEE80211BE */ chan = best; } @@ -1254,13 +1248,53 @@ static int acs_study_survey_based(struct hostapd_iface *iface) } +static int acs_study_bss_based(struct hostapd_iface *iface) +{ + struct wpa_scan_results *scan_res; + int j; + + wpa_printf(MSG_DEBUG, "ACS: Trying BSS-based ACS"); + + scan_res = hostapd_driver_get_scan_results(iface->bss[0]); + if (!scan_res) { + wpa_printf(MSG_INFO, "ACS: Scan request failed"); + hostapd_setup_interface_complete(iface, 1); + return -1; + } + + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan; + unsigned int bss_on_ch = 0; + size_t i; + + chan = &iface->current_mode->channels[j]; + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + + if (bss->freq == chan->freq) + bss_on_ch++; + } + + wpa_printf(MSG_MSGDUMP, + "ACS: Interference on ch %d (%d MHz): %d", + chan->chan, chan->freq, bss_on_ch); + chan->interference_factor = bss_on_ch; + chan->interference_bss_based = true; + } + + wpa_scan_results_free(scan_res); + return 0; +} + + static int acs_study_options(struct hostapd_iface *iface) { if (acs_study_survey_based(iface) == 0) return 0; - /* TODO: If no surveys are available/sufficient this is a good - * place to fallback to BSS-based ACS */ + wpa_printf(MSG_INFO, "ACS: Survey based ACS failed"); + if (acs_study_bss_based(iface) == 0) + return 0; return -1; } diff --git a/contrib/wpa/src/ap/afc.c b/contrib/wpa/src/ap/afc.c new file mode 100644 index 000000000000..fed4d940ee3d --- /dev/null +++ b/contrib/wpa/src/ap/afc.c @@ -0,0 +1,1123 @@ +/* + * Automated Frequency Coordination + * Copyright (c) 2024, Lorenzo Bianconi + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/json.h" +#include "common/hw_features_common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "acs.h" +#include "hw_features.h" + +#define HOSTAPD_AFC_RETRY_TIMEOUT 180 +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ +#define HOSTAPD_AFC_BUFSIZE 8192 + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx); + + +static int hostapd_afc_reset_channels(struct hostapd_iface *iface) +{ + int ret; + + ret = hostapd_get_hw_features(iface); + if (ret) { + wpa_printf(MSG_ERROR, "AFC: Failed to reset channel flags"); + return ret; + } + + ret = hostapd_set_current_hw_info(iface, iface->freq); + if (ret) + wpa_printf(MSG_ERROR, + "AFC: Failed to set current hardware info"); + + return ret; +} + + +static struct wpabuf * +hostapd_afc_build_location_request(struct hostapd_iface *iface) +{ + struct wpabuf *location_obj, *ellipse_obj = NULL; + struct hostapd_config *iconf = iface->conf; + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); + size_t location_len = 1024, ellipse_len = 128; + unsigned int i; + + location_obj = wpabuf_alloc(location_len); + if (!location_obj) + return NULL; + + json_start_object(location_obj, "location"); + if (iconf->afc.location.type != AFC_LINEAR_POLYGON) { + struct afc_linear_polygon *lp = + iconf->afc.location.linear_polygon_data; + + if (!lp) + goto error; + + ellipse_obj = wpabuf_alloc(ellipse_len); + if (!ellipse_obj) + goto error; + + json_start_object(ellipse_obj, "center"); + + json_add_double(ellipse_obj, "longitude", lp->longitude); + json_value_sep(ellipse_obj); + + json_add_double(ellipse_obj, "latitude", lp->latitude); + json_end_object(ellipse_obj); + json_value_sep(ellipse_obj); + } + + switch (iconf->afc.location.type) { + case AFC_LINEAR_POLYGON: + json_start_object(location_obj, "linearPolygon"); + json_start_array(location_obj, "outerBoundary"); + for (i = 0; i < iconf->afc.location.n_linear_polygon_data; i++) + { + struct afc_linear_polygon *lp = + &iconf->afc.location.linear_polygon_data[i]; + + json_start_object(location_obj, NULL); + + json_add_double(location_obj, "longitude", + lp->longitude); + json_value_sep(location_obj); + + json_add_double(location_obj, "latitude", + lp->latitude); + + json_end_object(location_obj); + if (i < iconf->afc.location.n_linear_polygon_data - 1) + json_value_sep(location_obj); + } + json_end_array(location_obj); + + /* linearPolygon */ + json_end_object(location_obj); + json_value_sep(location_obj); + break; + case AFC_RADIAL_POLYGON: + json_start_object(location_obj, "radialPolygon"); + wpabuf_put_buf(location_obj, ellipse_obj); + + json_start_array(location_obj, "outerBoundary"); + for (i = 0; i < iconf->afc.location.n_radial_polygon_data; i++) + { + struct afc_radial_polygon *rp = + &iconf->afc.location.radial_polygon_data[i]; + + json_start_object(location_obj, NULL); + json_add_double(location_obj, "length", rp->length); + json_value_sep(location_obj); + + json_add_double(location_obj, "angle", rp->angle); + json_end_object(location_obj); + if (i < iconf->afc.location.n_radial_polygon_data - 1) + json_value_sep(location_obj); + } + json_end_array(location_obj); + + /* radialPolygon */ + json_end_object(location_obj); + json_value_sep(location_obj); + break; + case AFC_ELLIPSE: + default: + json_start_object(location_obj, "ellipse"); + wpabuf_put_buf(location_obj, ellipse_obj); + + json_add_int(location_obj, "majorAxis", + iconf->afc.location.major_axis); + json_value_sep(location_obj); + json_add_int(location_obj, "minorAxis", + iconf->afc.location.minor_axis); + json_value_sep(location_obj); + json_add_int(location_obj, "orientation", + iconf->afc.location.orientation); + /* ellipse */ + json_end_object(location_obj); + json_value_sep(location_obj); + break; + } + + json_start_object(location_obj, "elevation"); + + json_add_double(location_obj, "height", iconf->afc.location.height); + json_value_sep(location_obj); + if (iconf->afc.location.height_type) { + json_add_string(location_obj, "heightType", + iconf->afc.location.height_type); + json_value_sep(location_obj); + } + json_add_int(location_obj, "verticalUncertainty", + iconf->afc.location.vertical_tolerance); + + /* elevation */ + json_end_object(location_obj); + json_value_sep(location_obj); + + json_add_int(location_obj, "indoorDeployment", is_ap_indoor); + + /* location */ + json_end_object(location_obj); + json_value_sep(location_obj); + + wpabuf_free(ellipse_obj); + return location_obj; + +error: + wpabuf_free(location_obj); + wpabuf_free(ellipse_obj); + + return NULL; +} + + +static struct wpabuf * hostapd_afc_get_opclass_chan_list(u8 op_class) +{ + struct wpabuf *chan_list_obj; + const struct oper_class_map *oper_class; + int chan_offset, chan; + size_t ch_len = 512; + + oper_class = get_oper_class(NULL, op_class); + if (!oper_class) + return NULL; + + chan_list_obj = wpabuf_alloc(ch_len); + if (!chan_list_obj) + return NULL; + json_start_array(chan_list_obj, "channelCfi"); + + switch (op_class) { + case 132: /* 40 MHz */ + chan_offset = 2; + break; + case 133: /* 80 MHz */ + chan_offset = 6; + break; + case 134: /* 160 MHz */ + chan_offset = 14; + break; + case 137: /* 320 MHz */ + /* + * global_op_class already use the central channels for + * 320 MHz, so fallthrough and use 0 for chan_offset. + */ + default: + chan_offset = 0; + break; + } + + for (chan = oper_class->min_chan; chan <= oper_class->max_chan; + chan += oper_class->inc) { + char ch_str[32]; + + if (chan + chan_offset > oper_class->max_chan) + break; + + os_snprintf(ch_str, sizeof(ch_str), "%s%d", + chan != oper_class->min_chan ? ", " : "", + chan + chan_offset); + wpabuf_put_str(chan_list_obj, ch_str); + } + json_end_array(chan_list_obj); + + return chan_list_obj; +} + + +static struct wpabuf * +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface) +{ + struct wpabuf *op_class_list_obj; + struct hostapd_config *iconf = iface->conf; + unsigned int i, num; + + op_class_list_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE); + if (!op_class_list_obj) + return NULL; + + json_start_array(op_class_list_obj, "inquiredChannels"); + num = int_array_len(iconf->afc.op_class); + for (i = 0; i < num; i++) { + struct wpabuf *chan_list_obj = NULL; + u8 op_class = iconf->afc.op_class[i]; + + if (!is_6ghz_op_class(op_class)) + continue; + + json_start_object(op_class_list_obj, NULL); + + json_add_int(op_class_list_obj, "globalOperatingClass", + op_class); + json_value_sep(op_class_list_obj); + + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class); + if (!chan_list_obj) + goto error; + + wpabuf_put_buf(op_class_list_obj, chan_list_obj); + json_end_object(op_class_list_obj); + + if (i < num - 1) + json_value_sep(op_class_list_obj); + + wpabuf_free(chan_list_obj); + chan_list_obj = NULL; + } + json_end_array(op_class_list_obj); + + return op_class_list_obj; + +error: + wpabuf_free(op_class_list_obj); + return NULL; +} + + +static struct wpabuf * +hostapd_afc_build_request(struct hostapd_iface *iface) +{ + struct wpabuf *req_obj, *location_obj, *op_class_list_obj = NULL; + struct hostapd_config *iconf = iface->conf; + unsigned int i; + char request_id_str[16]; + + req_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE); + if (!req_obj) + return NULL; + + json_start_object(req_obj, NULL); + + if (iconf->afc.request.version) { + json_add_string(req_obj, "version", iconf->afc.request.version); + json_value_sep(req_obj); + } + + json_start_array(req_obj, "availableSpectrumInquiryRequests"); + json_start_object(req_obj, NULL); + + iface->afc.request_id++; + os_snprintf(request_id_str, sizeof(request_id_str), "%u", + iface->afc.request_id); + json_add_string(req_obj, "requestId", request_id_str); + json_value_sep(req_obj); + + json_start_object(req_obj, "deviceDescriptor"); + if (iconf->afc.request.sn) { + json_add_string(req_obj, "serialNumber", iconf->afc.request.sn); + json_value_sep(req_obj); + } + + json_start_array(req_obj, "certificationId"); + + for (i = 0; i < iconf->afc.n_cert_ids; i++) { + json_start_object(req_obj, NULL); + json_add_string(req_obj, "rulesetId", + iconf->afc.cert_ids[i].ruleset); + json_value_sep(req_obj); + + json_add_string(req_obj, "id", iconf->afc.cert_ids[i].id); + json_end_object(req_obj); + + if (i < iconf->afc.n_cert_ids - 1) + json_value_sep(req_obj); + } + /* certificationId */ + json_end_array(req_obj); + + /* deviceDescriptor */ + json_end_object(req_obj); + json_value_sep(req_obj); + + location_obj = hostapd_afc_build_location_request(iface); + if (!location_obj) + goto error; + + wpabuf_put_buf(req_obj, location_obj); + + if (iconf->afc.freqs.num) { + json_start_array(req_obj, "inquiredFrequencyRange"); + for (i = 0; i < iconf->afc.freqs.num; i++) { + struct wpa_freq_range *fr = &iconf->afc.freqs.range[i]; + + json_start_object(req_obj, NULL); + + json_add_int(req_obj, "lowFrequency", fr->min); + json_value_sep(req_obj); + json_add_int(req_obj, "highFrequency", fr->max); + + json_end_object(req_obj); + if (i < iconf->afc.freqs.num - 1) + json_value_sep(req_obj); + } + json_end_array(req_obj); + json_value_sep(req_obj); + } + + op_class_list_obj = hostapd_afc_build_req_chan_list(iface); + if (!op_class_list_obj) + goto error; + + wpabuf_put_buf(req_obj, op_class_list_obj); + + if (iconf->afc.min_power) { + json_value_sep(req_obj); + json_add_int(req_obj, "minDesiredPower", iconf->afc.min_power); + } + + /* availableSpectrumInquiryRequests */ + json_end_object(req_obj); + json_end_array(req_obj); + + json_end_object(req_obj); + wpa_hexdump_ascii_buf(MSG_MSGDUMP, "AFC: Pending request", req_obj); + + wpabuf_free(location_obj); + wpabuf_free(op_class_list_obj); + + return req_obj; + +error: + wpabuf_free(req_obj); + wpabuf_free(location_obj); + wpabuf_free(op_class_list_obj); + + return NULL; +} + + +static int +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface, + struct json_token *freq_info) +{ + struct afc_freq_range_elem *f = NULL, *tmp; + struct json_token *freq_obj; + int count = 0; + + for (freq_obj = freq_info->child; freq_obj; + freq_obj = freq_obj->sibling) { + struct json_token *freq_range_obj, *token; + int low_freq, high_freq; + double max_psd; + + freq_range_obj = json_get_member(freq_obj, "frequencyRange"); + if (!freq_range_obj || freq_range_obj->type != JSON_OBJECT) + continue; + + token = json_get_member(freq_range_obj, "lowFrequency"); + if (!token || token->type != JSON_NUMBER) + continue; + low_freq = token->number; + + token = json_get_member(freq_range_obj, "highFrequency"); + if (!token || token->type != JSON_NUMBER) + continue; + high_freq = token->number; + + token = json_get_member(freq_obj, "maxPsd"); + if (!token) + token = json_get_member(freq_obj, "maxPSD"); + if (!token || (token->type != JSON_DOUBLE && + token->type != JSON_NUMBER)) + continue; + max_psd = token->type == JSON_DOUBLE ? + token->dnumber : (double) token->number; + + tmp = os_realloc_array(f, count + 1, sizeof(*f)); + if (!tmp) { + os_free(f); + return -ENOMEM; + } + f = tmp; + + f[count].low_freq = low_freq; + f[count].high_freq = high_freq; + f[count++].max_psd = max_psd; + } + os_free(iface->afc.freq_range); + iface->afc.freq_range = f; + iface->afc.num_freq_range = count; + + return 0; +} + + +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list, + int *chan_list_size, u8 op_class, + int center_chan, double power) +{ + int op_class_pwr_index, num_low_subchan, channel; + int count = *chan_list_size; + struct afc_chan_info_elem *c = *chan_list, *tmp; + + switch (op_class) { + case 132: /* 40 MHz */ + op_class_pwr_index = 1; + num_low_subchan = 2; + break; + case 133: /* 80 MHz */ + op_class_pwr_index = 2; + num_low_subchan = 6; + break; + case 134: /* 160 MHz */ + op_class_pwr_index = 3; + num_low_subchan = 14; + break; + case 137: /* 320 MHz */ + op_class_pwr_index = 4; + num_low_subchan = 30; + break; + default: + op_class_pwr_index = 0; + num_low_subchan = 0; + break; + } + + for (channel = center_chan - num_low_subchan; + channel <= center_chan + num_low_subchan; channel += 4) { + int i; + + for (i = 0; i < count; i++) { + if (c[i].chan == channel) + break; + } + + if (i == count) { + tmp = os_realloc_array(c, count + 1, sizeof(*c)); + if (!tmp) { + os_free(c); + *chan_list = NULL; + *chan_list_size = 0; + return -ENOMEM; + } + c = tmp; + + c[count].chan = channel; + os_memset(c[count].power, 0, sizeof(c[count].power)); + count++; + } + c[i].power[op_class_pwr_index] = power; + } + + *chan_list_size = count; + *chan_list = c; + + return 0; +} + + +static int +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface, + struct json_token *chan_info) +{ + struct afc_chan_info_elem *c = NULL; + struct json_token *chan_obj; + int count = 0; + + for (chan_obj = chan_info->child; chan_obj; + chan_obj = chan_obj->sibling) { + struct json_token *chan_cfi, *max_eirp; + struct json_token *token, *chan_arr, *power_arr; + int op_class; + + token = json_get_member(chan_obj, "globalOperatingClass"); + if (!token || token->type != JSON_NUMBER) + continue; + op_class = token->number; + + chan_cfi = json_get_member(chan_obj, "channelCfi"); + if (!chan_cfi || chan_cfi->type != JSON_ARRAY) + continue; + + max_eirp = json_get_member(chan_obj, "maxEirp"); + if (!max_eirp || max_eirp->type != JSON_ARRAY) + continue; + + chan_arr = chan_cfi->child; + power_arr = max_eirp->child; + while (chan_arr && power_arr) { + int channel, ret; + double power; + + if (chan_arr->type != JSON_NUMBER || + (power_arr->type != JSON_DOUBLE && + power_arr->type != JSON_NUMBER)) { + wpa_printf(MSG_DEBUG, + "AFC: Failed to parse array at opclass %d", + op_class); + break; + } + + channel = chan_arr->number; + power = power_arr->type == JSON_DOUBLE ? + power_arr->dnumber : (double) power_arr->number; + + ret = hostad_afc_update_chan_info(&c, &count, op_class, + channel, power); + if (ret) { + iface->afc.chan_info_list = NULL; + iface->afc.num_chan_info = 0; + return ret; + } + chan_arr = chan_arr->sibling; + power_arr = power_arr->sibling; + } + } + os_free(iface->afc.chan_info_list); + iface->afc.chan_info_list = c; + iface->afc.num_chan_info = count; + + return 0; +} + + +static int hostad_afc_get_timeout(struct json_token *obj) +{ + int year, month, day, hour, min, sec; + os_time_t t; + struct os_time now; + + if (sscanf(obj->string, "%d-%d-%dT%d:%d:%dZ", + &year, &month, &day, &hour, &min, &sec) != 6) + return HOSTAPD_AFC_TIMEOUT; + + if (os_mktime(year, month, day, hour, min, sec, &t) < 0) + return HOSTAPD_AFC_TIMEOUT; + + os_get_time(&now); + + return t < now.sec ? + HOSTAPD_AFC_RETRY_TIMEOUT : (t - now.sec) * 80 / 100; +} + + +static int +hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply, size_t len) +{ + struct hostapd_config *iconf = iface->conf; + int request_timeout = -1, ret = -EINVAL; + struct json_token *root = NULL, *reply_obj, *token; + + wpa_hexdump_ascii(MSG_MSGDUMP, "AFC: Received reply", reply, len); + + iface->afc_response = os_malloc(len + 1); + if (!iface->afc_response) { + wpa_printf(MSG_ERROR, + "AFC: Failed to alloc memory for storing reply"); + return -ENOMEM; + } + os_memcpy(iface->afc_response, reply, len); + iface->afc_response[len] = '\0'; + + root = json_parse(reply, len); + if (!root) { + wpa_printf(MSG_ERROR, "AFC: Failed to parse reply payload"); + goto fail; + } + + token = json_get_member(root, "version"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_ERROR, "AFC: Missing version in reply"); + goto fail; + } + if (iconf->afc.request.version && + os_strcmp(iconf->afc.request.version, token->string) != 0) { + wpa_printf(MSG_ERROR, "AFC: Mismatch in reply version"); + goto fail; + } + + reply_obj = json_get_member(root, "availableSpectrumInquiryResponses"); + if (!reply_obj || reply_obj->type != JSON_ARRAY) { + wpa_printf(MSG_ERROR, + "AFC: Missing availableSpectrumInquiry in reply"); + goto fail; + } + + for (token = reply_obj->child; token; token = token->sibling) { + struct json_token *reply_elem, *response; + int status = -EINVAL, timeout; + unsigned int j; + char request_id_str[16]; + + reply_elem = json_get_member(token, "requestId"); + if (!reply_elem || reply_elem->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "AFC: Missing requestId in reply element"); + continue; + } + + snprintf(request_id_str, sizeof(request_id_str), "%u", + iface->afc.request_id); + if (os_strcmp(request_id_str, reply_elem->string) != 0) { + wpa_printf(MSG_DEBUG, + "AFC: RequestId mismatch in reply element"); + continue; + } + + reply_elem = json_get_member(token, "rulesetId"); + if (!reply_elem || reply_elem->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "AFC: Missing rulesetId in reply element"); + continue; + } + + for (j = 0; j < iconf->afc.n_cert_ids; j++) { + if (os_strcmp(iconf->afc.cert_ids[j].ruleset, + reply_elem->string) == 0) + break; + } + + if (j == iconf->afc.n_cert_ids) { + wpa_printf(MSG_DEBUG, + "AFC: RulesetId mismatch in reply element"); + continue; + } + + response = json_get_member(token, "response"); + if (!response || response->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, + "AFC: Missing response field in reply element"); + continue; + } + + reply_elem = json_get_member(response, "shortDescription"); + if (reply_elem && reply_elem->type == JSON_STRING) + wpa_printf(MSG_DEBUG, "AFC: Reply element: %s", + reply_elem->string); + + reply_elem = json_get_member(response, "responseCode"); + if (reply_elem && reply_elem->type == JSON_NUMBER) + status = reply_elem->number; + + if (status < 0) { + wpa_printf(MSG_DEBUG, + "AFC: Reply invalid responseCode: %d", + status); + continue; + } + reply_elem = json_get_member(token, "availableFrequencyInfo"); + if (reply_elem && reply_elem->type == JSON_ARRAY && + hostad_afc_parse_available_freq_info(iface, reply_elem)) { + wpa_printf(MSG_DEBUG, + "AFC: Failed to parse availableFrequencyInfo"); + continue; + } + + reply_elem = json_get_member(token, "availableChannelInfo"); + if (reply_elem && reply_elem->type == JSON_ARRAY && + hostad_afc_parse_available_chan_info(iface, reply_elem)) { + wpa_printf(MSG_DEBUG, + "AFC: Failed to parse availableChannelInfo"); + continue; + } + + reply_elem = json_get_member(token, "availabilityExpireTime"); + if (!reply_elem || reply_elem->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "AFC: Missing expire time, use default timeout"); + continue; + } + + timeout = hostad_afc_get_timeout(reply_elem); + if (request_timeout < 0 || timeout < request_timeout) + request_timeout = timeout; + + ret = status; + } + + iface->afc.data_valid = true; + iface->afc.timeout = request_timeout; + if (iface->afc.timeout < 0) + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + +fail: + json_free(root); + return ret; +} + + +static int hostapd_afc_send_receive(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + struct wpabuf *request_obj = NULL; + struct timeval sock_timeout = { + .tv_sec = 10, + }; + struct sockaddr_un addr = { + .sun_family = AF_UNIX, +#ifdef __FreeBSD__ + .sun_len = sizeof(addr), +#endif /* __FreeBSD__ */ + }; + char *buf = NULL; + int sockfd, ret; + fd_set read_set; + + if (iface->afc.data_valid) { + /* AFC data already downloaded from the server */ + return 0; + } + + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; + if (!iconf->afc.socket) { + wpa_printf(MSG_ERROR, "AFC: Missing socket string"); + return -EINVAL; + } + + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { + wpa_printf(MSG_ERROR, "AFC: Malformed socket string %s", + iconf->afc.socket); + return -EINVAL; + } + + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path)); + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + wpa_printf(MSG_ERROR, "AFC: Failed creating socket"); + return sockfd; + } + + if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "AFC: Failed connecting socket"); + ret = -EIO; + goto close_sock; + } + + request_obj = hostapd_afc_build_request(iface); + if (!request_obj) { + ret = -EINVAL; + goto close_sock; + } + + ret = send(sockfd, wpabuf_head(request_obj), wpabuf_len(request_obj), + 0); + if (ret < 0 || (size_t) ret != wpabuf_len(request_obj)) { + wpa_printf(MSG_ERROR, "AFC: Failed sending request"); + ret = -EIO; + goto close_sock; + } + + FD_ZERO(&read_set); + FD_SET(sockfd, &read_set); + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) { + wpa_printf(MSG_ERROR, "AFC: select() failed on socket"); + ret = -errno; + goto close_sock; + } + + if (!FD_ISSET(sockfd, &read_set)) { + ret = -EIO; + goto close_sock; + } + + buf = os_zalloc(HOSTAPD_AFC_BUFSIZE); + if (!buf) { + ret = -ENOMEM; + goto close_sock; + } + + ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0); + if (!ret) + ret = -EIO; + if (ret < 0) + goto close_sock; + + ret = hostapd_afc_parse_reply(iface, buf, ret); + if (ret) { + wpa_printf(MSG_ERROR, "AFC: Failed parsing reply: %d", ret); + goto close_sock; + } + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_RECEIVED); +close_sock: + os_free(buf); + wpabuf_free(iface->afc_request); + iface->afc_request = request_obj; + close(sockfd); + + return ret; +} + + +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) +{ + const struct oper_class_map *oper_class; + int channel, chan_num; + + oper_class = get_oper_class(NULL, iface->conf->op_class); + if (!oper_class) + return false; + + switch (iface->conf->op_class) { + case 132: /* 40 MHz */ + chan_num = 2; + break; + case 133: /* 80 MHz */ + chan_num = 4; + break; + case 134: /* 160 MHz */ + chan_num = 8; + break; + case 137: /* 320 MHz */ + chan_num = 16; + break; + default: + chan_num = 1; + break; + } + + for (channel = oper_class->min_chan; channel <= oper_class->max_chan; + channel += oper_class->inc) { + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan = NULL; + int i, start_ch = channel; + + if (iface->conf->op_class == 137) + start_ch = channel - 30; + + for (i = 0; i < chan_num; i++) { + chan = hw_get_channel_chan(mode, start_ch + i * 4, + NULL); + + if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) + break; + } + + if (i == chan_num) + return true; + } + + return false; +} + + +int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + struct hostapd_config *iconf = iface->conf; + int ret; + + /* AFC is required just for standard power AP */ + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return 1; + + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq)) + return 1; + + if (iface->state == HAPD_IFACE_ACS) + return 1; + + iface->afc.request_id = os_random(); + ret = hostapd_afc_send_receive(iface); + if (ret < 0) { + /* + * If the connection to the AFCD failed, reschedule for a + * future attempt. + */ + wpa_printf(MSG_ERROR, "AFC: Connection failed: %d", ret); + if (ret == -EIO) + ret = 0; + goto resched; + } + + hostap_afc_disable_channels(iface); + if (!hostapd_afc_has_usable_chans(iface)) + goto resched; + + ret = hostapd_is_usable_chans(iface); + if (ret != 1) { + /* Trigger an ACS freq scan */ + iconf->channel = 0; + iface->freq = 0; + hostapd_set_and_check_bw320_offset(iface->conf, 0); + + if (!ret && acs_init(iface) != HOSTAPD_CHAN_ACS) { + wpa_printf(MSG_ERROR, "AFC: Could not start ACS"); + ret = -EINVAL; + } + } + +resched: + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); + + return ret; +} + + +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) +{ + wpabuf_free(iface->afc_request); + os_free(iface->afc_response); + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); + + iface->afc_request = NULL; + iface->afc_response = NULL; + + iface->afc.num_freq_range = 0; + iface->afc.num_chan_info = 0; + + iface->afc.chan_info_list = NULL; + iface->afc.freq_range = NULL; + + iface->afc.data_valid = false; +} + + +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + bool restart_iface = true; + + hostapd_afc_delete_data_from_server(iface); + if (iface->state != HAPD_IFACE_ENABLED) { + /* hostapd is not fully enabled yet, toggle the interface */ + goto restart_interface; + } + + if (hostapd_afc_send_receive(iface) < 0 || + hostapd_afc_reset_channels(iface)) { + restart_iface = false; + goto restart_interface; + } + + if (hostapd_is_usable_chans(iface) > 0) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE); + goto resched; + } + + restart_iface = hostapd_afc_has_usable_chans(iface); + if (restart_iface) { + /* Trigger an ACS freq scan */ + iface->conf->channel = 0; + iface->freq = 0; + hostapd_set_and_check_bw320_offset(iface->conf, 0); + } else { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE); + } + +restart_interface: + hostapd_disable_iface(iface); + if (restart_iface) + hostapd_enable_iface(iface); +resched: + eloop_register_timeout(iface->afc.timeout, 0, + hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostapd_afc_send_request(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); + eloop_register_timeout(0, 0, hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostapd_afc_stop(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); +} + + +void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode = NULL; + int i; + + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + if (mode->mode == HOSTAPD_MODE_IEEE80211A && + mode->is_6ghz) + break; + } + + if (i == iface->num_hw_features || + !he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) || + !iface->afc.data_valid) + return; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + unsigned int j; + + if (!is_6ghz_freq(chan->freq)) + continue; + + for (j = 0; j < iface->afc.num_freq_range; j++) { + if (chan->freq >= iface->afc.freq_range[j].low_freq && + chan->freq <= iface->afc.freq_range[j].high_freq) + break; + } + + if (j != iface->afc.num_freq_range) + continue; + + for (j = 0; j < iface->afc.num_chan_info; j++) { + if (chan->chan == iface->afc.chan_info_list[j].chan) + break; + } + + if (j != iface->afc.num_chan_info) + continue; + + chan->flag |= HOSTAPD_CHAN_DISABLED; + wpa_printf(MSG_MSGDUMP, + "AFC: Disabling freq=%d MHz (not allowed by AFC)", + chan->freq); + } +} + + +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + unsigned int i; + + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) || + !iface->afc.data_valid) + return -EINVAL; + + if (psd) { + for (i = 0; i < iface->afc.num_freq_range; i++) { + struct afc_freq_range_elem *f; + + f = &iface->afc.freq_range[i]; + if (iface->freq >= f->low_freq && + iface->freq <= f->high_freq) { + *power = (int) (2 * f->max_psd); + return 0; + } + } + } else { + for (i = 0; i < iface->afc.num_chan_info; i++) { + struct afc_chan_info_elem *c; + + c = &iface->afc.chan_info_list[i]; + if (c->chan == iface->conf->channel) { + unsigned int j; + + *power = 0; + for (j = 0; j < ARRAY_SIZE(c->power); j++) + *power = MAX(*power, + (int) (2 * c->power[j])); + return 0; + } + } + } + + return -EINVAL; +} diff --git a/contrib/wpa/src/ap/airtime_policy.c b/contrib/wpa/src/ap/airtime_policy.c index 68443115f16b..003c4c1256d9 100644 --- a/contrib/wpa/src/ap/airtime_policy.c +++ b/contrib/wpa/src/ap/airtime_policy.c @@ -119,7 +119,12 @@ static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight) static unsigned int get_airtime_quantum(unsigned int max_wt) { - unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt; + unsigned int quantum; + + if (max_wt == 0) + return AIRTIME_QUANTUM_MAX; + + quantum = AIRTIME_QUANTUM_TARGET / max_wt; if (quantum < AIRTIME_QUANTUM_MIN) quantum = AIRTIME_QUANTUM_MIN; diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c index 565b587166da..c5a5f3a917cf 100644 --- a/contrib/wpa/src/ap/ap_config.c +++ b/contrib/wpa/src/ap/ap_config.c @@ -170,6 +170,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_TESTING_OPTIONS bss->sae_commit_status = -1; bss->test_assoc_comeback_type = -1; + bss->association_response_status_code = -1; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_PASN @@ -177,6 +178,13 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->pasn_comeback_after = 10; bss->pasn_noauth = 1; #endif /* CONFIG_PASN */ + bss->urnm_mfpr_x20 = -1; + bss->urnm_mfpr = -1; +#ifdef CONFIG_ENC_ASSOC + bss->assoc_frame_encryption = 0; + bss->pmksa_caching_privacy = 0; + bss->eap_using_authentication_frames = 0; +#endif /* CONFIG_ENC_ASSOC */ } @@ -306,6 +314,8 @@ struct hostapd_config * hostapd_config_defaults(void) hostapd_set_and_check_bw320_offset(conf, 0); + conf->disable_mcs15_rx = true; + return conf; } @@ -491,34 +501,48 @@ int hostapd_setup_sae_pt(struct hostapd_bss_config *conf) #ifdef CONFIG_SAE struct hostapd_ssid *ssid = &conf->ssid; struct sae_password_entry *pw; + int *groups = conf->sae_groups; + int default_groups[] = { 19, 0, 0 }; if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !hostapd_sae_pw_id_in_use(conf) && - !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) && + !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2) && !hostapd_sae_pk_in_use(conf)) || conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK || - !wpa_key_mgmt_sae(conf->wpa_key_mgmt)) + !wpa_key_mgmt_sae(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2)) return 0; /* PT not needed */ + if (!groups) { + groups = default_groups; + if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2)) + default_groups[1] = 20; + } + sae_deinit_pt(ssid->pt); ssid->pt = NULL; if (ssid->wpa_passphrase) { - ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid, - ssid->ssid_len, + ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, (const u8 *) ssid->wpa_passphrase, os_strlen(ssid->wpa_passphrase), - NULL); + NULL, 0); if (!ssid->pt) return -1; } for (pw = conf->sae_passwords; pw; pw = pw->next) { sae_deinit_pt(pw->pt); - pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid, - ssid->ssid_len, + pw->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, (const u8 *) pw->password, os_strlen(pw->password), - pw->identifier); + (const u8 *) pw->identifier, + pw->identifier ? + os_strlen(pw->identifier) : 0); if (!pw->pt) return -1; } @@ -779,6 +803,8 @@ static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) #ifdef CONFIG_SAE_PK sae_deinit_pk(tmp->pk); #endif /* CONFIG_SAE_PK */ + os_free(tmp->success_mac); + os_free(tmp->fail_mac); os_free(tmp); } } @@ -865,6 +891,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); os_free(conf->time_zone); + os_free(conf->supported_rates); + os_free(conf->basic_rates); #ifdef CONFIG_IEEE80211R_AP hostapd_config_clear_rxkhs(conf); @@ -917,31 +945,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_wan_metrics); os_free(conf->hs20_connection_capability); os_free(conf->hs20_operating_class); - os_free(conf->hs20_icons); - if (conf->hs20_osu_providers) { - for (i = 0; i < conf->hs20_osu_providers_count; i++) { - struct hs20_osu_provider *p; - size_t j; - p = &conf->hs20_osu_providers[i]; - os_free(p->friendly_name); - os_free(p->server_uri); - os_free(p->method_list); - for (j = 0; j < p->icons_count; j++) - os_free(p->icons[j]); - os_free(p->icons); - os_free(p->osu_nai); - os_free(p->osu_nai2); - os_free(p->service_desc); - } - os_free(conf->hs20_osu_providers); - } - if (conf->hs20_operator_icon) { - for (i = 0; i < conf->hs20_operator_icon_count; i++) - os_free(conf->hs20_operator_icon[i]); - os_free(conf->hs20_operator_icon); - } - os_free(conf->subscr_remediation_url); - os_free(conf->hs20_sim_provisioning_url); os_free(conf->t_c_filename); os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ @@ -960,6 +963,11 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(conf->own_ie_override); + wpabuf_free(conf->rsne_override); + wpabuf_free(conf->rsnoe_override); + wpabuf_free(conf->rsno2e_override); + wpabuf_free(conf->rsnxe_override); + wpabuf_free(conf->rsnxoe_override); wpabuf_free(conf->sae_commit_override); wpabuf_free(conf->rsne_override_eapol); wpabuf_free(conf->rsnxe_override_eapol); @@ -1008,12 +1016,33 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #ifdef CONFIG_PASN os_free(conf->pasn_groups); +#ifdef CONFIG_TESTING_OPTIONS + os_free(conf->pasn_test_groups); +#endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ + wpabuf_clear_free(conf->sae_pw_id_key); + os_free(conf); } +#ifdef CONFIG_AFC +void hostapd_config_free_afc_cert_ids(struct hostapd_config *conf) +{ + unsigned int i; + + for (i = 0; i < conf->afc.n_cert_ids; i++) { + os_free(conf->afc.cert_ids[i].ruleset); + os_free(conf->afc.cert_ids[i].id); + } + os_free(conf->afc.cert_ids); + conf->afc.cert_ids = NULL; + conf->afc.n_cert_ids = 0; +} +#endif /* CONFIG_AFC */ + + /** * hostapd_config_free - Free hostapd configuration * @conf: Configuration data from hostapd_config_read(). @@ -1028,8 +1057,6 @@ void hostapd_config_free(struct hostapd_config *conf) for (i = 0; i < conf->num_bss; i++) hostapd_config_free_bss(conf->bss[i]); os_free(conf->bss); - os_free(conf->supported_rates); - os_free(conf->basic_rates); os_free(conf->acs_ch_list.range); os_free(conf->acs_freq_list.range); os_free(conf->driver_params); @@ -1038,6 +1065,18 @@ void hostapd_config_free(struct hostapd_config *conf) #endif /* CONFIG_ACS */ wpabuf_free(conf->lci); wpabuf_free(conf->civic); +#ifdef CONFIG_AFC + os_free(conf->afc.socket); + os_free(conf->afc.request.version); + os_free(conf->afc.request.sn); + hostapd_config_free_afc_cert_ids(conf); + os_free(conf->afc.cert_ids); + os_free(conf->afc.location.height_type); + os_free(conf->afc.location.linear_polygon_data); + os_free(conf->afc.location.radial_polygon_data); + os_free(conf->afc.freqs.range); + os_free(conf->afc.op_class); +#endif /* CONFIG_AFC */ os_free(conf); } @@ -1079,21 +1118,6 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, } -int hostapd_rate_found(int *list, int rate) -{ - int i; - - if (list == NULL) - return 0; - - for (i = 0; list[i] >= 0; i++) - if (list[i] == rate) - return 1; - - return 0; -} - - int hostapd_vlan_valid(struct hostapd_vlan *vlan, struct vlan_description *vlan_desc) { @@ -1204,7 +1228,7 @@ static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss) #endif /* CONFIG_SAE_PK */ -static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss) +bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss) { if (bss->wpa != WPA_PROTO_RSN) { wpa_printf(MSG_ERROR, @@ -1324,6 +1348,22 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } } + if (full_config) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (conf->bss[i] != bss && + os_strcmp(conf->bss[i]->iface, bss->iface) == 0) { + wpa_printf(MSG_ERROR, + "Duplicate interface '%s' for BSSID " + MACSTR " and " MACSTR, bss->iface, + MAC2STR(conf->bss[i]->bssid), + MAC2STR(bss->bssid)); + return -1; + } + } + } + #ifdef CONFIG_IEEE80211R_AP if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && (bss->nas_identifier == NULL || @@ -1491,9 +1531,31 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, #ifdef CONFIG_IEEE80211BE if (full_config && !bss->disable_11be && bss->disable_11ax) { bss->disable_11be = true; + bss->mld_ap = 0; wpa_printf(MSG_INFO, "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS"); } + + if (full_config && conf->ieee80211be && !bss->disable_11be && + !bss->beacon_prot && ap_pmf_enabled(bss)) { + bss->beacon_prot = 1; + wpa_printf(MSG_INFO, + "Enabling beacon protection as IEEE 802.11be is enabled for this BSS"); + } + + if (full_config && (!conf->ieee80211be || bss->disable_11be) && + bss->mld_ap) { + wpa_printf(MSG_INFO, + "Cannot enable mld_ap when IEEE 802.11be is disabled"); + return -1; + } + + if (full_config && (!conf->ieee80211be || bss->disable_11be) && + bss->bss_require_eht) { + bss->bss_require_eht = false; + wpa_printf(MSG_INFO, + "Disabling bss_require_eht as IEEE 802.11be is disabled for this BSS"); + } #endif /* CONFIG_IEEE80211BE */ if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) { @@ -1502,6 +1564,13 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, return -1; } + /* Do not advertise SPP A-MSDU support if not using CCMP/GCMP */ + if (full_config && bss->spp_amsdu && + !(bss->wpa && + bss->rsn_pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP))) + bss->spp_amsdu = false; + return 0; } @@ -1668,11 +1737,6 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, if (full_config) bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; #endif /* CONFIG_WEP */ - } else if (bss->osen) { - bss->ssid.security_policy = SECURITY_OSEN; - bss->wpa_group = WPA_CIPHER_CCMP; - bss->wpa_pairwise = 0; - bss->rsn_pairwise = WPA_CIPHER_CCMP; } else { bss->ssid.security_policy = SECURITY_PLAINTEXT; if (full_config) { diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h index ced2181ab8ae..726262c8170b 100644 --- a/contrib/wpa/src/ap/ap_config.h +++ b/contrib/wpa/src/ap/ap_config.h @@ -93,7 +93,6 @@ typedef enum hostap_security_policy { SECURITY_IEEE_802_1X = 2, SECURITY_WPA_PSK = 3, SECURITY_WPA = 4, - SECURITY_OSEN = 5 } secpolicy; struct hostapd_ssid { @@ -189,7 +188,6 @@ struct hostapd_eap_user { unsigned int wildcard_prefix:1; unsigned int password_hash:1; /* whether password is hashed with * nt_password_hash() */ - unsigned int remediation:1; unsigned int macacl:1; int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ struct hostapd_radius_attr *accept_attr; @@ -260,6 +258,10 @@ struct sae_password_entry { int vlan_id; struct sae_pt *pt; struct sae_pk *pk; + u8 *success_mac; + unsigned int num_success_mac, next_success_mac; + u8 *fail_mac; + unsigned int num_fail_mac, next_fail_mac; }; struct dpp_controller_conf { @@ -293,6 +295,9 @@ struct hostapd_bss_config { int max_num_sta; /* maximum number of STAs in station table */ + enum beacon_rate_type rate_type; + unsigned int beacon_rate; + int dtim_period; unsigned int bss_load_update_period; unsigned int chan_util_avg_period; @@ -325,6 +330,9 @@ struct hostapd_bss_config { struct hostapd_ssid ssid; + int *supported_rates; + int *basic_rates; + char *eap_req_id_text; /* optional displayable message sent with * EAP Request-Identity */ size_t eap_req_id_text_len; @@ -358,7 +366,11 @@ struct hostapd_bss_config { int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ int extended_key_id; int wpa_key_mgmt; + int rsn_override_key_mgmt; + int rsn_override_key_mgmt_2; enum mfp_options ieee80211w; + enum mfp_options rsn_override_mfp; + enum mfp_options rsn_override_mfp_2; int group_mgmt_cipher; int beacon_prot; /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ @@ -387,9 +399,14 @@ struct hostapd_bss_config { u32 wpa_pairwise_update_count; int wpa_disable_eapol_key_retries; int rsn_pairwise; + int rsn_override_pairwise; + int rsn_override_pairwise_2; int rsn_preauth; char *rsn_preauth_interfaces; + int rsn_override_omit_rsnxe; + int rsn_override_mlo_compat; + #ifdef CONFIG_IEEE80211R_AP /* IEEE 802.11r - Fast BSS Transition */ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; @@ -443,7 +460,6 @@ struct hostapd_bss_config { int pac_key_lifetime; int pac_key_refresh_time; int eap_teap_auth; - int eap_teap_pac_no_inner; int eap_teap_separate_result; int eap_teap_id; int eap_teap_method_sequence; @@ -458,6 +474,7 @@ struct hostapd_bss_config { char *radius_server_clients; int radius_server_auth_port; int radius_server_acct_port; + int radius_server_acct_log; int radius_server_ipv6; int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group @@ -554,6 +571,10 @@ struct hostapd_bss_config { bool disable_11ax; bool disable_11be; +#ifdef CONFIG_IEEE80211BE + bool bss_require_eht; +#endif /* CONFIG_IEEE80211BE */ + /* IEEE 802.11v */ int time_advertisement; char *time_zone; @@ -613,7 +634,6 @@ struct hostapd_bss_config { u8 qos_map_set[16 + 2 * 21]; unsigned int qos_map_set_len; - int osen; int proxy_arp; int na_mcast_to_ucast; @@ -629,37 +649,7 @@ struct hostapd_bss_config { size_t hs20_connection_capability_len; u8 *hs20_operating_class; u8 hs20_operating_class_len; - struct hs20_icon { - u16 width; - u16 height; - char language[3]; - char type[256]; - char name[256]; - char file[256]; - } *hs20_icons; - size_t hs20_icons_count; - u8 osu_ssid[SSID_MAX_LEN]; - size_t osu_ssid_len; - struct hs20_osu_provider { - unsigned int friendly_name_count; - struct hostapd_lang_string *friendly_name; - char *server_uri; - int *method_list; - char **icons; - size_t icons_count; - char *osu_nai; - char *osu_nai2; - unsigned int service_desc_count; - struct hostapd_lang_string *service_desc; - } *hs20_osu_providers, *last_osu; - size_t hs20_osu_providers_count; - size_t hs20_osu_providers_nai_count; - char **hs20_operator_icon; - size_t hs20_operator_icon_count; unsigned int hs20_deauth_req_timeout; - char *subscr_remediation_url; - u8 subscr_remediation_method; - char *hs20_sim_provisioning_url; char *t_c_filename; u32 t_c_timestamp; char *t_c_server_url; @@ -679,8 +669,18 @@ struct hostapd_bss_config { int sae_require_mfp; int sae_confirm_immediate; enum sae_pwe sae_pwe; + /* Accept an SAE association even when the STA advertises SAE H2E + * support in the RSNXE but authenticated using hunting-and-pecking. + * This is a workaround for WPA3-Personal Compatibility Mode and + * RSNXE-override interoperability (e.g., some MLO STAs that add only + * the RSNXE Override element and do not use H2E on 2.4/5 GHz) */ + bool sae_accept_h2e_without_use; int *sae_groups; struct sae_password_entry *sae_passwords; + int sae_password_psk; + int sae_track_password; + struct wpabuf *sae_pw_id_key; + unsigned int sae_pw_id_num; char *wowlan_triggers; /* Wake-on-WLAN triggers */ @@ -688,6 +688,11 @@ struct hostapd_bss_config { u8 bss_load_test[5]; u8 bss_load_test_set; struct wpabuf *own_ie_override; + struct wpabuf *rsne_override; + struct wpabuf *rsnoe_override; + struct wpabuf *rsno2e_override; + struct wpabuf *rsnxe_override; + struct wpabuf *rsnxoe_override; int sae_reflection_attack; int sae_commit_status; int sae_pk_omit; @@ -712,8 +717,10 @@ struct hostapd_bss_config { struct wpabuf *eapol_m1_elements; struct wpabuf *eapol_m3_elements; bool eapol_m3_no_encrypt; + bool eapol_key_reserved_random; int test_assoc_comeback_type; struct wpabuf *presp_elements; + int association_response_status_code; #ifdef CONFIG_IEEE80211BE u16 eht_oper_puncturing_override; @@ -905,6 +912,14 @@ struct hostapd_bss_config { */ int macsec_csindex; + /** + * macsec_icv_indicator - Always include ICV Indicator + * (for compatibility with older MACsec switches) + * + * Range: 0-1 (default: 0) + */ + int macsec_icv_indicator; + /** * mka_ckn - MKA pre-shared CKN */ @@ -932,6 +947,11 @@ struct hostapd_bss_config { /* Whether to allow PASN-UNAUTH */ int pasn_noauth; +#ifdef CONFIG_ENC_ASSOC + /* Whether to allow unauthenticated EPPKE (EPPKE without base AKM) */ + bool eppke_unauth; +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_TESTING_OPTIONS /* * Normally, KDK should be derived if and only if both sides support @@ -941,6 +961,12 @@ struct hostapd_bss_config { /* If set, corrupt the MIC in the 2nd Authentication frame of PASN */ int pasn_corrupt_mic; + + /* + * If set, override Supported Groups element in the 2nd Authentication + * frame of PASN for group negotiation testing. + */ + int *pasn_test_groups; #endif /* CONFIG_TESTING_OPTIONS */ int *pasn_groups; @@ -952,6 +978,9 @@ struct hostapd_bss_config { u16 pasn_comeback_after; #endif /* CONFIG_PASN */ + int urnm_mfpr_x20; + int urnm_mfpr; + unsigned int unsol_bcast_probe_resp_interval; u8 ext_capa_mask[EXT_CAPA_MAX_LEN]; @@ -959,9 +988,9 @@ struct hostapd_bss_config { u8 rnr; char *config_id; - bool xrates_supported; bool ssid_protection; + bool known_sta_identification; #ifdef CONFIG_IEEE80211BE /* The AP is part of an AP MLD */ @@ -981,6 +1010,14 @@ struct hostapd_bss_config { bool mld_indicate_disabled; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_IEEE80211BE */ + int mbssid_index; + + bool spp_amsdu; +#ifdef CONFIG_ENC_ASSOC + unsigned int assoc_frame_encryption:1; + unsigned int pmksa_caching_privacy:1; + unsigned int eap_using_authentication_frames:1; +#endif /* CONFIG_ENC_ASSOC */ }; /** @@ -1057,11 +1094,6 @@ struct hostapd_config { SHORT_PREAMBLE = 1 } preamble; - int *supported_rates; - int *basic_rates; - unsigned int beacon_rate; - enum beacon_rate_type rate_type; - const struct wpa_driver_ops *driver; char *driver_params; @@ -1139,6 +1171,7 @@ struct hostapd_config { double ignore_reassoc_probability; double corrupt_gtk_rekey_mic_probability; int ecsa_ie_only; + int csa_ie_only; bool delay_eapol_tx; #endif /* CONFIG_TESTING_OPTIONS */ @@ -1220,6 +1253,7 @@ struct hostapd_config { u8 punct_acs_threshold; u8 eht_default_pe_duration; u8 eht_bw320_offset; + bool require_eht; #endif /* CONFIG_IEEE80211BE */ /* EHT enable/disable config from CHAN_SWITCH */ @@ -1232,9 +1266,60 @@ struct hostapd_config { MBSSID_ENABLED = 1, ENHANCED_MBSSID_ENABLED = 2, } mbssid; + unsigned int mbssid_max; /* Whether to enable TWT responder in HT and VHT modes */ bool ht_vht_twt_responder; + + bool channel_usage; + bool peer_to_peer_twt; + + /* Set I2R LMR policy to allow LMR response from ISTA */ + bool i2r_lmr_policy; + + /* Disable MCS15 Subfield in EHT operation element */ + bool disable_mcs15_rx; + +#ifdef CONFIG_AFC + struct { + char *socket; + struct { + char *version; + char *sn; + } request; + unsigned int n_cert_ids; + struct afc_cert_id { + char *ruleset; + char *id; + } *cert_ids; + struct { + enum afc_location_type { + AFC_ELLIPSE, + AFC_LINEAR_POLYGON, + AFC_RADIAL_POLYGON, + } type; + unsigned int n_linear_polygon_data; + struct afc_linear_polygon { + double longitude; + double latitude; + } *linear_polygon_data; + unsigned int n_radial_polygon_data; + struct afc_radial_polygon { + double length; + double angle; + } *radial_polygon_data; + int major_axis; + int minor_axis; + int orientation; + double height; + char *height_type; + int vertical_tolerance; + } location; + struct wpa_freq_range_list freqs; + int *op_class; /* int array */ + int min_power; + } afc; +#endif /* CONFIG_AFC */ }; @@ -1373,7 +1458,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, const u8 *addr, struct vlan_description *vlan_id); -int hostapd_rate_found(int *list, int rate); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, int *vlan_id); @@ -1397,5 +1481,7 @@ int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, int vlan_id, const u8 *addr); void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, const u8 *addr); +bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss); +void hostapd_config_free_afc_cert_ids(struct hostapd_config *conf); #endif /* HOSTAPD_CONFIG_H */ diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c index c47349110e5f..4e4c637d5903 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.c +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -21,6 +21,7 @@ #include "p2p_hostapd.h" #include "hs20.h" #include "wpa_auth.h" +#include "hw_features.h" #include "ap_drv_ops.h" @@ -39,6 +40,8 @@ u32 hostapd_sta_flags_to_drv(u32 flags) res |= WPA_STA_AUTHENTICATED; if (flags & WLAN_STA_ASSOC) res |= WPA_STA_ASSOCIATED; + if (flags & WLAN_STA_SPP_AMSDU) + res |= WPA_STA_SPP_AMSDU; return res; } @@ -116,9 +119,11 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, goto fail; #endif /* CONFIG_FILS */ - pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf)); - if (add_buf_data(&assocresp, buf, pos - buf) < 0) - goto fail; + if (!hapd->conf->rsn_override_omit_rsnxe) { + pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf)); + if (add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; + } if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) @@ -181,11 +186,6 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, if (add_buf_data(&beacon, buf, pos - buf) < 0 || add_buf_data(&proberesp, buf, pos - buf) < 0) goto fail; - - pos = hostapd_eid_osen(hapd, buf); - if (add_buf_data(&beacon, buf, pos - buf) < 0 || - add_buf_data(&proberesp, buf, pos - buf) < 0) - goto fail; #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO @@ -370,7 +370,14 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) { char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; - return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, + const u8 *addr = hapd->own_addr; + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + addr = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, addr, NULL, NULL, force_ifname, if_addr, NULL, 0); } @@ -472,7 +479,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, size_t eht_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) + int set, const u8 *link_addr, bool mld_link_sta, + u16 eml_cap, bool epp_sta) { struct hostapd_sta_add_params params; @@ -502,6 +510,9 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.support_p2p_ps = supp_p2p_ps; params.set = set; params.mld_link_id = -1; +#ifdef CONFIG_ENC_ASSOC + params.epp_sta = epp_sta; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_IEEE80211BE /* @@ -512,6 +523,9 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.mld_link_id = hapd->mld_link_id; params.mld_link_addr = link_addr; params.mld_link_sta = mld_link_sta; + /* Copy EML capabilities of ML STA */ + if (link_addr) + params.eml_cap = eml_cap; } #endif /* CONFIG_IEEE80211BE */ @@ -519,16 +533,6 @@ int hostapd_sta_add(struct hostapd_data *hapd, } -int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, - u8 *tspec_ie, size_t tspec_ielen) -{ - if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) - return 0; - return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, - tspec_ielen); -} - - int hostapd_set_privacy(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) @@ -754,15 +758,7 @@ hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, !hapd->drv_priv) return NULL; return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, - flags, dfs_domain); -} - - -int hostapd_driver_commit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->commit == NULL) - return 0; - return hapd->driver->commit(hapd->drv_priv); + flags, dfs_domain, NULL, 0); } @@ -781,6 +777,37 @@ bool hostapd_drv_nl80211(struct hostapd_data *hapd) int hostapd_driver_scan(struct hostapd_data *hapd, struct wpa_driver_scan_params *params) { + params->link_id = -1; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + params->link_id = hapd->mld_link_id; + + if (!hapd->iface->scan_cb && hapd->conf->mld_ap && + hapd->iface->interfaces) { + /* Other links may be waiting for scan results */ + unsigned int i; + + for (i = 0; i < hapd->iface->interfaces->count; i++) { + struct hostapd_iface *h_iface = + hapd->iface->interfaces->iface[i]; + struct hostapd_data *h_hapd; + + if (!h_iface || h_iface == hapd->iface || + h_iface->num_bss == 0) + continue; + + h_hapd = h_iface->bss[0]; + + if (hostapd_is_ml_partner(hapd, h_hapd) && + h_hapd->iface->state == HAPD_IFACE_ACS) { + wpa_printf(MSG_INFO, + "ACS in progress in a partner link - try to scan later"); + return -EBUSY; + } + } + } +#endif /* CONFIG_IEEE80211BE */ + if (hapd->driver && hapd->driver->scan2) return hapd->driver->scan2(hapd->drv_priv, params); return -1; @@ -889,20 +916,23 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { const u8 *own_addr = hapd->own_addr; + int link_id = -1; #ifdef CONFIG_IEEE80211BE if (hapd->conf->mld_ap) { struct sta_info *sta = ap_get_sta(hapd, addr); - if (ap_sta_is_mld(hapd, sta)) + if (ap_sta_is_mld(hapd, sta)) { own_addr = hapd->mld->mld_addr; + link_id = hapd->mld_link_id; + } } #endif /* CONFIG_IEEE80211BE */ if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv) return 0; return hapd->driver->sta_disassoc(hapd->drv_priv, own_addr, addr, - reason); + reason, link_id); } @@ -916,9 +946,50 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, } +#ifdef CONFIG_IEEE80211BE +static bool hostapd_is_action_frame_link_agnostic(u8 category, u8 sub_category) +{ + /* As per IEEE Std 802.11be-2024, 35.3.14 (MLD individually addressed + * Management frame delivery), between an AP MLD and a non-AP MLD, the + * following individually addressed MMPDUs shall be intended for an MLD. + */ + switch (category) { + case WLAN_ACTION_BLOCK_ACK: + case WLAN_ACTION_FT: + case WLAN_ACTION_SA_QUERY: + case WLAN_ACTION_WNM: + switch (sub_category) { + case WNM_BSS_TRANS_MGMT_REQ: + case WNM_BSS_TRANS_MGMT_RESP: + case WNM_SLEEP_MODE_REQ: + case WNM_SLEEP_MODE_RESP: + return true; + default: + return false; + } + case WLAN_ACTION_ROBUST_AV_STREAMING: + switch (sub_category) { + case ROBUST_AV_SCS_REQ: + case ROBUST_AV_SCS_RESP: + case ROBUST_AV_MSCS_REQ: + case ROBUST_AV_MSCS_RESP: + return true; + default: + return false; + } + /* TODO: Handle EHT/EPCS related action frames once the support is + * added. */ + default: + return false; + } +} +#endif /* CONFIG_IEEE80211BE */ + + static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, - const u8 *data, size_t len, bool addr3_ap) + const u8 *data, size_t len, bool addr3_ap, + const u8 *forced_a3) { const u8 *own_addr = hapd->own_addr; const u8 *bssid; @@ -926,12 +997,15 @@ static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct sta_info *sta; + int link_id = -1; if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv) return 0; bssid = hapd->own_addr; - if (!addr3_ap && !is_multicast_ether_addr(dst) && - len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + if (forced_a3) { + bssid = forced_a3; + } else if (!addr3_ap && !is_multicast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { /* * Public Action frames to a STA that is not a member of the BSS * shall use wildcard BSSID value. @@ -956,11 +1030,15 @@ static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, own_addr = hapd->mld->mld_addr; bssid = own_addr; } + + if (!hostapd_is_action_frame_link_agnostic(data[0], data[1])) + link_id = hapd->mld_link_id; #endif /* CONFIG_IEEE80211BE */ } return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, - own_addr, bssid, data, len, 0); + own_addr, bssid, data, len, 0, + link_id); } @@ -968,7 +1046,8 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len) { - return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false); + return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false, + NULL); } @@ -977,7 +1056,19 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, unsigned int wait, const u8 *dst, const u8 *data, size_t len) { - return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true); + return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true, + NULL); +} + + +int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *a3, + const u8 *data, size_t len) +{ + return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false, + a3); } @@ -1018,6 +1109,12 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, } data.radar_background = radar_background; + data.link_id = -1; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + data.link_id = hapd->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ + res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); if (!res) { if (radar_background) @@ -1049,6 +1146,10 @@ void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, { int i; bool is_no_ir = false; + bool allow_6g_acs = is_6ghz_op_class(hapd->iconf->op_class) && + hostapd_config_check_bss_6g(hapd->conf) && + (hapd->iface->conf->ieee80211ax || + hapd->iface->conf->ieee80211be); for (i = 0; i < mode->num_channels; i++) { struct hostapd_channel_data *chan = &mode->channels[i]; @@ -1069,8 +1170,7 @@ void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, if (is_6ghz_freq(chan->freq) && ((hapd->iface->conf->acs_exclude_6ghz_non_psc && !is_6ghz_psc_frequency(chan->freq)) || - (!hapd->iface->conf->ieee80211ax && - !hapd->iface->conf->ieee80211be))) + !allow_6g_acs)) continue; if ((!(chan->flag & HOSTAPD_CHAN_DISABLED) || allow_disabled) && !(hapd->iface->conf->acs_exclude_dfs && @@ -1133,8 +1233,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) params.hw_mode = hapd->iface->conf->hw_mode; params.link_id = -1; #ifdef CONFIG_IEEE80211BE - if (hapd->conf->mld_ap && hapd->iconf->ieee80211be && - !hapd->conf->disable_11be) + if (hapd->conf->mld_ap) params.link_id = hapd->mld_link_id; #endif /* CONFIG_IEEE80211BE */ @@ -1250,3 +1349,60 @@ int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd, return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms); } #endif /* CONFIG_PASN */ + + +struct hostapd_multi_hw_info * +hostapd_get_multi_hw_info(struct hostapd_data *hapd, + unsigned int *num_multi_hws) +{ + if (!hapd->driver || !hapd->driver->get_multi_hw_info) + return NULL; + + return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws); +} + + +int hostapd_drv_add_pmkid(struct hostapd_data *hapd, + struct wpa_pmkid_params *params) +{ + if (!hapd->driver || !hapd->driver->add_pmkid || !hapd->drv_priv) + return 0; + return hapd->driver->add_pmkid(hapd->drv_priv, params); +} + + +int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk, + size_t pmk_len, const u8 *pmkid, int akmp) +{ + struct wpa_pmkid_params params; + + os_memset(¶ms, 0, sizeof(params)); + params.bssid = bssid; + params.pmkid = pmkid; + params.pmk = pmk; + params.pmk_len = pmk_len; + + return hostapd_drv_add_pmkid(hapd, ¶ms); +} + + +static int hostapd_drv_remove_pmkid(struct hostapd_data *hapd, + struct wpa_pmkid_params *params) +{ + if (!hapd->driver || !hapd->driver->remove_pmkid || !hapd->drv_priv) + return 0; + return hapd->driver->remove_pmkid(hapd->drv_priv, params); +} + + +int hostapd_remove_pmkid(struct hostapd_data *hapd, const u8 *sta_addr, + const u8 *pmkid) +{ + struct wpa_pmkid_params params; + + os_memset(¶ms, 0, sizeof(params)); + params.bssid = sta_addr; + params.pmkid = pmkid; + + return hostapd_drv_remove_pmkid(hapd, ¶ms); +} diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h index d7e79c840cb8..66913ed0fca7 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.h +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -49,7 +49,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, size_t eht_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); + int set, const u8 *link_addr, bool mld_link_sta, + u16 eml_cap, bool epp_sta); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -86,7 +87,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, u16 *flags, u8 *dfs_domain); -int hostapd_driver_commit(struct hostapd_data *hapd); int hostapd_drv_none(struct hostapd_data *hapd); bool hostapd_drv_nl80211(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, @@ -116,6 +116,11 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *a3, + const u8 *data, size_t len); static inline void hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) { @@ -130,8 +135,6 @@ int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len); int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); -int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, - u8 *tspec_ie, size_t tspec_ielen); int hostapd_start_dfs_cac(struct hostapd_iface *iface, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, @@ -267,14 +270,6 @@ static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd, return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); } -static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, - int auth_algs) -{ - if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) - return 0; - return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); -} - static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, const u8 *own_addr, const u8 *addr, int qos) @@ -393,7 +388,7 @@ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) { - int link_id = -1; + int link_id = -1, ret; if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv) return 0; @@ -401,7 +396,13 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) if (hapd->conf->mld_ap) link_id = hapd->mld_link_id; #endif /* CONFIG_IEEE80211BE */ - return hapd->driver->stop_ap(hapd->drv_priv, link_id); + + ret = hapd->driver->stop_ap(hapd->drv_priv, link_id); + if (ret) + return ret; + + hapd->beacon_set_done = 0; + return 0; } static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, @@ -478,4 +479,15 @@ static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211BE */ +struct hostapd_multi_hw_info * +hostapd_get_multi_hw_info(struct hostapd_data *hapd, + unsigned int *num_multi_hws); + +int hostapd_drv_add_pmkid(struct hostapd_data *hapd, + struct wpa_pmkid_params *params); +int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk, + size_t pmk_len, const u8 *pmkid, int akmp); +int hostapd_remove_pmkid(struct hostapd_data *hapd, const u8 *sta_addr, + const u8 *pmkid); + #endif /* AP_DRV_OPS */ diff --git a/contrib/wpa/src/ap/ap_mlme.c b/contrib/wpa/src/ap/ap_mlme.c index 309e69a3f95d..608b99213cd1 100644 --- a/contrib/wpa/src/ap/ap_mlme.c +++ b/contrib/wpa/src/ap/ap_mlme.c @@ -112,7 +112,9 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) if (sta->auth_alg != WLAN_AUTH_FT && sta->auth_alg != WLAN_AUTH_FILS_SK && sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && - sta->auth_alg != WLAN_AUTH_FILS_PK) + sta->auth_alg != WLAN_AUTH_FILS_PK && + sta->auth_alg != WLAN_AUTH_EPPKE && + sta->auth_alg != WLAN_AUTH_802_1X) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -140,7 +142,9 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, if (sta->auth_alg != WLAN_AUTH_FT && sta->auth_alg != WLAN_AUTH_FILS_SK && sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && - sta->auth_alg != WLAN_AUTH_FILS_PK) + sta->auth_alg != WLAN_AUTH_FILS_PK && + sta->auth_alg != WLAN_AUTH_EPPKE && + sta->auth_alg != WLAN_AUTH_802_1X) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c index 837b6909bde5..3e8031810f45 100644 --- a/contrib/wpa/src/ap/authsrv.c +++ b/contrib/wpa/src/ap/authsrv.c @@ -14,6 +14,7 @@ #include "eap_server/eap.h" #include "eap_server/eap_sim_db.h" #include "eapol_auth/eapol_auth_sm.h" +#include "radius/radius.h" #include "radius/radius_server.h" #include "hostapd.h" #include "ap_config.h" @@ -89,7 +90,6 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; - user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; user->t_c_timestamp = eap_user->t_c_timestamp; rv = 0; @@ -102,6 +102,114 @@ out: } +/** + * hostapd_radius_log_acct_req - Callback for logging received RADIUS + * accounting requests + * @ctx: Context (struct hostapd_data) + * @msg: Received RADIUS accounting request + * @status_type: Status type from the message (parsed Acct-Status-Type + * attribute) + * Returns: 0 on success, -1 on failure + */ +static int hostapd_radius_log_acct_req(void *ctx, struct radius_msg *msg, + u32 status_type) +{ + char nas_id[RADIUS_MAX_ATTR_LEN + 1] = ""; + char session_id[RADIUS_MAX_ATTR_LEN + 1] = ""; + char username[RADIUS_MAX_ATTR_LEN + 1] = ""; + char calling_station_id[3 * ETH_ALEN] = ""; + u32 session_time = 0, terminate_cause = 0, + bytes_in = 0, bytes_out = 0, + packets_in = 0, packets_out = 0, + gigawords_in = 0, gigawords_out = 0; + unsigned long long total_bytes_in = 0, total_bytes_out = 0; + + /* Parse NAS identification (required by RFC 2866, section 4.1) */ + if (radius_msg_get_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) nas_id, + sizeof(nas_id) - 1)) + nas_id[0] = '\0'; + + /* Process Accounting-On and Accounting-Off messages separately */ + if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON || + status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) { + wpa_printf(MSG_INFO, "RADIUS ACCT: NAS='%s' status='%s'", + nas_id, + status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON + ? "Accounting-On" : "Accounting-Off"); + return 0; + } + + /* Parse session ID (required by RFC 2866, section 5.5) */ + if (radius_msg_get_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) session_id, + sizeof(session_id) - 1) == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS ACCT: request doesn't include session ID"); + return -1; + } + + /* Parse user name */ + radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) username, + sizeof(username) - 1); + + /* Parse device identifier */ + radius_msg_get_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) calling_station_id, + sizeof(calling_station_id) - 1); + + switch (status_type) { + case RADIUS_ACCT_STATUS_TYPE_START: + wpa_printf(MSG_INFO, + "RADIUS ACCT: NAS='%s' session='%s' status='Accounting-Start' station='%s' username='%s'", + nas_id, session_id, calling_station_id, username); + break; + case RADIUS_ACCT_STATUS_TYPE_STOP: + case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE: + /* Parse counters */ + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, + &session_time); + radius_msg_get_attr_int32(msg, + RADIUS_ATTR_ACCT_TERMINATE_CAUSE, + &terminate_cause); + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, + &bytes_in); + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, + &bytes_out); + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS, + &packets_in); + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS, + &packets_out); + radius_msg_get_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + &gigawords_in); + radius_msg_get_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + &gigawords_out); + + /* RFC 2869, section 5.1 and 5.2 */ + total_bytes_in = ((u64) gigawords_in << 32) + bytes_in; + total_bytes_out = ((u64) gigawords_out << 32) + bytes_out; + + wpa_printf(MSG_INFO, + "RADIUS ACCT: NAS='%s' session='%s' status='%s' station='%s' username='%s' session_time=%u term_cause=%u pck_in=%u pck_out=%u bytes_in=%llu bytes_out=%llu", + nas_id, session_id, + status_type == RADIUS_ACCT_STATUS_TYPE_STOP ? + "Accounting-Stop" : "Accounting-Interim-Update", + calling_station_id, username, session_time, + terminate_cause, packets_in, packets_out, + total_bytes_in, total_bytes_out); + break; + default: + wpa_printf(MSG_DEBUG, + "RADIUS ACCT: Unknown request status type %u", + status_type); + return -1; + } + + return 0; +} + + static int hostapd_setup_radius_srv(struct hostapd_data *hapd) { struct radius_server_conf srv; @@ -129,6 +237,8 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.conf_ctx = hapd; srv.ipv6 = conf->radius_server_ipv6; srv.get_eap_user = hostapd_radius_get_eap_user; + if (conf->radius_server_acct_log) + srv.acct_req_cb = hostapd_radius_log_acct_req; srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.sqlite_file = conf->eap_user_sqlite; @@ -136,9 +246,6 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.dump_msk_file = conf->dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ #ifdef CONFIG_HS20 - srv.subscr_remediation_url = conf->subscr_remediation_url; - srv.subscr_remediation_method = conf->subscr_remediation_method; - srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url; srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp_domain = conf->erp_domain; @@ -224,7 +331,6 @@ static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd) cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime; cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time; cfg->eap_teap_auth = hapd->conf->eap_teap_auth; - cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner; cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result; cfg->eap_teap_id = hapd->conf->eap_teap_id; cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence; diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c index cec0c9829fd9..1a0ac922e9e9 100644 --- a/contrib/wpa/src/ap/beacon.c +++ b/contrib/wpa/src/ap/beacon.c @@ -3,6 +3,7 @@ * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2012, Jouni Malinen + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -262,32 +263,47 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, { u8 *pos = eid; u8 *end = eid + max_len; + u8 op_class; + bool force_global; if (!hapd->iconf->ieee80211d || max_len < 6 || hapd->iface->current_mode == NULL) return eid; + op_class = hostapd_get_oper_class_of_bss(hapd); + *pos++ = WLAN_EID_COUNTRY; pos++; /* length will be set later */ os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ pos += 3; - if (is_6ghz_op_class(hapd->iconf->op_class)) { + /* The 6 GHz band uses global operating classes */ + force_global = is_6ghz_op_class(op_class); + +#ifdef CONFIG_MBO + /* Wi-Fi Agile Muiltiband AP is required to use a global operating + * class. */ + if (hapd->conf->mbo_enabled) + force_global = true; +#endif /* CONFIG_MBO */ + + if (force_global) { /* Force the third octet of the country string to indicate * Global Operating Class (Table E-4) */ eid[4] = 0x04; + } + if (is_6ghz_op_class(op_class)) { /* Operating Triplet field */ /* Operating Extension Identifier (>= 201 to indicate this is * not a Subband Triplet field) */ *pos++ = 201; /* Operating Class */ - *pos++ = hapd->iconf->op_class; + *pos++ = op_class; /* Coverage Class */ *pos++ = 0; /* Subband Triplets are required only for the 20 MHz case */ - if (hapd->iconf->op_class == 131 || - hapd->iconf->op_class == 136) + if (op_class == 131 || op_class == 136) pos = hostapd_fill_subband_triplets(hapd, pos, end); } else { pos = hostapd_fill_subband_triplets(hapd, pos, end); @@ -390,11 +406,12 @@ static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len) } -static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len) +static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos, + size_t len) { const u8 *ie; - ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE); + ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE); if (!ie || 2U + ie[1] > len) return pos; @@ -403,6 +420,67 @@ static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len) } +static u8 * hostapd_get_rsne_override_2(struct hostapd_data *hapd, u8 *pos, + size_t len) +{ + const u8 *ie; + + ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (!ie || 2U + ie[1] > len) + return pos; + + os_memcpy(pos, ie, 2 + ie[1]); + return pos + 2 + ie[1]; +} + + +static u8 * hostapd_get_rsnxe_override(struct hostapd_data *hapd, u8 *pos, + size_t len) +{ + const u8 *ie; + + ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (!ie || 2U + ie[1] > len) + return pos; + + os_memcpy(pos, ie, 2 + ie[1]); + return pos + 2 + ie[1]; +} + + +static size_t hostapd_get_rsne_override_len(struct hostapd_data *hapd) +{ + const u8 *ie; + + ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE); + if (!ie) + return 0; + return 2 + ie[1]; +} + + +static size_t hostapd_get_rsne_override_2_len(struct hostapd_data *hapd) +{ + const u8 *ie; + + ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (!ie) + return 0; + return 2 + ie[1]; +} + + +static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd) +{ + const u8 *ie; + + ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (!ie) + return 0; + return 2 + ie[1]; +} + + static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) { #ifdef CONFIG_TESTING_OPTIONS @@ -428,6 +506,11 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) return eid; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->csa_ie_only) + return eid; +#endif /* CONFIG_TESTING_OPTIONS */ + *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; *eid++ = 4; *eid++ = hapd->cs_block_tx; @@ -439,17 +522,56 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) } +static u8 * hostapd_eid_max_cs_time(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_IEEE80211BE + u32 switch_time; + + /* Add Max Channel Switch Time element only if this AP is affiliated + * with an AP MLD and channel switch is in process. */ + if (!hapd->conf->mld_ap || !hapd->cs_freq_params.channel) + return eid; + + /* Switch time is basically time between CSA count 1 and CSA count + * 0 (1 beacon interval) + time for interface restart + time to + * send a Beacon frame in the new channel (1 beacon interval). + * + * TODO: Use dynamic interface restart time. For now, assume 1 sec. + */ + switch_time = USEC_TO_TU(1000 * 1000) + 2 * hapd->iconf->beacon_int; + + *eid++ = WLAN_EID_EXTENSION; + *eid++ = 4; + *eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME; + WPA_PUT_LE24(eid, switch_time); + eid += 3; +#endif /* CONFIG_IEEE80211BE */ + + return eid; +} + + static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) { u8 op_class, channel; + enum oper_chan_width chwidth; + int secondary_channel; + u8 seg0, seg1; if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !hapd->iface->freq) return eid; + hostapd_get_oper_chan_info_of_bss(hapd, &chwidth, &seg0, &seg1); + secondary_channel = hapd->iconf->secondary_channel; + + if (seg0 == hapd->iconf->channel && + chwidth == CONF_OPER_CHWIDTH_USE_HT) + secondary_channel = 0; + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hostapd_get_oper_chwidth(hapd->iconf), + secondary_channel, + chwidth, &op_class, &channel) == NUM_HOSTAPD_MODES) return eid; @@ -476,7 +598,6 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd, size_t len, rnr_len = 0; u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end; u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL; - size_t i; if (!iface->mbssid_max_interfaces || iface->num_bss > iface->mbssid_max_interfaces || @@ -484,14 +605,6 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd, !iface->ema_max_periodicity)) goto fail; - /* Make sure bss->xrates_supported is set for all BSSs to know whether - * it need to be non-inherited. */ - for (i = 0; i < iface->num_bss; i++) { - u8 buf[100]; - - hostapd_eid_ext_supp_rates(iface->bss[i], buf); - } - tx_bss = hostapd_mbssid_get_tx_bss(hapd); len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count, NULL, 0, &rnr_len); @@ -521,18 +634,24 @@ ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd, elem_count, elem_offset, NULL, 0, rnr_elem, &rnr_elem_count, rnr_elem_offset, rnr_len); - params->mbssid_tx_iface = tx_bss->conf->iface; - params->mbssid_index = hostapd_mbssid_get_bss_index(hapd); - params->mbssid_elem = elem; - params->mbssid_elem_len = end - elem; - params->mbssid_elem_count = elem_count; - params->mbssid_elem_offset = elem_offset; - params->rnr_elem = rnr_elem; - params->rnr_elem_len = rnr_len; - params->rnr_elem_count = rnr_elem_count; - params->rnr_elem_offset = rnr_elem_offset; + params->mbssid.mbssid_tx_iface = tx_bss->conf->iface; + params->mbssid.mbssid_index = hostapd_mbssid_get_bss_index(hapd); + params->mbssid.mbssid_elem = elem; + params->mbssid.mbssid_elem_len = end - elem; + params->mbssid.mbssid_elem_count = elem_count; + params->mbssid.mbssid_elem_offset = elem_offset; + params->mbssid.rnr_elem = rnr_elem; + params->mbssid.rnr_elem_len = rnr_len; + params->mbssid.rnr_elem_count = rnr_elem_count; + params->mbssid.rnr_elem_offset = rnr_elem_offset; if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) - params->ema = true; + params->mbssid.ema = true; + + params->mbssid.mbssid_tx_iface_linkid = -1; +#ifdef CONFIG_IEEE80211BE + if (tx_bss->conf->mld_ap) + params->mbssid.mbssid_tx_iface_linkid = tx_bss->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ return 0; @@ -568,7 +687,7 @@ static size_t he_elem_len(struct hostapd_data *hapd) size_t len = 0; #ifdef CONFIG_IEEE80211AX - if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax) + if (!hostapd_is_he_enabled(hapd)) return len; len += 3 + sizeof(struct ieee80211_he_capabilities) + @@ -601,7 +720,6 @@ struct probe_resp_params { bool is_p2p; /* Generated IEs will be included inside an ML element */ - bool is_ml_sta_info; struct hostapd_data *mld_ap; struct mld_info *mld_info; @@ -623,7 +741,7 @@ static void hostapd_free_probe_resp_params(struct probe_resp_params *params) #ifdef CONFIG_IEEE80211BE if (!params) return; - ap_sta_free_sta_profile(params->mld_info); + os_free(params->mld_info); params->mld_info = NULL; #endif /* CONFIG_IEEE80211BE */ @@ -633,8 +751,12 @@ static void hostapd_free_probe_resp_params(struct probe_resp_params *params) static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd, struct probe_resp_params *params) { + struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap : + hapd; size_t buflen = 0; + hapd = hostapd_mbssid_get_tx_bss(hapd); + #ifdef CONFIG_WPS if (hapd->wps_probe_resp_ie) buflen += wpabuf_len(hapd->wps_probe_resp_ie); @@ -661,31 +783,51 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd, buflen += he_elem_len(hapd); #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { + buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); buflen += 3 + sizeof(struct ieee80211_eht_operation); if (hapd->iconf->punct_bitmap) buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; - if (!params->is_ml_sta_info && hapd->conf->mld_ap) { - struct hostapd_data *ml_elem_ap = - params->mld_ap ? params->mld_ap : hapd; - + if (params->mld_ap && params->mld_ap->conf->mld_ap) { buflen += hostapd_eid_eht_ml_beacon_len( - ml_elem_ap, params->mld_info, !!params->mld_ap); + params->mld_ap, params->mld_info, + !!params->mld_ap); + + if (hapd->conf->mld_ap) + buflen += hostapd_eid_eht_ml_beacon_len( + hapd, NULL, false); + + /* For Max Channel Switch Time element during channel + * switch */ + buflen += 6; + } else if (hapd->conf->mld_ap) { + buflen += hostapd_eid_eht_ml_beacon_len( + hapd, params->mld_info, false); + + /* For Max Channel Switch Time element during channel + * switch */ + buflen += 6; } + + if (hapd->conf->mld_ap) + buflen += hostapd_eid_eht_ml_tid_to_link_map_len( + hapd); } #endif /* CONFIG_IEEE80211BE */ - buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL, + buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP, + NULL, params->known_bss, params->known_bss_len, NULL); - if (!params->is_ml_sta_info) - buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, - true); + buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, true); buflen += hostapd_mbo_ie_len(hapd); buflen += hostapd_eid_owe_trans_len(hapd); buflen += hostapd_eid_dpp_cc_len(hapd); + buflen += hostapd_get_rsne_override_len(hapd); + buflen += hostapd_get_rsne_override_2_len(hapd); + buflen += hostapd_get_rsnxe_override_len(hapd); return buflen; } @@ -695,18 +837,19 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, struct probe_resp_params *params, u8 *pos, size_t len) { + struct hostapd_data *hapd_probed = params->mld_ap ? params->mld_ap : + hapd; u8 *csa_pos; u8 *epos; + hapd = hostapd_mbssid_get_tx_bss(hapd); epos = pos + len; - if (!params->is_ml_sta_info) { - *pos++ = WLAN_EID_SSID; - *pos++ = hapd->conf->ssid.ssid_len; - os_memcpy(pos, hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len); - pos += hapd->conf->ssid.ssid_len; - } + *pos++ = WLAN_EID_SSID; + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; /* Supported rates */ pos = hostapd_eid_supp_rates(hapd, pos); @@ -719,18 +862,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, /* Power Constraint element */ pos = hostapd_eid_pwr_constraint(hapd, pos); - /* - * CSA IE - * TODO: This should be included inside the ML sta profile - */ - if (!params->is_ml_sta_info) { - csa_pos = hostapd_eid_csa(hapd, pos); - if (csa_pos != pos) - params->csa_pos = csa_pos - 1; - else - params->csa_pos = NULL; - pos = csa_pos; - } + /* CSA element */ + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + params->csa_pos = csa_pos - 1; + else + params->csa_pos = NULL; + pos = csa_pos; /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -740,24 +878,20 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, pos = hostapd_get_rsne(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); - pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0, + pos = hostapd_eid_mbssid(hapd_probed, pos, epos, + WLAN_FC_STYPE_PROBE_RESP, 0, NULL, params->known_bss, params->known_bss_len, NULL, NULL, NULL, 0); pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); pos = hostapd_get_mde(hapd, pos, epos - pos); - /* - * eCSA IE - * TODO: This should be included inside the ML sta profile - */ - if (!params->is_ml_sta_info) { - csa_pos = hostapd_eid_ecsa(hapd, pos); - if (csa_pos != pos) - params->ecsa_pos = csa_pos - 1; - else - params->ecsa_pos = NULL; - pos = csa_pos; - } + /* eCSA element */ + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + params->ecsa_pos = csa_pos - 1; + else + params->ecsa_pos = NULL; + pos = csa_pos; pos = hostapd_eid_supported_op_classes(hapd, pos); pos = hostapd_eid_ht_capabilities(hapd, pos); @@ -785,7 +919,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && + if (hostapd_is_vht_enabled(hapd) && !is_6ghz_op_class(hapd->iconf->op_class)) { pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); @@ -794,21 +928,23 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && + if (hostapd_is_he_enabled(hapd) && is_6ghz_op_class(hapd->iconf->op_class)) pos = hostapd_eid_txpower_envelope(hapd, pos); #endif /* CONFIG_IEEE80211AX */ - pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); + pos = hostapd_eid_chsw_wrapper(hapd, pos); - if (!params->is_ml_sta_info) - pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, - true); + pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, true); pos = hostapd_eid_fils_indic(hapd, pos, 0); + + /* Max Channel Switch Time element */ + pos = hostapd_eid_max_cs_time(hapd, pos); + pos = hostapd_get_rsnxe(hapd, pos, epos - pos); #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { u8 *cca_pos; pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP); @@ -829,17 +965,27 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { - struct hostapd_data *ml_elem_ap = - params->mld_ap ? params->mld_ap : hapd; - - if (ml_elem_ap->conf->mld_ap) + if (hostapd_is_eht_enabled(hapd)) { + if (params->mld_ap && params->mld_ap->conf->mld_ap) { pos = hostapd_eid_eht_ml_beacon( - ml_elem_ap, params->mld_info, + params->mld_ap, params->mld_info, pos, !!params->mld_ap); + if (hapd->conf->mld_ap) + pos = hostapd_eid_eht_ml_beacon( + hapd, NULL, pos, false); + + } else if (hapd->conf->mld_ap) { + pos = hostapd_eid_eht_ml_beacon(hapd, + params->mld_info, + pos, false); + } + pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP); pos = hostapd_eid_eht_operation(hapd, pos); + + if (hapd->conf->mld_ap) + pos = hostapd_eid_eht_ml_tid_to_link_map(hapd, pos); } #endif /* CONFIG_IEEE80211BE */ @@ -848,9 +994,8 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ - /* WPA / OSEN */ + /* WPA */ pos = hostapd_get_wpa_ie(hapd, pos, epos - pos); - pos = hostapd_get_osen_ie(hapd, pos, epos - pos); /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -885,6 +1030,10 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, pos = hostapd_eid_owe_trans(hapd, pos, epos - pos); pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos); + pos = hostapd_get_rsne_override(hapd, pos, epos - pos); + pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos); + pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos); + if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -906,6 +1055,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, static void hostapd_gen_probe_resp(struct hostapd_data *hapd, struct probe_resp_params *params) { + struct hostapd_data *hapd_probed = hapd; u8 *pos; size_t buflen; @@ -913,7 +1063,7 @@ static void hostapd_gen_probe_resp(struct hostapd_data *hapd, #define MAX_PROBERESP_LEN 768 buflen = MAX_PROBERESP_LEN; - buflen += hostapd_probe_resp_elems_len(hapd, params); + buflen += hostapd_probe_resp_elems_len(hapd_probed, params); params->resp = os_zalloc(buflen); if (!params->resp) { params->resp_len = 0; @@ -943,7 +1093,7 @@ static void hostapd_gen_probe_resp(struct hostapd_data *hapd, params->resp->u.probe_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd)); - pos = hostapd_probe_resp_fill_elems(hapd, params, + pos = hostapd_probe_resp_fill_elems(hapd_probed, params, params->resp->u.probe_resp.variable, buflen); @@ -957,7 +1107,6 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, int mld_id, u16 links) { - struct probe_resp_params sta_info_params; struct hostapd_data *link; params->mld_ap = NULL; @@ -969,21 +1118,27 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, "MLD: Got ML probe request with AP MLD ID %d for links %04x", mld_id, links); + /* + * Set mld_ap if the ML probe request explicitly requested a specific + * AP MLD ID. + */ + if (mld_id > 0) { + if (hapd == hostapd_mbssid_get_tx_bss(hapd)) { + hapd = hostapd_get_mbssid_bss_by_idx(hapd, mld_id); + if (!hapd) { + wpa_printf(MSG_INFO, + "Ignore Probe Request from " MACSTR + " since no matched non-TX BSS found for MBSSID Index %d", + MAC2STR(mgmt->sa), mld_id); + goto fail; + } + } + params->mld_ap = hapd; + } + for_each_mld_link(link, hapd) { struct mld_link_info *link_info; - size_t buflen; u8 mld_link_id = link->mld_link_id; - u8 *epos; - u8 buf[EHT_ML_MAX_STA_PROF_LEN]; - - /* - * Set mld_ap iff the ML probe request explicitly - * requested a specific MLD ID. In that case, the targeted - * AP may have been a nontransmitted BSSID on the same - * interface. - */ - if (mld_id != -1 && link->iface == hapd->iface) - params->mld_ap = link; /* Never duplicate main Probe Response frame body */ if (link == hapd) @@ -994,52 +1149,15 @@ static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, continue; link_info = ¶ms->mld_info->links[mld_link_id]; - - sta_info_params.req = params->req; - sta_info_params.is_p2p = false; - sta_info_params.is_ml_sta_info = true; - sta_info_params.mld_ap = NULL; - sta_info_params.mld_info = NULL; - - buflen = MAX_PROBERESP_LEN; - buflen += hostapd_probe_resp_elems_len(link, &sta_info_params); - - if (buflen > EHT_ML_MAX_STA_PROF_LEN) { - wpa_printf(MSG_DEBUG, - "MLD: Not including link %d in ML probe response (%zu bytes is too long)", - mld_link_id, buflen); - goto fail; - } - - /* - * NOTE: This does not properly handle inheritance and - * various other things. - */ - link_info->valid = true; - epos = buf; - - /* Capabilities is the only fixed parameter */ - WPA_PUT_LE16(epos, hostapd_own_capab_info(hapd)); - epos += 2; - - epos = hostapd_probe_resp_fill_elems( - link, &sta_info_params, epos, - EHT_ML_MAX_STA_PROF_LEN - 2); - link_info->resp_sta_profile_len = epos - buf; - os_free(link_info->resp_sta_profile); - link_info->resp_sta_profile = os_memdup( - buf, link_info->resp_sta_profile_len); - if (!link_info->resp_sta_profile) - link_info->resp_sta_profile_len = 0; - os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN); + os_memcpy(link_info, &hapd->partner_links[mld_link_id], + sizeof(hapd->partner_links[mld_link_id])); wpa_printf(MSG_DEBUG, - "MLD: ML probe response includes link sta info for %d: %u bytes (estimate %zu)", - mld_link_id, link_info->resp_sta_profile_len, - buflen); + "MLD: ML probe response includes link STA info for %d: %u bytes", + mld_link_id, link_info->resp_sta_profile_len); } - if (mld_id != -1 && !params->mld_ap) { + if (mld_id > 0 && !params->mld_ap) { wpa_printf(MSG_DEBUG, "MLD: No nontransmitted BSSID for MLD ID %d", mld_id); @@ -1305,6 +1423,7 @@ static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len, for_each_element_id(sub, 0, pos, len) { const struct ieee80211_eht_per_sta_profile *sta; u16 sta_control; + u8 link_id; if (*links == 0xffff) *links = 0; @@ -1324,7 +1443,9 @@ static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len, * partial profile was requested. */ sta_control = le_to_host16(sta->sta_control); - *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK); + link_id = sta_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK; + if (link_id < MAX_NUM_MLD_LINKS) + *links |= BIT(link_id); } if (!for_each_element_completed(sub, pos, len)) { @@ -1353,6 +1474,7 @@ void handle_probe_req(struct hostapd_data *hapd, size_t csa_offs_len; struct radius_sta rad_info; struct probe_resp_params params; + char *hex = NULL; #ifdef CONFIG_IEEE80211BE int mld_id; u16 links; @@ -1483,6 +1605,27 @@ void handle_probe_req(struct hostapd_data *hapd, res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len, elems.short_ssid_list, elems.short_ssid_list_len); + if (res == NO_SSID_MATCH && hapd->iconf->mbssid) { + /* If the Probe Request frame is directed to the TX BSS in an + * MBSSID set, check whether the SSID is matching any of the + * non-TX BSSs' SSID. */ + if (!(mgmt->da[0] & 0x01 || mgmt->bssid[0] & 0x01)) { + for (i = 0; i < hapd->iface->num_bss; i++) { + if (hapd->iface->bss[i] == hapd) + continue; + + res = ssid_match(hapd->iface->bss[i], + elems.ssid, elems.ssid_len, + elems.ssid_list, + elems.ssid_list_len, + elems.short_ssid_list, + elems.short_ssid_list_len); + if (res != NO_SSID_MATCH) + break; + } + } + } + if (res == NO_SSID_MATCH) { if (!(mgmt->da[0] & 0x01)) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR @@ -1585,8 +1728,20 @@ void handle_probe_req(struct hostapd_data *hapd, if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH) return; + if (hapd->conf->notify_mgmt_frames) { + size_t hex_len; + + hex_len = len * 2 + 1; + hex = os_malloc(hex_len); + if (hex) + wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len); + } + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR - " signal=%d", MAC2STR(mgmt->sa), ssi_signal); + " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal, + hex ? " buf=" : "", hex ? hex : ""); + + os_free(hex); os_memset(¶ms, 0, sizeof(params)); @@ -1603,7 +1758,6 @@ void handle_probe_req(struct hostapd_data *hapd, params.is_p2p = !!elems.p2p; params.known_bss = elems.mbssid_known_bss; params.known_bss_len = elems.mbssid_known_bss_len; - params.is_ml_sta_info = false; hostapd_gen_probe_resp(hapd, ¶ms); @@ -1630,7 +1784,8 @@ void handle_probe_req(struct hostapd_data *hapd, params.ecsa_pos - (u8 *) params.resp; } - ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack, + ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), + params.resp, params.resp_len, noack, csa_offs_len ? csa_offs : NULL, csa_offs_len, 0); @@ -1684,7 +1839,6 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, params.is_p2p = false; params.known_bss = NULL; params.known_bss_len = 0; - params.is_ml_sta_info = false; params.mld_ap = NULL; params.mld_info = NULL; @@ -1728,7 +1882,6 @@ u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd, probe_params.is_p2p = false; probe_params.known_bss = NULL; probe_params.known_bss_len = 0; - probe_params.is_ml_sta_info = false; probe_params.mld_ap = NULL; probe_params.mld_info = NULL; @@ -1754,21 +1907,21 @@ void sta_track_del(struct hostapd_sta_info *info) static u16 hostapd_gen_fils_discovery_phy_index(struct hostapd_data *hapd) { #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) + if (hostapd_is_eht_enabled(hapd)) return FD_CAP_PHY_INDEX_EHT; #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) + if (hostapd_is_he_enabled(hapd)) return FD_CAP_PHY_INDEX_HE; #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) + if (hostapd_is_vht_enabled(hapd)) return FD_CAP_PHY_INDEX_VHT; #endif /* CONFIG_IEEE80211AC */ - if (hapd->iconf->ieee80211n && !hapd->conf->disable_11n) + if (hostapd_is_ht_enabled(hapd)) return FD_CAP_PHY_INDEX_HT; return 0; @@ -2106,7 +2259,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef NEED_AP_MLME #define BEACON_HEAD_BUF_SIZE 256 -#define BEACON_TAIL_BUF_SIZE 512 +#define BEACON_TAIL_BUF_SIZE 1500 head = os_zalloc(BEACON_HEAD_BUF_SIZE); tail_len = BEACON_TAIL_BUF_SIZE; #ifdef CONFIG_WPS @@ -2134,7 +2287,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tail_len += he_elem_len(hapd); #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); tail_len += 3 + sizeof(struct ieee80211_eht_operation); if (hapd->iconf->punct_bitmap) @@ -2145,8 +2298,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, * long based on the common info and number of per * station profiles. For now use 256. */ - if (hapd->conf->mld_ap) + if (hapd->conf->mld_ap) { tail_len += 256; + tail_len += + hostapd_eid_eht_ml_tid_to_link_map_len(hapd); + + /* for Max Channel Switch Time element during channel + * switch */ + tail_len += 6; + } } #endif /* CONFIG_IEEE80211BE */ @@ -2157,13 +2317,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tail_len += hostapd_mbo_ie_len(hapd); tail_len += hostapd_eid_owe_trans_len(hapd); tail_len += hostapd_eid_dpp_cc_len(hapd); + tail_len += hostapd_get_rsne_override_len(hapd); + tail_len += hostapd_get_rsne_override_2_len(hapd); + tail_len += hostapd_get_rsnxe_override_len(hapd); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); - os_free(head); - os_free(tail); - return -1; + goto error; } tailend = tail + tail_len; @@ -2239,17 +2400,19 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); - if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) { + /* + * Include the Multiple BSSID element whenever MBSSID is enabled. The + * element may include zero or more nontransmitted BSSID profiles. + */ + if (hapd->iconf->mbssid) { if (ieee802_11_build_ap_params_mbssid(hapd, params)) { - os_free(head); - os_free(tail); wpa_printf(MSG_ERROR, "MBSSID: Failed to set beacon data"); - return -1; + goto error; } complete = hapd->iconf->mbssid == MBSSID_ENABLED || (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && - params->mbssid_elem_count == 1); + params->mbssid.mbssid_elem_count == 1); } tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete); @@ -2273,7 +2436,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && + if (hostapd_is_vht_enabled(hapd) && !is_6ghz_op_class(hapd->iconf->op_class)) { tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); @@ -2282,21 +2445,25 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax && + if (hostapd_is_he_enabled(hapd) && is_6ghz_op_class(hapd->iconf->op_class)) tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); #endif /* CONFIG_IEEE80211AX */ - tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); + tailpos = hostapd_eid_chsw_wrapper(hapd, tailpos); tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true); tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); + + /* Max Channel Switch Time element */ + tailpos = hostapd_eid_max_cs_time(hapd, tailpos); + tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos); tailpos = hostapd_eid_mbssid_config(hapd, tailpos, - params->mbssid_elem_count); + params->mbssid.mbssid_elem_count); #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { u8 *cca_pos; tailpos = hostapd_eid_he_capab(hapd, tailpos, @@ -2316,13 +2483,16 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { if (hapd->conf->mld_ap) tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL, tailpos, false); tailpos = hostapd_eid_eht_capab(hapd, tailpos, IEEE80211_MODE_AP); tailpos = hostapd_eid_eht_operation(hapd, tailpos); + if (hapd->conf->mld_ap) + tailpos = hostapd_eid_eht_ml_tid_to_link_map(hapd, + tailpos); } #endif /* CONFIG_IEEE80211BE */ @@ -2331,9 +2501,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ - /* WPA / OSEN */ + /* WPA */ tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos); - tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos); /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -2368,6 +2537,13 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tail + tail_len - tailpos); tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos); + tailpos = hostapd_get_rsne_override(hapd, tailpos, + tail + tail_len - tailpos); + tailpos = hostapd_get_rsne_override_2(hapd, tailpos, + tail + tail_len - tailpos); + tailpos = hostapd_get_rsnxe_override(hapd, tailpos, + tail + tail_len - tailpos); + if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -2400,25 +2576,27 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, /* If SAE offload is enabled, provide password to lower layer for * SAE authentication and PMK generation. */ - if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) && (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) { if (hostapd_sae_pk_in_use(hapd->conf)) { wpa_printf(MSG_ERROR, "SAE PK not supported with SAE offload"); - return -1; + goto error; } if (hostapd_sae_pw_id_in_use(hapd->conf)) { wpa_printf(MSG_ERROR, "SAE Password Identifiers not supported with SAE offload"); - return -1; + goto error; } - params->sae_password = sae_get_password(hapd, NULL, NULL, NULL, - NULL, NULL); + params->sae_password = sae_get_password(hapd, NULL, NULL, 0, + NULL, NULL, NULL); if (!params->sae_password) { wpa_printf(MSG_ERROR, "SAE password not configured for offload"); - return -1; + goto error; } } #endif /* CONFIG_SAE */ @@ -2431,9 +2609,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->proberesp_len = resp_len; params->dtim_period = hapd->conf->dtim_period; params->beacon_int = hapd->iconf->beacon_int; - params->basic_rates = hapd->iface->basic_rates; - params->beacon_rate = hapd->iconf->beacon_rate; - params->rate_type = hapd->iconf->rate_type; + params->basic_rates = hapd->basic_rates; + params->beacon_rate = hapd->conf->beacon_rate; + params->rate_type = hapd->conf->rate_type; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == @@ -2445,7 +2623,9 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, else if (hapd->conf->wpa & WPA_PROTO_WPA) params->pairwise_ciphers = hapd->conf->wpa_pairwise; params->group_cipher = hapd->conf->wpa_group; - params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params->key_mgmt_suites = hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2; params->auth_algs = hapd->conf->auth_algs; params->wpa_version = hapd->conf->wpa; params->privacy = hapd->conf->wpa; @@ -2478,7 +2658,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; else params->short_slot_time = -1; - if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) + if (!hostapd_is_ht_enabled(hapd)) params->ht_opmode = -1; else params->ht_opmode = hapd->iface->ht_op_mode; @@ -2494,10 +2674,6 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 params->disable_dgaf = hapd->conf->disable_dgaf; - if (hapd->conf->osen) { - params->privacy = 1; - params->osen = 1; - } #endif /* CONFIG_HS20 */ params->multicast_to_unicast = hapd->conf->multicast_to_unicast; params->pbss = hapd->conf->pbss; @@ -2514,14 +2690,21 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #ifdef CONFIG_IEEE80211BE - if (hapd->conf->mld_ap && hapd->iconf->ieee80211be && - !hapd->conf->disable_11be) { + if (hapd->conf->mld_ap) { params->mld_ap = true; params->mld_link_id = hapd->mld_link_id; } #endif /* CONFIG_IEEE80211BE */ return 0; + +#if defined(CONFIG_SAE) || defined(NEED_AP_MLME) +error: + os_free(head); + os_free(tail); + os_free(resp); + return -1; +#endif /* CONFIG_SAE || NEED_AP_MLME */ } @@ -2533,14 +2716,14 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) params->head = NULL; os_free(params->proberesp); params->proberesp = NULL; - os_free(params->mbssid_elem); - params->mbssid_elem = NULL; - os_free(params->mbssid_elem_offset); - params->mbssid_elem_offset = NULL; - os_free(params->rnr_elem); - params->rnr_elem = NULL; - os_free(params->rnr_elem_offset); - params->rnr_elem_offset = NULL; + os_free(params->mbssid.mbssid_elem); + params->mbssid.mbssid_elem = NULL; + os_free(params->mbssid.mbssid_elem_offset); + params->mbssid.mbssid_elem_offset = NULL; + os_free(params->mbssid.rnr_elem); + params->mbssid.rnr_elem = NULL; + os_free(params->mbssid.rnr_elem_offset); + params->mbssid.rnr_elem_offset = NULL; #ifdef CONFIG_FILS os_free(params->fd_frame_tmpl); params->fd_frame_tmpl = NULL; @@ -2576,6 +2759,14 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd) return -1; } +#ifdef CONFIG_IEEE80211AX + if (hapd->cca_in_progress) { + wpa_printf(MSG_ERROR, + "Cannot set beacons during CCA period"); + return -1; + } +#endif /* CONFIG_IEEE80211AX */ + hapd->beacon_set_done = 1; if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) @@ -2679,12 +2870,452 @@ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd) } +#ifdef CONFIG_IEEE80211BE + +static int hostapd_get_probe_resp_tmpl(struct hostapd_data *hapd, + struct probe_resp_params *params, + bool is_ml_sta_info) +{ + os_memset(params, 0, sizeof(*params)); + hostapd_gen_probe_resp(hapd, params); + if (!params->resp) + return -1; + + /* The caller takes care of freeing params->resp. */ + return 0; +} + + +static bool is_restricted_eid_in_sta_profile(u8 eid, bool tx_vap) +{ + switch (eid) { + case WLAN_EID_TIM: + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + case WLAN_EID_MULTIPLE_BSSID: + case WLAN_EID_REDUCED_NEIGHBOR_REPORT: + case WLAN_EID_NEIGHBOR_REPORT: + return true; + case WLAN_EID_SSID: + /* SSID is not restricted for non-transmitted BSSID */ + return tx_vap; + default: + return false; + } +} + + +static bool is_restricted_ext_eid_in_sta_profile(u8 ext_id) +{ + switch (ext_id) { + case WLAN_EID_EXT_MULTI_LINK: + return true; + default: + return false; + } +} + + +/* Create the link STA profiles based on inheritance from the reporting + * profile. + * + * NOTE: The same function is used for length calculation as well as filling + * data in the given buffer. This avoids risk of not updating the length + * function but filling function or vice versa. + */ +static size_t hostapd_add_sta_profile(struct ieee80211_mgmt *link_fdata, + size_t link_data_len, + struct ieee80211_mgmt *own_fdata, + size_t own_data_len, + u8 *sta_profile, bool tx_vap) +{ + const struct element *link_elem; + size_t sta_profile_len = 0; + const u8 *link_elem_data; + u8 link_ele_len; + u8 *link_data; + const struct element *own_elem; + u8 link_eid, own_eid, own_ele_len; + const u8 *own_elem_data; + u8 *own_data; + bool is_ext; + bool ie_found; + u8 non_inherit_ele_ext_list[256] = { 0 }; + u8 non_inherit_ele_ext_list_len = 0; + u8 non_inherit_ele_list[256] = { 0 }; + u8 non_inherit_ele_list_len = 0; + u8 num_link_elem_vendor_ies = 0, num_own_elem_vendor_ies = 0; + bool add_vendor_ies = false, is_identical_vendor_ies = true; + /* The bitmap of parsed EIDs. There are 256 EIDs and ext EIDs, so 32 + * bytes to store the bitmaps. */ + u8 parsed_eid_bmap[32] = { 0 }, parsed_ext_eid_bmap[32] = { 0 }; + /* extra len used in the logic includes the element id and len */ + u8 extra_len = 2; + + /* Include len for capab info */ + sta_profile_len += sizeof(le16); + if (sta_profile) { + os_memcpy(sta_profile, &link_fdata->u.probe_resp.capab_info, + sizeof(le16)); + sta_profile += sizeof(le16); + } + + own_data = own_fdata->u.probe_resp.variable; + link_data = link_fdata->u.probe_resp.variable; + + /* The below logic takes the reporting BSS data and reported BSS data + * and performs intersection to build the STA profile of the reported + * BSS. Certain elements are not added to the STA profile as + * recommended in standard. Matching element information in the + * reporting BSS profile are ignored in the STA profile. Remaining + * elements pertaining to the STA profile are appended at the end. */ + for_each_element(own_elem, own_data, own_data_len) { + is_ext = false; + ie_found = false; + + /* Pick one of own elements and get its EID and length */ + own_elem_data = own_elem->data; + own_ele_len = own_elem->datalen; + + if (own_elem->id == WLAN_EID_EXTENSION) { + is_ext = true; + own_eid = *(own_elem_data); + if (is_restricted_ext_eid_in_sta_profile(own_eid)) + continue; + } else { + own_eid = own_elem->id; + if (is_restricted_eid_in_sta_profile(own_eid, tx_vap)) + continue; + } + + for_each_element(link_elem, link_data, link_data_len) { + /* If the element type mismatches, do not consider + * this link element for comparison. */ + if ((link_elem->id == WLAN_EID_EXTENSION && + !is_ext) || + (is_ext && link_elem->id != WLAN_EID_EXTENSION)) + continue; + + /* Comparison can be done so get the link element and + * its EID and length. */ + link_elem_data = link_elem->data; + link_ele_len = link_elem->datalen; + + if (link_elem->id == WLAN_EID_EXTENSION) + link_eid = *(link_elem_data); + else + link_eid = link_elem->id; + + /* Ignore if EID does not match */ + if (own_eid != link_eid) + continue; + + ie_found = true; + + /* Ignore if the contents is identical. */ + if (own_ele_len == link_ele_len && + os_memcmp(own_elem->data, link_elem->data, + own_ele_len) == 0) { + if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { + is_identical_vendor_ies = true; + num_own_elem_vendor_ies++; + } + + /* Update the parsed EIDs bitmap */ + if (is_ext) + parsed_ext_eid_bmap[own_eid / 8] |= + BIT(own_eid % 8); + else + parsed_eid_bmap[own_eid / 8] |= + BIT(own_eid % 8); + break; + } + + /* No need to include this non-matching Vendor Specific + * element explicitly at this point. */ + if (own_eid == WLAN_EID_VENDOR_SPECIFIC) { + is_identical_vendor_ies = false; + continue; + } + + /* This element is present in the reported profile + * as well as present in the reporting profile. + * However, there is a mismatch in the contents and + * hence, include this in the per STA profile. */ + sta_profile_len += link_ele_len + extra_len; + if (sta_profile) { + os_memcpy(sta_profile, + link_elem->data - extra_len, + link_ele_len + extra_len); + sta_profile += link_ele_len + extra_len; + } + + /* Update the parsed EIDs bitmap */ + if (is_ext) + parsed_ext_eid_bmap[own_eid / 8] |= + BIT(own_eid % 8); + else + parsed_eid_bmap[own_eid / 8] |= + BIT(own_eid % 8); + break; + } + + /* We found at least one Vendor Specific element in reporting + * link which is not same (or present) in the reported link. We + * need to include all Vendor Specific elements from the + * reported link. */ + if (!is_identical_vendor_ies) + add_vendor_ies = true; + + /* This is a unique element in the reporting profile which is + * not present in the reported profile. Update the + * non-inheritance list. */ + if (!ie_found) { + u8 idx; + + if (is_ext) { + idx = non_inherit_ele_ext_list_len++; + non_inherit_ele_ext_list[idx] = own_eid; + } else { + idx = non_inherit_ele_list_len++; + non_inherit_ele_list[idx] = own_eid; + } + } + } + + /* Parse the remaining elements in the reported profile */ + for_each_element(link_elem, link_data, link_data_len) { + link_elem_data = link_elem->data; + link_ele_len = link_elem->datalen; + + /* No need to check this Vendor Specific element at this point. + * Just take the count and continue. */ + if (link_elem->id == WLAN_EID_VENDOR_SPECIFIC) { + num_link_elem_vendor_ies++; + continue; + } + + if (link_elem->id == WLAN_EID_EXTENSION) { + link_eid = *(link_elem_data); + + if ((parsed_ext_eid_bmap[link_eid / 8] & + BIT(link_eid % 8)) || + is_restricted_ext_eid_in_sta_profile(link_eid)) + continue; + } else { + link_eid = link_elem->id; + + if ((parsed_eid_bmap[link_eid / 8] & + BIT(link_eid % 8)) || + is_restricted_eid_in_sta_profile(link_eid, tx_vap)) + continue; + } + + sta_profile_len += link_ele_len + extra_len; + if (sta_profile) { + os_memcpy(sta_profile, link_elem_data - extra_len, + link_ele_len + extra_len); + sta_profile += link_ele_len + extra_len; + } + } + + /* Handle Vendor Specific elements + * Add all the Vendor Specific elements of the reported link if + * a. There is at least one non-matching Vendor Specific element, or + * b. The number of Vendor Specific elements in reporting and reported + * link is not same. */ + if (add_vendor_ies || + num_own_elem_vendor_ies != num_link_elem_vendor_ies) { + for_each_element(link_elem, link_data, link_data_len) { + link_elem_data = link_elem->data; + link_ele_len = link_elem->datalen; + + if (link_elem->id != WLAN_EID_VENDOR_SPECIFIC) + continue; + + sta_profile_len += link_ele_len + extra_len; + if (sta_profile) { + os_memcpy(sta_profile, + link_elem_data - extra_len, + link_ele_len + extra_len); + sta_profile += link_ele_len + extra_len; + } + } + } + + /* Handle non-inheritance + * Non-Inheritance element: + * Element ID Ext: 1 octet + * Length: 1 octet + * Ext tag number: 1 octet + * Length of Elements ID list: 1 octet + * Elements ID list: variable + * Length of Elements ID Extension list: 1 octet + * Elements ID extensions list: variable + */ + if (non_inherit_ele_list_len || non_inherit_ele_ext_list_len) + sta_profile_len += 3 + 2 + non_inherit_ele_list_len + + non_inherit_ele_ext_list_len; + + if (sta_profile && + (non_inherit_ele_list_len || non_inherit_ele_ext_list_len)) { + *sta_profile++ = WLAN_EID_EXTENSION; + *sta_profile++ = non_inherit_ele_list_len + + non_inherit_ele_ext_list_len + 3; + *sta_profile++ = WLAN_EID_EXT_NON_INHERITANCE; + *sta_profile++ = non_inherit_ele_list_len; + os_memcpy(sta_profile, non_inherit_ele_list, + non_inherit_ele_list_len); + sta_profile += non_inherit_ele_list_len; + *sta_profile++ = non_inherit_ele_ext_list_len; + os_memcpy(sta_profile, non_inherit_ele_ext_list, + non_inherit_ele_ext_list_len); + sta_profile += non_inherit_ele_ext_list_len; + } + + return sta_profile_len; +} + + +static u8 * hostapd_gen_sta_profile(struct ieee80211_mgmt *link_data, + size_t link_data_len, + struct ieee80211_mgmt *own_data, + size_t own_data_len, + size_t *sta_profile_len, bool tx_vap) +{ + u8 *sta_profile; + + /* Get the length first */ + *sta_profile_len = hostapd_add_sta_profile(link_data, link_data_len, + own_data, own_data_len, + NULL, tx_vap); + if (!(*sta_profile_len) || *sta_profile_len > EHT_ML_MAX_STA_PROF_LEN) + return NULL; + + sta_profile = os_zalloc(*sta_profile_len); + if (!sta_profile) + return NULL; + + /* Now fill in the data */ + hostapd_add_sta_profile(link_data, link_data_len, own_data, + own_data_len, sta_profile, tx_vap); + + /* The caller takes care of freeing the returned sta_profile */ + return sta_profile; +} + + +static void hostapd_gen_per_sta_profiles(struct hostapd_data *hapd) +{ + bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd); + size_t link_data_len, sta_profile_len; + size_t own_data_len, fixed; + struct probe_resp_params link_params; + struct probe_resp_params own_params; + struct ieee80211_mgmt *link_data; + struct ieee80211_mgmt *own_data; + struct mld_link_info *link_info; + struct hostapd_data *link_bss; + u8 link_id, *sta_profile; + + if (!hapd->conf->mld_ap || !hapd->started) + return; + + wpa_printf(MSG_DEBUG, "MLD: Generating per STA profiles for MLD %s", + hapd->conf->iface); + + wpa_printf(MSG_DEBUG, "MLD: Reporting link %d", hapd->mld_link_id); + + /* Generate a Probe Response template for self */ + if (hostapd_get_probe_resp_tmpl(hapd, &own_params, false)) { + wpa_printf(MSG_ERROR, + "MLD: Error in building per STA profiles"); + return; + } + + own_data = own_params.resp; + own_data_len = own_params.resp_len; + + /* Consider the length of the variable fields */ + fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + if (own_data_len < fixed) + goto fail; + own_data_len -= fixed; + + for_each_mld_link(link_bss, hapd) { + if (link_bss == hapd || !link_bss->started) + continue; + + link_id = link_bss->mld_link_id; + if (link_id >= MAX_NUM_MLD_LINKS) + continue; + + sta_profile = NULL; + sta_profile_len = 0; + + /* Generate a Probe Response frame template for partner link */ + if (hostapd_get_probe_resp_tmpl(link_bss, &link_params, true)) { + wpa_printf(MSG_ERROR, + "MLD: Could not get link STA probe response template for link %d", + link_id); + continue; + } + + link_data = link_params.resp; + link_data_len = link_params.resp_len; + + /* Consider length of the variable fields */ + fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + if (link_data_len < fixed) + continue; + link_data_len -= fixed; + + sta_profile = hostapd_gen_sta_profile(link_data, link_data_len, + own_data, own_data_len, + &sta_profile_len, tx_vap); + if (!sta_profile) { + wpa_printf(MSG_ERROR, + "MLD: Could not generate link STA profile for link %d", + link_id); + continue; + } + + link_info = &hapd->partner_links[link_id]; + link_info->valid = true; + + os_free(link_info->resp_sta_profile); + link_info->resp_sta_profile_len = sta_profile_len; + + link_info->resp_sta_profile = os_memdup(sta_profile, + sta_profile_len); + if (!link_info->resp_sta_profile) + link_info->resp_sta_profile_len = 0; + + os_memcpy(link_info->local_addr, link_bss->own_addr, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "MLD: Reported link STA info for %d: %u bytes", + link_id, link_info->resp_sta_profile_len); + + os_free(sta_profile); + os_free(link_params.resp); + } + +fail: + os_free(own_params.resp); +} + +#endif /* CONFIG_IEEE80211BE */ + + int ieee802_11_set_beacon(struct hostapd_data *hapd) { struct hostapd_iface *iface = hapd->iface; int ret; size_t i, j; bool is_6g, hapd_mld = false; +#ifdef CONFIG_IEEE80211BE + struct hostapd_data *link_bss; +#endif /* CONFIG_IEEE80211BE */ ret = __ieee802_11_set_beacon(hapd); if (ret != 0) @@ -2720,11 +3351,21 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) continue; #endif /* CONFIG_IEEE80211BE */ - if (other->bss[i] && other->bss[i]->started) + if (other->bss[i] && other->bss[i]->started && + other->bss[i]->beacon_set_done) __ieee802_11_set_beacon(other->bss[i]); } } +#ifdef CONFIG_IEEE80211BE + if (!hapd_mld) + return 0; + + /* Generate per STA profiles for each affiliated APs */ + for_each_mld_link(link_bss, hapd) + hostapd_gen_per_sta_profiles(link_bss); +#endif /* CONFIG_IEEE80211BE */ + return 0; } diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c index d4d73de19d19..90a0a49363e3 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.c +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" +#include "common/hw_features_common.h" #include "eapol_auth/eapol_auth_sm.h" #include "fst/fst_ctrl_iface.h" #include "hostapd.h" @@ -475,6 +476,10 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211BE if (sta->mld_info.mld_sta) { + u16 mld_sta_capa = sta->mld_info.common_info.mld_capa; + u8 max_simul_links = mld_sta_capa & + EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK; + for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) { if (!sta->mld_info.links[i].valid) continue; @@ -485,6 +490,11 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, if (!os_snprintf_error(buflen - len, ret)) len += ret; } + + ret = os_snprintf(buf + len, buflen - len, + "max_simul_links=%d\n", max_simul_links); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; } #endif /* CONFIG_IEEE80211BE */ @@ -661,15 +671,18 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - if (os_strstr(txtaddr, " tx=0")) - hostapd_drv_sta_remove(hapd, addr); - else - hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); - if (sta) - ap_sta_deauthenticate(hapd, sta, reason); - else if (addr[0] == 0xff) - hostapd_free_stas(hapd); + if (os_strstr(txtaddr, " tx=0")) { + hostapd_drv_sta_remove(hapd, addr); + if (sta) + ap_free_sta(hapd, sta); + } else { + hostapd_drv_sta_deauth(hapd, addr, reason); + if (sta) + ap_sta_deauthenticate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + } return 0; } @@ -723,15 +736,18 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - if (os_strstr(txtaddr, " tx=0")) - hostapd_drv_sta_remove(hapd, addr); - else - hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); - if (sta) - ap_sta_disassociate(hapd, sta, reason); - else if (addr[0] == 0xff) - hostapd_free_stas(hapd); + if (os_strstr(txtaddr, " tx=0")) { + hostapd_drv_sta_remove(hapd, addr); + if (sta) + ap_free_sta(hapd, sta); + } else { + hostapd_drv_sta_disassoc(hapd, addr, reason); + if (sta) + ap_sta_disassociate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + } return 0; } @@ -874,15 +890,12 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->conf->channel, iface->conf->enable_edmg, iface->conf->edmg_channel, - iface->conf->ieee80211n && !hapd->conf->disable_11n ? + hostapd_is_ht_enabled(hapd) ? iface->conf->secondary_channel : 0, - iface->conf->ieee80211n && !hapd->conf->disable_11n, - iface->conf->ieee80211ac && - !hapd->conf->disable_11ac, - iface->conf->ieee80211ax && - !hapd->conf->disable_11ax, - iface->conf->ieee80211be && - !hapd->conf->disable_11be, + hostapd_is_ht_enabled(hapd), + hostapd_is_vht_enabled(hapd), + hostapd_is_he_enabled(hapd), + hostapd_is_eht_enabled(hapd), iface->conf->beacon_int, hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) @@ -890,7 +903,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; #ifdef CONFIG_IEEE80211BE - if (iface->conf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { ret = os_snprintf(buf + len, buflen - len, "eht_oper_chwidth=%d\n" "eht_oper_centr_freq_seg0_idx=%d\n", @@ -911,6 +924,15 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } + if (hapd->iconf->punct_bitmap) { + ret = os_snprintf(buf + len, buflen - len, + "punct_bitmap=0x%x\n", + hapd->iconf->punct_bitmap); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + if (hapd->conf->mld_ap) { struct hostapd_data *link_bss; @@ -945,12 +967,21 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, return len; len += ret; } + + ret = os_snprintf(buf + len, buflen - len, + "ap_mld_type=%s\n", + (hapd->iface->mld_mld_capa & + EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK) + ? "NSTR" : "STR"); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; } } #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_IEEE80211AX - if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { ret = os_snprintf(buf + len, buflen - len, "he_oper_chwidth=%d\n" "he_oper_centr_freq_seg0_idx=%d\n" @@ -974,7 +1005,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, } #endif /* CONFIG_IEEE80211AX */ - if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { + if (hostapd_is_vht_enabled(hapd)) { ret = os_snprintf(buf + len, buflen - len, "vht_oper_chwidth=%d\n" "vht_oper_centr_freq_seg0_idx=%d\n" @@ -989,7 +1020,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } - if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { + if (hostapd_is_vht_enabled(hapd) && mode) { u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); @@ -1002,7 +1033,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } - if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { + if (hostapd_is_ht_enabled(hapd)) { ret = os_snprintf(buf + len, buflen - len, "ht_caps_info=%04x\n", hapd->iconf->ht_capab); @@ -1011,21 +1042,21 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } - if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { + if (hostapd_is_ht_enabled(hapd) && mode) { len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, mode->mcs_set); } - if (iface->current_rates && iface->num_rates) { + if (hapd->current_rates && hapd->num_rates) { ret = os_snprintf(buf + len, buflen - len, "supported_rates="); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; - for (j = 0; j < iface->num_rates; j++) { + for (j = 0; j < hapd->num_rates; j++) { ret = os_snprintf(buf + len, buflen - len, "%s%02x", j > 0 ? " " : "", - iface->current_rates[j].rate / 5); + hapd->current_rates[j].rate / 5); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1094,10 +1125,313 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, } -int hostapd_parse_csa_settings(const char *pos, +int hostapd_parse_freq_params(const char *pos, + struct hostapd_freq_params *params, + unsigned int freq) +{ + os_memset(params, 0, sizeof(*params)); + + if (freq) + params->freq = freq; + else + params->freq = atoi(pos); + + if (params->freq == 0) { + wpa_printf(MSG_ERROR, "freq_params: invalid freq provided"); + return -1; + } + +#define SET_FREQ_PARAM(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + params->str = atoi(pos2); \ + } \ + } while (0) + + SET_FREQ_PARAM(center_freq1); + SET_FREQ_PARAM(center_freq2); + SET_FREQ_PARAM(bandwidth); + SET_FREQ_PARAM(sec_channel_offset); + SET_FREQ_PARAM(punct_bitmap); + params->ht_enabled = !!os_strstr(pos, " ht"); + params->vht_enabled = !!os_strstr(pos, " vht"); + params->eht_enabled = !!os_strstr(pos, " eht"); + params->he_enabled = !!os_strstr(pos, " he") || + params->eht_enabled; +#undef SET_FREQ_PARAM + + return 0; +} + + +static struct hostapd_hw_modes * get_target_hw_mode(struct hostapd_iface *iface, + int freq) +{ + int i; + enum hostapd_hw_mode target_mode; + bool is_6ghz = is_6ghz_freq(freq); + + if (freq < 4000) + target_mode = HOSTAPD_MODE_IEEE80211G; + else if (freq > 50000) + target_mode = HOSTAPD_MODE_IEEE80211AD; + else + target_mode = HOSTAPD_MODE_IEEE80211A; + + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode; + + mode = &iface->hw_features[i]; + if (mode->mode == target_mode && mode->is_6ghz == is_6ghz) + return mode; + } + + return NULL; +} + + +static bool +hostapd_ctrl_is_freq_in_mode(struct hostapd_hw_modes *mode, + struct hostapd_multi_hw_info *current_hw_info, + int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!chan_in_current_hw_info(current_hw_info, chan)) + continue; + + if (chan->freq == freq) + return true; + } + return false; +} + + +static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params, + u16 punct_bitmap) +{ + u32 start_freq; + bool is_6g = is_6ghz_freq(params->freq); + + if (is_6g) { + const int bw_idx[] = { 20, 40, 80, 160, 320 }; + int idx, bw; + + /* The 6 GHz band requires HE to be enabled. */ + params->he_enabled = 1; + + if (params->center_freq1) { + if (params->freq == 5935) + idx = (params->center_freq1 - 5925) / 5; + else + idx = (params->center_freq1 - 5950) / 5; + + bw = center_idx_to_bw_6ghz(idx); + if (bw < 0 || bw >= (int) ARRAY_SIZE(bw_idx) || + bw_idx[bw] != params->bandwidth) + return -1; + } + } else { /* Non-6 GHz channel */ + /* An EHT STA is also an HE STA as defined in + * IEEE Std 802.11be-2024, 4.3.16a (Extremely high throughput + * (EHT) STA). */ + if (params->he_enabled || params->eht_enabled) { + params->he_enabled = 1; + /* An HE STA is also a VHT STA if operating in the 5 GHz + * band and an HE STA is also an HT STA in the 2.4 GHz + * band as defined in IEEE Std 802.11ax-2021, 4.3.15a. + * A VHT STA is an HT STA as defined in IEEE + * Std 802.11, 4.3.15. */ + if (IS_5GHZ(params->freq)) + params->vht_enabled = 1; + + params->ht_enabled = 1; + } + } + + switch (params->bandwidth) { + case 0: + /* bandwidth not specified: use 20 MHz by default */ + /* fall-through */ + case 20: + if (params->center_freq1 && + params->center_freq1 != params->freq) + return -1; + + if (params->center_freq2 || params->sec_channel_offset) + return -1; + + if (punct_bitmap) + return -1; + break; + case 40: + if (params->center_freq2 || + (!is_6g && !params->sec_channel_offset)) + return -1; + + if (punct_bitmap) + return -1; + + if (!params->center_freq1) + break; + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 10 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq - 10 != params->center_freq1) + return -1; + break; + case 0: + if (!is_6g) + return -1; + break; + default: + return -1; + } + break; + case 80: + if (!params->center_freq1 || + (!is_6g && !params->sec_channel_offset)) + return 1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq - 10 != params->center_freq1 && + params->freq + 30 != params->center_freq1) + return 1; + break; + case -1: + if (params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1) + return -1; + break; + case 0: + if (!is_6g) + return -1; + break; + default: + return -1; + } + + if (params->center_freq2 && punct_bitmap) + return -1; + + /* Adjacent and overlapped are not allowed for 80+80 */ + if (params->center_freq2 && + params->center_freq1 - params->center_freq2 <= 80 && + params->center_freq2 - params->center_freq1 <= 80) + return 1; + break; + case 160: + if (!params->center_freq1 || params->center_freq2 || + (!is_6g && !params->sec_channel_offset)) + return -1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 70 != params->center_freq1 && + params->freq + 30 != params->center_freq1 && + params->freq - 10 != params->center_freq1 && + params->freq - 50 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq + 50 != params->center_freq1 && + params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1 && + params->freq - 70 != params->center_freq1) + return -1; + break; + case 0: + if (!is_6g) + return -1; + break; + default: + return -1; + } + break; + case 320: + if (!params->center_freq1 || params->center_freq2) + return -1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 150 != params->center_freq1 && + params->freq + 110 != params->center_freq1 && + params->freq + 70 != params->center_freq1 && + params->freq + 30 != params->center_freq1 && + params->freq - 10 != params->center_freq1 && + params->freq - 50 != params->center_freq1 && + params->freq - 90 != params->center_freq1 && + params->freq - 130 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq + 130 != params->center_freq1 && + params->freq + 90 != params->center_freq1 && + params->freq + 50 != params->center_freq1 && + params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1 && + params->freq - 70 != params->center_freq1 && + params->freq - 110 != params->center_freq1 && + params->freq - 150 != params->center_freq1) + return -1; + break; + case 0: + break; + default: + return -1; + } + break; + default: + return -1; + } + + if (!punct_bitmap) + return 0; + + if (!params->eht_enabled) { + wpa_printf(MSG_ERROR, + "Preamble puncturing supported only in EHT"); + return -1; + } + + if (params->freq >= 2412 && params->freq <= 2484) { + wpa_printf(MSG_ERROR, + "Preamble puncturing is not supported in 2.4 GHz"); + return -1; + } + + start_freq = params->center_freq1 - (params->bandwidth / 2); + if (!is_punct_bitmap_valid(params->bandwidth, + (params->freq - start_freq) / 20, + punct_bitmap)) { + wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap"); + return -1; + } + + return 0; +} + + +int hostapd_parse_csa_settings(struct hostapd_iface *iface, + const char *pos, struct csa_settings *settings) { + struct hostapd_hw_modes *target_mode; char *end; + int ret; os_memset(settings, 0, sizeof(*settings)); settings->cs_count = strtol(pos, &end, 10); @@ -1106,42 +1440,37 @@ int hostapd_parse_csa_settings(const char *pos, return -1; } - settings->freq_params.freq = atoi(end); - if (settings->freq_params.freq == 0) { - wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); + settings->block_tx = !!os_strstr(pos, " blocktx"); + + ret = hostapd_parse_freq_params(end, &settings->freq_params, 0); + if (ret < 0) { + wpa_printf(MSG_INFO, + "chanswitch: failed to parse frequency parameters"); + return ret; + } + + target_mode = get_target_hw_mode(iface, settings->freq_params.freq); + if (!target_mode) { + wpa_printf(MSG_DEBUG, + "chanswitch: Invalid frequency settings provided for hw mode"); return -1; } -#define SET_CSA_SETTING(str) \ - do { \ - const char *pos2 = os_strstr(pos, " " #str "="); \ - if (pos2) { \ - pos2 += sizeof(" " #str "=") - 1; \ - settings->freq_params.str = atoi(pos2); \ - } \ - } while (0) + if (iface->num_hw_features > 1 && + !hostapd_ctrl_is_freq_in_mode(target_mode, iface->current_hw_info, + settings->freq_params.freq)) { + wpa_printf(MSG_INFO, + "chanswitch: Invalid frequency settings provided for multi band phy"); + return -1; + } -#define SET_CSA_SETTING_EXT(str) \ - do { \ - const char *pos2 = os_strstr(pos, " " #str "="); \ - if (pos2) { \ - pos2 += sizeof(" " #str "=") - 1; \ - settings->str = atoi(pos2); \ - } \ - } while (0) - - SET_CSA_SETTING(center_freq1); - SET_CSA_SETTING(center_freq2); - SET_CSA_SETTING(bandwidth); - SET_CSA_SETTING(sec_channel_offset); - SET_CSA_SETTING_EXT(punct_bitmap); - settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); - settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); - settings->freq_params.he_enabled = !!os_strstr(pos, " he"); - settings->freq_params.eht_enabled = !!os_strstr(pos, " eht"); - settings->block_tx = !!os_strstr(pos, " blocktx"); -#undef SET_CSA_SETTING -#undef SET_CSA_SETTING_EXT + ret = hostapd_ctrl_check_freq_params(&settings->freq_params, + settings->freq_params.punct_bitmap); + if (ret) { + wpa_printf(MSG_INFO, + "chanswitch: invalid frequency settings provided"); + return ret; + } return 0; } @@ -1174,6 +1503,7 @@ int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) size_t pmk_len; char *pos, *pos2; int akmp = 0, expiration = 0; + int ret; /* * Entry format: @@ -1209,8 +1539,18 @@ int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) return -1; - return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, - pmkid, expiration, akmp, NULL); + ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, + pmkid, expiration, akmp, NULL, false); + if (ret) + return ret; + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, + pmkid, expiration, akmp, NULL, true); +#endif /* CONFIG_IEEE80211BE */ + + return ret; } @@ -1609,6 +1949,11 @@ int hostapd_disassoc_deny_mac(struct hostapd_data *hapd) struct vlan_description vlan_id; for (sta = hapd->sta_list; sta; sta = sta->next) { +#ifdef CONFIG_IEEE80211BE + int link_id; + struct mld_link_info *info; +#endif /* CONFIG_IEEE80211BE */ + if (hostapd_maclist_found(hapd->conf->deny_mac, hapd->conf->num_deny_mac, sta->addr, &vlan_id) && @@ -1616,6 +1961,24 @@ int hostapd_disassoc_deny_mac(struct hostapd_data *hapd) !vlan_compare(&vlan_id, sta->vlan_desc))) ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_UNSPECIFIED); +#ifdef CONFIG_IEEE80211BE + for (link_id = 0; hapd->conf->mld_ap && + link_id < MAX_NUM_MLD_LINKS && + sta->mld_info.mld_sta; link_id++) { + info = &sta->mld_info.links[link_id]; + if (!info->valid || link_id != hapd->mld_link_id) + continue; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, + info->peer_addr, + &vlan_id) && + (!vlan_id.notempty || + !vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +#endif /* CONFIG_IEEE80211BE */ } return 0; diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.h b/contrib/wpa/src/ap/ctrl_iface_ap.h index 614f0426c1a5..4de140844f0b 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.h +++ b/contrib/wpa/src/ap/ctrl_iface_ap.h @@ -26,7 +26,11 @@ int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen); -int hostapd_parse_csa_settings(const char *pos, +int hostapd_parse_freq_params(const char *pos, + struct hostapd_freq_params *params, + unsigned int freq); +int hostapd_parse_csa_settings(struct hostapd_iface *iface, + const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c index af9dc16f5987..d72c8ddb4464 100644 --- a/contrib/wpa/src/ap/dfs.c +++ b/contrib/wpa/src/ap/dfs.c @@ -2,6 +2,7 @@ * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -253,6 +254,13 @@ static int dfs_find_channel(struct hostapd_iface *iface, for (i = 0; i < mode->num_channels; i++) { chan = &mode->channels[i]; + if (!chan_in_current_hw_info(iface->current_hw_info, chan)) { + wpa_printf(MSG_DEBUG, + "DFS: channel %d (%d) is not under current hardware index", + chan->freq, chan->chan); + continue; + } + /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && @@ -974,6 +982,7 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface, u8 new_vht_oper_chwidth; unsigned int i; unsigned int num_err = 0; + u8 op_class, chan; wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL @@ -982,6 +991,13 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface, new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth); + if (ieee80211_freq_to_channel_ext(freq, secondary_channel, + new_vht_oper_chwidth, &op_class, + &chan) != NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, "Update op_class %d->%d", + iface->conf->op_class, op_class); + iface->conf->op_class = op_class; + } /* Setup CSA request */ os_memset(&csa_settings, 0, sizeof(csa_settings)); @@ -1039,6 +1055,14 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface, oper_centr_freq_seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, oper_centr_freq_seg1_idx); + if (ieee80211_freq_to_channel_ext(freq, secondary_channel, + new_vht_oper_chwidth, + &op_class, &chan) != + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, "Update op_class %d->%d", + iface->conf->op_class, op_class); + iface->conf->op_class = op_class; + } hostapd_disable_iface(iface); hostapd_enable_iface(iface); @@ -1106,6 +1130,25 @@ static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface) } +static bool +hostapd_is_freq_in_current_hw_info(struct hostapd_iface *iface, int freq) +{ + struct hostapd_channel_data *chan; + + if (!iface->current_mode) + return false; + + chan = hw_mode_get_channel(iface->current_mode, freq, NULL); + + /* If channel data is not found for the given frequency, consider it is + * out of the current hardware info. */ + if (!chan) + return false; + + return chan_in_current_hw_info(iface->current_hw_info, chan); +} + + static bool hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq) { @@ -1143,6 +1186,14 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { + if (!hostapd_is_freq_in_current_hw_info(iface, freq)) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + DFS_EVENT_CAC_COMPLETED + "Ignoring since freq=%d info is out of own range", + freq); + return 0; + } + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d", success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, @@ -1214,6 +1265,14 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { + if (!hostapd_is_freq_in_current_hw_info(iface, freq)) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + DFS_EVENT_PRE_CAC_EXPIRED + "Ignoring since freq=%d info is out of own range", + freq); + return 0; + } + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); @@ -1276,6 +1335,7 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) u8 oper_centr_freq_seg1_idx = 0; enum dfs_channel_type channel_type = DFS_ANY_CHANNEL; int err = 1; + u8 op_class, chan; /* Radar detected during active CAC */ iface->cac_started = 0; @@ -1308,6 +1368,14 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) oper_centr_freq_seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(iface->conf, oper_centr_freq_seg1_idx); + if (ieee80211_freq_to_channel_ext(channel->freq, secondary_channel, + hostapd_get_oper_chwidth(iface->conf), + &op_class, &chan) != + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, "Update op_class %d->%d", + iface->conf->op_class, op_class); + iface->conf->op_class = op_class; + } err = 0; hostapd_setup_interface_complete(iface, err); @@ -1445,6 +1513,14 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { + if (!hostapd_is_freq_in_current_hw_info(iface, freq)) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + DFS_EVENT_RADAR_DETECTED + "Ignoring since freq=%d info is out of own range", + freq); + return 0; + } + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); @@ -1483,6 +1559,14 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { + if (!hostapd_is_freq_in_current_hw_info(iface, freq)) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + DFS_EVENT_NOP_FINISHED + "Ignoring since freq=%d info is out of own range", + freq); + return 0; + } + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); @@ -1536,10 +1620,19 @@ int hostapd_is_dfs_required(struct hostapd_iface *iface) } -int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, +int hostapd_dfs_start_cac(struct hostapd_data *hapd, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) { + struct hostapd_iface *iface = hapd->iface; + + if (!hostapd_is_freq_in_current_hw_info(iface, freq)) { + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + "Ignoring since freq=%d info is out of own range", + freq); + return 0; + } + if (hostapd_dfs_is_background_event(iface, freq)) { iface->radar_background.cac_started = 1; } else { @@ -1558,7 +1651,7 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, } /* TODO: How to check CAC time for ETSI weather channels? */ iface->dfs_cac_ms = 60000; - wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + wpa_msg(hapd->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " "seg1=%d cac_time=%ds%s", freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, diff --git a/contrib/wpa/src/ap/dfs.h b/contrib/wpa/src/ap/dfs.h index 606c1b39399f..a3cb13cd87bd 100644 --- a/contrib/wpa/src/ap/dfs.h +++ b/contrib/wpa/src/ap/dfs.h @@ -26,7 +26,7 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, int chan_offset, int chan_width, int cf1, int cf2); int hostapd_is_dfs_required(struct hostapd_iface *iface); int hostapd_is_dfs_chan_available(struct hostapd_iface *iface); -int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, +int hostapd_dfs_start_cac(struct hostapd_data *hapd, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); int hostapd_handle_dfs_offload(struct hostapd_iface *iface); diff --git a/contrib/wpa/src/ap/dpp_hostapd.c b/contrib/wpa/src/ap/dpp_hostapd.c index d1bffa81152b..eff28c6a9223 100644 --- a/contrib/wpa/src/ap/dpp_hostapd.c +++ b/contrib/wpa/src/ap/dpp_hostapd.c @@ -2,7 +2,7 @@ * hostapd / DPP integration * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -934,6 +934,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) #endif /* CONFIG_DPP2 */ hostapd_drv_send_action_cancel_wait(hapd); dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; } auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx, @@ -1382,6 +1383,21 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) } +static void hostapd_gas_req_wait(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth) + return; + + wpa_printf(MSG_DEBUG, "DPP: Timeout while waiting for Config Request"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) { wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); @@ -1400,6 +1416,9 @@ static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) if (!hapd->dpp_auth->configurator) hostapd_dpp_start_gas_client(hapd); + else + eloop_register_timeout(10, 0, hostapd_gas_req_wait, + hapd, NULL); } @@ -2142,11 +2161,22 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, intro.pmkid, expiration, - WPA_KEY_MGMT_DPP, pkhash) < 0) { + WPA_KEY_MGMT_DPP, pkhash, false) < 0) { wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); goto done; } +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap && + wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP, pkhash, true) < 0) { + wpa_printf(MSG_ERROR, + "DPP: Failed to add PMKSA cache entry (MLD)"); + goto done; + } +#endif /* CONFIG_IEEE80211BE */ + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], DPP_STATUS_OK); done: @@ -2916,11 +2946,22 @@ hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src, if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, intro.pmkid, expiration, - WPA_KEY_MGMT_DPP, pkhash) < 0) { + WPA_KEY_MGMT_DPP, pkhash, false) < 0) { wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); goto done; } +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap && + wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP, pkhash, true) < 0) { + wpa_printf(MSG_ERROR, + "DPP: Failed to add PMKSA cache entry (MLD)"); + goto done; + } +#endif /* CONFIG_IEEE80211BE */ + wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with " MACSTR, MAC2STR(src)); @@ -2940,6 +2981,10 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, const u8 *hdr; unsigned int pkex_t; + /* Discard DPP Action frames if there is no global DPP context */ + if (!hapd->iface->interfaces || !hapd->iface->interfaces->dpp) + return; + if (len < DPP_HDR_LEN) return; if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE) @@ -3079,6 +3124,7 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, struct wpabuf *resp; wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); + eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL); if (!auth || (!auth->auth_success && !auth->reconfig_success) || !ether_addr_equal(sa, auth->peer_mac_addr)) { #ifdef CONFIG_DPP2 @@ -3359,6 +3405,7 @@ void hostapd_dpp_stop(struct hostapd_data *hapd) #ifdef CONFIG_DPP3 hostapd_dpp_push_button_stop(hapd); #endif /* CONFIG_DPP3 */ + eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL); } @@ -3512,6 +3559,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL); #ifdef CONFIG_DPP2 eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout, hapd, NULL); @@ -3940,6 +3988,7 @@ int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd) } eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire, hapd, NULL); + hostapd_drv_dpp_listen(hapd, true); wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_STATUS "started"); return 0; @@ -3955,6 +4004,7 @@ void hostapd_dpp_push_button_stop(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL); if (hostapd_dpp_pb_active(hapd)) { wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode"); + hostapd_drv_dpp_listen(hapd, false); if (!ifaces->dpp_pb_result_indicated) wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT "failed"); diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c index 8b49b531e915..116a055033a0 100644 --- a/contrib/wpa/src/ap/drv_callbacks.c +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -1,6 +1,7 @@ /* * hostapd / Callback functions for driver wrappers * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +19,7 @@ #include "common/dpp.h" #include "common/sae.h" #include "common/hw_features_common.h" +#include "common/nan_de.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" @@ -43,6 +45,7 @@ #include "fils_hlp.h" #include "neighbor_db.h" #include "nan_usd_ap.h" +#include "interference.h" #ifdef CONFIG_FILS @@ -73,6 +76,8 @@ void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, elems.fils_session, sta->fils_hlp_resp); + if (!p) + return; reply_res = hostapd_sta_assoc(hapd, sta->addr, sta->fils_pending_assoc_is_reassoc, @@ -186,7 +191,22 @@ static int hostapd_update_sta_links_status(struct hostapd_data *hapd, /* Parse Subelements */ while (rem_len > 2) { - size_t ie_len = 2 + pos[1]; + size_t ie_len, subelem_defrag_len; + int num_frag_subelems; + + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlebuf, pos, + &subelem_defrag_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse MLE subelem"); + break; + } + + ie_len = 2 + subelem_defrag_len; + if ((size_t) num_frag_subelems * 2 > rem_len) + break; + rem_len -= num_frag_subelems * 2; if (rem_len < ie_len) break; @@ -197,13 +217,13 @@ static int hostapd_update_sta_links_status(struct hostapd_data *hapd, size_t sta_profile_len; u16 sta_ctrl; - if (pos[1] < BASIC_MLE_STA_CTRL_LEN + 1) { + if (subelem_defrag_len < BASIC_MLE_STA_CTRL_LEN + 1) { wpa_printf(MSG_DEBUG, "MLO: Invalid per-STA profile IE"); goto next_subelem; } - sta_profile_len = pos[1]; + sta_profile_len = subelem_defrag_len; sta_profile = &pos[2]; sta_ctrl = WPA_GET_LE16(sta_profile); link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK; @@ -248,6 +268,52 @@ out: #endif /* CONFIG_IEEE80211BE */ +#if defined(HOSTAPD) || defined(CONFIG_IEEE80211BE) +static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface, + const u8 *src, bool rsn, + struct sta_info **sta_ret) +{ + struct hostapd_data *hapd; + struct sta_info *sta; + unsigned int j; + + if (sta_ret) + *sta_ret = NULL; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, src); + if (sta && (sta->flags & WLAN_STA_ASSOC) && + (!rsn || sta->wpa_sm)) { + if (sta_ret) + *sta_ret = sta; + return hapd; + } +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + struct hostapd_data *p_hapd; + + for_each_mld_link(p_hapd, hapd) { + if (p_hapd == hapd) + continue; + + sta = ap_get_sta(p_hapd, src); + if (sta && (sta->flags & WLAN_STA_ASSOC) && + (!rsn || sta->wpa_sm)) { + if (sta_ret) + *sta_ret = sta; + return p_hapd; + } + } + } +#endif /* CONFIG_IEEE80211BE */ + } + + return NULL; +} +#endif /* HOSTAPD || CONFIG_IEEE80211BE */ + + int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *req_ies, size_t req_ies_len, const u8 *resp_ies, size_t resp_ies_len, @@ -268,6 +334,15 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct hostapd_iface *iface = hapd->iface; #endif /* CONFIG_OWE */ bool updated = false; + bool driver_acl; + +#ifdef CONFIG_P2P + if (hapd->p2p_group && (!hapd->started || hapd->disabled)) { + wpa_printf(MSG_DEBUG, + "hostapd_notif_assoc: Ignore assoc event - P2P GO not started or disabled"); + return 0; + } +#endif /* CONFIG_P2P */ if (addr == NULL) { /* @@ -317,12 +392,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, ie = elems.wpa_ie - 2; ielen = elems.wpa_ie_len + 2; wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq"); -#ifdef CONFIG_HS20 - } else if (elems.osen) { - ie = elems.osen - 2; - ielen = elems.osen_len + 2; - wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq"); -#endif /* CONFIG_HS20 */ } else { ie = NULL; ielen = 0; @@ -340,6 +409,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, * will not remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; + } else if (!hostapd_acceptable_sta_addr(hapd, addr, addr, false)) { + wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", + MAC2STR(addr)); + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_UNSPECIFIED); + return -1; } else { sta = ap_sta_add(hapd, addr); if (sta == NULL) { @@ -404,13 +478,57 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, * ACL if the driver supports ACL offload to avoid potentially * conflicting ACL rules. */ - if (hapd->iface->drv_max_acl_mac_addrs == 0 && + driver_acl = hapd->iface->drv_max_acl_mac_addrs > 0; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + driver_acl = false; +#endif /* CONFIG_IEEE80211BE */ + if (!driver_acl && hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) { wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", MAC2STR(addr)); reason = WLAN_REASON_UNSPECIFIED; goto fail; } +#ifdef CONFIG_IEEE80211BE + /* + * The idea is that ACL is per link. For MLO associations, check + * whether peer MLD MAC address is acceptable in all requested links. + * For each peer link address, check the corresponding association + * local link's ACL configuration whether it is acceptable. + */ + if (!driver_acl && hapd->conf->mld_ap && link_addr) { + int link_id; + struct mld_link_info *info; + struct hostapd_data *bss; + + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + info = &sta->mld_info.links[link_id]; + if (!info->valid) + continue; + + bss = hostapd_mld_get_link_bss(hapd, link_id); + if (bss && bss != hapd && + hostapd_check_acl(bss, addr, NULL) != + HOSTAPD_ACL_ACCEPT) { + wpa_printf(MSG_INFO, "STA " MACSTR + " not allowed to connect", + MAC2STR(addr)); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + if (bss && + hostapd_check_acl(bss, info->peer_addr, NULL) != + HOSTAPD_ACL_ACCEPT) { + wpa_printf(MSG_INFO, "link addr " MACSTR + " not allowed to connect", + MAC2STR(info->peer_addr)); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + } + } +#endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_P2P if (elems.p2p) { @@ -481,7 +599,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; goto fail; } #ifdef CONFIG_WPS @@ -513,6 +631,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, "Failed to initialize WPA state machine"); return -1; } + wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection, + elems.rsn_selection_len); + wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); #ifdef CONFIG_IEEE80211BE if (ap_sta_is_mld(hapd, sta)) { wpa_printf(MSG_DEBUG, @@ -528,9 +649,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, - elems.owe_dh, elems.owe_dh_len, NULL); + elems.owe_dh, elems.owe_dh_len, NULL, + ap_sta_is_mld(hapd, sta)); reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; switch (res) { case WPA_IE_OK: reason = WLAN_REASON_UNSPECIFIED; @@ -538,23 +660,23 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, break; case WPA_INVALID_IE: reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; break; case WPA_INVALID_GROUP: reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_INVALID_GROUP_CIPHER; break; case WPA_INVALID_PAIRWISE: reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; - status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + status = WLAN_STATUS_INVALID_PAIRWISE_CIPHER; break; case WPA_INVALID_AKMP: reason = WLAN_REASON_AKMP_NOT_VALID; - status = WLAN_STATUS_AKMP_NOT_VALID; + status = WLAN_STATUS_INVALID_AKMP; break; case WPA_NOT_ENABLED: reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; break; case WPA_ALLOC_FAIL: reason = WLAN_REASON_UNSPECIFIED; @@ -562,19 +684,19 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, break; case WPA_MGMT_FRAME_PROTECTION_VIOLATION: reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; break; case WPA_INVALID_MGMT_GROUP_CIPHER: reason = WLAN_REASON_CIPHER_SUITE_REJECTED; - status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + status = WLAN_STATUS_CIPHER_OUT_OF_POLICY; break; case WPA_INVALID_MDIE: reason = WLAN_REASON_INVALID_MDE; - status = WLAN_STATUS_INVALID_MDIE; + status = WLAN_STATUS_INVALID_MDE; break; case WPA_INVALID_PROTO: reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; break; case WPA_INVALID_PMKID: reason = WLAN_REASON_INVALID_PMKID; @@ -598,6 +720,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, else sta->flags &= ~WLAN_STA_MFP; + if (wpa_auth_uses_spp_amsdu(sta->wpa_sm)) + sta->flags |= WLAN_STA_SPP_AMSDU; + else + sta->flags &= ~WLAN_STA_SPP_AMSDU; + #ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, @@ -605,9 +732,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (status != WLAN_STATUS_SUCCESS) { if (status == WLAN_STATUS_INVALID_PMKID) reason = WLAN_REASON_INVALID_IE; - if (status == WLAN_STATUS_INVALID_MDIE) + if (status == WLAN_STATUS_INVALID_MDE) reason = WLAN_REASON_INVALID_IE; - if (status == WLAN_STATUS_INVALID_FTIE) + if (status == WLAN_STATUS_INVALID_FTE) reason = WLAN_REASON_INVALID_IE; goto fail; } @@ -619,14 +746,27 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->sae && !sta->sae->h2e && ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, WLAN_RSNX_CAPAB_SAE_H2E)) { - wpa_printf(MSG_INFO, "SAE: " MACSTR - " indicates support for SAE H2E, but did not use it", - MAC2STR(sta->addr)); - status = WLAN_STATUS_UNSPECIFIED_FAILURE; - reason = WLAN_REASON_UNSPECIFIED; - goto fail; + if (hapd->conf->sae_accept_h2e_without_use) { + wpa_printf(MSG_INFO, "SAE: " MACSTR + " indicates support for SAE H2E, but did not use it - accepting due to sae_accept_h2e_without_use", + MAC2STR(sta->addr)); + } else { + wpa_printf(MSG_INFO, "SAE: " MACSTR + " indicates support for SAE H2E, but did not use it", + MAC2STR(sta->addr)); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } } #endif /* CONFIG_SAE */ + + wpa_auth_set_ssid_protection( + sta->wpa_sm, + hapd->conf->ssid_protection && + ieee802_11_rsnx_capab_len( + elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_SSID_PROTECTION)); } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -639,7 +779,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #ifdef CONFIG_WPS_STRICT if (wps && wps_validate_assoc_req(wps) < 0) { reason = WLAN_REASON_INVALID_IE; - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; wpabuf_free(wps); goto fail; } @@ -655,29 +795,6 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags |= WLAN_STA_MAYBE_WPS; wpabuf_free(wps); #endif /* CONFIG_WPS */ -#ifdef CONFIG_HS20 - } else if (hapd->conf->osen) { - if (elems.osen == NULL) { - hostapd_logger( - hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "No HS 2.0 OSEN element in association request"); - return WLAN_STATUS_INVALID_IE; - } - - wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr, NULL); - if (sta->wpa_sm == NULL) { - wpa_printf(MSG_WARNING, - "Failed to initialize WPA state machine"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } - if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, - elems.osen - 2, elems.osen_len + 2) < 0) - return WLAN_STATUS_INVALID_IE; -#endif /* CONFIG_HS20 */ } #ifdef CONFIG_WPS skip_wpa_check: @@ -696,7 +813,7 @@ skip_wpa_check: #ifdef CONFIG_IEEE80211R_AP p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), sta->auth_alg, req_ies, req_ies_len, - !elems.rsnxe); + !elems.rsnxe, reassoc, sta->vlan_id); if (!p) { wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -773,6 +890,9 @@ skip_wpa_check: p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, elems.fils_session, sta->fils_hlp_resp); + if (!p) + goto fail; + wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)", buf, p - buf); } @@ -857,21 +977,22 @@ skip_wpa_check: } #endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + + hostapd_set_sta_flags(hapd, sta); + #ifdef CONFIG_IEEE80211BE if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len, !!reassoc, WLAN_STATUS_SUCCESS, - true)) { + true, NULL)) { status = WLAN_STATUS_UNSPECIFIED_FAILURE; reason = WLAN_REASON_UNSPECIFIED; goto fail; } #endif /* CONFIG_IEEE80211BE */ - new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; - sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; - sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; - - hostapd_set_sta_flags(hapd, sta); if (updated) ap_sta_set_authorized_event(hapd, sta, 1); @@ -897,6 +1018,10 @@ skip_wpa_check: } #endif /* CONFIG_P2P */ + if (elems.wfa_capab) + hostapd_wfa_capab(hapd, sta, elems.wfa_capab, + elems.wfa_capab + elems.wfa_capab_len); + return 0; fail: @@ -923,33 +1048,27 @@ static void hostapd_remove_sta(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_IEEE80211BE -static void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd, - struct sta_info *sta, - const u8 *addr) +void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd, + struct sta_info *sta, const u8 *addr) { - unsigned int link_id, i; + unsigned int i; struct hostapd_data *tmp_hapd; struct hapd_interfaces *interfaces = assoc_hapd->iface->interfaces; /* Remove STA entry in non-assoc links */ - for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { - if (!sta->mld_info.links[link_id].valid) + for (i = 0; i < interfaces->count; i++) { + struct sta_info *tmp_sta; + + tmp_hapd = interfaces->iface[i]->bss[0]; + + if (!tmp_hapd->conf->mld_ap || + assoc_hapd == tmp_hapd || + !hostapd_is_ml_partner(assoc_hapd, tmp_hapd)) continue; - for (i = 0; i < interfaces->count; i++) { - struct sta_info *tmp_sta; - - tmp_hapd = interfaces->iface[i]->bss[0]; - - if (!tmp_hapd->conf->mld_ap || - assoc_hapd == tmp_hapd || - assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id) - continue; - - tmp_sta = ap_get_sta(tmp_hapd, addr); - if (tmp_sta) - ap_free_sta(tmp_hapd, tmp_sta); - } + tmp_sta = ap_get_sta(tmp_hapd, addr); + if (tmp_sta) + ap_free_sta(tmp_hapd, tmp_sta); } /* Remove STA in assoc link */ @@ -980,7 +1099,7 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) sta = ap_get_sta(hapd, addr); #ifdef CONFIG_IEEE80211BE - if (hostapd_is_mld_ap(hapd)) { + if (hostapd_is_multiple_link_mld(hapd)) { struct hostapd_data *assoc_hapd; unsigned int i; @@ -994,7 +1113,7 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) struct hostapd_bss_config *hconf = h_hapd->conf; if (!hconf->mld_ap || - hconf->mld_id != hapd->conf->mld_id) + !hostapd_is_ml_partner(hapd, h_hapd)) continue; sta = ap_get_sta(h_hapd, addr); @@ -1037,6 +1156,20 @@ legacy: void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); +#ifdef CONFIG_IEEE80211BE + struct hostapd_data *orig_hapd = hapd; + + if (!sta && hapd->conf->mld_ap) { + hapd = hostapd_find_by_sta(hapd->iface, addr, true, &sta); + if (!hapd) { + wpa_printf(MSG_DEBUG, + "No partner link BSS found for STA " MACSTR + " - fallback to received context", + MAC2STR(addr)); + hapd = orig_hapd; + } + } +#endif /* CONFIG_IEEE80211BE */ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer) return; @@ -1402,10 +1535,9 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd, if (mode->mode == acs_res->hw_mode) { if (hapd->iface->freq > 0 && - !hw_get_chan(mode->mode, - hapd->iface->freq, - hapd->iface->hw_features, - hapd->iface->num_hw_features)) + !hw_mode_get_channel(mode, + hapd->iface->freq, + NULL)) continue; hapd->iface->current_mode = mode; break; @@ -1587,8 +1719,6 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, { struct sta_info *sta; u16 status = WLAN_STATUS_SUCCESS; - u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; - size_t resp_ies_len = 0; sta = ap_get_sta(hapd, rx_auth->peer); if (!sta) { @@ -1633,7 +1763,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, fail: hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, - status, resp_ies, resp_ies_len); + status, NULL, 0); } @@ -1714,14 +1844,14 @@ static void hostapd_action_rx(struct hostapd_data *hapd, WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == OUI_WFA && - mgmt->u.action.u.vs_public_action.variable[0] == NAN_OUI_TYPE) { + mgmt->u.action.u.vs_public_action.variable[0] == NAN_SDF_OUI_TYPE) { const u8 *pos, *end; pos = mgmt->u.action.u.vs_public_action.variable; end = drv_mgmt->frame + drv_mgmt->frame_len; pos++; - hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq, - pos, end - pos); + hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid, + drv_mgmt->freq, pos, end - pos); return; } #endif /* CONFIG_NAN_USD */ @@ -1788,6 +1918,11 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff && bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff) return HAPD_BROADCAST; +#ifdef CONFIG_NAN_USD + if (nan_de_is_nan_network_id(bssid)) + return HAPD_BROADCAST; /* Process NAN Network ID like broadcast + */ +#endif /* CONFIG_NAN_USD */ for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *hapd; @@ -1921,18 +2056,19 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, { struct ieee80211_hdr *hdr; struct hostapd_data *orig_hapd, *tmp_hapd; + const u8 *bssid; orig_hapd = hapd; hdr = (struct ieee80211_hdr *) buf; hapd = switch_link_hapd(hapd, link_id); - tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id); + bssid = get_hdr_bssid(hdr, len); + tmp_hapd = get_hapd_bssid(hapd->iface, bssid, link_id); if (tmp_hapd) { hapd = tmp_hapd; #ifdef CONFIG_IEEE80211BE - } else if (hapd->conf->mld_ap && - ether_addr_equal(hapd->mld->mld_addr, - get_hdr_bssid(hdr, len))) { + } else if (hapd->conf->mld_ap && bssid && + ether_addr_equal(hapd->mld->mld_addr, bssid)) { /* AP MLD address match - use hapd pointer as-is */ #endif /* CONFIG_IEEE80211BE */ } else { @@ -1979,50 +2115,6 @@ static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) } -static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface, - const u8 *src, bool rsn, - struct sta_info **sta_ret) -{ - struct hostapd_data *hapd; - struct sta_info *sta; - unsigned int j; - - if (sta_ret) - *sta_ret = NULL; - - for (j = 0; j < iface->num_bss; j++) { - hapd = iface->bss[j]; - sta = ap_get_sta(hapd, src); - if (sta && (sta->flags & WLAN_STA_ASSOC) && - (!rsn || sta->wpa_sm)) { - if (sta_ret) - *sta_ret = sta; - return hapd; - } -#ifdef CONFIG_IEEE80211BE - if (hapd->conf->mld_ap) { - struct hostapd_data *p_hapd; - - for_each_mld_link(p_hapd, hapd) { - if (p_hapd == hapd) - continue; - - sta = ap_get_sta(p_hapd, src); - if (sta && (sta->flags & WLAN_STA_ASSOC) && - (!rsn || sta->wpa_sm)) { - if (sta_ret) - *sta_ret = sta; - return p_hapd; - } - } - } -#endif /* CONFIG_IEEE80211BE */ - } - - return NULL; -} - - static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, const u8 *data, size_t data_len, enum frame_encryption encrypted, @@ -2166,6 +2258,8 @@ void hostapd_event_get_survey(struct hostapd_iface *iface, continue; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; + if (!(chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)) + continue; dl_list_del(&survey->list); dl_list_add_tail(&chan->survey_list, &survey->list); @@ -2191,6 +2285,10 @@ static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) hostapd_switch_channel_fallback(hapd->iface, &hapd->cs_freq_params); } + + /* Clear beacon_set_done so that the RNR and other beacon parameters + * are properly updated. */ + hapd->beacon_set_done = 0; } @@ -2248,11 +2346,26 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd, struct dfs_event *radar) { wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq); - hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled, + hostapd_dfs_start_cac(hapd, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } + +static void hostapd_event_incumbt_sig_intf_detected( + struct hostapd_data *hapd, struct incumbt_sig_intf_event *intf_info) +{ + wpa_printf(MSG_DEBUG, "Interference due to incumbent signal detected on %d MHz with intf bitmap 0x%x on link_id %d", + intf_info->freq, intf_info->chan_bw_interference_bitmap, + intf_info->link_id); + if (hostapd_incumbt_sig_intf_detected( + hapd->iface, intf_info->freq, intf_info->chan_width, + intf_info->cf1, intf_info->cf2, + intf_info->chan_bw_interference_bitmap)) + wpa_printf(MSG_INFO, + "Failed to handle incumbent signal interference"); +} + #endif /* NEED_AP_MLME */ @@ -2297,7 +2410,7 @@ static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd, } if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) { wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured"); - status = WLAN_STATUS_AKMP_NOT_VALID; + status = WLAN_STATUS_INVALID_AKMP; goto err; } if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) { @@ -2394,8 +2507,14 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success) if (bss->cca_color == 0) continue; - if (success) + if (success) { hapd->iface->conf->he_op.he_bss_color = bss->cca_color; + /* Clear the disabled flag set during CCA so that + * subsequent beacon rebuilds advertise the new color + * as enabled. + */ + hapd->iface->conf->he_op.he_bss_color_disabled = 0; + } bss->cca_in_progress = 0; if (ieee802_11_set_beacon(bss)) { @@ -2409,6 +2528,88 @@ static void hostapd_event_color_change(struct hostapd_data *hapd, bool success) #endif /* CONFIG_IEEE80211AX */ +static void hostapd_iface_enable(struct hostapd_data *hapd) +{ + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED); + if (hapd->disabled && hapd->started) { + hapd->disabled = 0; + /* + * Try to re-enable interface if the driver stopped it + * when the interface got disabled. + */ + if (hapd->wpa_auth) + wpa_auth_reconfig_group_keys(hapd->wpa_auth); + else + hostapd_reconfig_encryption(hapd); + hapd->reenable_beacon = 1; + ieee802_11_set_beacon(hapd); +#ifdef NEED_AP_MLME + } else if (hapd->disabled && hapd->iface->cac_started) { + wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); + hostapd_handle_dfs(hapd->iface); +#endif /* NEED_AP_MLME */ + } +} + + +static void hostapd_iface_disable(struct hostapd_data *hapd) +{ + hostapd_free_stas(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED); + hapd->disabled = 1; +} + + +#ifdef CONFIG_IEEE80211BE + +static void hostapd_mld_iface_enable(struct hostapd_data *hapd) +{ + struct hostapd_data *first_link, *link_bss; + + first_link = hostapd_mld_is_first_bss(hapd) ? hapd : + hostapd_mld_get_first_bss(hapd); + + /* Links have been removed. Re-add all links and enable them, but + * enable the first link BSS before doing that. */ + if (hostapd_drv_link_add(first_link, first_link->mld_link_id, + first_link->own_addr)) { + wpa_printf(MSG_ERROR, "MLD: Failed to re-add link %d in MLD %s", + first_link->mld_link_id, first_link->conf->iface); + return; + } + + hostapd_iface_enable(first_link); + + /* Add other affiliated links */ + for_each_mld_link(link_bss, first_link) { + if (link_bss == first_link) + continue; + + if (hostapd_drv_link_add(link_bss, link_bss->mld_link_id, + link_bss->own_addr)) { + wpa_printf(MSG_ERROR, + "MLD: Failed to re-add link %d in MLD %s", + link_bss->mld_link_id, + link_bss->conf->iface); + continue; + } + + hostapd_iface_enable(link_bss); + } +} + + +static void hostapd_mld_iface_disable(struct hostapd_data *hapd) +{ + struct hostapd_data *link_bss; + + for_each_mld_link(link_bss, hapd) + hostapd_iface_disable(link_bss); +} + +#endif /* CONFIG_IEEE80211BE */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -2497,6 +2698,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_client_poll_ok(hapd, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: + hapd = switch_link_hapd(hapd, data->rx_from_unknown.link_id); hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid, data->rx_from_unknown.addr, data->rx_from_unknown.wds); @@ -2639,6 +2841,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifdef NEED_AP_MLME case EVENT_INTERFACE_UNAVAILABLE: hostapd_event_iface_unavailable(hapd); + /* Update beacon information in all other interfaces to cover + * removal/disabling of this BSS. */ + hostapd_refresh_all_iface_beacons(hapd->iface); break; case EVENT_DFS_RADAR_DETECTED: if (!data) @@ -2684,32 +2889,32 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hapd = switch_link_hapd(hapd, data->dfs_event.link_id); hostapd_event_dfs_cac_started(hapd, &data->dfs_event); break; + case EVENT_INCUMBT_SIG_INTF_DETECTED: + if (!data) + break; + hapd = switch_link_hapd(hapd, + data->incumbt_sig_intf_event.link_id); + hostapd_event_incumbt_sig_intf_detected( + hapd, &data->incumbt_sig_intf_event); + break; #endif /* NEED_AP_MLME */ case EVENT_INTERFACE_ENABLED: - wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED); - if (hapd->disabled && hapd->started) { - hapd->disabled = 0; - /* - * Try to re-enable interface if the driver stopped it - * when the interface got disabled. - */ - if (hapd->wpa_auth) - wpa_auth_reconfig_group_keys(hapd->wpa_auth); - else - hostapd_reconfig_encryption(hapd); - hapd->reenable_beacon = 1; - ieee802_11_set_beacon(hapd); -#ifdef NEED_AP_MLME - } else if (hapd->disabled && hapd->iface->cac_started) { - wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); - hostapd_handle_dfs(hapd->iface); -#endif /* NEED_AP_MLME */ +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + hostapd_mld_iface_enable(hapd); + break; } +#endif /* CONFIG_IEEE80211BE */ + hostapd_iface_enable(hapd); break; case EVENT_INTERFACE_DISABLED: - hostapd_free_stas(hapd); - wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED); - hapd->disabled = 1; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + hostapd_mld_iface_disable(hapd); + break; + } +#endif /* CONFIG_IEEE80211BE */ + hostapd_iface_disable(hapd); break; #ifdef CONFIG_ACS case EVENT_ACS_CHANNEL_SELECTED: @@ -2762,6 +2967,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_event_color_change(hapd, true); break; #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_IEEE80211BE + case EVENT_MLD_INTERFACE_FREED: + wpa_printf(MSG_DEBUG, "MLD: Interface %s freed", + hapd->conf->iface); + hostapd_mld_interface_freed(hapd); + break; +#endif /* CONFIG_IEEE80211BE */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/contrib/wpa/src/ap/eap_user_db.c b/contrib/wpa/src/ap/eap_user_db.c index a510ee3e29fd..c0e903040fbe 100644 --- a/contrib/wpa/src/ap/eap_user_db.c +++ b/contrib/wpa/src/ap/eap_user_db.c @@ -89,8 +89,6 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) user->next = (void *) 1; } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { set_user_methods(user, argv[i]); - } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { - user->remediation = strlen(argv[i]) > 0; } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { user->t_c_timestamp = strtol(argv[i], NULL, 10); } diff --git a/contrib/wpa/src/ap/gas_query_ap.c b/contrib/wpa/src/ap/gas_query_ap.c index a471c793c413..5629e20909e3 100644 --- a/contrib/wpa/src/ap/gas_query_ap.c +++ b/contrib/wpa/src/ap/gas_query_ap.c @@ -536,16 +536,24 @@ int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, } if (action == WLAN_PA_GAS_COMEBACK_RESP) { - if (pos + 1 > data + len) + if (pos + 1 > data + len) { + wpa_printf(MSG_DEBUG, + "GAS: No room for frag_id in the response from " + MACSTR, MAC2STR(sa)); return 0; + } frag_id = *pos & 0x7f; more_frags = (*pos & 0x80) >> 7; pos++; } /* Comeback Delay */ - if (pos + 2 > data + len) + if (pos + 2 > data + len) { + wpa_printf(MSG_DEBUG, + "GAS: No room for Comeback Delay in the response from " + MACSTR, MAC2STR(sa)); return 0; + } comeback_delay = WPA_GET_LE16(pos); if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY) comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY; diff --git a/contrib/wpa/src/ap/gas_serv.c b/contrib/wpa/src/ap/gas_serv.c index 4642e49273d8..afb1b1186f6b 100644 --- a/contrib/wpa/src/ap/gas_serv.c +++ b/contrib/wpa/src/ap/gas_serv.c @@ -159,9 +159,14 @@ static void gas_serv_free_dialogs(struct hostapd_data *hapd, #ifdef CONFIG_HS20 static void anqp_add_hs_capab_list(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **_buf) { u8 *len; + struct wpabuf *buf; + + if (wpabuf_resize(_buf, 2 + 2 + 4 + 1 + 1 + 6) < 0) + return; + buf = *_buf; len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); @@ -179,14 +184,6 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); if (hapd->conf->hs20_operating_class) wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); - if (hapd->conf->hs20_osu_providers_count) - wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); - if (hapd->conf->hs20_osu_providers_nai_count) - wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); - if (hapd->conf->hs20_icons_count) - wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); - if (hapd->conf->hs20_operator_icon_count) - wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ @@ -207,7 +204,7 @@ static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, } -static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, +static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf **buf, u16 infoid) { struct anqp_element *elem; @@ -215,19 +212,16 @@ static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, elem = get_anqp_elem(hapd, infoid); if (!elem) return; - if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { - wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", - infoid); + if (wpabuf_resize(buf, 2 + 2 + wpabuf_len(elem->payload)) < 0) return; - } - wpabuf_put_le16(buf, infoid); - wpabuf_put_le16(buf, wpabuf_len(elem->payload)); - wpabuf_put_buf(buf, elem->payload); + wpabuf_put_le16(*buf, infoid); + wpabuf_put_le16(*buf, wpabuf_len(elem->payload)); + wpabuf_put_buf(*buf, elem->payload); } -static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, +static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf **buf, u16 infoid) { if (get_anqp_elem(hapd, infoid)) { @@ -240,14 +234,16 @@ static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, static void anqp_add_capab_list(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **_buf) { u8 *len; u16 id; + struct wpabuf *buf; - if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) + if (anqp_add_override(hapd, _buf, ANQP_CAPABILITY_LIST)) return; + buf = *_buf; len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) @@ -303,36 +299,54 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, wpabuf_put_le16(buf, id); } #ifdef CONFIG_HS20 - anqp_add_hs_capab_list(hapd, buf); + anqp_add_hs_capab_list(hapd, &buf); #endif /* CONFIG_HS20 */ gas_anqp_set_element_len(buf, len); } -static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) +static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) return; if (hapd->conf->venue_name) { u8 *len; - unsigned int i; - len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); - wpabuf_put_u8(buf, hapd->conf->venue_group); - wpabuf_put_u8(buf, hapd->conf->venue_type); + unsigned int i, count = 0; + size_t tlen = 2 + 2 + 1 + 1; + for (i = 0; i < hapd->conf->venue_name_count; i++) { struct hostapd_lang_string *vn; + vn = &hapd->conf->venue_name[i]; - wpabuf_put_u8(buf, 3 + vn->name_len); - wpabuf_put_data(buf, vn->lang, 3); - wpabuf_put_data(buf, vn->name, vn->name_len); + if (tlen + 1 + 3 + vn->name_len > 2 + 2 + 65535) { + wpa_printf(MSG_INFO, + "ANQP: Too many venue names configured to fit into an ANQP-element"); + break; + } + tlen += 1 + 3 + vn->name_len; + count++; } - gas_anqp_set_element_len(buf, len); + if (wpabuf_resize(buf, tlen) < 0) + return; + + len = gas_anqp_add_element(*buf, ANQP_VENUE_NAME); + wpabuf_put_u8(*buf, hapd->conf->venue_group); + wpabuf_put_u8(*buf, hapd->conf->venue_type); + for (i = 0; i < count; i++) { + struct hostapd_lang_string *vn; + + vn = &hapd->conf->venue_name[i]; + wpabuf_put_u8(*buf, 3 + vn->name_len); + wpabuf_put_data(*buf, vn->lang, 3); + wpabuf_put_data(*buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(*buf, len); } } -static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) +static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) return; @@ -340,70 +354,118 @@ static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) if (hapd->conf->venue_url) { u8 *len; unsigned int i; + size_t tlen = 2 + 2; - len = gas_anqp_add_element(buf, ANQP_VENUE_URL); for (i = 0; i < hapd->conf->venue_url_count; i++) { struct hostapd_venue_url *url; url = &hapd->conf->venue_url[i]; - wpabuf_put_u8(buf, 1 + url->url_len); - wpabuf_put_u8(buf, url->venue_number); - wpabuf_put_data(buf, url->url, url->url_len); + tlen += 1 + 1 + url->url_len; } - gas_anqp_set_element_len(buf, len); + if (wpabuf_resize(buf, tlen) < 0) + return; + + len = gas_anqp_add_element(*buf, ANQP_VENUE_URL); + for (i = 0; i < hapd->conf->venue_url_count; i++) { + struct hostapd_venue_url *url; + + url = &hapd->conf->venue_url[i]; + wpabuf_put_u8(*buf, 1 + url->url_len); + wpabuf_put_u8(*buf, url->venue_number); + wpabuf_put_data(*buf, url->url, url->url_len); + } + gas_anqp_set_element_len(*buf, len); } } static void anqp_add_network_auth_type(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) return; if (hapd->conf->network_auth_type) { - wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); - wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); - wpabuf_put_data(buf, hapd->conf->network_auth_type, + if (wpabuf_resize(buf, 2 + 2 + + hapd->conf->network_auth_type_len) < 0) + return; + wpabuf_put_le16(*buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(*buf, hapd->conf->network_auth_type_len); + wpabuf_put_data(*buf, hapd->conf->network_auth_type, hapd->conf->network_auth_type_len); } } static void anqp_add_roaming_consortium(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { - unsigned int i; + unsigned int i, count = 0; u8 *len; + size_t tlen = 2 + 2; if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) return; - len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { struct hostapd_roaming_consortium *rc; + rc = &hapd->conf->roaming_consortium[i]; - wpabuf_put_u8(buf, rc->len); - wpabuf_put_data(buf, rc->oi, rc->len); + if (tlen + 1 + rc->len > 2 + 2 + 65535) { + wpa_printf(MSG_INFO, + "ANQP: Too many roaming consortium OIs configured to fit into an ANQP-element"); + break; + } + tlen += 1 + rc->len; + count++; } - gas_anqp_set_element_len(buf, len); + + if (wpabuf_resize(buf, tlen) < 0) + return; + + len = gas_anqp_add_element(*buf, ANQP_ROAMING_CONSORTIUM); + for (i = 0; i < count; i++) { + struct hostapd_roaming_consortium *rc; + + rc = &hapd->conf->roaming_consortium[i]; + wpabuf_put_u8(*buf, rc->len); + wpabuf_put_data(*buf, rc->oi, rc->len); + } + gas_anqp_set_element_len(*buf, len); } static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) return; if (hapd->conf->ipaddr_type_configured) { - wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); - wpabuf_put_le16(buf, 1); - wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); + if (wpabuf_resize(buf, 2 + 2 + 1) < 0) + return; + wpabuf_put_le16(*buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(*buf, 1); + wpabuf_put_u8(*buf, hapd->conf->ipaddr_type_availability); } } +static size_t anqp_len_nai_realm_eap(struct hostapd_nai_realm_data *realm) +{ + unsigned int i; + size_t len = 1; + + for (i = 0; i < realm->eap_method_count; i++) { + struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; + + len += 1 + 1 + 1 + 3 * eap->num_auths; + } + + return len; +} + + static void anqp_add_nai_realm_eap(struct wpabuf *buf, struct hostapd_nai_realm_data *realm) { @@ -443,7 +505,7 @@ static void anqp_add_nai_realm_data(struct wpabuf *buf, static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, - struct wpabuf *buf, + struct wpabuf **buf, const u8 *home_realm, size_t home_realm_len) { @@ -455,6 +517,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, unsigned int realm_data_idx; unsigned int realm_idx; } matches[10]; + size_t tlen = 2 + 2 + 2; pos = home_realm; end = pos + home_realm_len; @@ -499,12 +562,17 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, for (k = 0; k < MAX_NAI_REALMS && realm->realm[k] && num_matching < 10; k++) { - if ((int) os_strlen(realm->realm[k]) != - rend - rpos || + size_t rlen; + + rlen = os_strlen(realm->realm[k]); + if ((int) rlen != rend - rpos || os_strncmp((char *) rpos, realm->realm[k], rend - rpos) != 0) continue; + tlen += 2 + 1 + 1 + rlen + + anqp_len_nai_realm_eap(realm); + matches[num_matching].realm_data_idx = j; matches[num_matching].realm_idx = k; @@ -516,8 +584,11 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, pos += realm_len; } - realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); - wpabuf_put_le16(buf, num_matching); + if (wpabuf_resize(buf, tlen) < 0) + return -1; + + realm_list_len = gas_anqp_add_element(*buf, ANQP_NAI_REALM); + wpabuf_put_le16(*buf, num_matching); /* * There are two ways to format. 1. each realm in a NAI Realm Data unit @@ -529,14 +600,14 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", matches[i].realm_data_idx, matches[i].realm_idx); realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; - anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); + anqp_add_nai_realm_data(*buf, realm, matches[i].realm_idx); } - gas_anqp_set_element_len(buf, realm_list_len); + gas_anqp_set_element_len(*buf, realm_list_len); return 0; } -static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, +static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf **buf, const u8 *home_realm, size_t home_realm_len, int nai_realm, int nai_home_realm) { @@ -546,27 +617,52 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, if (nai_realm && hapd->conf->nai_realm_data) { u8 *len; - unsigned int i, j; - len = gas_anqp_add_element(buf, ANQP_NAI_REALM); - wpabuf_put_le16(buf, hapd->conf->nai_realm_count); + unsigned int i, j, count = 0; + size_t tlen = 2 + 2 + 2; + for (i = 0; i < hapd->conf->nai_realm_count; i++) { + struct hostapd_nai_realm_data *realm; + size_t prev_tlen = tlen; + + realm = &hapd->conf->nai_realm_data[i]; + tlen += 2 + 1 + 1; + for (j = 0; realm->realm[j]; j++) { + if (j > 0) + tlen++; + tlen += os_strlen(realm->realm[j]); + } + tlen += anqp_len_nai_realm_eap(realm); + if (tlen > 65535) { + tlen = prev_tlen; + wpa_printf(MSG_INFO, + "ANQP: Too many NAI realms configured to fit into an ANQP-element"); + break; + } + count++; + } + if (wpabuf_resize(buf, tlen) < 0) + return; + + len = gas_anqp_add_element(*buf, ANQP_NAI_REALM); + wpabuf_put_le16(*buf, count); + for (i = 0; i < count; i++) { u8 *realm_data_len, *realm_len; struct hostapd_nai_realm_data *realm; realm = &hapd->conf->nai_realm_data[i]; - realm_data_len = wpabuf_put(buf, 2); - wpabuf_put_u8(buf, realm->encoding); - realm_len = wpabuf_put(buf, 1); + realm_data_len = wpabuf_put(*buf, 2); + wpabuf_put_u8(*buf, realm->encoding); + realm_len = wpabuf_put(*buf, 1); for (j = 0; realm->realm[j]; j++) { if (j > 0) - wpabuf_put_u8(buf, ';'); - wpabuf_put_str(buf, realm->realm[j]); + wpabuf_put_u8(*buf, ';'); + wpabuf_put_str(*buf, realm->realm[j]); } - *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; - anqp_add_nai_realm_eap(buf, realm); - gas_anqp_set_element_len(buf, realm_data_len); + *realm_len = (u8 *) wpabuf_put(*buf, 0) - realm_len - 1; + anqp_add_nai_realm_eap(*buf, realm); + gas_anqp_set_element_len(*buf, realm_data_len); } - gas_anqp_set_element_len(buf, len); + gas_anqp_set_element_len(*buf, len); } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) { hs20_add_nai_home_realm_matches(hapd, buf, home_realm, home_realm_len); @@ -575,30 +671,35 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) return; if (hapd->conf->anqp_3gpp_cell_net) { - wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); - wpabuf_put_le16(buf, + if (wpabuf_resize(buf, 2 + 2 + + hapd->conf->anqp_3gpp_cell_net_len) < 0) + return; + wpabuf_put_le16(*buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(*buf, hapd->conf->anqp_3gpp_cell_net_len); - wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, + wpabuf_put_data(*buf, hapd->conf->anqp_3gpp_cell_net, hapd->conf->anqp_3gpp_cell_net_len); } } -static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) +static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf **buf) { if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) return; if (hapd->conf->domain_name) { - wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); - wpabuf_put_le16(buf, hapd->conf->domain_name_len); - wpabuf_put_data(buf, hapd->conf->domain_name, + if (wpabuf_resize(buf, 2 + 2 + hapd->conf->domain_name_len) < 0) + return; + wpabuf_put_le16(*buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(*buf, hapd->conf->domain_name_len); + wpabuf_put_data(*buf, hapd->conf->domain_name, hapd->conf->domain_name_len); } } @@ -606,7 +707,7 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) #ifdef CONFIG_FILS static void anqp_add_fils_realm_info(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { size_t count; @@ -619,14 +720,16 @@ static void anqp_add_fils_realm_info(struct hostapd_data *hapd, if (count) { struct fils_realm *realm; - wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); - wpabuf_put_le16(buf, 2 * count); + if (wpabuf_resize(buf, 2 + 2 + 2 * count) < 0) + return; + wpabuf_put_le16(*buf, ANQP_FILS_REALM_INFO); + wpabuf_put_le16(*buf, 2 * count); dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, list) { if (count == 0) break; - wpabuf_put_data(buf, realm->hash, 2); + wpabuf_put_data(*buf, realm->hash, 2); count--; } } @@ -637,11 +740,26 @@ static void anqp_add_fils_realm_info(struct hostapd_data *hapd, #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **_buf) { if (hapd->conf->hs20_oper_friendly_name) { u8 *len; unsigned int i; + size_t tlen = 2 + 2 + 4 + 1 + 1; + struct wpabuf *buf; + + for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) + { + struct hostapd_lang_string *vn; + + vn = &hapd->conf->hs20_oper_friendly_name[i]; + tlen += 1 + 3 + vn->name_len; + } + + if (wpabuf_resize(_buf, tlen) < 0) + return; + buf = *_buf; + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); @@ -661,291 +779,84 @@ static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, static void anqp_add_wan_metrics(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (hapd->conf->hs20_wan_metrics) { - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); - wpabuf_put_u8(buf, 0); /* Reserved */ - wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); - gas_anqp_set_element_len(buf, len); + u8 *len; + + if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 + 13) < 0) + return; + len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(*buf, OUI_WFA); + wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(*buf, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(*buf, 0); /* Reserved */ + wpabuf_put_data(*buf, hapd->conf->hs20_wan_metrics, 13); + gas_anqp_set_element_len(*buf, len); } } static void anqp_add_connection_capability(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (hapd->conf->hs20_connection_capability) { - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); - wpabuf_put_u8(buf, 0); /* Reserved */ - wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, + u8 *len; + + if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 + + hapd->conf->hs20_connection_capability_len) < + 0) + return; + len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(*buf, OUI_WFA); + wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(*buf, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(*buf, 0); /* Reserved */ + wpabuf_put_data(*buf, hapd->conf->hs20_connection_capability, hapd->conf->hs20_connection_capability_len); - gas_anqp_set_element_len(buf, len); + gas_anqp_set_element_len(*buf, len); } } static void anqp_add_operating_class(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (hapd->conf->hs20_operating_class) { - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); - wpabuf_put_u8(buf, 0); /* Reserved */ - wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + u8 *len; + + if (wpabuf_resize(buf, 2 + 2 + 4 + 1 + 1 + + hapd->conf->hs20_operating_class_len) < 0) + return; + len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(*buf, OUI_WFA); + wpabuf_put_u8(*buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(*buf, HS20_STYPE_OPERATING_CLASS); + wpabuf_put_u8(*buf, 0); /* Reserved */ + wpabuf_put_data(*buf, hapd->conf->hs20_operating_class, hapd->conf->hs20_operating_class_len); - gas_anqp_set_element_len(buf, len); + gas_anqp_set_element_len(*buf, len); } } - -static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, - const char *name) -{ - size_t j; - struct hs20_icon *icon = NULL; - - for (j = 0; j < bss->hs20_icons_count && !icon; j++) { - if (os_strcmp(name, bss->hs20_icons[j].name) == 0) - icon = &bss->hs20_icons[j]; - } - if (!icon) - return; /* icon info not found */ - - wpabuf_put_le16(buf, icon->width); - wpabuf_put_le16(buf, icon->height); - wpabuf_put_data(buf, icon->language, 3); - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_u8(buf, os_strlen(icon->name)); - wpabuf_put_str(buf, icon->name); -} - - -static void anqp_add_osu_provider(struct wpabuf *buf, - struct hostapd_bss_config *bss, - struct hs20_osu_provider *p) -{ - u8 *len, *len2, *count; - unsigned int i; - - len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */ - - /* OSU Friendly Name Duples */ - len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->friendly_name_count; i++) { - struct hostapd_lang_string *s = &p->friendly_name[i]; - wpabuf_put_u8(buf, 3 + s->name_len); - wpabuf_put_data(buf, s->lang, 3); - wpabuf_put_data(buf, s->name, s->name_len); - } - WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); - - /* OSU Server URI */ - if (p->server_uri) { - wpabuf_put_u8(buf, os_strlen(p->server_uri)); - wpabuf_put_str(buf, p->server_uri); - } else - wpabuf_put_u8(buf, 0); - - /* OSU Method List */ - count = wpabuf_put(buf, 1); - for (i = 0; p->method_list && p->method_list[i] >= 0; i++) - wpabuf_put_u8(buf, p->method_list[i]); - *count = i; - - /* Icons Available */ - len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->icons_count; i++) - anqp_add_icon(buf, bss, p->icons[i]); - WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); - - /* OSU_NAI */ - if (p->osu_nai) { - wpabuf_put_u8(buf, os_strlen(p->osu_nai)); - wpabuf_put_str(buf, p->osu_nai); - } else - wpabuf_put_u8(buf, 0); - - /* OSU Service Description Duples */ - len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->service_desc_count; i++) { - struct hostapd_lang_string *s = &p->service_desc[i]; - wpabuf_put_u8(buf, 3 + s->name_len); - wpabuf_put_data(buf, s->lang, 3); - wpabuf_put_data(buf, s->name, s->name_len); - } - WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); - - WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); -} - - -static void anqp_add_osu_providers_list(struct hostapd_data *hapd, - struct wpabuf *buf) -{ - if (hapd->conf->hs20_osu_providers_count) { - size_t i; - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); - wpabuf_put_u8(buf, 0); /* Reserved */ - - /* OSU SSID */ - wpabuf_put_u8(buf, hapd->conf->osu_ssid_len); - wpabuf_put_data(buf, hapd->conf->osu_ssid, - hapd->conf->osu_ssid_len); - - /* Number of OSU Providers */ - wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count); - - for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { - anqp_add_osu_provider( - buf, hapd->conf, - &hapd->conf->hs20_osu_providers[i]); - } - - gas_anqp_set_element_len(buf, len); - } -} - - -static void anqp_add_osu_provider_nai(struct wpabuf *buf, - struct hs20_osu_provider *p) -{ - /* OSU_NAI for shared BSS (Single SSID) */ - if (p->osu_nai2) { - wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); - wpabuf_put_str(buf, p->osu_nai2); - } else { - wpabuf_put_u8(buf, 0); - } -} - - -static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, - struct wpabuf *buf) -{ - if (hapd->conf->hs20_osu_providers_nai_count) { - size_t i; - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); - wpabuf_put_u8(buf, 0); /* Reserved */ - - for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { - anqp_add_osu_provider_nai( - buf, &hapd->conf->hs20_osu_providers[i]); - } - - gas_anqp_set_element_len(buf, len); - } -} - - -static void anqp_add_icon_binary_file(struct hostapd_data *hapd, - struct wpabuf *buf, - const u8 *name, size_t name_len) -{ - struct hs20_icon *icon; - size_t i; - u8 *len; - - wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename", - name, name_len); - for (i = 0; i < hapd->conf->hs20_icons_count; i++) { - icon = &hapd->conf->hs20_icons[i]; - if (name_len == os_strlen(icon->name) && - os_memcmp(name, icon->name, name_len) == 0) - break; - } - - if (i < hapd->conf->hs20_icons_count) - icon = &hapd->conf->hs20_icons[i]; - else - icon = NULL; - - len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE); - wpabuf_put_u8(buf, 0); /* Reserved */ - - if (icon) { - char *data; - size_t data_len; - - data = os_readfile(icon->file, &data_len); - if (data == NULL || data_len > 65535) { - wpabuf_put_u8(buf, 2); /* Download Status: - * Unspecified file error */ - wpabuf_put_u8(buf, 0); - wpabuf_put_le16(buf, 0); - } else { - wpabuf_put_u8(buf, 0); /* Download Status: Success */ - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_le16(buf, data_len); - wpabuf_put_data(buf, data, data_len); - } - os_free(data); - } else { - wpabuf_put_u8(buf, 1); /* Download Status: File not found */ - wpabuf_put_u8(buf, 0); - wpabuf_put_le16(buf, 0); - } - - gas_anqp_set_element_len(buf, len); -} - - -static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, - struct wpabuf *buf) -{ - struct hostapd_bss_config *bss = hapd->conf; - size_t i; - u8 *len; - - if (!bss->hs20_operator_icon_count) - return; - - len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); - wpabuf_put_u8(buf, 0); /* Reserved */ - - for (i = 0; i < bss->hs20_operator_icon_count; i++) - anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); - - gas_anqp_set_element_len(buf, len); -} - #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, - struct wpabuf *buf) + struct wpabuf **buf) { if (hapd->conf->mbo_cell_data_conn_pref >= 0) { - u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); - wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); - wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); - gas_anqp_set_element_len(buf, len); + u8 *len; + + if (wpabuf_resize(buf, 2 + 4 + 1 + 1) < 0) + return; + len = gas_anqp_add_element(*buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(*buf, OUI_WFA); + wpabuf_put_u8(*buf, MBO_ANQP_OUI_TYPE); + wpabuf_put_u8(*buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(*buf, hapd->conf->mbo_cell_data_conn_pref); + gas_anqp_set_element_len(*buf, len); } } #endif /* CONFIG_MBO */ @@ -973,19 +884,17 @@ static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, const u8 *home_realm, size_t home_realm_len, - const u8 *icon_name, size_t icon_name_len, const u16 *extra_req, unsigned int num_extra_req) { struct wpabuf *buf; size_t len; unsigned int i; + size_t prev_len, max_len = 128 * hapd->conf->gas_frag_limit; len = 1400; if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) len += 1000; - if (request & ANQP_REQ_ICON_REQUEST) - len += 65536; #ifdef CONFIG_FILS if (request & ANQP_FILS_REALM_INFO) len += 2 * dl_list_len(&hapd->conf->fils_realms); @@ -997,76 +906,238 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, return NULL; if (request & ANQP_REQ_CAPABILITY_LIST) - anqp_add_capab_list(hapd, buf); - if (request & ANQP_REQ_VENUE_NAME) - anqp_add_venue_name(hapd, buf); - if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) - anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); - if (request & ANQP_REQ_NETWORK_AUTH_TYPE) - anqp_add_network_auth_type(hapd, buf); - if (request & ANQP_REQ_ROAMING_CONSORTIUM) - anqp_add_roaming_consortium(hapd, buf); - if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) - anqp_add_ip_addr_type_availability(hapd, buf); - if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) - anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, + anqp_add_capab_list(hapd, &buf); + + if (request & ANQP_REQ_VENUE_NAME) { + prev_len = wpabuf_len(buf); + anqp_add_venue_name(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_CALL_NUMBER); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + if (request & ANQP_REQ_NETWORK_AUTH_TYPE) { + prev_len = wpabuf_len(buf); + anqp_add_network_auth_type(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + if (request & ANQP_REQ_ROAMING_CONSORTIUM) { + prev_len = wpabuf_len(buf); + anqp_add_roaming_consortium(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) { + prev_len = wpabuf_len(buf); + anqp_add_ip_addr_type_availability(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) { + prev_len = wpabuf_len(buf); + anqp_add_nai_realm(hapd, &buf, home_realm, home_realm_len, request & ANQP_REQ_NAI_REALM, request & ANQP_REQ_NAI_HOME_REALM); - if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) - anqp_add_3gpp_cellular_network(hapd, buf); - if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) - anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); - if (request & ANQP_REQ_AP_CIVIC_LOCATION) - anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); - if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) - anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); - if (request & ANQP_REQ_DOMAIN_NAME) - anqp_add_domain_name(hapd, buf); - if (request & ANQP_REQ_EMERGENCY_ALERT_URI) - anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); - if (request & ANQP_REQ_TDLS_CAPABILITY) - anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); - if (request & ANQP_REQ_EMERGENCY_NAI) - anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) { + prev_len = wpabuf_len(buf); + anqp_add_3gpp_cellular_network(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_AP_CIVIC_LOCATION) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_AP_CIVIC_LOCATION); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_AP_LOCATION_PUBLIC_URI); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_DOMAIN_NAME) { + prev_len = wpabuf_len(buf); + anqp_add_domain_name(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_EMERGENCY_ALERT_URI) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_ALERT_URI); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_TDLS_CAPABILITY) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_TDLS_CAPABILITY); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_EMERGENCY_NAI) { + prev_len = wpabuf_len(buf); + anqp_add_elem(hapd, &buf, ANQP_EMERGENCY_NAI); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } for (i = 0; i < num_extra_req; i++) { + prev_len = wpabuf_len(buf); #ifdef CONFIG_FILS if (extra_req[i] == ANQP_FILS_REALM_INFO) { - anqp_add_fils_realm_info(hapd, buf); + anqp_add_fils_realm_info(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } continue; } #endif /* CONFIG_FILS */ if (extra_req[i] == ANQP_VENUE_URL) { - anqp_add_venue_url(hapd, buf); + anqp_add_venue_url(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } continue; } - anqp_add_elem(hapd, buf, extra_req[i]); + anqp_add_elem(hapd, &buf, extra_req[i]); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } } #ifdef CONFIG_HS20 - if (request & ANQP_REQ_HS_CAPABILITY_LIST) - anqp_add_hs_capab_list(hapd, buf); - if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) - anqp_add_operator_friendly_name(hapd, buf); - if (request & ANQP_REQ_WAN_METRICS) - anqp_add_wan_metrics(hapd, buf); - if (request & ANQP_REQ_CONNECTION_CAPABILITY) - anqp_add_connection_capability(hapd, buf); - if (request & ANQP_REQ_OPERATING_CLASS) - anqp_add_operating_class(hapd, buf); - if (request & ANQP_REQ_OSU_PROVIDERS_LIST) - anqp_add_osu_providers_list(hapd, buf); - if (request & ANQP_REQ_ICON_REQUEST) - anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); - if (request & ANQP_REQ_OPERATOR_ICON_METADATA) - anqp_add_operator_icon_metadata(hapd, buf); - if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) - anqp_add_osu_providers_nai_list(hapd, buf); + if (request & ANQP_REQ_HS_CAPABILITY_LIST) { + prev_len = wpabuf_len(buf); + anqp_add_hs_capab_list(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) { + prev_len = wpabuf_len(buf); + anqp_add_operator_friendly_name(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_WAN_METRICS) { + prev_len = wpabuf_len(buf); + anqp_add_wan_metrics(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_CONNECTION_CAPABILITY) { + prev_len = wpabuf_len(buf); + anqp_add_connection_capability(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } + + if (request & ANQP_REQ_OPERATING_CLASS) { + prev_len = wpabuf_len(buf); + anqp_add_operating_class(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) - anqp_add_mbo_cell_data_conn_pref(hapd, buf); + if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) { + prev_len = wpabuf_len(buf); + anqp_add_mbo_cell_data_conn_pref(hapd, &buf); + if (wpabuf_len(buf) > max_len) { + wpa_printf(MSG_INFO, + "ANQP: ANQP-element would go beyond maximum GAS response length - skip"); + buf->used = prev_len; + } + } #endif /* CONFIG_MBO */ return buf; @@ -1079,8 +1150,6 @@ struct anqp_query_info { unsigned int request; const u8 *home_realm_query; size_t home_realm_query_len; - const u8 *icon_name; - size_t icon_name_len; int p2p_sd; u16 extra_req[ANQP_MAX_EXTRA_REQ]; unsigned int num_extra_req; @@ -1245,20 +1314,6 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", hapd->conf->hs20_operating_class != NULL, qi); break; - case HS20_STYPE_OSU_PROVIDERS_LIST: - set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", - hapd->conf->hs20_osu_providers_count, qi); - break; - case HS20_STYPE_OPERATOR_ICON_METADATA: - set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, - "Operator Icon Metadata", - hapd->conf->hs20_operator_icon_count, qi); - break; - case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: - set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, - "OSU Providers NAI List", - hapd->conf->hs20_osu_providers_nai_count, qi); - break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); @@ -1284,23 +1339,6 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, } -static void rx_anqp_hs_icon_request(struct hostapd_data *hapd, - const u8 *pos, const u8 *end, - struct anqp_query_info *qi) -{ - qi->request |= ANQP_REQ_ICON_REQUEST; - qi->icon_name = pos; - qi->icon_name_len = end - pos; - if (hapd->conf->hs20_icons_count) { - wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query " - "(local)"); - } else { - wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not " - "available"); - } -} - - static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, const u8 *pos, const u8 *end, struct anqp_query_info *qi) @@ -1323,9 +1361,6 @@ static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, case HS20_STYPE_NAI_HOME_REALM_QUERY: rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); break; - case HS20_STYPE_ICON_REQUEST: - rx_anqp_hs_icon_request(hapd, pos, end, qi); - break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " "%u", subtype); @@ -1455,7 +1490,6 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, qi->home_realm_query_len, - qi->icon_name, qi->icon_name_len, qi->extra_req, qi->num_extra_req); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); @@ -1474,14 +1508,28 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; + unsigned int num_frag; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay = hapd->conf->gas_comeback_delay; } - wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " - "initial response - use GAS comeback"); + num_frag = wpabuf_len(buf) / hapd->conf->gas_frag_limit; + wpa_printf(MSG_DEBUG, + "ANQP: Too long response (%zu bytes) to fit in initial response (frag_limit %zu bytes) - use GAS comeback (%u fragments)", + wpabuf_len(buf), hapd->conf->gas_frag_limit, + num_frag); + if (num_frag > 128) { + wpa_printf(MSG_INFO, + "ANQP: Too long response (%zu bytes) to fit into GAS response", + wpabuf_len(buf)); + wpabuf_free(buf); + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, NULL); + goto done; + } di = gas_dialog_create(hapd, sa, dialog_token); if (!di) { wpa_printf(MSG_INFO, "ANQP: Could not create dialog " @@ -1505,6 +1553,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); } +done: if (!tx_buf) return; if (prot) diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c index 6ecf6ca7834e..c327ee88c5ae 100644 --- a/contrib/wpa/src/ap/hostapd.c +++ b/contrib/wpa/src/ap/hostapd.c @@ -66,12 +66,9 @@ static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); #endif /* CONFIG_WEP */ static int setup_interface2(struct hostapd_iface *iface); static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces); static void hostapd_interface_setup_failure_handler(void *eloop_ctx, void *timeout_ctx); -#ifdef CONFIG_IEEE80211AX -static void hostapd_switch_color_timeout_handler(void *eloop_data, - void *user_ctx); -#endif /* CONFIG_IEEE80211AX */ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -162,7 +159,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd) else hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); - if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) { + if (hapd->conf->wpa && hapd->wpa_auth == NULL) { hostapd_setup_wpa(hapd); if (hapd->wpa_auth) wpa_init_keys(hapd->wpa_auth); @@ -186,6 +183,14 @@ static void hostapd_reload_bss(struct hostapd_data *hapd) hostapd_neighbor_sync_own_report(hapd); + if (hapd->iface->current_mode && + hostapd_prepare_rates(hapd, hapd->iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates table."); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + } + ieee802_11_set_beacon(hapd); hostapd_update_wps(hapd); @@ -242,12 +247,47 @@ static int hostapd_iface_conf_changed(struct hostapd_config *newconf, if (os_strcmp(newconf->bss[i]->iface, oldconf->bss[i]->iface) != 0) return 1; +#ifdef CONFIG_IEEE80211BE + if (newconf->bss[i]->mld_ap != oldconf->bss[i]->mld_ap) + return 1; +#endif /* CONFIG_IEEE80211BE */ } return 0; } +static int hostapd_remove_hapd_iface(struct hostapd_iface *hapd_iface) +{ + struct hapd_interfaces *interfaces; + size_t i; + + hapd_iface->driver_ap_teardown = + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + + interfaces = hapd_iface->interfaces; + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i] == hapd_iface) + break; + } + + if (i >= interfaces->count) + return -1; + + hostapd_interface_deinit_free(hapd_iface); + + while (i < interfaces->count - 1) { + interfaces->iface[i] = interfaces->iface[i + 1]; + i++; + } + interfaces->count--; + + hostapd_cleanup_unused_mlds(interfaces); + return 0; +} + + int hostapd_reload_config(struct hostapd_iface *iface) { struct hapd_interfaces *interfaces = iface->interfaces; @@ -284,7 +324,12 @@ int hostapd_reload_config(struct hostapd_iface *iface) hostapd_config_free(newconf); return -1; } - hostapd_remove_iface(interfaces, hapd->conf->iface); + if (hostapd_remove_hapd_iface(iface) != 0) { + os_free(fname); + hostapd_config_free(newconf); + return -1; + } + iface = hostapd_init(interfaces, fname); os_free(fname); hostapd_config_free(newconf); @@ -302,7 +347,6 @@ int hostapd_reload_config(struct hostapd_iface *iface) "Failed to enable interface on config reload"); return res; } - iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; @@ -330,6 +374,7 @@ int hostapd_reload_config(struct hostapd_iface *iface) hostapd_reload_bss(hapd); } + iface->conf = newconf; hostapd_config_free(oldconf); @@ -354,7 +399,7 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, ifname, i); } } - if (hapd->conf->ieee80211w) { + if (ap_pmf_enabled(hapd->conf)) { for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, 0, NULL, @@ -400,8 +445,6 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211BE #ifdef CONFIG_TESTING_OPTIONS -#define TU_TO_USEC(_val) ((_val) * 1024) - static void hostapd_link_remove_timeout_handler(void *eloop_data, void *user_ctx) { @@ -440,6 +483,8 @@ int hostapd_link_remove(struct hostapd_data *hapd, u32 count) hapd->eht_mld_link_removal_count = count; hapd->eht_mld_bss_param_change++; + if (hapd->eht_mld_bss_param_change == 255) + hapd->eht_mld_bss_param_change = 0; eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int), hostapd_link_remove_timeout_handler, @@ -459,6 +504,11 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) hapd->probereq_cb = NULL; hapd->num_probereq_cb = 0; + os_free(hapd->current_rates); + hapd->current_rates = NULL; + os_free(hapd->basic_rates); + hapd->basic_rates = NULL; + #ifdef CONFIG_P2P wpabuf_free(hapd->p2p_beacon_ie); hapd->p2p_beacon_ie = NULL; @@ -521,7 +571,10 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) authsrv_deinit(hapd); - if (hapd->interface_added) { + /* For single drv, first bss would have interface_added flag set. + * Don't remove interface now. Driver deinit part will take care + */ + if (hapd->interface_added && hapd->iface->bss[0] != hapd) { hapd->interface_added = 0; if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { wpa_printf(MSG_WARNING, @@ -551,9 +604,9 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) wpabuf_free(hapd->time_adv); hapd->time_adv = NULL; -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) gas_serv_deinit(hapd); -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ bss_load_update_deinit(hapd); ndisc_snoop_deinit(hapd); @@ -610,6 +663,21 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) } +#ifdef CONFIG_IEEE80211BE +/* hostapd_mld_move_vlan_list - Move the VLAN list to the new first bss + * + * This function is used to copy the reference to the VLAN list from the old + * first BSS to the new first BSS when the first BSS is removed. + */ +static void hostapd_mld_move_vlan_list(struct hostapd_data *old_fbss, + struct hostapd_data *new_fbss) +{ + new_fbss->conf->vlan = old_fbss->conf->vlan; + old_fbss->conf->vlan = NULL; + +} +#endif /* CONFIG_IEEE80211BE */ + /* hostapd_bss_link_deinit - Per-BSS ML cleanup (deinitialization) * @hapd: Pointer to BSS data * @@ -620,10 +688,20 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd) static void hostapd_bss_link_deinit(struct hostapd_data *hapd) { #ifdef CONFIG_IEEE80211BE + int i; + if (!hapd->conf || !hapd->conf->mld_ap) return; - if (!hapd->mld->num_links) + /* Free per STA profiles */ + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + os_free(hapd->partner_links[i].resp_sta_profile); + os_memset(&hapd->partner_links[i], 0, + sizeof(hapd->partner_links[i])); + } + + /* Put all freeing logic above this */ + if (!hapd->mld || !hapd->mld->num_links) return; /* If not started, not yet linked to the MLD. However, the first @@ -693,15 +771,16 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) acs_cleanup(iface); hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; + iface->num_hw_features = 0; iface->current_mode = NULL; - os_free(iface->current_rates); - iface->current_rates = NULL; - os_free(iface->basic_rates); - iface->basic_rates = NULL; iface->cac_started = 0; ap_list_deinit(iface); sta_track_deinit(iface); airtime_policy_update_deinit(iface); + hostapd_free_multi_hw_info(iface->multi_hw_info); + iface->multi_hw_info = NULL; + iface->num_multi_hws = 0; + iface->current_hw_info = NULL; } @@ -715,6 +794,7 @@ void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + hostapd_afc_stop(iface); eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, NULL); @@ -724,6 +804,12 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) os_free(iface->config_fname); os_free(iface->bss); +#ifdef CONFIG_AFC + wpabuf_free(iface->afc_request); + os_free(iface->afc_response); + os_free(iface->afc.chan_info_list); + os_free(iface->afc.freq_range); +#endif /* CONFIG_AFC */ wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface); os_free(iface); } @@ -751,12 +837,6 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) return 0; } - /* - * When IEEE 802.1X is not enabled, the driver may need to know how to - * set authentication algorithms for static WEP. - */ - hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs); - for (i = 0; i < 4; i++) { if (hapd->conf->ssid.wep.key[i] && hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, 0, @@ -1363,7 +1443,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd) /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data - * @first: Whether this BSS is the first BSS of an interface; -1 = not first, + * @first: Whether this BSS is the first BSS of an interface; false = not first, * but interface may exist * @start_beacon: Whether Beacon frame template should be configured and * transmission of Beaconf rames started at this time. This is used when @@ -1377,7 +1457,7 @@ static int hostapd_bss_radius_init(struct hostapd_data *hapd) * initialized. Most of the modules that are initialized here will be * deinitialized in hostapd_cleanup(). */ -static int hostapd_setup_bss(struct hostapd_data *hapd, int first, +static int hostapd_setup_bss(struct hostapd_data *hapd, bool first, bool start_beacon) { struct hostapd_bss_config *conf = hapd->conf; @@ -1394,6 +1474,16 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", __func__, hapd, conf->iface, first); + /* Prepare per-BSS rates early from BSS config and current mode */ + if (hapd->iface->current_mode && + hostapd_prepare_rates(hapd, hapd->iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates table."); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + #ifdef EAP_SERVER_TNC if (conf->tnc && tncs_global_init() < 0) { wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); @@ -1408,7 +1498,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, } hapd->started = 1; - if (!first || first == -1) { + if (!first) { u8 *addr = hapd->own_addr; if (!is_zero_ether_addr(conf->bssid)) { @@ -1440,7 +1530,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, if (h_hapd) { hapd->drv_priv = h_hapd->drv_priv; hapd->interface_added = h_hapd->interface_added; - hostapd_mld_add_link(hapd); wpa_printf(MSG_DEBUG, "Setup of non first link (%d) BSS of MLD %s", hapd->mld_link_id, hapd->conf->iface); @@ -1454,7 +1543,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, conf->iface, addr, hapd, &hapd->drv_priv, force_ifname, if_addr, conf->bridge[0] ? conf->bridge : NULL, - first == -1)) { + 1)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); hapd->interface_added = 0; @@ -1471,7 +1560,6 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first, hapd->mld_link_id, hapd->conf->iface); os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN); - hostapd_mld_add_link(hapd); } #endif /* CONFIG_IEEE80211BE */ } @@ -1486,8 +1574,13 @@ setup_mld: MAC2STR(hapd->own_addr)); if (hostapd_drv_link_add(hapd, hapd->mld_link_id, - hapd->own_addr)) + hapd->own_addr)) { + wpa_printf(MSG_ERROR, + "MLD: Failed to add link %d in MLD %s", + hapd->mld_link_id, hapd->conf->iface); return -1; + } + hostapd_mld_add_link(hapd); } #endif /* CONFIG_IEEE80211BE */ @@ -1546,7 +1639,7 @@ setup_mld: /* * Short SSID calculation is identical to FCS and it is defined in - * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID). + * IEEE Std 802.11-2024, 9.4.2.169.3 (Calculating the Short-SSID). */ conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid, conf->ssid.ssid_len); @@ -1646,8 +1739,11 @@ setup_mld: wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); return -1; } +#ifdef CONFIG_IEEE8021X_AUTH + hapd->send_eap_req = ieee80211_send_eap_req; +#endif /* CONFIG_IEEE8021X_AUTH */ - if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd)) + if (conf->wpa && hostapd_setup_wpa(hapd)) return -1; if (accounting_init(hapd)) { @@ -1655,12 +1751,12 @@ setup_mld: return -1; } -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) if (gas_serv_init(hapd)) { wpa_printf(MSG_ERROR, "GAS server initialization failed"); return -1; } -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ if (conf->qos_map_set_len && hostapd_drv_set_qos_map(hapd, conf->qos_map_set, @@ -1778,26 +1874,34 @@ static int hostapd_set_acl_list(struct hostapd_data *hapd, int hostapd_set_acl(struct hostapd_data *hapd) { - struct hostapd_config *conf = hapd->iconf; + struct hostapd_bss_config *conf = hapd->conf; int err = 0; u8 accept_acl; if (hapd->iface->drv_max_acl_mac_addrs == 0) return 0; - if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) { +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + wpa_printf(MSG_DEBUG, + "Kernel doesn't support offloaded ACL for AP MLD. Use hostapd based ACL instead."); + return 0; + } +#endif /* CONFIG_IEEE80211BE */ + + if (conf->macaddr_acl == DENY_UNLESS_ACCEPTED) { accept_acl = 1; - err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac, - conf->bss[0]->num_accept_mac, + err = hostapd_set_acl_list(hapd, conf->accept_mac, + conf->num_accept_mac, accept_acl); if (err) { wpa_printf(MSG_DEBUG, "Failed to set accept acl"); return -1; } - } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) { + } else if (conf->macaddr_acl == ACCEPT_UNLESS_DENIED) { accept_acl = 0; - err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac, - conf->bss[0]->num_deny_mac, + err = hostapd_set_acl_list(hapd, conf->deny_mac, + conf->num_deny_mac, accept_acl); if (err) { wpa_printf(MSG_DEBUG, "Failed to set deny acl"); @@ -1808,12 +1912,36 @@ int hostapd_set_acl(struct hostapd_data *hapd) } +static int hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd) +{ +#ifdef CONFIG_IEEE80211BE + int ret; + + if (hapd->conf->mld_ap) { + ret = os_snprintf(hapd->ctrl_sock_iface, + sizeof(hapd->ctrl_sock_iface), "%s_%s%d", + hapd->conf->iface, WPA_CTRL_IFACE_LINK_NAME, + hapd->mld_link_id); + if (os_snprintf_error(sizeof(hapd->ctrl_sock_iface), ret)) + return -1; + } else { + os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface, + sizeof(hapd->ctrl_sock_iface)); + } +#endif /* CONFIG_IEEE80211BE */ + return 0; +} + + static int start_ctrl_iface_bss(struct hostapd_data *hapd) { if (!hapd->iface->interfaces || !hapd->iface->interfaces->ctrl_iface_init) return 0; + if (hostapd_set_ctrl_sock_iface(hapd)) + return -1; + if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", @@ -1834,6 +1962,10 @@ static int start_ctrl_iface(struct hostapd_iface *iface) for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *hapd = iface->bss[i]; + + if (hostapd_set_ctrl_sock_iface(hapd)) + return -1; + if (iface->interfaces->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface for %s", @@ -1862,18 +1994,30 @@ static void hostapd_no_ir_cleanup(struct hostapd_data *bss) static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface, void *ctx) { + struct hostapd_data *hapd = iface->bss[0]; bool all_no_ir, is_6ghz; int i, j; struct hostapd_hw_modes *mode = NULL; + struct hostapd_hw_modes *hw_features; + u16 num_hw_features, flags; + u8 dfs_domain; - if (hostapd_get_hw_features(iface)) - return 0; + if (hostapd_drv_none(hapd)) + return -1; + + hw_features = hostapd_get_hw_feature_data(hapd, &num_hw_features, + &flags, &dfs_domain); + if (!hw_features) { + wpa_printf(MSG_DEBUG, + "Could not fetching hardware channel list"); + return -1; + } all_no_ir = true; is_6ghz = false; - for (i = 0; i < iface->num_hw_features; i++) { - mode = &iface->hw_features[i]; + for (i = 0; i < num_hw_features; i++) { + mode = &hw_features[i]; if (mode->mode == iface->conf->hw_mode) { if (iface->freq > 0 && @@ -1895,26 +2039,25 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface, } if (!mode || !is_6ghz) - return 0; - iface->current_mode = mode; + goto free_hw_features; if (iface->state == HAPD_IFACE_ENABLED) { if (!all_no_ir) { struct hostapd_channel_data *chan; - chan = hw_get_channel_freq(iface->current_mode->mode, + chan = hw_get_channel_freq(mode->mode, iface->freq, NULL, - iface->hw_features, - iface->num_hw_features); + hw_features, + num_hw_features); if (!chan) { wpa_printf(MSG_ERROR, "NO_IR: Could not derive chan from freq"); - return 0; + goto free_hw_features; } if (!(chan->flag & HOSTAPD_CHAN_NO_IR)) - return 0; + goto free_hw_features; wpa_printf(MSG_DEBUG, "NO_IR: The current channel has NO_IR flag now, stop AP."); } else { @@ -1931,20 +2074,20 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface, if (all_no_ir) { wpa_printf(MSG_DEBUG, "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore"); - return 0; + goto free_hw_features; } if (!iface->conf->acs) { struct hostapd_channel_data *chan; - chan = hw_get_channel_freq(iface->current_mode->mode, + chan = hw_get_channel_freq(mode->mode, iface->freq, NULL, - iface->hw_features, - iface->num_hw_features); + hw_features, + num_hw_features); if (!chan) { wpa_printf(MSG_ERROR, "NO_IR: Could not derive chan from freq"); - return 0; + goto free_hw_features; } /* If the last operating channel is NO_IR, trigger ACS. @@ -1955,13 +2098,15 @@ static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface, if (acs_init(iface) != HOSTAPD_CHAN_ACS) wpa_printf(MSG_ERROR, "NO_IR: Could not start ACS"); - return 0; + goto free_hw_features; } } setup_interface2(iface); } +free_hw_features: + hostapd_free_hw_features(hw_features, num_hw_features); return 0; } @@ -2507,9 +2652,19 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, hostapd_hw_mode_txt(iface->conf->hw_mode), iface->conf->channel, iface->freq); + if (hostapd_set_current_hw_info(iface, iface->freq)) { + wpa_printf(MSG_ERROR, + "Failed to set current hardware info"); + goto fail; + } + #ifdef NEED_AP_MLME - /* Handle DFS only if it is not offloaded to the driver */ - if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) { + if (iface->assisted_dfs) { + wpa_printf(MSG_DEBUG, + "Request to start AP with assisted DFS"); + } else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) { + /* Handle DFS only if it is not offloaded to the driver + */ /* Check DFS */ res = hostapd_handle_dfs(iface); if (res <= 0) { @@ -2546,6 +2701,16 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } #endif /* CONFIG_MESH */ +#ifdef CONFIG_IEEE80211AX + /* Check AFC for 6 GHz channels. */ + res = hostapd_afc_handle_request(iface); + if (res <= 0) { + if (res < 0) + goto fail; + return res; + } +#endif /* CONFIG_IEEE80211AX */ + if (!delay_apply_cfg && hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, @@ -2567,17 +2732,6 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } } - if (iface->current_mode) { - if (hostapd_prepare_rates(iface, iface->current_mode)) { - wpa_printf(MSG_ERROR, "Failed to prepare rates " - "table."); - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Failed to prepare rates table."); - goto fail; - } - } - if (hapd->iconf->rts_threshold >= -1 && hostapd_set_rts(hapd, hapd->iconf->rts_threshold) && hapd->iconf->rts_threshold >= -1) { @@ -2604,6 +2758,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, for (;;) { hapd = iface->bss[j]; hostapd_bss_deinit_no_free(hapd); + hostapd_bss_link_deinit(hapd); hostapd_free_hapd_data(hapd); if (j == 0) break; @@ -2622,6 +2777,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, for (;;) { hapd = iface->bss[j]; hostapd_bss_deinit_no_free(hapd); + hostapd_bss_link_deinit(hapd); hostapd_free_hapd_data(hapd); if (j == 0) break; @@ -2640,17 +2796,11 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, hostapd_set_acl(hapd); - if (hostapd_driver_commit(hapd) < 0) { - wpa_printf(MSG_ERROR, "%s: Failed to commit driver " - "configuration", __func__); - goto fail; - } - /* * WPS UPnP module can be initialized only when the "upnp_iface" is up. * If "interface" and "upnp_iface" are the same (e.g., non-bridge - * mode), the interface is up only after driver_commit, so initialize - * WPS after driver_commit. + * mode), the interface is up only after the driver interface has been + * initialized, so initialize WPS after that. */ for (j = 0; j < iface->num_bss; j++) { if (hostapd_init_wps_complete(iface->bss[j])) @@ -2658,7 +2808,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && - !res_dfs_offload) { + !res_dfs_offload && !iface->assisted_dfs) { /* * If freq is DFS, and DFS is offloaded to the driver, then wait * for CAC to complete. @@ -2686,6 +2836,11 @@ dfs_offload: } #endif /* CONFIG_FST */ +#ifdef CONFIG_AFC + if (iface->afc.data_valid) + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE); +#endif /* CONFIG_AFC */ + hostapd_set_state(iface, HAPD_IFACE_ENABLED); hostapd_owe_update_trans(iface); airtime_policy_update_init(iface); @@ -2709,7 +2864,7 @@ dfs_offload: hostapd_neighbor_set_own_report(iface->bss[j]); if (iface->interfaces && iface->interfaces->count > 1) - ieee802_11_set_beacons(iface); + ieee802_11_update_beacons(iface); return 0; @@ -2909,6 +3064,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, #ifdef CONFIG_SAE dl_list_init(&hapd->sae_commit_queue); #endif /* CONFIG_SAE */ + dl_list_init(&hapd->erp_keys); return hapd; } @@ -2944,6 +3100,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) hostapd_set_state(iface, HAPD_IFACE_DISABLED); + hostapd_afc_stop(iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; iface->is_no_ir = false; @@ -3045,18 +3202,20 @@ static void hostapd_bss_alloc_link_id(struct hostapd_data *hapd) #endif /* CONFIG_IEEE80211BE */ -static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, - struct hapd_interfaces *interfaces) +void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, + struct hapd_interfaces *interfaces) { #ifdef CONFIG_IEEE80211BE struct hostapd_mld *mld, **all_mld; struct hostapd_bss_config *conf; size_t i; + if (hapd->mld) + return; + conf = hapd->conf; - if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap || - conf->disable_11be) + if (!hapd->iconf || !conf->mld_ap || !hostapd_is_eht_enabled(hapd)) return; for (i = 0; i < interfaces->mld_count; i++) { @@ -3080,9 +3239,17 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, os_strlcpy(mld->name, conf->iface, sizeof(conf->iface)); dl_list_init(&mld->links); + mld->ctrl_sock = -1; + if (hapd->conf->ctrl_interface) + mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface); wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name); + /* Initialize MLD control interfaces early to allow external monitoring + * of link setup operations. */ + if (interfaces->mld_ctrl_iface_init(mld)) + goto fail; + hapd->mld = mld; hostapd_mld_ref_inc(mld); hostapd_bss_alloc_link_id(hapd); @@ -3101,6 +3268,7 @@ fail: if (!mld) return; + interfaces->mld_ctrl_iface_deinit(mld); wpa_printf(MSG_DEBUG, "AP MLD %s: free mld %p", mld->name, mld); os_free(mld); hapd->mld = NULL; @@ -3142,6 +3310,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces) if (!remove && !forced_remove) continue; + interfaces->mld_ctrl_iface_deinit(mld); + wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name, forced_remove ? " (forced)" : ""); os_free(mld); @@ -3285,6 +3455,7 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, { struct hostapd_iface *new_iface = NULL, *iface = NULL; struct hostapd_data *hapd; + struct hostapd_config *conf; int k; size_t i, bss_idx; @@ -3300,17 +3471,26 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s", config_fname, phy, iface ? "" : " --> new PHY"); + + conf = interfaces->config_read_cb(config_fname); + if (!conf) + return NULL; + +#ifdef CONFIG_IEEE80211BE + /* AP MLD can be enabled with the same interface name, so even if we + * get the interface, we still need to allocate a new hostapd_iface + * structure. */ + if (conf->bss[0]->mld_ap) + iface = NULL; +#endif /* CONFIG_IEEE80211BE */ + if (iface) { - struct hostapd_config *conf; struct hostapd_bss_config **tmp_conf; struct hostapd_data **tmp_bss; struct hostapd_bss_config *bss; const char *ifname; /* Add new BSS to existing iface */ - conf = interfaces->config_read_cb(config_fname); - if (conf == NULL) - return NULL; if (conf->num_bss > 1) { wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config"); hostapd_config_free(conf); @@ -3360,6 +3540,8 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, conf->bss[0] = NULL; hostapd_config_free(conf); } else { + hostapd_config_free(conf); + /* Add a new iface with the first BSS */ new_iface = iface = hostapd_init(interfaces, config_fname); if (!iface) @@ -3394,20 +3576,24 @@ static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver, return; #ifdef CONFIG_IEEE80211BE - /* In case of non-ML operation, de-init. But if ML operation exist, - * even if that's the last BSS in the interface, the driver (drv) could - * be in use for a different AP MLD. Hence, need to check if drv is - * still being used by some other BSS before de-initiallizing. */ - if (!iface->bss[0]->conf->mld_ap) { + if (!driver->is_drv_shared || + !driver->is_drv_shared(drv_priv, iface->bss[0]->mld_link_id)) { driver->hapd_deinit(drv_priv); - } else if (hostapd_mld_is_first_bss(iface->bss[0]) && - driver->is_drv_shared && - !driver->is_drv_shared(drv_priv, iface->bss[0])) { - driver->hapd_deinit(drv_priv); - } else if (hostapd_if_link_remove(iface->bss[0], - WPA_IF_AP_BSS, - iface->bss[0]->conf->iface, - iface->bss[0]->mld_link_id)) { + hostapd_mld_interface_freed(iface->bss[0]); + iface->bss[0]->drv_priv = NULL; + return; + } + + if (iface->bss[0]->conf->mld_ap) { + if (hostapd_if_link_remove(iface->bss[0], + WPA_IF_AP_BSS, + iface->bss[0]->conf->iface, + iface->bss[0]->mld_link_id)) + wpa_printf(MSG_WARNING, + "Failed to remove link BSS interface %s", + iface->bss[0]->conf->iface); + } else if (hostapd_if_remove(iface->bss[0], WPA_IF_AP_BSS, + iface->bss[0]->conf->iface)) { wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", iface->bss[0]->conf->iface); } @@ -3466,7 +3652,7 @@ static void hostapd_deinit_driver(const struct wpa_driver_ops *driver, } -static void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface) +void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface) { size_t j; @@ -3510,8 +3696,12 @@ int hostapd_enable_iface(struct hostapd_iface *hapd_iface) if (hapd_iface->interfaces == NULL || hapd_iface->interfaces->driver_init == NULL || - hapd_iface->interfaces->driver_init(hapd_iface)) + hapd_iface->interfaces->driver_init(hapd_iface)) { + hostapd_deinit_driver(hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, + hapd_iface); return -1; + } if (hostapd_setup_interface(hapd_iface)) { hostapd_deinit_driver(hapd_iface->bss[0]->driver, @@ -3564,8 +3754,6 @@ int hostapd_reload_bss_only(struct hostapd_data *bss) int hostapd_disable_iface(struct hostapd_iface *hapd_iface) { size_t j; - const struct wpa_driver_ops *driver; - void *drv_priv; if (hapd_iface == NULL) return -1; @@ -3580,8 +3768,6 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) } wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); - driver = hapd_iface->bss[0]->driver; - drv_priv = hapd_iface->bss[0]->drv_priv; hapd_iface->driver_ap_teardown = !!(hapd_iface->drv_flags & @@ -3600,7 +3786,8 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) hostapd_free_hapd_data(hapd); } - hostapd_deinit_driver(driver, drv_priv, hapd_iface); + hostapd_deinit_driver(hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, hapd_iface); /* From hostapd_cleanup_iface: These were initialized in * hostapd_setup_interface and hostapd_setup_interface_complete @@ -3772,10 +3959,17 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) } if (new_iface) { - if (interfaces->driver_init(hapd_iface)) + if (interfaces->driver_init(hapd_iface)) { + hostapd_deinit_driver( + hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, + hapd_iface); goto fail; + } if (hostapd_setup_interface(hapd_iface)) { + /* Deinit link for the first bss */ + hostapd_bss_link_deinit(hapd_iface->bss[0]); hostapd_deinit_driver( hapd_iface->bss[0]->driver, hapd_iface->bss[0]->drv_priv, @@ -3792,7 +3986,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) if (start_ctrl_iface_bss(hapd) < 0 || (hapd_iface->state == HAPD_IFACE_ENABLED && - hostapd_setup_bss(hapd, -1, true))) { + hostapd_setup_bss(hapd, false, true))) { hostapd_bss_link_deinit(hapd); hostapd_cleanup(hapd); hapd_iface->bss[hapd_iface->num_bss - 1] = NULL; @@ -3956,7 +4150,7 @@ static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx) int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) { struct hostapd_iface *hapd_iface; - size_t i, j, k = 0; + size_t i, j; for (i = 0; i < interfaces->count; i++) { hapd_iface = interfaces->iface[i]; @@ -3964,21 +4158,7 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) return -1; if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Remove interface '%s'", buf); - hapd_iface->driver_ap_teardown = - !!(hapd_iface->drv_flags & - WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); - - hostapd_interface_deinit_free(hapd_iface); - k = i; - while (k < (interfaces->count - 1)) { - interfaces->iface[k] = - interfaces->iface[k + 1]; - k++; - } - interfaces->count--; - hostapd_cleanup_unused_mlds(interfaces); - - return 0; + return hostapd_remove_hapd_iface(hapd_iface); } for (j = 0; j < hapd_iface->conf->num_bss; j++) { @@ -4021,6 +4201,23 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211BE */ ap_sta_clear_disconnect_timeouts(hapd, sta); + ap_sta_clear_assoc_timeout(hapd, sta); + +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta)) { + struct hostapd_data *bss; + struct sta_info *lsta; + + for_each_mld_link(bss, hapd) { + if (bss == hapd) + continue; + lsta = ap_get_sta(bss, sta->addr); + if (lsta) + ap_sta_clear_assoc_timeout(bss, lsta); + } + } +#endif /* CONFIG_IEEE80211BE */ + sta->post_csa_sa_query = 0; #ifdef CONFIG_P2P @@ -4037,14 +4234,57 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) { - ap_sta_set_authorized(hapd, sta, 1); + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) { + if (ap_sta_set_authorized(hapd, sta, 1)) { + /* Update driver authorized flag for the STA to cover + * the case where AP SME is in the driver and there is + * no separate event for handling TX status event for + * the (Re)Association Response frame. */ + hostapd_set_sta_flags(hapd, sta); + } os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); } /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); + +#ifdef CONFIG_ENC_ASSOC + if (ap_sta_is_epp(sta) && sta->wpa_sm) { + const struct wpa_ptk *ptk; + const u8 *pmk; + size_t pmk_len; + + switch (sta->auth_alg) { + case WLAN_AUTH_EPPKE: + if (!sta->pasn) { + wpa_printf(MSG_INFO, + "EPP: Missing PASN data"); + return; + } + ptk = &sta->pasn->ptk; + pmk = sta->pasn->pmk; + pmk_len = sta->pasn->pmk_len; + break; +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: + ptk = &sta->eap_auth_data.ptk; + pmk = sta->eap_auth_data.pmk; + pmk_len = sta->eap_auth_data.pmk_len; + break; +#endif /* CONFIG_IEEE8021X_AUTH */ + default: + wpa_printf(MSG_INFO, + "EPP: Unsupported auth alg=%d for an EPP station", + sta->auth_alg); + return; + } + wpa_store_eppke_pmk_ptk_sm(sta->wpa_sm, ptk, pmk, + pmk_len); + wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); + } +#endif /* CONFIG_ENC_ASSOC */ + if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && sta->auth_alg != WLAN_AUTH_FILS_SK && @@ -4150,14 +4390,26 @@ void free_beacon_data(struct beacon_data *beacon) beacon->proberesp_ies = NULL; os_free(beacon->assocresp_ies); beacon->assocresp_ies = NULL; + os_free(beacon->mbssid.mbssid_elem); + beacon->mbssid.mbssid_elem = NULL; + os_free(beacon->mbssid.mbssid_elem_offset); + beacon->mbssid.mbssid_elem_offset = NULL; + os_free(beacon->mbssid.rnr_elem); + beacon->mbssid.rnr_elem = NULL; + os_free(beacon->mbssid.rnr_elem_offset); + beacon->mbssid.rnr_elem_offset = NULL; } -static int hostapd_build_beacon_data(struct hostapd_data *hapd, - struct beacon_data *beacon) +int hostapd_build_beacon_data(struct hostapd_data *hapd, + struct beacon_data *beacon) { struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra; struct wpa_driver_ap_params params; + struct hostapd_data *tx_bss; + u8 *mbssid_start_eid, *rnr_start_eid; + size_t size = 0; + int i; int ret; os_memset(beacon, 0, sizeof(*beacon)); @@ -4221,6 +4473,76 @@ static int hostapd_build_beacon_data(struct hostapd_data *hapd, beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); } + /* MBSSID element */ + if (!params.mbssid.mbssid_elem_len) + goto done; + + tx_bss = hostapd_mbssid_get_tx_bss(hapd); + beacon->mbssid.mbssid_tx_iface = tx_bss->conf->iface; + beacon->mbssid.mbssid_tx_iface_linkid = + params.mbssid.mbssid_tx_iface_linkid; + beacon->mbssid.mbssid_index = params.mbssid.mbssid_index; + + beacon->mbssid.mbssid_elem_len = params.mbssid.mbssid_elem_len; + beacon->mbssid.mbssid_elem_count = params.mbssid.mbssid_elem_count; + if (params.mbssid.mbssid_elem) { + beacon->mbssid.mbssid_elem = + os_memdup(params.mbssid.mbssid_elem, + params.mbssid.mbssid_elem_len); + if (!beacon->mbssid.mbssid_elem) + goto free_beacon; + } + beacon->mbssid.ema = params.mbssid.ema; + + if (params.mbssid.mbssid_elem_offset) { + beacon->mbssid.mbssid_elem_offset = + os_calloc(beacon->mbssid.mbssid_elem_count, + sizeof(u8 *)); + if (!beacon->mbssid.mbssid_elem_offset) + goto free_beacon; + + mbssid_start_eid = beacon->mbssid.mbssid_elem; + beacon->mbssid.mbssid_elem_offset[0] = mbssid_start_eid; + for (i = 0; i < beacon->mbssid.mbssid_elem_count - 1; i++) { + size = params.mbssid.mbssid_elem_offset[i + 1] - + params.mbssid.mbssid_elem_offset[i]; + mbssid_start_eid = mbssid_start_eid + size; + beacon->mbssid.mbssid_elem_offset[i + 1] = + mbssid_start_eid; + } + } + + /* RNR element */ + if (!params.mbssid.rnr_elem_len) + goto done; + + if (params.mbssid.rnr_elem) { + beacon->mbssid.rnr_elem = os_memdup(params.mbssid.rnr_elem, + params.mbssid.rnr_elem_len); + if (!beacon->mbssid.rnr_elem) + goto free_beacon; + } + + beacon->mbssid.rnr_elem_len = params.mbssid.rnr_elem_len; + beacon->mbssid.rnr_elem_count = params.mbssid.rnr_elem_count; + if (params.mbssid.rnr_elem_offset) { + beacon->mbssid.rnr_elem_offset = + os_calloc(beacon->mbssid.rnr_elem_count + 1, + sizeof(u8 *)); + if (!beacon->mbssid.rnr_elem_offset) + goto free_beacon; + + rnr_start_eid = beacon->mbssid.rnr_elem; + beacon->mbssid.rnr_elem_offset[0] = rnr_start_eid; + for (i = 0; i < beacon->mbssid.rnr_elem_count - 1; i++) { + size = params.mbssid.rnr_elem_offset[i + 1] - + params.mbssid.rnr_elem_offset[i]; + rnr_start_eid = rnr_start_eid + size; + beacon->mbssid.rnr_elem_offset[i + 1] = rnr_start_eid; + } + } + +done: ret = 0; free_beacon: /* if the function fails, the caller should not free beacon data */ @@ -4241,13 +4563,12 @@ free_ap_params: * the same hw_mode. Any other changes to MAC parameters or provided settings * are not supported. */ -static int hostapd_change_config_freq(struct hostapd_data *hapd, - struct hostapd_config *conf, - struct hostapd_freq_params *params, - struct hostapd_freq_params *old_params) +int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, + struct hostapd_freq_params *params, + struct hostapd_freq_params *old_params) { - int channel; - u8 seg0 = 0, seg1 = 0; + u8 channel, op_class, seg0 = 0, seg1 = 0; struct hostapd_hw_modes *mode; if (!params->channel) { @@ -4332,52 +4653,120 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, ieee80211_freq_to_chan(params->center_freq2, &seg1) == NUM_HOSTAPD_MODES) return -1; + if (ieee80211_freq_to_channel_ext(params->freq, + conf->secondary_channel, + hostapd_get_oper_chwidth(hapd->iconf), + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return -1; + + wpa_printf(MSG_DEBUG, + "%s:Update op_class %d->%d and channel=%d based on freq=%d", + __func__, conf->op_class, op_class, channel, params->freq); + conf->op_class = op_class; hostapd_set_oper_centr_freq_seg0_idx(conf, seg0); hostapd_set_oper_centr_freq_seg1_idx(conf, seg1); +#ifdef CONFIG_IEEE80211BE + conf->punct_bitmap = params->punct_bitmap; +#endif /* CONFIG_IEEE80211BE */ + /* TODO: maybe call here hostapd_config_check here? */ return 0; } +enum oper_chan_width +hostapd_chan_width_from_freq_params(struct hostapd_freq_params *freq_params) +{ + switch (freq_params->bandwidth) { + case 80: + if (freq_params->center_freq2) + return CONF_OPER_CHWIDTH_80P80MHZ; + else + return CONF_OPER_CHWIDTH_80MHZ; + case 160: + return CONF_OPER_CHWIDTH_160MHZ; + case 320: + return CONF_OPER_CHWIDTH_320MHZ; + default: + return CONF_OPER_CHWIDTH_USE_HT; + } +} + + static int hostapd_fill_csa_settings(struct hostapd_data *hapd, struct csa_settings *settings) { struct hostapd_iface *iface = hapd->iface; struct hostapd_freq_params old_freq; int ret; -#ifdef CONFIG_IEEE80211BE - u16 old_punct_bitmap; -#endif /* CONFIG_IEEE80211BE */ - u8 chan, bandwidth; + enum oper_chan_width chanwidth; + u8 chan; + int sec_channel_offset; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || hapd->csa_in_progress) return -1; - switch (settings->freq_params.bandwidth) { - case 80: - if (settings->freq_params.center_freq2) - bandwidth = CONF_OPER_CHWIDTH_80P80MHZ; + chanwidth = hostapd_chan_width_from_freq_params(&settings->freq_params); + sec_channel_offset = settings->freq_params.sec_channel_offset; +#ifdef CONFIG_IEEE80211BE + /* When Switching to an 11BE channel, adjust the legacy BW and center + * frequencies accordingly + */ + if (chanwidth == CONF_OPER_CHWIDTH_320MHZ || + settings->freq_params.punct_bitmap) { + enum oper_chan_width chan_op_bw = chanwidth; + u8 oper_centr_freq0_idx = 0, oper_centr_freq1_idx = 0, pri_chan = 0; + + ieee80211_freq_to_chan(settings->freq_params.center_freq1, + &oper_centr_freq0_idx); + ieee80211_freq_to_chan(settings->freq_params.center_freq2, + &oper_centr_freq1_idx); + ieee80211_freq_to_chan(settings->freq_params.freq, + &pri_chan); + + punct_update_legacy_bw(settings->freq_params.punct_bitmap, + pri_chan, + &chan_op_bw, + &oper_centr_freq0_idx, + &oper_centr_freq1_idx); + + /* Downgrade the EHT 320 MHz BW to legacy 160 MHz BW and + * calculate the correspoding 160 MHz center freq for later + * ECSA opclass calculation which needs legacy BW and + * secondary channel offset. + */ + if (chan_op_bw == CONF_OPER_CHWIDTH_320MHZ) { + chan_op_bw = CONF_OPER_CHWIDTH_160MHZ; + + if (pri_chan < oper_centr_freq0_idx) + oper_centr_freq0_idx -= 16; + else + oper_centr_freq0_idx += 16; + } + + chanwidth = chan_op_bw; + if (oper_centr_freq0_idx == 0 || oper_centr_freq0_idx == pri_chan) + sec_channel_offset = 0; + else if (oper_centr_freq0_idx > pri_chan) + sec_channel_offset = 1; else - bandwidth = CONF_OPER_CHWIDTH_80MHZ; - break; - case 160: - bandwidth = CONF_OPER_CHWIDTH_160MHZ; - break; - case 320: - bandwidth = CONF_OPER_CHWIDTH_320MHZ; - break; - default: - bandwidth = CONF_OPER_CHWIDTH_USE_HT; - break; + sec_channel_offset = -1; + + wpa_printf(MSG_DEBUG, + "ECSA Legacy BW %d chan1 %d chan2 %d sec_chan %d", + chan_op_bw, oper_centr_freq0_idx, oper_centr_freq1_idx, + sec_channel_offset); } +#endif /* CONFIG_IEEE80211BE */ if (ieee80211_freq_to_channel_ext( settings->freq_params.freq, - settings->freq_params.sec_channel_offset, - bandwidth, + sec_channel_offset, + chanwidth, &hapd->iface->cs_oper_class, &chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, @@ -4398,18 +4787,13 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, if (ret) return ret; -#ifdef CONFIG_IEEE80211BE - old_punct_bitmap = iface->conf->punct_bitmap; - iface->conf->punct_bitmap = settings->punct_bitmap; -#endif /* CONFIG_IEEE80211BE */ ret = hostapd_build_beacon_data(hapd, &settings->beacon_after); /* change back the configuration */ -#ifdef CONFIG_IEEE80211BE - iface->conf->punct_bitmap = old_punct_bitmap; -#endif /* CONFIG_IEEE80211BE */ - hostapd_change_config_freq(iface->bss[0], iface->conf, - &old_freq, NULL); + if (hostapd_change_config_freq(iface->bss[0], iface->conf, + &old_freq, NULL) < 0) + wpa_printf(MSG_INFO, + "Failed to switch back to old frequency after preparing beacon data for CSA"); if (ret) return ret; @@ -4516,6 +4900,42 @@ int hostapd_switch_channel(struct hostapd_data *hapd, } +int hostapd_force_channel_switch(struct hostapd_iface *iface, + struct csa_settings *settings) +{ + int ret = 0; + + if (!settings->freq_params.channel) { + /* Check if the new channel is supported */ + settings->freq_params.channel = hostapd_hw_get_channel( + iface->bss[0], settings->freq_params.freq); + if (!settings->freq_params.channel) + return -1; + } + + ret = hostapd_disable_iface(iface); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to disable the interface"); + return ret; + } + + hostapd_chan_switch_config(iface->bss[0], &settings->freq_params); + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings->freq_params, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "Failed to set the new channel in config"); + return ret; + } + + ret = hostapd_enable_iface(iface); + if (ret) + wpa_printf(MSG_DEBUG, "Failed to enable the interface"); + + return ret; +} + + void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params) @@ -4606,9 +5026,10 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd, { struct hostapd_iface *iface = hapd->iface; u8 old_color; + u8 old_disabled; int ret; - if (!iface || iface->conf->he_op.he_bss_color_disabled) + if (!iface) return -1; settings->link_id = -1; @@ -4618,12 +5039,16 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211BE */ old_color = iface->conf->he_op.he_bss_color; + old_disabled = iface->conf->he_op.he_bss_color_disabled; + iface->conf->he_op.he_bss_color = hapd->cca_color; + iface->conf->he_op.he_bss_color_disabled = 0; ret = hostapd_build_beacon_data(hapd, &settings->beacon_after); - if (ret) - return ret; iface->conf->he_op.he_bss_color = old_color; + iface->conf->he_op.he_bss_color_disabled = old_disabled; + if (ret) + return ret; settings->cca_count = hapd->cca_count; settings->cca_color = hapd->cca_color, @@ -4645,8 +5070,7 @@ int hostapd_fill_cca_settings(struct hostapd_data *hapd, } -static void hostapd_switch_color_timeout_handler(void *eloop_data, - void *user_ctx) +void hostapd_switch_color_timeout_handler(void *eloop_data, void *user_ctx) { struct hostapd_data *hapd = (struct hostapd_data *) eloop_data; os_time_t delta_t; @@ -4661,6 +5085,14 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data, if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD) return; + /* IEEE Std 802.11-2024, 26.17.3.5.1: once the BSS color collision has + * persisted for dot11BSSColorCollisionAPPeriod the AP shall set BSS + * Color Disabled to 1. Set it now so that beacon_cca (built inside + * hostapd_fill_cca_settings()) and any incidental beacon rebuild + * during the CCA countdown carry the correct Disabled=1 state. + */ + hapd->iface->conf->he_op.he_bss_color_disabled = 1; + r = os_random() % HE_OPERATION_BSS_COLOR_MAX; for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) { if (r && !(hapd->color_collision_bitmap & (1ULL << r))) @@ -4685,6 +5117,7 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data, struct cca_settings settings; int ret; + os_memset(&settings, 0, sizeof(settings)); hostapd_cleanup_cca_params(bss); bss->cca_color = r; bss->cca_count = 10; @@ -4705,11 +5138,24 @@ static void hostapd_switch_color_timeout_handler(void *eloop_data, } +bool hostapd_is_cca_in_progress(struct hostapd_iface *iface) +{ + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->cca_in_progress) + return true; + } + + return false; +} + + void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap) { struct os_reltime now; - if (hapd->cca_in_progress) + if (hostapd_is_cca_in_progress(hapd->iface)) return; if (os_get_reltime(&now)) @@ -4843,10 +5289,7 @@ u8 hostapd_get_mld_id(struct hostapd_data *hapd) if (!hapd->conf->mld_ap) return 255; - /* MLD ID 0 represents self */ - return 0; - - /* TODO: MLD ID for Multiple BSS cases */ + return hostapd_mbssid_get_bss_index(hapd); } @@ -4902,8 +5345,16 @@ int hostapd_mld_remove_link(struct hostapd_data *hapd) if (dl_list_empty(&mld->links)) { mld->fbss = NULL; } else { + struct hostapd_data *last_link_bss; + next_fbss = dl_list_entry(mld->links.next, struct hostapd_data, link); + last_link_bss = dl_list_last(&mld->links, struct hostapd_data, + link); + + if (next_fbss != last_link_bss) + hostapd_mld_move_vlan_list(hapd, next_fbss); + mld->fbss = next_fbss; } @@ -4947,6 +5398,40 @@ struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd) return mld->fbss; } + +void hostapd_mld_interface_freed(struct hostapd_data *hapd) +{ + struct hostapd_data *link_bss = NULL; + + if (!hapd || !hapd->conf->mld_ap) + return; + + for_each_mld_link(link_bss, hapd) + link_bss->drv_priv = NULL; +} + + +/* Return the number of currently active links, not counting the calling link + * (i.e., a value that is suitable to be used as-is in fields that use encoding + * of the value minus 1). */ +u8 hostapd_get_active_links(struct hostapd_data *hapd) +{ + struct hostapd_data *link_bss; + u8 active_links = 0; + + if (!hapd || !hapd->conf->mld_ap) + return 0; + + for_each_mld_link(link_bss, hapd) { + if (link_bss == hapd || !link_bss->started) + continue; + + active_links++; + } + + return active_links; +} + #endif /* CONFIG_IEEE80211BE */ @@ -4964,3 +5449,172 @@ u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd) return punct_bitmap; } + + +struct hostapd_data * +hostapd_get_mbssid_bss_by_idx(struct hostapd_data *hapd, size_t idx) +{ + if (idx < hapd->iface->num_bss) + return hapd->iface->bss[idx]; + + return NULL; +} + + +static bool hostapd_acceptable_sta_addr_bss(struct hostapd_data *hapd, + const u8 *addr, const u8 *addr_ml, + bool mld) +{ + if (ether_addr_equal(addr, hapd->own_addr)) + return false; + +#ifdef CONFIG_IEEE80211BE + if (hapd->mld && mld && + (ether_addr_equal(addr_ml, hapd->own_addr) || + ether_addr_equal(addr_ml, hapd->mld->mld_addr))) + return false; + if (hapd->mld && ether_addr_equal(addr, hapd->mld->mld_addr)) + return false; +#endif /* CONFIG_IEEE80211BE */ + + return true; +} + + +struct hostapd_acceptable_sta_add_ctx { + const u8 *addr; + const u8 *addr_ml; + bool mld; +}; + + +static int hostapd_acceptable_sta_addr_iface(struct hostapd_iface *iface, + void *_ctx) +{ + struct hostapd_acceptable_sta_add_ctx *ctx = _ctx; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + + if (!hostapd_acceptable_sta_addr_bss(hapd, ctx->addr, + ctx->addr_ml, ctx->mld)) + return -1; + } + + return 0; +} + + +bool hostapd_acceptable_sta_addr(struct hostapd_data *hapd, const u8 *addr, + const u8 *addr_ml, bool mld) +{ + struct hostapd_acceptable_sta_add_ctx ctx; + + if (!hostapd_acceptable_sta_addr_bss(hapd, addr, addr_ml, mld)) + return false; + + ctx.addr = addr; + ctx.addr_ml = addr_ml; + ctx.mld = mld; + if (hapd->iface->interfaces && + hostapd_for_each_interface(hapd->iface->interfaces, + hostapd_acceptable_sta_addr_iface, + &ctx) < 0) + return false; + + return true; +} + + +void hostapd_get_oper_chan_info_of_bss(struct hostapd_data *hapd, + enum oper_chan_width *width, + u8 *seg0, u8 *seg1) +{ + *width = hostapd_get_oper_chwidth(hapd->iconf); + *seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf); + *seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf); + +#ifdef CONFIG_IEEE80211BE + /* Re-derive the legacy channel width for EHT disabled BSS on an EHT + * capable interface for punctured channel and 320 MHz bandwidth cases. + */ + if (hapd->iconf->ieee80211be && !hostapd_is_eht_enabled(hapd)) { + u16 punct_bitmap = hostapd_get_punct_bitmap(hapd); + + if (punct_bitmap) + punct_update_legacy_bw(punct_bitmap, + hapd->iconf->channel, + width, seg0, seg1); + + if (*width == CONF_OPER_CHWIDTH_320MHZ) + *width = CONF_OPER_CHWIDTH_160MHZ; + } +#endif /* CONFIG_IEEE80211BE */ +} + + +enum oper_chan_width +hostapd_get_oper_chan_width_of_bss(struct hostapd_data *hapd) +{ + enum oper_chan_width width; + u8 seg0, seg1; + + hostapd_get_oper_chan_info_of_bss(hapd, &width, &seg0, &seg1); + + return width; +} + + +u8 hostapd_get_oper_class_of_bss(struct hostapd_data *hapd) +{ + u8 op_class = hapd->iconf->op_class; + +#ifdef CONFIG_IEEE80211BE + /* For EHT disabled BSS, re-derive the legacy operating class from + * operating frequency parameters as the bandwidth that it advertises + * in VHT/HE Operation element can be lower than EHT enabled BSS of the + * interface. + * + * If EHT is not disabled on the BSS, interface's operating class is + * returned without recomputation against operating frequency parameters + * as operating channel information is expected to be aligned with the + * interface's operating class configuration. + */ + if (hapd->iconf->ieee80211be && !hostapd_is_eht_enabled(hapd) && + hapd->iface->freq) { + enum oper_chan_width bss_chwidth, iface_chwidth; + u8 seg0, seg1; + u8 chan; + + iface_chwidth = hostapd_get_oper_chwidth(hapd->iconf); + hostapd_get_oper_chan_info_of_bss(hapd, &bss_chwidth, + &seg0, &seg1); + + /* If the BSS channel width is different from the interface + * channel width, re-compute the operating class as per the BSS + * channel width. + */ + if (bss_chwidth != iface_chwidth) { + int secondary_channel = hapd->iconf->secondary_channel; + + /* Check if legacy bandwidth is downgraded to 20 MHz + * and derive operating class accordingly. + */ + if (seg0 == hapd->iconf->channel && + bss_chwidth == CONF_OPER_CHWIDTH_USE_HT) + secondary_channel = 0; + + if (ieee80211_freq_to_channel_ext( + hapd->iface->freq, + secondary_channel, + bss_chwidth, &op_class, + &chan) == NUM_HOSTAPD_MODES || + chan != hapd->iconf->channel) + return hapd->iconf->op_class; + } + } +#endif /* CONFIG_IEEE80211BE */ + + return op_class; +} diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h index dcf395ca5ee8..8d4f3462a31a 100644 --- a/contrib/wpa/src/ap/hostapd.h +++ b/contrib/wpa/src/ap/hostapd.h @@ -35,6 +35,7 @@ struct ieee80211_ht_capabilities; struct full_dynamic_vlan; enum wps_event; union wps_event_data; +struct rsn_pmksa_cache_entry; #ifdef CONFIG_MESH struct mesh_conf; #endif /* CONFIG_MESH */ @@ -97,7 +98,13 @@ struct hapd_interfaces { #ifdef CONFIG_IEEE80211BE struct hostapd_mld **mld; size_t mld_count; + int (*mld_ctrl_iface_init)(struct hostapd_mld *mld); + void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld); #endif /* CONFIG_IEEE80211BE */ + +#ifdef CONFIG_PROCESS_COORDINATION + struct proc_coord *pc; +#endif /* CONFIG_PROCESS_COORDINATION */ }; enum hostapd_chan_status { @@ -167,6 +174,21 @@ struct hostapd_sae_commit_queue { u8 msg[]; }; +struct mld_link_info { + u8 valid:1; + u8 nstr_bitmap_len:2; + u8 local_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + + u8 nstr_bitmap[2]; + + u16 capability; + + u16 status; + u16 resp_sta_profile_len; + u8 *resp_sta_profile; +}; + /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -204,6 +226,10 @@ struct hostapd_data { void *msg_ctx; /* ctx for wpa_msg() calls */ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ + int num_rates; + struct hostapd_rate_data *current_rates; + int *basic_rates; + struct radius_client_data *radius; u64 acct_session_id; struct radius_das_data *radius_das; @@ -304,6 +330,13 @@ struct hostapd_data { size_t psk_len); void *new_psk_cb_ctx; +#ifdef CONFIG_IEEE8021X_AUTH + void (*send_eap_req)(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, u16 auth_transaction, u16 status, + struct rsn_pmksa_cache_entry *cached_pmk, + const u8 *eap_req, size_t eap_req_len); +#endif /* CONFIG_IEEE8021X_AUTH */ + /* channel switch parameters */ struct hostapd_freq_params cs_freq_params; u8 cs_count; @@ -476,6 +509,14 @@ struct hostapd_data { struct hostapd_mld *mld; struct dl_list link; u8 mld_link_id; + + /* Cached partner info for ML probe response */ + struct mld_link_info partner_links[MAX_NUM_MLD_LINKS]; + + /* 5 characters for "_link", up to 2 characters for , so in + * total, additional 7 characters required. */ + char ctrl_sock_iface[IFNAMSIZ + 7 + 1]; + #ifdef CONFIG_TESTING_OPTIONS u8 eht_mld_link_removal_count; #endif /* CONFIG_TESTING_OPTIONS */ @@ -483,6 +524,8 @@ struct hostapd_data { #ifdef CONFIG_NAN_USD struct nan_de *nan_de; + /* Whether nan_de should be freed on deinit */ + bool nan_de_is_owned; #endif /* CONFIG_NAN_USD */ u64 scan_cookie; /* Scan instance identifier for the ongoing HT40 scan @@ -518,6 +561,10 @@ struct hostapd_mld { struct hostapd_data *fbss; struct dl_list links; /* List head of all affiliated links */ + + int ctrl_sock; + struct dl_list ctrl_dst; + char *ctrl_interface; /* Directory for UNIX domain sockets */ }; #define HOSTAPD_MLD_MAX_REF_COUNT 0xFF @@ -598,11 +645,6 @@ struct hostapd_iface { struct hostapd_hw_modes *hw_features; int num_hw_features; struct hostapd_hw_modes *current_mode; - /* Rates that are currently used (i.e., filtered copy of - * current_mode->channels */ - int num_rates; - struct hostapd_rate_data *current_rates; - int *basic_rates; int freq; bool radar_detected; @@ -711,6 +753,43 @@ struct hostapd_iface { bool is_no_ir; bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */ + + struct hostapd_multi_hw_info *multi_hw_info; + unsigned int num_multi_hws; + struct hostapd_multi_hw_info *current_hw_info; + + /* Use assisted DFS functionality from the driver. This is used only + * in wpa_supplicant builds for P2P GO functionality. */ + bool assisted_dfs; + +#ifdef CONFIG_AFC + struct wpabuf *afc_request; + char *afc_response; + struct { + unsigned int request_id; + int timeout; + unsigned int num_freq_range; + struct afc_freq_range_elem { + int low_freq; + int high_freq; + /** + * Maximum EIRP power spectral density received from + * the AFC coordinator for this band + */ + double max_psd; + } *freq_range; + unsigned int num_chan_info; + struct afc_chan_info_elem { + int chan; + /** + * Maximum EIRP power received from the AFC coordinator + * for this channel for each op_class + */ + double power[5]; + } *chan_info_list; + bool data_valid; + } afc; +#endif /* CONFIG_AFC */ }; /* hostapd.c */ @@ -733,6 +812,8 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, struct hostapd_iface * hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, const char *config_fname, int debug); +void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, + struct hapd_interfaces *interfaces); void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); void hostapd_interface_deinit_free(struct hostapd_iface *iface); @@ -753,6 +834,8 @@ void hostapd_chan_switch_config(struct hostapd_data *hapd, struct hostapd_freq_params *freq_params); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); +int hostapd_force_channel_switch(struct hostapd_iface *iface, + struct csa_settings *settings); void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params); @@ -763,6 +846,38 @@ void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx); void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap); void hostapd_cleanup_cca_params(struct hostapd_data *hapd); +#ifdef CONFIG_AFC +int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power); +int hostapd_afc_handle_request(struct hostapd_iface *iface); +void hostapd_afc_send_request(struct hostapd_iface *iface); +void hostapd_afc_stop(struct hostapd_iface *iface); +void hostap_afc_disable_channels(struct hostapd_iface *iface); +#else /* CONFIG_AFC */ +static inline int +hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, + int *power) +{ + return -EINVAL; +} + +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface) +{ + return 1; +} + +static inline void hostapd_afc_send_request(struct hostapd_iface *iface) +{ +} + +static inline void hostapd_afc_stop(struct hostapd_iface *iface) +{ +} + +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) +{ +} +#endif /* CONFIG_AFC */ /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -781,6 +896,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *req_ie, size_t req_ielen, const u8 *resp_ie, size_t resp_ielen, const u8 *link_addr, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd, + struct sta_info *sta, const u8 *addr); void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, const u8 *addr, int reason_code); @@ -806,6 +923,10 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, enum smps_mode smps_mode, enum chan_width chan_width, u8 rx_nss); +int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, + struct hostapd_freq_params *params, + struct hostapd_freq_params *old_params); #ifdef CONFIG_FST void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, struct fst_wpa_obj *iface_obj); @@ -822,15 +943,22 @@ bool hostapd_is_ml_partner(struct hostapd_data *hapd1, u8 hostapd_get_mld_id(struct hostapd_data *hapd); int hostapd_mld_add_link(struct hostapd_data *hapd); int hostapd_mld_remove_link(struct hostapd_data *hapd); +u8 hostapd_get_active_links(struct hostapd_data *hapd); struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd); +int hostapd_build_beacon_data(struct hostapd_data *hapd, + struct beacon_data *beacon); void free_beacon_data(struct beacon_data *beacon); int hostapd_fill_cca_settings(struct hostapd_data *hapd, struct cca_settings *settings); +void hostapd_switch_color_timeout_handler(void *eloop_data, void *user_ctx); +bool hostapd_is_cca_in_progress(struct hostapd_iface *iface); +void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface); #ifdef CONFIG_IEEE80211BE bool hostapd_mld_is_first_bss(struct hostapd_data *hapd); +void hostapd_mld_interface_freed(struct hostapd_data *hapd); #define for_each_mld_link(partner, self) \ dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link) @@ -849,4 +977,53 @@ static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd) u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd); +void hostapd_get_oper_chan_info_of_bss(struct hostapd_data *hapd, + enum oper_chan_width *width, + u8 *seg0, u8 *seg1); + +u8 hostapd_get_oper_class_of_bss(struct hostapd_data *hapd); + +enum oper_chan_width +hostapd_get_oper_chan_width_of_bss(struct hostapd_data *hapd); + +static inline bool ap_pmf_enabled(struct hostapd_bss_config *conf) +{ + return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION || + conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION || + conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION; +} + +enum oper_chan_width +hostapd_chan_width_from_freq_params(struct hostapd_freq_params *freq_params); + +struct hostapd_data * +hostapd_get_mbssid_bss_by_idx(struct hostapd_data *hapd, size_t idx); + +bool hostapd_acceptable_sta_addr(struct hostapd_data *hapd, const u8 *addr, + const u8 *addr_ml, bool mld); + +static inline bool +hostapd_is_ht_enabled(struct hostapd_data *hapd) +{ + return hapd->iconf->ieee80211n && !hapd->conf->disable_11n; +} + +static inline bool +hostapd_is_vht_enabled(struct hostapd_data *hapd) +{ + return hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; +} + +static inline bool +hostapd_is_he_enabled(struct hostapd_data *hapd) +{ + return hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax; +} + +static inline bool +hostapd_is_eht_enabled(struct hostapd_data *hapd) +{ + return hapd->iconf->ieee80211be && !hapd->conf->disable_11be; +} + #endif /* HOSTAPD_H */ diff --git a/contrib/wpa/src/ap/hs20.c b/contrib/wpa/src/ap/hs20.c index 05e9b9d20fd7..4ae3b6b49b0a 100644 --- a/contrib/wpa/src/ap/hs20.c +++ b/contrib/wpa/src/ap/hs20.c @@ -44,113 +44,6 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) } -u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid) -{ - u8 *len; - u16 capab; - - if (!hapd->conf->osen) - return eid; - - *eid++ = WLAN_EID_VENDOR_SPECIFIC; - len = eid++; /* to be filled */ - WPA_PUT_BE24(eid, OUI_WFA); - eid += 3; - *eid++ = HS20_OSEN_OUI_TYPE; - - /* Group Data Cipher Suite */ - RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); - eid += RSN_SELECTOR_LEN; - - /* Pairwise Cipher Suite Count and List */ - WPA_PUT_LE16(eid, 1); - eid += 2; - RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP); - eid += RSN_SELECTOR_LEN; - - /* AKM Suite Count and List */ - WPA_PUT_LE16(eid, 1); - eid += 2; - RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN); - eid += RSN_SELECTOR_LEN; - - /* RSN Capabilities */ - capab = 0; - if (hapd->conf->wmm_enabled) { - /* 4 PTKSA replay counters when using WMM */ - capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); - } - if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { - capab |= WPA_CAPABILITY_MFPC; - if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) - capab |= WPA_CAPABILITY_MFPR; - } -#ifdef CONFIG_OCV - if (hapd->conf->ocv && - (hapd->iface->drv_flags2 & - (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) - capab |= WPA_CAPABILITY_OCVC; -#endif /* CONFIG_OCV */ - WPA_PUT_LE16(eid, capab); - eid += 2; - - *len = eid - len - 1; - - return eid; -} - - -int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, - u8 osu_method, const char *url) -{ - struct wpabuf *buf; - size_t len = 0; - int ret; - - /* TODO: should refuse to send notification if the STA is not associated - * or if the STA did not indicate support for WNM-Notification */ - - if (url) { - len = 1 + os_strlen(url); - if (5 + len > 255) { - wpa_printf(MSG_INFO, "HS 2.0: Too long URL for " - "WNM-Notification: '%s'", url); - return -1; - } - } - - buf = wpabuf_alloc(4 + 7 + len); - if (buf == NULL) - return -1; - - wpabuf_put_u8(buf, WLAN_ACTION_WNM); - wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); - wpabuf_put_u8(buf, 1); /* Dialog token */ - wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ - - /* Subscription Remediation subelement */ - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5 + len); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED); - if (url) { - wpabuf_put_u8(buf, len - 1); - wpabuf_put_data(buf, url, len - 1); - wpabuf_put_u8(buf, osu_method); - } else { - /* Server URL and Server Method fields not included */ - wpabuf_put_u8(buf, 0); - } - - ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, - wpabuf_head(buf), wpabuf_len(buf)); - - wpabuf_free(buf); - - return ret; -} - - int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, const u8 *addr, const struct wpabuf *payload) diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index c4556603d3da..9fe84f70add2 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -72,16 +72,62 @@ static char * dfs_info(struct hostapd_channel_data *chan) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static void move_survey_data(struct hostapd_channel_data *new_chan, + struct hostapd_channel_data *old_chan) +{ + struct freq_survey *survey, *tmp; + + /* Copy the survey list from the old to new channel */ + if (old_chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) { + dl_list_init(&new_chan->survey_list); + dl_list_for_each_safe(survey, tmp, &old_chan->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + dl_list_add(&new_chan->survey_list, + &survey->list); + } + new_chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; + } +} + + +static void move_hw_mode_data(struct hostapd_hw_modes *new_feature, + struct hostapd_hw_modes *old_feature) +{ + int i, j; + + for (i = 0; i < new_feature->num_channels; i++) { + struct hostapd_channel_data *new_chan; + struct hostapd_channel_data *old_chan; + + new_chan = &new_feature->channels[i]; + + for (j = 0; j < old_feature->num_channels; j++) { + old_chan = &old_feature->channels[j]; + + if (new_chan->freq != old_chan->freq) + continue; + + move_survey_data(new_chan, old_chan); + break; + } + } +} + + int hostapd_get_hw_features(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; int i, j; + unsigned int k; u16 num_modes, flags; struct hostapd_hw_modes *modes; u8 dfs_domain; enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY; bool is_6ghz = false; bool orig_mode_valid = false; + struct hostapd_multi_hw_info *multi_hw_info; + unsigned int num_multi_hws; if (hostapd_drv_none(hapd)) return -1; @@ -110,9 +156,6 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) is_6ghz = iface->current_mode->is_6ghz; iface->current_mode = NULL; } - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = modes; - iface->num_hw_features = num_modes; for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; @@ -142,7 +185,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) } else if (((feature->channels[j].flag & HOSTAPD_CHAN_RADAR) && !(iface->drv_flags & - WPA_DRIVER_FLAGS_DFS_OFFLOAD)) || + WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + !iface->assisted_dfs) || (feature->channels[j].flag & HOSTAPD_CHAN_NO_IR)) { feature->channels[j].flag |= @@ -160,29 +204,66 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) feature->channels[j].max_tx_power, dfs ? dfs_info(&feature->channels[j]) : ""); } + + /* Move any old data that should be kept */ + for (j = 0; j < iface->num_hw_features; j++) { + struct hostapd_hw_modes *old_feature = + &iface->hw_features[j]; + + if (feature->mode != old_feature->mode) + continue; + + move_hw_mode_data(feature, old_feature); + } } + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = modes; + iface->num_hw_features = num_modes; + + hostap_afc_disable_channels(iface); + if (orig_mode_valid && !iface->current_mode) { wpa_printf(MSG_ERROR, "%s: Could not update iface->current_mode", __func__); } + multi_hw_info = hostapd_get_multi_hw_info(hapd, &num_multi_hws); + if (!multi_hw_info) + return 0; + + hostapd_free_multi_hw_info(iface->multi_hw_info); + iface->multi_hw_info = multi_hw_info; + iface->num_multi_hws = num_multi_hws; + + wpa_printf(MSG_DEBUG, "Multiple underlying hardwares info:"); + + for (k = 0; k < num_multi_hws; k++) { + struct hostapd_multi_hw_info *hw_info = &multi_hw_info[k]; + + wpa_printf(MSG_DEBUG, + " %d. hw_idx=%u, frequency range: %d-%d MHz", + k + 1, hw_info->hw_idx, hw_info->start_freq, + hw_info->end_freq); + } + return 0; } -int hostapd_prepare_rates(struct hostapd_iface *iface, +int hostapd_prepare_rates(struct hostapd_data *hapd, struct hostapd_hw_modes *mode) { + struct hostapd_bss_config *conf = hapd->conf; int i, num_basic_rates = 0; - int basic_rates_a[] = { 60, 120, 240, -1 }; - int basic_rates_b[] = { 10, 20, -1 }; - int basic_rates_g[] = { 10, 20, 55, 110, -1 }; - int *basic_rates; + int basic_rates_a[] = { 60, 120, 240, 0 }; + int basic_rates_b[] = { 10, 20, 0 }; + int basic_rates_g[] = { 10, 20, 55, 110, 0 }; + const int *basic_rates; - if (iface->conf->basic_rates) - basic_rates = iface->conf->basic_rates; + if (conf->basic_rates) + basic_rates = conf->basic_rates; else switch (mode->mode) { case HOSTAPD_MODE_IEEE80211A: basic_rates = basic_rates_a; @@ -199,22 +280,15 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, return -1; } - i = 0; - while (basic_rates[i] >= 0) - i++; - if (i) - i++; /* -1 termination */ - os_free(iface->basic_rates); - iface->basic_rates = os_malloc(i * sizeof(int)); - if (iface->basic_rates) - os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); + os_free(hapd->basic_rates); + hapd->basic_rates = int_array_dup(basic_rates); - os_free(iface->current_rates); - iface->num_rates = 0; + os_free(hapd->current_rates); + hapd->num_rates = 0; - iface->current_rates = + hapd->current_rates = os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); - if (!iface->current_rates) { + if (!hapd->current_rates) { wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " "table."); return -1; @@ -223,27 +297,26 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, for (i = 0; i < mode->num_rates; i++) { struct hostapd_rate_data *rate; - if (iface->conf->supported_rates && - !hostapd_rate_found(iface->conf->supported_rates, - mode->rates[i])) + if (conf->supported_rates && + !int_array_includes(conf->supported_rates, mode->rates[i])) continue; - rate = &iface->current_rates[iface->num_rates]; + rate = &hapd->current_rates[hapd->num_rates]; rate->rate = mode->rates[i]; - if (hostapd_rate_found(basic_rates, rate->rate)) { + if (int_array_includes(basic_rates, rate->rate)) { rate->flags |= HOSTAPD_RATE_BASIC; num_basic_rates++; } wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", - iface->num_rates, rate->rate, rate->flags); - iface->num_rates++; + hapd->num_rates, rate->rate, rate->flags); + hapd->num_rates++; } - if ((iface->num_rates == 0 || num_basic_rates == 0) && - (!iface->conf->ieee80211n || !iface->conf->require_ht)) { + if ((hapd->num_rates == 0 || num_basic_rates == 0) && + (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) { wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " "rate sets (%d,%d).", - iface->num_rates, num_basic_rates); + hapd->num_rates, num_basic_rates); return -1; } @@ -506,12 +579,6 @@ static void ap_ht40_scan_retry(void *eloop_data, void *user_data) else ieee80211n_scan_channels_5g(iface, ¶ms); - params.link_id = -1; -#ifdef CONFIG_IEEE80211BE - if (iface->bss[0]->conf->mld_ap) - params.link_id = iface->bss[0]->mld_link_id; -#endif /* CONFIG_IEEE80211BE */ - ret = hostapd_driver_scan(iface->bss[0], ¶ms); iface->num_ht40_scan_tries++; os_free(params.freqs); @@ -533,6 +600,9 @@ static void ap_ht40_scan_retry(void *eloop_data, void *user_data) wpa_printf(MSG_DEBUG, "Failed to request a scan in device, bringing up in HT20 mode"); + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0); + hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT); iface->conf->secondary_channel = 0; iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; hostapd_setup_interface_complete(iface, 0); @@ -563,11 +633,6 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface) else ieee80211n_scan_channels_5g(iface, ¶ms); - params.link_id = -1; -#ifdef CONFIG_IEEE80211BE - if (iface->bss[0]->conf->mld_ap) - params.link_id = iface->bss[0]->mld_link_id; -#endif /* CONFIG_IEEE80211BE */ ret = hostapd_driver_scan(iface->bss[0], ¶ms); os_free(params.freqs); @@ -716,10 +781,18 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211BE +static int ieee80211be_supported_eht_capab(struct hostapd_iface *iface) +{ + return iface->current_mode->eht_capab[IEEE80211_MODE_AP].eht_supported; +} +#endif /* CONFIG_IEEE80211BE */ + + #ifdef CONFIG_IEEE80211AX static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface) { - return 1; + return iface->current_mode->he_capab[IEEE80211_MODE_AP].he_supported; } #endif /* CONFIG_IEEE80211AX */ @@ -743,10 +816,19 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) if (!ieee80211n_supported_ht_capab(iface)) return -1; +#ifdef CONFIG_IEEE80211BE + if (iface->conf->ieee80211be && + !ieee80211be_supported_eht_capab(iface)) { + wpa_printf(MSG_ERROR, "Driver does not support EHT"); + return -1; + } +#endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_IEEE80211AX if (iface->conf->ieee80211ax && - !ieee80211ax_supported_he_capab(iface)) + !ieee80211ax_supported_he_capab(iface)) { + wpa_printf(MSG_ERROR, "Driver does not support HE"); return -1; + } #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211AC if (iface->conf->ieee80211ac && @@ -1003,16 +1085,87 @@ static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface) } +static int hostapd_is_usable_chan_seg(struct hostapd_iface *iface) +{ + int central, oper_chwidth, err; + int start_chan, start_freq, chan_num, i; + + oper_chwidth = hostapd_get_oper_chwidth(iface->conf); + central = hostapd_get_oper_centr_freq_seg0_idx(iface->conf); + + switch (oper_chwidth) { + case CONF_OPER_CHWIDTH_80MHZ: + case CONF_OPER_CHWIDTH_80P80MHZ: + chan_num = 4; + break; + case CONF_OPER_CHWIDTH_160MHZ: + chan_num = 8; + break; + case CONF_OPER_CHWIDTH_320MHZ: + chan_num = 16; + break; + default: + return 0; + } + start_chan = central - chan_num * 2 + 2; + start_freq = hw_get_freq(iface->current_mode, start_chan); + + if (!start_freq) { + wpa_printf(MSG_ERROR, "Frequency not present (seg0)"); + return 0; + } + + for (i = 0; i < chan_num; i++) { + int freq = start_freq + i * 20; + + err = hostapd_is_usable_chan(iface, freq, freq == iface->freq); + if (err <= 0) { + wpa_printf(MSG_ERROR, + "Frequency %d is not allowed (seg0)", + freq); + return err; + } + } + + if (oper_chwidth != CONF_OPER_CHWIDTH_80P80MHZ) + return 1; + + central = hostapd_get_oper_centr_freq_seg1_idx(iface->conf); + start_chan = central - chan_num * 2 + 2; + start_freq = hw_get_freq(iface->current_mode, start_chan); + + if (!start_freq) { + wpa_printf(MSG_ERROR, "Frequency not present (seg1)"); + return 0; + } + + for (i = 0; i < chan_num; i++) { + int freq = start_freq + i * 20; + + err = hostapd_is_usable_chan(iface, freq, freq == iface->freq); + if (err <= 0) { + wpa_printf(MSG_ERROR, + "Frequency %d is not allowed (seg1)", + freq); + return err; + } + } + + return 1; +} + + /* Returns: * 1 = usable * 0 = not usable * -1 = not currently usable due to 6 GHz NO-IR */ -static int hostapd_is_usable_chans(struct hostapd_iface *iface) +int hostapd_is_usable_chans(struct hostapd_iface *iface) { - int secondary_freq; + int secondary_freq, new_sec; + enum hostapd_chan_width_attr bw_flag; struct hostapd_channel_data *pri_chan; - int err, err2; + int err, err2, central, oper_chwidth; if (!iface->current_mode) return 0; @@ -1025,11 +1178,6 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) return 0; } - err = hostapd_is_usable_chan(iface, pri_chan->freq, 1); - if (err <= 0) { - wpa_printf(MSG_ERROR, "Primary frequency not allowed"); - return err; - } err = hostapd_is_usable_edmg(iface); if (err <= 0) return err; @@ -1037,6 +1185,24 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) if (!hostapd_is_usable_punct_bitmap(iface)) return 0; + oper_chwidth = hostapd_get_oper_chwidth(iface->conf); + + /* + * The central channel is not filled in acs_adjust_center_freq() after + * ACS of 80+80 MHz. In that case we only check the primary 40 MHz. + */ + central = hostapd_get_oper_centr_freq_seg0_idx(iface->conf); + + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + oper_chwidth != CONF_OPER_CHWIDTH_USE_HT && central) + return hostapd_is_usable_chan_seg(iface); + + err = hostapd_is_usable_chan(iface, pri_chan->freq, 1); + if (err <= 0) { + wpa_printf(MSG_ERROR, "Primary frequency not allowed"); + return err; + } + if (!iface->conf->secondary_channel) return 1; @@ -1053,18 +1219,19 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) if (!iface->conf->ht40_plus_minus_allowed) return err; - /* Both HT40+ and HT40- are set, pick a valid secondary channel */ - secondary_freq = iface->freq + 20; - err2 = hostapd_is_usable_chan(iface, secondary_freq, 0); - if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { - iface->conf->secondary_channel = 1; - return 1; - } + /* Both HT40+ and HT40- are set, check the swapped pri/sec channel + * option for a 40 MHz channel */ + new_sec = -iface->conf->secondary_channel; + bw_flag = new_sec == 1 ? + HOSTAPD_CHAN_WIDTH_40P : HOSTAPD_CHAN_WIDTH_40M; - secondary_freq = iface->freq - 20; + secondary_freq = iface->freq + new_sec * 20; err2 = hostapd_is_usable_chan(iface, secondary_freq, 0); - if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { - iface->conf->secondary_channel = -1; + if (err2 <= 0) + return err; + + if (pri_chan->allowed_bw & bw_flag) { + iface->conf->secondary_channel = new_sec; return 1; } @@ -1391,3 +1558,34 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface, } return 0; } + + +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info) +{ + os_free(multi_hw_info); +} + + +int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq) +{ + struct hostapd_multi_hw_info *hw_info; + unsigned int i; + + if (!iface->num_multi_hws) + return 0; + + for (i = 0; i < iface->num_multi_hws; i++) { + hw_info = &iface->multi_hw_info[i]; + + if (hw_info->start_freq <= oper_freq && + hw_info->end_freq >= oper_freq) { + iface->current_hw_info = hw_info; + wpa_printf(MSG_DEBUG, + "Mode: Selected underlying hardware: hw_idx=%u", + iface->current_hw_info->hw_idx); + return 0; + } + } + + return -1; +} diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h index c682c6d20b81..4993e68afd62 100644 --- a/contrib/wpa/src/ap/hw_features.h +++ b/contrib/wpa/src/ap/hw_features.h @@ -24,12 +24,15 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); int hostapd_check_edmg_capab(struct hostapd_iface *iface); int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface); -int hostapd_prepare_rates(struct hostapd_iface *iface, +int hostapd_prepare_rates(struct hostapd_data *hapd, struct hostapd_hw_modes *mode); void hostapd_stop_setup_timers(struct hostapd_iface *iface); int hostapd_hw_skip_mode(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); int hostapd_determine_mode(struct hostapd_iface *iface); +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info); +int hostapd_set_current_hw_info(struct hostapd_iface *iface, int oper_freq); +int hostapd_is_usable_chans(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -77,7 +80,7 @@ static inline int hostapd_check_edmg_capab(struct hostapd_iface *iface) return 0; } -static inline int hostapd_prepare_rates(struct hostapd_iface *iface, +static inline int hostapd_prepare_rates(struct hostapd_data *hapd, struct hostapd_hw_modes *mode) { return 0; @@ -103,6 +106,21 @@ static inline int hostapd_determine_mode(struct hostapd_iface *iface) return 0; } +static inline +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info) +{ +} + +static inline int hostapd_set_current_hw_info(struct hostapd_iface *iface, + u32 oper_freq) +{ + return 0; +} + +static inline int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + return 1; +} #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index 6c516bc8a93d..dbba14cd55be 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -1,6 +1,7 @@ /* * hostapd / IEEE 802.11 Management * Copyright (c) 2002-2017, Jouni Malinen + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +18,8 @@ #include "crypto/sha384.h" #include "crypto/sha512.h" #include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" @@ -26,6 +29,7 @@ #include "common/wpa_common.h" #include "common/wpa_ctrl.h" #include "common/ptksa_cache.h" +#include "common/nan_de.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -86,6 +90,10 @@ static void handle_auth(struct hostapd_data *hapd, int rssi, int from_queue); static int add_associated_sta(struct hostapd_data *hapd, struct sta_info *sta, int reassoc); +#ifdef CONFIG_IEEE8021X_AUTH +static struct rsn_pmksa_cache_entry * +pmksa_cache_search(void *ctx, const u8 *spa, const u8 *pmkid, bool is_ml); +#endif /* CONFIG_IEEE8021X_AUTH */ static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len) @@ -116,68 +124,67 @@ static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len) } -u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) +static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf) { - u8 *pos = eid; - int i, num, count; - int h2e_required; + u8 *pos = buf; + int i; - if (hapd->iface->current_rates == NULL) - return eid; + if (!hapd->current_rates) + return 0; - *pos++ = WLAN_EID_SUPP_RATES; - num = hapd->iface->num_rates; - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) - num++; - if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) - num++; -#ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) - num++; -#endif /* CONFIG_IEEE80211AX */ - h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || - hostapd_sae_pw_id_in_use(hapd->conf) == 2) && - hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && - wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); - if (h2e_required) - num++; - if (num > 8) { - /* rest of the rates are encoded in Extended supported - * rates element */ - num = 8; - } - - *pos++ = num; - for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; - i++) { - count++; - *pos = hapd->iface->current_rates[i].rate / 5; - if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + for (i = 0; i < hapd->num_rates; i++) { + *pos = hapd->current_rates[i].rate / 5; + if (hapd->current_rates[i].flags & HOSTAPD_RATE_BASIC) *pos |= 0x80; pos++; } - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { - count++; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; - } - if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { - count++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; - } #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) { - count++; + if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY; - } #endif /* CONFIG_IEEE80211AX */ - if (h2e_required && count < 8) { - count++; +#ifdef CONFIG_IEEE80211BE + if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be && + (hapd->iconf->require_eht || hapd->conf->bss_require_eht)) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY; +#endif /* CONFIG_IEEE80211BE */ + +#ifdef CONFIG_SAE + if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || + hostapd_sae_pw_id_in_use(hapd->conf) == 2) && + hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && + wpa_key_mgmt_only_sae(hapd->conf->wpa_key_mgmt)) *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; - } +#endif /* CONFIG_SAE */ + + return pos - buf; +} + + +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 buf[100]; + size_t len; + + len = hostapd_supp_rates(hapd, buf); + if (len == 0) + return eid; + /* Only up to first eight values in this element */ + if (len > 8) + len = 8; + + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = len; + os_memcpy(pos, buf, len); + pos += len; return pos; } @@ -186,72 +193,19 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; - int i, num, count; - int h2e_required; + u8 buf[100]; + size_t len; - hapd->conf->xrates_supported = false; - if (hapd->iface->current_rates == NULL) + len = hostapd_supp_rates(hapd, buf); + /* Starting from the 9th value for this element */ + if (len <= 8) return eid; - num = hapd->iface->num_rates; - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) - num++; - if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) - num++; -#ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) - num++; -#endif /* CONFIG_IEEE80211AX */ - h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || - hostapd_sae_pw_id_in_use(hapd->conf) == 2) && - hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && - wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt); - if (h2e_required) - num++; - if (num <= 8) - return eid; - num -= 8; - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = num; - for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; - i++) { - count++; - if (count <= 8) - continue; /* already in SuppRates IE */ - *pos = hapd->iface->current_rates[i].rate / 5; - if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) - *pos |= 0x80; - pos++; - } + *pos++ = len - 8; + os_memcpy(pos, &buf[8], len - 8); + pos += len - 8; - if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { - count++; - if (count > 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; - } - - if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { - count++; - if (count > 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; - } - -#ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) { - count++; - if (count > 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY; - } -#endif /* CONFIG_IEEE80211AX */ - - if (h2e_required) { - count++; - if (count > 8) - *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; - } - - hapd->conf->xrates_supported = true; return pos; } @@ -308,11 +262,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) if (hapd->conf->wpa) privacy = 1; -#ifdef CONFIG_HS20 - if (hapd->conf->osen) - privacy = 1; -#endif /* CONFIG_HS20 */ - if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; @@ -414,18 +363,36 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; const u8 *sa = hapd->own_addr; struct wpabuf *ml_resp = NULL; + size_t ml_resp_len = 0; +#ifdef CONFIG_IEEE8021X_AUTH + size_t mic_len = 0; +#endif /* CONFIG_IEEE8021X_AUTH */ #ifdef CONFIG_IEEE80211BE if (ap_sta_is_mld(hapd, sta)) { ml_resp = hostapd_ml_auth_resp(hapd); if (!ml_resp) return -1; + ml_resp_len = wpabuf_len(ml_resp); } #endif /* CONFIG_IEEE80211BE */ - rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; - if (ml_resp) - rlen += wpabuf_len(ml_resp); + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len + ml_resp_len; +#ifdef CONFIG_IEEE8021X_AUTH + /* Add MIC element for an Authentication frame carrying an EAP-Success + * message and for an Authentication frame with transaction sequence + * frame 2, if PMKSA caching was used. + */ + if (auth_alg == WLAN_AUTH_802_1X && + (resp == WLAN_STATUS_802_1_X_AUTH_SUCCESS || + sta->eap_auth_data.add_mic)) { + mic_len = wpa_mic_len(sta->eap_auth_data.akm, + sta->eap_auth_data.pmk_len, + RSN_HASH_NOT_SPECIFIED); + rlen += 2 + mic_len; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + buf = os_zalloc(rlen); if (!buf) { wpabuf_free(ml_resp); @@ -462,7 +429,7 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_SAE if (hapd->conf->sae_confirm_immediate == 2 && auth_alg == WLAN_AUTH_SAE) { - if (auth_transaction == 1 && sta && + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && sta && (resp == WLAN_STATUS_SUCCESS || resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT || resp == WLAN_STATUS_SAE_PK)) { @@ -474,7 +441,8 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } - if (auth_transaction == 2 && sta && sta->sae_postponed_commit) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM && + sta && sta->sae_postponed_commit) { wpa_printf(MSG_DEBUG, "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm"); if (hostapd_drv_send_mlme(hapd, @@ -489,6 +457,59 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_SAE */ #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_IEEE8021X_AUTH + if (auth_alg == WLAN_AUTH_802_1X && + (resp == WLAN_STATUS_802_1_X_AUTH_SUCCESS || + sta->eap_auth_data.add_mic)) { + const u8 *frame, *data, *rsne, *rsnxe; + u8 data_buf[500], mic[WPA_1X_MAX_MIC_LEN]; + size_t frame_len, data_len; + const u8 *aa = sa; + u8 *ptr = reply->u.auth.variable + ies_len + ml_resp_len; + +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta)) + aa = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + + rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN); + if (!rsne) { + wpa_printf(MSG_INFO, "No AP RSNE"); + return -1; + } + os_memcpy(data_buf, rsne, 2 + rsne[1]); + data_len = 2 + rsne[1]; + + rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX); + if (rsnxe) { + wpa_printf(MSG_DEBUG, "Found AP RSNXE"); + os_memcpy(&data_buf[data_len], rsnxe, 2 + rsnxe[1]); + data_len += 2 + rsnxe[1]; + } else { + wpa_printf(MSG_DEBUG, "No AP RSNXE"); + } + data = data_buf; + + /* MIC element */ + ptr[0] = WLAN_EID_MIC; + ptr[1] = mic_len; + os_memset(ptr + 2, 0, mic_len); + + frame = (const u8 *) &reply->u.auth.auth_alg; + frame_len = rlen - IEEE80211_HDRLEN; + if (wpa_auth_8021x_mic(sta->eap_auth_data.akm, + sta->eap_auth_data.ptk.kck, + sta->eap_auth_data.ptk.kck_len, + aa, sta->addr, data, data_len, + frame, frame_len, mic)) { + wpa_printf(MSG_INFO, "Failed to derive MIC"); + return -1; + } + os_memcpy(ptr + 2, mic, mic_len); + } +#endif /* CONFIF_IEEE8021X_AUTH */ + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else @@ -500,6 +521,26 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_IEEE8021X_AUTH +static void send_8021x_auth_reply(struct hostapd_data *hapd, + struct sta_info *sta, + u16 auth_transaction, u16 resp, + struct wpabuf *ies) +{ + send_auth_reply(hapd, sta, sta->addr, WLAN_AUTH_802_1X, + auth_transaction, resp, wpabuf_head(ies), + wpabuf_len(ies), "send-8021x-auth-reply"); + + if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS && + resp != WLAN_STATUS_802_1_X_AUTH_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + wpabuf_free(ies); +} +#endif /* IEEE8021X_AUTH */ + + #ifdef CONFIG_IEEE80211R_AP static void handle_auth_ft_finish(void *ctx, const u8 *dst, u16 auth_transaction, u16 status, @@ -547,9 +588,178 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state, } +static bool in_mac_addr_list(const u8 *list, unsigned int num, const u8 *addr) +{ + unsigned int i; + + for (i = 0; list && i < num; i++) { + if (ether_addr_equal(&list[i * ETH_ALEN], addr)) + return true; + } + + return false; +} + + +static struct sae_password_entry * +sae_password_find_pw(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sae_password_entry *pw = NULL; + + if (!sta->sae || !sta->sae->tmp || !sta->sae->tmp->used_pw) + return NULL; + + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (pw == sta->sae->tmp->used_pw) + return pw; + } + + return NULL; +} + + +static bool is_other_sae_password(struct hostapd_data *hapd, + struct sta_info *sta, + struct sae_password_entry *used_pw) +{ + struct sae_password_entry *pw; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (pw == used_pw || + pw->identifier || + !is_broadcast_ether_addr(pw->peer_addr)) + continue; + + if (in_mac_addr_list(pw->success_mac, + pw->num_success_mac, + sta->addr)) + return true; + + if (!in_mac_addr_list(pw->fail_mac, pw->num_fail_mac, + sta->addr)) + return true; + } + + return false; +} + + +static bool has_sae_success_seen(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct sae_password_entry *pw; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier || + !is_broadcast_ether_addr(pw->peer_addr)) + continue; + + if (in_mac_addr_list(pw->success_mac, + pw->num_success_mac, + sta->addr)) + return true; + } + + return false; +} + + +static int sae_password_mark_success(struct hostapd_data *hapd, + struct sae_password_entry *pw, + const u8 *addr) +{ + if (in_mac_addr_list(pw->success_mac, pw->num_success_mac, addr)) + return 0; + + if (!pw->success_mac) { + pw->success_mac = os_zalloc(hapd->conf->sae_track_password * + ETH_ALEN); + if (!pw->success_mac) + return -1; + pw->num_success_mac = hapd->conf->sae_track_password; + } + + os_memcpy(&pw->success_mac[pw->next_success_mac * ETH_ALEN], addr, + ETH_ALEN); + pw->next_success_mac = (pw->next_success_mac + 1) % pw->num_success_mac; + return 0; +} + + +static void sae_password_track_success(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct sae_password_entry *pw; + + if (!hapd->conf->sae_track_password) + return; + + pw = sae_password_find_pw(hapd, sta); + if (!pw) + return; + + sae_password_mark_success(hapd, pw, sta->addr); +} + + +static bool sae_password_track_fail(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct sae_password_entry *pw; + + if (!hapd->conf->sae_track_password) + return false; + + pw = sae_password_find_pw(hapd, sta); + if (!pw) + return false; + + if (in_mac_addr_list(pw->fail_mac, + pw->num_fail_mac, + sta->addr)) + return is_other_sae_password(hapd, sta, pw); + + if (!pw->fail_mac) { + pw->fail_mac = os_zalloc(hapd->conf->sae_track_password * + ETH_ALEN); + if (!pw->fail_mac) + return false; + pw->num_fail_mac = hapd->conf->sae_track_password; + } + + os_memcpy(&pw->fail_mac[pw->next_fail_mac * ETH_ALEN], sta->addr, + ETH_ALEN); + pw->next_fail_mac = (pw->next_fail_mac + 1) % pw->num_fail_mac; + + return is_other_sae_password(hapd, sta, pw); +} + + +int sae_password_bind(struct hostapd_data *hapd, const u8 *addr, + const char *password) +{ + struct sae_password_entry *pw; + + if (!hapd->conf->sae_track_password) + return -1; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier || + !is_broadcast_ether_addr(pw->peer_addr) || + os_strcmp(password, pw->password) != 0) + continue; + + return sae_password_mark_success(hapd, pw, addr); + } + + return -1; +} + + const char * sae_get_password(struct hostapd_data *hapd, struct sta_info *sta, - const char *rx_id, + const u8 *rx_id, size_t rx_id_len, struct sae_password_entry **pw_entry, struct sae_pt **s_pt, const struct sae_pk **s_pk) @@ -560,6 +770,45 @@ const char * sae_get_password(struct hostapd_data *hapd, const struct sae_pk *pk = NULL; struct hostapd_sta_wpa_psk_short *psk = NULL; + /* With sae_track_password functionality enabled, try to first find the + * next viable wildcard-address password if a password identifier was + * not used. Select an wildcard-addr entry if the STA is known to have + * used it successfully before. If no such entry exists, pick a + * wildcard-addr entry that does not have a failed entry tracked for the + * STA. */ + if (!rx_id && sta && hapd->conf->sae_track_password) { + struct sae_password_entry *success = NULL, *no_fail = NULL; + + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier || + !is_broadcast_ether_addr(pw->peer_addr)) + continue; + if (in_mac_addr_list(pw->success_mac, + pw->num_success_mac, + sta->addr)) { + success = pw; + break; + } + + if (!no_fail && + !in_mac_addr_list(pw->fail_mac, pw->num_fail_mac, + sta->addr)) + no_fail = pw; + } + + pw = success ? success : no_fail; + if (pw) { + password = pw->password; + pt = pw->pt; + if (!(hapd->conf->mesh & MESH_ENABLED)) + pk = pw->pk; + goto found; + } + } + + /* If sae_track_password functionality is not enabled or no suitable + * password entry was found with it, pick the first entry that matches + * the STA MAC address and password identifier (if used). */ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { if (!is_broadcast_ether_addr(pw->peer_addr) && (!sta || @@ -568,7 +817,8 @@ const char * sae_get_password(struct hostapd_data *hapd, if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) continue; if (rx_id && pw->identifier && - os_strcmp(rx_id, pw->identifier) != 0) + (rx_id_len != os_strlen(pw->identifier) || + os_memcmp(rx_id, pw->identifier, rx_id_len) != 0)) continue; password = pw->password; pt = pw->pt; @@ -576,12 +826,12 @@ const char * sae_get_password(struct hostapd_data *hapd, pk = pw->pk; break; } - if (!password) { + if (!password && !rx_id && !hapd->conf->sae_password_psk) { password = hapd->conf->ssid.wpa_passphrase; pt = hapd->conf->ssid.pt; } - if (!password && sta) { + if (!password && sta && !rx_id) { for (psk = sta->psk; psk; psk = psk->next) { if (psk->is_passphrase) { password = psk->passphrase; @@ -590,6 +840,74 @@ const char * sae_get_password(struct hostapd_data *hapd, } } + /* Try to decrypt the received password identifier if no plaintext + * identifier match was found. */ + if (!password && rx_id && rx_id_len > 4 + 4 + AES_BLOCK_SIZE && + hapd->conf->sae_pw_id_key) { + u8 *plain, *pos, *counter; + size_t plain_len; + const u8 *id; + size_t id_len; + + plain = os_malloc(rx_id_len); + if (!plain) + goto fail; + if (aes_siv_decrypt( + wpabuf_head(hapd->conf->sae_pw_id_key), + wpabuf_len(hapd->conf->sae_pw_id_key), + rx_id, rx_id_len, 0, NULL, NULL, plain) < 0) + goto fail; + plain_len = rx_id_len - AES_BLOCK_SIZE; + wpa_hexdump_ascii(MSG_DEBUG, + "SAE: Decrypted password identifier info", + plain, plain_len); + /* 4 octet date | Password ID | | 4 octet counter */ + counter = plain + plain_len - 4; + wpa_printf(MSG_DEBUG, "SAE: Generation time %u counter %u", + WPA_GET_BE32(plain), WPA_GET_BE32(counter)); + id = pos = plain + 4; + while (pos < counter) { + if (*pos == 0x00) + break; + pos++; + } + id_len = pos - id; + wpa_hexdump_ascii(MSG_DEBUG, + "SAE: Decrypted password identifier", + id, id_len); + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + (!sta || + !ether_addr_equal(pw->peer_addr, sta->addr))) + continue; + if (!pw->identifier || + os_strlen(pw->identifier) != id_len || + os_memcmp(id, pw->identifier, id_len) != 0) + continue; + password = pw->password; + if (!(hapd->conf->mesh & MESH_ENABLED)) + pk = pw->pk; + if (sta && sta->sae && sta->sae->tmp) { + os_free(sta->sae->tmp->dec_pw_id); + sta->sae->tmp->dec_pw_id = + os_zalloc(id_len + 1); + if (sta->sae->tmp->dec_pw_id) { + os_memcpy(sta->sae->tmp->dec_pw_id, + id, id_len); + sta->sae->tmp->dec_pw_id_len = id_len; + sta->sae->tmp->pw_id_counter = + WPA_GET_BE32(counter); + wpa_printf(MSG_DEBUG, + "SAE: Bound decrypted password identifier to STA"); + } + } + break; + } + fail: + os_free(plain); + } + +found: if (pw_entry) *pw_entry = pw; if (s_pt) @@ -608,7 +926,8 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct wpabuf *buf; const char *password = NULL; struct sae_password_entry *pw; - const char *rx_id = NULL; + const u8 *rx_id = NULL; + size_t rx_id_len = 0; int use_pt = 0; struct sae_pt *pt = NULL; const struct sae_pk *pk = NULL; @@ -620,7 +939,11 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211BE */ if (sta->sae->tmp) { - rx_id = sta->sae->tmp->pw_id; + rx_id = sta->sae->tmp->parsed_pw_id ? + sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id; + rx_id_len = sta->sae->tmp->parsed_pw_id ? + sta->sae->tmp->parsed_pw_id_len : + sta->sae->tmp->pw_id_len; use_pt = sta->sae->h2e; #ifdef CONFIG_SAE_PK os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN); @@ -636,16 +959,49 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, status_code == WLAN_STATUS_SAE_PK) use_pt = 1; - password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk); - if (!password || (use_pt && !pt)) { + password = sae_get_password(hapd, sta, rx_id, rx_id_len, &pw, &pt, &pk); + if (!password) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } - if (update && use_pt && - sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr, - NULL, pk) < 0) - return NULL; + if (use_pt) { + struct sae_pt *tmp_pt = NULL; + bool failed = false; + + if (!pt && pw) { + int groups[2] = { sta->sae->group, 0 }; + + wpa_printf(MSG_DEBUG, + "SAE: Derive PT for encrypted PW ID"); + tmp_pt = sae_derive_pt(groups, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, + (const u8 *) pw->password, + os_strlen(pw->password), + rx_id, rx_id_len); + if (!tmp_pt) { + wpa_printf(MSG_DEBUG, + "SAE: Could not derive PT"); + return NULL; + } + pt = tmp_pt; + update = 1; + } + + if (!pt) { + wpa_printf(MSG_DEBUG, "SAE: No PT available"); + return NULL; + } + + if (update && + sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr, + NULL, pk) < 0) + failed = true; + + sae_deinit_pt(tmp_pt); + if (failed) + return NULL; + } if (update && !use_pt && sae_prepare_commit(own_addr, sta->addr, @@ -655,6 +1011,9 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, return NULL; } + if (pw && sta->sae->tmp) + sta->sae->tmp->used_pw = pw; + if (pw && pw->vlan_id) { if (!sta->sae->tmp) { wpa_printf(MSG_INFO, @@ -665,11 +1024,11 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, } buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + - (rx_id ? 3 + os_strlen(rx_id) : 0)); + (rx_id ? 3 + rx_id_len : 0)); if (buf && sae_write_commit(sta->sae, buf, sta->sae->tmp ? sta->sae->tmp->anti_clogging_token : NULL, - rx_id) < 0) { + rx_id, rx_id_len) < 0) { wpabuf_free(buf); buf = NULL; } @@ -712,7 +1071,8 @@ static int auth_sae_send_commit(struct hostapd_data *hapd, u16 status; data = auth_build_sae_commit(hapd, sta, update, status_code); - if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) + if (!data && sta->sae->tmp && + (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id)) return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -927,41 +1287,52 @@ static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, } -void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +static int sae_assign_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int vlan_id) { #ifndef CONFIG_NO_VLAN struct vlan_description vlan_desc; - if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { + if (vlan_id > 0) { wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR " to VLAN ID %d", - MAC2STR(sta->addr), sta->sae->tmp->vlan_id); + MAC2STR(sta->addr), vlan_id); if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) { os_memset(&vlan_desc, 0, sizeof(vlan_desc)); vlan_desc.notempty = 1; - vlan_desc.untagged = sta->sae->tmp->vlan_id; + vlan_desc.untagged = vlan_id; if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { wpa_printf(MSG_INFO, "Invalid VLAN ID %d in sae_password", - sta->sae->tmp->vlan_id); - return; + vlan_id); + return -1; } if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || ap_sta_bind_vlan(hapd, sta) < 0) { wpa_printf(MSG_INFO, "Failed to assign VLAN ID %d from sae_password to " - MACSTR, sta->sae->tmp->vlan_id, + MACSTR, vlan_id, MAC2STR(sta->addr)); - return; + return -1; } } else { - sta->vlan_id = sta->sae->tmp->vlan_id; + sta->vlan_id = vlan_id; } } #endif /* CONFIG_NO_VLAN */ + return 0; +} + + +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->sae->tmp && + sae_assign_vlan(hapd, sta, sta->sae->tmp->vlan_id) < 0) + return; + sta->flags |= WLAN_STA_AUTH; sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); @@ -972,8 +1343,23 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) sta->sae->peer_commit_scalar = NULL; wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmk_len, - sta->sae->pmkid, sta->sae->akmp); + sta->sae->pmkid, sta->sae->akmp, + ap_sta_is_mld(hapd, sta), sta->vlan_id); sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); + if (sta->sae->tmp) { + struct sae_temporary_data *tmp = sta->sae->tmp; + + wpabuf_free(sta->sae_pw_id); + sta->sae_pw_id = NULL; + if (tmp->dec_pw_id) { + sta->sae_pw_id = wpabuf_alloc_copy( + tmp->dec_pw_id, tmp->dec_pw_id_len); + sta->sae_pw_id_counter = tmp->pw_id_counter; + } else if (tmp->pw_id) { + sta->sae_pw_id = wpabuf_alloc_copy( + tmp->pw_id, tmp->pw_id_len); + } + } } @@ -985,14 +1371,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, *sta_removed = 0; - if (auth_transaction != 1 && auth_transaction != 2) + if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT && + auth_transaction != WLAN_AUTH_TR_SEQ_SAE_CONFIRM) return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u", MAC2STR(sta->addr), sae_state_txt(sta->sae->state), auth_transaction); - if (auth_transaction == 1 && sae_proto_instance_disabled(sta)) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && + sae_proto_instance_disabled(sta)) { wpa_printf(MSG_DEBUG, "SAE: Protocol instance temporarily disabled - discard received SAE commit"); return WLAN_STATUS_SUCCESS; @@ -1000,8 +1388,11 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, switch (sta->sae->state) { case SAE_NOTHING: - if (auth_transaction == 1) { - if (sta->sae->tmp) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { + struct sae_temporary_data *tmp = sta->sae->tmp; + bool immediate_confirm; + + if (tmp) { sta->sae->h2e = (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || @@ -1011,8 +1402,23 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, } ret = auth_sae_send_commit(hapd, sta, !allow_reuse, status_code); + if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER + MACSTR, MAC2STR(sta->addr)); if (ret) return ret; + + if (tmp && tmp->parsed_pw_id && !tmp->pw_id) { + tmp->pw_id = tmp->parsed_pw_id; + tmp->pw_id_len = tmp->parsed_pw_id_len; + tmp->parsed_pw_id = NULL; + tmp->parsed_pw_id_len = 0; + wpa_hexdump_ascii(MSG_DEBUG, + "SAE: Known Password Identifier bound to this STA", + tmp->pw_id, tmp->pw_id_len); + } + sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); if (sae_process_commit(sta->sae) < 0) @@ -1028,8 +1434,22 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * overridden with explicit configuration so that the * infrastructure BSS case sends both frames together. */ - if ((hapd->conf->mesh & MESH_ENABLED) || - hapd->conf->sae_confirm_immediate) { + immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) || + hapd->conf->sae_confirm_immediate; + + /* If sae_track_password is enabled and the STA has not + * yet been tracked to having successfully completed + * SAE authentication with the password that the AP + * tries to use, do not send Confirm immediately to + * avoid an explicit indication on the STA side on + * password mismatch. */ + if (immediate_confirm && + hapd->conf->sae_track_password && + (!sta->sae->tmp || !sta->sae->tmp->parsed_pw_id) && + !has_sae_success_seen(hapd, sta)) + immediate_confirm = false; + + if (immediate_confirm) { /* * Send both Commit and Confirm immediately * based on SAE finite state machine @@ -1061,7 +1481,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, break; case SAE_COMMITTED: sae_clear_retransmit_timer(hapd, sta); - if (auth_transaction == 1) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1108,7 +1528,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, break; case SAE_CONFIRMED: sae_clear_retransmit_timer(hapd, sta); - if (auth_transaction == 1) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -1131,7 +1551,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, } break; case SAE_ACCEPTED: - if (auth_transaction == 1 && + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && (hapd->conf->mesh & MESH_ENABLED)) { wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", @@ -1139,7 +1559,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); ap_free_sta(hapd, sta); *sta_removed = 1; - } else if (auth_transaction == 1) { + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ret = auth_sae_send_commit(hapd, sta, 1, status_code); if (ret) @@ -1173,16 +1593,23 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; - int i, *groups = hapd->conf->sae_groups; - int default_groups[] = { 19, 0 }; + struct hostapd_bss_config *conf = hapd->conf; + int i, *groups = conf->sae_groups; + int default_groups[] = { 19, 0, 0 }; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); - if (!groups) + if (!groups) { groups = default_groups; + if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2)) + default_groups[1] = 20; + } + for (i = 0; groups[i] > 0; i++) { if (sae->group == groups[i]) break; @@ -1247,12 +1674,18 @@ static int sae_status_success(struct hostapd_data *hapd, u16 status_code) static int sae_is_group_enabled(struct hostapd_data *hapd, int group) { - int *groups = hapd->conf->sae_groups; - int default_groups[] = { 19, 0 }; + struct hostapd_bss_config *conf = hapd->conf; + int *groups = conf->sae_groups; + int default_groups[] = { 19, 0, 0 }; int i; - if (!groups) + if (!groups) { groups = default_groups; + if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2)) + default_groups[1] = 20; + } for (i = 0; groups[i] > 0; i++) { if (groups[i] == group) @@ -1309,17 +1742,24 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, { int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; - int *groups = hapd->conf->sae_groups; - int default_groups[] = { 19, 0 }; + struct hostapd_bss_config *conf = hapd->conf; + int *groups = conf->sae_groups; + int default_groups[] = { 19, 0, 0 }; const u8 *pos, *end; int sta_removed = 0; bool success_status; - if (!groups) + if (!groups) { groups = default_groups; + if (wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt | + conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2)) + default_groups[1] = 20; + } #ifdef CONFIG_TESTING_OPTIONS - if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { + if (hapd->conf->sae_reflection_attack && + auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; @@ -1331,7 +1771,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto remove_sta; } - if (hapd->conf->sae_commit_override && auth_transaction == 1) { + if (hapd->conf->sae_commit_override && + auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); send_auth_reply(hapd, sta, sta->addr, WLAN_AUTH_SAE, @@ -1343,7 +1784,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { - if (auth_transaction != 1 || + if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT || !sae_status_success(hapd, status_code)) { wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u", status_code); @@ -1355,6 +1796,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = -1; goto remove_sta; } + if (!hostapd_sae_pw_id_in_use(hapd->conf)) + sta->sae->no_pw_id = 1; sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } @@ -1366,7 +1809,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sta->mesh_sae_pmksa_caching = 0; } - if (auth_transaction == 1) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { const u8 *token = NULL; size_t token_len = 0; int allow_reuse = 0; @@ -1456,7 +1899,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * previously set parameters. */ pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; - if (end - pos >= (int) sizeof(le16) && + if ((!sta->sae->tmp || + !sta->sae->tmp->try_other_password) && + end - pos >= (int) sizeof(le16) && sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)) == WLAN_STATUS_SUCCESS) { @@ -1493,6 +1938,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sae_clear_retransmit_timer(hapd, sta); sae_set_state(sta, SAE_NOTHING, "Unknown Password Identifier"); + if (sta->sae->state == SAE_NOTHING) + goto reply; goto remove_sta; } @@ -1544,7 +1991,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = sae_sm_step(hapd, sta, auth_transaction, status_code, allow_reuse, &sta_removed); - } else if (auth_transaction == 2) { + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "SAE authentication (RX confirm, status=%u (%s))", @@ -1580,9 +2027,21 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (sae_check_confirm(sta->sae, var, var_len, NULL) < 0) { + if (sae_password_track_fail(hapd, sta)) { + wpa_printf(MSG_DEBUG, + "SAE: Reject mismatching Confirm so that another password can be attempted by " + MACSTR, + MAC2STR(sta->addr)); + if (sta->sae->tmp) + sta->sae->tmp-> + try_other_password = 1; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } resp = WLAN_STATUS_CHALLENGE_FAIL; goto reply; } + sae_password_track_success(hapd, sta); sta->sae->rc = peer_send_confirm; } resp = sae_sm_step(hapd, sta, auth_transaction, @@ -1609,24 +2068,16 @@ reply: !data && end - pos >= 2) data = wpabuf_alloc_copy(pos, 2); - sae_sme_send_external_auth_status(hapd, sta, resp); send_auth_reply(hapd, sta, sta->addr, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0, "auth-sae"); - if (sta->sae && sta->sae->tmp && sta->sae->tmp->pw_id && - resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER && - auth_transaction == 1) { - wpa_printf(MSG_DEBUG, - "SAE: Clear stored password identifier since this SAE commit was not accepted"); - os_free(sta->sae->tmp->pw_id); - sta->sae->tmp->pw_id = NULL; - } + sae_sme_send_external_auth_status(hapd, sta, resp); } remove_sta: - if (auth_transaction == 1) + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) success_status = sae_status_success(hapd, status_code); else success_status = status_code == WLAN_STATUS_SUCCESS; @@ -1778,34 +2229,650 @@ static u16 wpa_res_to_status_code(enum wpa_validate_result res) case WPA_IE_OK: return WLAN_STATUS_SUCCESS; case WPA_INVALID_IE: - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; case WPA_INVALID_GROUP: - return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + return WLAN_STATUS_INVALID_GROUP_CIPHER; case WPA_INVALID_PAIRWISE: - return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + return WLAN_STATUS_INVALID_PAIRWISE_CIPHER; case WPA_INVALID_AKMP: - return WLAN_STATUS_AKMP_NOT_VALID; + return WLAN_STATUS_INVALID_AKMP; case WPA_NOT_ENABLED: - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; case WPA_ALLOC_FAIL: return WLAN_STATUS_UNSPECIFIED_FAILURE; case WPA_MGMT_FRAME_PROTECTION_VIOLATION: return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; case WPA_INVALID_MGMT_GROUP_CIPHER: - return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + return WLAN_STATUS_CIPHER_OUT_OF_POLICY; case WPA_INVALID_MDIE: - return WLAN_STATUS_INVALID_MDIE; + return WLAN_STATUS_INVALID_MDE; case WPA_INVALID_PROTO: - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; case WPA_INVALID_PMKID: return WLAN_STATUS_INVALID_PMKID; case WPA_DENIED_OTHER_REASON: return WLAN_STATUS_ASSOC_DENIED_UNSPEC; } - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } +#ifdef CONFIG_IEEE8021X_AUTH + +static struct wpabuf * +prepare_802_1x_auth_resp(struct hostapd_data *hapd, struct sta_info *sta, + u16 auth_transaction, u16 status, + struct rsn_pmksa_cache_entry *cached_pmk, + const u8 *eap_req, size_t eap_req_len) +{ + struct wpabuf *pub = NULL, *data; + bool enc_assoc = ap_sta_support_enc_assoc(hapd, + sta->eap_auth_data.rsnxe, + sta->eap_auth_data.rsnxe_len); + + data = wpabuf_alloc(1000 + eap_req_len); + if (!data) { + wpa_printf(MSG_INFO, + "Authentication frame buffer allocation failed"); + return NULL; + } + + /* Encapsulation Length field */ + wpabuf_put_le16(data, eap_req_len); + /* Encapsulation field */ + wpabuf_put_data(data, eap_req, eap_req_len); + + if (status != WLAN_STATUS_SUCCESS && + status != WLAN_STATUS_802_1_X_AUTH_SUCCESS) + goto reply; + + /* Authentication frames with transaction sequence greater than or + * equal to 3 contain Authentication fields only. + */ + if (auth_transaction == 2) { + /* Per IEEE 802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X), a responder + * that sets + * dot11EPPReAssociationFrameEncryptionSupportActivated + * to false or does not receive the RSNXE in the first + * Authentication frame with the (Re)Association Frame + * Encryption Support field set to 1 shall not include + * a Diffie-Hellman Parameter element nor a Nonce element + * nor an RSNE in the second Authentication frame for + * IEEE 802.1X authentication. + */ + if (enc_assoc) { + u8 a_nonce[WPA_NONCE_LEN]; + struct hostapd_bss_config *conf = hapd->conf; + int res; + + /* Derive own public key */ + if (sta->eap_auth_data.ecdh) { + pub = crypto_ecdh_get_pubkey( + sta->eap_auth_data.ecdh, 1); + if (!pub) { + status = + WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + } + + /* ANonce generation */ + if (random_get_bytes(a_nonce, WPA_NONCE_LEN) < 0) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + os_memcpy(sta->eap_auth_data.anonce, a_nonce, + WPA_NONCE_LEN); + + + if (pub && wpabuf_resize(&data, wpabuf_len(pub)) < 0) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + + /* Per IEEE 802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X), + * responder shall include an RSNE with the AKM and + * pairwise cipher suite as indicated in the first + * Authentication frame. + */ + res = wpa_write_802_1x_rsne( + hapd->wpa_auth, + wpabuf_mhead_u8(data) + wpabuf_len(data), + (wpabuf_size(data) - wpabuf_len(data)), + cached_pmk ? cached_pmk->pmkid : NULL, + sta->eap_auth_data.akm, + sta->eap_auth_data.cipher, + conf->wpa_group, + conf->group_mgmt_cipher, + conf->ieee80211w); + if (res < 0) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + wpabuf_put(data, res); + + /* DH Parameter element */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); + wpabuf_put_u8(data, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(data, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(data, sta->eap_auth_data.group); + wpabuf_put_buf(data, pub); + + /* ANonce in Nonce element */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); + wpabuf_put_u8(data, 1 + WPA_NONCE_LEN); + wpabuf_put_u8(data, WLAN_EID_EXT_NONCE); + wpabuf_put_data(data, a_nonce, WPA_NONCE_LEN); + } else { + /* Per IEEE 802.11bi/D4.0, 12.16.5 (IEEE 802.1X + * authentication utilizing Authentication frames), the + * Responser shall construct the second Authentication + * frame with an AKM Suite Selector element indicating + * the same IEEE 802.1X AKM indicated in the first + * Authentication frame. + */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); + wpabuf_put_u8(data, 1 + RSN_SELECTOR_LEN); + wpabuf_put_u8(data, WLAN_EID_EXT_AKM_SUITE_SELECTOR); + RSN_SELECTOR_PUT(wpabuf_put(data, RSN_SELECTOR_LEN), + wpa_akm_to_suite( + sta->eap_auth_data.akm)); + } + } /* if (auth_transaction == 2) */ +reply: + wpabuf_free(pub); + return data; +} + + +u16 wpa_auth_validate_802_1x_frame(struct hostapd_data *hapd, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + struct wpa_ie_data rsn; + const int default_groups[] = { 19, 0 }; + bool enc_assoc = ap_sta_support_enc_assoc(hapd, + elems->rsnxe, + elems->rsnxe_len); + + /* Per IEEE P802.11bi/D4.0, 12.16.8.3 (IEEE 802.1X), an originator that + * sets dot11EPPReAssociationFrameEncryptionSupportActivated to false or + * does not receive the RSNXE from the responder with the + * (Re)Association Frame Encryption Support field set to 1 shall not + * include a Diffie-Hellman Parameter element nor an RSNE nor an RSNXE + * nor a Nonce element in the first Authentication frame for IEEE 802.1X + * authentication. + */ + if (!enc_assoc && + (elems->rsn_ie || elems->nonce || elems->owe_dh)) { + wpa_printf(MSG_INFO, + "Invalid inclusion of RSNE/Nonce/DHE when (Re)Association frame encryption is not supported"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (enc_assoc && + (!elems->rsn_ie || !elems->nonce || + elems->nonce_len != WPA_NONCE_LEN || !elems->owe_dh)) { + wpa_printf(MSG_ERROR, "Missing RSNE/DHIE/Nonce"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* Both RSNE and AKM Suite Selector element shall not be present at the + * same time. */ + if (elems->rsn_ie && elems->akm_suite_selector) { + wpa_printf(MSG_INFO, + "Incorrect inclusion of both RSNE and AKM Suite Selector element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (enc_assoc && + (!elems->rsn_ie || + wpa_parse_wpa_ie_rsn(elems->rsn_ie - 2, elems->rsn_ie_len + 2, + &rsn) < 0)) { + wpa_printf(MSG_INFO, "No valid RSNE"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (enc_assoc && elems->rsn_ie) { + if (!(rsn.pairwise_cipher & hapd->conf->rsn_pairwise)) { + wpa_printf(MSG_INFO, + "Invalid pairwise cipher (0x%x) in RSNE", + rsn.pairwise_cipher); + return WPA_INVALID_PAIRWISE; + } + sta->eap_auth_data.cipher = rsn.pairwise_cipher; + wpa_printf(MSG_DEBUG, "Received pairwise cipher (0x%x) in RSNE", + rsn.pairwise_cipher); + + if (!(rsn.key_mgmt & hapd->conf->wpa_key_mgmt)) { + wpa_printf(MSG_INFO, "Invalid key mgmt (0x%x) in RSNE", + rsn.key_mgmt); + return WPA_INVALID_AKMP; + } + sta->eap_auth_data.akm = rsn.key_mgmt; + wpa_printf(MSG_DEBUG, "Received keymgmt (0x%x) in RSNE", + rsn.key_mgmt); + } + + /* Validate AKM Suite Selector element */ + if (elems->akm_suite_selector) { + sta->eap_auth_data.akm = rsn_key_mgmt_to_wpa_akm( + RSN_SELECTOR_GET(elems->akm_suite_selector)); + if (!(sta->eap_auth_data.akm & hapd->conf->wpa_key_mgmt)) { + wpa_printf(MSG_INFO, + "Invalid key mgmt (0x%x) in AKM Suite Selector element", + sta->eap_auth_data.akm); + return WPA_INVALID_AKMP; + } + wpa_printf(MSG_DEBUG, + "Received keymgmt (0x%x) in AKM Suite Selector element", + sta->eap_auth_data.akm); + } + + if (elems->rsnxe) { + os_free(sta->eap_auth_data.rsnxe); + sta->eap_auth_data.rsnxe = + os_memdup(elems->rsnxe, elems->rsnxe_len); + sta->eap_auth_data.rsnxe_len = elems->rsnxe_len; + } + + /* Store SNonce */ + if (elems->nonce && elems->nonce_len == WPA_NONCE_LEN) { + os_memcpy(sta->eap_auth_data.snonce, elems->nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "SNonce", elems->nonce, WPA_NONCE_LEN); + } + + /* Validate DH Parameter element */ + if (elems->owe_dh) { + u16 group; + u8 pubkey_len; + const u8 *pubkey; + struct wpabuf *secret; + + group = WPA_GET_LE16(elems->owe_dh); + if (!int_array_includes(default_groups, group)) { + wpa_printf(MSG_INFO, + "Received unsupported group value %u", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + sta->eap_auth_data.group = group; + pubkey = elems->owe_dh + 2; + pubkey_len = elems->owe_dh_len - 2; + + /* TODO: Any more validation of peer public key needed? */ + if (!pubkey_len) { + wpa_printf(MSG_INFO, "Missing DH public key"); + return WLAN_STATUS_INVALID_PUBLIC_KEY; + } + + /* Setup ECDH context */ + crypto_ecdh_deinit(sta->eap_auth_data.ecdh); + sta->eap_auth_data.ecdh = crypto_ecdh_init(group); + if (!sta->eap_auth_data.ecdh) { + wpa_printf(MSG_INFO, "Failed to setup ECDH context"); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + /* Generate shared secret */ + wpabuf_clear_free(sta->eap_auth_data.dhss); + sta->eap_auth_data.dhss = NULL; + secret = crypto_ecdh_set_peerkey(sta->eap_auth_data.ecdh, 0, + pubkey, pubkey_len); + if (!secret) { + wpa_printf(MSG_INFO, "Invalid peer public key"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DH shared secret", secret); + sta->eap_auth_data.dhss = secret; + } + + return WLAN_STATUS_SUCCESS; +} + + +/** + * ieee80211_send_eap_req - Callback function to send EAP-Request message in an + * Authentication frame + * + * This function is called from ieee802_1x_eapol_send() using the + * hapd->send_eap_req callback. Its main purpose is to prepend the EAP-Request + * data with an IEEE 802.1X header and call prepare_802_1x_auth_resp() to send + * out the next IEEE 802.1X Authentication frame to the station. If this is an + * EAP-Success frame, it also fetches the MSK derived from the successful EAP + * authentication to derive PMK and PTK and configure the TK to the driver. + */ +void ieee80211_send_eap_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, u16 auth_transaction, u16 status, + struct rsn_pmksa_cache_entry *cached_pmk, + const u8 *eap_req, size_t eap_req_len) +{ + bool enc_assoc = ap_sta_support_enc_assoc(hapd, + sta->eap_auth_data.rsnxe, + sta->eap_auth_data.rsnxe_len); + struct ieee802_1x_hdr *xhdr; + u8 *data; + size_t len = eap_req_len + sizeof(struct ieee802_1x_hdr); + struct wpabuf *reply; + + wpa_printf(MSG_DEBUG, + "Process EAP-Request data for TX using an Authentication frame"); + + data = os_malloc(len); + if (!data) { + wpa_printf(MSG_ERROR, "malloc() failed for %s", __func__); + return; + } + + xhdr = (struct ieee802_1x_hdr *) data; + xhdr->version = hapd->conf->eapol_version; + xhdr->type = type; + xhdr->length = host_to_be16(eap_req_len); + + if (eap_req && eap_req_len > 0) + os_memcpy(xhdr + 1, eap_req, eap_req_len); + + wpa_hexdump(MSG_MSGDUMP, "EAP-Request", eap_req, eap_req_len); + + /* EAP-Success */ + if (enc_assoc && eap_req_len > 0 && eap_req[0] == 3) { + u8 msk[2 * PMK_LEN] = { 0 }; + size_t _len = 2 * PMK_LEN; + size_t pmk_len, kdk_len; + bool is_ml = ap_sta_is_mld(hapd, sta); + enum wpa_alg alg = + wpa_cipher_to_alg(sta->eap_auth_data.cipher); + size_t key_len = + wpa_cipher_key_len(sta->eap_auth_data.cipher); + const u8 *aa = hapd->own_addr; + struct rsn_pmksa_cache *pmksa = + wpa_auth_get_pmksa_cache(hapd->wpa_auth, is_ml); + struct rsn_pmksa_cache_entry *entry; +#ifdef CONFIG_TESTING_OPTIONS + bool force_kdk = hapd->conf->force_kdk_derivation; +#else /* CONFIG_TESTING_OPTIONS */ + bool force_kdk = false; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_IEEE80211BE + if (is_ml) + aa = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + + /* Per IEEE 802.11bi/D4.0, 12.16.5 (IEEE 802.1X authentication + * utilizing Authentication frames), if the IEEE 802.1X + * authentication is successful, the Status Code field + * is set to 802_1_X_AUTH_SUCCESS. */ + status = WLAN_STATUS_802_1_X_AUTH_SUCCESS; + /* TODO: If the IEEE 802.1X authentication fails, + * the status code is set to 802_1_X_AUTH_FAILED. */ + os_memset(&sta->eap_auth_data.ptk, 0, sizeof(struct wpa_ptk)); + if (wpa_auth_802_1x_get_msk(hapd->wpa_auth, sta->addr, + msk, &_len)) { + wpa_printf(MSG_INFO, "Failed to get MSK"); + os_free(data); + return; + } + + if (wpa_key_mgmt_sha384(sta->eap_auth_data.akm)) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; + + sta->eap_auth_data.pmk_len = pmk_len; + os_memcpy(sta->eap_auth_data.pmk, msk, pmk_len); + + if (force_kdk || + (wpa_auth_ap_support_secure_ltf(hapd->wpa_auth) && + ieee802_11_rsnx_capab(sta->eap_auth_data.rsnxe, + WLAN_RSNX_CAPAB_SECURE_LTF))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + if (wpa_auth_802_1x_pmk_to_ptk( + msk, sta->eap_auth_data.pmk_len, + sta->addr, aa, + sta->eap_auth_data.snonce, + sta->eap_auth_data.anonce, + sta->eap_auth_data.akm, + sta->eap_auth_data.cipher, + wpabuf_head_u8(sta->eap_auth_data.dhss), + wpabuf_len(sta->eap_auth_data.dhss), + &sta->eap_auth_data.ptk, kdk_len)) { + wpa_printf(MSG_INFO, "Failed to derive the PTK"); + os_free(data); + return; + } + wpa_printf(MSG_DEBUG, "PTK derived successfully"); + + if (wpa_auth_802_1x_set_key(hapd->wpa_auth, + alg, sta->addr, + sta->eap_auth_data.ptk.tk, + key_len)) { + wpa_printf(MSG_INFO, + "Failed to set the TK to the driver"); + os_free(data); + return; + } + + /* Delete DHss after successful PTK derivation */ + wpabuf_clear_free(sta->eap_auth_data.dhss); + sta->eap_auth_data.dhss = NULL; + + /* TODO: Fill session_timeout? */ + wpa_hexdump_key(MSG_DEBUG, "IEEE802.1X: Cache PMK", + msk, pmk_len); + + entry = pmksa_cache_auth_add(pmksa, msk, pmk_len, NULL, + sta->eap_auth_data.ptk.kck, + sta->eap_auth_data.ptk.kck_len, + aa, sta->addr, 0, sta->eapol_sm, + sta->eap_auth_data.akm); + if (!entry) { + wpa_printf(MSG_INFO, "Failed to add PMKSA entry"); + return; + } + os_memcpy(sta->eap_auth_data.epp_pmkid_cur, entry->pmkid, + PMKID_LEN); + } + + reply = prepare_802_1x_auth_resp(hapd, sta, auth_transaction, status, + cached_pmk, data, len); + if (reply) + send_8021x_auth_reply(hapd, sta, auth_transaction, status, + reply); + os_free(data); +} + + +static void handle_auth_802_1x(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction) +{ + struct ieee802_1x_hdr *eapol_pdu; + u16 encap_len, resp = WLAN_STATUS_SUCCESS; + const u8 *end; + struct wpabuf *reply; + + if (len < 2) { + wpa_printf(MSG_INFO, "Missing Encapsulation Length field"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + end = pos + len; + encap_len = WPA_GET_LE16(pos); + pos += 2; + if (encap_len > end - pos) { + wpa_printf(MSG_INFO, "Truncated Encapsulation field"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* Start of Encapsulation field */ + eapol_pdu = (struct ieee802_1x_hdr *) pos; + + if (auth_transaction == 1 && + eapol_pdu->type != IEEE802_1X_TYPE_EAPOL_START) { + wpa_printf(MSG_INFO, + "Received unexpected EAPOL PDU type %u in the first Authentication frame", + eapol_pdu->type); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + pos += encap_len; + sta->eap_auth_data.auth_transaction = auth_transaction; + + /* Process Authentication frame elements + * Authentication frames with transaction sequence greater + * than or equal to 3 contain Authentication fields only. + */ + if (auth_transaction == 1) { + struct wpa_ie_data data; + struct ieee802_11_elems elems; + struct rsn_pmksa_cache_entry *cached_pmk = NULL; + bool is_ml = ap_sta_is_mld(hapd, sta); + bool enc_assoc, pmkid_privacy; + size_t i; + + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_INFO, "Could not parse elements"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + resp = wpa_auth_validate_802_1x_frame(hapd, sta, &elems); + if (resp) + goto fail; + + enc_assoc = ap_sta_support_enc_assoc(hapd, elems.rsnxe, + elems.rsnxe_len); + + pmkid_privacy = hapd->conf->pmksa_caching_privacy && + ieee802_11_rsnx_capab_len( + elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); + + os_memset(&data, 0, sizeof(data)); + if (enc_assoc && + (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, + elems.rsn_ie_len + 2, &data) < 0)) { + wpa_printf(MSG_INFO, "No valid RSNE"); + goto fail; + } + + for (i = 0; i < data.num_pmkid; i++) { + const u8 *aa; + enum wpa_alg alg; + size_t key_len, kdk_len; +#ifdef CONFIG_TESTING_OPTIONS + bool force_kdk = hapd->conf->force_kdk_derivation; +#else /* CONFIG_TESTING_OPTIONS */ + bool force_kdk = false; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump(MSG_DEBUG, "RSNE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + + cached_pmk = pmksa_cache_search( + hapd, pmkid_privacy ? NULL : sta->addr, + &data.pmkid[i * PMKID_LEN], is_ml); + if (!cached_pmk) + continue; + + aa = hapd->own_addr; + alg = wpa_cipher_to_alg(sta->eap_auth_data.cipher); + key_len = wpa_cipher_key_len(sta->eap_auth_data.cipher); + +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta)) + aa = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + wpa_printf(MSG_DEBUG, + "Found a matching PMKSA cache entry"); + os_memcpy(sta->eap_auth_data.epp_pmkid_cur, + cached_pmk->pmkid, PMKID_LEN); + reply = prepare_802_1x_auth_resp( + hapd, sta, auth_transaction + 1, + WLAN_STATUS_SUCCESS, cached_pmk, NULL, 0); + if (!reply) { + wpa_printf(MSG_INFO, + "Failed to prepare IEEE 802.1X Authentication frame"); + return; + } + + if (force_kdk || + (wpa_auth_ap_support_secure_ltf(hapd->wpa_auth) && + ieee802_11_rsnx_capab(sta->eap_auth_data.rsnxe, + WLAN_RSNX_CAPAB_SECURE_LTF))) + kdk_len = WPA_KDK_MAX_LEN; + else + kdk_len = 0; + + if (wpa_auth_802_1x_pmk_to_ptk( + cached_pmk->pmk, cached_pmk->pmk_len, + sta->addr, aa, + sta->eap_auth_data.snonce, + sta->eap_auth_data.anonce, + sta->eap_auth_data.akm, + sta->eap_auth_data.cipher, + wpabuf_head_u8(sta->eap_auth_data.dhss), + wpabuf_len(sta->eap_auth_data.dhss), + &sta->eap_auth_data.ptk, kdk_len)) { + wpa_printf(MSG_INFO, "Failed to derive PTK"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_printf(MSG_DEBUG, "PTK derived successfully"); + + if (wpa_auth_802_1x_set_key(hapd->wpa_auth, alg, + sta->addr, + sta->eap_auth_data.ptk.tk, + key_len)) { + wpa_printf(MSG_INFO, + "Failed to set TK to driver"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_802_1X; + sta->eap_auth_data.add_mic = true; + send_8021x_auth_reply(hapd, sta, auth_transaction + 1, + WLAN_STATUS_SUCCESS, reply); + /* Delete DHss after successful PTK derivation */ + wpabuf_clear_free(sta->eap_auth_data.dhss); + sta->eap_auth_data.dhss = NULL; + return; + } + + /* Start EAPOL SM to process EAPOL PDU */ + if (!sta->eapol_sm) { + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (!sta->eapol_sm) + return; + } + + ieee802_1x_eapol_sm_set_port_enabled(sta->eapol_sm, true); + } + + /* Forward the extracted EAP PDU to AS */ + ieee802_1x_receive(hapd, sta->addr, (const u8 *) eapol_pdu, + encap_len, FRAME_NOT_ENCRYPTED); + return; + +fail: + reply = prepare_802_1x_auth_resp(hapd, sta, auth_transaction + 1, + resp, NULL, NULL, 0); + if (reply) + send_8021x_auth_reply(hapd, sta, auth_transaction + 1, resp, + reply); +} + +#endif /* CONFIG_IEEE8021X_AUTH */ + + #ifdef CONFIG_FILS static void handle_auth_fils_finish(struct hostapd_data *hapd, @@ -1826,7 +2893,8 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, struct wpa_ie_data rsn; struct rsn_pmksa_cache_entry *pmksa = NULL; - if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) + if (auth_transaction != WLAN_AUTH_TR_SEQ_SAE_COMMIT || + status_code != WLAN_STATUS_SUCCESS) return; end = pos + len; @@ -1935,24 +3003,26 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, goto fail; } + wpa_auth_set_rsn_selection(sta->wpa_sm, elems.rsn_selection, + elems.rsn_selection_len); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, elems.rsn_ie - 2, elems.rsn_ie_len + 2, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, - elems.mdie, elems.mdie_len, NULL, 0, NULL); + elems.mdie, elems.mdie_len, NULL, 0, NULL, + ap_sta_is_mld(hapd, sta)); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) goto fail; - if (!elems.fils_nonce) { + if (!elems.nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.nonce, NONCE_LEN); + os_memcpy(sta->fils_snonce, elems.nonce, NONCE_LEN); /* PMKID List */ if (rsn.pmkid && rsn.num_pmkid > 0) { @@ -2060,7 +3130,7 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub) { - u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_nonce[NONCE_LEN]; size_t ielen; struct wpabuf *data = NULL; const u8 *ie; @@ -2095,12 +3165,12 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, ie = ie_buf; } - if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { + if (random_get_bytes(fils_nonce, NONCE_LEN) < 0) { *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", - fils_nonce, FILS_NONCE_LEN); + fils_nonce, NONCE_LEN); #ifdef CONFIG_FILS_SK_PFS if (sta->fils_dh_ss && sta->fils_ecdh) { @@ -2152,10 +3222,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, /* FILS Nonce */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ - wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ + wpabuf_put_u8(data, 1 + NONCE_LEN); /* Length */ /* Element ID Extension */ - wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); + wpabuf_put_u8(data, WLAN_EID_EXT_NONCE); + wpabuf_put_data(data, fils_nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ @@ -2217,7 +3287,7 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, sta->fils_erp_pmkid, session_timeout, wpa_auth_sta_key_mgmt(sta->wpa_sm), - NULL) < 0) { + NULL, ap_sta_is_mld(hapd, sta)) < 0) { wpa_printf(MSG_ERROR, "FILS: Failed to add PMKSA cache entry based on ERP"); } @@ -2441,13 +3511,13 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, goto fail; } - if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) { + if (random_get_bytes(fils->anonce, NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce", - fils->anonce, FILS_NONCE_LEN); + fils->anonce, NONCE_LEN); ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce, fils->anonce, NULL, 0, pmk, &pmk_len); @@ -2460,7 +3530,9 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, wpabuf_head(pasn->secret), wpabuf_len(pasn->secret), pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn), - pasn_get_cipher(sta->pasn), sta->pasn->kdk_len); + pasn_get_cipher(sta->pasn), sta->pasn->kdk_len, + sta->pasn->kek_len, &sta->pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); goto fail; @@ -2484,6 +3556,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, fils->erp_resp = erp_resp; ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL, WLAN_STATUS_SUCCESS); + wpabuf_free(pasn->frame); + pasn->frame = NULL; fils->erp_resp = NULL; if (ret) { @@ -2556,7 +3630,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + if (!elems.rsn_ie || !elems.nonce || !elems.nonce || !elems.wrapped_data || !elems.fils_session) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; @@ -2569,7 +3643,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - ret = wpa_pasn_validate_rsne(&rsne_data); + ret = wpa_pasn_validate_rsne(&rsne_data, false); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); return -1; @@ -2581,9 +3655,8 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta, return -1; } - wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.nonce, NONCE_LEN); + os_memcpy(fils->nonce, elems.nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session, FILS_SESSION_LEN); @@ -2635,35 +3708,243 @@ static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len, } +static struct rsn_pmksa_cache_entry * +pmksa_cache_search(void *ctx, const u8 *spa, const u8 *pmkid, bool is_ml) +{ + struct hostapd_data *hapd = ctx; + struct rsn_pmksa_cache_entry *entry; + struct rsn_pmksa_cache *pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth, + is_ml); + + entry = pmksa_cache_auth_get(pmksa, spa, pmkid); + if (entry) + return entry; + +#ifdef CONFIG_IEEE80211BE + if (is_ml) { + struct hostapd_data *tmp_hapd; + + /* Search in link caches of each affiliated AP MLD link */ + for_each_mld_link(tmp_hapd, hapd) { + pmksa = wpa_auth_get_pmksa_cache(tmp_hapd->wpa_auth, + false); + entry = pmksa_cache_auth_get(pmksa, spa, pmkid); + if (entry) + return entry; + } + } else if (hapd->conf->mld_ap) { + /* Search in the MLD cache */ + pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth, true); + entry = pmksa_cache_auth_get(pmksa, spa, pmkid); + if (entry) + return entry; + } +#endif /* CONFIG_IEEE80211BE */ + + return NULL; +} + + +#ifdef CONFIG_ENC_ASSOC +static int eppke_set_key(void *ctx, enum wpa_alg alg, const u8 *addr, + int vlan_id, const u8 *key, size_t key_len) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, + 0, vlan_id, 1, NULL, 0, key, key_len, + KEY_FLAG_PAIRWISE_RX_TX); +} +#else /* CONFIG_ENC_ASSOC */ +#define eppke_set_key NULL +#endif /* CONFIG_ENC_ASSOC */ + + +#ifdef CONFIG_SAE +/** + * hapd_pasn_get_pt_for_pw_id - Look up SAE PT for a password identifier + * + * Called by the PASN responder when an SAE commit frame contains a password + * identifier that was not known at PASN-setup time (e.g., for EPPKE where + * the PT cannot be pre-selected before the commit is received). + * + * For plaintext identifiers sae_get_password() returns the pre-computed PT + * (pw_entry->pt). We must NOT return that pointer directly because the + * caller will free it; clone it so the caller always owns the returned PT. + * counter and dec_pw_id are set to 0/NULL for plaintext identifiers. + * + * For encrypted password identifiers the PT is not pre-computed (the + * pre-computed PT is keyed to the decrypted identifier, not the encrypted + * one). Derive the PT on-the-fly using the raw (encrypted) identifier as + * the salt, mirroring what auth_build_sae_commit() does for regular SAE. + * The decrypted blob is parsed to extract the real password identifier + * (dec_pw_id) and the counter, both returned to the caller. dec_pw_id is + * heap-allocated and the caller takes ownership (must free with os_free()). + */ +static struct sae_pt * +hapd_pasn_get_pt_for_pw_id(void *ctx, const u8 *pw_id, size_t pw_id_len, + int group, const char **password, + unsigned int *counter, + u8 **dec_pw_id, size_t *dec_pw_id_len) +{ + struct hostapd_data *hapd = ctx; + struct sae_password_entry *pw_entry = NULL; + struct sae_pt *pt = NULL; + int groups[2] = { group, 0 }; + + *counter = 0; + *dec_pw_id = NULL; + *dec_pw_id_len = 0; + + *password = sae_get_password(hapd, NULL, pw_id, pw_id_len, &pw_entry, + &pt, NULL); + if (!*password) + return NULL; + + if (pt) { + /* Plaintext identifier: sae_get_password() found a + * pre-computed PT. Clone it so the caller can free it + * without affecting the password entry's own PT. + * counter and dec_pw_id stay 0/NULL for plaintext. */ + return sae_derive_pt(groups, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, + (const u8 *) pw_entry->password, + os_strlen(pw_entry->password), + pw_id, pw_id_len); + } + + if (pw_entry) { + /* Encrypted identifier: no pre-computed PT exists for the + * raw (encrypted) identifier. Decrypt the blob once to + * extract the real password identifier (dec_pw_id) and the + * counter, then derive the PT using the encrypted bytes as + * the salt (matching auth_build_sae_commit()). + * + * Decrypted format: + * 4-byte date | Password ID | NUL padding | 4-byte counter + */ + if (hapd->conf->sae_pw_id_key && + pw_id_len > 4 + 4 + AES_BLOCK_SIZE) { + u8 *plain; + size_t plain_len; + + plain = os_malloc(pw_id_len); + if (plain && + aes_siv_decrypt( + wpabuf_head(hapd->conf->sae_pw_id_key), + wpabuf_len(hapd->conf->sae_pw_id_key), + pw_id, pw_id_len, + 0, NULL, NULL, plain) == 0) { + const u8 *id, *pos; + + plain_len = pw_id_len - AES_BLOCK_SIZE; + /* Counter is the last 4 bytes */ + *counter = WPA_GET_BE32(plain + plain_len - 4); + wpa_printf(MSG_DEBUG, + "SAE: Generation time %u counter %u", + WPA_GET_BE32(plain), *counter); + /* Real password ID starts at byte 4, + * NUL-terminated before the counter */ + id = plain + 4; + pos = id; + while (pos < plain + plain_len - 4) { + if (*pos == 0x00) + break; + pos++; + } + *dec_pw_id_len = pos - id; + wpa_hexdump_ascii( + MSG_DEBUG, + "SAE: Decrypted password identifier", + id, *dec_pw_id_len); + *dec_pw_id = os_memdup(id, *dec_pw_id_len); + if (!*dec_pw_id) + *dec_pw_id_len = 0; + } + os_free(plain); + } + + pt = sae_derive_pt(groups, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, + (const u8 *) pw_entry->password, + os_strlen(pw_entry->password), + pw_id, pw_id_len); + if (!pt) + wpa_printf(MSG_DEBUG, + "PASN: Failed to derive PT for encrypted password identifier"); + return pt; + } + + return NULL; +} +#endif /* CONFIG_SAE */ + + static void hapd_initialize_pasn(struct hostapd_data *hapd, struct sta_info *sta) { struct pasn_data *pasn = sta->pasn; - pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL); + pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, + NULL, eppke_set_key, pmksa_cache_search); pasn_set_bssid(pasn, hapd->own_addr); pasn_set_own_addr(pasn, hapd->own_addr); +#ifdef CONFIG_PMKSA_PRIVACY + pasn->pmksa_caching_privacy = hapd->conf->pmksa_caching_privacy; +#endif /* CONFIG_PMKSA_PRIVACY */ +#if defined(CONFIG_IEEE80211BE) && defined(CONFIG_ENC_ASSOC) + /* Per IEEE802.11bi/D4.0, 12.16.9 (Enhanced privacy + * protection key exchange), if (Re)Association frame + * Encryption is activated, KEK in PASN shall be true. + */ + if (hapd->conf->assoc_frame_encryption) + pasn->derive_kek = true; + if (hapd->conf->mld_ap) + pasn_set_own_mld_addr(pasn, hapd->mld->mld_addr); +#endif /* CONFIG_IEEE80211BE && CONFIG_ENC_ASSOC */ pasn_set_peer_addr(pasn, sta->addr); - pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt); - pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise); - pasn->pasn_groups = hapd->conf->pasn_groups; + pasn_set_wpa_key_mgmt(pasn, hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2); + pasn_set_rsn_pairwise(pasn, hapd->conf->rsn_pairwise | + hapd->conf->rsn_override_pairwise); + pasn_set_mfp(pasn, hapd->conf->ieee80211w); + os_free(pasn->pasn_groups); + pasn->pasn_groups = int_array_dup(hapd->conf->pasn_groups); pasn->noauth = hapd->conf->pasn_noauth; +#ifdef CONFIG_ENC_ASSOC + pasn->eppke_unauth = hapd->conf->eppke_unauth; +#endif /* CONFIG_ENC_ASSOC */ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) pasn_enable_kdk_derivation(pasn); #ifdef CONFIG_TESTING_OPTIONS pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic; + pasn->pasn_test_groups = hapd->conf->pasn_test_groups; if (hapd->conf->force_kdk_derivation) pasn_enable_kdk_derivation(pasn); #endif /* CONFIG_TESTING_OPTIONS */ pasn->use_anti_clogging = use_anti_clogging(hapd); - pasn_set_password(pasn, sae_get_password(hapd, sta, NULL, NULL, - &pasn->pt, NULL)); - pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len); + sae_get_password(hapd, sta, NULL, 0, NULL, &pasn->pt, NULL); +#ifdef CONFIG_SAE + /* Register a callback so the PASN responder can look up the correct + * SAE PT when the STA's commit frame contains a password identifier + * that was not known at setup time (EPPKE cases). + */ + pasn->get_pt_for_pw_id = hapd_pasn_get_pt_for_pw_id; +#endif /* CONFIG_SAE */ + pasn_set_rsne(pasn, wpa_auth_get_wpa_ie(hapd->wpa_auth, + &pasn->rsn_ie_len)); pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX)); pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching; - pasn_set_responder_pmksa(pasn, - wpa_auth_get_pmksa_cache(hapd->wpa_auth)); +#ifdef CONFIG_ENC_ASSOC + pasn->tk_configured = false; +#endif /* CONFIG_ENC_ASSOC */ + pasn_set_responder_pmksa( + pasn, + wpa_auth_get_pmksa_cache(hapd->wpa_auth, + ap_sta_is_epp(sta) ? + ap_sta_is_mld(hapd, sta) : false)); pasn->comeback_after = hapd->conf->pasn_comeback_after; pasn->comeback_idx = hapd->comeback_idx; @@ -2740,6 +4021,14 @@ static void hapd_pasn_update_params(struct hostapd_data *hapd, return; } +#ifdef CONFIG_ENC_ASSOC + pasn->auth_alg = mgmt->u.auth.auth_alg; + pasn->authorized = ap_sta_is_authorized(sta); +#ifdef CONFIG_IEEE80211BE + pasn->is_ml_peer = sta->mld_info.mld_sta; +#endif /* CONFIG_IEEE80211BE */ +#endif /* CONFIG_ENC_ASSOC */ + pasn_set_akmp(pasn, rsn_data.key_mgmt); pasn_set_cipher(pasn, rsn_data.pairwise_cipher); @@ -2797,6 +4086,32 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 trans_seq, u16 status) { + int ret; +#ifdef CONFIG_P2P + struct ieee802_11_elems elems; + + if (len < 24) { + wpa_printf(MSG_DEBUG, "PASN: Too short Management frame"); + return; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return; + } + + if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) == + (P2P_ENABLED | P2P_GROUP_OWNER) && + hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) { + p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq); + return; + } +#endif /* CONFIG_P2P */ + if (hapd->conf->wpa != WPA_PROTO_RSN) { wpa_printf(MSG_INFO, "PASN: RSN is not configured"); return; @@ -2805,8 +4120,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR, MAC2STR(sta->addr)); - if (trans_seq == 1) { - if (sta->pasn) { + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + if (sta->pasn && sta->auth_alg != WLAN_AUTH_EPPKE) { wpa_printf(MSG_DEBUG, "PASN: Not expecting transaction == 1"); return; @@ -2818,7 +4133,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - sta->pasn = pasn_data_init(); + if (!sta->pasn) + sta->pasn = pasn_data_init(); if (!sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Failed to allocate PASN context"); @@ -2828,10 +4144,17 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, hapd_initialize_pasn(hapd, sta); hapd_pasn_update_params(hapd, sta, mgmt, len); - if (handle_auth_pasn_1(sta->pasn, hapd->own_addr, - sta->addr, mgmt, len) < 0) + ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr, + mgmt, len, false); + wpabuf_free(sta->pasn->frame); + sta->pasn->frame = NULL; + if (ret < 0) { + hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr, + sta->addr, 0, 0, + NULL, 0, NULL, 1); ap_free_sta(hapd, sta); - } else if (trans_seq == 3) { + } + } else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { if (!sta->pasn) { wpa_printf(MSG_DEBUG, "PASN: Not expecting transaction == 3"); @@ -2845,19 +4168,44 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, return; } - if (handle_auth_pasn_3(sta->pasn, hapd->own_addr, - sta->addr, mgmt, len) == 0) { + ret = handle_auth_pasn_3(sta->pasn, hapd->own_addr, sta->addr, + mgmt, len); + if (ret == 0) { +#ifdef CONFIG_ENC_ASSOC + if (ap_sta_is_epp(sta)) { + sta->auth_alg = WLAN_AUTH_EPPKE; + sta->flags |= WLAN_STA_AUTH; + } +#endif /* CONFIG_ENC_ASSOC */ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr, pasn_get_cipher(sta->pasn), 43200, pasn_get_ptk(sta->pasn), NULL, NULL, + pasn_get_akmp(sta->pasn), + sta->pasn->auth_alg); +#ifdef CONFIG_ENC_ASSOC + /* TODO: Support VLAN ID assignment based on configured + * SAE passwords. */ + if (ap_sta_is_epp(sta) && !sta->pasn->tk_configured && + sta->pasn->eppke_set_key) + sta->pasn->eppke_set_key( + sta->pasn->cb_ctx, + wpa_cipher_to_alg(sta->pasn->cipher), + sta->addr, 0, + sta->pasn->ptk.tk, + sta->pasn->ptk.tk_len); +#endif /* CONFIG_ENC_ASSOC */ + if (!ap_sta_is_epp(sta)) + pasn_set_keys_from_cache( + hapd, hapd->own_addr, + sta->addr, + pasn_get_cipher(sta->pasn), pasn_get_akmp(sta->pasn)); - - pasn_set_keys_from_cache(hapd, hapd->own_addr, - sta->addr, - pasn_get_cipher(sta->pasn), - pasn_get_akmp(sta->pasn)); } - ap_free_sta(hapd, sta); + if (!ap_sta_is_epp(sta) || + (ret < 0 && + ap_sta_is_epp(sta) && !ap_sta_is_authorized(sta))) + ap_free_sta(hapd, sta); + } else { wpa_printf(MSG_DEBUG, "PASN: Invalid transaction %u - ignore", trans_seq); @@ -2882,9 +4230,7 @@ static void handle_auth(struct hostapd_data *hapd, u16 seq_ctrl; struct radius_sta rad_info; const u8 *dst, *sa; -#ifdef CONFIG_IEEE80211BE bool mld_sta = false; -#endif /* CONFIG_IEEE80211BE */ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", @@ -2929,10 +4275,10 @@ static void handle_auth(struct hostapd_data *hapd, challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s " + "auth_transaction=%d status_code=%d protected=%d%s " "seq_ctrl=0x%x%s%s", MAC2STR(sa), auth_alg, auth_transaction, - status_code, !!(fc & WLAN_FC_ISWEP), + status_code, !!(fc & WLAN_FC_PROTECTED), challenge ? " challenge" : "", seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", from_queue ? " (from queue)" : ""); @@ -2947,6 +4293,17 @@ static void handle_auth(struct hostapd_data *hapd, } #endif /* CONFIG_NO_RC4 */ +#ifdef CONFIG_IEEE8021X_AUTH + if (auth_alg == WLAN_AUTH_802_1X && + !hapd->conf->eap_using_authentication_frames) { + wpa_printf(MSG_INFO, + "Unsupported authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + if (hapd->tkip_countermeasures) { wpa_printf(MSG_DEBUG, "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); @@ -2961,7 +4318,10 @@ static void handle_auth(struct hostapd_data *hapd, auth_alg == WLAN_AUTH_FT) || #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE - (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + (hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ #ifdef CONFIG_FILS @@ -2976,6 +4336,19 @@ static void handle_auth(struct hostapd_data *hapd, (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) && auth_alg == WLAN_AUTH_PASN) || #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + (hapd->conf->wpa && + ((hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_EPPKE) && + hapd->conf->assoc_frame_encryption && + auth_alg == WLAN_AUTH_EPPKE) || +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + (hapd->conf->wpa && + wpa_key_mgmt_wpa_ieee8021x(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_802_1X) || +#endif /* CONFIG_IEEE8021X_AUTH */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -2984,10 +4357,24 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } - if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || + if (!(auth_transaction == 1 || +#ifdef CONFIG_SAE + (auth_alg == WLAN_AUTH_SAE && + auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) || +#endif /* CONFIG_SAE */ #ifdef CONFIG_PASN - (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) || + (auth_alg == WLAN_AUTH_PASN && + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) || #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + (auth_alg == WLAN_AUTH_EPPKE && + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) || +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + /* EAP over Auth involves variable number of frames depending + * on the EAP authentication method */ + auth_alg == WLAN_AUTH_802_1X || +#endif /* CONFIG_IEEE8021X_AUTH */ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", auth_transaction); @@ -2995,25 +4382,13 @@ static void handle_auth(struct hostapd_data *hapd, goto fail; } - if (ether_addr_equal(mgmt->sa, hapd->own_addr)) { + if (!hostapd_acceptable_sta_addr(hapd, mgmt->sa, sa, mld_sta)) { wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", MAC2STR(sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } -#ifdef CONFIG_IEEE80211BE - if (mld_sta && - (ether_addr_equal(sa, hapd->own_addr) || - ether_addr_equal(sa, hapd->mld->mld_addr))) { - wpa_printf(MSG_INFO, - "Station " MACSTR " not allowed to authenticate", - MAC2STR(sa)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } -#endif /* CONFIG_IEEE80211BE */ - if (hapd->conf->no_auth_if_seen_on) { struct hostapd_data *other; @@ -3081,10 +4456,28 @@ static void handle_auth(struct hostapd_data *hapd, if (res == HOSTAPD_ACL_PENDING) return; +#ifdef CONFIG_IEEE80211BE + if (mld_sta) { + res = ieee802_11_allowed_address(hapd, mgmt->sa, + (const u8 *) mgmt, len, + &rad_info); + if (res == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Authentication frame from " MACSTR + " due to ACL reject", MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (res == HOSTAPD_ACL_PENDING) + return; + } +#endif /* CONFIG_IEEE80211BE */ + #ifdef CONFIG_SAE if (auth_alg == WLAN_AUTH_SAE && !from_queue && - (auth_transaction == 1 || - (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) { + (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT || + (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM && + auth_sae_queued_addr(hapd, sa)))) { /* Handle SAE Authentication commit message through a queue to * provide more control for postponing the needed heavy * processing under a possible DoS attack scenario. In addition, @@ -3148,6 +4541,13 @@ static void handle_auth(struct hostapd_data *hapd, } } +#if defined(CONFIG_ENC_ASSOC) || defined(CONFIG_IEEE8021X_AUTH) + if (auth_alg == WLAN_AUTH_EPPKE || auth_alg == WLAN_AUTH_802_1X) { + wpa_printf(MSG_DEBUG, "Mark the station as an EPP peer"); + sta->epp_sta = true; + } +#endif /* CONFIG_ENC_ASSOC || CONFIG_IEEE8021X_AUTH */ + #ifdef CONFIG_IEEE80211BE /* Set the non-AP MLD information based on the initial Authentication * frame. Once the STA entry has been added to the driver, the driver @@ -3175,6 +4575,16 @@ static void handle_auth(struct hostapd_data *hapd, os_memcpy(sta->mld_info.links[link_id].local_addr, hapd->own_addr, ETH_ALEN); } + + if ((!(sta->flags & WLAN_STA_MFP) || + !ap_sta_is_authorized(sta)) && sta->wpa_sm) { + struct wpa_state_machine *sm = sta->wpa_sm; + + clear_wpa_sm_for_each_partner_link(hapd, sta); + clear_wpa_sm_for_all_sta(hapd, sm); + wpa_auth_sta_deinit(sm); + sta->wpa_sm = NULL; + } } #endif /* CONFIG_IEEE80211BE */ @@ -3240,7 +4650,7 @@ static void handle_auth(struct hostapd_data *hapd, #ifndef CONFIG_NO_RC4 case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, - fc & WLAN_FC_ISWEP); + fc & WLAN_FC_PROTECTED); if (resp != 0) wpa_printf(MSG_DEBUG, "auth_shared_key() failed: status=%d", resp); @@ -3306,6 +4716,17 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_fils_finish); return; #endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: + handle_auth_802_1x(hapd, sta, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth), + auth_alg, auth_transaction); + return; +#endif /* CONFIG_IEEE8021X_AUTH */ +#ifdef CONFIG_ENC_ASSOC + case WLAN_AUTH_EPPKE: +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_PASN case WLAN_AUTH_PASN: handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction, @@ -3344,7 +4765,10 @@ static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd) if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1) return 0; - num_bss_nontx = hapd->iface->num_bss - 1; + if (hapd->iface->conf->mbssid_max > 0) + num_bss_nontx = hapd->iface->conf->mbssid_max - 1; + else + num_bss_nontx = hapd->iface->conf->num_bss - 1; while (num_bss_nontx > 0) { max_bssid_ind++; num_bss_nontx >>= 1; @@ -3756,7 +5180,8 @@ static u16 owe_process_assoc_req(struct hostapd_data *hapd, wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, - sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL); + sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, + NULL, ap_sta_is_mld(hapd, sta)); return WLAN_STATUS_SUCCESS; } @@ -3772,7 +5197,7 @@ u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, if (!rsn_ie || rsn_ie_len < 2) { wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR, MAC2STR(peer)); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } rsn_ie -= 2; rsn_ie_len += 2; @@ -3788,13 +5213,13 @@ u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, wpa_printf(MSG_DEBUG, "OWE: Unexpected key mgmt 0x%x from " MACSTR, (unsigned int) data.key_mgmt, MAC2STR(peer)); - return WLAN_STATUS_AKMP_NOT_VALID; + return WLAN_STATUS_INVALID_AKMP; } if (!owe_dh) { wpa_printf(MSG_DEBUG, "OWE: No Diffie-Hellman Parameter element from " MACSTR, MAC2STR(peer)); - return WLAN_STATUS_AKMP_NOT_VALID; + return WLAN_STATUS_INVALID_AKMP; } return WLAN_STATUS_SUCCESS; @@ -3814,7 +5239,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, if (!rsn_ie || rsn_ie_len < 2) { wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq"); - status = WLAN_STATUS_INVALID_IE; + status = WLAN_STATUS_INVALID_ELEMENT; goto end; } @@ -3836,15 +5261,15 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, rsn_ie_len += 2; res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, rsn_ie, rsn_ie_len, - NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL); + NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL, + ap_sta_is_mld(hapd, sta)); status = wpa_res_to_status_code(res); if (status != WLAN_STATUS_SUCCESS) goto end; status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); if (status != WLAN_STATUS_SUCCESS) goto end; - owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie), - NULL, 0); + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie)); if (!owe_buf) { status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto end; @@ -3888,19 +5313,84 @@ end: #endif /* CONFIG_OWE */ +static bool hapd_is_known_sta(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ies, size_t ies_len) +{ + const u8 *ie, *pos, *end, *timestamp_pos, *mic; + u64 timestamp; + u8 mic_len; + + if (!hapd->conf->known_sta_identification) + return false; + + ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION); + if (!ie) + return false; + + pos = ie + 3; + end = &ie[2 + ie[1]]; + if (end - pos < 8 + 1) + return false; /* truncated element */ + timestamp_pos = pos; + timestamp = WPA_GET_LE64(pos); + pos += 8; + mic_len = *pos++; + if (mic_len > end - pos) + return false; /* truncated element */ + mic = pos; + + wpa_printf(MSG_DEBUG, "RSN: STA " MACSTR + " included Known STA Identification element: Timestamp=0x%llx mic_len=%u", + MAC2STR(sta->addr), (unsigned long long) timestamp, mic_len); + + if (timestamp <= sta->last_known_sta_id_timestamp) { + wpa_printf(MSG_DEBUG, + "RSN: Ignore reused or old Known STA Identification"); + return false; + } + + if (!wpa_auth_sm_known_sta_identification(sta->wpa_sm, timestamp_pos, + mic, mic_len)) { + wpa_printf(MSG_DEBUG, + "RSN: Ignore Known STA Identification with invalid MIC or due to KCK not available"); + return false; + } + + wpa_printf(MSG_DEBUG, "RSN: Valid Known STA Identification"); + sta->last_known_sta_id_timestamp = timestamp; + + return true; +} + + static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta, - int reassoc) + int reassoc, const u8 *ies, size_t ies_len, + bool enc_assoc) { if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) != (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) return false; +#ifdef CONFIG_ENC_ASSOC + if (enc_assoc && sta->epp_sta) { + /* Skip SA Query since either the STA knows the PTK that is in + * use in the existing association or a new EPPKE authentication + * has already authenticated the STA and has replaced the TK and + * there is not really any point in starting SA Query procedure. + */ + return false; + } +#endif /* CONFIG_ENC_ASSOC */ + if (!sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); if (!sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + if (hapd_is_known_sta(hapd, sta, ies, ies_len)) + return false; + /* * STA has already been associated with MFP and SA Query timeout * has not been reached. Reject the association attempt @@ -3918,54 +5408,70 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta, static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, - struct ieee802_11_elems *elems, int reassoc, - bool link) + struct ieee802_11_elems *elems, + enum link_parse_type type, + struct wpa_state_machine *assoc_wpa_sm) { int resp; const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; - struct hostapd_data *assoc_hapd; - struct sta_info *assoc_sta = NULL; +#ifdef CONFIG_PMKSA_PRIVACY + bool derive_next_pmkid = true; +#endif /* CONFIG_PMKSA_PRIVACY */ +#ifdef CONFIG_IEEE8021X_AUTH + bool mic_check = true; +#endif /* CONFIG_IEEE8021X_AUTH */ +#ifdef CONFIG_SAE + bool epp_sta = false; + +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif /* CONFIG_ENC_ASSOC */ +#endif /* CONFIG_SAE */ + + if (type != LINK_PARSE_RECONF) { + resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); + if (resp != WLAN_STATUS_SUCCESS) + goto out; + } - resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); - if (resp != WLAN_STATUS_SUCCESS) - return resp; resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; resp = check_ext_capab(hapd, sta, elems->ext_capab, elems->ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; resp = copy_supp_rates(hapd, sta, elems); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && !(sta->flags & WLAN_STA_HT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory HT PHY - reject association"); - return WLAN_STATUS_ASSOC_DENIED_NO_HT; + resp = WLAN_STATUS_ASSOC_DENIED_NO_HT; + goto out; } #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac) { resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && @@ -3973,30 +5479,32 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory VHT PHY - reject association"); - return WLAN_STATUS_ASSOC_DENIED_NO_VHT; + resp = WLAN_STATUS_ASSOC_DENIED_NO_VHT; + goto out; } if (hapd->conf->vendor_vht && !elems->vht_capabilities) { resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht, elems->vendor_vht_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP, elems->he_capabilities, elems->he_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support mandatory HE PHY - reject association"); - return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; + resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; + goto out; } if (is_6ghz_op_class(hapd->iconf->op_class)) { @@ -4005,29 +5513,39 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support mandatory HE PHY - reject association"); - return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; + resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED; + goto out; } resp = copy_sta_he_6ghz_capab(hapd, sta, elems->he_6ghz_band_cap); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } } #endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP, elems->he_capabilities, elems->he_capabilities_len, elems->eht_capabilities, elems->eht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; - if (!link) { + if ((hapd->iconf->require_eht || hapd->conf->bss_require_eht) && + !(sta->flags & WLAN_STA_EHT)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station does not support mandatory EHT PHY - reject association"); + return WLAN_STATUS_DENIED_EHT_NOT_SUPPORTED; + } + + if (!assoc_wpa_sm) { resp = hostapd_process_ml_assoc_req(hapd, elems, sta); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } } #endif /* CONFIG_IEEE80211BE */ @@ -4045,6 +5563,80 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE8021X_AUTH + /* Per IEEE 802.11bi/D4.0, 12.16.6 ((Re)Association Request/Response + * frame encryption), if IEEE 802.1X is used and FT protocol is not + * used, the EPP non-AP STA shall include a MIC element in the + * (Re)Association Request frame. + * Skip MIC validation on partner AP MLD links. + */ +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta) && + hapd->mld_link_id != sta->mld_assoc_link_id) + mic_check = false; +#endif /* CONFIG_IEEE80211BE */ + if (ap_sta_is_epp(sta) && sta->auth_alg == WLAN_AUTH_802_1X && + mic_check) { + const u8 *data; + u8 mic_len, data_buf[(255 + 2) * 2], mic[WPA_1X_MAX_MIC_LEN]; + const u8 *aa = hapd->own_addr; + size_t data_len = 0; + int ret; + + if (!elems->mic) { + wpa_printf(MSG_DEBUG, "802.1X: Missing MIC element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } + + if (wpa_key_mgmt_sha384(sta->eap_auth_data.akm)) + mic_len = SHA384_MAC_LEN / 2; + else + mic_len = SHA256_MAC_LEN / 2; + + if (mic_len != elems->mic_len) { + wpa_printf(MSG_DEBUG, "802.1X: Invalid MIC len: %u", + elems->mic_len); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } + +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta)) + aa = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + + os_memcpy(data_buf, elems->rsn_ie - 2, elems->rsn_ie_len + 2); + data_len += 2 + elems->rsn_ie_len; + os_memcpy(data_buf + data_len, elems->rsnxe - 2, + elems->rsnxe_len + 2); + data_len += 2 + elems->rsnxe_len; + + data = data_buf; + ret = wpa_auth_8021x_mic(sta->eap_auth_data.akm, + sta->eap_auth_data.ptk.kck, + sta->eap_auth_data.ptk.kck_len, aa, + sta->addr, data, data_len, + NULL, 0, mic); + wpa_hexdump_key(MSG_DEBUG, "802.1X: Frame MIC", + elems->mic, elems->mic_len); + if (ret || os_memcmp(mic, elems->mic, mic_len) != 0) { + wpa_printf(MSG_INFO, "802.1X: Failed MIC verification"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } + } +#endif /* CONFIG_IEEE8021X_AUTH */ + + /* Link Reconfiguration Request frame for add link operation will not + * have RSN and other security IEs. So, skip the checks. + */ + if (type == LINK_PARSE_RECONF) { + wpa_printf(MSG_DEBUG, + "MLD: Skip security IE checks for Link Reconfiguration request"); + goto skip_wpa_ies; + } + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) { wpa_ie = elems->rsn_ie; wpa_ie_len = elems->rsn_ie_len; @@ -4075,7 +5667,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " "(Re)Association Request - reject"); - return WLAN_STATUS_INVALID_IE; + resp = WLAN_STATUS_INVALID_ELEMENT; + goto out; } } else if (hapd->conf->wps_state && wpa_ie == NULL) { wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " @@ -4087,7 +5680,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "No WPA/RSN IE in association request"); - return WLAN_STATUS_INVALID_IE; + resp = WLAN_STATUS_INVALID_ELEMENT; + goto out; } if (hapd->conf->wpa && wpa_ie) { @@ -4101,10 +5695,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_ie_len += 2; if (!sta->wpa_sm) { - if (!link) - assoc_sta = hostapd_ml_get_assoc_sta( - hapd, sta, &assoc_hapd); - + /* NOTE: For links other than the assoc-link the + * separate wpa_sm is only allocated internally to this + * function. + */ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); @@ -4112,8 +5706,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (!sta->wpa_sm) { wpa_printf(MSG_WARNING, "Failed to initialize RSN state machine"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } +#ifdef CONFIG_SAE + if (sta->sae && sta->sae->state == SAE_ACCEPTED && + wpa_key_mgmt_sae_ext_key(sta->sae->akmp)) + wpa_auth_set_hash_alg_sae_ext_key( + sta->wpa_sm, sta->sae->pmk_len); +#endif /* CONFIG_SAE */ } #ifdef CONFIG_IEEE80211BE @@ -4128,6 +5729,11 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211BE */ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); + if (sta->auth_alg == WLAN_AUTH_SAE) + wpa_auth_set_sae_pw_id(sta->wpa_sm, sta->sae_pw_id, + sta->sae_pw_id_counter); + wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection, + elems->rsn_selection_len); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, wpa_ie, wpa_ie_len, @@ -4137,39 +5743,130 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, 0, elems->mdie, elems->mdie_len, elems->owe_dh, elems->owe_dh_len, - assoc_sta ? assoc_sta->wpa_sm : NULL); + assoc_wpa_sm, + ap_sta_is_mld(hapd, sta)); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; if (wpa_auth_uses_mfp(sta->wpa_sm)) sta->flags |= WLAN_STA_MFP; else sta->flags &= ~WLAN_STA_MFP; + if (wpa_auth_uses_spp_amsdu(sta->wpa_sm)) + sta->flags |= WLAN_STA_SPP_AMSDU; + else + sta->flags &= ~WLAN_STA_SPP_AMSDU; + +#ifdef CONFIG_PMKSA_PRIVACY + /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy), when both + * the AP and non-AP STA support PMKSA caching privacy, the non-AP STA + * shall include a Nonce element in the (Re)Association Request frame. + * Skip Nonce element processing for partner AP MLD links. */ +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta) && + hapd->mld_link_id != sta->mld_assoc_link_id) + derive_next_pmkid = false; +#endif /* CONFIG_IEEE80211BE */ + + if (derive_next_pmkid && ap_sta_is_epp(sta) && + hapd->conf->pmksa_caching_privacy && + ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) { + int akmp; + size_t pmk_len; + u8 *pmkid_next; + + if (!elems->nonce) { + wpa_printf(MSG_DEBUG, "STA " MACSTR + " did not include Nonce element to compute next PMKID", + MAC2STR(sta->addr)); + goto skip_pmkid_update; + } + os_memcpy(sta->snonce, elems->nonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, + "RSN: Received SNonce to compute next PMKID", + sta->snonce, NONCE_LEN); + + switch (sta->auth_alg) { + case WLAN_AUTH_EPPKE: + if (!sta->pasn) { + wpa_printf(MSG_INFO, + "EPPKE: Missing PASN data - cannot derive a new PMKID"); + goto skip_pmkid_update; + } + pmk_len = sta->pasn->pmk_len; + pmkid_next = sta->epp_pmkid_next; + break; +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: + pmk_len = sta->eap_auth_data.pmk_len; + pmkid_next = sta->eap_auth_data.epp_pmkid_next; + break; +#endif /* CONFIG_IEEE8021X_AUTH */ + default: + wpa_printf(MSG_INFO, + "EPP: Unsupported auth alg %u for PMKID privacy", + sta->auth_alg); + goto skip_pmkid_update; + } + + if (random_get_bytes(sta->anonce, NONCE_LEN) < 0) + goto skip_pmkid_update; + wpa_hexdump_key(MSG_DEBUG, + "EPP: Generated ANonce to compute next PMKID", + sta->anonce, NONCE_LEN); + + akmp = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (akmp < 0 || + wpa_auth_epp_derive_new_pmkid(sta->anonce, sta->snonce, + akmp, pmk_len, + pmkid_next) < 0) { + wpa_printf(MSG_INFO, + "EPP: Failed to generate new PMKID"); + goto skip_pmkid_update; + } + wpa_hexdump_key(MSG_DEBUG, "EPP: New PMKID", + pmkid_next, PMKID_LEN); + } +skip_pmkid_update: +#endif /* CONFIG_PMKSA_PRIVACY */ + #ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { - if (!reassoc) { + if (type != LINK_PARSE_REASSOC) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " "to use association (not " "re-association) with FT auth_alg", MAC2STR(sta->addr)); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies, ies_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } #endif /* CONFIG_IEEE80211R_AP */ - if (link) + if (assoc_wpa_sm) goto skip_sae_owe; #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && - sta->sae->state == SAE_ACCEPTED) + sta->sae->state == SAE_ACCEPTED) { + if (!sta->sae->h2e && + wpa_key_mgmt_sae_ext_key(wpa_auth_sta_key_mgmt( + sta->wpa_sm))) { + wpa_printf(MSG_DEBUG, "SAE: STA " MACSTR + " tried to use EXT-KEY AKM without H2E", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); + } if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { @@ -4179,18 +5876,24 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: No PMKSA cache entry found for " MACSTR, MAC2STR(sta->addr)); - return WLAN_STATUS_INVALID_PMKID; + resp = WLAN_STATUS_INVALID_PMKID; + goto out; } wpa_printf(MSG_DEBUG, "SAE: " MACSTR " using PMKSA caching", MAC2STR(sta->addr)); - } else if (wpa_auth_uses_sae(sta->wpa_sm) && + sae_assign_vlan(hapd, sta, sa->sae_vlan_id); + if (wpa_key_mgmt_sae_ext_key(sa->akmp)) + wpa_auth_set_hash_alg_sae_ext_key( + sta->wpa_sm, sa->pmk_len); + } else if (!epp_sta && wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg != WLAN_AUTH_SAE && !(sta->auth_alg == WLAN_AUTH_FT && wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); - return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto out; } if (hapd->conf->sae_pwe == SAE_PWE_BOTH && @@ -4198,21 +5901,31 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->sae && !sta->sae->h2e && ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len, WLAN_RSNX_CAPAB_SAE_H2E)) { - wpa_printf(MSG_INFO, "SAE: " MACSTR - " indicates support for SAE H2E, but did not use it", - MAC2STR(sta->addr)); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (hapd->conf->sae_accept_h2e_without_use) { + wpa_printf(MSG_INFO, "SAE: " MACSTR + " indicates support for SAE H2E, but did not use it - accepting due to sae_accept_h2e_without_use", + MAC2STR(sta->addr)); + } else { + wpa_printf(MSG_INFO, "SAE: " MACSTR + " indicates support for SAE H2E, but did not use it", + MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } } #endif /* CONFIG_SAE */ #ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + if (((hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & + WPA_KEY_MGMT_OWE) && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && elems->owe_dh) { resp = owe_process_assoc_req(hapd, sta, elems->owe_dh, elems->owe_dh_len); if (resp != WLAN_STATUS_SUCCESS) - return resp; + goto out; } #endif /* CONFIG_OWE */ skip_sae_owe: @@ -4225,7 +5938,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && hapd->conf->dpp_netaccesskey && sta->wpa_sm && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && - elems->owe_dh) { + elems->owe_dh && !assoc_wpa_sm) { sta->dpp_pfs = dpp_pfs_init( wpabuf_head(hapd->conf->dpp_netaccesskey), wpabuf_len(hapd->conf->dpp_netaccesskey)); @@ -4240,12 +5953,13 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems->owe_dh_len) < 0) { dpp_pfs_free(sta->dpp_pfs); sta->dpp_pfs = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } } - - wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? - sta->dpp_pfs->secret : NULL); + if (!assoc_wpa_sm) + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); pfs_fail: #endif /* CONFIG_DPP2 */ @@ -4256,7 +5970,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_INFO, "Station tried to use TKIP with HT " "association"); - return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + resp = WLAN_STATUS_CIPHER_OUT_OF_POLICY; + goto out; } wpa_auth_set_ssid_protection( @@ -4265,34 +5980,14 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, ieee802_11_rsnx_capab_len( elems->rsnxe, elems->rsnxe_len, WLAN_RSNX_CAPAB_SSID_PROTECTION)); -#ifdef CONFIG_HS20 - } else if (hapd->conf->osen) { - if (!elems->osen) { - hostapd_logger( - hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "No HS 2.0 OSEN element in association request"); - return WLAN_STATUS_INVALID_IE; - } - - wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr, NULL); - if (sta->wpa_sm == NULL) { - wpa_printf(MSG_WARNING, "Failed to initialize WPA " - "state machine"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } - if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, - elems->osen - 2, elems->osen_len + 2) < 0) - return WLAN_STATUS_INVALID_IE; -#endif /* CONFIG_HS20 */ } else wpa_auth_sta_no_wpa(sta->wpa_sm); +skip_wpa_ies: + #ifdef CONFIG_P2P - p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); + if (ies && ies_len) + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 @@ -4308,7 +6003,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "HS 2.0: PMF not negotiated by release %d station " MACSTR, release, MAC2STR(sta->addr)); - return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + goto out; } } else { sta->hs20_ie = NULL; @@ -4339,12 +6035,14 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { wpa_printf(MSG_INFO, "MBO: Reject WPA2 association without PMF"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } #endif /* CONFIG_MBO */ #if defined(CONFIG_FILS) && defined(CONFIG_OCV) - if (wpa_auth_uses_ocv(sta->wpa_sm) && + if (type != LINK_PARSE_RECONF && + wpa_auth_uses_ocv(sta->wpa_sm) && (sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK)) { @@ -4356,14 +6054,17 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (hostapd_drv_channel_info(hapd, &ci) != 0) { wpa_printf(MSG_WARNING, "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } if (get_sta_tx_parameters(sta->wpa_sm, channel_width_to_int(ci.chanwidth), ci.seg1_idx, &tx_chanwidth, - &tx_seg1_idx) < 0) - return WLAN_STATUS_UNSPECIFIED_FAILURE; + &tx_seg1_idx) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci, tx_chanwidth, tx_seg1_idx); @@ -4379,7 +6080,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr=" MACSTR " frame=fils-reassoc-req error=%s", MAC2STR(sta->addr), ocv_errorstr); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; } } #endif /* CONFIG_FILS && CONFIG_OCV */ @@ -4414,12 +6116,46 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, hapd->conf->max_acceptable_idle_period; } - return WLAN_STATUS_SUCCESS; + if (elems->wfa_capab) + hostapd_wfa_capab(hapd, sta, elems->wfa_capab, + elems->wfa_capab + elems->wfa_capab_len); + +out: + if (resp != WLAN_STATUS_SUCCESS || assoc_wpa_sm) { + struct wpa_state_machine *sm = sta->wpa_sm; + +#ifdef CONFIG_IEEE80211BE + clear_wpa_sm_for_each_partner_link(hapd, sta); + clear_wpa_sm_for_all_sta(hapd, sm); +#endif /* CONFIG_IEEE80211BE */ + + wpa_auth_sta_deinit(sm); + + /* Only keep a reference to the main wpa_sm and drop the + * per-link instance. + * This reference is needed during group rekey handling. + */ + if (resp == WLAN_STATUS_SUCCESS) { + sta->wpa_sm = assoc_wpa_sm; +#ifdef CONFIG_IEEE80211BE + set_wpa_sm_for_each_partner_link(hapd, sta, + assoc_wpa_sm); +#endif /* CONFIG_IEEE80211BE */ + } else { + sta->wpa_sm = NULL; +#ifdef CONFIG_IEEE80211BE + clear_wpa_sm_for_each_partner_link(hapd, sta); +#endif /* CONFIG_IEEE80211BE */ + } + } + + return resp; } static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *ies, size_t ies_len, int reassoc) + const u8 *ies, size_t ies_len, + enum link_parse_type type) { struct ieee802_11_elems elems; @@ -4430,15 +6166,14 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc, - false); + return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, type, NULL); } #ifdef CONFIG_IEEE80211BE -static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd, - struct mld_link_info *link) +void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd, + struct mld_link_info *link) { u8 buf[EHT_ML_MAX_STA_PROF_LEN]; u8 *p = buf; @@ -4462,18 +6197,18 @@ static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd, p = hostapd_eid_ht_capabilities(hapd, p); p = hostapd_eid_ht_operation(hapd, p); - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + if (hostapd_is_vht_enabled(hapd)) { p = hostapd_eid_vht_capabilities(hapd, p, 0); p = hostapd_eid_vht_operation(hapd, p); } - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_he_operation(hapd, p); p = hostapd_eid_spatial_reuse(hapd, p); p = hostapd_eid_he_mu_edca_parameter_set(hapd, p); p = hostapd_eid_he_6ghz_band_cap(hapd, p); - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_eht_operation(hapd, p); } @@ -4498,11 +6233,12 @@ out: } -static int ieee80211_ml_process_link(struct hostapd_data *hapd, - struct sta_info *origin_sta, - struct mld_link_info *link, - const u8 *ies, size_t ies_len, - bool reassoc, bool offload) +int ieee80211_ml_process_link(struct hostapd_data *hapd, + struct sta_info *origin_sta, + struct mld_link_info *link, + const u8 *ies, size_t ies_len, + enum link_parse_type type, bool offload, + bool *set_beacon) { struct ieee802_11_elems elems; struct wpabuf *mlbuf = NULL; @@ -4520,7 +6256,7 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd, } sta = ap_get_sta(hapd, origin_sta->addr); - if (sta) { + if (sta || TEST_FAIL()) { wpa_printf(MSG_INFO, "MLD: link: Station already exists"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; sta = NULL; @@ -4534,36 +6270,48 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd, goto out; } - mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true); - if (!mlbuf) - goto out; + if (type != LINK_PARSE_RECONF) { + mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, + true); + if (!mlbuf) + goto out; - if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf, - hapd->mld_link_id, true) == - ParseFailed) { - wpa_printf(MSG_DEBUG, - "MLD: link: Failed to parse association request Multi-Link element"); - status = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto out; + if (ieee802_11_parse_link_assoc_req(&elems, mlbuf, + hapd->mld_link_id, true) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "MLD: link: Failed to parse association request Multi-Link element"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto out; + } } sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK; sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id; + ap_sta_set_mld(sta, true); + sta->auth_alg = origin_sta->auth_alg; +#ifdef CONFIG_ENC_ASSOC + sta->epp_sta = origin_sta->epp_sta; +#endif /* CONFIG_ENC_ASSOC */ - status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true); + status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, type, + origin_sta->wpa_sm); if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "MLD: link: Element check failed"); goto out; } - ap_sta_set_mld(sta, true); - os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info)); for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { struct mld_link_info *li = &sta->mld_info.links[i]; li->resp_sta_profile = NULL; li->resp_sta_profile_len = 0; + + if (type == LINK_PARSE_RECONF && i == hapd->mld_link_id) { + os_memcpy(li->local_addr, hapd->own_addr, ETH_ALEN); + os_memcpy(li->peer_addr, link->peer_addr, ETH_ALEN); + } } if (!offload) { @@ -4579,16 +6327,10 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd, } hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32); sta->listen_interval = origin_sta->listen_interval; - if (update_ht_state(hapd, sta) > 0) - ieee802_11_update_beacons(hapd->iface); + if (update_ht_state(hapd, sta) > 0 && set_beacon) + *set_beacon = true; } - /* Maintain state machine reference on all link STAs, this is needed - * during group rekey handling. - */ - wpa_auth_sta_deinit(sta->wpa_sm); - sta->wpa_sm = origin_sta->wpa_sm; - /* * Do not initialize the EAPOL state machine. * TODO: Maybe it is needed? @@ -4599,20 +6341,24 @@ static int ieee80211_ml_process_link(struct hostapd_data *hapd, hapd->mld_link_id, sta->aid); sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK; + sta->vlan_id = origin_sta->vlan_id; /* TODO: What other processing is required? */ - if (!offload && add_associated_sta(hapd, sta, reassoc)) + if (!offload && + add_associated_sta(hapd, sta, type == LINK_PARSE_REASSOC)) status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; out: wpabuf_free(mlbuf); link->status = status; - if (!offload) + if (!offload && type != LINK_PARSE_RECONF) ieee80211_ml_build_assoc_resp(hapd, link); wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status); if (status != WLAN_STATUS_SUCCESS) { + wpa_release_link_auth_ref(origin_sta->wpa_sm, + hapd->mld_link_id, true); if (sta) ap_free_sta(hapd, sta); return -1; @@ -4622,8 +6368,10 @@ out: } -bool hostapd_is_mld_ap(struct hostapd_data *hapd) +bool hostapd_is_multiple_link_mld(struct hostapd_data *hapd) { + struct hostapd_data *bss; + if (!hapd->conf->mld_ap) return false; @@ -4631,7 +6379,18 @@ bool hostapd_is_mld_ap(struct hostapd_data *hapd) hapd->iface->interfaces->count <= 1) return false; - return true; + /* + * Checking for interfaces count above is not sufficient as there + * could be non-MLD interfaces or MLD interface that are not affiliated + * with the same MLD as the currently processing one. So need to check + * if other partner links exist for this the same AP MLD. + */ + for_each_mld_link(bss, hapd) { + if (bss != hapd) + return true; + } + + return false; } #endif /* CONFIG_IEEE80211BE */ @@ -4641,12 +6400,14 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, bool reassoc, int tx_link_status, - bool offload) + bool offload, + bool *set_beacon) { + int ret = 0; #ifdef CONFIG_IEEE80211BE unsigned int i; - if (!hostapd_is_mld_ap(hapd)) + if (!hostapd_is_multiple_link_mld(hapd)) return 0; for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { @@ -4681,15 +6442,17 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd, if (!offload) ieee80211_ml_build_assoc_resp(hapd, link); } else { - if (ieee80211_ml_process_link(bss, sta, link, - ies, ies_len, reassoc, - offload)) - return -1; + if (ieee80211_ml_process_link( + bss, sta, link, ies, ies_len, + reassoc ? LINK_PARSE_REASSOC : + LINK_PARSE_ASSOC, offload, + set_beacon)) + ret = -1; } } #endif /* CONFIG_IEEE80211BE */ - return 0; + return ret; } @@ -4724,7 +6487,12 @@ static int add_associated_sta(struct hostapd_data *hapd, struct ieee80211_eht_capabilities eht_cap; int set = 1; const u8 *mld_link_addr = NULL; - bool mld_link_sta = false; + bool mld_link_sta = false, epp_sta = false; + u16 eml_cap = 0; + +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_IEEE80211BE if (ap_sta_is_mld(hapd, sta)) { @@ -4735,6 +6503,7 @@ static int add_associated_sta(struct hostapd_data *hapd, if (hapd->mld_link_id != sta->mld_assoc_link_id) set = 0; + eml_cap = sta->mld_info.common_info.eml_capa; } #endif /* CONFIG_IEEE80211BE */ @@ -4815,7 +6584,8 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->he_6ghz_capab, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, - set, mld_link_addr, mld_link_sta)) { + set, mld_link_addr, mld_link_sta, eml_cap, + epp_sta)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not %s STA to kernel driver", @@ -4838,7 +6608,7 @@ static int add_associated_sta(struct hostapd_data *hapd, static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 status_code, int reassoc, const u8 *ies, size_t ies_len, int rssi, - int omit_rsnxe, bool allow_mld_addr_trans) + int omit_rsnxe) { int send_len; u8 *buf; @@ -4863,11 +6633,13 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, buflen += 5 + sta->dpp_pfs->curve->prime_len; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP); buflen += 3 + sizeof(struct ieee80211_eht_operation); if (hapd->iconf->punct_bitmap) buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; + if (ap_sta_is_mld(hapd, sta)) + buflen += hostapd_eid_eht_ml_tid_to_link_map_len(hapd); } #endif /* CONFIG_IEEE80211BE */ @@ -4919,7 +6691,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + buflen - p, sta->auth_alg, ies, ies_len, - omit_rsnxe); + omit_rsnxe, reassoc, + sta->vlan_id); if (!p) { wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); @@ -4934,16 +6707,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK)) p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p, - buf + buflen - p, - ies, ies_len); + buf + buflen - p); #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (sta && status_code == WLAN_STATUS_SUCCESS && - (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + ((hapd->conf->wpa_key_mgmt | + hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & + WPA_KEY_MGMT_OWE)) p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, - buf + buflen - p, - ies, ies_len); + buf + buflen - p); #endif /* CONFIG_OWE */ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) @@ -4953,7 +6727,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ht_operation(hapd, p); #ifdef CONFIG_IEEE80211AC - if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac && + if (hostapd_is_vht_enabled(hapd) && !is_6ghz_op_class(hapd->iconf->op_class)) { u32 nsts = 0, sta_nsts; @@ -4977,7 +6751,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_IEEE80211AX - if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) { + if (hostapd_is_he_enabled(hapd)) { p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_he_operation(hapd, p); p = hostapd_eid_cca(hapd, p); @@ -4988,7 +6762,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211AX */ p = hostapd_eid_ext_capab(hapd, p, false); - p = hostapd_eid_bss_max_idle_period(hapd, p, sta->max_idle_period); + p = hostapd_eid_bss_max_idle_period(hapd, p, + sta ? sta->max_idle_period : 0); if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); @@ -5019,16 +6794,19 @@ rsnxe_done: #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_IEEE80211BE - if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { + if (hostapd_is_eht_enabled(hapd)) { if (hapd->conf->mld_ap) p = hostapd_eid_eht_ml_assoc(hapd, sta, p); p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP); p = hostapd_eid_eht_operation(hapd, p); + if (ap_sta_is_mld(hapd, sta)) + p = hostapd_eid_eht_ml_tid_to_link_map(hapd, p); } #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_OWE) && sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && !wpa_auth_sta_get_pmksa(sta->wpa_sm)) { @@ -5051,6 +6829,95 @@ rsnxe_done: } #endif /* CONFIG_OWE */ +#ifdef CONFIG_ENC_ASSOC + if (sta && + (sta->auth_alg == WLAN_AUTH_EPPKE || + sta->auth_alg == WLAN_AUTH_802_1X) && + wpa_auth_ap_sta_support_assoc_enc(sta->wpa_sm) && + status_code == WLAN_STATUS_SUCCESS) { + /* Ensure GMK/Counter are initialized before the Key Delivery + * element is built. For auth-frame STAs the normal 4-way + * handshake path (SM_STATE AUTHENTICATION2) is skipped, so + * wpa_group_ensure_init() must be called here instead. + * Without this, the first non-auth-frame STA that connects + * later triggers a GTK rotation that is unknown to this STA, + * breaking broadcast frame reception. */ + wpa_auth_ensure_group_init(sta->wpa_sm); + reply->frame_control |= WLAN_FC_PROTECTED; + +#ifdef CONFIG_PMKSA_PRIVACY + /* Include a Nonce element (ANonce) to compute next PMKID */ + if (wpa_auth_ap_sta_support_pmkid_privacy(sta->wpa_sm)) { + switch (sta->auth_alg) { + case WLAN_AUTH_EPPKE: +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: +#endif /* CONFIG_IEEE8021X_AUTH */ + break; + default: + wpa_printf(MSG_INFO, + "EPP: Unsupported auth alg %u for PMKID privacy support", + sta->auth_alg); + goto skip_nonce; + } + + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + NONCE_LEN; /* Length */ + *p++ = WLAN_EID_EXT_NONCE; /* Element ID Extension */ + os_memcpy(p, sta->anonce, NONCE_LEN); + p += NONCE_LEN; + } + skip_nonce: +#ifdef CONFIG_SAE + /* For EPPKE with SAE, propagate the password identifier from + * the PASN wrapped SAE commit to the WPA state machine so that + * wpa_auth_eid_key_delivery() can include the SAE PW IDs KDE + * in the encrypted (Re)Association Response frame. + */ + if (sta->auth_alg == WLAN_AUTH_EPPKE && sta->pasn && + wpa_key_mgmt_sae(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { + const u8 *pw_id = NULL; + size_t pw_id_len = 0; + + /* Use the decrypted (real) identifier when the STA + * presented an encrypted alternative identifier. + * Fall back to the raw parsed identifier for the + * plaintext case (first connection). */ + if (sta->pasn->dec_pw_id && + sta->pasn->dec_pw_id_len) { + pw_id = sta->pasn->dec_pw_id; + pw_id_len = sta->pasn->dec_pw_id_len; + } else if (sta->pasn->sae.tmp) { + if (sta->pasn->sae.tmp->parsed_pw_id) { + pw_id = sta->pasn->sae.tmp->parsed_pw_id; + pw_id_len = sta->pasn->sae.tmp->parsed_pw_id_len; + } else if (sta->pasn->sae.tmp->pw_id) { + pw_id = sta->pasn->sae.tmp->pw_id; + pw_id_len = + sta->pasn->sae.tmp->pw_id_len; + } + } + if (pw_id && pw_id_len) { + struct wpabuf *pw_id_buf; + + pw_id_buf = wpabuf_alloc_copy(pw_id, pw_id_len); + if (pw_id_buf) { + wpa_auth_set_sae_pw_id( + sta->wpa_sm, pw_id_buf, + sta->pasn->sae_pw_id_counter); + wpabuf_free(pw_id_buf); + } + } + } +#endif /* CONFIG_SAE */ +#endif /* CONFIG_PMKSA_PRIVACY */ + + p = wpa_auth_write_assoc_resp_eppke(sta->wpa_sm, p, + buf + buflen - p, + ap_sta_is_mld(hapd, sta)); + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_DPP2 if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && @@ -5179,14 +7046,14 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "OWE: Using IE override"); *status = WLAN_STATUS_SUCCESS; return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, - owe_buf_len, NULL, 0); + owe_buf_len); } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, - owe_buf_len, NULL, 0); + owe_buf_len); *status = WLAN_STATUS_SUCCESS; return owe_buf; } @@ -5202,7 +7069,7 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, return NULL; owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, - owe_buf_len, NULL, 0); + owe_buf_len); if (sta->owe_ecdh && owe_buf) { struct wpabuf *pub; @@ -5244,8 +7111,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, sta->fils_pending_assoc_is_reassoc, sta->fils_pending_assoc_req, - sta->fils_pending_assoc_req_len, 0, 0, - true); + sta->fils_pending_assoc_req_len, 0, 0); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; @@ -5291,7 +7157,7 @@ static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd, u8 mld_addr[ETH_ALEN]; const u8 *pos; - if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be) + if (!hostapd_is_eht_enabled(hapd)) return NULL; if (reassoc) { @@ -5410,10 +7276,22 @@ static void handle_assoc(struct hostapd_data *hapd, sta = handle_mlo_translate(hapd, mgmt, len, reassoc, &assoc_hapd); if (sta) { - wpa_printf(MSG_DEBUG, - "MLD: Switching to assoc hapd/station"); - hapd = assoc_hapd; - mld_addrs_not_translated = true; + if (hapd != assoc_hapd) { + wpa_printf(MSG_DEBUG, + "MLD: Switching to assoc hapd/station"); + hapd = assoc_hapd; + mld_addrs_not_translated = true; + } + + /* Allow link address to be changed if an SA query + * procedure has expired. */ + if (sta->sa_query_timed_out) { + u8 _link = hapd->mld_link_id; + + os_memcpy(sta->mld_info.links[_link].peer_addr, + mgmt->sa, ETH_ALEN); + } + } } #endif /* CONFIG_IEEE80211BE */ @@ -5536,7 +7414,9 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_MBO */ - if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) { + if (hapd->conf->wpa && + check_sa_query(hapd, sta, reassoc, pos, left, + fc & WLAN_FC_PROTECTED)) { resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; goto fail; } @@ -5575,10 +7455,16 @@ static void handle_assoc(struct hostapd_data *hapd, /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ - resp = check_assoc_ies(hapd, sta, pos, left, reassoc); + resp = check_assoc_ies(hapd, sta, pos, left, + reassoc ? LINK_PARSE_REASSOC : LINK_PARSE_ASSOC); if (resp != WLAN_STATUS_SUCCESS) goto fail; - omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX); +#ifdef CONFIG_IEEE80211R_AP + if (reassoc && sta->auth_alg == WLAN_AUTH_FT) + omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX); +#endif /* CONFIG_IEEE80211R_AP */ + if (hapd->conf->rsn_override_omit_rsnxe) + omit_rsnxe = 1; if (hostapd_get_aid(hapd, sta) < 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -5671,9 +7557,6 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_FILS */ - if (set_beacon) - ieee802_11_update_beacons(hapd->iface); - fail: /* @@ -5696,7 +7579,7 @@ static void handle_assoc(struct hostapd_data *hapd, */ if (sta) hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc, - resp, false); + resp, false, &set_beacon); if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta, reassoc)) @@ -5706,6 +7589,9 @@ static void handle_assoc(struct hostapd_data *hapd, if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && sta->fils_pending_assoc_req) { + if (set_beacon) + ieee802_11_update_beacons(hapd->iface); + /* Do not reschedule fils_hlp_timeout in case the station * retransmits (Re)Association Request frame while waiting for * the previously started FILS HLP wait, so that the timeout can @@ -5725,6 +7611,9 @@ static void handle_assoc(struct hostapd_data *hapd, sta->fils_hlp_resp = NULL; } if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { + if (set_beacon) + ieee802_11_update_beacons(hapd->iface); + sta->fils_pending_assoc_req = tmp; sta->fils_pending_assoc_req_len = left; sta->fils_pending_assoc_is_reassoc = reassoc; @@ -5739,13 +7628,25 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_FILS */ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->association_response_status_code >= 0) { + wpa_printf(MSG_DEBUG, + "TESTING: Forcing association response status code to %d", + hapd->conf->association_response_status_code); + resp = hapd->conf->association_response_status_code; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (resp >= 0) reply_res = send_assoc_resp(hapd, mld_addrs_not_translated ? NULL : sta, mgmt->sa, resp, reassoc, - pos, left, rssi, omit_rsnxe, - !mld_addrs_not_translated); + pos, left, rssi, omit_rsnxe); + + if (set_beacon) + ieee802_11_update_beacons(hapd->iface); + os_free(tmp); /* @@ -5846,7 +7747,7 @@ static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd, struct sta_info *assoc_sta; struct sta_info *tmp_sta; - if (!hostapd_is_mld_ap(hapd)) + if (!hostapd_is_multiple_link_mld(hapd)) return false; /* @@ -5970,10 +7871,51 @@ static void handle_beacon(struct hostapd_data *hapd, } +static int hostapd_action_vs(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + unsigned int freq, bool protected) +{ + const u8 *pos, *end; + u32 oui_type; + + pos = (const u8 *) &mgmt->u.action; + end = ((const u8 *) mgmt) + len; + + if (end - pos < 1 + 4) + return -1; + pos++; + + oui_type = WPA_GET_BE32(pos); + pos += 4; + + switch (oui_type) { + case WFA_CAPAB_VENDOR_TYPE: + hostapd_wfa_capab(hapd, sta, pos, end); + return 0; + default: + wpa_printf(MSG_DEBUG, + "Ignore unknown Vendor Specific Action frame OUI/type %08x%s", + oui_type, protected ? " (protected)" : ""); + break; + } + + return -1; +} + + static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && - category != WLAN_ACTION_HT; + category != WLAN_ACTION_HT && + category != WLAN_ACTION_UNPROTECTED_WNM && + category != WLAN_ACTION_SELF_PROTECTED && + category != WLAN_ACTION_UNPROTECTED_DMG && + category != WLAN_ACTION_VHT && + category != WLAN_ACTION_UNPROTECTED_S1G && + category != WLAN_ACTION_HE && + category != WLAN_ACTION_EHT && + category != WLAN_ACTION_VENDOR_SPECIFIC; } @@ -6009,7 +7951,7 @@ static int handle_action(struct hostapd_data *hapd, } if (sta && (sta->flags & WLAN_STA_MFP) && - !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && + !(mgmt->frame_control & host_to_le16(WLAN_FC_PROTECTED)) && robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -6114,14 +8056,14 @@ static int handle_action(struct hostapd_data *hapd, WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == OUI_WFA && mgmt->u.action.u.vs_public_action.variable[0] == - NAN_OUI_TYPE) { + NAN_SDF_OUI_TYPE) { const u8 *pos, *end; pos = mgmt->u.action.u.vs_public_action.variable; end = ((const u8 *) mgmt) + len; pos++; - hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq, - pos, end - pos); + hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid, + freq, pos, end - pos); return 1; } #endif /* CONFIG_NAN_USD */ @@ -6142,12 +8084,25 @@ static int handle_action(struct hostapd_data *hapd, (u8 *) mgmt, len, freq) == 0) return 1; } + if (sta && + hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0) + return 1; + break; + case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED: + if (sta && + hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0) + return 1; break; #ifndef CONFIG_NO_RRM case WLAN_ACTION_RADIO_MEASUREMENT: hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); return 1; #endif /* CONFIG_NO_RRM */ +#ifdef CONFIG_IEEE80211BE + case WLAN_ACTION_PROTECTED_EHT: + ieee802_11_rx_protected_eht_action(hapd, mgmt, len); + return 1; +#endif /* CONFIG_IEEE80211BE */ } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -6155,32 +8110,6 @@ static int handle_action(struct hostapd_data *hapd, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); - if (!is_multicast_ether_addr(mgmt->da) && - !(mgmt->u.action.category & 0x80) && - !is_multicast_ether_addr(mgmt->sa)) { - struct ieee80211_mgmt *resp; - - /* - * IEEE 802.11-REVma/D9.0 - 7.3.1.11 - * Return the Action frame to the source without change - * except that MSB of the Category set to 1. - */ - wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " - "frame back to sender"); - resp = os_memdup(mgmt, len); - if (resp == NULL) - return 0; - os_memcpy(resp->da, resp->sa, ETH_ALEN); - os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); - resp->u.action.category |= 0x80; - - if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) { - wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " - "Action frame"); - } - os_free(resp); - } return 1; } @@ -6232,10 +8161,6 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, int ret = 0; unsigned int freq; int ssi_signal = fi ? fi->ssi_signal : 0; -#ifdef CONFIG_NAN_USD - static const u8 nan_network_id[ETH_ALEN] = - { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 }; -#endif /* CONFIG_NAN_USD */ if (len < 24) return 0; @@ -6267,6 +8192,10 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, } if (!is_broadcast_ether_addr(mgmt->bssid) && +#ifdef CONFIG_NAN_USD + !nan_de_is_nan_network_id(mgmt->bssid) && + !nan_de_is_p2p_network_id(mgmt->bssid) && +#endif /* CONFIG_NAN_USD */ #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && @@ -6304,6 +8233,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_NAN_USD !ether_addr_equal(mgmt->da, nan_network_id) && + !ether_addr_equal(mgmt->da, p2p_network_id) && #endif /* CONFIG_NAN_USD */ !ether_addr_equal(mgmt->da, hapd->own_addr)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -6410,9 +8340,18 @@ static void handle_auth_cb(struct hostapd_data *hapd, fail: success_status = status_code == WLAN_STATUS_SUCCESS; #ifdef CONFIG_SAE - if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1) + if (auth_alg == WLAN_AUTH_SAE && + auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) success_status = sae_status_success(hapd, status_code); #endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE8021X_AUTH + if (auth_alg == WLAN_AUTH_802_1X && + status_code == WLAN_STATUS_802_1_X_AUTH_SUCCESS) { + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_802_1X; + success_status = true; + } +#endif /* CONFIG_IEEE8021X_AUTH */ if (!success_status && sta->added_unassoc) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; @@ -6500,7 +8439,7 @@ static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211BE struct hostapd_data *tmp_hapd; - if (!hostapd_is_mld_ap(hapd)) + if (!hostapd_is_multiple_link_mld(hapd)) return; for_each_mld_link(tmp_hapd, hapd) { @@ -6599,12 +8538,13 @@ static void handle_assoc_cb(struct hostapd_data *hapd, new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; - if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && - !hapd->conf->osen) || + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || sta->auth_alg == WLAN_AUTH_FILS_SK || sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || sta->auth_alg == WLAN_AUTH_FILS_PK || - sta->auth_alg == WLAN_AUTH_FT) { + sta->auth_alg == WLAN_AUTH_FT || + sta->auth_alg == WLAN_AUTH_EPPKE || + sta->auth_alg == WLAN_AUTH_802_1X) { /* * Open, static WEP, FT protocol, or FILS; no separate * authorization step. @@ -6617,7 +8557,86 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else mlme_associate_indication(hapd, sta); - sta->sa_query_timed_out = 0; + ap_sta_set_sa_query_timeout(hapd, sta, 0); + +#ifdef CONFIG_PMKSA_PRIVACY + if (ok && status == WLAN_STATUS_SUCCESS && sta->epp_sta && + wpa_auth_ap_sta_support_pmkid_privacy(sta->wpa_sm)) { + bool is_ml = ap_sta_is_mld(hapd, sta); + struct rsn_pmksa_cache_entry *entry, *next; + struct rsn_pmksa_cache *pmksa, *t_pmksa; + const u8 *pmkid_cur, *pmkid_next; + + switch (sta->auth_alg) { + case WLAN_AUTH_EPPKE: + if (!sta->pasn) { + wpa_printf(MSG_INFO, "EPP: Missing PASN data"); + goto skip_update; + } + pmkid_cur = sta->pasn->epp_pmkid_cur; + pmkid_next = sta->epp_pmkid_next; + break; +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: + pmkid_cur = sta->eap_auth_data.epp_pmkid_cur; + pmkid_next = sta->eap_auth_data.epp_pmkid_next; + break; +#endif /* CONFIG_IEEE8021X_AUTH */ + default: + wpa_printf(MSG_INFO, + "EPP: Unsupported auth alg %u for PMKID privacy support", + sta->auth_alg); + goto skip_update; + } + + pmksa = t_pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth, + is_ml); + + entry = pmksa_cache_auth_get(t_pmksa, NULL, pmkid_cur); + if (entry) + goto update_pmksa_entry; + +#ifdef CONFIG_IEEE80211BE + if (!entry && is_ml) { + struct hostapd_data *tmp_hapd; + + /* Search in link caches of each AP MLD link */ + for_each_mld_link(tmp_hapd, hapd) { + t_pmksa = wpa_auth_get_pmksa_cache( + tmp_hapd->wpa_auth, false); + entry = pmksa_cache_auth_get(t_pmksa, NULL, + pmkid_cur); + if (entry) + break; + } + } else if (!entry && !is_ml && hapd->conf->mld_ap) { + /* Search in the MLD cache */ + t_pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth, + true); + entry = pmksa_cache_auth_get(t_pmksa, NULL, pmkid_cur); + } +#endif /* CONFIG_IEEE80211BE */ + +update_pmksa_entry: + if (entry) { + wpa_printf(MSG_DEBUG, + "EPP: PMKSA caching privacy on - update PMKSA cache entry"); + next = os_memdup(entry, sizeof(*entry)); + if (!next) + goto skip_update; + os_memcpy(next->pmkid, pmkid_next, PMKID_LEN); + os_memcpy(next->spa, sta->addr, ETH_ALEN); + next->vlan_desc = NULL; + next->identity = NULL; + next->dpp_pkhash = NULL; + next->cui = NULL; + pmksa_cache_from_eapol_data(next, sta->eapol_sm); + pmksa_cache_free_entry(t_pmksa, entry); + pmksa_cache_auth_add_entry(pmksa, next); + } + } +skip_update: +#endif /* CONFIG_PMKSA_PRIVACY */ if (sta->eapol_sm == NULL) { /* @@ -6814,6 +8833,19 @@ static void handle_action_cb(struct hostapd_data *hapd, } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_IEEE80211BE + /* Frame header (24B) + Category (1B) + Action code (1B) + + * Dialog token (1B) + Count (1B) + Status list (count * 3B) + */ + if (len >= IEEE80211_HDRLEN + 3 + 1 + 3 && + mgmt->u.action.category == WLAN_ACTION_PROTECTED_EHT && + mgmt->u.action.u.link_reconf_resp.action == + WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE) { + hostapd_link_reconf_resp_tx_status(hapd, sta, mgmt, len, ok); + return; + } +#endif /* CONFIG_IEEE80211BE */ + #ifndef CONFIG_NO_RRM if (len < 24 + 5 + sizeof(*report)) return; @@ -6905,8 +8937,16 @@ int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { - /* TODO */ - return 0; + int len = 0, ret; + + ret = os_snprintf(buf + len, buflen - len, + "auth_alg=%d\n", + sta->auth_alg); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + return len; } @@ -7103,42 +9143,59 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) */ if (is_6ghz_op_class(iconf->op_class)) { enum max_tx_pwr_interpretation tx_pwr_intrpn; + int err; + int max_eirp_psd = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + int max_eirp_power = iconf->reg_def_cli_eirp; /* Same Maximum Transmit Power for all 20 MHz bands */ tx_pwr_count = 0; tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD; /* Default Transmit Power Envelope for Global Operating Class */ - if (hapd->iconf->reg_def_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_def_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + err = hostap_afc_get_chan_max_eirp_power(iface, true, + &max_eirp_psd); + if (err < 0) { + if (hapd->iconf->reg_def_cli_eirp_psd != -1) + max_eirp_psd = + hapd->iconf->reg_def_cli_eirp_psd; + else + max_eirp_psd = + REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, - REG_DEFAULT_CLIENT, tx_pwr); + REG_DEFAULT_CLIENT, max_eirp_psd); /* Indoor Access Point must include an additional TPE for * subordinate devices */ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) { - /* TODO: Extract PSD limits from channel data */ - if (hapd->iconf->reg_sub_cli_eirp_psd != -1) - tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd; - else - tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + if (err < 0) { + /* non-AFC connection */ + if (hapd->iconf->reg_sub_cli_eirp_psd != -1) + max_eirp_psd = + hapd->iconf->reg_sub_cli_eirp_psd; + else + max_eirp_psd = + REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2; + } eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn, REG_SUBORDINATE_CLIENT, - tx_pwr); + max_eirp_psd); } - if (iconf->reg_def_cli_eirp != -1 && - he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) - eid = hostapd_add_tpe_info( - eid, tx_pwr_count, REGULATORY_CLIENT_EIRP, - REG_DEFAULT_CLIENT, - hapd->iconf->reg_def_cli_eirp); + if (hostap_afc_get_chan_max_eirp_power(iface, false, + &max_eirp_power)) { + max_eirp_power = iconf->reg_def_cli_eirp; + if (max_eirp_power == -1 || + !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) + return eid; + } - return eid; + return hostapd_add_tpe_info(eid, tx_pwr_count, + REGULATORY_CLIENT_EIRP, + REG_DEFAULT_CLIENT, + max_eirp_power); } #endif /* CONFIG_IEEE80211AX */ @@ -7220,37 +9277,160 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) } -u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +/* Wide Bandwidth Channel Switch subelement */ +static u8 * hostapd_eid_wb_channel_switch(struct hostapd_data *hapd, u8 *eid, + u8 chan1, u8 chan2) { - u8 bw, chan1 = 0, chan2 = 0; + u8 bw; + + /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per + * IEEE Std 802.11-2024, 9.4.2.156 and Table 9-316 (VHT Operation + * Information subfields). + */ + switch (hapd->cs_freq_params.bandwidth) { + case 320: + /* As per IEEE Std 802.11be-2024, 35.15.3 (Channel switching + * methods for an EHT BSS), for EHT BSS operating channel width + * wider than 160 MHz, the announced BSS bandwidth in the Wide + * Bandwidth Channel Switch element is less than the BSS + * bandwidth in the Bandwidth Indication element + */ + + /* Modifying the center frequency to 160 MHz */ + if (hapd->cs_freq_params.channel < chan1) + chan1 -= 16; + else + chan1 += 16; + + /* fallthrough */ + case 160: + /* Update the CCFS0 and CCFS1 values in the element based on + * IEEE Std 802.11-2024, Table 9-316 (VHT Operation + * Information subfields). + */ + + /* CCFS1 - The channel center frequency index of the 160 MHz + * channel. */ + chan2 = chan1; + + /* CCFS0 - The channel center frequency index of the 80 MHz + * channel segment that contains the primary channel. */ + if (hapd->cs_freq_params.channel < chan1) + chan1 -= 8; + else + chan1 += 8; + + bw = 1; + break; + case 80: + bw = 1; + break; + case 40: + bw = 0; + break; + default: + /* not valid VHT bandwidth or not in CSA */ + return eid; + } + + *eid++ = WLAN_EID_WIDE_BW_CHSWITCH; + *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ + *eid++ = bw; /* New Channel Width */ + *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ + *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ + + return eid; +} + + +#ifdef CONFIG_IEEE80211BE +/* Bandwidth Indication element that is also used as the Bandwidth Indication + * For Channel Switch subelement within a Channel Switch Wrapper element. */ +static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid, + u8 chan1, u8 chan2) +{ + u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap; + struct ieee80211_bw_ind_element *bw_ind_elem; + size_t elen = 4; + + if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap) + return eid; + + if (punct_bitmap) + elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; + + *eid++ = WLAN_EID_EXTENSION; + *eid++ = 1 + elen; + *eid++ = WLAN_EID_EXT_BANDWIDTH_INDICATION; + + bw_ind_elem = (struct ieee80211_bw_ind_element *) eid; + os_memset(bw_ind_elem, 0, sizeof(struct ieee80211_bw_ind_element)); + + switch (hapd->cs_freq_params.bandwidth) { + case 320: + bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_320MHZ; + chan2 = chan1; + if (hapd->cs_freq_params.channel < chan1) + chan1 -= 16; + else + chan1 += 16; + break; + case 160: + bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_160MHZ; + chan2 = chan1; + if (hapd->cs_freq_params.channel < chan1) + chan1 -= 8; + else + chan1 += 8; + break; + case 80: + bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_80MHZ; + break; + case 40: + if (hapd->cs_freq_params.sec_channel_offset == 1) + bw_ind_elem->bw_ind_info.control |= + BW_IND_CHANNEL_WIDTH_40MHZ; + else + bw_ind_elem->bw_ind_info.control |= + BW_IND_CHANNEL_WIDTH_20MHZ; + break; + default: + bw_ind_elem->bw_ind_info.control |= BW_IND_CHANNEL_WIDTH_20MHZ; + break; + } + + bw_ind_elem->bw_ind_info.ccfs0 = chan1; + bw_ind_elem->bw_ind_info.ccfs1 = chan2; + + if (punct_bitmap) { + bw_ind_elem->bw_ind_params |= + BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT; + bw_ind_elem->bw_ind_info.disabled_chan_bitmap = + host_to_le16(punct_bitmap); + } + + return eid + elen; +} +#endif /* CONFIG_IEEE80211BE */ + + +u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +{ + u8 chan1 = 0, chan2 = 0; + u8 *eid_len_offset; int freq1; + if (!hostapd_is_vht_enabled(hapd) && + !hostapd_is_he_enabled(hapd) && + !hostapd_is_eht_enabled(hapd)) + return eid; + if (!hapd->cs_freq_params.channel || (!hapd->cs_freq_params.vht_enabled && !hapd->cs_freq_params.he_enabled && !hapd->cs_freq_params.eht_enabled)) return eid; - /* bandwidth: 0: 40, 1: 80, 160, 80+80, 4: 320 as per - * IEEE P802.11-REVme/D4.0, 9.4.2.159 and Table 9-314. */ - switch (hapd->cs_freq_params.bandwidth) { - case 40: - bw = 0; - break; - case 80: - bw = 1; - break; - case 160: - bw = 1; - break; - case 320: - bw = 4; - break; - default: - /* not valid VHT bandwidth or not in CSA */ - return eid; - } - freq1 = hapd->cs_freq_params.center_freq1 ? hapd->cs_freq_params.center_freq1 : hapd->cs_freq_params.freq; @@ -7264,28 +9444,18 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) return eid; *eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER; - *eid++ = 5; /* Length of Channel Switch Wrapper */ - *eid++ = WLAN_EID_WIDE_BW_CHSWITCH; - *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ - *eid++ = bw; /* New Channel Width */ - if (hapd->cs_freq_params.bandwidth == 160) { - /* Update the CCFS0 and CCFS1 values in the element based on - * IEEE P802.11-REVme/D4.0, Table 9-314 */ + eid_len_offset = eid++; /* Length of Channel Switch Wrapper element */ - /* CCFS1 - The channel center frequency index of the 160 MHz - * channel. */ - chan2 = chan1; + eid = hostapd_eid_wb_channel_switch(hapd, eid, chan1, chan2); - /* CCFS0 - The channel center frequency index of the 80 MHz - * channel segment that contains the primary channel. */ - if (hapd->cs_freq_params.channel < chan1) - chan1 -= 8; - else - chan1 += 8; +#ifdef CONFIG_IEEE80211BE + if (hostapd_is_eht_enabled(hapd)) { + /* Bandwidth Indication For Channel Switch subelement */ + eid = hostapd_eid_bw_indication(hapd, eid, chan1, chan2); } - *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ - *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ +#endif /* CONFIG_IEEE80211BE */ + *eid_len_offset = (eid - eid_len_offset) - 1; return eid; } @@ -7320,6 +9490,44 @@ static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd, } +#ifdef CONFIG_IEEE80211BE +static bool hostapd_mbssid_mld_match(struct hostapd_data *tx_hapd, + struct hostapd_data *ml_hapd, + u8 *match_idx) +{ + size_t bss_idx; + + if (!ml_hapd->conf->mld_ap) + return false; + + if (!tx_hapd->iconf->mbssid || tx_hapd->iface->num_bss <= 1) { + if (hostapd_is_ml_partner(tx_hapd, ml_hapd)) { + if (match_idx) + *match_idx = 0; + return true; + } + + return false; + } + + for (bss_idx = 0; bss_idx < tx_hapd->iface->num_bss; bss_idx++) { + struct hostapd_data *bss = tx_hapd->iface->bss[bss_idx]; + + if (!bss) + continue; + + if (hostapd_is_ml_partner(bss, ml_hapd)) { + if (match_idx) + *match_idx = bss_idx; + return true; + } + } + + return false; +} +#endif /* CONFIG_IEEE80211BE */ + + struct mbssid_ie_profiles { u8 start; u8 end; @@ -7328,15 +9536,22 @@ struct mbssid_ie_profiles { static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles, bool ap_mld, u8 tbtt_info_len, bool mld_update, struct hostapd_data *reporting_hapd, - struct hostapd_data *bss) + struct hostapd_data *bss, u8 *match_idx) { - if (skip_profiles && + bool reporting_ap_mld = false; + +#ifdef CONFIG_IEEE80211BE + reporting_ap_mld = !!reporting_hapd->conf->mld_ap; +#endif /* CONFIG_IEEE80211BE */ + + if (!mld_update && skip_profiles && i >= skip_profiles->start && i < skip_profiles->end) return true; - /* No need to report if length is for normal TBTT and the BSS is - * affiliated with an AP MLD. MLD TBTT will include this. */ - if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld) + /* No need to report if length is for normal TBTT and both the reporting + * AP and neighbor AP are affiliated with an AP MLD. MLD TBTT will + * include this. */ + if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld && reporting_ap_mld) return true; /* No need to report if length is for MLD TBTT and the BSS is not @@ -7352,7 +9567,17 @@ static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles, /* If building for ML RNR and they are not ML partners, don't include. */ - if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss)) + if (mld_update && + !hostapd_mbssid_mld_match(reporting_hapd, bss, match_idx)) + return true; + + /* When MLD parameters are added to beacon RNR and in case of EMA + * beacons we report only affiliated APs belonging to the reported + * non Tx profiles and TX profile will be reported in every EMA beacon. + */ + if (mld_update && skip_profiles && match_idx && + (*match_idx < skip_profiles->start || + *match_idx >= skip_profiles->end)) return true; #endif /* CONFIG_IEEE80211BE */ @@ -7360,6 +9585,60 @@ static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles, } +static bool hostapd_rnr_get_bss_info(struct hostapd_data *hapd, + struct hostapd_data *reporting_hapd, + struct mbssid_ie_profiles *skip_profiles, + size_t i, u8 tbtt_info_len, + bool mld_update, + u8 *op_class, u8 *channel, u8 *match_idx) +{ + struct hostapd_data *bss; + bool ap_mld = false; + u8 tmp_match_idx = 255; + enum oper_chan_width bss_chwidth; + int secondary_channel; + u8 seg0, seg1; + + if (!hapd->iface || i >= hapd->iface->num_bss || !op_class || !channel) + return false; + + bss = hapd->iface->bss[i]; + if (!bss || !bss->conf || !bss->started || !bss->beacon_set_done || + bss == reporting_hapd) + return false; + +#ifdef CONFIG_IEEE80211BE + ap_mld = !!bss->conf->mld_ap; +#endif /* CONFIG_IEEE80211BE */ + + /* MLD RNR has to be included for the parameter change count */ + if (bss->conf->ignore_broadcast_ssid && !(ap_mld && mld_update)) + return false; + + if (!match_idx) + match_idx = &tmp_match_idx; + + if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len, + mld_update, reporting_hapd, bss, match_idx)) + return false; + + hostapd_get_oper_chan_info_of_bss(bss, &bss_chwidth, &seg0, &seg1); + secondary_channel = bss->iconf->secondary_channel; + + if (seg0 == bss->iconf->channel && + bss_chwidth == CONF_OPER_CHWIDTH_USE_HT) + secondary_channel = 0; + + if (ieee80211_freq_to_channel_ext( + bss->iface->freq, + secondary_channel, bss_chwidth, + op_class, channel) == NUM_HOSTAPD_MODES) + return false; + + return true; +} + + static size_t hostapd_eid_rnr_iface_len(struct hostapd_data *hapd, struct hostapd_data *reporting_hapd, @@ -7367,83 +9646,120 @@ hostapd_eid_rnr_iface_len(struct hostapd_data *hapd, struct mbssid_ie_profiles *skip_profiles, bool mld_update) { + struct hostapd_iface *iface = hapd->iface; size_t total_len = 0, len = *current_len; - int tbtt_count, total_tbtt_count = 0; - size_t i, start; + int total_tbtt_count = 0; + size_t i; u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN; + bool reporting_ap_mld = false; + bool have_pending_group; + u8 pending_op_class = 0, pending_channel = 0; + bool *tbtt_added = NULL; + +#ifdef CONFIG_IEEE80211BE + reporting_ap_mld = !!reporting_hapd->conf->mld_ap; +#endif /* CONFIG_IEEE80211BE */ repeat_rnr_len: - start = 0; - tbtt_count = 0; + os_free(tbtt_added); + tbtt_added = os_zalloc(iface->num_bss); + if (!tbtt_added) + return total_len; + + have_pending_group = false; + for (;;) { + int tbtt_count = 0; + bool group_found = false, group_pending = false; + u8 rnr_op_class = 0, rnr_channel = 0; + + if (have_pending_group) { + rnr_op_class = pending_op_class; + rnr_channel = pending_channel; + group_found = true; + } + + if (!group_found) { + for (i = 0; i < iface->num_bss; i++) { + if (tbtt_added[i]) + continue; + if (!hostapd_rnr_get_bss_info( + hapd, reporting_hapd, skip_profiles, + i, tbtt_info_len, mld_update, + &rnr_op_class, &rnr_channel, NULL)) + continue; + group_found = true; + break; + } + } + + if (!group_found) + break; - while (start < hapd->iface->num_bss) { if (!len || - len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 || - tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) { + len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255) { len = RNR_HEADER_LEN; total_len += RNR_HEADER_LEN; - tbtt_count = 0; } len += RNR_TBTT_HEADER_LEN; total_len += RNR_TBTT_HEADER_LEN; - for (i = start; i < hapd->iface->num_bss; i++) { - struct hostapd_data *bss = hapd->iface->bss[i]; - bool ap_mld = false; + for (i = 0; i < iface->num_bss; i++) { + u8 bss_op_class, bss_channel; - if (!bss || !bss->conf || !bss->started) + if (tbtt_added[i]) continue; -#ifdef CONFIG_IEEE80211BE - ap_mld = bss->conf->mld_ap; -#endif /* CONFIG_IEEE80211BE */ - - if (bss == reporting_hapd || - bss->conf->ignore_broadcast_ssid) + if (!hostapd_rnr_get_bss_info( + hapd, reporting_hapd, skip_profiles, + i, tbtt_info_len, mld_update, + &bss_op_class, &bss_channel, NULL)) continue; - if (hostapd_skip_rnr(i, skip_profiles, ap_mld, - tbtt_info_len, mld_update, - reporting_hapd, bss)) + if (rnr_op_class != bss_op_class || + rnr_channel != bss_channel) continue; if (len + tbtt_info_len > 255 || - tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) + tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) { + group_pending = true; break; + } len += tbtt_info_len; total_len += tbtt_info_len; tbtt_count++; + tbtt_added[i] = true; } - start = i; - } - total_tbtt_count += tbtt_count; - - /* If building for co-location, re-build again but this time include - * ML TBTTs. - */ - if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) { - tbtt_info_len = RNR_TBTT_INFO_MLD_LEN; - - /* If no TBTT was found, adjust the len and total_len since it - * would have incremented before we checked all BSSs. */ if (!tbtt_count) { len -= RNR_TBTT_HEADER_LEN; total_len -= RNR_TBTT_HEADER_LEN; + break; } + total_tbtt_count += tbtt_count; + + if (group_pending) { + have_pending_group = true; + pending_op_class = rnr_op_class; + pending_channel = rnr_channel; + } else { + have_pending_group = false; + } + } + + /* If building for co-location, re-build again but this time include + * ML TBTTs if the reporting AP is affiliated with an AP MLD. + */ + if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN && + reporting_ap_mld) { + tbtt_info_len = RNR_TBTT_INFO_MLD_LEN; goto repeat_rnr_len; } - /* This is possible when in the re-built case and no suitable TBTT was - * found. Adjust the length accordingly. */ - if (!tbtt_count && total_tbtt_count) { - len -= RNR_TBTT_HEADER_LEN; - total_len -= RNR_TBTT_HEADER_LEN; - } + os_free(tbtt_added); if (!total_tbtt_count) total_len = 0; @@ -7521,6 +9837,7 @@ static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd, static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type, + struct mbssid_ie_profiles *skip_profiles, size_t *current_len) { size_t len = 0; @@ -7528,7 +9845,7 @@ static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type, struct hostapd_iface *iface; size_t i; - if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap) + if (!hapd->iface || !hapd->iface->interfaces) return 0; /* TODO: Allow for FILS/Action as well */ @@ -7543,7 +9860,8 @@ static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type, continue; len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd, - current_len, NULL, true); + current_len, skip_profiles, + true); } #endif /* CONFIG_IEEE80211BE */ @@ -7587,7 +9905,8 @@ size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, if (include_mld_params && (type != WLAN_FC_STYPE_BEACON || hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED)) - total_len += hostapd_eid_rnr_mlo_len(hapd, type, ¤t_len); + total_len += hostapd_eid_rnr_mlo_len(hapd, type, NULL, + ¤t_len); return total_len; } @@ -7650,30 +9969,27 @@ static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid, static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd, struct hostapd_data *reporting_hapd, - struct mbssid_ie_profiles *skip_profiles, size_t i, u8 *tbtt_count, size_t *len, u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len, - u8 op_class, bool mld_update) + u8 op_class, u8 channel, + u8 match_idx) { struct hostapd_iface *iface = hapd->iface; struct hostapd_data *bss = iface->bss[i]; u8 bss_param = 0; +#ifdef CONFIG_IEEE80211BE bool ap_mld = false; +#endif /* CONFIG_IEEE80211BE */ u8 *eid = *pos; -#ifdef CONFIG_IEEE80211BE - ap_mld = !!hapd->conf->mld_ap; -#endif /* CONFIG_IEEE80211BE */ - - if (!bss || !bss->conf || !bss->started || - bss == reporting_hapd || bss->conf->ignore_broadcast_ssid) + if (!bss || !bss->conf || bss == reporting_hapd) return false; - if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len, - mld_update, reporting_hapd, bss)) - return false; +#ifdef CONFIG_IEEE80211BE + ap_mld = !!bss->conf->mld_ap; +#endif /* CONFIG_IEEE80211BE */ - if (*len + RNR_TBTT_INFO_LEN > 255 || + if (*len + tbtt_info_len > 255 || *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) return true; @@ -7683,7 +9999,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd, *tbtt_count_pos = eid++; *eid++ = tbtt_info_len; *eid++ = op_class; - *eid++ = bss->iconf->channel; + *eid++ = channel; *len += RNR_TBTT_HEADER_LEN; } @@ -7701,7 +10017,7 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd, bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID; } - if (is_6ghz_op_class(hapd->iconf->op_class) && + if (is_6ghz_op_class(op_class) && bss->conf->unsol_bcast_probe_resp_interval) bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE; @@ -7711,24 +10027,24 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd, *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER; #ifdef CONFIG_IEEE80211BE - if (ap_mld) { + /* Include the MLD parameters only when TBTT length is for ML RNR */ + if (ap_mld && tbtt_info_len == RNR_TBTT_INFO_MLD_LEN) { u8 param_ch = bss->eht_mld_bss_param_change; - bool is_partner; - /* If BSS is not a partner of the reporting_hapd + /* If BSS is not a partner of the reporting_hapd or + * it is one of the nontransmitted hapd, * a) MLD ID advertised shall be 255. * b) Link ID advertised shall be 15. * c) BPCC advertised shall be 255 */ - is_partner = hostapd_is_ml_partner(bss, reporting_hapd); /* MLD ID */ - *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF; + *eid++ = match_idx; /* Link ID (Bit 3 to Bit 0) * BPCC (Bit 4 to Bit 7) */ - *eid++ = is_partner ? + *eid++ = match_idx < 255 ? bss->mld_link_id | ((param_ch & 0xF) << 4) : (MAX_NUM_MLD_LINKS | 0xF0); /* BPCC (Bit 3 to Bit 0) */ - *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F; + *eid = match_idx < 255 ? ((param_ch & 0xF0) >> 4) : 0x0F; #ifdef CONFIG_TESTING_OPTIONS if (bss->conf->mld_indicate_disabled) *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED; @@ -7752,65 +10068,125 @@ static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd, bool mld_update) { struct hostapd_iface *iface = hapd->iface; - size_t i, start; + size_t i; size_t len = *current_len; u8 *eid_start = eid, *size_offset = (eid - len) + 1; u8 *tbtt_count_pos = size_offset + 1; - u8 tbtt_count, total_tbtt_count = 0, op_class, channel; + u8 total_tbtt_count = 0; u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN; + bool reporting_ap_mld = false; + bool have_pending_group; + u8 pending_op_class = 0, pending_channel = 0; + bool *tbtt_added = NULL; if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq) return eid; - if (ieee80211_freq_to_channel_ext(iface->freq, - hapd->iconf->secondary_channel, - hostapd_get_oper_chwidth(hapd->iconf), - &op_class, &channel) == - NUM_HOSTAPD_MODES) - return eid; +#ifdef CONFIG_IEEE80211BE + reporting_ap_mld = !!reporting_hapd->conf->mld_ap; +#endif /* CONFIG_IEEE80211BE */ repeat_rnr: - start = 0; - tbtt_count = 0; - while (start < iface->num_bss) { + os_free(tbtt_added); + tbtt_added = os_zalloc(iface->num_bss); + if (!tbtt_added) + return eid; + + have_pending_group = false; + for (;;) { + u8 tbtt_count = 0; + bool group_found = false, group_pending = false; + u8 rnr_op_class = 0, rnr_channel = 0; + + if (have_pending_group) { + rnr_op_class = pending_op_class; + rnr_channel = pending_channel; + group_found = true; + } + + if (!group_found) { + for (i = 0; i < iface->num_bss; i++) { + if (tbtt_added[i]) + continue; + if (!hostapd_rnr_get_bss_info( + hapd, reporting_hapd, skip_profiles, + i, tbtt_info_len, mld_update, + &rnr_op_class, &rnr_channel, NULL)) + continue; + group_found = true; + break; + } + } + + if (!group_found) + break; + if (!len || - len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 || - tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) { + len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255) { eid_start = eid; *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT; size_offset = eid++; len = RNR_HEADER_LEN; - tbtt_count = 0; } - for (i = start; i < iface->num_bss; i++) { - if (hostapd_eid_rnr_bss(hapd, reporting_hapd, - skip_profiles, i, + for (i = 0; i < iface->num_bss; i++) { + u8 op_class, channel, match_idx = 255; + + if (tbtt_added[i]) + continue; + + if (!hostapd_rnr_get_bss_info( + hapd, reporting_hapd, skip_profiles, + i, tbtt_info_len, mld_update, + &op_class, &channel, &match_idx)) + continue; + + if (rnr_op_class != op_class || + rnr_channel != channel) + continue; + + if (hostapd_eid_rnr_bss(hapd, reporting_hapd, i, &tbtt_count, &len, &eid, &tbtt_count_pos, tbtt_info_len, - op_class, mld_update)) + op_class, channel, + match_idx)) { + group_pending = true; break; - } + } - start = i; + tbtt_added[i] = true; + } if (tbtt_count) { *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1); *size_offset = (eid - size_offset) - 1; + } else { + break; + } + + total_tbtt_count += tbtt_count; + + if (group_pending) { + have_pending_group = true; + pending_op_class = rnr_op_class; + pending_channel = rnr_channel; + } else { + have_pending_group = false; } } - total_tbtt_count += tbtt_count; - /* If building for co-location, re-build again but this time include - * ML TBTTs. + * ML TBTTs if the reporting AP is affiliated with an AP MLD. */ - if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) { + if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN && + reporting_ap_mld) { tbtt_info_len = RNR_TBTT_INFO_MLD_LEN; goto repeat_rnr; } + os_free(tbtt_added); + if (!total_tbtt_count) return eid_start; @@ -7819,8 +10195,8 @@ repeat_rnr: } -u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid, - size_t *current_len) +static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid, + size_t *current_len) { struct hostapd_iface *iface; size_t i; @@ -7844,14 +10220,16 @@ u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid, } -u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type, - u8 *eid, size_t *current_len) +static u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type, + u8 *eid, + struct mbssid_ie_profiles *skip_profiles, + size_t *current_len) { #ifdef CONFIG_IEEE80211BE struct hostapd_iface *iface; size_t i; - if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap) + if (!hapd->iface || !hapd->iface->interfaces) return eid; /* TODO: Allow for FILS/Action as well */ @@ -7866,7 +10244,7 @@ u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type, continue; eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid, - current_len, NULL, true); + current_len, skip_profiles, true); } #endif /* CONFIG_IEEE80211BE */ @@ -7910,7 +10288,7 @@ u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, if (include_mld_params && (type != WLAN_FC_STYPE_BEACON || hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED)) - eid = hostapd_eid_rnr_mlo(hapd, type, eid, ¤t_len); + eid = hostapd_eid_rnr_mlo(hapd, type, eid, NULL, ¤t_len); if (eid == eid_start + 2) return eid_start; @@ -7956,8 +10334,8 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd, size_t known_bss_len) { struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd); - size_t len, i; - u8 ext_capa[20]; + size_t len, i, tx_xrate_len; + u8 ext_capa[20], buf[100]; /* Element ID: 1 octet * Length: 1 octet @@ -7970,10 +10348,12 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd, */ len = 1; + tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf; + for (i = *bss_index; i < hapd->iface->num_bss; i++) { struct hostapd_data *bss = hapd->iface->bss[i]; const u8 *auth, *rsn = NULL, *rsnx = NULL; - size_t nontx_profile_len, auth_len; + size_t nontx_profile_len, auth_len, xrate_len; u8 ie_count = 0; if (!bss || !bss->conf || !bss->started || @@ -8011,12 +10391,25 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd, ie_count++; if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX)) ie_count++; - if (bss->conf->xrates_supported) - nontx_profile_len += 8; - else if (hapd->conf->xrates_supported) + + xrate_len = hostapd_eid_ext_supp_rates(bss, buf) - buf; + + if (xrate_len) + nontx_profile_len += xrate_len; + else if (tx_xrate_len) ie_count++; + +#ifdef CONFIG_IEEE80211BE + /* For ML Probe Response frame, the solicited hapd's MLE will + * be in the frame body */ + if (bss->conf->mld_ap && + (bss != hapd || frame_type != WLAN_FC_STYPE_PROBE_RESP)) + nontx_profile_len += hostapd_eid_eht_basic_ml_len( + bss, NULL, true, false); +#endif /* CONFIG_IEEE80211BE */ + if (ie_count) - nontx_profile_len += 4 + ie_count; + nontx_profile_len += 4 + ie_count + 1; if (len + nontx_profile_len > 255) break; @@ -8031,22 +10424,34 @@ static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd, } -size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type, +size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd_probed, u32 frame_type, u8 *elem_count, const u8 *known_bss, size_t known_bss_len, size_t *rnr_len) { + struct hostapd_data *hapd = hostapd_mbssid_get_tx_bss(hapd_probed); size_t len = 0, bss_index = 1; - bool ap_mld = false; -#ifdef CONFIG_IEEE80211BE - ap_mld = hapd->conf->mld_ap; -#endif /* CONFIG_IEEE80211BE */ - - if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 || + if (!hapd->iconf->mbssid || (frame_type != WLAN_FC_STYPE_BEACON && frame_type != WLAN_FC_STYPE_PROBE_RESP)) return 0; + /* + * Include the Multiple BSSID element whenever MBSSID is enabled. The + * element may include zero or more nontransmitted BSSID profiles. + */ + if (hapd->iface->num_bss == 1) { + if (frame_type == WLAN_FC_STYPE_BEACON) { + if (!elem_count) { + wpa_printf(MSG_INFO, + "MBSSID: Insufficient data for Beacon frames"); + return 0; + } + *elem_count = 1; + } + return 3; + } + if (frame_type == WLAN_FC_STYPE_BEACON) { if (!elem_count) { wpa_printf(MSG_INFO, @@ -8059,7 +10464,7 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type, while (bss_index < hapd->iface->num_bss) { size_t rnr_count = bss_index; - len += hostapd_eid_mbssid_elem_len(hapd, frame_type, + len += hostapd_eid_mbssid_elem_len(hapd_probed, frame_type, &bss_index, known_bss, known_bss_len); @@ -8073,7 +10478,11 @@ size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type, *rnr_len += hostapd_eid_rnr_iface_len( hapd, hostapd_mbssid_get_tx_bss(hapd), - &rnr_cur_len, &skip_profiles, ap_mld); + &rnr_cur_len, &skip_profiles, false); + + *rnr_len += hostapd_eid_rnr_mlo_len( + hostapd_mbssid_get_tx_bss(hapd), frame_type, + &skip_profiles, &rnr_cur_len); } } @@ -8090,21 +10499,26 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, const u8 *known_bss, size_t known_bss_len) { struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd); - size_t i; + size_t i, tx_xrate_len; u8 *eid_len_offset, *max_bssid_indicator_offset; + u8 buf[100]; *eid++ = WLAN_EID_MULTIPLE_BSSID; eid_len_offset = eid++; max_bssid_indicator_offset = eid++; + tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf; + for (i = *bss_index; i < hapd->iface->num_bss; i++) { struct hostapd_data *bss = hapd->iface->bss[i]; struct hostapd_bss_config *conf; + struct hostapd_bss_config *tx_conf = tx_bss->conf; u8 *eid_len_pos, *nontx_bss_start = eid; const u8 *auth, *rsn = NULL, *rsnx = NULL; u8 ie_count = 0, non_inherit_ie[3]; - size_t auth_len = 0; + size_t auth_len = 0, xrate_len; u16 capab_info; + u8 mbssindex = i; if (!bss || !bss->conf || !bss->started || mbssid_known_bss(i, known_bss, known_bss_len)) @@ -8125,10 +10539,14 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len); eid += conf->ssid.ssid_len; + if (conf->mbssid_index && + conf->mbssid_index > tx_conf->mbssid_index) + mbssindex = conf->mbssid_index - tx_conf->mbssid_index; + *eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX; if (frame_type == WLAN_FC_STYPE_BEACON) { *eid++ = 3; - *eid++ = i; /* BSSID Index */ + *eid++ = mbssindex; /* BSSID Index */ if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && (conf->dtim_period % elem_count)) conf->dtim_period = elem_count; @@ -8144,7 +10562,7 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, /* Probe Request frame does not include DTIM Period and * DTIM Count fields. */ *eid++ = 1; - *eid++ = i; /* BSSID Index */ + *eid++ = mbssindex; /* BSSID Index */ } auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len); @@ -8163,15 +10581,24 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, } eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid); + xrate_len = hostapd_eid_ext_supp_rates(bss, eid) - eid; + eid += xrate_len; /* List of Element ID values in increasing order */ if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN)) non_inherit_ie[ie_count++] = WLAN_EID_RSN; - if (hapd->conf->xrates_supported && - !bss->conf->xrates_supported) + if (tx_xrate_len && !xrate_len) non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES; if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX)) non_inherit_ie[ie_count++] = WLAN_EID_RSNX; +#ifdef CONFIG_IEEE80211BE + /* For ML Probe Response frame, the solicited hapd's MLE will + * be in the frame body */ + if (bss->conf->mld_ap && + (bss != hapd || frame_type != WLAN_FC_STYPE_PROBE_RESP)) + eid = hostapd_eid_eht_basic_ml_common(bss, eid, NULL, + true, false); +#endif /* CONFIG_IEEE80211BE */ if (ie_count) { *eid++ = WLAN_EID_EXTENSION; *eid++ = 2 + ie_count + 1; @@ -8199,21 +10626,18 @@ static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end, } -u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, +u8 * hostapd_eid_mbssid(struct hostapd_data *hapd_probed, u8 *eid, u8 *end, unsigned int frame_stype, u8 elem_count, u8 **elem_offset, const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid, u8 *rnr_count, u8 **rnr_offset, size_t rnr_len) { + struct hostapd_data *hapd = hostapd_mbssid_get_tx_bss(hapd_probed); size_t bss_index = 1, cur_len = 0; u8 elem_index = 0, *rnr_start_eid = rnr_eid; - bool add_rnr, ap_mld = false; + bool add_rnr; -#ifdef CONFIG_IEEE80211BE - ap_mld = hapd->conf->mld_ap; -#endif /* CONFIG_IEEE80211BE */ - - if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 || + if (!hapd->iconf->mbssid || (frame_stype != WLAN_FC_STYPE_BEACON && frame_stype != WLAN_FC_STYPE_PROBE_RESP)) return eid; @@ -8228,6 +10652,20 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, frame_stype == WLAN_FC_STYPE_BEACON && rnr_eid && rnr_count && rnr_offset && rnr_len; + /* + * Include the Multiple BSSID element whenever MBSSID is enabled. The + * element may include zero or more nontransmitted BSSID profiles. + */ + if (hapd->iface->num_bss == 1) { + if (frame_stype == WLAN_FC_STYPE_BEACON) + elem_offset[0] = eid; + + return hostapd_eid_mbssid_elem( + hapd_probed, eid, end, frame_stype, + hostapd_max_bssid_indicator(hapd), + &bss_index, elem_count, known_bss, known_bss_len); + } + while (bss_index < hapd->iface->num_bss) { unsigned int rnr_start_count = bss_index; @@ -8241,7 +10679,8 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, elem_offset[elem_index] = eid; elem_index = elem_index + 1; } - eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype, + eid = hostapd_eid_mbssid_elem(hapd_probed, eid, end, + frame_stype, hostapd_max_bssid_indicator(hapd), &bss_index, elem_count, known_bss, known_bss_len); @@ -8256,7 +10695,10 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, cur_len = 0; rnr_eid = hostapd_eid_rnr_iface( hapd, hostapd_mbssid_get_tx_bss(hapd), - rnr_eid, &cur_len, &skip_profiles, ap_mld); + rnr_eid, &cur_len, &skip_profiles, false); + rnr_eid = hostapd_eid_rnr_mlo( + hostapd_mbssid_get_tx_bss(hapd), frame_stype, + rnr_eid, &skip_profiles, &cur_len); } } diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h index dd4995f3f31a..664d585b994c 100644 --- a/contrib/wpa/src/ap/ieee802_11.h +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -9,6 +9,8 @@ #ifndef IEEE802_11_H #define IEEE802_11_H +#include "utils/list.h" + struct hostapd_iface; struct hostapd_data; struct sta_info; @@ -24,6 +26,36 @@ struct sae_pk; struct sae_pt; struct sae_password_entry; struct mld_info; +struct mld_link_info; +struct rsn_pmksa_cache_entry; + +enum link_parse_type { + LINK_PARSE_ASSOC, + LINK_PARSE_REASSOC, + LINK_PARSE_RECONF, +}; + +#define LINK_RECONF_GROUP_KDE_MAX_LEN 255 + +struct link_reconf_req_info { + struct dl_list list; + u16 status; + u8 link_id; + u8 local_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + size_t sta_prof_len; + u8 sta_prof[]; +}; + +struct link_reconf_req_list { + u8 sta_mld_addr[ETH_ALEN]; + u8 dialog_token; + u16 links_add_ok; + u16 links_del_ok; + u16 new_valid_links; + struct dl_list del_req; /* list of struct link_reconf_req_info */ + struct dl_list add_req; /* list of struct link_reconf_req_info */ +}; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); @@ -63,7 +95,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, enum ieee80211_op_mode opmode); @@ -94,6 +126,12 @@ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd, u8 *eid, bool include_mld_id); u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info, u8 *eid); +u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, + u8 *eid, struct mld_info *mld_info, + bool include_mld_id, bool include_bpcc); +size_t hostapd_eid_eht_basic_ml_len(struct hostapd_data *hapd, + struct sta_info *info, + bool include_mld_id, bool include_pbcc); size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd, struct mld_info *info, bool include_mld_id); @@ -130,6 +168,8 @@ u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta, int hostapd_get_he_twt_responder(struct hostapd_data *hapd, enum ieee80211_op_mode mode); bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd); +void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, const u8 *end); u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); @@ -250,9 +290,12 @@ u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end, u8 **elem_offset, const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid, u8 *rnr_count, u8 **rnr_offset, size_t rnr_len); -bool hostapd_is_mld_ap(struct hostapd_data *hapd); +bool hostapd_is_multiple_link_mld(struct hostapd_data *hapd); +int sae_password_bind(struct hostapd_data *hapd, const u8 *addr, + const char *password); const char * sae_get_password(struct hostapd_data *hapd, - struct sta_info *sta, const char *rx_id, + struct sta_info *sta, const u8 *rx_id, + size_t rx_id_len, struct sae_password_entry **pw_entry, struct sae_pt **s_pt, const struct sae_pk **s_pk); struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, @@ -262,6 +305,33 @@ int hostapd_process_assoc_ml_info(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, bool reassoc, int tx_link_status, - bool offload); + bool offload, + bool *set_beacon); + +void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr); +int ieee80211_ml_process_link(struct hostapd_data *hapd, + struct sta_info *origin_sta, + struct mld_link_info *link, + const u8 *ies, size_t ies_len, + enum link_parse_type type, bool offload, + bool *set_beacon); +void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd, + struct mld_link_info *link); + +void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len); +void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok); + +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); + +void ieee80211_send_eap_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, u16 auth_transaction, u16 status, + struct rsn_pmksa_cache_entry *cached_pmk, + const u8 *eap_req, size_t eap_req_len); #endif /* IEEE802_11_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_eht.c b/contrib/wpa/src/ap/ieee802_11_eht.c index b935ee889a89..02e360c23f40 100644 --- a/contrib/wpa/src/ap/ieee802_11_eht.c +++ b/contrib/wpa/src/ap/ieee802_11_eht.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11be EHT - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -8,10 +8,14 @@ #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" @@ -44,34 +48,14 @@ static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info) static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass, - int he_oper_chwidth, const u8 *he_phy_cap, + const u8 *he_phy_cap, const u8 *eht_phy_cap) { u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS; bool band24, band5, band6; - u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK; u8 cap_chwidth; - switch (he_oper_chwidth) { - case CONF_OPER_CHWIDTH_80P80MHZ: - he_phy_cap_chwidth |= - HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G; - /* fall through */ - case CONF_OPER_CHWIDTH_160MHZ: - he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - /* fall through */ - case CONF_OPER_CHWIDTH_80MHZ: - case CONF_OPER_CHWIDTH_USE_HT: - he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; - break; - } - cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX]; - if (he_oper_chwidth != -1) - he_phy_cap_chwidth &= cap_chwidth; - else - he_phy_cap_chwidth = cap_chwidth; band24 = mode == HOSTAPD_MODE_IEEE80211B || mode == HOSTAPD_MODE_IEEE80211G || @@ -81,18 +65,18 @@ static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass, band6 = is_6ghz_op_class(opclass); if (band24 && - (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0) + (cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0) return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY; if (band5 && - (he_phy_cap_chwidth & + (cap_chwidth & (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G | HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0) return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY; if (band5 && - (he_phy_cap_chwidth & + (cap_chwidth & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G | HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS; @@ -122,7 +106,6 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd, return 0; len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class, - hapd->iconf->he_oper_chwidth, mode->he_capab[opmode].phy_cap, eht_cap->phy_cap); len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]), @@ -177,7 +160,6 @@ u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid, mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class, - hapd->iconf->he_oper_chwidth, mode->he_capab[opmode].phy_cap, eht_cap->phy_cap); if (mcs_nss_len) { @@ -241,6 +223,9 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid) oper->basic_eht_mcs_nss_set[2] = 0x00; oper->basic_eht_mcs_nss_set[3] = 0x00; + if (hapd->iconf->disable_mcs15_rx) + oper->oper_params |= EHT_OPER_MCS15_DISABLE; + if (!eht_oper_info_present) return pos + elen; @@ -332,7 +317,6 @@ static bool check_valid_eht_mcs(struct hostapd_data *hapd, sta_mcs = capab->optional; if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class, - hapd->iconf->he_oper_chwidth, mode->he_capab[opmode].phy_cap, mode->eht_capab[opmode].phy_cap) == EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY) @@ -374,7 +358,7 @@ static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode, if (len < cap_len) return true; - cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap, + cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap, cap->phy_cap); if (len < cap_len) return true; @@ -395,13 +379,14 @@ u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta, 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->ieee80211be || hapd->conf->disable_11be || + if (!hostapd_is_eht_enabled(hapd) || !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN || !eht_capab || ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class, he_capab, eht_capab, eht_capab_len) || - !check_valid_eht_mcs(hapd, eht_capab, opmode)) { + !check_valid_eht_mcs(hapd, eht_capab, opmode) || + !(sta->flags & WLAN_STA_HE)) { sta->flags &= ~WLAN_STA_EHT; os_free(sta->eht_capab); sta->eht_capab = NULL; @@ -439,9 +424,20 @@ void hostapd_get_eht_capab(struct hostapd_data *hapd, } -static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, - u8 *eid, struct mld_info *mld_info, - bool include_mld_id) +/* Beacon or a non ML Probe Response frame should include + * Common Info Length(1) + MLD MAC Address(6) + + * Link ID Info(1) + BSS Parameters Change count(1) + + * EML Capabilities (2) + MLD Capabilities (2) + */ +#define EHT_ML_COMMON_INFO_LEN 13 +/* + * control (2) + station info length (1) + MAC address (6) + + * beacon interval (2) + TSF offset (8) + DTIM info (2) + */ +#define EHT_ML_STA_INFO_LEN 21 +u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, + u8 *eid, struct mld_info *mld_info, + bool include_mld_id, bool include_bpcc) { struct wpabuf *buf; u16 control; @@ -452,6 +448,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, u8 common_info_len; u16 mld_cap; u8 max_simul_links, active_links; + u16 mld_eml_capa; /* * As the Multi-Link element can exceed the size of 255 bytes need to @@ -475,7 +472,6 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, * BSS Parameters Change Count (1) + EML Capabilities (2) + * MLD Capabilities and Operations (2) */ -#define EHT_ML_COMMON_INFO_LEN 13 common_info_len = EHT_ML_COMMON_INFO_LEN; if (include_mld_id) { @@ -494,16 +490,22 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, /* Own Link ID */ wpabuf_put_u8(buf, hapd->mld_link_id); - /* Currently hard code the BSS Parameters Change Count to 0x1 */ - wpabuf_put_u8(buf, 0x1); + wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change); - wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x", - hapd->iface->mld_eml_capa); - wpabuf_put_le16(buf, hapd->iface->mld_eml_capa); + /* Set EMLSR/EMLMR Transition and Padding delay to zero for AP MLD as + * per IEEE Std 802.11be-2024, 9.4.2.322.2.3 (Common Info field of the + * Basic Multi-Link element). + */ + mld_eml_capa = hapd->iface->mld_eml_capa & + ~(EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK | + EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK); + + wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x", mld_eml_capa); + wpabuf_put_le16(buf, mld_eml_capa); mld_cap = hapd->iface->mld_mld_capa; max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK; - active_links = hapd->mld->num_links - 1; + active_links = hostapd_get_active_links(hapd); if (active_links > max_simul_links) { wpa_printf(MSG_ERROR, @@ -518,6 +520,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, /* TODO: Advertise T2LM based on driver support as well */ mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK; + mld_cap |= EHT_ML_MLD_CAPA_LINK_RECONF_OP_SUPPORT; + wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x", mld_cap); wpabuf_put_le16(buf, mld_cap); @@ -534,16 +538,9 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, /* Add link info for the other links */ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { struct mld_link_info *link = &mld_info->links[link_id]; + size_t sta_info_len = EHT_ML_STA_INFO_LEN; struct hostapd_data *link_bss; - - /* - * control (2) + station info length (1) + MAC address (6) + - * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS - * parameters change counter (1) + station profile length. - */ -#define EHT_ML_STA_INFO_LEN 22 - size_t total_len = EHT_ML_STA_INFO_LEN + - link->resp_sta_profile_len; + size_t total_len; /* Skip the local one */ if (link_id == hapd->mld_link_id || !link->valid) @@ -556,8 +553,15 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, continue; } + /* BSS Parameters Change Count (1) for (Re)Association Response + * frames */ + if (include_bpcc) + sta_info_len++; + + total_len = sta_info_len + link->resp_sta_profile_len; + /* Per-STA Profile subelement */ - wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE); + wpabuf_put_u8(buf, MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE); if (total_len <= 255) wpabuf_put_u8(buf, total_len); @@ -566,18 +570,21 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, /* STA Control */ control = (link_id & 0xf) | - EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK | - EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK | - EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK | - EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK | - EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK | - EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK; + BASIC_MLE_STA_CTRL_PRES_STA_MAC | + BASIC_MLE_STA_CTRL_COMPLETE_PROFILE | + BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET | + BASIC_MLE_STA_CTRL_PRES_BEACON_INT | + BASIC_MLE_STA_CTRL_PRES_DTIM_INFO; + + if (include_bpcc) + control |= BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT; + wpabuf_put_le16(buf, control); /* STA Info */ /* STA Info Length */ - wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2); + wpabuf_put_u8(buf, sta_info_len - 2); wpabuf_put_data(buf, link->local_addr, ETH_ALEN); wpabuf_put_le16(buf, link_bss->iconf->beacon_int); @@ -593,7 +600,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, wpabuf_put_u8(buf, link_bss->conf->dtim_period); /* BSS Parameters Change Count */ - wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change); + if (include_bpcc) + wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change); if (!link->resp_sta_profile) continue; @@ -606,7 +614,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, ptr = link->resp_sta_profile; len = link->resp_sta_profile_len; - slice_len = 255 - EHT_ML_STA_INFO_LEN; + slice_len = 255 - sta_info_len; wpabuf_put_data(buf, ptr, slice_len); len -= slice_len; @@ -618,7 +626,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, else slice_len = 255; - wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT); + wpabuf_put_u8(buf, + MULTI_LINK_SUB_ELEM_ID_FRAGMENT); wpabuf_put_u8(buf, slice_len); wpabuf_put_data(buf, ptr, slice_len); @@ -667,6 +676,75 @@ out: } +size_t hostapd_eid_eht_basic_ml_len(struct hostapd_data *hapd, + struct sta_info *info, + bool include_mld_id, bool include_bpcc) +{ + int link_id; + size_t len, num_frags; + + if (!hapd->conf->mld_ap) + return 0; + + /* Include WLAN_EID_EXT_MULTI_LINK (1) */ + len = 1; + /* control field */ + len += 2; + /* Common info len for Basic MLE */ + len += EHT_ML_COMMON_INFO_LEN; + if (include_mld_id) + len++; + + if (!info) + goto out; + + /* Add link info for the other links */ + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + struct mld_link_info *link = &info->mld_info.links[link_id]; + struct hostapd_data *link_bss; + size_t sta_prof_len = EHT_ML_STA_INFO_LEN + + link->resp_sta_profile_len; + + /* Skip the local one */ + if (link_id == hapd->mld_link_id || !link->valid) + continue; + + link_bss = hostapd_mld_get_link_bss(hapd, link_id); + if (!link_bss) { + wpa_printf(MSG_ERROR, + "MLD: Couldn't find link BSS - skip it"); + continue; + } + + /* BSS Parameters Change Count (1) for (Re)Association Response + * frames */ + if (include_bpcc) + sta_prof_len++; + + /* Per-STA Profile Subelement(1), Length (1) */ + len += 2; + len += sta_prof_len; + /* Consider Fragment EID(1) and Length (1) for each subelement + * fragment. */ + if (sta_prof_len > 255) { + num_frags = (sta_prof_len / 255 - 1) + + !!(sta_prof_len % 255); + len += num_frags * 2; + } + + } + +out: + if (len > 255) { + num_frags = (len / 255 - 1) + !!(len % 255); + len += num_frags * 2; + } + + /* WLAN_EID_EXTENSION (1) + length (1) */ + return len + 2; +} + + static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid) { #ifdef CONFIG_TESTING_OPTIONS @@ -741,7 +819,7 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid) static size_t hostapd_eid_eht_ml_len(struct mld_info *info, - bool include_mld_id) + bool include_mld_id, bool include_bpcc) { size_t len = 0; size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN; @@ -761,19 +839,24 @@ static size_t hostapd_eid_eht_ml_len(struct mld_info *info, sta_len += link->resp_sta_profile_len; + /* BSS Parameters Change Count (1) for (Re)Association Response + * frames */ + if (include_bpcc) + sta_len++; + /* Element data and (fragmentation) headers */ eht_ml_len += sta_len; - eht_ml_len += 2 + sta_len / 255 * 2; + eht_ml_len += 2 + (sta_len - 1) / 255 * 2; } - /* Element data */ - len += eht_ml_len; + /* EID_EXT_MULTI_LINK (1) + Element data */ + len += 1 + eht_ml_len; - /* First header (254 bytes of data) */ - len += 3; + /* Fragmentation headers */ + len += (len - 1) / 255 * 2; - /* Fragmentation headers; +1 for shorter first chunk */ - len += (eht_ml_len + 1) / 255 * 2; + /* Outer header EID_EXT (1) + length (1) */ + len += 2; return len; } @@ -785,7 +868,8 @@ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd, struct mld_info *info, u8 *eid, bool include_mld_id) { - eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id); + eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id, + false); return hostapd_eid_eht_reconf_ml(hapd, eid); } @@ -798,7 +882,7 @@ u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info, return eid; eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info, - false); + false, true); ap_sta_free_sta_profile(&info->mld_info); return hostapd_eid_eht_reconf_ml(hapd, eid); } @@ -808,7 +892,7 @@ size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd, struct mld_info *info, bool include_mld_id) { - return hostapd_eid_eht_ml_len(info, include_mld_id); + return hostapd_eid_eht_ml_len(info, include_mld_id, false); } @@ -871,6 +955,8 @@ sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len, wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len); + if (len - 2 < prime_len * (ec ? 3 : 2)) + goto truncated; /* scalar */ pos += prime_len; @@ -882,6 +968,7 @@ sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len, } if (pos - mgmt->u.auth.variable > (int) len) { + truncated: wpa_printf(MSG_DEBUG, "EHT: Too short SAE commit Authentication frame"); return NULL; @@ -905,16 +992,38 @@ sae_confirm_skip_fixed_fields(struct hostapd_data *hapd, return pos; /* send confirm integer */ + if (len < 2) + goto truncated; pos += 2; /* * At this stage we should already have an MLD station and actually SA - * will be replaced with the MLD MAC address by the driver. + * will be replaced with the MLD MAC address by the driver. However, + * there is at least a theoretical race condition in a case where the + * peer sends the SAE confirm message quickly enough for the driver + * translation mechanism to not be available to update the SAE confirm + * message addresses. Work around that by searching for the STA entry + * using the link address of the non-AP MLD if no match is found based + * on the MLD MAC address. */ sta = ap_get_sta(hapd, mgmt->sa); if (!sta) { wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm"); - return NULL; + for (sta = hapd->sta_list; sta; sta = sta->next) { + int link_id = hapd->mld_link_id; + + if (!sta->mld_info.mld_sta || + sta->mld_info.links[link_id].valid || + !ether_addr_equal( + mgmt->sa, + sta->mld_info.links[link_id].peer_addr)) + continue; + wpa_printf(MSG_DEBUG, + "SAE: Found MLD STA for SAE confirm based on link address"); + break; + } + if (!sta) + return NULL; } if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) { @@ -929,9 +1038,12 @@ sae_confirm_skip_fixed_fields(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu", sta->sae->tmp->kck_len); + if (len - 2 < sta->sae->tmp->kck_len) + goto truncated; pos += sta->sae->tmp->kck_len; if (pos - mgmt->u.auth.variable > (int) len) { + truncated: wpa_printf(MSG_DEBUG, "EHT: Too short SAE confirm Authentication frame"); return NULL; @@ -948,20 +1060,43 @@ static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd, size_t len) { u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); -#ifdef CONFIG_SAE +#if defined(CONFIG_SAE) || defined(CONFIG_IEEE8021X_AUTH) u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); u16 status_code = le_to_host16(mgmt->u.auth.status_code); -#endif /* CONFIG_SAE */ +#endif /* CONFIG_SAE or CONFIG_IEEE8021X_AUTH */ const u8 *pos = mgmt->u.auth.variable; - /* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69 + /* Skip fixed fields as based on IEEE Std 802.11-2024, Table 9-71 * (Presence of fields and elements in Authentications frames) */ switch (auth_alg) { case WLAN_AUTH_OPEN: + case WLAN_AUTH_EPPKE: return pos; +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: { + u16 encap_len; + + /* Extract 2-octet Encapsulation Length from variable field */ + if (len < 2) + return NULL; + + encap_len = WPA_GET_LE16(pos); + pos += 2; + len -= 2; + + /* Skip Encapsulation field if present */ + if (encap_len > 0) { + if (len < encap_len) + return NULL; + pos += encap_len; + } + + return pos; + } +#endif /* CONFIG_IEEE8021X_AUTH */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: - if (auth_transaction == 1) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { if (status_code == WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "EHT: SAE H2E is mandatory for MLD"); @@ -970,7 +1105,7 @@ static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd, return sae_commit_skip_fixed_fields(mgmt, len, pos, status_code); - } else if (auth_transaction == 2) { + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) { return sae_confirm_skip_fixed_fields(hapd, mgmt, len, pos, status_code); } @@ -1099,29 +1234,7 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd, /* Common Info Length and MLD MAC Address must always be present */ common_info_len = 1 + ETH_ALEN; - - if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) { - wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected"); - goto out; - } - - if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) { - wpa_printf(MSG_DEBUG, - "MLD: BSS Parameters Change Count not expected"); - goto out; - } - - if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) { - wpa_printf(MSG_DEBUG, - "MLD: Medium Synchronization Delay Information not expected"); - goto out; - } - - if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) - common_info_len += 2; - - if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) - common_info_len += 2; + /* Ignore optional fields */ if (sizeof(*ml) + common_info_len > ml_len) { wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info"); @@ -1131,7 +1244,7 @@ int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd, common_info = (struct eht_ml_basic_common_info *) ml->variable; /* Common information length includes the length octet */ - if (common_info->len != common_info_len) { + if (common_info->len < common_info_len) { wpa_printf(MSG_DEBUG, "MLD: Invalid common info len=%u", common_info->len); goto out; @@ -1157,9 +1270,11 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, size_t ml_len, common_info_len; struct mld_link_info *link_info; struct mld_info *info = &sta->mld_info; - const u8 *pos; + const u8 *pos, *end; int ret = -1; u16 ml_control; + const u8 *ml_end; + u8 saved_peer_addr[ETH_ALEN]; mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true); if (!mlbuf) @@ -1167,6 +1282,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, ml = wpabuf_head(mlbuf); ml_len = wpabuf_len(mlbuf); + ml_end = ((const u8 *) ml) + ml_len; ml_control = le_to_host16(ml->ml_control); if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) != @@ -1208,7 +1324,13 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, goto out; } - wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu", + if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) { + common_info_len += 2; + } else { + wpa_printf(MSG_DEBUG, "MLD: EXT ML capabilities not present"); + } + + wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%zu", common_info_len); if (sizeof(*ml) + common_info_len > ml_len) { @@ -1219,7 +1341,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, common_info = (const struct eht_ml_basic_common_info *) ml->variable; /* Common information length includes the length octet */ - if (common_info->len != common_info_len) { + if (common_info->len < common_info_len) { wpa_printf(MSG_DEBUG, "MLD: Invalid common info len=%u (expected %zu)", common_info->len, common_info_len); @@ -1227,6 +1349,7 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, } pos = common_info->variable; + end = ((const u8 *) common_info) + common_info->len; if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) { info->common_info.eml_capa = WPA_GET_LE16(pos); @@ -1238,6 +1361,10 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, info->common_info.mld_capa = WPA_GET_LE16(pos); pos += 2; + if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) { + pos += 2; + } + wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x", MAC2STR(info->common_info.mld_addr), info->common_info.eml_capa, info->common_info.mld_capa); @@ -1253,23 +1380,58 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, goto out; } + /* Reset all per-link state before parsing the new MLE. A STA may send + * back-to-back Reassociation Request frames with different MLEs (e.g., + * fewer links or without NSTR). The valid-flag-only reset would leave + * stale fields (nstr_bitmap_len, peer_addr, nstr_bitmap, capability) in + * non-association link slots and leak resp_sta_profile heap + * allocations. Free those allocations and zero all link slots. The + * association link's peer_addr holds the link-specific MAC (set from + * mgmt->sa at auth time, before driver address translation) and must be + * saved and restored. + */ + os_memcpy(saved_peer_addr, info->links[hapd->mld_link_id].peer_addr, + ETH_ALEN); + ap_sta_free_sta_profile(info); + os_memset(info->links, 0, sizeof(info->links)); info->links[hapd->mld_link_id].valid = 1; + os_memcpy(info->links[hapd->mld_link_id].local_addr, + hapd->own_addr, ETH_ALEN); + os_memcpy(info->links[hapd->mld_link_id].peer_addr, + saved_peer_addr, ETH_ALEN); - /* Parse the link info field */ - ml_len -= sizeof(*ml) + common_info_len; - - while (ml_len > 2) { - size_t sub_elem_len = *(pos + 1); - size_t sta_info_len; + /* Parse the Link Info field that starts after the end of the variable + * length Common Info field. */ + pos = end; + while (ml_end - pos > 2) { + size_t sub_elem_len, sta_info_len; u16 control; + const u8 *sub_elem_end; + int num_frag_subelems; + u8 link_id; - wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu", - sub_elem_len); + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &sub_elem_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse MLE subelem"); + goto out; + } - if (2 + sub_elem_len > ml_len) { + if ((size_t) num_frag_subelems * 2 > ml_len) + goto out; + ml_len -= num_frag_subelems * 2; + ml_end = ((const u8 *) ml) + ml_len; + + wpa_printf(MSG_DEBUG, + "MLD: sub element len=%zu, Fragment subelems=%u", + sub_elem_len, num_frag_subelems); + + if (2 + sub_elem_len > (size_t) (ml_end - pos)) { wpa_printf(MSG_DEBUG, "MLD: Invalid link info len: %zu %zu", - 2 + sub_elem_len, ml_len); + 2 + sub_elem_len, ml_end - pos); goto out; } @@ -1278,7 +1440,6 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, "MLD: Skip vendor specific subelement"); pos += 2 + sub_elem_len; - ml_len -= 2 + sub_elem_len; continue; } @@ -1287,41 +1448,43 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, "MLD: Skip unknown Multi-Link element subelement ID=%u", *pos); pos += 2 + sub_elem_len; - ml_len -= 2 + sub_elem_len; continue; } /* Skip the subelement ID and the length */ pos += 2; - ml_len -= 2; + sub_elem_end = pos + sub_elem_len; /* Get the station control field */ - if (sub_elem_len < 2) { + if (sub_elem_end - pos < 2) { wpa_printf(MSG_DEBUG, "MLD: Too short Per-STA Profile subelement"); goto out; } control = WPA_GET_LE16(pos); - link_info = &info->links[control & - EHT_PER_STA_CTRL_LINK_ID_MSK]; + link_id = control & BASIC_MLE_STA_CTRL_LINK_ID_MASK; + if (link_id >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid Link ID in Per-STA Profile subelement"); + goto out; + } + link_info = &info->links[link_id]; pos += 2; - ml_len -= 2; - sub_elem_len -= 2; - if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) { + if (!(control & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE)) { wpa_printf(MSG_DEBUG, "MLD: Per-STA complete profile expected"); goto out; } - if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) { + if (!(control & BASIC_MLE_STA_CTRL_PRES_STA_MAC)) { wpa_printf(MSG_DEBUG, "MLD: Per-STA MAC address not present"); goto out; } - if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK | - EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) { + if ((control & (BASIC_MLE_STA_CTRL_PRES_BEACON_INT | + BASIC_MLE_STA_CTRL_PRES_DTIM_INFO))) { wpa_printf(MSG_DEBUG, "MLD: Beacon/DTIM interval not expected"); goto out; @@ -1330,8 +1493,8 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, /* The length octet and the MAC address must be present */ sta_info_len = 1 + ETH_ALEN; - if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) { - if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK) + if (control & BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR) { + if (control & BASIC_MLE_STA_CTRL_NSTR_BITMAP) link_info->nstr_bitmap_len = 2; else link_info->nstr_bitmap_len = 1; @@ -1339,45 +1502,42 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, sta_info_len += link_info->nstr_bitmap_len; - if (sta_info_len > ml_len || sta_info_len != *pos || - sta_info_len > sub_elem_len) { + if (sta_info_len > (size_t) (sub_elem_end - pos) || + sta_info_len > *pos || + *pos > sub_elem_end - pos || + sta_info_len > (size_t) (sub_elem_end - pos)) { wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length"); goto out; } + sta_info_len = *pos; + end = pos + sta_info_len; + /* skip the length */ pos++; - ml_len--; /* get the link address */ os_memcpy(link_info->peer_addr, pos, ETH_ALEN); wpa_printf(MSG_DEBUG, "MLD: assoc: link id=%u, addr=" MACSTR, - control & EHT_PER_STA_CTRL_LINK_ID_MSK, + control & BASIC_MLE_STA_CTRL_LINK_ID_MASK, MAC2STR(link_info->peer_addr)); pos += ETH_ALEN; - ml_len -= ETH_ALEN; /* Get the NSTR bitmap */ if (link_info->nstr_bitmap_len) { os_memcpy(link_info->nstr_bitmap, pos, link_info->nstr_bitmap_len); pos += link_info->nstr_bitmap_len; - ml_len -= link_info->nstr_bitmap_len; } - sub_elem_len -= sta_info_len; + pos = end; - wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len); - if (sub_elem_len > ml_len) - goto out; - - if (sub_elem_len > 2) + if (sub_elem_end - pos >= 2) link_info->capability = WPA_GET_LE16(pos); - pos += sub_elem_len; - ml_len -= sub_elem_len; + pos = sub_elem_end; wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR ", nstr bitmap len=%u", @@ -1387,19 +1547,1338 @@ u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd, link_info->valid = true; } - if (ml_len) { - wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail", - ml_len); - goto out; - } - ret = hostapd_mld_validate_assoc_info(hapd, sta); out: wpabuf_free(mlbuf); if (ret) { + ap_sta_free_sta_profile(info); os_memset(info, 0, sizeof(*info)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } return WLAN_STATUS_SUCCESS; } + + +void ml_deinit_link_reconf_req(struct link_reconf_req_list **req_list_ptr) +{ + struct link_reconf_req_list *req_list; + struct link_reconf_req_info *info, *tmp; + + if (!(*req_list_ptr)) + return; + + wpa_printf(MSG_DEBUG, "MLD: Deinit Link Reconf Request context"); + + req_list = *req_list_ptr; + + dl_list_for_each_safe(info, tmp, &req_list->add_req, + struct link_reconf_req_info, list) { + dl_list_del(&info->list); + os_free(info); + } + + dl_list_for_each_safe(info, tmp, &req_list->del_req, + struct link_reconf_req_info, list) { + dl_list_del(&info->list); + os_free(info); + } + + os_free(req_list); + *req_list_ptr = NULL; +} + + +void hostapd_link_reconf_resp_tx_status(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + u8 dialog_token = mgmt->u.action.u.link_reconf_resp.dialog_token; + struct hostapd_data *assoc_hapd, *lhapd, *other_hapd; + struct sta_info *assoc_sta, *lsta, *other_sta; + struct link_reconf_req_list *req_list; + struct link_reconf_req_info *info; + uint8_t link_id; + + wpa_printf(MSG_DEBUG, + "MLD: Link Reconf Response TX status - dialog token=%u ok=%d", + dialog_token, ok); + + assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd); + if (!assoc_sta) { + wpa_printf(MSG_INFO, "MLD: Assoc STA not found for " MACSTR, + MAC2STR(mgmt->da)); + return; + } + + if (!assoc_sta->reconf_req) { + wpa_printf(MSG_DEBUG, + "MLD: Unexpected Link Reconf Request TX status"); + return; + } + + req_list = assoc_sta->reconf_req; + + if (!ether_addr_equal(mgmt->da, req_list->sta_mld_addr)) { + wpa_printf(MSG_DEBUG, + "MLD: Link Reconfiguration Response TX status from wrong STA"); + return; + } + + if (dialog_token != req_list->dialog_token) { + wpa_printf(MSG_DEBUG, + "MLD: Link Reconfiguration session expired for %u", + dialog_token); + return; + } + + if (!ok) { + wpa_printf(MSG_INFO, + "MLD: Link Reconf Response ack failed for " MACSTR + "; revert link additions", + MAC2STR(mgmt->da)); + + dl_list_for_each(info, &req_list->add_req, + struct link_reconf_req_info, list) { + if (info->status != WLAN_STATUS_SUCCESS) + continue; + + lhapd = NULL; + lsta = NULL; + lhapd = hostapd_mld_get_link_bss(hapd, info->link_id); + if (lhapd) + lsta = ap_get_sta(lhapd, + req_list->sta_mld_addr); + + if (lsta) + ap_free_sta(lhapd, lsta); + } + goto exit; + } + + if (dl_list_empty(&req_list->del_req)) + goto exit; + + dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info, + list) { + if (info->status != WLAN_STATUS_SUCCESS) + continue; + + link_id = info->link_id; + lhapd = hostapd_mld_get_link_bss(hapd, link_id); + if (!lhapd) { + wpa_printf(MSG_INFO, + "MLD: Link (%u) hapd cannot be NULL", + link_id); + continue; + } + + lsta = ap_get_sta(lhapd, mgmt->da); + if (!lsta) { + wpa_printf(MSG_INFO, + "MLD: Link (%u) STA cannot be NULL", + link_id); + continue; + } + + /* Reassign assoc_sta to the link with lowest link ID */ + if (!hostapd_sta_is_link_sta(lhapd, lsta) && + lsta == assoc_sta) { + struct mld_info *mld_info = &assoc_sta->mld_info; + int i; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (i == assoc_sta->mld_assoc_link_id || + !mld_info->links[i].valid || + req_list->links_del_ok & BIT(i)) { + continue; + } + break; + } + + if (i == MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_INFO, + "MLD: No new assoc STA could be found; disconnect STA"); + hostapd_notif_disassoc_mld(assoc_hapd, + assoc_sta, + assoc_sta->addr); + return; + } + wpa_printf(MSG_DEBUG, "MLD: New assoc link=%d", i); + + /* Reset wpa_auth and assoc link ID */ + for_each_mld_link(other_hapd, lhapd) { + other_sta = ap_get_sta(other_hapd, mgmt->da); + if (other_sta) + other_sta->mld_assoc_link_id = i; + } + + /* Reset reconfig request queue which will be freed + * at the end */ + assoc_sta->reconf_req = NULL; + + /* assoc_sta switched */ + assoc_sta = hostapd_ml_get_assoc_sta(lhapd, lsta, + &assoc_hapd); + + /* assoc_sta cannot be NULL since both AP and STA are + * MLD and new valid assoc_sta is already found */ + if (!assoc_sta) + goto exit; + + if (assoc_hapd == lhapd) { + wpa_printf(MSG_ERROR, + "MLD: assoc_hapd is not updated; please check"); + goto exit; + } + + assoc_sta->reconf_req = req_list; + wpa_reset_assoc_sm_info(assoc_sta->wpa_sm, + assoc_hapd->wpa_auth, i); + } + + /* Free as a link STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_LINK_STA_REMOVED "sta=" MACSTR " link_id=%u", + MAC2STR(lsta->addr), link_id); + ap_free_sta(lhapd, lsta); + + for_each_mld_link(other_hapd, lhapd) { + struct mld_link_info *link; + + other_sta = ap_get_sta(other_hapd, mgmt->da); + if (!other_sta) + continue; + + link = &other_sta->mld_info.links[link_id]; + os_free(link->resp_sta_profile); + link->resp_sta_profile = NULL; + link->resp_sta_profile_len = 0; + link->valid = false; + } + wpa_auth_set_ml_info(assoc_sta->wpa_sm, + assoc_sta->mld_assoc_link_id, + &assoc_sta->mld_info); + } + +exit: + ml_deinit_link_reconf_req(&req_list); + if (assoc_sta && assoc_sta->reconf_req) + assoc_sta->reconf_req = NULL; +} + + +static bool recover_from_zero_links(u16 *links_del_ok, u8 *recovery_link) +{ + u8 pos = 0; + u16 del_links; + + del_links = *links_del_ok; + + while (del_links) { + if (del_links & 1) + break; + del_links >>= 1; + pos++; + } + + /* No link found */ + if (!del_links) { + wpa_printf(MSG_DEBUG, + "MLD: Total valid links is 0 and no del-link found to reject for recovery"); + return false; + } + + *recovery_link = pos; + *links_del_ok &= ~BIT(*recovery_link); + wpa_printf(MSG_INFO, + "MLD: Del-link request for link (%u) rejected to recover from no remaining links", + *recovery_link); + return true; +} + + +static u16 +hostapd_ml_process_reconf_link(struct hostapd_data *hapd, + struct sta_info *assoc_sta, const u8 *ies, + size_t ies_len, u8 link_id, const u8 *link_addr) +{ + struct hostapd_data *lhapd, *other_hapd; + struct mld_link_info link; + struct sta_info *lsta, *other_sta; + + lhapd = hostapd_mld_get_link_bss(hapd, link_id); + if (!lhapd) /* This cannot be NULL */ + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + os_memset(&link, 0, sizeof(link)); + + link.valid = 1; + os_memcpy(link.local_addr, lhapd->own_addr, ETH_ALEN); + os_memcpy(link.peer_addr, link_addr, ETH_ALEN); + + /* Parse STA profile, check the IEs, and send ADD_LINK_STA */ + ieee80211_ml_process_link(lhapd, assoc_sta, &link, ies, ies_len, + LINK_PARSE_RECONF, false, NULL); + if (link.status != WLAN_STATUS_SUCCESS) + return link.status; + + lsta = ap_get_sta(lhapd, assoc_sta->addr); + if (!lsta) + return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + + for_each_mld_link(other_hapd, lhapd) { + struct mld_link_info *_link; + + other_sta = ap_get_sta(other_hapd, lsta->addr); + if (!other_sta) + continue; + + _link = &other_sta->mld_info.links[link_id]; + _link->valid = true; + _link->status = WLAN_STATUS_SUCCESS; + os_memcpy(_link->local_addr, other_hapd->own_addr, ETH_ALEN); + os_memcpy(_link->peer_addr, link_addr, ETH_ALEN); + } + wpa_auth_set_ml_info(lsta->wpa_sm, lsta->mld_assoc_link_id, + &lsta->mld_info); + + return WLAN_STATUS_SUCCESS; +} + + +static int +hostapd_reject_all_reconf_req(struct hostapd_data *hapd, u8 *pos, + struct link_reconf_req_list *req_list) +{ + struct link_reconf_req_info *info; + struct hostapd_data *lhapd; + struct sta_info *lsta; + u16 status; + u8 *buf = pos; + + dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info, + list) { + lhapd = NULL; + lsta = NULL; + *pos++ = info->link_id; + status = info->status != WLAN_STATUS_SUCCESS ? info->status : + WLAN_STATUS_UNSPECIFIED_FAILURE; + WPA_PUT_LE16(pos, status); + + if (info->status == WLAN_STATUS_SUCCESS) { + lhapd = hostapd_mld_get_link_bss(hapd, info->link_id); + if (lhapd) + lsta = ap_get_sta(lhapd, + req_list->sta_mld_addr); + + if (lsta) + ap_free_sta(lhapd, lsta); + + info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_printf(MSG_DEBUG, "MLD: Reject add-link=%u with status=%u", + info->link_id, status); + pos += 2; + } + + dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info, + list) { + *pos++ = info->link_id; + status = info->status != WLAN_STATUS_SUCCESS ? info->status : + WLAN_STATUS_UNSPECIFIED_FAILURE; + WPA_PUT_LE16(pos, status); + + if (info->status == WLAN_STATUS_SUCCESS) + info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_printf(MSG_DEBUG, "MLD: Reject del-link=%u with status=%u", + info->link_id, status); + pos += 2; + } + + return pos - buf; +} + + +static int +hostapd_send_link_reconf_resp(struct hostapd_data *hapd, + struct sta_info *assoc_sta, + struct link_reconf_req_list *req_list) +{ + u8 *buf, *orig_pos, *pos; + struct ieee80211_mgmt *mgmt; + struct link_reconf_req_info *info; + struct mld_info mld; + int ret; + unsigned int count; + u8 dialog_token; + bool reject_all = false; + size_t len, pos_len, kde_len, mle_len; + + count = dl_list_len(&req_list->add_req) + + dl_list_len(&req_list->del_req); + if (!count) + return 0; + + os_memset(&mld, 0, sizeof(mld)); + + dialog_token = req_list->dialog_token; + + /* + * Link Reconfiguration Response: + * + * IEEE80211 Header (24B) + + * Category (1B) + Action code (1B) + Dialog Token (1B) + + * Count (1B) + Status list (count * 3B) + + * Optional: Group Key Data field (variable) + + * Optional: OCI element (6B) + + * Optional: Basic Multi-Link element (variable) + */ + len = IEEE80211_HDRLEN + 3 + 1 + count * 3; + kde_len = mle_len = 0; + + if (req_list->links_add_ok) { + kde_len = wpa_auth_ml_group_kdes_len( + assoc_sta->wpa_sm, req_list->links_add_ok) + 1; + len += kde_len; + +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(assoc_sta->wpa_sm)) + len += OCV_OCI_EXTENDED_LEN; +#endif /* CONFIG_OCV */ + + mld.mld_sta = true; + dl_list_for_each(info, &req_list->add_req, + struct link_reconf_req_info, list) { + struct mld_link_info *link = &mld.links[info->link_id]; + struct hostapd_data *lhapd = NULL; + + if (info->status != WLAN_STATUS_SUCCESS) + continue; + + link->status = info->status; + + lhapd = hostapd_mld_get_link_bss(hapd, info->link_id); + if (!lhapd) + continue; + + link->valid = true; + ieee80211_ml_build_assoc_resp(lhapd, link); + } + /* TODO: Basic MLE is not supposed to include BPCC in Link + * Reconfiguration Response, but mac80211 implementation for + * processing this frame requires that to be present. For now, + * include that subfield as a workaround. This should be removed + * once mac80211 is fixed to match the standard (or this comment + * be removed if the standard is modified to match + * implementation). */ + mle_len = hostapd_eid_eht_ml_len(&mld, false, true); + len += mle_len; + } + + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_INFO, + "MLD: Failed to allocate Link Reconf Response buffer (%zu bytes)", + len); + return -1; + } + + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, assoc_sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->mld->mld_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->mld->mld_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_ACTION_PROTECTED_EHT; + mgmt->u.action.u.link_reconf_resp.action = + WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE; + mgmt->u.action.u.link_reconf_resp.dialog_token = dialog_token; + mgmt->u.action.u.link_reconf_resp.count = count; + + orig_pos = pos = mgmt->u.action.u.link_reconf_resp.variable; + pos_len = 28; /* IEEE80211 Header, category, code, token, count */ + + dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info, + list) { + *pos++ = info->link_id; + WPA_PUT_LE16(pos, info->status); + pos += 2; + pos_len += 3; + } + + dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info, + list) { + /* Mark the status as INVALID for rejected link to recover */ + if (!(req_list->links_del_ok & BIT(info->link_id)) && + info->status == WLAN_STATUS_SUCCESS) + info->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + *pos++ = info->link_id; + WPA_PUT_LE16(pos, info->status); + pos += 2; + pos_len += 3; + } + + if (!req_list->links_add_ok) + goto send_resp; + + /* Key Data for add links */ + if (kde_len) { + u8 *kde_pos = pos; + + kde_pos = wpa_auth_ml_group_kdes(assoc_sta->wpa_sm, ++kde_pos, + req_list->links_add_ok); + *pos = kde_pos - pos - 1; + if (kde_len - 1 != *pos) { + reject_all = true; + goto reject_all_req; + } + + wpa_hexdump_key(MSG_DEBUG, "MLD: Group KDE", pos + 1, *pos); + + pos += kde_len; + pos_len += kde_len; + } + +#ifdef CONFIG_OCV + /* OCI element for add links */ + if (wpa_auth_uses_ocv(assoc_sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci)) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to fetch OCI; reject all requests"); + reject_all = true; + goto reject_all_req; + } + + if (ocv_insert_extended_oci(&ci, pos)) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to add OCI element; reject all requests"); + reject_all = true; + goto reject_all_req; + } + + pos += OCV_OCI_EXTENDED_LEN; + pos_len += OCV_OCI_EXTENDED_LEN; + } +#endif /* CONFIG_OCV */ + + /* Basic Multi-Link element for add links */ + if (mle_len) { + u8 *mle_pos = pos; + + /* TODO: Basic MLE is not supposed to include BPCC in Link + * Reconfiguration Response, but mac80211 implementation for + * processing this frame requires that to be present. For now, + * include that subfield as a workaround. This should be removed + * once mac80211 is fixed to match the standard (or this comment + * be removed if the standard is modified to match + * implementation). */ + mle_pos = hostapd_eid_eht_basic_ml_common(hapd, mle_pos, &mld, + false, true); + if ((size_t) (mle_pos - pos) != mle_len) { + wpa_printf(MSG_DEBUG, + "MLD: Unexpected MLE length: %td != %zu", + mle_pos - pos, mle_len); + reject_all = true; + goto reject_all_req; + } + + pos += mle_len; + pos_len += mle_len; + } + +reject_all_req: + if (reject_all) { + pos = orig_pos; + pos_len = 28; /* reset pos_len */ + pos += hostapd_reject_all_reconf_req(hapd, orig_pos, req_list); + pos_len += pos - orig_pos; + + req_list->links_add_ok = req_list->links_del_ok = 0; + req_list->new_valid_links = 0; + } + +send_resp: + ret = hostapd_drv_send_mlme(hapd, mgmt, pos_len, 0, NULL, 0, 0); + os_free(buf); + + if (mld.mld_sta) + ap_sta_free_sta_profile(&mld); + + return ret; +} + + +static int +hostapd_ml_check_sta_entry_by_link_addr_iter(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + const u8 *link_addr = ctx; + struct mld_link_info li; + + if (!link_addr) + return 0; + + if (sta->mld_info.mld_sta) { + li = sta->mld_info.links[hapd->mld_link_id]; + if (!li.valid || !ether_addr_equal(li.peer_addr, link_addr)) + return 0; + + wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR + " exists for AP (link_id=%u) as a non-AP STA affiliated with non-AP MLD " + MACSTR, MAC2STR(link_addr), + hapd->mld_link_id, MAC2STR(sta->addr)); + return 1; + } + + if (ether_addr_equal(sta->addr, link_addr)) { + wpa_printf(MSG_DEBUG, "MLD: STA with address " MACSTR + " exists for AP (link_id=%u) as a legacy STA", + MAC2STR(link_addr), hapd->mld_link_id); + return 1; + } + + return 0; +} + + +/* Returns: + * 0 = successful parsing + * 1 = per-STA profile (subelement) skipped or rejected + * -1 = fail due to fatal errors + */ +static int +hostapd_parse_link_reconf_req_sta_profile(struct hostapd_data *hapd, + struct link_reconf_req_list *req, + const u8 *buf, size_t len) +{ + struct link_reconf_req_info *info = NULL; + const struct ieee80211_eht_per_sta_profile *per_sta_prof; + const struct element *elem; + struct hostapd_data *lhapd = NULL; + struct sta_info *lsta; + size_t sta_info_len, sta_prof_len = 0; + u16 sta_control, reconf_type_mask; + u8 link_id, reconf_type; + const u8 *sta_info = NULL, *end; + u8 sta_addr[ETH_ALEN]; + size_t nstr_bitmap_size = 0; + int ret = -1; + u16 status; + + wpa_hexdump(MSG_MSGDUMP, "MLD: Link Reconfig Request subelement", + buf, len); + + if (len < sizeof(*elem) + 2UL) + goto out; + + elem = (const struct element *) buf; + end = buf + len; + + os_memset(sta_addr, 0, ETH_ALEN); + + if (elem->id != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) { + wpa_printf(MSG_DEBUG, "MLD: Unexpected subelement (%u) found", + elem->id); + ret = 1; /* skip this subelement */ + goto out; + } + + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + per_sta_prof = (const struct ieee80211_eht_per_sta_profile *) + elem->data; + sta_control = le_to_host16(per_sta_prof->sta_control); + sta_info_len = 1; + + link_id = sta_control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK; + wpa_printf(MSG_DEBUG, "MLD: Per-STA profile for link=%u", link_id); + if (link_id >= MAX_NUM_MLD_LINKS) + goto out; + + reconf_type_mask = + sta_control & EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK; + reconf_type = + EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_VAL(reconf_type_mask); + + switch (reconf_type) { + case EHT_RECONF_TYPE_ADD_LINK: + case EHT_RECONF_TYPE_DELETE_LINK: + break; + default: + wpa_printf(MSG_ERROR, + "MLD: Unsupported Reconfiguration type %u", + reconf_type); + ret = 1; /* skip this per-STA profile */ + goto out; + } + + if (!(sta_control & EHT_PER_STA_RECONF_CTRL_MAC_ADDR)) { + wpa_printf(MSG_DEBUG, + "MLD: STA MAC address not set in STA control"); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + sta_info_len += ETH_ALEN; + + if (sta_control & EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER) { + wpa_printf(MSG_DEBUG, + "MLD: AP removal timer set in STA control"); + sta_info_len += 2; + } + + if (sta_control & EHT_PER_STA_RECONF_CTRL_OP_PARAMS) { + wpa_printf(MSG_DEBUG, "MLD: Op params set in STA control"); + sta_info_len += 3; + } + + if (!(sta_control & EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE)) { + if (reconf_type == EHT_RECONF_TYPE_ADD_LINK) { + wpa_printf(MSG_DEBUG, + "MLD: Complete profile not set in STA control"); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + } else { + if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) + wpa_printf(MSG_DEBUG, + "MLD: Complete profile set in STA control"); + } + + if (sta_control & EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION) { + nstr_bitmap_size = 1; + if (sta_control & + EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE) + nstr_bitmap_size = 2; + + if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) + wpa_printf(MSG_DEBUG, + "MLD: NSTR Indication set in STA control"); + } + sta_info_len += nstr_bitmap_size; + + sta_info = per_sta_prof->variable; + if (*sta_info > end - sta_info) { + wpa_printf(MSG_DEBUG, "MLD: Not enough room for STA Info"); + goto out; + } + + if (*sta_info < sta_info_len) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid Reconf STA Info len (%u); min expected=%zu", + *sta_info, sta_info_len); + goto out; + } + + sta_info_len = *sta_info; + wpa_hexdump(MSG_MSGDUMP, "MLD: STA Info", sta_info, sta_info_len); + + os_memcpy(sta_addr, sta_info + 1, ETH_ALEN); + wpa_printf(MSG_DEBUG, "MLD: Link STA addr=" MACSTR, MAC2STR(sta_addr)); + + sta_info += sta_info_len; + + lhapd = hostapd_mld_get_link_bss(hapd, link_id); + if (!lhapd) { + wpa_printf(MSG_DEBUG, "MLD: No AP link found for link id=%u", + link_id); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + + lsta = ap_get_sta(lhapd, req->sta_mld_addr); + + if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) { + /* DELETE_LINK request shall not have STA profile */ + if (len != sizeof(sta_control) + sta_info_len) + wpa_printf(MSG_DEBUG, + "MLD: Delete link request has STA profile"); + + if (!lsta || !ap_sta_is_mld(lhapd, lsta) || + !lsta->mld_info.links[link_id].valid) { + wpa_printf(MSG_DEBUG, + "MLD: STA invalid for link id=%u peer addr=" + MACSTR, link_id, MAC2STR(sta_addr)); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + + if (!ether_addr_equal(lsta->mld_info.links[link_id].peer_addr, + sta_addr)) { + wpa_printf(MSG_DEBUG, + "MLD: STA invalid for addr=" MACSTR, + MAC2STR(sta_addr)); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + + status = WLAN_STATUS_SUCCESS; + ret = 0; + goto add_to_list; + } + + /* EHT_RECONF_TYPE_ADD_LINK */ + if (len < sizeof(sta_control) + sta_info_len + 2) + goto out; + sta_prof_len = len - sizeof(sta_control) - sta_info_len - 2; + if (sta_prof_len > (size_t) (end - sta_info)) { + wpa_printf(MSG_DEBUG, "MLD: STA Profile with excess length"); + goto out; + } + + if (lsta) { + wpa_printf(MSG_DEBUG, + "MLD: STA exists for link id=%u MLD addr=" MACSTR, + link_id, MAC2STR(req->sta_mld_addr)); + ret = 1; /* reject this per-STA profile */ + goto add_to_list; + } + + /* Check if link address is already used by any connected legacy + * non-AP STA or non-AP STA affiliated with a non-AP MLD. + */ + if (ap_for_each_sta(lhapd, hostapd_ml_check_sta_entry_by_link_addr_iter, + sta_addr)) { + ret = 1; /* Reject this per-STA profile */ + goto add_to_list; + } + + status = WLAN_STATUS_SUCCESS; /* IE validations done later */ + ret = 0; + +add_to_list: + if (sta_info && sta_prof_len) + wpa_hexdump(MSG_MSGDUMP, "MLD: STA Profile", + sta_info, sta_prof_len); + + info = os_zalloc(sizeof(struct link_reconf_req_info) + sta_prof_len); + if (!info) { + wpa_printf(MSG_DEBUG, "MLD: Failed to allocate request info"); + ret = 1; /* skip this per-STA profile */ + goto out; + } + + info->link_id = link_id; + info->status = status; + os_memcpy(info->peer_addr, sta_addr, ETH_ALEN); + if (lhapd) + os_memcpy(info->local_addr, lhapd->own_addr, ETH_ALEN); + + if (reconf_type == EHT_RECONF_TYPE_DELETE_LINK) { + dl_list_add_tail(&req->del_req, &info->list); + } else if (sta_info) { + os_memcpy(info->sta_prof, sta_info, sta_prof_len); + info->sta_prof_len = sta_prof_len; + + dl_list_add_tail(&req->add_req, &info->list); + } else { + os_free(info); + } + wpa_printf(MSG_INFO, "MLD: Link (%d) parsed to %s request; status=%u", + link_id, + reconf_type == EHT_RECONF_TYPE_DELETE_LINK ? "del" : "add", + status); + +out: + if (ret < 0) + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse reconf req STA profile"); + return ret; +} + + +static int +hostapd_parse_link_reconf_req_reconf_mle( + struct hostapd_data *hapd, const u8 *mle, size_t mle_len, + struct link_reconf_req_list **req_list_ptr) +{ + struct link_reconf_req_list *req_list; + struct wpabuf *mlbuf = NULL; + struct sta_info *sta; + const struct ieee80211_eht_ml *ml; + const struct eht_ml_reconf_common_info *ml_common_info; + size_t len, ml_common_len; + u16 ml_control; + const u8 *pos, *end; + int ret = -1; + + mlbuf = ieee802_11_defrag(mle, mle_len, true); + if (!mlbuf) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to defrag Reconfiguration MLE"); + goto fail; + } + + ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf); + len = wpabuf_len(mlbuf); + end = ((const u8 *) ml) + len; + + wpa_hexdump(MSG_DEBUG, "MLD: Defragged Reconfiguration MLE", + (const void *) ml, len); + + if (len < sizeof(*ml) + ETH_ALEN + 1UL) + goto fail; + + ml_control = WPA_GET_LE16((const u8 *) ml) >> 4; + ml_common_len = 1; + if (!(ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)) + goto fail; + ml_common_len += ETH_ALEN; + + if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA) + ml_common_len += 2; + + if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA) + ml_common_len += 2; + + if (ml_control & RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) + ml_common_len += 2; + + ml_common_info = + (const struct eht_ml_reconf_common_info *) ml->variable; + if (len < sizeof(*ml) + ml_common_info->len) { + wpa_printf(MSG_DEBUG, + "MLD: Unexpected Reconfiguration ML element length (%zu < %zu)", + len, sizeof(*ml) + ml_common_info->len); + goto fail; + } + + if (ml_common_info->len < ml_common_len) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid Reconf common info len (%u); min expected=%zu", + ml_common_info->len, ml_common_len); + goto fail; + } + + pos = (const u8 *) ml_common_info->variable; + + sta = ap_get_sta(hapd, pos); + if (!sta || !ap_sta_is_mld(hapd, sta)) { + wpa_printf(MSG_DEBUG, "MLD: STA invalid%s for " MACSTR, + sta ? "" : " (NULL)", MAC2STR(pos)); + goto fail; + } + + *req_list_ptr = os_zalloc(sizeof(struct link_reconf_req_list)); + if (!(*req_list_ptr)) { + wpa_printf(MSG_ERROR, "MLD: Failed to allocate request list"); + goto fail; + } + req_list = *req_list_ptr; + + os_memcpy(req_list->sta_mld_addr, pos, ETH_ALEN); + dl_list_init(&req_list->del_req); + dl_list_init(&req_list->add_req); + + pos = ml->variable + ml_common_info->len; + + while (end - pos > 2) { + size_t sub_elem_len; + int num_frag_subelems; + + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &sub_elem_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse Reconfiguration MLE subelem"); + goto fail; + } + + if ((size_t) num_frag_subelems * 2 > len) + goto fail; + len -= num_frag_subelems * 2; + end = ((const u8 *) ml) + len; + + if (sub_elem_len + 2 > (size_t) (end - pos)) + goto fail; + + if (hostapd_parse_link_reconf_req_sta_profile( + hapd, req_list, pos, sub_elem_len + 2) < 0) + goto fail; + + pos += sub_elem_len + 2; + } + + ret = 0; + +fail: + if (ret) + ml_deinit_link_reconf_req(req_list_ptr); + + wpabuf_free(mlbuf); + return ret; +} + + +static bool +hostapd_validate_link_reconf_req(struct hostapd_data *hapd, + struct sta_info *sta, + struct link_reconf_req_list *req_list) +{ + struct hostapd_data *assoc_hapd, *lhapd; + struct link_reconf_req_info *info; + struct sta_info *assoc_sta, *lsta; + struct mld_info *mld_info; + u8 recovery_link; + u16 valid_links = 0, links_add_ok = 0, links_del_ok = 0, status; + size_t link_kde_len, total_kde_len = 0; + int i; + + assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd); + if (!assoc_sta) + return false; + + if (dl_list_empty(&req_list->add_req) && + dl_list_empty(&req_list->del_req)) { + wpa_printf(MSG_DEBUG, "MLD: No add or delete request found"); + return false; + } + + mld_info = &assoc_sta->mld_info; + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (mld_info->links[i].valid && + mld_info->links[i].status == WLAN_STATUS_SUCCESS) + valid_links |= BIT(i); + } + + /* Check IEs for add-link STA profiles */ + dl_list_for_each(info, &req_list->add_req, struct link_reconf_req_info, + list) { + lhapd = NULL; + lsta = NULL; + + wpa_printf(MSG_DEBUG, + "MLD: Add Link Reconf STA for link id=%u status=%u", + info->link_id, info->status); + if (info->status != WLAN_STATUS_SUCCESS || + info->sta_prof_len < 2) + continue; + + /* Offset 2 bytes for Capabilities in STA Profile */ + status = hostapd_ml_process_reconf_link(hapd, assoc_sta, + info->sta_prof + 2, + info->sta_prof_len - 2, + info->link_id, + info->peer_addr); + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "MLD: Add link IE validation failed for link=%u", + info->link_id); + info->status = status; + continue; + } + + link_kde_len = wpa_auth_ml_group_kdes_len(assoc_sta->wpa_sm, + BIT(info->link_id)); + + /* Since Group KDE element Length subfield is one byte, + * accept as many add-link requests as can be fit. + */ + if (total_kde_len + link_kde_len > + LINK_RECONF_GROUP_KDE_MAX_LEN) { + wpa_printf(MSG_INFO, + "MLD: Group KDEs cannot fit (%zu > %u) for link=%u", + total_kde_len + link_kde_len, + LINK_RECONF_GROUP_KDE_MAX_LEN, + info->link_id); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + lhapd = hostapd_mld_get_link_bss(hapd, info->link_id); + if (lhapd) + lsta = ap_get_sta(lhapd, + req_list->sta_mld_addr); + + if (lsta) + ap_free_sta(lhapd, lsta); + } else { + total_kde_len += link_kde_len; + links_add_ok |= BIT(info->link_id); + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_LINK_STA_ADDED "sta=" MACSTR + " link_id=%u", MAC2STR(req_list->sta_mld_addr), + info->link_id); + } + + info->status = status; + } + + dl_list_for_each(info, &req_list->del_req, struct link_reconf_req_info, + list) { + wpa_printf(MSG_DEBUG, + "MLD: Del Link Reconf STA for link id=%u status=%u", + info->link_id, info->status); + if (info->status == WLAN_STATUS_SUCCESS) + links_del_ok |= BIT(info->link_id); + } + + wpa_printf(MSG_INFO, "MLD: valid_links=0x%x add_ok=0x%x del_ok=0x%x", + valid_links, links_add_ok, links_del_ok); + + if ((links_add_ok && (valid_links & links_add_ok)) || + (links_del_ok && !(valid_links & links_del_ok))) { + wpa_printf(MSG_INFO, + "MLD: Links requested failed to satisfy valid links"); + return false; + } + + if (links_add_ok & links_del_ok) { + wpa_printf(MSG_INFO, + "MLD: Links (0x%x) present in both valid add and delete requests", + links_add_ok & links_del_ok); + return false; + } + + valid_links |= links_add_ok; + valid_links &= ~links_del_ok; + if (!valid_links) { + if (!recover_from_zero_links(&links_del_ok, &recovery_link)) { + wpa_printf(MSG_INFO, + "MLD: Total-links validation failed"); + return false; + } + /* Add the recovery link back to valid_links */ + valid_links |= BIT(recovery_link); + } + + req_list->new_valid_links = valid_links; + req_list->links_add_ok = links_add_ok; + req_list->links_del_ok = links_del_ok; + + /* TODO: Add support to handle multiple requests from the non-AP MLD */ + assoc_sta->reconf_req = req_list; + + return true; +} + + +static int +hostapd_handle_link_reconf_req(struct hostapd_data *hapd, const u8 *buf, + size_t len) +{ + struct ieee802_11_elems elems; + struct hostapd_data *assoc_hapd; + struct sta_info *sta, *assoc_sta = NULL; + u8 dialog_token; + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + struct link_reconf_req_list *req_list = NULL; + const u8 *pos = NULL; + int ret = -1; + + wpa_printf(MSG_DEBUG, + "MLD: Link Reconfiguration Request frame from " MACSTR, + MAC2STR(mgmt->sa)); + + /* Min length: IEEE80211 Header (24B) + Category (1B) + Action (1B) + + * Dialog token (1B) + + * Reconfiguration MLE header and extension ID (3B) + */ + if (len < IEEE80211_HDRLEN + 3 + 3) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid minimum length (%zu) for Link Reconfiguration Request", + len); + goto out; + } + + dialog_token = mgmt->u.action.u.link_reconf_req.dialog_token; + pos = mgmt->u.action.u.link_reconf_req.variable; + + sta = ap_get_sta(hapd, mgmt->sa); + if (!sta) { + wpa_printf(MSG_DEBUG, "MLD: No STA found for " MACSTR + "; drop Link Reconfiguration Request", + MAC2STR(mgmt->sa)); + goto out; + } + + if (!ap_sta_is_mld(hapd, sta)) { + wpa_printf(MSG_DEBUG, + "MLD: Not an MLD connection; drop Link Reconfiguration Request"); + goto out; + } + + assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd); + if (!assoc_sta) { + wpa_printf(MSG_DEBUG, + "MLD: Not able to get assoc link STA; drop Link Reconfiguration Request"); + goto out; + } + + if (assoc_sta->reconf_req) { + wpa_printf(MSG_INFO, + "MLD: Link Reconfiguration Request from this STA with token=%u is already in progress", + assoc_sta->reconf_req->dialog_token); + goto out; + } + + /* Parse Reconfiguration Multi-Link element and OCI elements */ + if (ieee802_11_parse_elems(pos, len - (pos - buf), &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "MLD: Could not parse Link Reconfiguration Request"); + goto out; + } + + if (!elems.reconf_mle || !elems.reconf_mle_len) { + wpa_printf(MSG_DEBUG, "MLD: No Reconfiguration ML element"); + goto out; + } + + /* Process Reconfiguration MLE */ + if (hostapd_parse_link_reconf_req_reconf_mle(hapd, elems.reconf_mle, + elems.reconf_mle_len, + &req_list)) { + wpa_printf(MSG_INFO, + "MLD: Reconfiguration MLE parsing failed; drop Link Reconfiguration Request"); + goto out; + } + + /* Do OCI element validation */ + if (dl_list_empty(&req_list->add_req)) + goto skip_oci_validation; + +#ifdef CONFIG_OCV + if (!elems.oci || !elems.oci_len) { + if (wpa_auth_uses_ocv(assoc_sta->wpa_sm) == 1) { + wpa_printf(MSG_INFO, + "MLD: No OCI element present; drop Link Reconfiguration Request"); + goto out; + } + } else { + struct wpa_channel_info ci; + + if (!wpa_auth_uses_ocv(assoc_sta->wpa_sm)) { + wpa_printf(MSG_INFO, + "MLD: Unexpected OCI element found; drop Link Reconfiguration Request"); + goto out; + } + + if (hostapd_drv_channel_info(hapd, &ci)) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to get channel info to verify OCI element"); + goto out; + } + + if (!ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx)) { + wpa_printf(MSG_INFO, + "MLD: OCI verification failed; drop Link Reconfiguration Request"); + goto out; + } + } +#endif /* CONFIG_OCV */ + +skip_oci_validation: + /* Do STA profile validation */ + if (!hostapd_validate_link_reconf_req(hapd, assoc_sta, req_list)) + goto out; + + req_list->dialog_token = dialog_token; + ret = hostapd_send_link_reconf_resp(hapd, assoc_sta, req_list); + if (ret) + wpa_printf(MSG_INFO, + "MLD: Failed to send Link Reconfiguration Response (%d)", + ret); + +out: + if (ret) { + ml_deinit_link_reconf_req(&req_list); + if (assoc_sta && assoc_sta->reconf_req) + assoc_sta->reconf_req = NULL; + } + return ret; +} + + +void ieee802_11_rx_protected_eht_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + const u8 *payload; + u8 action; + + if (!hapd->conf->mld_ap) + return; + + payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; + action = *payload++; + + switch (action) { + case WLAN_PROT_EHT_LINK_RECONFIG_REQUEST: + if (hostapd_handle_link_reconf_req(hapd, (const u8 *) mgmt, + len)) + wpa_printf(MSG_INFO, + "MLD: Link Reconf Request processing failed"); + return; + } + + wpa_printf(MSG_DEBUG, + "MLD: Unsupported Protected EHT Action %u from " MACSTR + " discarded", action, MAC2STR(mgmt->sa)); +} + + +size_t hostapd_eid_eht_ml_tid_to_link_map_len(struct hostapd_data *hapd) +{ + if (!hapd->conf->mld_ap) + return 0; + +#ifdef CONFIG_TESTING_OPTIONS + /* + * Allocate enough space for mld_indicate_disabled. i.e.: + * Element ID, Length, and Element ID Extension (3) + + * Control including presence bitmap (2) + 8 * 2 byte link mappings + */ + return 3 + 2 + 8 * 2; +#else /* CONFIG_TESTING_OPTIONS */ + return 0; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +u8 * hostapd_eid_eht_ml_tid_to_link_map(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_TESTING_OPTIONS + struct hostapd_data *other_hapd; + bool need_ttlm = false; + u16 ttlm = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + u8 *pos = eid; + + if (!hapd->conf->mld_ap) + return eid; + +#ifdef CONFIG_TESTING_OPTIONS + for_each_mld_link(other_hapd, hapd) { + if (other_hapd->conf->mld_indicate_disabled) + need_ttlm = true; + else + ttlm |= BIT(other_hapd->mld_link_id); + } + + if (need_ttlm) { + int i; + + *pos++ = WLAN_EID_EXTENSION; + /* ext EID + 2 bytes control + 8 * link mappings */ + *pos++ = 1 + 2 + 8 * 2; + *pos++ = WLAN_EID_EXT_TID_TO_LINK_MAPPING; + *pos++ = EHT_TID_TO_LINK_MAP_DIRECTION_BOTH; + *pos++ = 0xff; /* link mapping presence bitmap */ + + for (i = 0; i < 8; i++) { + WPA_PUT_LE16(pos, ttlm); + pos += 2; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return pos; +} diff --git a/contrib/wpa/src/ap/ieee802_11_he.c b/contrib/wpa/src/ap/ieee802_11_he.c index a2deda6c4718..bc0b50c6e6ab 100644 --- a/contrib/wpa/src/ap/ieee802_11_he.c +++ b/contrib/wpa/src/ap/ieee802_11_he.c @@ -2,6 +2,7 @@ * hostapd / IEEE 802.11ax HE * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. * Copyright (c) 2019 John Crispin + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -70,7 +71,8 @@ static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len) u8 ppe_thres_hdr; cap = (struct ieee80211_he_capabilities *) buf; - cap_len = sizeof(*cap) - sizeof(cap->optional); + cap_len = sizeof(cap->he_mac_capab_info) + + sizeof(cap->he_phy_capab_info); if (len < cap_len) return 1; @@ -91,37 +93,21 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, { struct ieee80211_he_capabilities *cap; struct hostapd_hw_modes *mode = hapd->iface->current_mode; - u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK; + const struct he_capabilities *he_capab; u8 *pos = eid; - u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0; + u8 ie_size = 0, mcs_nss_size, ppet_size; + u8 *epos; if (!mode) return eid; - ie_size = sizeof(*cap) - sizeof(cap->optional); - ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0], - mode->he_capab[opmode].phy_cap); + he_capab = &mode->he_capab[opmode]; - switch (hapd->iface->conf->he_oper_chwidth) { - case CONF_OPER_CHWIDTH_80P80MHZ: - he_oper_chwidth |= - HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G; - mcs_nss_size += 4; - /* fall through */ - case CONF_OPER_CHWIDTH_160MHZ: - he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - mcs_nss_size += 4; - /* fall through */ - case CONF_OPER_CHWIDTH_80MHZ: - case CONF_OPER_CHWIDTH_USE_HT: - he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; - break; - default: - break; - } + mcs_nss_size = ieee80211_he_mcs_set_size(he_capab->phy_cap); + ppet_size = ieee80211_he_ppet_size(he_capab->ppet[0], + he_capab->phy_cap); - ie_size += mcs_nss_size + ppet_size; + ie_size = IEEE80211_HE_CAPAB_MIN_LEN + mcs_nss_size + ppet_size; *pos++ = WLAN_EID_EXTENSION; *pos++ = 1 + ie_size; @@ -130,14 +116,16 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, cap = (struct ieee80211_he_capabilities *) pos; os_memset(cap, 0, sizeof(*cap)); - os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap, + os_memcpy(cap->he_mac_capab_info, he_capab->mac_cap, HE_MAX_MAC_CAPAB_SIZE); - os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap, + os_memcpy(cap->he_phy_capab_info, he_capab->phy_cap, HE_MAX_PHY_CAPAB_SIZE); - os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size); + epos = (u8 *) &cap->he_basic_supported_mcs_set; + os_memcpy(epos, he_capab->mcs, mcs_nss_size); + epos += mcs_nss_size; + if (ppet_size) - os_memcpy(&cap->optional[mcs_nss_size], - mode->he_capab[opmode].ppet, ppet_size); + os_memcpy(epos, he_capab->ppet, ppet_size); if (hapd->iface->conf->he_phy_capab.he_su_beamformer) cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= @@ -160,9 +148,6 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &= ~HE_PHYCAP_MU_BEAMFORMER_CAPAB; - cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &= - he_oper_chwidth; - pos += ie_size; return pos; @@ -221,7 +206,7 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) if (is_6ghz_op_class(hapd->iconf->op_class)) { enum oper_chan_width oper_chwidth = - hostapd_get_oper_chwidth(hapd->iconf); + hapd->iconf->he_oper_chwidth; u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx; u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf); u8 control; @@ -229,6 +214,9 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) u16 punct_bitmap = hostapd_get_punct_bitmap(hapd); if (punct_bitmap) { + oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf); + seg0 = hostapd_get_oper_centr_freq_seg0_idx( + hapd->iconf); punct_update_legacy_bw(punct_bitmap, hapd->iconf->channel, &oper_chwidth, &seg0, &seg1); @@ -427,14 +415,15 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab, { u16 sta_rx_mcs_set, ap_tx_mcs_set; u8 mcs_count = 0; - const u16 *ap_mcs_set, *sta_mcs_set; + const u16 *ap_mcs_set; + const u8 *sta_mcs_set; int i; if (!hapd->iface->current_mode) return 1; ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs; - sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *) - sta_he_capab)->optional; + sta_mcs_set = (const u8 *) &((const struct ieee80211_he_capabilities *) + sta_he_capab)->he_basic_supported_mcs_set; /* * Disable HE capabilities for STAs for which there is not even a single @@ -458,7 +447,7 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab, int j; /* AP Tx MCS map vs. STA Rx MCS map */ - sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]); + sta_rx_mcs_set = WPA_GET_LE16(&sta_mcs_set[i * 4]); ap_tx_mcs_set = WPA_GET_LE16((const u8 *) &ap_mcs_set[(i * 2) + 1]); @@ -485,7 +474,7 @@ u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, size_t he_capab_len) { if (!he_capab || !(sta->flags & WLAN_STA_WMM) || - !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax || + !hostapd_is_he_enabled(hapd) || !check_valid_he_mcs(hapd, he_capab, opmode) || ieee80211_invalid_he_cap_size(he_capab, he_capab_len) || he_capab_len > sizeof(struct ieee80211_he_capabilities)) { @@ -514,9 +503,9 @@ u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *he_6ghz_capab) { - if (!he_6ghz_capab || !hapd->iconf->ieee80211ax || - hapd->conf->disable_11ax || - !is_6ghz_op_class(hapd->iconf->op_class)) { + if (!he_6ghz_capab || !hostapd_is_he_enabled(hapd) || + !is_6ghz_op_class(hapd->iconf->op_class) || + !(sta->flags & WLAN_STA_HE)) { sta->flags &= ~WLAN_STA_6GHZ; os_free(sta->he_6ghz_capab); sta->he_6ghz_capab = NULL; @@ -545,7 +534,7 @@ int hostapd_get_he_twt_responder(struct hostapd_data *hapd, if (!hapd->iface->current_mode || !hapd->iface->current_mode->he_capab[mode].he_supported || - !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax) + !hostapd_is_he_enabled(hapd)) return 0; mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap; diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c index f90f1254e040..04bb60f45db3 100644 --- a/contrib/wpa/src/ap/ieee802_11_ht.c +++ b/contrib/wpa/src/ap/ieee802_11_ht.c @@ -26,8 +26,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) struct ieee80211_ht_capabilities *cap; u8 *pos = eid; - if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || - hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class)) + if (!hostapd_is_ht_enabled(hapd) || !hapd->iface->current_mode || + is_6ghz_op_class(hapd->iconf->op_class)) return eid; *pos++ = WLAN_EID_HT_CAP; @@ -79,12 +79,57 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) } +static void set_ht_param(struct hostapd_data *hapd, + struct ieee80211_ht_operation *oper) +{ + int secondary_channel = hapd->iconf->secondary_channel; +#ifdef CONFIG_IEEE80211BE + enum oper_chan_width chwidth = hostapd_get_oper_chwidth(hapd->iconf); + u16 bw = 0, punct_bitmap = hostapd_get_punct_bitmap(hapd); + u8 offset, chan_bit_pos; + + switch (chwidth) { + case CONF_OPER_CHWIDTH_80MHZ: + bw = 80; + offset = 6; + break; + case CONF_OPER_CHWIDTH_160MHZ: + bw = 160; + offset = 14; + break; + case CONF_OPER_CHWIDTH_320MHZ: + bw = 320; + offset = 30; + break; + default: + goto no_update; + } + + chan_bit_pos = (hapd->iconf->channel - + hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf) + + offset) / 4; + /* Check if secondary channel is punctured */ + if (bw >= 80 && punct_bitmap && secondary_channel && + (punct_bitmap & BIT(chan_bit_pos + secondary_channel))) + return; /* Do not indicate punctured secondary channel for HT */ +no_update: +#endif /* CONFIG_IEEE80211BE */ + + if (secondary_channel == 1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; + if (secondary_channel == -1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; +} + + u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_ht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n || + if (!hostapd_is_ht_enabled(hapd) || is_6ghz_op_class(hapd->iconf->op_class)) return eid; @@ -96,12 +141,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) oper->primary_chan = hapd->iconf->channel; oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); - if (hapd->iconf->secondary_channel == 1) - oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | - HT_INFO_HT_PARAM_STA_CHNL_WIDTH; - if (hapd->iconf->secondary_channel == -1) - oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | - HT_INFO_HT_PARAM_STA_CHNL_WIDTH; + set_ht_param(hapd, oper); pos += sizeof(*oper); @@ -363,7 +403,7 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, * frame. */ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || - !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { + !hostapd_is_ht_enabled(hapd)) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; @@ -417,6 +457,7 @@ void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) iface->num_sta_ht40_intolerant--; if (iface->num_sta_ht40_intolerant == 0 && + iface->conf->obss_interval && (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * diff --git a/contrib/wpa/src/ap/ieee802_11_shared.c b/contrib/wpa/src/ap/ieee802_11_shared.c index 3dd3a6a7719e..30f1c8c153ba 100644 --- a/contrib/wpa/src/ap/ieee802_11_shared.c +++ b/contrib/wpa/src/ap/ieee802_11_shared.c @@ -381,6 +381,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx, /* Bit 13 - Collocated Interference Reporting */ *pos |= 0x20; } + if (hapd->iface->conf->civic) + *pos |= 0x40; /* Bit 14 - Civic Location */ + if (hapd->iface->conf->lci) + *pos |= 0x80; /* Bit 15 - Geospatial Location */ break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) @@ -476,6 +480,19 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx, *pos |= 0x01; /* Bit 88 - SAE PK Exclusively */ #endif /* CONFIG_SAE_PK */ break; + case 12: /* Bits 96-103 */ + if (hapd->iconf->i2r_lmr_policy) + *pos |= 0x02; /* Bit 97 - I2R LMR Feedback Policy */ + if (hapd->iconf->peer_to_peer_twt) + *pos |= 0x10; /* Bit 100 - Peer to Peer TWT */ + if (hapd->conf->known_sta_identification) + *pos |= 0x40; /* Bit 102 - Known STA Identification + * Enabled */ + break; + case 13: /* Bits 104-111 */ + if (hapd->iconf->channel_usage) + *pos |= 0x01; /* Bit 104 - Channel Usage support */ + break; } } @@ -1034,7 +1051,8 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, int requested_bw; if (sta->ht_capabilities) - ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & + ht_40mhz = !!(le_to_host16(sta->ht_capabilities-> + ht_capabilities_info) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); if (sta->vht_operation) { @@ -1070,9 +1088,9 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, * normal clients), use it to determine the supported channel * bandwidth. */ - vht_chanwidth = capab->vht_capabilities_info & + vht_chanwidth = le_to_host32(capab->vht_capabilities_info) & VHT_CAP_SUPP_CHAN_WIDTH_MASK; - vht_80p80 = capab->vht_capabilities_info & + vht_80p80 = le_to_host32(capab->vht_capabilities_info) & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; /* TODO: Also take into account Extended NSS BW Support field */ @@ -1097,7 +1115,7 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) { u8 *pos = eid; bool sae_pk = false; - u32 capab = 0, tmp; + u64 capab = 0, tmp; size_t flen; if (!(hapd->conf->wpa & WPA_PROTO_RSN)) @@ -1124,10 +1142,37 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); - if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP) - capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP) { + if (hapd->conf->urnm_mfpr != 0) + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (hapd->conf->urnm_mfpr_x20 == 1) + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20); + } if (hapd->conf->ssid_protection) capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION); + if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) && + hapd->conf->spp_amsdu) + capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); +#ifdef CONFIG_ENC_ASSOC + /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy) + * a STA that sets the PMKSA Caching Privacy Support + * field in the RSNXE to 1 shall set the (Re)Association + * Frame Encryption Support field in the RSNXE to 1. + */ + if ((hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) && + (hapd->conf->assoc_frame_encryption || + hapd->conf->pmksa_caching_privacy)) + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_EPPKE) + capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + if (hapd->conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); + if (hapd->conf->eap_using_authentication_frames) + capab |= BIT(WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES); + if (hapd->conf->eppke_unauth) + capab |= BIT_ULL(WLAN_RSNX_CAPAB_UNAUTH_EPPKE); +#endif /* CONFIG_ENC_ASSOC */ if (!capab) return eid; /* no supported extended RSN capabilities */ @@ -1221,8 +1266,52 @@ struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd) { return hapd->iconf->ht_vht_twt_responder && - ((hapd->iconf->ieee80211n && !hapd->conf->disable_11n) || - (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)) && + (hostapd_is_ht_enabled(hapd) || + hostapd_is_vht_enabled(hapd)) && (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER); } + + +static void hostapd_wfa_gen_capab(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *capab, size_t len) +{ + char *hex; + size_t buflen; + + wpa_printf(MSG_DEBUG, + "WFA: Received indication of generational capabilities from " + MACSTR, MAC2STR(sta->addr)); + wpa_hexdump(MSG_DEBUG, "WFA: Generational Capabilities", capab, len); + + buflen = 2 * len + 1; + hex = os_zalloc(buflen); + if (!hex) + return; + wpa_snprintf_hex(hex, buflen, capab, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, WFA_GEN_CAPAB_RX MACSTR " %s", + MAC2STR(sta->addr), hex); + os_free(hex); +} + + +void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, const u8 *end) +{ + u8 capab_len; + const u8 *gen_capa; + + if (end - pos < 1) + return; + capab_len = *pos++; + if (capab_len > end - pos) + return; + pos += capab_len; /* skip the Capabilities field */ + + /* Wi-Fi Alliance Capabilities attributes use a header that is similar + * to the one used in Information Elements. */ + gen_capa = get_ie(pos, end - pos, WFA_CAPA_ATTR_GENERATIONAL_CAPAB); + if (gen_capa) + hostapd_wfa_gen_capab(hapd, sta, gen_capa + 2, gen_capa[1]); +} diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c index 4dc325ce89bb..65143626a771 100644 --- a/contrib/wpa/src/ap/ieee802_11_vht.c +++ b/contrib/wpa/src/ap/ieee802_11_vht.c @@ -21,6 +21,24 @@ #include "dfs.h" +static struct hostapd_hw_modes * +mode_for_vht_capab(struct hostapd_data *hapd, struct hostapd_hw_modes *mode) +{ + if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && + mode->vht_capab == 0 && hapd->iface->hw_features) { + int i; + + for (i = 0; i < hapd->iface->num_hw_features; i++) { + if (hapd->iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) + return &hapd->iface->hw_features[i]; + } + } + + return mode; +} + + u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) { struct ieee80211_vht_capabilities *cap; @@ -30,18 +48,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) if (!mode || is_6ghz_op_class(hapd->iconf->op_class)) return eid; - if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && - mode->vht_capab == 0 && hapd->iface->hw_features) { - int i; - - for (i = 0; i < hapd->iface->num_hw_features; i++) { - if (hapd->iface->hw_features[i].mode == - HOSTAPD_MODE_IEEE80211A) { - mode = &hapd->iface->hw_features[i]; - break; - } - } - } + mode = mode_for_vht_capab(hapd, mode); *pos++ = WLAN_EID_VHT_CAP; *pos++ = sizeof(*cap); @@ -95,6 +102,9 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) #ifdef CONFIG_IEEE80211BE if (punct_bitmap) { + oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf); + seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf); + seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf); punct_update_legacy_bw(punct_bitmap, hapd->iconf->channel, &oper_chwidth, &seg0, &seg1); @@ -140,9 +150,10 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) } -static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, +static int check_valid_vht_mcs(struct hostapd_data *hapd, const u8 *sta_vht_capab) { + struct hostapd_hw_modes *mode = hapd->iface->current_mode; const struct ieee80211_vht_capabilities *vht_cap; struct ieee80211_vht_capabilities ap_vht_cap; u16 sta_rx_mcs_set, ap_tx_mcs_set; @@ -150,6 +161,7 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, if (!mode) return 1; + mode = mode_for_vht_capab(hapd, mode); /* * Disable VHT caps for STAs for which there is not even a single @@ -166,10 +178,10 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map); for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) { - if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3) + if (((ap_tx_mcs_set >> (i * 2)) & 0x3) == 3) continue; - if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3) + if (((sta_rx_mcs_set >> (i * 2)) & 0x3) == 3) continue; return 1; @@ -186,8 +198,9 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || !(sta->flags & WLAN_STA_WMM) || - !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac || - !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { + !hostapd_is_vht_enabled(hapd) || + !check_valid_vht_mcs(hapd, vht_capab) || + !(sta->flags & WLAN_STA_HT)) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); sta->vht_capabilities = NULL; @@ -212,7 +225,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_oper) { - if (!vht_oper) { + if (!vht_oper || !(sta->flags & WLAN_STA_VHT)) { os_free(sta->vht_operation); sta->vht_operation = NULL; return WLAN_STATUS_SUCCESS; diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c index f4103ac9a16d..7f47318b2710 100644 --- a/contrib/wpa/src/ap/ieee802_1x.c +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -45,7 +45,7 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_HS20 */ static bool ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, - int remediation, bool logoff); + bool logoff); static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, @@ -149,9 +149,9 @@ static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd, bool authorized) { #ifdef CONFIG_IEEE80211BE - unsigned int i, link_id; + unsigned int i; - if (!hostapd_is_mld_ap(hapd)) + if (!hostapd_is_multiple_link_mld(hapd)) return; /* @@ -161,32 +161,35 @@ static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd, if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id) return; - for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { - struct mld_link_info *link = &sta->mld_info.links[link_id]; + for (i = 0; i < hapd->iface->interfaces->count; i++) { + struct sta_info *tmp_sta; + struct mld_link_info *link; + struct hostapd_data *tmp_hapd = + hapd->iface->interfaces->iface[i]->bss[0]; + if (!hostapd_is_ml_partner(hapd, tmp_hapd)) + continue; + + link = &sta->mld_info.links[tmp_hapd->mld_link_id]; if (!link->valid) continue; - for (i = 0; i < hapd->iface->interfaces->count; i++) { - struct sta_info *tmp_sta; - struct hostapd_data *tmp_hapd = - hapd->iface->interfaces->iface[i]->bss[0]; - - if (!hostapd_is_ml_partner(hapd, tmp_hapd)) + for (tmp_sta = tmp_hapd->sta_list; tmp_sta; + tmp_sta = tmp_sta->next) { + if (tmp_sta == sta || + tmp_sta->mld_assoc_link_id != + sta->mld_assoc_link_id || + tmp_sta->aid != sta->aid) continue; - for (tmp_sta = tmp_hapd->sta_list; tmp_sta; - tmp_sta = tmp_sta->next) { - if (tmp_sta == sta || - tmp_sta->mld_assoc_link_id != - sta->mld_assoc_link_id || - tmp_sta->aid != sta->aid) - continue; + if (!ether_addr_equal( + tmp_sta->mld_info.common_info.mld_addr, + sta->mld_info.common_info.mld_addr)) + continue; - ieee802_1x_set_authorized(tmp_hapd, tmp_sta, - authorized, true); - break; - } + ieee802_1x_set_authorized(tmp_hapd, tmp_sta, + authorized, true); + break; } } #endif /* CONFIG_IEEE80211BE */ @@ -453,8 +456,7 @@ static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, return -1; } - suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) || - hapd->conf->osen) ? + suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2)) ? WPA_PROTO_RSN : WPA_PROTO_WPA, hapd->conf->wpa_group); if (!hostapd_config_get_radius_attr(req_attr, @@ -583,7 +585,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211R_AP */ - if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && + if (hapd->conf->wpa && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) return -1; @@ -1125,7 +1127,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct rsn_pmksa_cache_entry *pmksa; int key_mgmt; - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen && + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->wps_state) return; @@ -1134,6 +1136,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, (unsigned long) len, MAC2STR(sa), encrypted); sta = ap_get_sta(hapd, sa); if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && +#ifdef CONFIG_IEEE8021X_AUTH + !sta->epp_sta && +#endif /* CONFIG_IEEE8021X_AUTH */ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not associated/Pre-authenticating STA"); @@ -1185,7 +1190,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } - if (!hapd->conf->ieee802_1x && !hapd->conf->osen && + if (!hapd->conf->ieee802_1x && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used"); @@ -1252,6 +1257,29 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start from STA"); +#ifdef CONFIG_IEEE80211R_AP + if (hapd->conf->wpa && + wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && + ((wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) && + (sta->flags & WLAN_STA_AUTHORIZED)) || + sta->auth_alg == WLAN_AUTH_FT)) { + /* When FT is used, reauthentication to generate a new + * PMK-R0 would be complicated since the current AP + * might not be the one with which the currently used + * PMK-R0 was generated. IEEE Std 802.11-2020, 13.4.2 + * (FT initial mobility domain association in an RSN) + * mandates STA to perform a new FT initial mobility + * domain association whenever its Supplicant would + * trigger sending of an EAPOL-Start frame. As such, + * this EAPOL-Start frame should not have been sent. + * Discard it to avoid unexpected behavior. */ + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "discard unexpected EAPOL-Start from STA that uses FT"); + break; + } +#endif /* CONFIG_IEEE80211R_AP */ sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { @@ -1337,7 +1365,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) { + if (!force_1x && !hapd->conf->ieee802_1x) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS"); /* @@ -1348,6 +1376,19 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) return; } +#ifdef CONFIG_IEEE8021X_AUTH + if (sta->auth_alg == WLAN_AUTH_802_1X) { + wpa_printf(MSG_DEBUG, + "IEEE 802.1X: Ignore STA - EAP in Authentication frames"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ + ieee802_1x_free_station(hapd, sta); + return; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); if (key_mgmt != -1 && (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || @@ -1471,10 +1512,6 @@ void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; -#ifdef CONFIG_HS20 - eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); -#endif /* CONFIG_HS20 */ - if (sta->pending_eapol_rx) { wpabuf_free(sta->pending_eapol_rx->buf); os_free(sta->pending_eapol_rx); @@ -1750,32 +1787,6 @@ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, #ifdef CONFIG_HS20 -static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len) -{ - sta->remediation = 1; - os_free(sta->remediation_url); - if (len > 2) { - sta->remediation_url = os_malloc(len); - if (!sta->remediation_url) - return; - sta->remediation_method = pos[0]; - os_memcpy(sta->remediation_url, pos + 1, len - 1); - sta->remediation_url[len - 1] = '\0'; - wpa_printf(MSG_DEBUG, - "HS 2.0: Subscription remediation needed for " - MACSTR " - server method %u URL %s", - MAC2STR(sta->addr), sta->remediation_method, - sta->remediation_url); - } else { - sta->remediation_url = NULL; - wpa_printf(MSG_DEBUG, - "HS 2.0: Subscription remediation needed for " - MACSTR, MAC2STR(sta->addr)); - } - /* TODO: assign the STA into remediation VLAN or add filtering */ -} - - static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *pos, size_t len) @@ -1891,7 +1902,6 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, size_t len; buf = NULL; - sta->remediation = 0; sta->hs20_deauth_requested = 0; sta->hs20_deauth_on_ack = 0; @@ -1916,9 +1926,6 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, continue; /* invalid WFA VSA */ switch (type) { - case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION: - ieee802_1x_hs20_sub_rem(sta, pos, sublen); - break; case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ: ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen); break; @@ -2306,9 +2313,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, const u8 *data, size_t datalen) { -#ifdef CONFIG_WPS + struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; +#ifdef CONFIG_WPS + if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == WLAN_STA_MAYBE_WPS) { const u8 *identity; @@ -2330,7 +2339,16 @@ static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, } #endif /* CONFIG_WPS */ - ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +#ifdef CONFIG_IEEE8021X_AUTH + if (sta->epp_sta && hapd->send_eap_req) { + hapd->send_eap_req(hapd, sta, type, + sta->eap_auth_data.auth_transaction + 1, + WLAN_STATUS_SUCCESS, NULL, data, datalen); + return; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + + ieee802_1x_send(hapd, sta, type, data, datalen); } @@ -2347,7 +2365,7 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, - int preauth, int remediation, bool logoff) + int preauth, bool logoff) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; @@ -2357,7 +2375,7 @@ static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, return false; } - return ieee802_1x_finished(hapd, sta, success, remediation, logoff); + return ieee802_1x_finished(hapd, sta, success, logoff); } @@ -2399,7 +2417,6 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; - user->remediation = eap_user->remediation; rv = 0; out: @@ -2557,8 +2574,6 @@ int ieee802_1x_init(struct hostapd_data *hapd) } #endif /* CONFIG_IEEE80211BE */ - dl_list_init(&hapd->erp_keys); - os_memset(&conf, 0, sizeof(conf)); conf.eap_cfg = hapd->eap_cfg; conf.ctx = hapd; @@ -2987,7 +3002,11 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, id = eap_get_identity(sm->eap, &id_len); if (id) identity_buf = dup_binstr(id, id_len); + } else if (sm->eap && sm->identity) { + identity_buf = dup_binstr(sm->identity, + sm->identity_len); } + ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -3003,8 +3022,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, - sm->identity ? (char *) sm->identity : - (identity_buf ? identity_buf : "N/A")); + identity_buf ? identity_buf : "N/A"); os_free(identity_buf); if (os_snprintf_error(buflen - len, ret)) return len; @@ -3041,17 +3059,6 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - if (sta->remediation) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " - MACSTR " to indicate Subscription Remediation", - MAC2STR(sta->addr)); - hs20_send_wnm_notification(hapd, sta->addr, - sta->remediation_method, - sta->remediation_url); - os_free(sta->remediation_url); - sta->remediation_url = NULL; - } - if (sta->hs20_deauth_req) { wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR " to indicate imminent deauthentication", @@ -3074,7 +3081,7 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) static bool ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, - int remediation, bool logoff) + bool logoff) { const u8 *key; size_t len; @@ -3084,16 +3091,7 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd, struct os_reltime now, remaining; #ifdef CONFIG_HS20 - if (remediation && !sta->remediation) { - sta->remediation = 1; - os_free(sta->remediation_url); - sta->remediation_url = - os_strdup(hapd->conf->subscr_remediation_url); - sta->remediation_method = 1; /* SOAP-XML SPP */ - } - - if (success && (sta->remediation || sta->hs20_deauth_req || - sta->hs20_t_c_filtering)) { + if (success && (sta->hs20_deauth_req || sta->hs20_t_c_filtering)) { wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " MACSTR " in 100 ms", MAC2STR(sta->addr)); eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); @@ -3114,7 +3112,7 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd, } else { session_timeout = dot11RSNAConfigPMKLifetime; } - if (success && key && len >= PMK_LEN && !sta->remediation && + if (success && key && len >= PMK_LEN && !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, sta->eapol_sm) == 0) { @@ -3142,3 +3140,13 @@ static bool ieee802_1x_finished(struct hostapd_data *hapd, return false; } + + +#ifdef CONFIG_IEEE8021X_AUTH +void ieee802_1x_eapol_sm_set_port_enabled(struct eapol_state_machine *sm, + bool value) +{ + if (sm && sm->eap_if) + sm->eap_if->portEnabled = value; +} +#endif /* CONFIG_IEEE8021X_AUTH */ diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h index 1469351c1e55..35d16d2c7236 100644 --- a/contrib/wpa/src/ap/ieee802_1x.h +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -66,4 +66,6 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct eapol_state_machine * ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_eapol_sm_set_port_enabled(struct eapol_state_machine *sm, + bool value); #endif /* IEEE802_1X_H */ diff --git a/contrib/wpa/src/ap/interference.c b/contrib/wpa/src/ap/interference.c new file mode 100644 index 000000000000..c93cdc75f13f --- /dev/null +++ b/contrib/wpa/src/ap/interference.c @@ -0,0 +1,592 @@ +/* + * Management of interference due to incumbent signals in 6 GHz band + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * 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/hw_features_common.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "beacon.h" +#include "interference.h" + + +#define BASE_6GHZ_FREQ 5950 +/* Channel 2 (op class 136) has a center frequency of 5935 MHz. */ +#define BASE_6GHZ_CH2_FREQ 5935 + +enum chan_seg { + SEG_PRI20 = 0x1, + SEG_SEC20 = 0x2, + SEG_SEC40_LOW = 0x4, + SEG_SEC40_UP = 0x8, + SEG_SEC40 = 0xC, + SEG_SEC80_LOW = 0x10, + SEG_SEC80_LOW_UP = 0x20, + SEG_SEC80_UP_LOW = 0x40, + SEG_SEC80_UP = 0x80, + SEG_SEC80 = 0xF0, + SEG_SEC160_LOW = 0x0100, + SEG_SEC160_LOW_UP = 0x0200, + SEG_SEC160_LOW_UP_UP = 0x0400, + SEG_SEC160_LOW_UP_UP_UP = 0x0800, + SEG_SEC160_UP_LOW_LOW_LOW = 0x1000, + SEG_SEC160_UP_LOW_LOW = 0x2000, + SEG_SEC160_UP_LOW = 0x4000, + SEG_SEC160_UP = 0x8000, + SEG_SEC160 = 0xFF00, +}; + + +static bool is_chan_disabled(struct hostapd_hw_modes *mode, int chan_num) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == chan_num) + return !!(chan->flag & HOSTAPD_CHAN_DISABLED); + } + + return true; +} + + +/* + * chan_range_available - Check whether the channel can operate in the given + * bandwidth in 6 GHz + * @first_chan_idx - Channel index of the first 20 MHz channel in a segment + * @num_chans - Number of 20 MHz channels needed for the operating bandwidth + * @allowed_arr - Range of channels allowed in the given bandwidth + * @allowed_arr_size - Number of channels allowed in the given bandwidth + * Returns: Whether the channel can operation in the given bandwidth + */ +static bool chan_range_available(struct hostapd_hw_modes *mode, + unsigned int first_chan_idx, + unsigned int num_chans, + const int *allowed_arr, + unsigned int allowed_arr_size) +{ + struct hostapd_channel_data *first_chan; + unsigned int i; + + first_chan = &mode->channels[first_chan_idx]; + + if (!chan_pri_allowed(first_chan)) { + wpa_printf(MSG_DEBUG, "Intf: Primary channel %d not allowed", + first_chan->chan); + return false; + } + + /* 20 MHz channel, so no need to check the range */ + if (num_chans == 1) + return true; + + if (allowed_arr) { + for (i = 0; i < allowed_arr_size; i++) { + if (first_chan->chan == allowed_arr[i]) + break; + } + if (i == allowed_arr_size) + return false; + } + + /* + * Check whether all the 20 MHz channels in the given operating range + * are enabled. + */ + for (i = 1; i < num_chans; i++) { + if (is_chan_disabled(mode, first_chan->chan + i * 4)) + return false; + } + + return true; +} + + +static bool is_in_chanlist(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + if (!iface->conf->acs_ch_list.num) + return true; + + return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); +} + + +static int get_center_freq_6g(int chan_idx, enum chan_width chan_width, + int *center_freq) +{ + if (!center_freq) + return -1; + + *center_freq = 0; + + switch (chan_width) { + case CHAN_WIDTH_20: + if (chan_idx == 2) { + *center_freq = BASE_6GHZ_CH2_FREQ; + break; + } + if (chan_idx >= 1 && chan_idx <= 233) + *center_freq = ((chan_idx / 4) * 4 + 1) * 5 + + BASE_6GHZ_FREQ; + break; + case CHAN_WIDTH_40: + if (chan_idx >= 1 && chan_idx <= 229) + *center_freq = ((chan_idx / 8) * 8 + 3) * 5 + + BASE_6GHZ_FREQ; + break; + case CHAN_WIDTH_80: + if (chan_idx >= 1 && chan_idx <= 221) + *center_freq = ((chan_idx / 16) * 16 + 7) * 5 + + BASE_6GHZ_FREQ; + break; + case CHAN_WIDTH_160: + if (chan_idx >= 1 && chan_idx <= 221) + *center_freq = ((chan_idx / 32) * 32 + 15) * 5 + + BASE_6GHZ_FREQ; + break; + case CHAN_WIDTH_320: + if (chan_idx >= 1 && chan_idx <= 221) + *center_freq = ((chan_idx / 32) * 32 + 31) * 5 + + BASE_6GHZ_FREQ; + break; + default: + break; + } + + if (*center_freq == 0) + return -1; + + return 0; +} + + +static bool is_interference_in_chanlist(int freq_start, int freq_end, + const int *interference_freqs) +{ + int i; + + for (i = freq_start; i <= freq_end; i += 20) { + if (int_array_includes(interference_freqs, i)) + return true; + } + return false; +} + + + +static const int * get_allowed_channel_array(int num_chans, unsigned int *size) +{ + static const int allowed_40_6g[] = { 1, 9, 17, 25, 33, 41, 49, 57, 65, + 73, 81, 89, 97, 105, 113, 121, 129, + 137, 145, 153, 161, 169, 177, 185, + 193, 201, 209, 217, 225, 233 }; + static const int allowed_80_6g[] = { 1, 17, 33, 49, 65, 81, 97, 113, + 129, 145, 161, 177, + 193, 209 }; + static const int allowed_160_6g[] = { 1, 33, 65, 97, 129, 161, 193 }; + static const int allowed_320_6g[] = { 1, 65, 129, 33, 97, 161 }; + + switch (num_chans) { + case 2: + *size = ARRAY_SIZE(allowed_40_6g); + return allowed_40_6g; + case 4: + *size = ARRAY_SIZE(allowed_80_6g); + return allowed_80_6g; + case 8: + *size = ARRAY_SIZE(allowed_160_6g); + return allowed_160_6g; + case 16: + *size = ARRAY_SIZE(allowed_320_6g); + return allowed_320_6g; + default: + *size = 0; + return NULL; + } +} + + +/** + * intf_find_channel_list - Find the list of channels that can operate with + * channel width chan_width and not present within the range of current + * operating range. + * @chan_width - Channel width to be checked + * @chandef_list - Pointer array to hold the list of valid available chandef + * Returns: The total number of available chandefs that support the provided + * bandwidth + */ +static int intf_find_channel_list(struct hostapd_iface *iface, int chan_width, + struct hostapd_channel_data **chandef_list, + const int *interference_freqs) +{ + struct hostapd_hw_modes *mode = iface->current_mode; + int new_center_freq, new_start_freq, new_end_freq; + const int *allowed_arr = NULL; + unsigned int allowed_arr_size = 0; + struct hostapd_channel_data *chan; + int i; + unsigned int channel_idx = 0, n_chans; + int ret; + + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + n_chans = 1; + break; + case CHAN_WIDTH_40: + n_chans = 2; + break; + case CHAN_WIDTH_80: + n_chans = 4; + break; + case CHAN_WIDTH_80P80: + case CHAN_WIDTH_160: + n_chans = 8; + break; + case CHAN_WIDTH_320: + n_chans = 16; + break; + default: + n_chans = 1; + break; + } + + allowed_arr = get_allowed_channel_array(n_chans, &allowed_arr_size); + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (!chan_in_current_hw_info(iface->current_hw_info, chan)) { + wpa_printf(MSG_DEBUG, + "Intf: Channel %d (%d) is not under current hardware index", + chan->freq, chan->chan); + continue; + } + + /* Skip incompatible chandefs */ + if (!chan_range_available(mode, i, n_chans, + allowed_arr, allowed_arr_size)) { + wpa_printf(MSG_DEBUG, + "Intf: Range not available for chan %d (%d)", + chan->freq, chan->chan); + continue; + } + + if (!is_in_chanlist(iface, chan)) { + wpa_printf(MSG_DEBUG, + "Intf: Channel %d (%d) not in chanlist", + chan->freq, chan->chan); + continue; + } + + ret = get_center_freq_6g(chan->chan, chan_width, + &new_center_freq); + if (ret) { + wpa_printf(MSG_INFO, + "Intf: Couldn't find center freq for chan %d chan_width %d", + chan->chan, chan_width); + return 0; + } + + new_start_freq = (new_center_freq - + channel_width_to_int(chan_width) / 2) + 10; + new_end_freq = (new_center_freq + + channel_width_to_int(chan_width) / 2) - 10; + + if (is_interference_in_chanlist(new_start_freq, new_end_freq, + interference_freqs)) { + wpa_printf(MSG_DEBUG, + "Intf: Found frequency which has interference in channel (%d)", + chan->chan); + continue; + } + + wpa_printf(MSG_DEBUG, + "Intf: Adding channel %d (%d) to valid chandef list", + chan->freq, chan->chan); + chandef_list[channel_idx] = chan; + channel_idx++; + } + + return channel_idx; +} + + +static enum chan_width downgrade_bandwidth(enum chan_width chan_width) +{ + switch (chan_width) { + case CHAN_WIDTH_320: + return CHAN_WIDTH_160; + case CHAN_WIDTH_160: + return CHAN_WIDTH_80; + case CHAN_WIDTH_80: + return CHAN_WIDTH_40; + case CHAN_WIDTH_40: + return CHAN_WIDTH_20; + default: + return CHAN_WIDTH_20_NOHT; + } +} + + +/* + * hostapd_incumbt_sig_intf_detected - Incumbent signal interference is detected + * in the operating channel. The interference channel information is available + * as a bitmap(chan_bw_interference_bitmap). If interference has occurred in + * the primary channel, do a complete channel switch to a different channel; + * otherwise, reduce the operating bandwidth and continue AP operation using + * the same primary channel. + */ +int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq, + int chan_width, int cf1, int cf2, + u32 chan_bw_interference_bitmap) +{ + struct csa_settings settings; + struct hostapd_channel_data *chan_data = NULL; + struct hostapd_channel_data *chan_temp; + struct hostapd_channel_data **available_chandef_list = NULL; + int ret = 0; + unsigned int i; + u32 _rand; + u32 chan_idx; + int num_available_chandefs = 0; + enum chan_width new_chan_width; + int new_center_freq; + int current_start_freq; + int temp_width; + struct hostapd_hw_modes *mode = iface->current_mode; + int *interference_freqs = NULL; + int primary_chan_bit = -1; + int segment_freq; + int start_freq = (cf1 - channel_width_to_int(chan_width) / 2) + 10; + u8 channel_no; + unsigned int num_segments; + int new_bw; + + wpa_printf(MSG_DEBUG, + "Intf: input freq=%d, chan_width=%d, cf1=%d, cf2=%d, chan_bw_interference_bitmap=0x%x", + freq, chan_width, cf1, cf2, chan_bw_interference_bitmap); + + num_segments = channel_width_to_int(chan_width) / 20; + if (!num_segments) { + wpa_printf(MSG_INFO, "Intf: Invalid channel width %d", + chan_width); + return -1; + } + + /* Determine which 20 MHz segment contains the primary channel */ + for (i = 0; i < num_segments; i++) { + segment_freq = start_freq + i * 20; + if (segment_freq == freq) { + primary_chan_bit = i; + break; + } + } + + /* Check whether interference has occurred in primary 20 MHz channel */ + if (primary_chan_bit >= 0 && + (chan_bw_interference_bitmap & BIT(primary_chan_bit))) { + available_chandef_list = os_calloc( + mode->num_channels, + sizeof(struct hostapd_channel_data *)); + if (!available_chandef_list) { + wpa_printf(MSG_ERROR, + "Intf: Failed to allocate available_chandef_list"); + return -1; + } + + /* Store frequencies with interference in interference_freqs */ + current_start_freq = + (cf1 - channel_width_to_int(chan_width) / 2) + 10; + for (i = 0; i < num_segments; i++) { + if (chan_bw_interference_bitmap & BIT(i)) { + wpa_printf(MSG_DEBUG, + "Intf: Found incumbent signal interference in frequency %d", + current_start_freq + 20 * i); + int_array_add_unique(&interference_freqs, + current_start_freq + + 20 * i); + if (!interference_freqs) { + wpa_printf(MSG_ERROR, + "Intf: Failed to store interference frequencies"); + ret = -1; + goto exit; + } + } + } + + /* Find a random channel to be switched */ + temp_width = chan_width; + + while (temp_width > CHAN_WIDTH_20_NOHT) { + num_available_chandefs = + intf_find_channel_list(iface, temp_width, + available_chandef_list, + interference_freqs); + if (num_available_chandefs > 0) + break; + wpa_printf(MSG_DEBUG, "Intf: Downgrading bandwidth"); + temp_width = downgrade_bandwidth(temp_width); + } + + if (num_available_chandefs == 0) { + wpa_printf(MSG_INFO, "Intf: No available_chandefs"); + goto exit; + } + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + + chan_idx = _rand % num_available_chandefs; + chan_data = available_chandef_list[chan_idx]; + new_chan_width = temp_width; + + wpa_printf(MSG_DEBUG, "Intf: Got random channel %d (%d)", + chan_data->freq, chan_data->chan); + } else { + /* + * Interference is not present in the primary 20 MHz, so + * reduce bandwidth. + */ + u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(iface->conf); + u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(iface->conf); + enum oper_chan_width oper_chwidth; + int j; + + for (j = 0; j < mode->num_channels; j++) { + chan_temp = &mode->channels[j]; + if (chan_temp->freq == freq) + chan_data = chan_temp; + } + if (!chan_data) { + wpa_printf(MSG_INFO, "Intf: No channel found"); + goto exit; + } + + oper_chwidth = chan_width_to_oper_chwidth(chan_width); + + if (chan_width > CHAN_WIDTH_40) { + punct_update_legacy_bw(chan_bw_interference_bitmap, + iface->conf->channel, + &oper_chwidth, &seg0, &seg1); + if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) + new_chan_width = CHAN_WIDTH_160; + else if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ) + new_chan_width = CHAN_WIDTH_80; + else if (seg0 == 0) + new_chan_width = CHAN_WIDTH_20; + else + new_chan_width = CHAN_WIDTH_40; + } else { + new_chan_width = CHAN_WIDTH_20; + } + } + + if (new_chan_width > CHAN_WIDTH_20) { + ret = get_center_freq_6g(chan_data->chan, new_chan_width, + &new_center_freq); + if (ret) { + wpa_printf(MSG_ERROR, + "Intf: Couldn't find center freq for chan : %d chan_width : %d", + chan_data->chan, new_chan_width); + goto exit; + } + } else { + new_center_freq = chan_data->freq; + } + + ieee80211_freq_to_chan(chan_data->freq, &channel_no); + + os_memset(&settings, 0, sizeof(settings)); + settings.cs_count = 5; + settings.freq_params.freq = chan_data->freq; + + switch (new_chan_width) { + case CHAN_WIDTH_40: + new_bw = 40; + break; + case CHAN_WIDTH_80P80: + case CHAN_WIDTH_80: + new_bw = 80; + break; + case CHAN_WIDTH_160: + new_bw = 160; + break; + case CHAN_WIDTH_320: + new_bw = 320; + break; + default: + new_bw = 20; + break; + } + + settings.freq_params.bandwidth = new_bw; + settings.freq_params.channel = channel_no; + settings.freq_params.center_freq1 = new_center_freq; + settings.freq_params.ht_enabled = iface->conf->ieee80211n; + settings.freq_params.vht_enabled = iface->conf->ieee80211ac; + settings.freq_params.he_enabled = iface->conf->ieee80211ax; + settings.freq_params.eht_enabled = iface->conf->ieee80211be; + wpa_printf(MSG_DEBUG, + "Intf: channel=%u, freq=%d, bw=%d, center_freq1=%d", + settings.freq_params.channel, + settings.freq_params.freq, + settings.freq_params.bandwidth, + settings.freq_params.center_freq1); + + if (chan_data->freq == iface->freq) { + if (hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings.freq_params, NULL)) { + wpa_printf(MSG_INFO, + "Intf: Failed to update bandwidth"); + ret = -1; + goto exit; + } + if (hostapd_set_freq( + iface->bss[0], iface->conf->hw_mode, iface->freq, + iface->conf->channel, iface->conf->enable_edmg, + iface->conf->edmg_channel, iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->ieee80211ax, + iface->conf->ieee80211be, + iface->conf->secondary_channel, + hostapd_get_oper_chwidth(iface->conf), + hostapd_get_oper_centr_freq_seg0_idx(iface->conf), + hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) { + wpa_printf(MSG_ERROR, + "Intf: Failed to apply bandwidth update"); + ret = -1; + goto exit; + } + ieee802_11_set_beacons(iface); + goto exit; + } + + /* Channel and bandwidth have been decided, triggering channel switch */ + for (i = 0; i < iface->num_bss; i++) { + /* Save CHAN_SWITCH VHT and HE config */ + hostapd_chan_switch_config(iface->bss[i], + &settings.freq_params); + + ret = hostapd_switch_channel(iface->bss[i], &settings); + if (ret) { + wpa_printf(MSG_ERROR, + "Intf: Channel switch failed"); + break; + } + } + +exit: + os_free(available_chandef_list); + os_free(interference_freqs); + return ret; +} diff --git a/contrib/wpa/src/ap/interference.h b/contrib/wpa/src/ap/interference.h new file mode 100644 index 000000000000..fb13abab1573 --- /dev/null +++ b/contrib/wpa/src/ap/interference.h @@ -0,0 +1,18 @@ +/* + * Management of interference due to incumbent signals in 6 GHz band + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef INTERFERENCE_H +#define INTERFERENCE_H + +struct hostapd_iface; + +int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq, + int chan_width, + int cf1, int cf2, + u32 chan_bw_interference_bitmap); + +#endif /* INTERFERENCE_H */ diff --git a/contrib/wpa/src/ap/nan_usd_ap.c b/contrib/wpa/src/ap/nan_usd_ap.c index 52a967a4ec41..076415f31979 100644 --- a/contrib/wpa/src/ap/nan_usd_ap.c +++ b/contrib/wpa/src/ap/nan_usd_ap.c @@ -1,6 +1,6 @@ /* * NAN unsynchronized service discovery (USD) - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,38 +29,38 @@ static int hostapd_nan_de_tx(void *ctx, unsigned int freq, wpabuf_len(buf)); /* TODO: Force use of OFDM */ - return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, - wpabuf_head(buf), wpabuf_len(buf)); + return hostapd_drv_send_action_forced_addr3(hapd, hapd->iface->freq, 0, + dst, bssid, + wpabuf_head(buf), + wpabuf_len(buf)); } static int hostapd_nan_de_listen(void *ctx, unsigned int freq, - unsigned int duration) + unsigned int duration, const u8 *forced_addr) { return 0; } static void -hostapd_nan_de_discovery_result(void *ctx, int subscribe_id, - enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len, - int peer_publish_id, const u8 *peer_addr, - bool fsd, bool fsd_gas) +hostapd_nan_de_discovery_result(void *ctx, struct nan_discovery_result *res) { struct hostapd_data *hapd = ctx; char *ssi_hex; - ssi_hex = os_zalloc(2 * ssi_len + 1); + ssi_hex = os_zalloc(2 * res->ssi_len + 1); if (!ssi_hex) return; - if (ssi) - wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + if (res->ssi) + wpa_snprintf_hex(ssi_hex, 2 * res->ssi_len + 1, + res->ssi, res->ssi_len); wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT "subscribe_id=%d publish_id=%d address=" MACSTR " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s", - subscribe_id, peer_publish_id, MAC2STR(peer_addr), - fsd, fsd_gas, srv_proto_type, ssi_hex); + res->subscribe_id, res->peer_publish_id, + MAC2STR(res->peer_addr), res->fsd, res->fsd_gas, + res->srv_proto_type, ssi_hex); os_free(ssi_hex); } @@ -127,7 +127,8 @@ static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id, static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id, const u8 *ssi, size_t ssi_len, - const u8 *peer_addr) + const u8 *peer_addr, + const u8 *buf, size_t len) { struct hostapd_data *hapd = ctx; char *ssi_hex; @@ -148,6 +149,10 @@ int hostapd_nan_usd_init(struct hostapd_data *hapd) { struct nan_callbacks cb; + /* If nan_de is already initialized, do not create a new instance. */ + if (hapd->nan_de) + return 0; + os_memset(&cb, 0, sizeof(cb)); cb.ctx = hapd; cb.tx = hostapd_nan_de_tx; @@ -158,26 +163,33 @@ int hostapd_nan_usd_init(struct hostapd_data *hapd) cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated; cb.receive = hostapd_nan_de_receive; - hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb); + hapd->nan_de = nan_de_init(hapd->own_addr, false, true, 0, &cb); if (!hapd->nan_de) return -1; + + hapd->nan_de_is_owned = true; + return 0; } void hostapd_nan_usd_deinit(struct hostapd_data *hapd) { - nan_de_deinit(hapd->nan_de); + if (hapd->nan_de_is_owned) { + nan_de_deinit(hapd->nan_de); + hapd->nan_de_is_owned = false; + } hapd->nan_de = NULL; } void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src, - unsigned int freq, const u8 *buf, size_t len) + const u8 *a3, unsigned int freq, + const u8 *buf, size_t len) { if (!hapd->nan_de) return; - nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len); + nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len, 0); } @@ -192,7 +204,7 @@ void hostapd_nan_usd_flush(struct hostapd_data *hapd) int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, - struct nan_publish_params *params) + struct nan_publish_params *params, bool p2p) { int publish_id; struct wpabuf *elems = NULL; @@ -201,7 +213,7 @@ int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name, return -1; publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type, - ssi, elems, params); + ssi, elems, params, p2p, NULL); wpabuf_free(elems); return publish_id; } @@ -231,7 +243,7 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, - struct nan_subscribe_params *params) + struct nan_subscribe_params *params, bool p2p) { int subscribe_id; struct wpabuf *elems = NULL; @@ -240,7 +252,8 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd, return -1; subscribe_id = nan_de_subscribe(hapd->nan_de, service_name, - srv_proto_type, ssi, elems, params); + srv_proto_type, ssi, elems, params, p2p, + NULL); wpabuf_free(elems); return subscribe_id; } @@ -258,10 +271,11 @@ void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd, int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle, const struct wpabuf *ssi, const struct wpabuf *elems, - const u8 *peer_addr, u8 req_instance_id) + const u8 *peer_addr, + u8 req_instance_id) { if (!hapd->nan_de) return -1; return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr, - req_instance_id); + req_instance_id, NULL, NULL); } diff --git a/contrib/wpa/src/ap/nan_usd_ap.h b/contrib/wpa/src/ap/nan_usd_ap.h index 58ff5fc4808b..69400dfba05c 100644 --- a/contrib/wpa/src/ap/nan_usd_ap.h +++ b/contrib/wpa/src/ap/nan_usd_ap.h @@ -1,6 +1,6 @@ /* * NAN unsynchronized service discovery (USD) - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -16,12 +16,13 @@ enum nan_service_protocol_type; int hostapd_nan_usd_init(struct hostapd_data *hapd); void hostapd_nan_usd_deinit(struct hostapd_data *hapd); void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src, + const u8 *a3, unsigned int freq, const u8 *buf, size_t len); void hostapd_nan_usd_flush(struct hostapd_data *hapd); int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, - struct nan_publish_params *params); + struct nan_publish_params *params, bool p2p); void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id); int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id, const struct wpabuf *ssi); @@ -29,7 +30,7 @@ int hostapd_nan_usd_subscribe(struct hostapd_data *hapd, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, - struct nan_subscribe_params *params); + struct nan_subscribe_params *params, bool p2p); void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd, int subscribe_id); int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle, diff --git a/contrib/wpa/src/ap/neighbor_db.c b/contrib/wpa/src/ap/neighbor_db.c index f7a7d83d427d..fd4f881df029 100644 --- a/contrib/wpa/src/ap/neighbor_db.c +++ b/contrib/wpa/src/ap/neighbor_db.c @@ -203,16 +203,15 @@ void hostapd_free_neighbor_db(struct hostapd_data *hapd) #ifdef NEED_AP_MLME -static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, - int ht, int vht, int he) +static enum nr_chan_width +hostapd_get_nr_chan_width(struct hostapd_data *hapd, + int ht, int vht, int he, + enum oper_chan_width oper_chwidth, + int secondary_channel) { - enum oper_chan_width oper_chwidth; - - oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf); - if (!ht && !vht && !he) return NR_CHAN_WIDTH_20; - if (!hapd->iconf->secondary_channel) + if (!secondary_channel) return NR_CHAN_WIDTH_20; if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT) return NR_CHAN_WIDTH_40; @@ -231,14 +230,16 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) { #ifdef NEED_AP_MLME u16 capab = hostapd_own_capab_info(hapd); - int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; - int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; - int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax; - bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be; + int ht = hostapd_is_ht_enabled(hapd); + int vht = hostapd_is_vht_enabled(hapd); + int he = hostapd_is_he_enabled(hapd); + bool eht = he && hostapd_is_eht_enabled(hapd); struct wpa_ssid_value ssid; u8 channel, op_class; u8 center_freq1_idx = 0, center_freq2_idx = 0; + enum oper_chan_width oper_chwidth; enum nr_chan_width width; + int secondary_channel; u32 bssid_info; struct wpabuf *nr; @@ -264,10 +265,7 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) } if (ht) { - bssid_info |= NEI_REP_BSSID_INFO_HT | - NEI_REP_BSSID_INFO_DELAYED_BA; - - /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ + bssid_info |= NEI_REP_BSSID_INFO_HT; if (vht) bssid_info |= NEI_REP_BSSID_INFO_VHT; } @@ -278,23 +276,30 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) bssid_info |= NEI_REP_BSSID_INFO_EHT; /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + hostapd_get_oper_chan_info_of_bss(hapd, &oper_chwidth, + ¢er_freq1_idx, ¢er_freq2_idx); + secondary_channel = hapd->iconf->secondary_channel; + + if (center_freq1_idx == hapd->iconf->channel && + oper_chwidth == CONF_OPER_CHWIDTH_USE_HT) + secondary_channel = 0; + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hostapd_get_oper_chwidth(hapd->iconf), + secondary_channel, + oper_chwidth, &op_class, &channel) == NUM_HOSTAPD_MODES) return; - width = hostapd_get_nr_chan_width(hapd, ht, vht, he); - if (vht) { - center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx( - hapd->iconf); - if (width == NR_CHAN_WIDTH_80P80) - center_freq2_idx = - hostapd_get_oper_centr_freq_seg1_idx( - hapd->iconf); - } else if (ht) { + width = hostapd_get_nr_chan_width(hapd, ht, vht, he, oper_chwidth, + secondary_channel); + + if (width != NR_CHAN_WIDTH_80P80) + center_freq2_idx = 0; + + if (!vht && ht) { + center_freq1_idx = 0; ieee80211_freq_to_chan(hapd->iface->freq + - 10 * hapd->iconf->secondary_channel, + 10 * secondary_channel, ¢er_freq1_idx); } @@ -313,12 +318,13 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) wpabuf_put_le32(nr, bssid_info); wpabuf_put_u8(nr, op_class); wpabuf_put_u8(nr, channel); - wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht, + he)); /* * Wide Bandwidth Channel subelement may be needed to allow the - * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 - * Figure 9-301. + * receiving STA to send packets to the AP. See IEEE Std 802.11-2024, + * Figure 9-423 (Wide Bandwidth Channel subelement format). */ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); wpabuf_put_u8(nr, 3); diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.c b/contrib/wpa/src/ap/pmksa_cache_auth.c index 2fce8383d975..8769032aaa93 100644 --- a/contrib/wpa/src/ap/pmksa_cache_auth.c +++ b/contrib/wpa/src/ap/pmksa_cache_auth.c @@ -141,8 +141,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) } -static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, - struct eapol_state_machine *eapol) +void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) { struct vlan_description *vlan_desc; @@ -470,6 +470,17 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) } +/** + * pmksa_cache_auth_set_ctx - Set the context for PMKSA cache + * @cache: Pointer to the PMKSA cache structure + * @ctx: Context pointer to be stored in the cache + */ +void pmksa_cache_auth_set_ctx(struct rsn_pmksa_cache *cache, void *ctx) +{ + cache->ctx = ctx; +} + + /** * pmksa_cache_auth_get - Fetch a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() @@ -644,29 +655,25 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @buf: Buffer for the list * @len: Length of the buffer + * @index: Externally stored index counter * Returns: Number of bytes written to buffer * * This function is used to generate a text format representation of the * current PMKSA cache contents for the ctrl_iface PMKSA command. */ -int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len, + int *index) { - int i, ret; + int ret; char *pos = buf; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; os_get_reltime(&now); - ret = os_snprintf(pos, buf + len - pos, - "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); - if (os_snprintf_error(buf + len - pos, ret)) - return pos - buf; - pos += ret; - i = 0; entry = pmksa->pmksa; while (entry) { ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", - i, MAC2STR(entry->spa)); + *index, MAC2STR(entry->spa)); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; @@ -679,6 +686,7 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) return pos - buf; pos += ret; entry = entry->next; + (*index)++; } return pos - buf; } diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.h b/contrib/wpa/src/ap/pmksa_cache_auth.h index e38e7eca66e7..f7b7450faa3d 100644 --- a/contrib/wpa/src/ap/pmksa_cache_auth.h +++ b/contrib/wpa/src/ap/pmksa_cache_auth.h @@ -33,9 +33,17 @@ struct rsn_pmksa_cache_entry { struct radius_class_data radius_class; u8 eap_type_authsrv; struct vlan_description *vlan_desc; + int sae_vlan_id; int opportunistic; u64 acct_multi_session_id; +#ifdef CONFIG_IEEE80211BE + /** + * This field is used to identify whether the entry is from ml_pmksa or + * pmksa. + */ + bool is_ml; +#endif /* CONFIG_IEEE80211BE */ }; struct rsn_pmksa_cache; @@ -45,6 +53,7 @@ struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx), void *ctx); void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa); +void pmksa_cache_auth_set_ctx(struct rsn_pmksa_cache *cache, void *ctx); struct rsn_pmksa_cache_entry * pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *pmkid); @@ -75,9 +84,12 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); -int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len, + int *index); void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, char *buf, size_t len); +void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol); #endif /* PMKSA_CACHE_H */ diff --git a/contrib/wpa/src/ap/rrm.c b/contrib/wpa/src/ap/rrm.c index fbcddf3f98c9..73771e4c5e11 100644 --- a/contrib/wpa/src/ap/rrm.c +++ b/contrib/wpa/src/ap/rrm.c @@ -536,13 +536,14 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, if (!hapd->range_req_token) /* For wraparounds */ hapd->range_req_token++; - /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ + /* IEEE Std 802.11-2024, 9.6.6.2 (Radio Measurement Request frame + * format) */ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ wpabuf_put_le16(buf, 0); /* Number of Repetitions */ - /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ + /* IEEE Std 802.11-2024, 9.4.2.19 (Measurement Request element) */ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); len = wpabuf_put(buf, 1); /* Length will be set later */ @@ -554,7 +555,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ - /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ + /* IEEE Std 802.11-2024, 9.4.2.19.19 (FTM Range request) */ wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c index 13613dbab0d6..a88f275afb0f 100644 --- a/contrib/wpa/src/ap/sta_info.c +++ b/contrib/wpa/src/ap/sta_info.c @@ -77,6 +77,24 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) } +#ifdef CONFIG_IEEE80211BE +struct sta_info * ap_get_link_sta(struct hostapd_data *hapd, + const u8 *link_addr) +{ + struct sta_info *link_sta; + + for (link_sta = hapd->sta_list; link_sta; link_sta = link_sta->next) { + if (link_sta->mld_info.mld_sta && + ether_addr_equal(link_sta->mld_info.links[hapd->mld_link_id].peer_addr, + link_addr)) + return link_sta; + } + + return NULL; +} +#endif /* CONFIG_IEEE80211BE */ + + #ifdef CONFIG_P2P struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) { @@ -191,9 +209,10 @@ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta) static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { #ifdef CONFIG_IEEE80211BE - if (hostapd_sta_is_link_sta(hapd, sta) && - !hostapd_drv_link_sta_remove(hapd, sta->addr)) + if (hostapd_sta_is_link_sta(hapd, sta)) { + hostapd_drv_link_sta_remove(hapd, sta->addr); return; + } #endif /* CONFIG_IEEE80211BE */ hostapd_drv_sta_remove(hapd, sta->addr); @@ -201,8 +220,9 @@ static void __ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_IEEE80211BE -static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, - struct sta_info *psta) + +void set_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, + struct sta_info *psta, void *wpa_sm) { struct sta_info *lsta; struct hostapd_data *lhapd; @@ -215,15 +235,54 @@ static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, continue; lsta = ap_get_sta(lhapd, psta->addr); - if (lsta) - lsta->wpa_sm = NULL; + if (lsta) { + if (lsta->wpa_sm && lsta->wpa_sm != wpa_sm) + wpa_printf(MSG_DEBUG, + "AP MLD: Replacing lsta->wpa_sm %p with %p for a partner link", + lsta->wpa_sm, wpa_sm); + lsta->wpa_sm = wpa_sm; + } } } + + +void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, + struct sta_info *psta) +{ + set_wpa_sm_for_each_partner_link(hapd, psta, NULL); +} + + +void clear_wpa_sm_for_all_sta(struct hostapd_data *hapd, + struct wpa_state_machine *wpa_sm) +{ + struct hostapd_data *lhapd; + + if (!hapd->mld) + return; + + for_each_mld_link(lhapd, hapd) { + struct sta_info *sta; + + for (sta = lhapd->sta_list; sta; sta = sta->next) { + if (sta->wpa_sm == wpa_sm) { + wpa_printf(MSG_DEBUG, + "AP MLD: Clearing to be freed wpa_sm=%p pointer from sta=%p", + sta->wpa_sm, sta); + sta->wpa_sm = NULL; + } + } + } +} + #endif /* CONFIG_IEEE80211BE */ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { +#ifndef CONFIG_NO_VLAN + struct hostapd_data *vlan_bss = hapd; +#endif /* CONFIG_NO_VLAN */ int set_beacon = 0; accounting_sta_stop(hapd, sta); @@ -334,22 +393,27 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); + ap_sta_clear_assoc_timeout(hapd, sta); sae_clear_retransmit_timer(hapd, sta); ieee802_1x_free_station(hapd, sta); #ifdef CONFIG_IEEE80211BE - if (!ap_sta_is_mld(hapd, sta) || - hapd->mld_link_id == sta->mld_assoc_link_id) { - wpa_auth_sta_deinit(sta->wpa_sm); - /* Remove references from partner links. */ - clear_wpa_sm_for_each_partner_link(hapd, sta); - } - /* Release group references in case non-association link STA is removed * before association link STA */ if (hostapd_sta_is_link_sta(hapd, sta)) - wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id); + wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id, + false); + if (!ap_sta_is_mld(hapd, sta) || + hapd->mld_link_id == sta->mld_assoc_link_id) { + struct wpa_state_machine *sm = sta->wpa_sm; + + /* Remove references from partner links. */ + clear_wpa_sm_for_each_partner_link(hapd, sta); + + clear_wpa_sm_for_all_sta(hapd, sm); + wpa_auth_sta_deinit(sm); + } #else /* CONFIG_IEEE80211BE */ wpa_auth_sta_deinit(sta->wpa_sm); #endif /* CONFIG_IEEE80211BE */ @@ -361,13 +425,20 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_NO_RADIUS */ #ifndef CONFIG_NO_VLAN +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (!vlan_bss) + vlan_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ /* * sta->wpa_sm->group needs to be released before so that * vlan_remove_dynamic() can check that no stations are left on the * AP_VLAN netdev. */ if (sta->vlan_id) - vlan_remove_dynamic(hapd, sta->vlan_id); + vlan_remove_dynamic(vlan_bss, sta->vlan_id); if (sta->vlan_id_bound) { /* * Need to remove the STA entry before potentially removing the @@ -378,7 +449,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } - vlan_remove_dynamic(hapd, sta->vlan_id_bound); + vlan_remove_dynamic(vlan_bss, sta->vlan_id_bound); } #endif /* CONFIG_NO_VLAN */ @@ -391,7 +462,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) if (sta->gas_dialog) { int i; @@ -399,7 +470,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) gas_serv_dialog_clear(&sta->gas_dialog[i]); os_free(sta->gas_dialog); } -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); @@ -418,7 +489,6 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); - os_free(sta->remediation_url); os_free(sta->t_c_url); wpabuf_free(sta->hs20_deauth_req); os_free(sta->hs20_session_info_url); @@ -467,6 +537,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_IEEE80211BE ap_sta_free_sta_profile(&sta->mld_info); + ml_deinit_link_reconf_req(&sta->reconf_req); #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_TESTING_OPTIONS @@ -474,6 +545,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) forced_memzero(sta->last_tk, WPA_TK_MAX_LEN); #endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_free(sta->sae_pw_id); + +#ifdef CONFIG_IEEE8021X_AUTH + crypto_ecdh_deinit(sta->eap_auth_data.ecdh); + wpabuf_clear_free(sta->eap_auth_data.dhss); + os_free(sta->eap_auth_data.rsnxe); +#endif /* CONFIG_IEEE8021X_AUTH */ + os_free(sta); } @@ -580,6 +659,17 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) sta->timeout_next = STA_DISASSOC; goto skip_poll; } else if (inactive_sec < max_inactivity) { +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->skip_inactivity_poll == -1) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Force inactivity timeout for station " + MACSTR + " even though it has been active %is ago", + MAC2STR(sta->addr), inactive_sec); + sta->timeout_next = STA_DISASSOC; + goto skip_poll; + } +#endif /* CONFIG_TESTING_OPTIONS */ /* station activity detected; reset timeout state */ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has been active %is ago", @@ -791,6 +881,25 @@ void ap_sta_session_warning_timeout(struct hostapd_data *hapd, } +static void ap_sta_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->flags & WLAN_STA_ASSOC) + return; + + wpa_printf(MSG_DEBUG, "STA " MACSTR + " did not complete association in time - remove it", + MAC2STR(sta->addr)); + if (sta->flags & WLAN_STA_AUTH) + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + else + ap_free_sta(hapd, sta); +} + + struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; @@ -821,11 +930,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) } for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { - if (!hapd->iface->basic_rates) + if (!hapd->basic_rates) break; - if (hapd->iface->basic_rates[i] < 0) + if (hapd->basic_rates[i] <= 0) break; - sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5; + sta->supported_rates[i] = hapd->basic_rates[i] / 5; } sta->supported_rates_len = i; @@ -856,6 +965,9 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) &sta->probe_ie_taxonomy); #endif /* CONFIG_TAXONOMY */ + if (!(hapd->conf->mesh & MESH_ENABLED)) + eloop_register_timeout(60, 0, ap_sta_assoc_timeout, hapd, sta); + return sta; } @@ -924,7 +1036,8 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) static void ap_sta_disconnect_common(struct hostapd_data *hapd, - struct sta_info *sta, unsigned int timeout) + struct sta_info *sta, unsigned int timeout, + bool free_1x) { sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; @@ -933,17 +1046,21 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "reschedule ap_handle_timer timeout (%u sec) for " MACSTR, - MAC2STR(sta->addr), timeout); + timeout, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(hapd, sta); + if (free_1x) + ieee802_1x_free_station(hapd, sta); #ifdef CONFIG_IEEE80211BE - if (!hapd->conf->mld_ap || + if (!ap_sta_is_mld(hapd, sta) || hapd->mld_link_id == sta->mld_assoc_link_id) { - wpa_auth_sta_deinit(sta->wpa_sm); + struct wpa_state_machine *sm = sta->wpa_sm; + clear_wpa_sm_for_each_partner_link(hapd, sta); + clear_wpa_sm_for_all_sta(hapd, sm); + wpa_auth_sta_deinit(sm); } #else /* CONFIG_IEEE80211BE */ wpa_auth_sta_deinit(sta->wpa_sm); @@ -953,6 +1070,18 @@ static void ap_sta_disconnect_common(struct hostapd_data *hapd, } +static void ap_sta_disassociate_common(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason) +{ + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_disassoc_cb_timeout, hapd, sta); +} + + static void ap_sta_handle_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { @@ -970,14 +1099,9 @@ static void ap_sta_handle_disassociate(struct hostapd_data *hapd, sta->timeout_next = STA_DEAUTH; } - ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC); - - sta->disassoc_reason = reason; - sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; - eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); - eloop_register_timeout(hapd->iface->drv_flags & - WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, - ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC, + true); + ap_sta_disassociate_common(hapd, sta, reason); } @@ -993,25 +1117,9 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) } -static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd, +static void ap_sta_deauthenticate_common(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { - if (hapd->iface->current_mode && - hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { - /* Deauthentication is not used in DMG/IEEE 802.11ad; - * disassociate the STA instead. */ - ap_sta_disassociate(hapd, sta, reason); - return; - } - - wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, - hapd->conf->iface, MAC2STR(sta->addr)); - - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); - - sta->timeout_next = STA_REMOVE; - ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH); - sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -1021,9 +1129,47 @@ static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd, } +static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + + sta->timeout_next = STA_REMOVE; + ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH, + true); + ap_sta_deauthenticate_common(hapd, sta, reason); +} + + +static void ap_sta_handle_disconnect(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: disconnect STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + sta->timeout_next = STA_REMOVE; + + ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH, + false); + ap_sta_deauthenticate_common(hapd, sta, reason); +} + + +enum ap_sta_disconnect_op { + AP_STA_DEAUTHENTICATE, + AP_STA_DISASSOCIATE, + AP_STA_DISCONNECT +}; + static bool ap_sta_ml_disconnect(struct hostapd_data *hapd, struct sta_info *sta, u16 reason, - bool disassoc) + enum ap_sta_disconnect_op op) { #ifdef CONFIG_IEEE80211BE struct hostapd_data *assoc_hapd, *tmp_hapd; @@ -1031,7 +1177,7 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd, unsigned int i, link_id; struct hapd_interfaces *interfaces; - if (!hostapd_is_mld_ap(hapd)) + if (!hostapd_is_multiple_link_mld(hapd)) return false; /* @@ -1072,25 +1218,30 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd, tmp_sta->aid != assoc_sta->aid) continue; - if (disassoc) + if (op == AP_STA_DISASSOCIATE) ap_sta_handle_disassociate(tmp_hapd, tmp_sta, reason); - else + else if (op == AP_STA_DEAUTHENTICATE) ap_sta_handle_deauthenticate(tmp_hapd, tmp_sta, reason); - + else + ap_sta_handle_disconnect(tmp_hapd, + tmp_sta, + reason); break; } } } /* Disconnect the station on which the association was performed. */ - if (disassoc) + if (op == AP_STA_DISASSOCIATE) ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason); - else + else if (op == AP_STA_DEAUTHENTICATE) ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason); + else + ap_sta_handle_disconnect(assoc_hapd, assoc_sta, reason); return true; #else /* CONFIG_IEEE80211BE */ @@ -1102,7 +1253,7 @@ static bool ap_sta_ml_disconnect(struct hostapd_data *hapd, void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { - if (ap_sta_ml_disconnect(hapd, sta, reason, true)) + if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISASSOCIATE)) return; ap_sta_handle_disassociate(hapd, sta, reason); @@ -1112,7 +1263,15 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { - if (ap_sta_ml_disconnect(hapd, sta, reason, false)) + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + ap_sta_disassociate(hapd, sta, reason); + return; + } + + if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DEAUTHENTICATE)) return; ap_sta_handle_deauthenticate(hapd, sta, reason); @@ -1139,10 +1298,19 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) { struct hostapd_vlan *vlan; + struct hostapd_data *vlan_bss = hapd; int vlan_id = MAX_VLAN_ID + 2; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (!vlan_bss) + vlan_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ + retry: - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == vlan_id) { vlan_id++; goto retry; @@ -1152,12 +1320,22 @@ retry: } -int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, - struct vlan_description *vlan_desc) +static int ap_sta_set_vlan_helper(struct hostapd_data *hapd, + struct sta_info *sta, + struct vlan_description *vlan_desc) { struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; + struct hostapd_data *vlan_bss = hapd; int old_vlan_id, vlan_id = 0, ret = 0; +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (!vlan_bss) + vlan_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ + /* Check if there is something to do */ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { /* This sta is lacking its own vif */ @@ -1174,7 +1352,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, /* find a free vlan_id sufficiently big */ vlan_id = ap_sta_get_free_vlan_id(hapd); /* Get wildcard VLAN */ - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == VLAN_ID_WILDCARD) break; } @@ -1188,7 +1366,7 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, goto done; } } else if (vlan_desc && vlan_desc->notempty) { - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) break; if (vlan->vlan_id == VLAN_ID_WILDCARD) @@ -1201,10 +1379,10 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, vlan_id = vlan_desc->untagged; if (vlan_desc->tagged[0]) { /* Tagged VLAN configuration */ - vlan_id = ap_sta_get_free_vlan_id(hapd); + vlan_id = ap_sta_get_free_vlan_id(vlan_bss); } } else { - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "missing vlan and wildcard for vlan=%d%s", @@ -1217,9 +1395,9 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, } if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { - vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); + vlan = vlan_add_dynamic(vlan_bss, vlan, vlan_id, vlan_desc); if (vlan == NULL) { - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not add dynamic VLAN interface for vlan=%d%s", @@ -1231,13 +1409,13 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, goto done; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN interface '%s'", vlan->ifname); } else if (vlan && vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "updated existing dynamic VLAN interface '%s'", @@ -1249,24 +1427,63 @@ done: sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; if (vlan_id != old_vlan_id && old_vlan_id) - vlan_remove_dynamic(hapd, old_vlan_id); + vlan_remove_dynamic(vlan_bss, old_vlan_id); return ret; } -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc) +{ + int ret; +#ifdef CONFIG_IEEE80211BE + size_t i; + struct hapd_interfaces *interfaces = hapd->iface->interfaces; +#endif /* CONFIG_IEEE80211BE */ + + ret = ap_sta_set_vlan_helper(hapd, sta, vlan_desc); + if (ret) + return ret; +#ifdef CONFIG_IEEE80211BE + for (i = 0; interfaces && i < interfaces->count; i++) { + struct sta_info *tmp_sta; + struct hostapd_data *tmp_hapd = interfaces->iface[i]->bss[0]; + + if (!tmp_hapd->conf->mld_ap || + hapd == tmp_hapd || + !hostapd_is_ml_partner(hapd, tmp_hapd)) + continue; + + tmp_sta = ap_get_sta(tmp_hapd, sta->addr); + if (tmp_sta) + ap_sta_set_vlan_helper(tmp_hapd, tmp_sta, vlan_desc); + } + +#endif /* CONFIG_IEEE80211BE */ + + return 0; +} + + +static int ap_sta_bind_vlan_helper(struct hostapd_data *hapd, + struct sta_info *sta) { #ifndef CONFIG_NO_VLAN const char *iface; struct hostapd_vlan *vlan = NULL; + struct hostapd_data *vlan_bss = hapd; int ret; int old_vlanid = sta->vlan_id_bound; int mld_link_id = -1; #ifdef CONFIG_IEEE80211BE - if (hapd->conf->mld_ap) + if (hapd->conf->mld_ap) { mld_link_id = hapd->mld_link_id; + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (!vlan_bss) + vlan_bss = hapd; + } #endif /* CONFIG_IEEE80211BE */ if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) { @@ -1281,7 +1498,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) iface = hapd->conf->ssid.vlan; if (sta->vlan_id > 0) { - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == sta->vlan_id) break; } @@ -1299,7 +1516,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) if (sta->vlan_id > 0 && !vlan && !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " "binding station to (vlan_id=%d)", sta->vlan_id); @@ -1307,7 +1524,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) goto done; } else if (vlan && vlan->dynamic_vlan > 0) { vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "updated existing dynamic VLAN interface '%s'", @@ -1318,24 +1535,25 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) sta->vlan_id_bound = sta->vlan_id; skip_counting: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "binding station to interface " "'%s'", iface); - if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) + if (wpa_auth_sta_set_vlan(sta->wpa_sm, hapd->wpa_auth, + sta->vlan_id) < 0) wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id, mld_link_id); if (ret < 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + hostapd_logger(vlan_bss, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " "entry to vlan_id=%d", sta->vlan_id); } /* During 1x reauth, if the vlan id changes, then remove the old id. */ if (old_vlanid > 0 && old_vlanid != sta->vlan_id) - vlan_remove_dynamic(hapd, old_vlanid); + vlan_remove_dynamic(vlan_bss, old_vlanid); done: return ret; @@ -1345,6 +1563,60 @@ done: } +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) +{ + int ret; +#ifdef CONFIG_IEEE80211BE + size_t i; + struct hapd_interfaces *interfaces = hapd->iface->interfaces; +#endif /* CONFIG_IEEE80211BE */ + + ret = ap_sta_bind_vlan_helper(hapd, sta); + if (ret) + return ret; +#ifdef CONFIG_IEEE80211BE + for (i = 0; interfaces && i < interfaces->count; i++) { + struct sta_info *tmp_sta; + struct hostapd_data *tmp_hapd = interfaces->iface[i]->bss[0]; + + if (!tmp_hapd->conf->mld_ap || + hapd == tmp_hapd || + !hostapd_is_ml_partner(hapd, tmp_hapd)) + continue; + + tmp_sta = ap_get_sta(tmp_hapd, sta->addr); + if (tmp_sta) + ap_sta_bind_vlan_helper(tmp_hapd, tmp_sta); + } +#endif /* CONFIG_IEEE80211BE */ + + return 0; +} + + +void ap_sta_set_sa_query_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int value) +{ + sta->sa_query_timed_out = value; +#ifdef CONFIG_IEEE80211BE + if (ap_sta_is_mld(hapd, sta)) { + struct hostapd_data *lhapd; + + for_each_mld_link(lhapd, hapd) { + struct sta_info *lsta; + + if (lhapd == hapd) + continue; + + lsta = ap_get_sta(lhapd, sta->addr); + if (lsta) + lsta->sa_query_timed_out = value; + } + } +#endif /* CONFIG_IEEE80211BE */ +} + + int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) { u32 tu; @@ -1357,7 +1629,7 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "association SA Query timed out"); - sta->sa_query_timed_out = 1; + ap_sta_set_sa_query_timeout(hapd, sta, 1); os_free(sta->sa_query_trans_id); sta->sa_query_trans_id = NULL; sta->sa_query_count = 0; @@ -1529,10 +1801,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd, char dpp_pkhash_buf[100]; char keyid_buf[100]; char ip_addr[100]; + char vlanid_buf[20]; dpp_pkhash_buf[0] = '\0'; keyid_buf[0] = '\0'; ip_addr[0] = '\0'; + vlanid_buf[0] = '\0'; + #ifdef CONFIG_P2P if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { os_snprintf(ip_addr, sizeof(ip_addr), @@ -1561,15 +1836,21 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd, dpp_pkhash, SHA256_MAC_LEN); } - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s", - buf, ip_addr, keyid_buf, dpp_pkhash_buf); +#ifndef CONFIG_NO_VLAN + if (sta->vlan_id) + os_snprintf(vlanid_buf, sizeof(vlanid_buf), + " vlanid=%u", sta->vlan_id); +#endif /* CONFIG_NO_VLAN */ + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s", + buf, ip_addr, keyid_buf, dpp_pkhash_buf, vlanid_buf); if (hapd->msg_ctx_parent && hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED "%s%s%s%s", + AP_STA_CONNECTED "%s%s%s%s%s", buf, ip_addr, keyid_buf, - dpp_pkhash_buf); + dpp_pkhash_buf, vlanid_buf); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); @@ -1596,12 +1877,13 @@ void ap_sta_set_authorized_event(struct hostapd_data *hapd, } -void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, +bool ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { if (!ap_sta_set_authorized_flag(hapd, sta, authorized)) - return; + return false; ap_sta_set_authorized_event(hapd, sta, authorized); + return true; } @@ -1625,41 +1907,19 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, if (sta == NULL) return; - ap_sta_set_authorized(hapd, sta, 0); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - hostapd_set_sta_flags(hapd, sta); - wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); - ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); - wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout " - "for " MACSTR " (%d seconds - " - "AP_MAX_INACTIVITY_AFTER_DEAUTH)", - hapd->conf->iface, __func__, MAC2STR(sta->addr), - AP_MAX_INACTIVITY_AFTER_DEAUTH); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, - ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { /* Deauthentication is not used in DMG/IEEE 802.11ad; * disassociate the STA instead. */ - sta->disassoc_reason = reason; - sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; - eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); - eloop_register_timeout(hapd->iface->drv_flags & - WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? - 2 : 0, 0, ap_sta_disassoc_cb_timeout, - hapd, sta); + ap_sta_disassociate_common(hapd, sta, reason); return; } - sta->deauth_reason = reason; - sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; - eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); - eloop_register_timeout(hapd->iface->drv_flags & - WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, - ap_sta_deauth_cb_timeout, hapd, sta); + if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISCONNECT)) + return; + + ap_sta_handle_disconnect(hapd, sta, reason); } @@ -1712,13 +1972,20 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, } +void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd, + struct sta_info *sta) +{ + eloop_cancel_timeout(ap_sta_assoc_timeout, hapd, sta); +} + + int ap_sta_flags_txt(u32 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", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1741,6 +2008,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_EHT ? "[EHT]" : ""), (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""), (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), + (flags & WLAN_STA_SPP_AMSDU ? "[SPP-A-MSDU]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? "[WNM_SLEEP_MODE]" : "")); if (os_snprintf_error(buflen, res)) @@ -1825,8 +2093,12 @@ static void ap_sta_remove_link_sta(struct hostapd_data *hapd, int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) { const u8 *mld_link_addr = NULL; - bool mld_link_sta = false; + bool mld_link_sta = false, epp_sta = false; + u16 eml_cap = 0; +#ifdef CONFIG_ENC_ASSOC + epp_sta = sta->epp_sta; +#endif /* CONFIG_ENC_ASSOC */ /* * If a station that is already associated to the AP, is trying to * authenticate again, remove the STA entry, in order to make sure the @@ -1841,6 +2113,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) mld_link_sta = sta->mld_assoc_link_id != mld_link_id; mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr; + eml_cap = sta->mld_info.common_info.eml_capa; /* * In case the AP is affiliated with an AP MLD, we need to @@ -1859,7 +2132,7 @@ int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta) sta->supported_rates_len, 0, NULL, NULL, NULL, 0, NULL, 0, NULL, sta->flags, 0, 0, 0, 0, - mld_link_addr, mld_link_sta)) { + mld_link_addr, mld_link_sta, eml_cap, epp_sta)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h index 84629358cc22..9810eb22f9a4 100644 --- a/contrib/wpa/src/ap/sta_info.h +++ b/contrib/wpa/src/ap/sta_info.h @@ -22,6 +22,7 @@ /* 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) @@ -70,6 +71,25 @@ struct pending_eapol_rx { enum frame_encryption encrypted; }; +struct eap_over_auth_data { + int akm; + int cipher; + u16 group; + u16 auth_transaction; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; + u8 *rsnxe; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + struct wpa_ptk ptk; + size_t rsnxe_len; + struct crypto_ecdh *ecdh; + struct wpabuf *dhss; + bool add_mic; + u8 epp_pmkid_cur[PMKID_LEN]; + u8 epp_pmkid_next[PMKID_LEN]; +}; + #define EHT_ML_MAX_STA_PROF_LEN 1024 struct mld_info { bool mld_sta; @@ -81,20 +101,7 @@ struct mld_info { u16 mld_capa; } common_info; - struct mld_link_info { - u8 valid:1; - u8 nstr_bitmap_len:2; - u8 local_addr[ETH_ALEN]; - u8 peer_addr[ETH_ALEN]; - - u8 nstr_bitmap[2]; - - u16 capability; - - u16 status; - u16 resp_sta_profile_len; - u8 *resp_sta_profile; - } links[MAX_NUM_MLD_LINKS]; + struct mld_link_info links[MAX_NUM_MLD_LINKS]; }; struct sta_info { @@ -111,6 +118,16 @@ struct sta_info { u8 supported_rates[WLAN_SUPP_RATES_MAX]; int supported_rates_len; u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ +#ifdef CONFIG_ENC_ASSOC + bool epp_sta; /* Indicates if the station is an EPP peer */ +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_PMKSA_PRIVACY + u8 snonce[NONCE_LEN]; /* SNonce to compute next PMKID if + * PMKID caching privacy is on */ + u8 anonce[NONCE_LEN]; /* ANonce to compute next PMKID if + * PMKID caching privacy is on */ + u8 epp_pmkid_next[PMKID_LEN]; +#endif /* CONFIG_PMKSA_PRIVACY */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; @@ -144,7 +161,6 @@ struct sta_info { unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; unsigned int qos_map_enabled:1; - unsigned int remediation:1; unsigned int hs20_deauth_requested:1; unsigned int hs20_deauth_on_ack:1; unsigned int session_timeout_set:1; @@ -230,8 +246,6 @@ struct sta_info { struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */ struct wpabuf *roaming_consortium; - u8 remediation_method; - char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; @@ -277,7 +291,7 @@ struct sta_info { #endif /* CONFIG_TAXONOMY */ #ifdef CONFIG_FILS - u8 fils_snonce[FILS_NONCE_LEN]; + u8 fils_snonce[NONCE_LEN]; u8 fils_session[FILS_SESSION_LEN]; u8 fils_erp_pmkid[PMKID_LEN]; u8 *fils_pending_assoc_req; @@ -331,10 +345,20 @@ struct sta_info { #ifdef CONFIG_IEEE80211BE struct mld_info mld_info; u8 mld_assoc_link_id; + struct link_reconf_req_list *reconf_req; #endif /* CONFIG_IEEE80211BE */ u16 max_idle_period; /* if nonzero, the granted BSS max idle period in * units of 1000 TUs */ + + u64 last_known_sta_id_timestamp; + + struct wpabuf *sae_pw_id; + unsigned int sae_pw_id_counter; + +#ifdef CONFIG_IEEE8021X_AUTH + struct eap_over_auth_data eap_auth_data; +#endif /* CONFIG_IEEE8021X_AUTH */ }; @@ -359,6 +383,8 @@ int ap_for_each_sta(struct hostapd_data *hapd, void *ctx), void *ctx); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +struct sta_info * ap_get_link_sta(struct hostapd_data *hapd, + const u8 *link_addr); struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); @@ -387,6 +413,8 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, struct vlan_description *vlan_desc); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_set_sa_query_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int value); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, struct sta_info *sta); @@ -399,7 +427,7 @@ bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta, int authorized); void ap_sta_set_authorized_event(struct hostapd_data *hapd, struct sta_info *sta, int authorized); -void ap_sta_set_authorized(struct hostapd_data *hapd, +bool ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); static inline int ap_sta_is_authorized(struct sta_info *sta) { @@ -410,6 +438,8 @@ void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, struct sta_info *sta); +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); void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, @@ -442,5 +472,32 @@ static inline void ap_sta_set_mld(struct sta_info *sta, bool mld) void ap_sta_free_sta_profile(struct mld_info *info); void hostapd_free_link_stas(struct hostapd_data *hapd); +void set_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, + struct sta_info *psta, void *wpa_sm); +void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd, + struct sta_info *psta); +void clear_wpa_sm_for_all_sta(struct hostapd_data *hapd, + struct wpa_state_machine *wpa_sm); +static inline bool ap_sta_is_epp(const struct sta_info *sta) +{ +#ifdef CONFIG_ENC_ASSOC + return sta && sta->epp_sta; +#else /* CONFIG_ENC_ASSOC */ + return false; +#endif /* CONFIG_ENC_ASSOC */ +} + +static inline bool ap_sta_support_enc_assoc(struct hostapd_data *hapd, + const u8 *rsnxe, size_t rsnxe_len) +{ +#ifdef CONFIG_ENC_ASSOC + return (hapd->conf->assoc_frame_encryption && + ieee802_11_rsnx_capab_len(rsnxe, + rsnxe_len, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)); +#else /* CONFIG_ENC_ASSOC */ + return false; +#endif /* CONFIG_ENC_ASSOC */ +} #endif /* STA_INFO_H */ diff --git a/contrib/wpa/src/ap/vlan_full.c b/contrib/wpa/src/ap/vlan_full.c index 19aa3c649a5d..687fa9c23b63 100644 --- a/contrib/wpa/src/ap/vlan_full.c +++ b/contrib/wpa/src/ap/vlan_full.c @@ -462,11 +462,19 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) { char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan; + struct hostapd_data *vlan_bss = hapd; int untagged, *tagged, i, notempty; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); - for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (vlan_bss == NULL) + vlan_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ + for (vlan = vlan_bss->conf->vlan; vlan; vlan = vlan->next) { if (vlan->configured || os_strcmp(ifname, vlan->ifname) != 0) continue; @@ -563,10 +571,20 @@ static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, void vlan_dellink(const char *ifname, struct hostapd_data *hapd) { - struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + struct hostapd_vlan *first, *prev, *vlan; + struct hostapd_data *vlan_bss = hapd; wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + vlan_bss = hostapd_mld_get_first_bss(hapd); + if (!vlan_bss) + vlan_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ + + vlan = vlan_bss->conf->vlan; first = prev = vlan; while (vlan) { diff --git a/contrib/wpa/src/ap/wnm_ap.c b/contrib/wpa/src/ap/wnm_ap.c index af8cccaefcde..90922cd85c75 100644 --- a/contrib/wpa/src/ap/wnm_ap.c +++ b/contrib/wpa/src/ap/wnm_ap.c @@ -148,7 +148,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 #define MAX_BIGTK_SUBELEM_LEN 26 - mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + MAX_BIGTK_SUBELEM_LEN + oci_ie_len); @@ -740,7 +740,7 @@ static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd, switch (report_ie->type) { #ifdef CONFIG_IEEE80211AX case WNM_EVENT_TYPE_BSS_COLOR_COLLISION: - if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax) + if (!hostapd_is_he_enabled(hapd)) return; if (report_ie->len < fixed_field_len + tsf_len + 8) { @@ -756,7 +756,7 @@ static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd, hostapd_switch_color(hapd->iface->bss[0], bitmap); break; case WNM_EVENT_TYPE_BSS_COLOR_IN_USE: - if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax) + if (!hostapd_is_he_enabled(hapd)) return; if (report_ie->len < fixed_field_len + tsf_len + 1) { wpa_printf(MSG_DEBUG, @@ -1030,7 +1030,13 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, if (disassoc_timer) { #ifdef CONFIG_IEEE80211BE - if (ap_sta_is_mld(hapd, sta)) { + /* Link removal is scheduled only when the Link Removal Imminent + * field is set to 1 in BTM as per IEEE Std 802.11be-2024, + * 9.6.13.9 (BSS Transition Management Request frame format); + * else schedule full disconnection. + */ + if (ap_sta_is_mld(hapd, sta) && + (req_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT)) { int i; unsigned int links = 0; diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c index 504ecf13428f..d736d1bc9a47 100644 --- a/contrib/wpa/src/ap/wpa_auth.c +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -38,6 +38,7 @@ #define STATE_MACHINE_DATA struct wpa_state_machine #define STATE_MACHINE_DEBUG_PREFIX "WPA" #define STATE_MACHINE_ADDR wpa_auth_get_spa(sm) +#define KDE_ALL_LINKS 0xffff static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); @@ -54,8 +55,6 @@ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_request_new_ptk(struct wpa_state_machine *sm); -static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, - struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, @@ -63,16 +62,18 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, struct wpa_ptk *ptk, int force_sha256, u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name, size_t *key_len, bool no_kdk); -static void wpa_group_free(struct wpa_authenticator *wpa_auth, - struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +#ifdef CONFIG_IEEE80211BE +static void wpa_group_put_vlan(struct wpa_authenticator *wpa_auth, + int vlan_id); +#endif /* CONFIG_IEEE80211BE */ static int ieee80211w_kde_len(struct wpa_state_machine *sm); static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); -static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, - struct wpa_group *group); +static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 eapol_key_timeout_first = 100; /* ms */ @@ -112,34 +113,41 @@ static void wpa_gkeydone_sta(struct wpa_state_machine *sm) int link_id; #endif /* CONFIG_IEEE80211BE */ - if (!sm->wpa_auth) - return; - - sm->wpa_auth->group->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = false; #ifdef CONFIG_IEEE80211BE - for_each_sm_auth(sm, link_id) - sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations--; + for_each_sm_auth(sm, link_id) { + struct wpa_authenticator *partner_auth = + sm->mld_links[link_id].wpa_auth; + struct wpa_group *partner_group = + wpa_select_vlan_wpa_group(partner_auth->group, + sm->group->vlan_id); + partner_group->GKeyDoneStations--; + } #endif /* CONFIG_IEEE80211BE */ } #ifdef CONFIG_IEEE80211BE -void wpa_release_link_auth_ref(struct wpa_state_machine *sm, - int release_link_id) +void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id, + bool rejected) { - int link_id; + struct wpa_authenticator *wpa_auth; + struct mld_link *link; - if (!sm || release_link_id >= MAX_NUM_MLD_LINKS) + if (!sm || link_id >= MAX_NUM_MLD_LINKS) return; - for_each_sm_auth(sm, link_id) { - if (link_id == release_link_id) { - wpa_group_put(sm->mld_links[link_id].wpa_auth, - sm->mld_links[link_id].wpa_auth->group); - sm->mld_links[link_id].wpa_auth = NULL; + link = &sm->mld_links[link_id]; + if (link->valid) { + link->valid = false; + link->rejected = rejected; + wpa_auth = link->wpa_auth; + if (wpa_auth) { + link->wpa_auth = NULL; + wpa_group_put_vlan(wpa_auth, sm->group->vlan_id); } } } @@ -367,6 +375,25 @@ static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_MESH */ +static inline int wpa_auth_get_drv_flags(struct wpa_authenticator *wpa_auth, + u64 *drv_flags, u64 *drv_flags2) +{ + if (!wpa_auth->cb->get_drv_flags) + return -1; + return wpa_auth->cb->get_drv_flags(wpa_auth->cb_ctx, drv_flags, + drv_flags2); +} + + +static bool wpa_auth_4way_handshake_offload(struct wpa_authenticator *wpa_auth) +{ + u64 drv_flags = 0, drv_flags2 = 0; + + return wpa_auth_get_drv_flags(wpa_auth, &drv_flags, &drv_flags2) == 0 && + (drv_flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK); +} + + int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -387,6 +414,20 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_IEEE80211BE +static int wpa_auth_for_each_partner_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, + void *ctx), + void *cb_ctx) +{ + if (!wpa_auth->cb->for_each_partner_auth) + return 0; + return wpa_auth->cb->for_each_partner_auth(wpa_auth->cb_ctx, cb, + cb_ctx); +} +#endif /* CONFIG_IEEE80211BE */ + + void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth, const u8 *addr, int cipher, u32 life_time, const struct wpa_ptk *ptk) @@ -616,7 +657,7 @@ void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm) MACSTR " (%d seconds)", MAC2STR(wpa_auth_get_spa(sm)), sm->wpa_auth->conf.wpa_ptk_rekey); - eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm); eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0, wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -631,11 +672,39 @@ static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) } + +#ifdef CONFIG_IEEE80211BE +static int wpa_auth_pmksa_clear_auth_cb(struct wpa_authenticator *wpa_auth, + void *ctx) +{ + return wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, ctx); +} +#endif /* CONFIG_IEEE80211BE */ + + static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, void *ctx) { struct wpa_authenticator *wpa_auth = ctx; + +#ifdef CONFIG_IEEE80211BE + if (!entry->is_ml) { + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); + } else { + /* Clear stale PMKSA references across partner MLD links. */ + wpa_auth_for_each_partner_auth(wpa_auth, + wpa_auth_pmksa_clear_auth_cb, + entry); + } +#else /* CONFIG_IEEE80211BE */ wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +#endif /* CONFIG_IEEE80211BE */ + + /* Remove matching PMKID from the driver, if it had been added, e.g., + * by external SAE authentication */ + if (wpa_auth->cb->remove_pmkid) + wpa_auth->cb->remove_pmkid(wpa_auth->cb_ctx, entry->spa, + entry->pmkid); } @@ -721,6 +790,19 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, } +static void wpa_deinit_groups(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *prev; + + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + bin_clear_free(prev, sizeof(*prev)); + } +} + + /** * wpa_init - Initialize WPA authenticator * @addr: Authenticator address @@ -756,36 +838,48 @@ struct wpa_authenticator * wpa_init(const u8 *addr, if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); - os_free(wpa_auth); - return NULL; + goto fail; } wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); - if (!wpa_auth->group) { - os_free(wpa_auth->wpa_ie); - os_free(wpa_auth); - return NULL; - } + if (!wpa_auth->group) + goto fail; + /* Per-link PMKSA cache */ wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, wpa_auth); if (!wpa_auth->pmksa) { wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); - os_free(wpa_auth->group); - os_free(wpa_auth->wpa_ie); - os_free(wpa_auth); - return NULL; + goto fail; } +#ifdef CONFIG_IEEE80211BE + /* MLD-level PMKSA cache */ + if (wpa_auth->is_ml && wpa_auth->primary_auth) { + wpa_auth->ml_pmksa = pmksa_cache_auth_init( + wpa_auth_pmksa_free_cb, wpa_auth); + if (!wpa_auth->ml_pmksa) { + wpa_printf(MSG_ERROR, + "MLD-level PMKSA cache initialization failed."); + goto fail; + } + } else if (wpa_auth->is_ml) { + struct wpa_authenticator *pa = wpa_get_primary_auth(wpa_auth); + + if (!pa) { + wpa_printf(MSG_ERROR, + "Could not find primary authenticator."); + goto fail; + } + wpa_auth->ml_pmksa = pa->ml_pmksa; + } +#endif /* CONFIG_IEEE80211BE */ + #ifdef CONFIG_IEEE80211R_AP wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (!wpa_auth->ft_pmk_cache) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); - os_free(wpa_auth->group); - os_free(wpa_auth->wpa_ie); - pmksa_cache_auth_deinit(wpa_auth->pmksa); - os_free(wpa_auth); - return NULL; + goto fail; } #endif /* CONFIG_IEEE80211R_AP */ @@ -820,14 +914,23 @@ struct wpa_authenticator * wpa_init(const u8 *addr, if (conf->tx_bss_auth && conf->beacon_prot) { conf->tx_bss_auth->non_tx_beacon_prot = true; - if (!conf->tx_bss_auth->conf.beacon_prot) - conf->tx_bss_auth->conf.beacon_prot = true; if (!conf->tx_bss_auth->conf.group_mgmt_cipher) conf->tx_bss_auth->conf.group_mgmt_cipher = conf->group_mgmt_cipher; } return wpa_auth; + +fail: + wpa_deinit_groups(wpa_auth); + os_free(wpa_auth->wpa_ie); + pmksa_cache_auth_deinit(wpa_auth->pmksa); +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->primary_auth) + pmksa_cache_auth_deinit(wpa_auth->ml_pmksa); +#endif /* CONFIG_IEEE80211BE */ + os_free(wpa_auth); + return NULL; } @@ -863,16 +966,36 @@ static void wpa_auth_free_conf(struct wpa_auth_config *conf) */ void wpa_deinit(struct wpa_authenticator *wpa_auth) { - struct wpa_group *group, *prev; +#ifdef CONFIG_IEEE80211BE + struct wpa_authenticator *next_pa; +#endif /* CONFIG_IEEE80211BE */ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); - - /* TODO: Assign ML primary authenticator to next link authenticator and - * start rekey timer. */ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); pmksa_cache_auth_deinit(wpa_auth->pmksa); +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->is_ml && wpa_auth->primary_auth) { + next_pa = wpa_auth->cb->next_primary_auth(wpa_auth->cb_ctx); + + if (!next_pa) { + /* Deinit PMKSA entry list if last link */ + pmksa_cache_auth_deinit(wpa_auth->ml_pmksa); + } else { + /* Assign ML primary authenticator to the next link + * authenticator and start rekey timer. + */ + next_pa->primary_auth = true; + pmksa_cache_auth_set_ctx(next_pa->ml_pmksa, next_pa); + if (next_pa->conf.wpa_group_rekey) + eloop_register_timeout( + next_pa->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, next_pa, NULL); + } + } +#endif /* CONFIG_IEEE80211BE */ + #ifdef CONFIG_IEEE80211R_AP wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; @@ -883,16 +1006,8 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) bitfield_free(wpa_auth->ip_pool); #endif /* CONFIG_P2P */ - os_free(wpa_auth->wpa_ie); - - group = wpa_auth->group; - while (group) { - prev = group; - group = group->next; - bin_clear_free(prev, sizeof(*prev)); - } - + wpa_deinit_groups(wpa_auth); wpa_auth_free_conf(&wpa_auth->conf); os_free(wpa_auth); } @@ -960,12 +1075,51 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, } +#ifdef CONFIG_ENC_ASSOC +void wpa_store_eppke_pmk_ptk_sm(struct wpa_state_machine *sm, + const struct wpa_ptk *ptk, const u8 *pmk, + size_t pmk_len) +{ + os_memcpy(&sm->PTK, ptk, sizeof(struct wpa_ptk)); + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + sm->PTK_valid = true; + sm->pairwise_set = true; + sm->hash_alg = ptk->hash_alg; +} +#endif /* CONFIG_ENC_ASSOC */ + + int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm) { if (!wpa_auth || !wpa_auth->conf.wpa || !sm) return -1; +#ifdef CONFIG_ENC_ASSOC + if (sm->auth_alg == WLAN_AUTH_EPPKE) { + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, + "EPPKE authentication already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = true; + return 0; + } +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + if (wpa_auth->conf.assoc_frame_encryption && + sm->auth_alg == WLAN_AUTH_802_1X && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) { + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, + "IEEE 802.1X (EAP) over Authentication frames already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = true; + return 0; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + #ifdef CONFIG_IEEE80211R_AP if (sm->ft_completed) { wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, @@ -1002,7 +1156,13 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = false; - sm->AuthenticationRequest = true; + + if (wpa_auth_4way_handshake_offload(sm->wpa_auth)) + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Skip EAPOL for 4-way handshake offload case"); + else + sm->AuthenticationRequest = true; + return wpa_sm_step(sm); } @@ -1046,17 +1206,22 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); os_free(sm->rsnxe); + os_free(sm->rsn_selection); #ifdef CONFIG_IEEE80211BE for_each_sm_auth(sm, link_id) { - wpa_group_put(sm->mld_links[link_id].wpa_auth, - sm->mld_links[link_id].wpa_auth->group); + struct wpa_authenticator *wpa_auth; + + wpa_auth = sm->mld_links[link_id].wpa_auth; sm->mld_links[link_id].wpa_auth = NULL; + sm->mld_links[link_id].valid = false; + wpa_group_put_vlan(wpa_auth, sm->group->vlan_id); } #endif /* CONFIG_IEEE80211BE */ wpa_group_put(sm->wpa_auth, sm->group); #ifdef CONFIG_DPP2 wpabuf_clear_free(sm->dpp_z); #endif /* CONFIG_DPP2 */ + wpabuf_free(sm->sae_pw_id); bin_clear_free(sm, sizeof(*sm)); } @@ -1086,10 +1251,10 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) primary_auth, NULL); } - eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm); sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); - eloop_cancel_timeout(wpa_rekey_ptk, wpa_auth, sm); + eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm); #ifdef CONFIG_IEEE80211R_AP wpa_ft_sta_deinit(sm); #endif /* CONFIG_IEEE80211R_AP */ @@ -1368,6 +1533,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = true; + sm->hash_alg = sm->PTK.hash_alg; ret = 0; fail: @@ -1551,7 +1717,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); - mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); keyhdrlen = sizeof(*key) + mic_len + 2; if (data_len < sizeof(*hdr) + keyhdrlen) { @@ -1802,7 +1968,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, continue_fuzz: #endif /* TEST_FUZZ */ sm->MICVerified = true; - eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm); sm->pending_1_of_4_timeout = 0; } @@ -1886,7 +2052,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, sm->EAPOLKeyReceived = true; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); - os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + if (msg == PAIRWISE_2) + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); wpa_sm_step(sm); out: @@ -1978,7 +2145,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, int i; u8 *key_mic, *key_data; - mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); keyhdrlen = sizeof(*key) + mic_len + 2; len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; @@ -2064,6 +2231,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (key_rsc) os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); +#ifdef CONFIG_TESTING_OPTIONS + if (conf->eapol_key_reserved_random && + random_get_bytes(key->key_id, sizeof(key->key_id)) < 0) + os_memset(key->key_id, 0x11, sizeof(key->key_id)); +#endif /* CONFIG_TESTING_OPTIONS */ + if (kde && !encr) { os_memcpy(key_data, kde, kde_len); WPA_PUT_BE16(key_mic + mic_len, kde_len); @@ -2085,6 +2258,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, 1, aad, aad_len, key_mic + 2) < 0) { wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); + os_free(hdr); return; } @@ -2155,7 +2329,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, - sm->wpa_key_mgmt, version, + sm->wpa_key_mgmt, sm->hash_alg, version, (u8 *) hdr, len, key_mic) < 0) { os_free(hdr); return; @@ -2255,7 +2429,7 @@ static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, u16 key_info; int ret = 0; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos; - size_t mic_len = wpa_mic_len(akmp, pmk_len); + size_t mic_len = wpa_mic_len(akmp, pmk_len, PTK->hash_alg); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; @@ -2266,7 +2440,7 @@ static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, mic_pos, mic_len); os_memset(mic_pos, 0, mic_len); - if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, + if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, PTK->hash_alg, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, mic_pos) || os_memcmp_const(mic, mic_pos, mic_len) != 0) @@ -2293,7 +2467,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) wpa_printf(MSG_DEBUG, "RSN: PTK Key ID 1 removal from the driver failed"); sm->pairwise_set = false; - eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_rekey_ptk, ELOOP_ALL_CTX, sm); } @@ -2333,6 +2507,13 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: + if (event == WPA_REAUTH && sm->auth_alg == WLAN_AUTH_EPPKE && + !sm->started) { + wpa_printf(MSG_DEBUG, + "EPPKE: Do not start state machine for a STA that has used EPPKE and reassociates"); + break; + } + if (!sm->started) { /* * When using WPS, we may end up here if the STA @@ -2348,7 +2529,12 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) if (wpa_sm_step(sm) == 1) return 1; /* should not really happen */ sm->Init = false; - sm->AuthenticationRequest = true; + + if (wpa_auth_4way_handshake_offload(sm->wpa_auth)) + wpa_printf(MSG_DEBUG, + "Skip EAPOL for 4-way handshake offload case"); + else + sm->AuthenticationRequest = true; break; } @@ -2424,6 +2610,16 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) (event == WPA_AUTH || event == WPA_ASSOC)) remove_ptk = 0; #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + if (sm->auth_alg == WLAN_AUTH_EPPKE && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + if (sm->auth_alg == WLAN_AUTH_802_1X && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_IEEE8021X_AUTH */ if (remove_ptk) { sm->PTK_valid = false; @@ -2508,11 +2704,38 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static bool wpa_group_first_sta_seen_mbssid(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (!wpa_auth->cb->first_sta_seen_mbssid) + return false; + + return wpa_auth->cb->first_sta_seen_mbssid(wpa_auth->cb_ctx, + group->vlan_id); +} + + static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { - if (group->first_sta_seen) + bool first_sta_seen_mbssid = + wpa_group_first_sta_seen_mbssid(wpa_auth, group); + + /* Skip group keys renewal for below cases: + * This is not the first station association on this authenticator. + * In MBSSID case, this is not the first station association in the + * MBSSID set. + */ + if (group->first_sta_seen || first_sta_seen_mbssid) return; + + if (!first_sta_seen_mbssid && wpa_auth->conf.tx_bss_auth) { + struct wpa_group *tx_group = + wpa_auth->conf.tx_bss_auth->group; + tx_group->bigtk_set = false; + tx_group->bigtk_configured = false; + } + /* * System has run bit further than at the time hostapd was started * potentially very early during boot up. This provides better chances @@ -2532,7 +2755,7 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, } if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 || - wpa_gtk_update(wpa_auth, group) < 0 || + wpa_group_update_gtk(wpa_auth, group) < 0 || wpa_group_config_group_keys(wpa_auth, group) < 0) { wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed"); group->first_sta_seen = false; @@ -2543,9 +2766,18 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, SM_STATE(WPA_PTK, AUTHENTICATION2) { +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + wpa_group_ensure_init(sm->mld_links[link_id].wpa_auth, + sm->mld_links[link_id].wpa_auth->group); +#endif /* CONFIG_IEEE80211BE */ sm->ReAuthenticationRequest = false; /* @@ -2755,8 +2987,7 @@ SM_STATE(WPA_PTK, PTKSTART) if (sm->wpa == WPA_VERSION_WPA2 && (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) || (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) || - wpa_key_mgmt_sae(sm->wpa_key_mgmt)) && - sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { + wpa_key_mgmt_sae(sm->wpa_key_mgmt))) { pmkid = buf; kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; @@ -3034,8 +3265,8 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, forced_memzero(ick, sizeof(ick)); /* Store nonces for (Re)Association Request/Response frame processing */ - os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); - os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); + os_memcpy(sm->SNonce, snonce, NONCE_LEN); + os_memcpy(sm->ANonce, anonce, NONCE_LEN); return res; } @@ -3252,10 +3483,10 @@ int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, aad_len[1] = ETH_ALEN; /* The STA's nonce */ aad[2] = sm->SNonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The AP's nonce */ aad[3] = sm->ANonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Request frame from the Capability Information * field to the FILS Session element (both inclusive). @@ -3311,10 +3542,10 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, aad_len[1] = ETH_ALEN; /* The AP's nonce */ aad[2] = sm->ANonce; - aad_len[2] = FILS_NONCE_LEN; + aad_len[2] = NONCE_LEN; /* The STA's nonce */ aad[3] = sm->SNonce; - aad_len[3] = FILS_NONCE_LEN; + aad_len[3] = NONCE_LEN; /* * The (Re)Association Response frame from the Capability Information * field (the same offset in both Association and Reassociation @@ -3403,7 +3634,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, /* GTK KDE */ gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -3591,6 +3822,12 @@ static int wpa_auth_validate_ml_kdes_m2(struct wpa_state_machine *sm, return -1; } + /* Skip rejected links although the non-AP MLD will send them in + * M2 of the initial 4-way handshake. */ + if (sm->mld_links[i].rejected) { + n_links++; + continue; + } if (!sm->mld_links[i].valid || i == sm->mld_assoc_link_id) { wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid link ID=%u", i); @@ -3651,7 +3888,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->update_snonce = false; os_memset(&PTK, 0, sizeof(PTK)); - mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); derive_kdk = sm->wpa_auth->conf.secure_ltf && ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF); @@ -3807,7 +4044,6 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) key_data_buf_len = key_data_length; if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8, key_data, key_data_buf)) { - bin_clear_free(key_data_buf, key_data_buf_len); wpa_printf(MSG_INFO, "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data"); goto out; @@ -3825,9 +4061,6 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; eapol_key_ie_len = kde.rsn_ie_len; - } else if (kde.osen) { - eapol_key_ie = kde.osen; - eapol_key_ie_len = kde.osen_len; } else { eapol_key_ie = kde.wpa_ie; eapol_key_ie_len = kde.wpa_ie_len; @@ -3914,6 +4147,34 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) goto out; } #endif /* CONFIG_IEEE80211R_AP */ + + /* Verify RSN Selection element for RSN overriding */ + if ((wpa_auth->conf.rsn_override_key_mgmt || + wpa_auth->conf.rsn_override_key_mgmt_2) && + ((rsn_is_snonce_cookie(sm->SNonce) && !kde.rsn_selection) || + (!rsn_is_snonce_cookie(sm->SNonce) && kde.rsn_selection) || + (sm->rsn_selection && !kde.rsn_selection) || + (!sm->rsn_selection && kde.rsn_selection) || + (sm->rsn_selection && kde.rsn_selection && + (sm->rsn_selection_len != kde.rsn_selection_len || + os_memcmp(sm->rsn_selection, kde.rsn_selection, + sm->rsn_selection_len) != 0)))) { + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO, + "RSN Selection element from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4"); + wpa_printf(MSG_DEBUG, + "SNonce cookie for RSN overriding %sused", + rsn_is_snonce_cookie(sm->SNonce) ? "" : "not "); + wpa_hexdump(MSG_DEBUG, "RSN Selection in AssocReq", + sm->rsn_selection, sm->rsn_selection_len); + wpa_hexdump(MSG_DEBUG, "RSN Selection in EAPOL-Key msg 2/4", + kde.rsn_selection, kde.rsn_selection_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + goto out; + + } + #ifdef CONFIG_P2P if (kde.ip_addr_req && kde.ip_addr_req[0] && wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { @@ -3958,7 +4219,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (wpa_auth_validate_ml_kdes_m2(sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - return; + goto out; } if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && @@ -3969,7 +4230,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } sm->pending_1_of_4_timeout = 0; - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && sm->PMK != pmk) { /* PSK may have changed from the previous choice, so update @@ -3992,6 +4253,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = true; + sm->hash_alg = sm->PTK.hash_alg; out: forced_memzero(pmk_r0, sizeof(pmk_r0)); forced_memzero(pmk_r1, sizeof(pmk_r1)); @@ -4053,7 +4315,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) else os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. @@ -4066,6 +4328,11 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) NULL, 0); forced_memzero(&igtk, sizeof(igtk)); + if (gsm->vlan_id) { + wpa_printf(MSG_DEBUG, "Fetch BIGTK from default VLAN"); + gsm = wpa_auth->group; + } + if (wpa_auth->conf.tx_bss_auth) { wpa_auth = wpa_auth->conf.tx_bss_auth; conf = &wpa_auth->conf; @@ -4079,19 +4346,11 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) bigtk.keyid[0] = gsm->GN_bigtk; bigtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) + wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0) os_memset(bigtk.pn, 0, sizeof(bigtk.pn)); else os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn)); os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len); - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { - /* - * Provide unique random BIGTK to each OSEN STA to prevent use - * of BIGTK in the BSS. - */ - if (random_get_bytes(bigtk.bigtk, len) < 0) - return pos; - } pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK, (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len, NULL, 0); @@ -4177,20 +4436,24 @@ static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid, void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, struct wpa_auth_ml_link_key_info *info, - bool mgmt_frame_prot, bool beacon_prot) + bool mgmt_frame_prot, bool beacon_prot, + bool rekey, int vlan_id) { struct wpa_group *gsm = a->group; u8 rsc[WPA_KEY_RSC_LEN]; wpa_printf(MSG_DEBUG, - "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u", - info->link_id, mgmt_frame_prot, beacon_prot); + "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u VLAN ID:%d", + info->link_id, mgmt_frame_prot, beacon_prot, vlan_id); + + if (vlan_id) + gsm = wpa_select_vlan_wpa_group(gsm, vlan_id); info->gtkidx = gsm->GN & 0x03; info->gtk = gsm->GTK[gsm->GN - 1]; info->gtk_len = gsm->GTK_len; - if (wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0) + if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0) os_memset(info->pn, 0, sizeof(info->pn)); else os_memcpy(info->pn, rsc, sizeof(info->pn)); @@ -4202,7 +4465,7 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, info->igtk = gsm->IGTK[gsm->GN_igtk - 4]; info->igtk_len = wpa_cipher_key_len(a->conf.group_mgmt_cipher); - if (wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0) + if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0) os_memset(info->ipn, 0, sizeof(info->ipn)); else os_memcpy(info->ipn, rsc, sizeof(info->ipn)); @@ -4210,6 +4473,11 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, if (!beacon_prot) return; + if (gsm->vlan_id) { + wpa_printf(MSG_DEBUG, "Fetch BIGTK from default VLAN"); + gsm = a->group; + } + if (a->conf.tx_bss_auth) { a = a->conf.tx_bss_auth; gsm = a->group; @@ -4218,7 +4486,7 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, info->bigtkidx = gsm->GN_bigtk; info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6]; - if (wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0) + if (rekey || wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0) os_memset(info->bipn, 0, sizeof(info->bipn)); else os_memcpy(info->bipn, rsc, sizeof(info->bipn)); @@ -4226,16 +4494,17 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth, - struct wpa_auth_ml_key_info *info) + struct wpa_auth_ml_key_info *info, + bool rekey, int vlan_id) { if (!wpa_auth->cb->get_ml_key_info) return; - wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info); + wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info, rekey, vlan_id); } -static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm) +size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links) { struct wpa_authenticator *wpa_auth; size_t kde_len = 0; @@ -4248,6 +4517,9 @@ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm) if (!sm->mld_links[link_id].valid) continue; + if (!(req_links & BIT(link_id))) + continue; + wpa_auth = sm->mld_links[link_id].wpa_auth; if (!wpa_auth || !wpa_auth->group) continue; @@ -4283,15 +4555,16 @@ static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm) } -static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) +u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos, + u16 req_links) { struct wpa_auth_ml_key_info ml_key_info; unsigned int i, link_id; u8 *start = pos; + bool rekey = sm->wpa_ptk_group_state == WPA_PTK_GROUP_REKEYNEGOTIATING; /* First fetch the key information from all the authenticators */ os_memset(&ml_key_info, 0, sizeof(ml_key_info)); - ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1; /* * Assume that management frame protection and beacon protection are the @@ -4304,13 +4577,20 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) if (!sm->mld_links[link_id].valid) continue; + if (!(req_links & BIT(link_id))) + continue; + ml_key_info.links[i++].link_id = link_id; } + ml_key_info.n_mld_links = i; - wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info); + wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info, rekey, + sm->group->vlan_id); /* Add MLO GTK KDEs */ - for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + for (i = 0; i < ml_key_info.n_mld_links; i++) { + link_id = ml_key_info.links[i].link_id; + if (!sm->mld_links[link_id].valid || !ml_key_info.links[i].gtk_len) continue; @@ -4335,18 +4615,18 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) os_memcpy(pos, ml_key_info.links[i].gtk, ml_key_info.links[i].gtk_len); pos += ml_key_info.links[i].gtk_len; - - i++; } if (!sm->mgmt_frame_prot) { - wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", + wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td", pos - start); return pos; } /* Add MLO IGTK KDEs */ - for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + for (i = 0; i < ml_key_info.n_mld_links; i++) { + link_id = ml_key_info.links[i].link_id; + if (!sm->mld_links[link_id].valid || !ml_key_info.links[i].igtk_len) continue; @@ -4378,18 +4658,18 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) os_memcpy(pos, ml_key_info.links[i].igtk, ml_key_info.links[i].igtk_len); pos += ml_key_info.links[i].igtk_len; - - i++; } if (!sm->wpa_auth->conf.beacon_prot) { - wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", + wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td", pos - start); return pos; } /* Add MLO BIGTK KDEs */ - for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + for (i = 0; i < ml_key_info.n_mld_links; i++) { + link_id = ml_key_info.links[i].link_id; + if (!sm->mld_links[link_id].valid || !ml_key_info.links[i].bigtk || !ml_key_info.links[i].igtk_len) @@ -4422,11 +4702,9 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) os_memcpy(pos, ml_key_info.links[i].bigtk, ml_key_info.links[i].igtk_len); pos += ml_key_info.links[i].igtk_len; - - i++; } - wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start); + wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td", pos - start); return pos; } @@ -4446,7 +4724,7 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm) /* For the MAC Address KDE */ kde_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN; - /* MLO Link KDE for each link */ + /* MLO Link KDE and RSN Override Link KDE for each link */ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { struct wpa_authenticator *wpa_auth; const u8 *ie; @@ -4455,18 +4733,44 @@ static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm) if (!wpa_auth) continue; + /* MLO Link KDE */ kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN; + ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, WLAN_EID_RSN); if (ie) kde_len += 2 + ie[1]; + ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, WLAN_EID_RSNX); if (ie) kde_len += 2 + ie[1]; + + if ((!wpa_auth->conf.rsn_override_key_mgmt && + !wpa_auth->conf.rsn_override_key_mgmt_2) || + !rsn_is_snonce_cookie(sm->SNonce)) + continue; + + /* RSN Override Link KDE */ + kde_len += 2 + RSN_SELECTOR_LEN + 1; + + ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNE_OVERRIDE_IE_VENDOR_TYPE); + if (ie) + kde_len += 2 + ie[1]; + + ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (ie) + kde_len += 2 + ie[1]; + + ie = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (ie) + kde_len += 2 + ie[1]; } - kde_len += wpa_auth_ml_group_kdes_len(sm); + kde_len += wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS); #endif /* CONFIG_IEEE80211BE */ return kde_len; @@ -4488,8 +4792,9 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { struct wpa_authenticator *wpa_auth; - const u8 *rsne, *rsnxe; - size_t rsne_len, rsnxe_len; + const u8 *rsne, *rsnxe, *rsnoe, *rsno2e, *rsnxoe; + size_t rsne_len, rsnxe_len, rsnoe_len, rsno2e_len, rsnxoe_len; + size_t kde_len; wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id); if (!wpa_auth) @@ -4508,6 +4813,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) RSN_SELECTOR_LEN + 1 + ETH_ALEN + rsne_len + rsnxe_len); + /* MLO Link KDE */ *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN + rsne_len + rsnxe_len; @@ -4535,16 +4841,165 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) os_memcpy(pos, rsnxe, rsnxe_len); pos += rsnxe_len; } + + if ((!wpa_auth->conf.rsn_override_key_mgmt && + !wpa_auth->conf.rsn_override_key_mgmt_2) || + !rsn_is_snonce_cookie(sm->SNonce)) + continue; + + rsnoe = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNE_OVERRIDE_IE_VENDOR_TYPE); + rsnoe_len = rsnoe ? 2 + rsnoe[1] : 0; + + rsno2e = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + rsno2e_len = rsno2e ? 2 + rsno2e[1] : 0; + + rsnxoe = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len, + RSNXE_OVERRIDE_IE_VENDOR_TYPE); + rsnxoe_len = rsnxoe ? 2 + rsnxoe[1] : 0; + + wpa_printf(MSG_DEBUG, + "RSN: RSN Override Link KDE: link=%u, len=%zu", + link_id, RSN_SELECTOR_LEN + rsnoe_len + rsno2e_len + + rsnxoe_len); + + /* RSN Override Link KDE */ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + kde_len = RSN_SELECTOR_LEN + 1 + rsnoe_len + rsno2e_len + + rsnxoe_len; + if (kde_len > 255) { + wpa_printf(MSG_ERROR, + "RSN: RSNOE/RSNO2E/RSNXOE too long (KDE length %zu) to fit in RSN Override Link KDE for link %u", + kde_len, link_id); + return NULL; + } + *pos++ = kde_len; + + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_RSN_OVERRIDE_LINK); + pos += RSN_SELECTOR_LEN; + + *pos++ = link_id; + + if (rsnoe_len) { + os_memcpy(pos, rsnoe, rsnoe_len); + pos += rsnoe_len; + } + + if (rsno2e_len) { + os_memcpy(pos, rsno2e, rsno2e_len); + pos += rsno2e_len; + } + + if (rsnxoe_len) { + os_memcpy(pos, rsnxoe, rsnxoe_len); + pos += rsnxoe_len; + } } - wpa_printf(MSG_DEBUG, "RSN: MLO Link KDE len = %ld", pos - start); - pos = wpa_auth_ml_group_kdes(sm, pos); + wpa_printf(MSG_DEBUG, + "RSN: MLO Link KDEs and RSN Override Link KDEs len = %td", + pos - start); + pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS); #endif /* CONFIG_IEEE80211BE */ return pos; } +#ifdef CONFIG_SAE +static u8 * add_sae_pw_ids(struct wpa_state_machine *sm, u8 *pos, u8 *end) +{ + static const size_t max_padding = 8; + u8 *start = pos, *len; + unsigned int i; + struct wpa_auth_config *conf = &sm->wpa_auth->conf; + u8 *data, *dpos; + unsigned int counter; + size_t pw_id_len = wpabuf_len(sm->sae_pw_id); + struct os_time t; + size_t kde_len; + + wpa_printf(MSG_DEBUG, "RSN: Add SAE Password Identifiers KDE (num=%u)", + conf->sae_pw_id_num); + wpa_hexdump_buf(MSG_DEBUG, "RSN: Real SAE Password Identifier", + sm->sae_pw_id); + data = os_malloc(pw_id_len + max_padding + 4 + 4); + if (!data) + return NULL; + + if (end - pos < 2 + RSN_SELECTOR_LEN + 1) { + pos = NULL; + goto fail; + } + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + len = pos++; /* Length to be filled */ + RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_SAE_PW_IDS); + pos += RSN_SELECTOR_LEN; + + *pos++ = 0; /* Flags */ + + counter = sm->sae_pw_id_counter + conf->sae_pw_id_num; + + os_get_time(&t); + + WPA_PUT_BE32(data, t.sec); + os_memcpy(data + 4, wpabuf_head(sm->sae_pw_id), pw_id_len); + + for (i = 0; i < conf->sae_pw_id_num; i++) { + size_t pad_len, dlen, elen; + + pad_len = 1 + os_random() % max_padding; + dpos = data + 4 + pw_id_len; + os_memset(dpos, 0, pad_len); + dpos += pad_len; + WPA_PUT_BE32(dpos, counter); + dpos += 4; + dlen = dpos - data; + elen = dlen + AES_BLOCK_SIZE; + counter++; + + kde_len = (pos - len - 1) + (1 + elen); + if ((size_t) (end - pos) < 1 + elen || kde_len > 255) { + wpa_printf(MSG_INFO, + "RSN: Not enough room in the buffer for a new SAE Password Identifier - send only %u", + i); + break; + } + + *pos++ = elen; + if (aes_siv_encrypt(conf->sae_pw_id_key, + sizeof(conf->sae_pw_id_key), + data, dlen, 0, NULL, NULL, pos) < 0) { + wpa_printf(MSG_INFO, + "RSN: Failed to encrypt SAE Password Identifier"); + pos = NULL; + goto fail; + } + pos += elen; + } + + kde_len = pos - len - 1; + if (kde_len > 255) { + wpa_printf(MSG_INFO, + "RSN: SAE Password Identifiers do not fit in a KDE"); + wpa_hexdump_key(MSG_DEBUG, "RSN: KDE", start, pos - start); + pos = NULL; + goto fail; + } + + *len = kde_len; + + wpa_hexdump_key(MSG_DEBUG, "RSN: SAE Password Identifiers KDE", + start, pos - start); + +fail: + os_free(data); + return pos; +} +#endif /* CONFIG_SAE */ + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32]; @@ -4552,7 +5007,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) struct wpa_group *gsm = sm->group; u8 *wpa_ie; int secure, gtkidx, encr = 0; - u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL; + u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL, *wpa_ie_buf3 = NULL; u8 hdr[2]; struct wpa_auth_config *conf = &sm->wpa_auth->conf; #ifdef CONFIG_IEEE80211BE @@ -4560,6 +5015,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) #else /* CONFIG_IEEE80211BE */ bool is_mld = false; #endif /* CONFIG_IEEE80211BE */ +#ifdef CONFIG_SAE + bool sae_pw_ids = false; +#endif /* CONFIG_SAE */ SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = false; @@ -4593,6 +5051,39 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } + if ((conf->rsn_override_key_mgmt || conf->rsn_override_key_mgmt_2) && + !rsn_is_snonce_cookie(sm->SNonce)) { + u8 *ie; + size_t ie_len; + u32 ids[] = { + RSNE_OVERRIDE_IE_VENDOR_TYPE, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE, + RSNXE_OVERRIDE_IE_VENDOR_TYPE, + 0 + }; + int i; + + wpa_printf(MSG_DEBUG, + "RSN: Remove RSNE/RSNXE override elements"); + wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs before edits", + wpa_ie, wpa_ie_len); + wpa_ie_buf3 = os_memdup(wpa_ie, wpa_ie_len); + if (!wpa_ie_buf3) + goto done; + wpa_ie = wpa_ie_buf3; + + for (i = 0; ids[i]; i++) { + ie = (u8 *) get_vendor_ie(wpa_ie, wpa_ie_len, ids[i]); + if (ie) { + ie_len = 2 + ie[1]; + os_memmove(ie, ie + ie_len, + wpa_ie_len - (ie + ie_len - wpa_ie)); + wpa_ie_len -= ie_len; + } + } + wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs after edits", + wpa_ie, wpa_ie_len); + } #ifdef CONFIG_TESTING_OPTIONS if (conf->rsne_override_eapol_set) { wpa_ie_buf2 = replace_ie( @@ -4628,6 +5119,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) return; } + if (!sm->use_ext_key_id && sm->TimeoutCtr == 1 && + wpa_auth_set_key(sm->wpa_auth, 0, + wpa_cipher_to_alg(sm->pairwise), + sm->addr, 0, sm->PTK.tk, + wpa_cipher_key_len(sm->pairwise), + KEY_FLAG_PAIRWISE_NEXT)) { + /* Continue anyway since the many drivers do not support + * configuration of the TK for RX-only purposes for + * cases where multiple keys might be in use in parallel + * and this being an optional optimization to avoid race + * condition during TK changes that could result in some + * protected frames getting discarded. */ + } + #ifdef CONFIG_PASN if (sm->wpa_auth->conf.secure_ltf && ieee802_11_rsnx_capab(sm->rsnxe, @@ -4647,8 +5152,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; - if (conf->disable_gtk || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -4714,6 +5218,17 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) if (sm->ssid_protection) kde_len += 2 + conf->ssid_len; +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae(sm->wpa_key_mgmt) && + conf->sae_pw_id_num && + sm->sae_pw_id && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE)) { + kde_len += 2 + 255; + sae_pw_ids = true; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_TESTING_OPTIONS if (conf->eapol_m3_elements) kde_len += wpabuf_len(conf->eapol_m3_elements); @@ -4832,6 +5347,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) #endif /* CONFIG_DPP2 */ pos = wpa_auth_ml_kdes(sm, pos); + if (!pos) { + wpa_printf(MSG_ERROR, "RSN: Failed to add MLO KDEs"); + goto done; + } if (sm->ssid_protection) { *pos++ = WLAN_EID_SSID; @@ -4840,6 +5359,23 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos += conf->ssid_len; } +#ifdef CONFIG_SAE + if (sae_pw_ids) { + u8 *npos; + + npos = add_sae_pw_ids(sm, pos, kde + kde_len); + if (!npos) { + wpa_printf(MSG_DEBUG, + "RSN: Failed to add SAE Password Identifiers KDE"); + /* Ignore this since it is not a fatal error for the + * Authenticator and the STA can decide whether to + * proceed without getting new identifiers. */ + } else { + pos = npos; + } + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_TESTING_OPTIONS if (conf->eapol_m3_elements) { os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements), @@ -4853,8 +5389,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | - (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? - WPA_KEY_INFO_MIC : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, + sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, 0, encr); @@ -4862,6 +5398,7 @@ done: bin_clear_free(kde, kde_len); os_free(wpa_ie_buf); os_free(wpa_ie_buf2); + os_free(wpa_ie_buf3); } @@ -4882,7 +5419,7 @@ static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm) * Note: last_rx_eapol_key length fields have already been validated in * wpa_receive(). */ - mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key; key = (const struct wpa_eapol_key *) (hdr + 1); @@ -5192,13 +5729,14 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) sm->TimeoutEvt = false; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ os_memset(rsc, 0, WPA_KEY_RSC_LEN); - if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE && !is_mld) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -5229,14 +5767,14 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) kde_len = pos - kde; #ifdef CONFIG_IEEE80211BE } else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) { - kde_len = wpa_auth_ml_group_kdes_len(sm); + kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS); if (kde_len) { kde_buf = os_malloc(kde_len); if (!kde_buf) return; kde = pos = kde_buf; - pos = wpa_auth_ml_group_kdes(sm, pos); + pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS); kde_len = pos - kde_buf; } #endif /* CONFIG_IEEE80211BE */ @@ -5247,8 +5785,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | - (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? - WPA_KEY_INFO_MIC : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, + sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), rsc, NULL, kde, kde_len, gsm->GN, 1); @@ -5273,7 +5811,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) sm->EAPOLKeyReceived = false; #ifdef CONFIG_OCV - mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); /* * Note: last_rx_eapol_key length fields have already been validated in @@ -5411,7 +5949,7 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, wpa_hexdump_key(MSG_DEBUG, "GTK", group->GTK[group->GN - 1], group->GTK_len); - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (wpa_auth_pmf_enabled(conf)) { len = wpa_cipher_key_len(conf->group_mgmt_cipher); os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); @@ -5423,10 +5961,18 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, group->IGTK[group->GN_igtk - 4], len); } - if (!wpa_auth->non_tx_beacon_prot && - conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) + /* Skip BIGTK generation for groups with a non-zero VLAN ID since only + * a single BIGTK is shared for all VLANs in a BSS. */ + if (group->vlan_id) return ret; - if (!conf->beacon_prot) + + if (!wpa_auth->non_tx_beacon_prot && + !wpa_auth_pmf_enabled(conf)) + return ret; + + /* Skip BIGTK generation if neither the TX BSS nor any of the non-TX BSS + * enable beacon protection */ + if (!wpa_auth->non_tx_beacon_prot && !conf->beacon_prot) return ret; if (wpa_auth->conf.tx_bss_auth) { @@ -5475,11 +6021,44 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { - if (ctx != NULL && ctx != sm->group) + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + struct wpa_group *group = sm->group; +#ifdef CONFIG_IEEE80211BE + int link_id; + + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + struct wpa_group *g; + + if (!sm->mld_links[link_id].valid) + continue; + if (!sm->mld_links[link_id].wpa_auth) + continue; + g = wpa_select_vlan_wpa_group( + sm->mld_links[link_id].wpa_auth->group, + sm->group->vlan_id); + if (g == ctx) { + group = g; + wpa_auth = sm->mld_links[link_id].wpa_auth; + break; + } + } +#endif /* CONFIG_IEEE80211BE */ + + if (ctx && ctx != group) return 0; +#ifdef CONFIG_IEEE80211BE + /* For ML STA, run rekey on the association link and send G1 with keys + * for all links. This is based on assumption that MLD level + * Authenticator updates group keys on all affiliated links in one shot + * and not independently or concurrently for separate links. */ + if (sm->mld_assoc_link_id >= 0 && + sm->mld_assoc_link_id != wpa_auth->link_id) + return 0; +#endif /* CONFIG_IEEE80211BE */ + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { - wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, "Not in PTKINITDONE; skip Group Key update"); sm->GUpdateStationKeys = false; @@ -5491,7 +6070,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) * Since we clear the GKeyDoneStations before the loop, the * station needs to be counted here anyway. */ - wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), + wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG, "GUpdateStationKeys was already set when marking station for GTK rekeying"); } @@ -5501,6 +6080,17 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) return 0; sm->group->GKeyDoneStations++; +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) { + struct wpa_authenticator *partner_auth = + sm->mld_links[link_id].wpa_auth; + struct wpa_group *partner_group = + wpa_select_vlan_wpa_group(partner_auth->group, + sm->group->vlan_id); + partner_group->GKeyDoneStations++; + } +#endif /* CONFIG_IEEE80211BE */ + sm->GUpdateStationKeys = true; wpa_sm_step(sm); @@ -5547,7 +6137,7 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) return 0; pos += 8; os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -5586,7 +6176,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) pos += 6; os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len); - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use * of IGTK in the BSS. @@ -5625,14 +6215,6 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos) pos += 6; os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len); - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { - /* - * Provide unique random BIGTK to each STA to prevent use - * of BIGTK in the BSS. - */ - if (random_get_bytes(pos, len) < 0) - return 0; - } pos += len; wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit", @@ -5646,8 +6228,8 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos) #endif /* CONFIG_WNM_AP */ -static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { int tmp; @@ -5663,7 +6245,7 @@ static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, /* "GKeyDoneStations = GNoStations" is done in more robust way by * counting the STAs that are marked with GUpdateStationKeys instead of * including all STAs that could be in not-yet-completed state. */ - wpa_gtk_update(wpa_auth, group); + return wpa_gtk_update(wpa_auth, group); } @@ -5705,6 +6287,8 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, { struct wpa_auth_config *conf = &wpa_auth->conf; int ret = 0; + enum wpa_alg alg; + size_t len; if (wpa_auth_set_key(wpa_auth, group->vlan_id, wpa_cipher_to_alg(conf->wpa_group), @@ -5713,36 +6297,44 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, KEY_FLAG_GROUP_TX_DEFAULT) < 0) ret = -1; - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { - enum wpa_alg alg; - size_t len; + alg = wpa_cipher_to_alg(conf->group_mgmt_cipher); + len = wpa_cipher_key_len(conf->group_mgmt_cipher); - alg = wpa_cipher_to_alg(conf->group_mgmt_cipher); - len = wpa_cipher_key_len(conf->group_mgmt_cipher); + if (wpa_auth_pmf_enabled(conf) && ret == 0 && + wpa_auth_set_key(wpa_auth, group->vlan_id, alg, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], len, + KEY_FLAG_GROUP_TX_DEFAULT) < 0) + ret = -1; - if (ret == 0 && - wpa_auth_set_key(wpa_auth, group->vlan_id, alg, - broadcast_ether_addr, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], len, - KEY_FLAG_GROUP_TX_DEFAULT) < 0) - ret = -1; + /* Skip setting of BIGTK in following cases: + * PMF is not enabled and no beacon protection requirement for any of + * the non TX BSSs in the MBSSID set. + * Groups with a non-zero VLAN ID since only a single BIGTK is shared + * for all VLANs in a BSS. + * If beacon protection is enabled neither in the TX BSS nor in any of + * the non-TX BSS. + */ + if (!wpa_auth->non_tx_beacon_prot && !wpa_auth_pmf_enabled(conf)) + return ret; - if (ret || !conf->beacon_prot) + if (ret || (!conf->beacon_prot && !wpa_auth->non_tx_beacon_prot) || + group->vlan_id) + return ret; + + if (wpa_auth->conf.tx_bss_auth) { + wpa_auth = wpa_auth->conf.tx_bss_auth; + group = wpa_auth->group; + if (!group->bigtk_set || group->bigtk_configured) return ret; - if (wpa_auth->conf.tx_bss_auth) { - wpa_auth = wpa_auth->conf.tx_bss_auth; - group = wpa_auth->group; - if (!group->bigtk_set || group->bigtk_configured) - return ret; - } - if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg, - broadcast_ether_addr, group->GN_bigtk, - group->BIGTK[group->GN_bigtk - 6], len, - KEY_FLAG_GROUP_TX_DEFAULT) < 0) - ret = -1; - else - group->bigtk_configured = true; } + if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg, + broadcast_ether_addr, group->GN_bigtk, + group->BIGTK[group->GN_bigtk - 6], len, + KEY_FLAG_GROUP_TX_DEFAULT) < 0) + ret = -1; + else + group->bigtk_configured = true; return ret; } @@ -5836,12 +6428,16 @@ static void wpa_group_sm_step_links(struct wpa_state_machine *sm) if (!sm || !sm->wpa_auth) return; - wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group); + wpa_group_sm_step(sm->wpa_auth, sm->group); #ifdef CONFIG_IEEE80211BE for_each_sm_auth(sm, link_id) { - wpa_group_sm_step(sm->mld_links[link_id].wpa_auth, - sm->mld_links[link_id].wpa_auth->group); + struct wpa_authenticator *partner_auth = + sm->mld_links[link_id].wpa_auth; + struct wpa_group *partner_group = + wpa_select_vlan_wpa_group(partner_auth->group, + sm->group->vlan_id); + wpa_group_sm_step(partner_auth, partner_group); } #endif /* CONFIG_IEEE80211BE */ } @@ -6269,19 +6865,37 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, size_t pmk_len, const u8 *pmkid, - int akmp) + int akmp, bool is_ml, int vlan_id) { + struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa; + struct rsn_pmksa_cache_entry *entry; + const u8 *aa = wpa_auth->addr; + if (wpa_auth->conf.disable_pmksa_caching) return -1; wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len); if (!akmp) akmp = WPA_KEY_MGMT_SAE; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, - NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp)) - return 0; - return -1; +#ifdef CONFIG_IEEE80211BE + if (is_ml) { + pmksa = wpa_auth->ml_pmksa; + aa = wpa_auth->mld_addr; + } +#endif /* CONFIG_IEEE80211BE */ + + entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, + aa, addr, 0, NULL, akmp); + if (!entry) + return -1; + + entry->sae_vlan_id = vlan_id; +#ifdef CONFIG_IEEE80211BE + entry->is_ml = is_ml; +#endif /* CONFIG_IEEE80211BE */ + + return 0; } @@ -6294,20 +6908,33 @@ void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid) int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, size_t pmk_len, const u8 *pmkid, - int session_timeout, int akmp, const u8 *dpp_pkhash) + int session_timeout, int akmp, const u8 *dpp_pkhash, + bool is_ml) { + struct rsn_pmksa_cache *pmksa; + const u8 *aa; struct rsn_pmksa_cache_entry *entry; if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching) return -1; wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN); - entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, - NULL, 0, wpa_auth->addr, addr, session_timeout, - NULL, akmp); + pmksa = wpa_auth->pmksa; + aa = wpa_auth->addr; +#ifdef CONFIG_IEEE80211BE + if (is_ml) { + pmksa = wpa_auth->ml_pmksa; + aa = wpa_auth->mld_addr; + } +#endif /* CONFIG_IEEE80211BE */ + entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa, + addr, session_timeout, NULL, akmp); if (!entry) return -1; +#ifdef CONFIG_IEEE80211BE + entry->is_ml = is_ml; +#endif /* CONFIG_IEEE80211BE */ if (dpp_pkhash) entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN); @@ -6322,28 +6949,66 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, if (!wpa_auth || !wpa_auth->pmksa) return; + pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); if (pmksa) { wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " MACSTR " based on request", MAC2STR(sta_addr)); pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); } + +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->ml_pmksa) { + pmksa = pmksa_cache_auth_get(wpa_auth->ml_pmksa, + sta_addr, NULL); + if (pmksa) { + wpa_printf(MSG_DEBUG, + "WPA: Remove PMKSA cache entry for " MACSTR + " based on request (MLD)", + MAC2STR(sta_addr)); + pmksa_cache_free_entry(wpa_auth->ml_pmksa, pmksa); + } + } +#endif /* CONFIG_IEEE80211BE */ } int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, size_t len) { + int ret, index; + char *pos = buf, *end = buf + len; + if (!wpa_auth || !wpa_auth->pmksa) return 0; - return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); + + ret = os_snprintf(pos, len, + "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + index = 0; + pos += pmksa_cache_auth_list(wpa_auth->pmksa, pos, end - pos, &index); +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->ml_pmksa) + pos += pmksa_cache_auth_list(wpa_auth->ml_pmksa, + pos, end - pos, &index); +#endif /* CONFIG_IEEE80211BE */ + + return pos - buf; } void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) { - if (wpa_auth && wpa_auth->pmksa) + if (wpa_auth && wpa_auth->pmksa) { pmksa_cache_auth_flush(wpa_auth->pmksa); +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->ml_pmksa && wpa_auth->primary_auth) + pmksa_cache_auth_flush(wpa_auth->ml_pmksa); +#endif /* CONFIG_IEEE80211BE */ + } } @@ -6401,11 +7066,15 @@ int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, struct rsn_pmksa_cache * -wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth) +wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml) { if (!wpa_auth || !wpa_auth->pmksa) return NULL; +#ifdef CONFIG_IEEE80211BE + return is_ml ? wpa_auth->ml_pmksa : wpa_auth->pmksa; +#else /* CONFIG_IEEE80211BE */ return wpa_auth->pmksa; +#endif /* CONFIG_IEEE80211BE */ } @@ -6419,6 +7088,26 @@ wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, } +int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 **pmk, size_t *pmk_len, + const u8 **pmkid) +{ + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = wpa_auth_pmksa_get(wpa_auth, sta_addr, NULL); + if (!pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR, + MAC2STR(sta_addr)); + return -1; + } + + *pmk = pmksa->pmk; + *pmk_len = pmksa->pmk_len; + *pmkid = pmksa->pmkid; + return 0; +} + + void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, struct wpa_state_machine *sm, struct wpa_authenticator *wpa_auth, @@ -6488,6 +7177,18 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_IEEE80211BE +static void wpa_group_put_vlan(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *vlan_group = + wpa_select_vlan_wpa_group(wpa_auth->group, vlan_id); + + wpa_group_put(wpa_auth, vlan_group); +} +#endif /* CONFIG_IEEE80211BE */ + + /* * Add a group that has its references counter set to zero. Caller needs to * call wpa_group_get() on the return value to mark the entry in use. @@ -6605,14 +7306,15 @@ int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) } -int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, int vlan_id) { struct wpa_group *group; if (!sm || !sm->wpa_auth) return 0; - group = sm->wpa_auth->group; + group = wpa_auth->group; while (group) { if (group->vlan_id == vlan_id) break; @@ -6620,11 +7322,19 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) } if (!group) { - group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + group = wpa_auth_add_group(wpa_auth, vlan_id); if (!group) return -1; } +#ifdef CONFIG_IEEE80211BE + if (sm->mld_assoc_link_id >= 0 && + (sm->mld_assoc_link_id != wpa_auth->link_id)) { + wpa_group_get(wpa_auth, group); + return 0; + } +#endif /* CONFIG_IEEE80211BE */ + if (sm->group == group) return 0; @@ -6665,7 +7375,7 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 timeout by %u ms because of acknowledged frame", timeout_ms); - eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_cancel_timeout(wpa_send_eapol_timeout, ELOOP_ALL_CTX, sm); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); @@ -6813,6 +7523,45 @@ void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg) } +void wpa_auth_set_hash_alg_sae_ext_key(struct wpa_state_machine *sm, + size_t pmk_len) +{ + if (!sm) + return; + + if (pmk_len == 32) + sm->hash_alg = RSN_HASH_SHA256; + else if (pmk_len == 48) + sm->hash_alg = RSN_HASH_SHA384; + else if (pmk_len == 64) + sm->hash_alg = RSN_HASH_SHA512; +} + + +void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie, + size_t len) +{ + if (!sm) + return; + os_free(sm->rsn_selection); + sm->rsn_selection = NULL; + sm->rsn_selection_len = 0; + sm->rsn_override = false; + sm->rsn_override_2 = false; + if (ie) { + if (len >= 1) { + if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE) + sm->rsn_override = true; + else if (ie[0] == RSN_SELECTION_RSNE_OVERRIDE_2) + sm->rsn_override_2 = true; + } + sm->rsn_selection = os_memdup(ie, len); + if (sm->rsn_selection) + sm->rsn_selection_len = len; + } +} + + #ifdef CONFIG_DPP2 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) { @@ -7033,8 +7782,8 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | - (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? - WPA_KEY_INFO_MIC : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, + sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, 0, encr); @@ -7100,8 +7849,8 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | - (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? - WPA_KEY_INFO_MIC : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, + sm->hash_alg) ? WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), rsc, NULL, kde, kde_len, gsm->GN, 1); @@ -7216,6 +7965,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, struct wpa_get_link_auth_ctx ctx; sm_link->valid = link->valid; + sm_link->rejected = false; if (!link->valid) continue; @@ -7234,11 +7984,8 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, ctx.wpa_auth = NULL; wpa_auth_for_each_auth(sm->wpa_auth, wpa_get_link_sta_auth, &ctx); - if (ctx.wpa_auth) { + if (ctx.wpa_auth) sm_link->wpa_auth = ctx.wpa_auth; - wpa_group_get(sm_link->wpa_auth, - sm_link->wpa_auth->group); - } } else { sm_link->wpa_auth = sm->wpa_auth; } @@ -7252,3 +7999,313 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, } #endif /* CONFIG_IEEE80211BE */ } + + +bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm, + const u8 *timestamp, + const u8 *mic, size_t mic_len) +{ + size_t exp_mic_len; + u8 exp_mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + int ver; + + if (!sm) + return false; + + if (!sm->PTK_valid || !mic_len || sm->PTK.kck_len == 0) { + wpa_printf(MSG_DEBUG, + "RSN: No KCK to verify Known STA Identification"); + return false; + } + + exp_mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len, sm->hash_alg); + if (mic_len != exp_mic_len) { + wpa_printf(MSG_DEBUG, + "RSN: MIC length mismatch in Known STA Identification (received %zu, expected %zu)", + mic_len, exp_mic_len); + return false; + } + + if (wpa_use_akm_defined(sm->wpa_key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; + else if (wpa_use_cmac(sm->wpa_key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt, + sm->hash_alg, ver, timestamp, 8, exp_mic) || + os_memcmp_const(mic, exp_mic, exp_mic_len) != 0) { + wpa_printf(MSG_DEBUG, + "RSN: Invalid MIC in Known STA Identification"); + return false; + } + + return true; +} + + +#ifdef CONFIG_ENC_ASSOC + +u8 * wpa_auth_eid_key_delivery(u8 *eid, size_t max_len, + struct wpa_state_machine *sm, bool is_ml) +{ + size_t gtk_len, kde_len = 0; + u8 rsc[WPA_KEY_RSC_LEN] = {0}, *gtk; + struct wpa_group *gsm = sm->group; + u8 hdr[2]; + u8 *kde, *buf; + const u8 *ptr; + size_t slice_len; + size_t buflen = 1024; + + buflen += 2 + 255; /* extra room for SAE PW IDs KDE */ + + /* TODO: Make sure there is sufficient length for the element */ + buf = os_malloc(buflen); + if (!buf) + return eid; + + ptr = buf; + kde = buf; + if (is_ml) { +#ifdef CONFIG_IEEE80211BE + kde = wpa_auth_ml_group_kdes(sm, kde, KDE_ALL_LINKS); + kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS); +#endif /* CONFIG_IEEE80211BE */ + } else { + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + hdr[0] = gsm->GN & 0x03; /* KeyID bits */ + hdr[1] = 0; + kde = wpa_add_kde(kde, RSN_KEY_DATA_GROUPKEY, hdr, sizeof(hdr), + gtk, gtk_len); + kde = ieee80211w_kde_add(sm, kde); + /* GTK KDE: 0xdd|len(1B)|RSN Selector(4B)|KeyID(2B)|GTK| */ + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + } + +#ifdef CONFIG_SAE + /* For EPPKE, the 4-way handshake is skipped, so deliver the SAE + * Password Identifiers KDE here in the encrypted (Re)Association + * Response frame. */ + if (sm->auth_alg == WLAN_AUTH_EPPKE && + wpa_key_mgmt_sae(sm->wpa_key_mgmt) && + sm->wpa_auth->conf.sae_pw_id_num && + sm->sae_pw_id && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE)) { + u8 *new_kde = add_sae_pw_ids(sm, kde, buf + buflen); + + if (new_kde) { + kde_len += new_kde - kde; + kde = new_kde; + } + } +#endif /* CONFIG_SAE */ + + if (!is_ml && sm->group->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + + /* + * As the Key Delivery element can exceed the size of 255 bytes need to + * handle fragmentation. + */ + slice_len = kde_len <= 246 ? kde_len : 246; + /* ElementID(0xff)|Length(1B)|ElementID Extn(1B)|RSC(8B)|KDE list */ + *eid++ = WLAN_EID_EXTENSION; + *eid++ = slice_len + 1 + WPA_KEY_RSC_LEN; + *eid++ = WLAN_EID_EXT_KEY_DELIVERY; + os_memcpy(eid, rsc, WPA_KEY_RSC_LEN); + eid += WPA_KEY_RSC_LEN; + + os_memcpy(eid, ptr, slice_len); + + ptr += slice_len; + eid += slice_len; + kde_len -= slice_len; + + while (kde_len) { + slice_len = kde_len <= 255 ? kde_len : 255; + *eid++ = WLAN_EID_FRAGMENT; + *eid++ = slice_len; + os_memcpy(eid, ptr, slice_len); + + ptr += slice_len; + eid += slice_len; + kde_len -= slice_len; + } + + bin_clear_free(buf, buflen); + return eid; +} + + +bool wpa_auth_ap_sta_support_assoc_enc(struct wpa_state_machine *sm) +{ + if (!sm) + return false; + + return sm->wpa_auth->conf.assoc_frame_encryption && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); +} + + +void wpa_auth_ensure_group_init(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + + if (!sm) + return; + + wpa_group_ensure_init(sm->wpa_auth, sm->group); +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + wpa_group_ensure_init(sm->mld_links[link_id].wpa_auth, + sm->mld_links[link_id].wpa_auth->group); +#endif /* CONFIG_IEEE80211BE */ + + return; +} + + +u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, bool is_ml) +{ + int res; + u8 *end = pos + max_len; + + if (!sm) + return pos; + + res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL); + if (res < 0) + return pos; + pos += res; + pos = wpa_auth_eid_key_delivery(pos, end - pos, sm, is_ml); + + return pos; +} + +#endif /* CONFIG_ENC_ASSOC */ + + +#ifdef CONFIG_PMKSA_PRIVACY + +int wpa_auth_epp_derive_new_pmkid(const u8 *anonce, const u8 *snonce, + int akmp, size_t pmk_len, u8 *pmkid) +{ + return rsn_pmkid_privacy(anonce, snonce, akmp, pmk_len, pmkid); +} + + +bool wpa_auth_ap_sta_support_pmkid_privacy(struct wpa_state_machine *sm) +{ + struct wpa_auth_config *conf; + + if (!sm) + return false; + + conf = &sm->wpa_auth->conf; + + return conf->pmksa_caching_privacy && + ieee802_11_rsnx_capab(sm->rsnxe, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); +} + +#endif /* CONFIG_PMKSA_PRIVACY */ + + +void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm, + struct wpa_authenticator *wpa_auth, + u8 mld_assoc_link_id) +{ +#ifdef CONFIG_IEEE80211BE + assoc_sm->wpa_auth = wpa_auth; + assoc_sm->mld_assoc_link_id = mld_assoc_link_id; +#endif /* CONFIG_IEEE80211BE */ +} + + +/* wpa_select_vlan_wpa_group - Traverse through the wpa_group list and select + * the one that matches the vlan_id. + * + * @gsm: Head of wpa_group list + * @vlan_id: vlan_id used to search the group key state machine data that + * corresponds to the specified VLAN group + * Returns: Pointer to wpa_group that corresponds to the VLAN group on success, + * or pointer to the head of wpa_group list that was passed in. + */ +struct wpa_group * wpa_select_vlan_wpa_group(struct wpa_group *gsm, int vlan_id) +{ + struct wpa_group *vlan_gsm = gsm; + + while (vlan_gsm) { + if (vlan_gsm->vlan_id == vlan_id) + break; + + vlan_gsm = vlan_gsm->next; + } + + if (!vlan_gsm) { + wpa_printf(MSG_DEBUG, "%s: VLAN group not found", __func__); + vlan_gsm = gsm; + } + + return vlan_gsm; +} + + +void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm, + const struct wpabuf *pw_id, + unsigned int counter) +{ + if (sm) { + wpabuf_free(sm->sae_pw_id); + sm->sae_pw_id = wpabuf_dup(pw_id); + sm->sae_pw_id_counter = counter; + } +} + + +bool wpa_auth_get_first_sta_seen(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *group; + + if (!wpa_auth) + return false; + + group = wpa_select_vlan_wpa_group(wpa_auth->group, vlan_id); + return group->first_sta_seen; +} + +#ifdef CONFIG_IEEE8021X_AUTH +int wpa_auth_802_1x_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len) +{ + return wpa_auth_get_msk(wpa_auth, addr, msk, len); +} + + +int wpa_auth_802_1x_set_key(struct wpa_authenticator *wpa_auth, + enum wpa_alg alg, const u8 *addr, + u8 *key, size_t key_len) +{ + + return wpa_auth_set_key(wpa_auth, 0, alg, addr, 0, key, + key_len, KEY_FLAG_PAIRWISE_RX_TX); + +} + +bool wpa_auth_ap_support_secure_ltf(struct wpa_authenticator *wpa_auth) +{ + return wpa_auth->conf.secure_ltf; +} +#endif /* CONFIG_IEEE8021X_AUTH */ diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h index b588f9fdcd78..af354b9cd970 100644 --- a/contrib/wpa/src/ap/wpa_auth.h +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -17,7 +17,7 @@ struct vlan_description; struct mld_info; -#define MAX_OWN_IE_OVERRIDE 256 +#define MAX_OWN_IE_OVERRIDE 257 #ifdef _MSC_VER #pragma pack(push, 1) @@ -173,6 +173,8 @@ struct wpa_auth_config { int wpa; int extended_key_id; int wpa_key_mgmt; + int rsn_override_key_mgmt; + int rsn_override_key_mgmt_2; int wpa_pairwise; int wpa_group; int wpa_group_rekey; @@ -184,6 +186,8 @@ struct wpa_auth_config { u32 wpa_pairwise_update_count; int wpa_disable_eapol_key_retries; int rsn_pairwise; + int rsn_override_pairwise; + int rsn_override_pairwise_2; int rsn_preauth; int eapol_version; int wmm_enabled; @@ -192,6 +196,8 @@ struct wpa_auth_config { int okc; int tx_status; enum mfp_options ieee80211w; + enum mfp_options rsn_override_mfp; + enum mfp_options rsn_override_mfp_2; int beacon_prot; int group_mgmt_cipher; int sae_require_mfp; @@ -224,6 +230,21 @@ struct wpa_auth_config { double corrupt_gtk_rekey_mic_probability; u8 own_ie_override[MAX_OWN_IE_OVERRIDE]; size_t own_ie_override_len; + bool rsne_override_set; + u8 rsne_override[MAX_OWN_IE_OVERRIDE]; + size_t rsne_override_len; + bool rsnoe_override_set; + u8 rsnoe_override[MAX_OWN_IE_OVERRIDE]; + size_t rsnoe_override_len; + bool rsno2e_override_set; + u8 rsno2e_override[MAX_OWN_IE_OVERRIDE]; + size_t rsno2e_override_len; + bool rsnxe_override_set; + u8 rsnxe_override[MAX_OWN_IE_OVERRIDE]; + size_t rsnxe_override_len; + bool rsnxoe_override_set; + u8 rsnxoe_override[MAX_OWN_IE_OVERRIDE]; + size_t rsnxoe_override_len; u8 rsne_override_eapol[MAX_OWN_IE_OVERRIDE]; size_t rsne_override_eapol_len; u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE]; @@ -245,6 +266,7 @@ struct wpa_auth_config { struct wpabuf *eapol_m1_elements; struct wpabuf *eapol_m3_elements; bool eapol_m3_no_encrypt; + bool eapol_key_reserved_random; #endif /* CONFIG_TESTING_OPTIONS */ unsigned int oci_freq_override_eapol_m3; unsigned int oci_freq_override_eapol_g1; @@ -262,10 +284,21 @@ struct wpa_auth_config { #endif /* CONFIG_FILS */ enum sae_pwe sae_pwe; bool sae_pk; + bool urnm_mfpr_x20; + bool urnm_mfpr; unsigned int secure_ltf:1; unsigned int secure_rtt:1; unsigned int prot_range_neg:1; +#ifdef CONFIG_ENC_ASSOC + unsigned int assoc_frame_encryption:1; + unsigned int pmksa_caching_privacy:1; + unsigned int eap_using_authentication_frames:1; +#ifdef CONFIG_PASN + /* Whether to allow unauthenticated EPPKE (EPPKE without base AKM) */ + bool eppke_unauth; +#endif /* CONFIG_PASN */ +#endif /* CONFIG_ENC_ASSOC */ int owe_ptk_workaround; u8 transition_disable; @@ -295,6 +328,14 @@ struct wpa_auth_config { #endif /* CONFIG_IEEE80211BE */ bool ssid_protection; + + int rsn_override_omit_rsnxe; + int rsn_override_mlo_compat; + + bool spp_amsdu; + + unsigned int sae_pw_id_num; + u8 sae_pw_id_key[32]; }; typedef enum { @@ -355,6 +396,12 @@ struct wpa_auth_callbacks { void *ctx), void *cb_ctx); int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); +#ifdef CONFIG_IEEE80211BE + int (*for_each_partner_auth)(void *ctx, + int (*cb)(struct wpa_authenticator *a, + void *ctx), + void *cb_ctx); +#endif /* CONFIG_IEEE80211BE */ int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, @@ -389,8 +436,6 @@ struct wpa_auth_callbacks { int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); - int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, - size_t tspec_ielen); #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); @@ -400,9 +445,13 @@ struct wpa_auth_callbacks { size_t ltf_keyseed_len); #endif /* CONFIG_PASN */ #ifdef CONFIG_IEEE80211BE - int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info); + int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info, + bool rekey, int vlan_id); + struct wpa_authenticator * (*next_primary_auth)(void *ctx); #endif /* CONFIG_IEEE80211BE */ int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2); + int (*remove_pmkid)(void *ctx, const u8 *sta_addr, const u8 *pmkid); + bool (*first_sta_seen_mbssid)(void *ctx, int vlan_id); }; struct wpa_authenticator * wpa_init(const u8 *addr, @@ -429,11 +478,13 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 *rsnxe, size_t rsnxe_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len, - struct wpa_state_machine *assoc_sm); + struct wpa_state_machine *assoc_sm, + bool is_ml); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm); void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv); int wpa_auth_uses_ocv(struct wpa_state_machine *sm); struct wpa_state_machine * @@ -462,6 +513,8 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm); const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len); const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +bool wpa_auth_get_first_sta_seen(struct wpa_authenticator *wpa_auth, + int vlan_id); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm); @@ -481,11 +534,12 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, size_t pmk_len, const u8 *pmkid, - int akmp); + int akmp, bool is_ml, int vlan_id); void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, size_t pmk_len, const u8 *pmkid, - int session_timeout, int akmp, const u8 *dpp_pkhash); + int session_timeout, int akmp, const u8 *dpp_pkhash, + bool is_ml); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, @@ -500,10 +554,13 @@ wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache * -wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth); +wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth, bool is_ml); struct rsn_pmksa_cache_entry * wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, const u8 *pmkid); +int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 **pmk, size_t *pmk_len, + const u8 **pmkid); struct rsn_pmksa_cache_entry * wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, const u8 *pmkid); @@ -511,15 +568,17 @@ void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, struct wpa_state_machine *sm, struct wpa_authenticator *wpa_auth, u8 *pmkid, u8 *pmk, size_t *pmk_len); -int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); +bool wpa_auth_ap_sta_support_pmkid_privacy(struct wpa_state_machine *sm); #ifdef CONFIG_IEEE80211R_AP u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len, - int omit_rsnxe); + int omit_rsnxe, bool reassoc, int vlan_id); void wpa_ft_process_auth(struct wpa_state_machine *sm, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, @@ -597,19 +656,34 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, size_t pmk_len, const u8 *pmkid); u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, - u8 *pos, size_t max_len, - const u8 *req_ies, size_t req_ies_len); + u8 *pos, size_t max_len); u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, - u8 *pos, size_t max_len, - const u8 *req_ies, size_t req_ies_len); + u8 *pos, size_t max_len); bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth, u8 *fd_rsn_info); void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); +void wpa_auth_set_hash_alg_sae_ext_key(struct wpa_state_machine *sm, + size_t pmk_len); +void wpa_auth_set_rsn_selection(struct wpa_state_machine *sm, const u8 *ie, + size_t len); void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val); void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth, u8 val); +u8 * wpa_auth_eid_key_delivery(u8 *eid, size_t max_len, + struct wpa_state_machine *sm, + bool is_ml); +u8 * wpa_auth_write_assoc_resp_eppke(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, bool is_ml); +void wpa_store_eppke_pmk_ptk_sm(struct wpa_state_machine *sm, + const struct wpa_ptk *ptk, const u8 *pmk, + size_t pmk_len); +int wpa_auth_epp_derive_new_pmkid(const u8 *anonce, const u8 *snonce, + int akmp, size_t pmk_len, u8 *pmkid); +bool wpa_auth_ap_sta_support_assoc_enc(struct wpa_state_machine *sm); +void wpa_auth_ensure_group_init(struct wpa_state_machine *sm); + int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, void (*cb)(void *ctx1, void *ctx2), void *ctx1, void *ctx2); @@ -644,10 +718,18 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm, u8 mld_assoc_link_id, struct mld_info *info); void wpa_auth_ml_get_key_info(struct wpa_authenticator *a, struct wpa_auth_ml_link_key_info *info, - bool mgmt_frame_prot, bool beacon_prot); + bool mgmt_frame_prot, bool beacon_prot, + bool rekey, int vlan_id); -void wpa_release_link_auth_ref(struct wpa_state_machine *sm, - int release_link_id); +void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id, + bool rejected); + +size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links); +u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos, + u16 req_links); +void wpa_reset_assoc_sm_info(struct wpa_state_machine *assoc_sm, + struct wpa_authenticator *wpa_auth, + u8 mld_assoc_link_id); #define for_each_sm_auth(sm, link_id) \ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \ @@ -655,4 +737,35 @@ void wpa_release_link_auth_ref(struct wpa_state_machine *sm, sm->mld_links[link_id].wpa_auth && \ sm->wpa_auth != sm->mld_links[link_id].wpa_auth) +static inline bool wpa_auth_pmf_enabled(struct wpa_auth_config *conf) +{ + return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION || + conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION || + conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION; +} + +bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm, + const u8 *timestamp, + const u8 *mic, size_t mic_len); +struct wpa_group * wpa_select_vlan_wpa_group(struct wpa_group *gsm, + int vlan_id); +void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm, + const struct wpabuf *pw_id, + unsigned int counter); + +int wpa_auth_802_1x_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len); +int wpa_auth_802_1x_set_key(struct wpa_authenticator *wpa_auth, + enum wpa_alg alg, const u8 *addr, + u8 *key, size_t key_len); +bool wpa_auth_ap_support_secure_ltf(struct wpa_authenticator *wpa_auth); +int wpa_write_802_1x_rsne(struct wpa_authenticator *wpa_auth, u8 *buf, + size_t len, const u8 *pmkid, int akmp, + int pairwise_cipher, int group_cipher, + int group_mgmt_cipher, enum mfp_options mfp); +int wpa_write_eppke_rsne(const u8 *wpa_ie, size_t wpa_ie_len, + u8 *buf, size_t len, + const u8 *pmkid, int akmp, + int pairwise_cipher, enum mfp_options mfp); + #endif /* WPA_AUTH_H */ diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c index de16c314de8e..76b14ddd35cc 100644 --- a/contrib/wpa/src/ap/wpa_auth_ft.c +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -765,19 +765,6 @@ wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth, } -static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, - const u8 *sta_addr, - u8 *tspec_ie, size_t tspec_ielen) -{ - if (wpa_auth->cb->add_tspec == NULL) { - wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); - return -1; - } - return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie, - tspec_ielen); -} - - #ifdef CONFIG_OCV static int wpa_channel_info(struct wpa_authenticator *wpa_auth, struct wpa_channel_info *ci) @@ -2212,7 +2199,8 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, } -static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len, + bool reassoc, int vlan_id) { u8 *subelem; struct wpa_auth_config *conf = &sm->wpa_auth->conf; @@ -2224,6 +2212,11 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) const u8 *kek; size_t kek_len; +#ifdef CONFIG_IEEE80211BE + if (reassoc && vlan_id) + gsm = wpa_select_vlan_wpa_group(gsm, vlan_id); +#endif /* CONFIG_IEEE80211BE */ + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { kek = sm->PTK.kek2; kek_len = sm->PTK.kek2_len; @@ -2247,8 +2240,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) pad_len += 8; if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); - if (conf->disable_gtk || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -2260,7 +2252,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) keybuf[key_len] = 0xdd; key_len += pad_len; key = keybuf; - } else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + } else if (conf->disable_gtk) { /* * Provide unique random GTK to each STA to prevent use of GTK * in the BSS. @@ -2339,7 +2331,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) pos += 6; *pos++ = igtk_len; igtk = gsm->IGTK[gsm->GN_igtk - 4]; - if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + if (conf->disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. @@ -2372,7 +2364,6 @@ static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len) const u8 *kek, *bigtk; size_t kek_len; size_t bigtk_len; - u8 stub_bigtk[WPA_IGTK_MAX_LEN]; if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { kek = sm->PTK.kek2; @@ -2400,17 +2391,6 @@ static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len) pos += 6; *pos++ = bigtk_len; bigtk = gsm->BIGTK[gsm->GN_bigtk - 6]; - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { - /* - * Provide unique random BIGTK to each OSEN STA to prevent use - * of BIGTK in the BSS. - */ - if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) { - os_free(subelem); - return NULL; - } - bigtk = stub_bigtk; - } if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) { wpa_printf(MSG_DEBUG, "FT: BIGTK subelem encryption failed: kek_len=%d", @@ -2499,24 +2479,6 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, } #endif /* NEED_AP_MLME */ - if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { - int res; - - res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, - sizeof(struct wmm_tspec_element)); - if (res >= 0) { - if (res) - rdie->status_code = host_to_le16(res); - else { - /* TSPEC accepted; include updated TSPEC in - * response */ - rdie->descr_count = 1; - pos += sizeof(struct wmm_tspec_element); - } - return pos; - } - } - wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); return pos; @@ -2558,7 +2520,7 @@ static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end, u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len, - int omit_rsnxe) + int omit_rsnxe, bool reassoc, int vlan_id) { u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; u8 *fte_mic, *elem_count; @@ -2648,7 +2610,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, /* Fast BSS Transition Information */ if (auth_alg == WLAN_AUTH_FT) { - subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + subelem = wpa_ft_gtk_subelem(sm, &subelem_len, reassoc, + vlan_id); if (!subelem) { wpa_printf(MSG_DEBUG, "FT: Failed to add GTK subelement"); @@ -3204,19 +3167,19 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, sm->wpa_auth->conf.mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); - retval = WLAN_STATUS_INVALID_MDIE; + retval = WLAN_STATUS_INVALID_MDE; goto out; } if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3314,6 +3277,25 @@ pmk_r1_derived: os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len); sm->pmk_r1_len = pmk_r1_len; + /* + * Make sure the pairwise suite indicate by the STA matches the one that + * was used during initial mobility domain association. + */ + if (!(parse.pairwise_cipher & pairwise)) { + wpa_printf(MSG_ERROR, + "FT: Pairwise cipher from PMK-R1 cache (0x%x) not used in the RSNE (0x%x) - reject", + pairwise, parse.pairwise_cipher); + retval = WLAN_STATUS_INVALID_PAIRWISE_CIPHER; + goto out; + } + + if (sm->pmk_r1_len == SHA512_MAC_LEN) + sm->hash_alg = RSN_HASH_SHA512; + else if (sm->pmk_r1_len == SHA384_MAC_LEN) + sm->hash_alg = RSN_HASH_SHA384; + else + sm->hash_alg = RSN_HASH_SHA256; + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); @@ -3328,7 +3310,7 @@ pmk_r1_derived: ftie = (const struct rsn_ftie_sha512 *) parse.ftie; if (!ftie || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3339,7 +3321,7 @@ pmk_r1_derived: ftie = (const struct rsn_ftie_sha384 *) parse.ftie; if (!ftie || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3350,7 +3332,7 @@ pmk_r1_derived: ftie = (const struct rsn_ftie *) parse.ftie; if (!ftie || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3536,7 +3518,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, os_memcmp(mdie->mobility_domain, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); - retval = WLAN_STATUS_INVALID_MDIE; + retval = WLAN_STATUS_INVALID_MDE; goto out; } @@ -3555,7 +3537,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, wpa_printf(MSG_DEBUG, "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)", parse.fte_mic_len, mic_len); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3565,7 +3547,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.fte_snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3575,13 +3557,13 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.fte_anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3594,13 +3576,13 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3612,7 +3594,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", conf->r1_key_holder, FT_R1KH_ID_LEN); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3674,7 +3656,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE", parse.rsnxe ? parse.rsnxe - 2 : NULL, parse.rsnxe ? parse.rsnxe_len + 2 : 0); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } @@ -3721,7 +3703,7 @@ int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, OCV_FAILURE "addr=" MACSTR " frame=ft-reassoc-req error=%s", MAC2STR(sm->addr), ocv_errorstr); - retval = WLAN_STATUS_INVALID_FTIE; + retval = WLAN_STATUS_INVALID_FTE; goto out; } } @@ -3818,7 +3800,7 @@ static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR, MAC2STR(sm->addr)); wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr, - WLAN_STATUS_SUCCESS, ies, ies_len); + resp, ies, ies_len); } diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c index e725615d932c..ba645d525ea0 100644 --- a/contrib/wpa/src/ap/wpa_auth_glue.c +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -35,7 +35,82 @@ #include "wpa_auth_glue.h" -static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, +static void hostapd_wpa_auth_config_update(struct hostapd_data *hapd, + struct wpa_auth_config *_conf) +{ + struct hostapd_data *tx_bss; + + _conf->msg_ctx = hapd->msg_ctx; + tx_bss = hostapd_mbssid_get_tx_bss(hapd); + if (tx_bss != hapd) + _conf->tx_bss_auth = tx_bss->wpa_auth; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) + _conf->tx_status = 1; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) + _conf->ap_mlme = 1; + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) && + (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || + (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + "Disable PTK0 rekey support - replaced with disconnect"); + _conf->wpa_deny_ptk0_rekey = 1; + } + + if (_conf->extended_key_id && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported"); + else + _conf->extended_key_id = 0; + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) + _conf->beacon_prot = 0; + +#ifdef CONFIG_OCV + if (!(hapd->iface->drv_flags2 & + (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) + _conf->ocv = 0; +#endif /* CONFIG_OCV */ + + _conf->secure_ltf = + !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP); + _conf->secure_rtt = + !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP); + _conf->prot_range_neg = + !!(hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP); + +#ifdef CONFIG_ENC_ASSOC + if (!(hapd->iface->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) + _conf->assoc_frame_encryption = 0; +#endif /* CONFIG_ENC_ASSOC */ + +#ifdef CONFIG_IEEE80211BE + _conf->mld_addr = NULL; + _conf->link_id = -1; + _conf->first_link_auth = NULL; + + if (hapd->conf->mld_ap) { + struct hostapd_data *lhapd; + + _conf->mld_addr = hapd->mld->mld_addr; + _conf->link_id = hapd->mld_link_id; + + for_each_mld_link(lhapd, hapd) { + if (lhapd == hapd) + continue; + + if (lhapd->wpa_auth) + _conf->first_link_auth = lhapd->wpa_auth; + } + } +#endif /* CONFIG_IEEE80211BE */ +} + +static void hostapd_wpa_auth_conf(struct hostapd_iface *iface, + struct hostapd_bss_config *conf, struct hostapd_config *iconf, struct wpa_auth_config *wconf) { @@ -43,8 +118,16 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, os_memset(wconf, 0, sizeof(*wconf)); wconf->wpa = conf->wpa; +#ifdef CONFIG_ENC_ASSOC + wconf->assoc_frame_encryption = conf->assoc_frame_encryption; + wconf->pmksa_caching_privacy = conf->pmksa_caching_privacy; + wconf->eap_using_authentication_frames = + conf->eap_using_authentication_frames; +#endif /* CONFIG_ENC_ASSOC */ wconf->extended_key_id = conf->extended_key_id; wconf->wpa_key_mgmt = conf->wpa_key_mgmt; + wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt; + wconf->rsn_override_key_mgmt_2 = conf->rsn_override_key_mgmt_2; wconf->wpa_pairwise = conf->wpa_pairwise; wconf->wpa_group = conf->wpa_group; wconf->wpa_group_rekey = conf->wpa_group_rekey; @@ -56,6 +139,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, conf->wpa_disable_eapol_key_retries; wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; + wconf->rsn_override_pairwise = conf->rsn_override_pairwise; + wconf->rsn_override_pairwise_2 = conf->rsn_override_pairwise_2; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; #ifdef CONFIG_MACSEC @@ -70,6 +155,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_OCV */ wconf->okc = conf->okc; wconf->ieee80211w = conf->ieee80211w; + wconf->rsn_override_mfp = conf->rsn_override_mfp; + wconf->rsn_override_mfp_2 = conf->rsn_override_mfp_2; wconf->beacon_prot = conf->beacon_prot; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; wconf->sae_require_mfp = conf->sae_require_mfp; @@ -103,17 +190,6 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; - if (conf->osen) { - wconf->disable_gtk = 1; - wconf->wpa = WPA_PROTO_OSEN; - wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; - wconf->wpa_pairwise = 0; - wconf->wpa_group = WPA_CIPHER_CCMP; - wconf->rsn_pairwise = WPA_CIPHER_CCMP; - wconf->rsn_preauth = 0; - wconf->disable_pmksa_caching = 1; - wconf->ieee80211w = 1; - } #endif /* CONFIG_HS20 */ #ifdef CONFIG_TESTING_OPTIONS wconf->corrupt_gtk_rekey_mic_probability = @@ -126,6 +202,46 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wpabuf_head(conf->own_ie_override), wconf->own_ie_override_len); } + if (conf->rsne_override && + wpabuf_len(conf->rsne_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->rsne_override_len = wpabuf_len(conf->rsne_override); + os_memcpy(wconf->rsne_override, + wpabuf_head(conf->rsne_override), + wconf->rsne_override_len); + wconf->rsne_override_set = true; + } + if (conf->rsnoe_override && + wpabuf_len(conf->rsnoe_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->rsnoe_override_len = wpabuf_len(conf->rsnoe_override); + os_memcpy(wconf->rsnoe_override, + wpabuf_head(conf->rsnoe_override), + wconf->rsnoe_override_len); + wconf->rsnoe_override_set = true; + } + if (conf->rsno2e_override && + wpabuf_len(conf->rsno2e_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->rsno2e_override_len = wpabuf_len(conf->rsno2e_override); + os_memcpy(wconf->rsno2e_override, + wpabuf_head(conf->rsno2e_override), + wconf->rsno2e_override_len); + wconf->rsno2e_override_set = true; + } + if (conf->rsnxe_override && + wpabuf_len(conf->rsnxe_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->rsnxe_override_len = wpabuf_len(conf->rsnxe_override); + os_memcpy(wconf->rsnxe_override, + wpabuf_head(conf->rsnxe_override), + wconf->rsnxe_override_len); + wconf->rsnxe_override_set = true; + } + if (conf->rsnxoe_override && + wpabuf_len(conf->rsnxoe_override) <= MAX_OWN_IE_OVERRIDE) { + wconf->rsnxoe_override_len = wpabuf_len(conf->rsnxoe_override); + os_memcpy(wconf->rsnxoe_override, + wpabuf_head(conf->rsnxoe_override), + wconf->rsnxoe_override_len); + wconf->rsnxoe_override_set = true; + } if (conf->rsne_override_eapol && wpabuf_len(conf->rsne_override_eapol) <= MAX_OWN_IE_OVERRIDE) { wconf->rsne_override_eapol_set = 1; @@ -190,6 +306,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, if (conf->eapol_m3_elements) wconf->eapol_m3_elements = wpabuf_dup(conf->eapol_m3_elements); wconf->eapol_m3_no_encrypt = conf->eapol_m3_no_encrypt; + wconf->eapol_key_reserved_random = conf->eapol_key_reserved_random; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_P2P os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); @@ -219,15 +336,35 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->dpp_pfs = conf->dpp_pfs; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_PASN +#ifdef CONFIG_ENC_ASSOC + wconf->eppke_unauth = conf->eppke_unauth; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_TESTING_OPTIONS wconf->force_kdk_derivation = conf->force_kdk_derivation; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ + if (conf->urnm_mfpr_x20 == 1) + wconf->urnm_mfpr_x20 = true; + if (conf->urnm_mfpr != 0) + wconf->urnm_mfpr = true; wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS; wconf->no_disconnect_on_group_keyerror = conf->bss_max_idle && conf->ap_max_inactivity && conf->no_disconnect_on_group_keyerror; + + wconf->rsn_override_omit_rsnxe = conf->rsn_override_omit_rsnxe; + wconf->rsn_override_mlo_compat = conf->rsn_override_mlo_compat; + wconf->spp_amsdu = conf->spp_amsdu && + (iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU); + + if (conf->sae_pw_id_num && conf->sae_pw_id_key && + wpabuf_len(conf->sae_pw_id_key) == sizeof(wconf->sae_pw_id_key)) { + wconf->sae_pw_id_num = conf->sae_pw_id_num; + os_memcpy(wconf->sae_pw_id_key, + wpabuf_head(conf->sae_pw_id_key), + wpabuf_len(conf->sae_pw_id_key)); + } } @@ -371,13 +508,17 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, #endif /* CONFIG_SAE */ #ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & + WPA_KEY_MGMT_OWE) && sta && sta->owe_pmk) { if (psk_len) *psk_len = sta->owe_pmk_len; return sta->owe_pmk; } - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { + if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt | + hapd->conf->rsn_override_key_mgmt_2) & + WPA_KEY_MGMT_OWE) && sta) { struct rsn_pmksa_cache_entry *sa; sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); @@ -459,10 +600,20 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, size_t key_len, enum key_flag key_flag) { struct hostapd_data *hapd = ctx; + struct hostapd_data *link_bss = hapd; const char *ifname = hapd->conf->iface; + int set_tx = !(key_flag & KEY_FLAG_NEXT); +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + link_bss = hostapd_mld_get_first_bss(hapd); + if (!link_bss) + link_bss = hapd; + } +#endif /* CONFIG_IEEE80211BE */ if (vlan_id > 0) { - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + ifname = hostapd_get_vlan_id_ifname(link_bss->conf->vlan, + vlan_id); if (!ifname) { if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) @@ -512,8 +663,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, hapd->last_gtk_len = key_len; } #endif /* CONFIG_TESTING_OPTIONS */ - return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1, - NULL, 0, key, key_len, key_flag); + return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, + set_tx, NULL, 0, key, key_len, key_flag); } @@ -632,6 +783,29 @@ static int hostapd_wpa_auth_for_each_auth( } +#ifdef CONFIG_IEEE80211BE +static int hostapd_wpa_auth_for_each_partner_auth( + void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *hapd = ctx; + struct hostapd_data *bss; + + if (cb(hapd->wpa_auth, cb_ctx)) + return 1; + + if (!hapd->mld) + return 0; + + for_each_mld_link(bss, hapd) { + if (bss != hapd && bss->wpa_auth && cb(bss->wpa_auth, cb_ctx)) + return 1; + } + return 0; +} +#endif /* CONFIG_IEEE80211BE */ + + #ifdef CONFIG_IEEE80211R_AP struct wpa_ft_rrb_rx_later_data { @@ -972,7 +1146,7 @@ static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher, struct hostapd_data *hapd = ctx; ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time, - ptk, NULL, NULL, 0); + ptk, NULL, NULL, 0, 0); } @@ -1441,15 +1615,6 @@ static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, } -static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, - u8 *tspec_ie, size_t tspec_ielen) -{ - struct hostapd_data *hapd = ctx; - return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); -} - - - static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, const char *ft_iface) { @@ -1535,10 +1700,31 @@ static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr, #endif /* CONFIG_PASN */ +static bool hostapd_first_sta_seen_mbssid(void *ctx, int vlan_id) +{ + struct hostapd_data *hapd = ctx; + + if (hapd->iconf->mbssid) { + size_t i; + + for (i = 0; i < hapd->iface->num_bss; i++) { + struct hostapd_data *tmp_hapd = hapd->iface->bss[i]; + + if (wpa_auth_get_first_sta_seen(tmp_hapd->wpa_auth, + vlan_id)) + return true; + } + } + + return false; +} + + #ifdef CONFIG_IEEE80211BE static int hostapd_wpa_auth_get_ml_key_info(void *ctx, - struct wpa_auth_ml_key_info *info) + struct wpa_auth_ml_key_info *info, + bool rekey, int vlan_id) { struct hostapd_data *hapd = ctx; unsigned int i; @@ -1562,7 +1748,8 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx, wpa_auth_ml_get_key_info(hapd->wpa_auth, &info->links[i], info->mgmt_frame_prot, - info->beacon_prot); + info->beacon_prot, + rekey, vlan_id); continue; } @@ -1573,7 +1760,8 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx, wpa_auth_ml_get_key_info(bss->wpa_auth, &info->links[i], info->mgmt_frame_prot, - info->beacon_prot); + info->beacon_prot, + rekey, vlan_id); link_bss_found = true; break; } @@ -1586,6 +1774,21 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx, return 0; } + +static struct wpa_authenticator * hostapd_next_primary_auth(void *cb_ctx) +{ + struct hostapd_data *hapd = cb_ctx, *bss; + + for_each_mld_link(bss, hapd) { + if (bss == hapd) + continue; + if (bss->wpa_auth) + return bss->wpa_auth; + } + + return NULL; +} + #endif /* CONFIG_IEEE80211BE */ @@ -1603,6 +1806,15 @@ static int hostapd_wpa_auth_get_drv_flags(void *ctx, } +static int hostapd_wpa_auth_remove_pmkid(void *ctx, const u8 *sta_addr, + const u8 *pmkid) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_remove_pmkid(hapd, sta_addr, pmkid); +} + + int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; @@ -1621,6 +1833,9 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .get_sta_count = hostapd_wpa_auth_get_sta_count, .for_each_sta = hostapd_wpa_auth_for_each_sta, .for_each_auth = hostapd_wpa_auth_for_each_auth, +#ifdef CONFIG_IEEE80211BE + .for_each_partner_auth = hostapd_wpa_auth_for_each_partner_auth, +#endif /* CONFIG_IEEE80211BE */ .send_ether = hostapd_wpa_auth_send_ether, .send_oui = hostapd_wpa_auth_send_oui, .channel_info = hostapd_channel_info, @@ -1637,7 +1852,6 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .send_ft_action = hostapd_wpa_auth_send_ft_action, .add_sta = hostapd_wpa_auth_add_sta, .add_sta_ft = hostapd_wpa_auth_add_sta_ft, - .add_tspec = hostapd_wpa_auth_add_tspec, .set_vlan = hostapd_wpa_auth_set_vlan, .get_vlan = hostapd_wpa_auth_get_vlan, .set_identity = hostapd_wpa_auth_set_identity, @@ -1655,75 +1869,17 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) #endif /* CONFIG_PASN */ #ifdef CONFIG_IEEE80211BE .get_ml_key_info = hostapd_wpa_auth_get_ml_key_info, + .next_primary_auth = hostapd_next_primary_auth, #endif /* CONFIG_IEEE80211BE */ .get_drv_flags = hostapd_wpa_auth_get_drv_flags, + .remove_pmkid = hostapd_wpa_auth_remove_pmkid, + .first_sta_seen_mbssid = hostapd_first_sta_seen_mbssid, }; const u8 *wpa_ie; size_t wpa_ie_len; - struct hostapd_data *tx_bss; - hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); - _conf.msg_ctx = hapd->msg_ctx; - tx_bss = hostapd_mbssid_get_tx_bss(hapd); - if (tx_bss != hapd) - _conf.tx_bss_auth = tx_bss->wpa_auth; - if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) - _conf.tx_status = 1; - if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) - _conf.ap_mlme = 1; - - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) && - (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || - (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && - !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { - wpa_msg(hapd->msg_ctx, MSG_INFO, - "Disable PTK0 rekey support - replaced with disconnect"); - _conf.wpa_deny_ptk0_rekey = 1; - } - - if (_conf.extended_key_id && - (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) - wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported"); - else - _conf.extended_key_id = 0; - - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) - _conf.beacon_prot = 0; - -#ifdef CONFIG_OCV - if (!(hapd->iface->drv_flags2 & - (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV))) - _conf.ocv = 0; -#endif /* CONFIG_OCV */ - - _conf.secure_ltf = - !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP); - _conf.secure_rtt = - !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP); - _conf.prot_range_neg = - !!(hapd->iface->drv_flags2 & - WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP); - -#ifdef CONFIG_IEEE80211BE - _conf.mld_addr = NULL; - _conf.link_id = -1; - _conf.first_link_auth = NULL; - - if (hapd->conf->mld_ap) { - struct hostapd_data *lhapd; - - _conf.mld_addr = hapd->mld->mld_addr; - _conf.link_id = hapd->mld_link_id; - - for_each_mld_link(lhapd, hapd) { - if (lhapd == hapd) - continue; - - if (lhapd->wpa_auth) - _conf.first_link_auth = lhapd->wpa_auth; - } - } -#endif /* CONFIG_IEEE80211BE */ + hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf, &_conf); + hostapd_wpa_auth_config_update(hapd, &_conf); hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { @@ -1788,7 +1944,10 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) void hostapd_reconfig_wpa(struct hostapd_data *hapd) { struct wpa_auth_config wpa_auth_conf; - hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf); + + hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf, + &wpa_auth_conf); + hostapd_wpa_auth_config_update(hapd, &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); } diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h index 4e5ba3e2ed6f..c9427bb72688 100644 --- a/contrib/wpa/src/ap/wpa_auth_i.h +++ b/contrib/wpa/src/ap/wpa_auth_i.h @@ -61,6 +61,7 @@ struct wpa_state_machine { unsigned int pmk_len; u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ struct wpa_ptk PTK; + enum rsn_hash_alg hash_alg; u8 keyidx_active; bool use_ext_key_id; bool PTK_valid; @@ -97,6 +98,7 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; unsigned int pmkid_set:1; + unsigned int spp_amsdu:1; unsigned int ptkstart_without_success; @@ -111,6 +113,8 @@ struct wpa_state_machine { size_t wpa_ie_len; u8 *rsnxe; size_t rsnxe_len; + u8 *rsn_selection; + size_t rsn_selection_len; enum { WPA_VERSION_NO_WPA = 0 /* WPA not used */, @@ -124,6 +128,9 @@ struct wpa_state_machine { u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; + bool rsn_override; + bool rsn_override_2; + #ifdef CONFIG_IEEE80211R_AP u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the * first 384 bits of MSK */ @@ -178,6 +185,7 @@ struct wpa_state_machine { struct mld_link { bool valid; + bool rejected; u8 peer_addr[ETH_ALEN]; struct wpa_authenticator *wpa_auth; @@ -185,6 +193,9 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211BE */ bool ssid_protection; + + struct wpabuf *sae_pw_id; + unsigned int sae_pw_id_counter; }; @@ -261,6 +272,8 @@ struct wpa_authenticator { #endif /* CONFIG_P2P */ #ifdef CONFIG_IEEE80211BE + /* MLD-level PMKSA cache for non-AP MLD entries only. */ + struct rsn_pmksa_cache *ml_pmksa; bool is_ml; u8 mld_addr[ETH_ALEN]; u8 link_id; diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c index 2efadf896186..8fa424376e43 100644 --- a/contrib/wpa/src/ap/wpa_auth_ie.c +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -89,7 +89,8 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) } -static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf) +static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf, + enum mfp_options mfp) { u16 capab = 0; @@ -99,9 +100,9 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf) /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); } - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (mfp != NO_MGMT_FRAME_PROTECTION) { capab |= WPA_CAPABILITY_MFPC; - if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + if (mfp == MGMT_FRAME_PROTECTION_REQUIRED) capab |= WPA_CAPABILITY_MFPR; } #ifdef CONFIG_OCV @@ -119,24 +120,19 @@ static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf) } -int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, - const u8 *pmkid) +static u8 * rsne_write_data(u8 *buf, size_t len, u8 *pos, int group, + int pairwise, int key_mgmt, u16 rsn_capab, + const u8 *pmkid, enum mfp_options mfp, + int group_mgmt_cipher) { - struct rsn_ie_hdr *hdr; int num_suites, res; - u8 *pos, *count; + u8 *count; u32 suite; - hdr = (struct rsn_ie_hdr *) buf; - hdr->elem_id = WLAN_EID_RSN; - WPA_PUT_LE16(hdr->version, RSN_VERSION); - pos = (u8 *) (hdr + 1); - - suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group); if (suite == 0) { - wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", - conf->wpa_group); - return -1; + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", group); + return NULL; } RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; @@ -153,7 +149,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_RSN_TESTING */ - res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); + res = rsn_cipher_put_suites(pos, pairwise); num_suites += res; pos += res * RSN_SELECTOR_LEN; @@ -167,8 +163,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", - conf->rsn_pairwise); - return -1; + pairwise); + return NULL; } WPA_PUT_LE16(count, num_suites); @@ -184,102 +180,102 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_RSN_TESTING */ - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + if (key_mgmt & WPA_KEY_MGMT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_IEEE80211R_AP - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_SHA384 - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_SHA384 */ - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + if (key_mgmt & WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SHA384 - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) { + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_SHA384 */ - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_SAE - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + if (key_mgmt & WPA_KEY_MGMT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { + if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + if (key_mgmt & WPA_KEY_MGMT_FT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) { + if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_SAE */ - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_FILS - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); pos += RSN_SELECTOR_LEN; num_suites++; } #ifdef CONFIG_IEEE80211R_AP - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); pos += RSN_SELECTOR_LEN; num_suites++; } - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); pos += RSN_SELECTOR_LEN; num_suites++; @@ -287,33 +283,33 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, #endif /* CONFIG_IEEE80211R_AP */ #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + if (key_mgmt & WPA_KEY_MGMT_OWE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + if (key_mgmt & WPA_KEY_MGMT_DPP) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_DPP */ -#ifdef CONFIG_HS20 - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) { - RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } -#endif /* CONFIG_HS20 */ #ifdef CONFIG_PASN - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) { + if (key_mgmt & WPA_KEY_MGMT_PASN) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); pos += RSN_SELECTOR_LEN; num_suites++; } #endif /* CONFIG_PASN */ +#ifdef CONFIG_ENC_ASSOC + if (key_mgmt & WPA_KEY_MGMT_EPPKE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -325,18 +321,18 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", - conf->wpa_key_mgmt); - return -1; + key_mgmt); + return NULL; } WPA_PUT_LE16(count, num_suites); /* RSN Capabilities */ - WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf)); + WPA_PUT_LE16(pos, rsn_capab); pos += 2; if (pmkid) { if (2 + PMKID_LEN > buf + len - pos) - return -1; + return NULL; /* PMKID Count */ WPA_PUT_LE16(pos, 1); pos += 2; @@ -344,21 +340,19 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += PMKID_LEN; } - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && - conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { + + if (mfp != NO_MGMT_FRAME_PROTECTION && + group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { if (2 + 4 > buf + len - pos) - return -1; - if (pmkid == NULL) { + return NULL; + if (!pmkid) { /* PMKID Count */ WPA_PUT_LE16(pos, 0); pos += 2; } /* Management Group Cipher Suite */ - switch (conf->group_mgmt_cipher) { - case WPA_CIPHER_AES_128_CMAC: - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); - break; + switch (group_mgmt_cipher) { case WPA_CIPHER_BIP_GMAC_128: RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128); break; @@ -371,8 +365,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, default: wpa_printf(MSG_DEBUG, "Invalid group management cipher (0x%x)", - conf->group_mgmt_cipher); - return -1; + group_mgmt_cipher); + return NULL; } pos += RSN_SELECTOR_LEN; } @@ -384,12 +378,12 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, * the element. */ int pmkid_count_set = pmkid != NULL; - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + if (mfp != NO_MGMT_FRAME_PROTECTION) pmkid_count_set = 1; /* PMKID Count */ WPA_PUT_LE16(pos, 0); pos += 2; - if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + if (mfp == NO_MGMT_FRAME_PROTECTION) { /* Management Group Cipher Suite */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; @@ -399,6 +393,27 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += 17; } #endif /* CONFIG_RSN_TESTING */ + return pos; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + u8 *pos; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + pos = rsne_write_data(buf, len, pos, conf->wpa_group, + conf->rsn_pairwise, conf->wpa_key_mgmt, + wpa_own_rsn_capab(conf, conf->ieee80211w), pmkid, + conf->ieee80211w, conf->group_mgmt_cipher); + if (!pos) + return -1; hdr->len = (pos - buf) - 2; @@ -406,16 +421,139 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } -int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) +static int wpa_write_rsne_override(struct wpa_auth_config *conf, u8 *buf, + size_t len) { - u8 *pos = buf; - u32 capab = 0, tmp; - size_t flen; + u8 *pos, *len_pos; - if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) && + pos = buf; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + len_pos = pos++; + + WPA_PUT_BE32(pos, RSNE_OVERRIDE_IE_VENDOR_TYPE); + pos += 4; + + WPA_PUT_LE16(pos, RSN_VERSION); + pos += 2; + + pos = rsne_write_data(buf, len, pos, conf->wpa_group, + conf->rsn_override_pairwise, + conf->rsn_override_key_mgmt, + wpa_own_rsn_capab(conf, conf->rsn_override_mfp), + NULL, conf->rsn_override_mfp, + conf->group_mgmt_cipher); + if (!pos) + return -1; + + *len_pos = (pos - buf) - 2; + + return pos - buf; +} + + +static int wpa_write_rsne_override_2(struct wpa_auth_config *conf, u8 *buf, + size_t len) +{ + u8 *pos, *len_pos; + + pos = buf; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + len_pos = pos++; + + WPA_PUT_BE32(pos, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + pos += 4; + + WPA_PUT_LE16(pos, RSN_VERSION); + pos += 2; + + pos = rsne_write_data(buf, len, pos, conf->wpa_group, + conf->rsn_override_pairwise_2, + conf->rsn_override_key_mgmt_2, + wpa_own_rsn_capab(conf, conf->rsn_override_mfp_2), + NULL, conf->rsn_override_mfp_2, + conf->group_mgmt_cipher); + if (!pos) + return -1; + + *len_pos = (pos - buf) - 2; + + return pos - buf; +} + + +#ifdef CONFIG_IEEE8021X_AUTH +int wpa_write_802_1x_rsne(struct wpa_authenticator *wpa_auth, u8 *buf, + size_t len, const u8 *pmkid, int akmp, + int pairwise_cipher, int group_cipher, + int group_mgmt_cipher, enum mfp_options mfp) +{ + struct rsn_ie_hdr *hdr; + u8 *pos; + struct wpa_auth_config *conf = &wpa_auth->conf; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + pos = rsne_write_data(buf, len, pos, group_cipher, + pairwise_cipher, akmp, + wpa_own_rsn_capab(conf, conf->ieee80211w), + pmkid, mfp, group_mgmt_cipher); + if (!pos) + return -1; + + hdr->len = pos - buf - 2; + + return pos - buf; +} +#endif /* CONFIG_IEEE8021X_AUTH */ + + +#ifdef CONFIG_ENC_ASSOC +int wpa_write_eppke_rsne(const u8 *wpa_ie, size_t wpa_ie_len, + u8 *buf, size_t len, + const u8 *pmkid, int akmp, + int pairwise_cipher, enum mfp_options mfp) +{ + struct rsn_ie_hdr *hdr; + struct wpa_ie_data data; + const u8 *rsne; + u8 *pos; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + os_memset(&data, 0, sizeof(data)); + rsne = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_RSN); + if (!rsne || wpa_parse_wpa_ie_rsn(rsne, 2 + rsne[1], &data) < 0) { + wpa_printf(MSG_INFO, + "EPPKE: No valid AP RSNE as a starting point"); + return -1; + } + pos = rsne_write_data(buf, len, pos, data.group_cipher, pairwise_cipher, + akmp, data.capabilities, pmkid, mfp, + data.mgmt_group_cipher); + if (!pos) + return -1; + + hdr->len = pos - buf - 2; + + return pos - buf; +} +#endif /* CONFIG_ENC_ASSOC */ + + +static u64 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt) +{ + u64 capab = 0; + + if (wpa_key_mgmt_sae(key_mgmt) && (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk || - wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) { + wpa_key_mgmt_sae_ext_key(key_mgmt))) { capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); #ifdef CONFIG_SAE_PK if (conf->sae_pk) @@ -427,10 +565,46 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); if (conf->secure_rtt) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); - if (conf->prot_range_neg) - capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (conf->prot_range_neg) { + if (conf->urnm_mfpr) + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (conf->urnm_mfpr_x20) + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20); + } if (conf->ssid_protection) capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION); + if (conf->spp_amsdu) + capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); +#ifdef CONFIG_ENC_ASSOC + /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy) + * a STA that sets the PMKSA Caching Privacy Support + * field in the RSNXE to 1 shall set the (Re)Association + * Frame Encryption Support field in the RSNXE to 1. + */ + if (conf->assoc_frame_encryption || + conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + if (key_mgmt & WPA_KEY_MGMT_EPPKE) + capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + if (conf->pmksa_caching_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); + if (conf->eap_using_authentication_frames) + capab |= BIT(WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES); + if (conf->eppke_unauth) + capab |= BIT_ULL(WLAN_RSNX_CAPAB_UNAUTH_EPPKE); +#endif /* CONFIG_ENC_ASSOC */ + + return capab; +} + + +int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + u8 *pos = buf; + u64 capab, tmp; + size_t flen; + + capab = rsnxe_capab(conf, conf->wpa_key_mgmt); if (!capab) return 0; /* no supported extended RSN capabilities */ @@ -455,60 +629,45 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len) } -static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid) +static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf, + size_t len) { - u8 *len; - u16 capab; + u8 *pos = buf; + u64 capab, tmp; + size_t flen; - *eid++ = WLAN_EID_VENDOR_SPECIFIC; - len = eid++; /* to be filled */ - WPA_PUT_BE24(eid, OUI_WFA); - eid += 3; - *eid++ = HS20_OSEN_OUI_TYPE; + capab = rsnxe_capab(conf, conf->rsn_override_key_mgmt | + conf->rsn_override_key_mgmt_2); - /* Group Data Cipher Suite */ - RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); - eid += RSN_SELECTOR_LEN; - - /* Pairwise Cipher Suite Count and List */ - WPA_PUT_LE16(eid, 1); - eid += 2; - RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP); - eid += RSN_SELECTOR_LEN; - - /* AKM Suite Count and List */ - WPA_PUT_LE16(eid, 1); - eid += 2; - RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN); - eid += RSN_SELECTOR_LEN; - - /* RSN Capabilities */ - capab = 0; - if (conf->wmm_enabled) { - /* 4 PTKSA replay counters when using WMM */ - capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + if (!capab) + return 0; /* no supported extended RSN capabilities */ + tmp = capab; + flen = 0; + while (tmp) { + flen++; + tmp >>= 8; } - if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { - capab |= WPA_CAPABILITY_MFPC; - if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) - capab |= WPA_CAPABILITY_MFPR; + if (len < 2 + flen) + return -1; + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + flen; + WPA_PUT_BE32(pos, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + pos += 4; + + while (capab) { + *pos++ = capab & 0xff; + capab >>= 8; } -#ifdef CONFIG_OCV - if (conf->ocv) - capab |= WPA_CAPABILITY_OCVC; -#endif /* CONFIG_OCV */ - WPA_PUT_LE16(eid, capab); - eid += 2; - *len = eid - len - 1; - - return eid; + return pos - buf; } int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { - u8 *pos, buf[128]; + u8 *pos, buf[1500]; int res; #ifdef CONFIG_TESTING_OPTIONS @@ -530,21 +689,55 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) pos = buf; - if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) { - pos = wpa_write_osen(&wpa_auth->conf, pos); - } if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.rsne_override_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Forced own RSNE for testing", + wpa_auth->conf.rsne_override, + wpa_auth->conf.rsne_override_len); + if (sizeof(buf) - (pos - buf) < + wpa_auth->conf.rsne_override_len) + return -1; + os_memcpy(pos, wpa_auth->conf.rsne_override, + wpa_auth->conf.rsne_override_len); + pos += wpa_auth->conf.rsne_override_len; + goto rsnxe; + } +#endif /* CONFIG_TESTING_OPTIONS */ res = wpa_write_rsn_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos, NULL); if (res < 0) return res; pos += res; - res = wpa_write_rsnxe(&wpa_auth->conf, pos, - buf + sizeof(buf) - pos); +#ifdef CONFIG_TESTING_OPTIONS + rsnxe: + if (wpa_auth->conf.rsnxe_override_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Forced own RSNXE for testing", + wpa_auth->conf.rsnxe_override, + wpa_auth->conf.rsnxe_override_len); + if (sizeof(buf) - (pos - buf) < + wpa_auth->conf.rsnxe_override_len) + return -1; + os_memcpy(pos, wpa_auth->conf.rsnxe_override, + wpa_auth->conf.rsnxe_override_len); + pos += wpa_auth->conf.rsnxe_override_len; + goto fte; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (wpa_auth->conf.rsn_override_omit_rsnxe) + res = 0; + else + res = wpa_write_rsnxe(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } +#ifdef CONFIG_TESTING_OPTIONS +fte: +#endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, @@ -561,7 +754,87 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) return res; pos += res; } + if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_auth->conf.rsn_override_key_mgmt) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.rsnoe_override_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Forced own RSNOE for testing", + wpa_auth->conf.rsnoe_override, + wpa_auth->conf.rsnoe_override_len); + if (sizeof(buf) - (pos - buf) < + wpa_auth->conf.rsnoe_override_len) + return -1; + os_memcpy(pos, wpa_auth->conf.rsnoe_override, + wpa_auth->conf.rsnoe_override_len); + pos += wpa_auth->conf.rsnoe_override_len; + goto rsno2e; + } +#endif /* CONFIG_TESTING_OPTIONS */ + res = wpa_write_rsne_override(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_TESTING_OPTIONS +rsno2e: +#endif /* CONFIG_TESTING_OPTIONS */ + if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_auth->conf.rsn_override_key_mgmt_2) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.rsno2e_override_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Forced own RSNO2E for testing", + wpa_auth->conf.rsno2e_override, + wpa_auth->conf.rsno2e_override_len); + if (sizeof(buf) - (pos - buf) < + wpa_auth->conf.rsno2e_override_len) + return -1; + os_memcpy(pos, wpa_auth->conf.rsno2e_override, + wpa_auth->conf.rsno2e_override_len); + pos += wpa_auth->conf.rsno2e_override_len; + goto rsnxoe; + } +#endif /* CONFIG_TESTING_OPTIONS */ + res = wpa_write_rsne_override_2(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_TESTING_OPTIONS +rsnxoe: +#endif /* CONFIG_TESTING_OPTIONS */ + if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) && + (wpa_auth->conf.rsn_override_key_mgmt || + wpa_auth->conf.rsn_override_key_mgmt_2)) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_auth->conf.rsnxoe_override_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Forced own RSNXOE for testing", + wpa_auth->conf.rsnxoe_override, + wpa_auth->conf.rsnxoe_override_len); + if (sizeof(buf) - (pos - buf) < + wpa_auth->conf.rsnxoe_override_len) + return -1; + os_memcpy(pos, wpa_auth->conf.rsnxoe_override, + wpa_auth->conf.rsnxoe_override_len); + pos += wpa_auth->conf.rsnxoe_override_len; + goto done; + } +#endif /* CONFIG_TESTING_OPTIONS */ + res = wpa_write_rsnxe_override(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_TESTING_OPTIONS +done: +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "RSN: Own IEs", buf, pos - buf); os_free(wpa_auth->wpa_ie); wpa_auth->wpa_ie = os_malloc(pos - buf); if (wpa_auth->wpa_ie == NULL) @@ -609,6 +882,32 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) } +#ifdef CONFIG_IEEE80211BE + +struct wpa_auth_link_iter_data { + struct wpa_authenticator *wpa_auth; + struct rsn_pmksa_cache_entry *pmksa; + const u8 *spa; + const u8 *pmkid; +}; + +static int wpa_auth_pmksa_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_link_iter_data *data = ctx; + + if (a == data->wpa_auth || + !ether_addr_equal(a->mld_addr, data->wpa_auth->mld_addr)) + return 0; + + data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid); + if (data->pmksa) + return 1; + return 0; +} + +#endif /* CONFIG_IEEE80211BE */ + + enum wpa_validate_result wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int freq, @@ -616,7 +915,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 *rsnxe, size_t rsnxe_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len, - struct wpa_state_machine *assoc_sm) + struct wpa_state_machine *assoc_sm, bool is_ml) { struct wpa_auth_config *conf = &wpa_auth->conf; struct wpa_ie_data data; @@ -624,6 +923,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, u32 selector; size_t i; const u8 *pmkid = NULL; + bool ap_pmf_enabled; if (wpa_auth == NULL || sm == NULL) return WPA_NOT_ENABLED; @@ -717,14 +1017,14 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (data.key_mgmt & WPA_KEY_MGMT_DPP) selector = RSN_AUTH_KEY_MGMT_DPP; #endif /* CONFIG_DPP */ -#ifdef CONFIG_HS20 - else if (data.key_mgmt & WPA_KEY_MGMT_OSEN) - selector = RSN_AUTH_KEY_MGMT_OSEN; -#endif /* CONFIG_HS20 */ #ifdef CONFIG_SHA384 else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) selector = RSN_AUTH_KEY_MGMT_802_1X_SHA384; #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_ENC_ASSOC + else if (data.key_mgmt & WPA_KEY_MGMT_EPPKE) + selector = RSN_AUTH_KEY_MGMT_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, @@ -773,7 +1073,26 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, return WPA_INVALID_GROUP; } - key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (sm->rsn_override_2) + key_mgmt = data.key_mgmt & + wpa_auth->conf.rsn_override_key_mgmt_2; + else if (sm->rsn_override) + key_mgmt = data.key_mgmt & wpa_auth->conf.rsn_override_key_mgmt; + else + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + + /* For an MLO partner link, the STA may not include an RSN Selection + * element (no RSNO support), so sm->rsn_override* are both false and + * key_mgmt is matched against wpa_key_mgmt (e.g., WPA-PSK on 2.4 GHz). + * But the STA negotiated via the primary link (e.g., SAE on 6 GHz) + * and sends SAE in the per-STA profile RSNE assuming that the same + * AKM is enabled on all affiliated links. When rsn_override_mlo_compat + * is enabled, fall back to rsn_override_key_mgmt so that SAE is + * accepted on the partner link in such cases. + */ + if (!key_mgmt && assoc_sm && wpa_auth->conf.rsn_override_mlo_compat) + key_mgmt = data.key_mgmt & wpa_auth->conf.rsn_override_key_mgmt; + if (!key_mgmt) { wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " MACSTR, data.key_mgmt, MAC2STR(sm->addr)); @@ -835,14 +1154,20 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (key_mgmt & WPA_KEY_MGMT_DPP) sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP; #endif /* CONFIG_DPP */ -#ifdef CONFIG_HS20 - else if (key_mgmt & WPA_KEY_MGMT_OSEN) - sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; -#endif /* CONFIG_HS20 */ +#ifdef CONFIG_ENC_ASSOC + else if (key_mgmt & WPA_KEY_MGMT_EPPKE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ else sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - if (version == WPA_PROTO_RSN) + if (version == WPA_PROTO_RSN && sm->rsn_override_2) + ciphers = data.pairwise_cipher & + wpa_auth->conf.rsn_override_pairwise_2; + else if (version == WPA_PROTO_RSN && sm->rsn_override) + ciphers = data.pairwise_cipher & + wpa_auth->conf.rsn_override_pairwise; + else if (version == WPA_PROTO_RSN) ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; else ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; @@ -902,8 +1227,16 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, wpa_auth->conf.ocv : 0); } #endif /* CONFIG_OCV */ + if (sm->rsn_override_2) + ap_pmf_enabled = conf->rsn_override_mfp_2 != + NO_MGMT_FRAME_PROTECTION; + else if (sm->rsn_override) + ap_pmf_enabled = conf->rsn_override_mfp != + NO_MGMT_FRAME_PROTECTION; + else + ap_pmf_enabled = conf->ieee80211w != NO_MGMT_FRAME_PROTECTION; - if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || + if (!ap_pmf_enabled || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else @@ -916,6 +1249,14 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } + if (wpa_auth->conf.spp_amsdu && + ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU) && + (ciphers & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP))) + sm->spp_amsdu = 1; + else + sm->spp_amsdu = 0; + #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { @@ -990,10 +1331,39 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, sm->pmksa = NULL; for (i = 0; i < data.num_pmkid; i++) { + struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa; + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", &data.pmkid[i * PMKID_LEN], PMKID_LEN); - sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr, +#ifdef CONFIG_IEEE80211BE + if (is_ml) + pmksa = wpa_auth->ml_pmksa; +#endif /* CONFIG_IEEE80211BE */ + sm->pmksa = pmksa_cache_auth_get(pmksa, sm->addr, &data.pmkid[i * PMKID_LEN]); +#ifdef CONFIG_IEEE80211BE + if (!sm->pmksa && !is_ml && wpa_auth->is_ml) + sm->pmksa = pmksa_cache_auth_get( + wpa_auth->ml_pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (!sm->pmksa && is_ml) { + struct wpa_auth_link_iter_data idata; + + idata.wpa_auth = wpa_auth; + idata.pmksa = NULL; + idata.spa = sm->addr; + idata.pmkid = &data.pmkid[i * PMKID_LEN]; + wpa_auth_for_each_auth(wpa_auth, + wpa_auth_pmksa_iter, + &idata); + if (idata.pmksa) + sm->pmksa = idata.pmksa; + } +#endif /* CONFIG_IEEE80211BE */ + if (!sm->pmksa && !is_zero_ether_addr(sm->p2p_dev_addr)) + sm->pmksa = pmksa_cache_auth_get( + wpa_auth->pmksa, sm->p2p_dev_addr, + &data.pmkid[i * PMKID_LEN]); if (sm->pmksa) { pmkid = sm->pmksa->pmkid; break; @@ -1025,7 +1395,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", sm->pmksa->eap_type_authsrv, - vlan ? vlan->untagged : 0, + vlan ? vlan->untagged : + sm->pmksa->sae_vlan_id, (vlan && vlan->tagged[0]) ? "+" : ""); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } @@ -1044,7 +1415,21 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, !!(drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP); - if (!ap_sae_offload && data.num_pmkid && !sm->pmksa) { + /* Authenticator needs to have a PMKSA corresponding to a + * PMKID (if present) included by the STA in (Re)Association + * Request frame if PMKSA caching is attempted to be used. In + * case of SAE, this follows Open System authentication. IEEE + * Std 802.11 mandates the AP to reject (re)association trying + * to use PMKSA caching for SAE authentication. While the + * PMKID (if any) in the RSNE in (Re)Association Request frame + * following SAE authentication (i.e., in the case of no PMKSA + * caching) is not really supposed to include an unknown PMKID, + * the standard does not require the AP to reject association. + * The PMKSA that was just derived using SAE authentication + * can be used regardless of which PMKID(s) are indicated in the + * (Re)Association Request frame. */ + if (!ap_sae_offload && data.num_pmkid && !sm->pmksa && + sm->auth_alg == WLAN_AUTH_OPEN) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "No PMKSA cache entry found for SAE"); return WPA_INVALID_PMKID; @@ -1086,7 +1471,7 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); sm->wpa_ie_len = wpa_ie_len; - if (rsnxe && rsnxe_len) { + if (sm->wpa != WPA_VERSION_WPA && rsnxe && rsnxe_len) { if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) { os_free(sm->rsnxe); sm->rsnxe = os_malloc(rsnxe_len); @@ -1105,42 +1490,17 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } -#ifdef CONFIG_HS20 -int wpa_validate_osen(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *osen_ie, size_t osen_ie_len) -{ - if (wpa_auth == NULL || sm == NULL) - return -1; - - /* TODO: parse OSEN element */ - sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; - sm->mgmt_frame_prot = 1; - sm->pairwise = WPA_CIPHER_CCMP; - sm->wpa = WPA_VERSION_WPA2; - - if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) { - os_free(sm->wpa_ie); - sm->wpa_ie = os_malloc(osen_ie_len); - if (sm->wpa_ie == NULL) - return -1; - } - - os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len); - sm->wpa_ie_len = osen_ie_len; - - return 0; -} - -#endif /* CONFIG_HS20 */ - - int wpa_auth_uses_mfp(struct wpa_state_machine *sm) { return sm ? sm->mgmt_frame_prot : 0; } +int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm) +{ + return sm ? sm->spp_amsdu : 0; +} + #ifdef CONFIG_OCV void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv) @@ -1160,8 +1520,7 @@ int wpa_auth_uses_ocv(struct wpa_state_machine *sm) #ifdef CONFIG_OWE u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, - u8 *pos, size_t max_len, - const u8 *req_ies, size_t req_ies_len) + u8 *pos, size_t max_len) { int res; struct wpa_auth_config *conf; @@ -1194,8 +1553,7 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, #ifdef CONFIG_FILS u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, - u8 *pos, size_t max_len, - const u8 *req_ies, size_t req_ies_len) + u8 *pos, size_t max_len) { int res; @@ -1229,7 +1587,7 @@ bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth, return false; /* RSN Capability (B0..B15) */ - WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf)); + WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf, conf->ieee80211w)); pos += 2; /* Group Data Cipher Suite Selector (B16..B21) */ diff --git a/contrib/wpa/src/ap/wpa_auth_kay.c b/contrib/wpa/src/ap/wpa_auth_kay.c index 625f40512750..20a5aaa10a56 100644 --- a/contrib/wpa/src/ap/wpa_auth_kay.c +++ b/contrib/wpa/src/ap/wpa_auth_kay.c @@ -331,6 +331,7 @@ int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd, hapd->conf->macsec_port, hapd->conf->mka_priority, hapd->conf->macsec_csindex, + hapd->conf->macsec_icv_indicator, hapd->conf->iface, hapd->own_addr); /* ieee802_1x_kay_init() frees kay_ctx on failure */ @@ -477,7 +478,7 @@ void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd, cak->len = hapd->conf->mka_cak_len; os_memcpy(cak->key, hapd->conf->mka_cak, cak->len); - ckn->len = hapd->conf->mka_ckn_len;; + ckn->len = hapd->conf->mka_ckn_len; os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len); res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true); diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c index 82d4d5fdd599..ceaf9a453832 100644 --- a/contrib/wpa/src/ap/wps_hostapd.c +++ b/contrib/wpa/src/ap/wps_hostapd.c @@ -20,6 +20,7 @@ #include "wps/wps_defs.h" #include "wps/wps_dev_attr.h" #include "wps/wps_attr_parse.h" +#include "crypto/dh_group5.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" @@ -1071,6 +1072,7 @@ static void hostapd_free_wps(struct wps_context *wps) wps_device_data_free(&wps->dev); bin_clear_free(wps->network_key, wps->network_key_len); hostapd_wps_nfc_clear(wps); + dh5_free(wps->dh_ctx); wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); forced_memzero(wps->psk, sizeof(wps->psk)); diff --git a/contrib/wpa/src/build.rules b/contrib/wpa/src/build.rules index c756ccb84e8c..eb6759ac2068 100644 --- a/contrib/wpa/src/build.rules +++ b/contrib/wpa/src/build.rules @@ -83,13 +83,16 @@ _make_dirs: @mkdir -p $(sort $(_DIRS)) $(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs + @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< $(BUILDDIR)/$(PROJ)/%.o: %.c $(CONFIG_FILE) | _make_dirs + @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< # for the fuzzing tests $(BUILDDIR)/$(PROJ)/wpa_supplicant/%.o: $(ROOTDIR)wpa_supplicant/%.c $(CONFIG_FILE) | _make_dirs + @echo $(CURDIR): '$(CC) -c -o $@ $(CFLAGS) $<' >$@.cmd $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< diff --git a/contrib/wpa/src/common/Makefile b/contrib/wpa/src/common/Makefile index e2c5f03c2c3b..fef65df6dad8 100644 --- a/contrib/wpa/src/common/Makefile +++ b/contrib/wpa/src/common/Makefile @@ -1,3 +1,4 @@ +CFLAGS += -DCONFIG_IEEE80211AC CFLAGS += -DCONFIG_IEEE80211R CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_SAE diff --git a/contrib/wpa/src/common/common_module_tests.c b/contrib/wpa/src/common/common_module_tests.c index a95ae36dc078..bb6b9cf972b7 100644 --- a/contrib/wpa/src/common/common_module_tests.c +++ b/contrib/wpa/src/common/common_module_tests.c @@ -416,7 +416,7 @@ static int sae_tests(void) goto fail; /* Check that output matches the test vector */ - if (sae_write_commit(&sae, buf, NULL, NULL) < 0) + if (sae_write_commit(&sae, buf, NULL, NULL, 0) < 0) goto fail; wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf); @@ -449,7 +449,9 @@ static int sae_tests(void) pt_info = sae_derive_pt(pt_groups, (const u8 *) ssid, os_strlen(ssid), - (const u8 *) pw, os_strlen(pw), pwid); + (const u8 *) pw, os_strlen(pw), + (const u8 *) pwid, + os_strlen(pwid)); if (!pt_info) goto fail; @@ -608,7 +610,8 @@ static int sae_pk_tests(void) static int pasn_test_pasn_auth(void) { - /* Test vector taken from IEEE P802.11az/D2.6, J.12 */ + /* Test vector taken from IEEE Std 802.11-2024, + * J.12 (PASN Test Vectors) */ const u8 pmk[] = { 0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6, 0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef, @@ -646,12 +649,13 @@ static int pasn_test_pasn_auth(void) }; struct wpa_ptk ptk; int ret; + enum rsn_hash_alg hash_alg; ret = pasn_pmk_to_ptk(pmk, sizeof(pmk), spa_addr, bssid, dhss, sizeof(dhss), &ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP, - WPA_KDK_MAX_LEN); + WPA_KDK_MAX_LEN, 0, &hash_alg, false); if (ret) return ret; @@ -680,7 +684,8 @@ static int pasn_test_pasn_auth(void) static int pasn_test_no_pasn_auth(void) { - /* Test vector taken from IEEE P802.11az/D2.6, J.13 */ + /* Test vector taken from IEEE Std 802.11-2024, + * J.13 (KDK Test Vectors when PASN authentication is not used) */ const u8 pmk[] = { 0xde, 0xf4, 0x3e, 0x55, 0x67, 0xe0, 0x1c, 0xa6, 0x64, 0x92, 0x65, 0xf1, 0x9a, 0x29, 0x0e, 0xef, diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h index 8cca094e8f14..c88766e7512a 100644 --- a/contrib/wpa/src/common/defs.h +++ b/contrib/wpa/src/common/defs.h @@ -39,7 +39,6 @@ #define WPA_KEY_MGMT_WAPI_PSK BIT(12) #define WPA_KEY_MGMT_WAPI_CERT BIT(13) #define WPA_KEY_MGMT_CCKM BIT(14) -#define WPA_KEY_MGMT_OSEN BIT(15) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) #define WPA_KEY_MGMT_FILS_SHA256 BIT(18) @@ -53,6 +52,7 @@ #define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26) #define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27) #define WPA_KEY_MGMT_IEEE8021X_SHA384 BIT(28) +#define WPA_KEY_MGMT_EPPKE BIT(29) #define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ @@ -69,7 +69,6 @@ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | WPA_KEY_MGMT_CCKM | - WPA_KEY_MGMT_OSEN | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_IEEE8021X_SUITE_B | WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | @@ -129,6 +128,22 @@ static inline int wpa_key_mgmt_sae_ext_key(int akm) WPA_KEY_MGMT_FT_SAE_EXT_KEY)); } +static inline int wpa_key_mgmt_only_sae(int akm) +{ + return wpa_key_mgmt_sae(akm) && + !(akm & ~(WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_SAE_EXT_KEY | + WPA_KEY_MGMT_FT_SAE | + WPA_KEY_MGMT_FT_SAE_EXT_KEY)); +} + +static inline int wpa_key_mgmt_only_sae_ext_key(int akm) +{ + return wpa_key_mgmt_sae_ext_key(akm) && + !(akm & ~(WPA_KEY_MGMT_SAE_EXT_KEY | + WPA_KEY_MGMT_FT_SAE_EXT_KEY)); +} + static inline int wpa_key_mgmt_fils(int akm) { return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 | @@ -144,7 +159,6 @@ static inline int wpa_key_mgmt_sha256(int akm) WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE | - WPA_KEY_MGMT_OSEN | WPA_KEY_MGMT_IEEE8021X_SUITE_B | WPA_KEY_MGMT_FILS_SHA256 | WPA_KEY_MGMT_FT_FILS_SHA256)); @@ -172,7 +186,8 @@ static inline int wpa_key_mgmt_wpa(int akm) wpa_key_mgmt_fils(akm) || wpa_key_mgmt_sae(akm) || akm == WPA_KEY_MGMT_OWE || - akm == WPA_KEY_MGMT_DPP; + akm == WPA_KEY_MGMT_DPP || + akm == WPA_KEY_MGMT_EPPKE; } static inline int wpa_key_mgmt_wpa_any(int akm) @@ -193,10 +208,27 @@ static inline int wpa_key_mgmt_cross_akm(int akm) WPA_KEY_MGMT_SAE_EXT_KEY)); } +static inline bool wpa_key_mgmt_eppke(int akm) +{ + return !!(akm & WPA_KEY_MGMT_EPPKE); +} + +static inline int wpa_key_mgmt_enhanced_open(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_OWE | + WPA_KEY_MGMT_EPPKE)); +} + +static inline int wpa_key_mgmt_only_enhanced_open(int akm) +{ + return wpa_key_mgmt_enhanced_open(akm) && + !(akm & ~(WPA_KEY_MGMT_OWE | + WPA_KEY_MGMT_EPPKE)); +} + #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) #define WPA_PROTO_WAPI BIT(2) -#define WPA_PROTO_OSEN BIT(3) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) @@ -205,12 +237,19 @@ static inline int wpa_key_mgmt_cross_akm(int akm) #define WPA_AUTH_ALG_SAE BIT(4) #define WPA_AUTH_ALG_FILS BIT(5) #define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) +#define WPA_AUTH_ALG_EPPKE BIT(7) +#define WPA_AUTH_ALG_802_1X BIT(8) static inline int wpa_auth_alg_fils(int alg) { return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); } +static inline bool wpa_auth_alg_eppke(int alg) +{ + return !!(alg & WPA_AUTH_ALG_EPPKE); +} + enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, @@ -481,6 +520,7 @@ enum key_flag { KEY_FLAG_GROUP = BIT(4), KEY_FLAG_PAIRWISE = BIT(5), KEY_FLAG_PMK = BIT(6), + KEY_FLAG_NEXT = BIT(7), /* Used flag combinations */ KEY_FLAG_RX_TX = KEY_FLAG_RX | KEY_FLAG_TX, KEY_FLAG_GROUP_RX_TX = KEY_FLAG_GROUP | KEY_FLAG_RX_TX, @@ -493,8 +533,10 @@ enum key_flag { KEY_FLAG_PAIRWISE_RX = KEY_FLAG_PAIRWISE | KEY_FLAG_RX, KEY_FLAG_PAIRWISE_RX_TX_MODIFY = KEY_FLAG_PAIRWISE_RX_TX | KEY_FLAG_MODIFY, + KEY_FLAG_PAIRWISE_NEXT = KEY_FLAG_PAIRWISE_RX | KEY_FLAG_NEXT, /* Max allowed flags for each key type */ - KEY_FLAG_PAIRWISE_MASK = KEY_FLAG_PAIRWISE_RX_TX_MODIFY, + KEY_FLAG_PAIRWISE_MASK = KEY_FLAG_PAIRWISE_RX_TX_MODIFY | + KEY_FLAG_NEXT, KEY_FLAG_GROUP_MASK = KEY_FLAG_GROUP_RX_TX_DEFAULT, KEY_FLAG_PMK_MASK = KEY_FLAG_PMK, }; @@ -532,4 +574,15 @@ enum sae_pwe { SAE_PWE_NOT_SET = 4, }; +enum wpa_p2p_mode { + WPA_P2P_MODE_WFD_R1 = 0, + WPA_P2P_MODE_WFD_R2 = 1, + WPA_P2P_MODE_WFD_PCC = 2, +}; + +#define USEC_80211_TU 1024 + +#define USEC_TO_TU(m) ((m) / USEC_80211_TU) +#define TU_TO_USEC(m) ((m) * USEC_80211_TU) + #endif /* DEFS_H */ diff --git a/contrib/wpa/src/common/dpp.c b/contrib/wpa/src/common/dpp.c index 3b9f35e8dfeb..7601b0d72049 100644 --- a/contrib/wpa/src/common/dpp.c +++ b/contrib/wpa/src/common/dpp.c @@ -2,7 +2,7 @@ * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -299,7 +299,8 @@ int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) } -int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version) +static int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, + const char *version) { #ifdef CONFIG_DPP2 if (!version || DPP_VERSION < 2) @@ -1035,6 +1036,10 @@ struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth, json_value_sep(json); json_add_string(json, "pkcs10", csr); } +#ifdef CONFIG_DPP3 + json_value_sep(json); + json_add_int(json, "capabilities", DPP_ENROLLEE_CAPAB_SAE_PW_ID); +#endif /* CONFIG_DPP3 */ if (extra_name && extra_value && extra_name[0] && extra_value[0]) { json_value_sep(json); wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value); @@ -1139,8 +1144,18 @@ int dpp_configuration_valid(const struct dpp_configuration *conf) return 0; if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) return 0; + if (dpp_akm_psk(conf->akm) && conf->passphrase) { + size_t len = os_strlen(conf->passphrase); + + if (len > 63 || len < 8) + return 0; + } if (dpp_akm_sae(conf->akm) && !conf->passphrase) return 0; +#ifdef CONFIG_DPP3 + if (conf->idpass && (!conf->passphrase || !dpp_akm_sae(conf->akm))) + return 0; +#endif /* CONFIG_DPP3 */ return 1; } @@ -1150,6 +1165,9 @@ void dpp_configuration_free(struct dpp_configuration *conf) if (!conf) return; str_clear_free(conf->passphrase); +#ifdef CONFIG_DPP3 + os_free(conf->idpass); +#endif /* CONFIG_DPP3 */ os_free(conf->group_id); os_free(conf->csrattrs); os_free(conf->extra_name); @@ -1228,14 +1246,28 @@ static int dpp_configuration_parse_helper(struct dpp_authentication *auth, end = os_strchr(pos, ' '); pass_len = end ? (size_t) (end - pos) : os_strlen(pos); pass_len /= 2; - if (pass_len > 63 || pass_len < 8) - goto fail; conf->passphrase = os_zalloc(pass_len + 1); if (!conf->passphrase || hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) goto fail; } +#ifdef CONFIG_DPP3 + pos = os_strstr(cmd, " idpass="); + if (pos) { + size_t idpass_len; + + pos += 8; + end = os_strchr(pos, ' '); + idpass_len = end ? (size_t) (end - pos) : os_strlen(pos); + idpass_len /= 2; + conf->idpass = os_zalloc(idpass_len + 1); + if (!conf->idpass || + hexstr2bin(pos, (u8 *) conf->idpass, idpass_len) < 0) + goto fail; + } +#endif /* CONFIG_DPP3 */ + pos = os_strstr(cmd, " psk="); if (pos) { pos += 5; @@ -1595,6 +1627,13 @@ static void dpp_build_legacy_cred_params(struct wpabuf *buf, if (conf->passphrase && os_strlen(conf->passphrase) < 64) { json_add_string_escape(buf, "pass", conf->passphrase, os_strlen(conf->passphrase)); +#ifdef CONFIG_DPP3 + if (conf->idpass) { + json_value_sep(buf); + json_add_string_escape(buf, "idpass", conf->idpass, + os_strlen(conf->idpass)); + } +#endif /* CONFIG_DPP3 */ } else if (conf->psk_set) { char psk[2 * sizeof(conf->psk) + 1]; @@ -1917,6 +1956,16 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, const char *akm_str; size_t len = 1000; + +#ifdef CONFIG_DPP3 + if (conf->idpass && + !(auth->enrollee_capabilities & DPP_ENROLLEE_CAPAB_SAE_PW_ID)) { + wpa_printf(MSG_DEBUG, + "DPP: Enrollee does not support SAE Password Identifier - cannot generate config object"); + return NULL; + } +#endif /* CONFIG_DPP3 */ + if (conf->extra_name && conf->extra_value) len += 10 + os_strlen(conf->extra_name) + os_strlen(conf->extra_value); @@ -2535,6 +2584,18 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, cont: #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + token = json_get_member(root, "capabilities"); + if (token && token->type == JSON_NUMBER) { + wpa_printf(MSG_DEBUG, "DPP: capabilities = 0x%x", + token->number); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_ENROLLEE_CAPABILITY "%d", + token->number); + auth->enrollee_capabilities = token->number; + } +#endif /* CONFIG_DPP3 */ + resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole, cert_req); @@ -2558,13 +2619,25 @@ static int dpp_parse_cred_legacy(struct dpp_config_obj *conf, if (pass && pass->type == JSON_STRING) { size_t len = os_strlen(pass->string); +#ifdef CONFIG_DPP3 + struct json_token *saepi = json_get_member(cred, "idpass"); +#endif /* CONFIG_DPP3 */ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", pass->string, len); - if (len < 8 || len > 63) + if (dpp_akm_psk(conf->akm) && (len < 8 || len > 63)) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected pass length %zu for a config object that includes PSK", + len); return -1; + } os_strlcpy(conf->passphrase, pass->string, sizeof(conf->passphrase)); +#ifdef CONFIG_DPP3 + if (saepi && saepi->type == JSON_STRING) + os_strlcpy(conf->password_id, saepi->string, + sizeof(conf->password_id)); +#endif /* CONFIG_DPP3 */ } else if (psk_hex && psk_hex->type == JSON_STRING) { if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) { wpa_printf(MSG_DEBUG, diff --git a/contrib/wpa/src/common/dpp.h b/contrib/wpa/src/common/dpp.h index 0f843da6a394..6aef72165e92 100644 --- a/contrib/wpa/src/common/dpp.h +++ b/contrib/wpa/src/common/dpp.h @@ -2,7 +2,7 @@ * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -134,6 +134,9 @@ enum dpp_connector_key { #define DPP_MAX_SHARED_SECRET_LEN 66 #define DPP_CP_LEN 64 +/* DPP Configuration Request - Enrollee Capabilities */ +#define DPP_ENROLLEE_CAPAB_SAE_PW_ID BIT(0) + struct dpp_curve_params { const char *name; size_t hash_len; @@ -260,6 +263,7 @@ struct dpp_configuration { /* For legacy configuration */ char *passphrase; + char *idpass; u8 psk[32]; int psk_set; @@ -356,6 +360,9 @@ struct dpp_authentication { u8 ssid_len; int ssid_charset; char passphrase[64]; +#ifdef CONFIG_DPP3 + char password_id[64]; +#endif /* CONFIG_DPP3 */ u8 psk[PMK_LEN]; int psk_set; enum dpp_akm akm; @@ -393,6 +400,7 @@ struct dpp_authentication { char *e_name; char *e_mud_url; int *e_band_support; + unsigned int enrollee_capabilities; #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; char *discovery_override; diff --git a/contrib/wpa/src/common/dpp_auth.c b/contrib/wpa/src/common/dpp_auth.c index f81f1eecbc7e..a6caab97a99b 100644 --- a/contrib/wpa/src/common/dpp_auth.c +++ b/contrib/wpa/src/common/dpp_auth.c @@ -1869,7 +1869,7 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, SHA256_MAC_LEN) != 0) { wpa_hexdump(MSG_DEBUG, "DPP: Expected Responder Bootstrapping Key Hash", - auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + auth->own_bi->pubkey_hash, SHA256_MAC_LEN); dpp_auth_fail(auth, "Responder Bootstrapping Key Hash mismatch"); return -1; diff --git a/contrib/wpa/src/common/dpp_backup.c b/contrib/wpa/src/common/dpp_backup.c index fb3f776d2ea2..25c0bd548c52 100644 --- a/contrib/wpa/src/common/dpp_backup.c +++ b/contrib/wpa/src/common/dpp_backup.c @@ -161,7 +161,7 @@ static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth) /* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */ attr = dpp_build_attribute(auth->conf); attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET); - if (!priv_key || !attr || !alg) + if (!attr || !alg) goto fail; /* @@ -515,6 +515,7 @@ out: wpabuf_free(enc_cont_info); return env; fail: + wpa_printf(MSG_INFO, "DPP: Failed to build DPPEnvelopedData"); wpabuf_free(env); env = NULL; goto out; diff --git a/contrib/wpa/src/common/dpp_crypto.c b/contrib/wpa/src/common/dpp_crypto.c index f17f95a2ca34..4b5f24378e7b 100644 --- a/contrib/wpa/src/common/dpp_crypto.c +++ b/contrib/wpa/src/common/dpp_crypto.c @@ -2371,6 +2371,7 @@ struct crypto_ec_point * dpp_decrypt_e_id(struct crypto_ec_key *ppkey, crypto_ec_point_invert(ec, e_id) || crypto_ec_point_add(ec, e_id, e_prime_id_point, e_id)) { crypto_ec_point_deinit(e_id, 1); + e_id = NULL; goto fail; } diff --git a/contrib/wpa/src/common/dpp_i.h b/contrib/wpa/src/common/dpp_i.h index dfa4a3cb866f..6f7e8b73cf3b 100644 --- a/contrib/wpa/src/common/dpp_i.h +++ b/contrib/wpa/src/common/dpp_i.h @@ -2,7 +2,7 @@ * DPP module internal definitions * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. diff --git a/contrib/wpa/src/common/dpp_reconfig.c b/contrib/wpa/src/common/dpp_reconfig.c index 452c50247512..e55789c87e44 100644 --- a/contrib/wpa/src/common/dpp_reconfig.c +++ b/contrib/wpa/src/common/dpp_reconfig.c @@ -569,7 +569,7 @@ fail: } -struct wpabuf * +static struct wpabuf * dpp_reconfig_build_conf(struct dpp_authentication *auth) { struct wpabuf *msg = NULL, *clear; diff --git a/contrib/wpa/src/common/dpp_tcp.c b/contrib/wpa/src/common/dpp_tcp.c index 2fad3e1d7620..5d5a161fd500 100644 --- a/contrib/wpa/src/common/dpp_tcp.c +++ b/contrib/wpa/src/common/dpp_tcp.c @@ -1,7 +1,7 @@ /* * DPP over TCP * Copyright (c) 2019-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -2291,7 +2291,7 @@ void dpp_controller_new_qr_code(struct dpp_global *dpp, dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) { struct dpp_authentication *auth = conn->auth; - if (!auth->response_pending || + if (!auth || !auth->response_pending || dpp_notify_new_qr_code(auth, bi) != 1) continue; wpa_printf(MSG_DEBUG, diff --git a/contrib/wpa/src/common/gas.c b/contrib/wpa/src/common/gas.c index ba21b225efc9..6a18c455f408 100644 --- a/contrib/wpa/src/common/gas.c +++ b/contrib/wpa/src/common/gas.c @@ -79,6 +79,12 @@ struct wpabuf * gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { + if (frag_id > 127) { + wpa_printf(MSG_INFO, + "GAS: Invalid frag_id=%u - cannot build comeback response", + frag_id); + return NULL; + } return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, status_code, frag_id, more, comeback_delay, size); diff --git a/contrib/wpa/src/common/gas_server.c b/contrib/wpa/src/common/gas_server.c index 107550039fe6..cdf77364348a 100644 --- a/contrib/wpa/src/common/gas_server.c +++ b/contrib/wpa/src/common/gas_server.c @@ -2,7 +2,7 @@ * Generic advertisement service (GAS) server * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2020, The Linux Foundation - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -44,6 +44,7 @@ struct gas_server_response { struct gas_server_handler *handler; u16 comeback_delay; bool initial_resp_sent; + bool timeout_extended; }; struct gas_server { @@ -61,6 +62,17 @@ static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx) { struct gas_server_response *response = eloop_ctx; + if (!response->resp && response->comeback_delay && + !response->timeout_extended) { + wpa_printf(MSG_DEBUG, + "GAS: Extend timeout while waiting for response"); + response->timeout_extended = true; + eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, + gas_server_response_timeout, + response, user_ctx); + return; + } + wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data", response, MAC2STR(response->dst), response->dialog_token, diff --git a/contrib/wpa/src/common/gas_server.h b/contrib/wpa/src/common/gas_server.h index 8d5eaa256bac..6036877a38c7 100644 --- a/contrib/wpa/src/common/gas_server.h +++ b/contrib/wpa/src/common/gas_server.h @@ -2,7 +2,7 @@ * Generic advertisement service (GAS) server * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2020, The Linux Foundation - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c index 2c47bf812483..e928738ee568 100644 --- a/contrib/wpa/src/common/hw_features_common.c +++ b/contrib/wpa/src/common/hw_features_common.c @@ -387,6 +387,7 @@ static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0) switch (bitmap) { case 0x6: + case 0x9: *seg0 = 0; return; case 0x8: @@ -432,6 +433,27 @@ static void punct_update_legacy_bw_160(u8 bitmap, u8 pri, } +static void punct_update_legacy_bw_320(u16 bitmap, u8 pri, + enum oper_chan_width *width, u8 *seg0) +{ + if (pri < *seg0) { + *seg0 -= 16; + if (bitmap & 0x00FF) { + *width = 1; + punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, + seg0); + } + } else { + *seg0 += 16; + if (bitmap & 0xFF00) { + *width = 1; + punct_update_legacy_bw_160((bitmap & 0xFF00) >> 8, + pri, width, seg0); + } + } +} + + void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width, u8 *seg0, u8 *seg1) { @@ -446,7 +468,10 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width, punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0); } - /* TODO: 320 MHz */ + if (*width == CONF_OPER_CHWIDTH_320MHZ && (bitmap & 0xFFFF)) { + *width = CONF_OPER_CHWIDTH_160MHZ; + punct_update_legacy_bw_320(bitmap & 0xFFFF, pri, width, seg0); + } } @@ -481,6 +506,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, data->sec_channel_offset = sec_channel_offset; data->center_freq1 = freq + sec_channel_offset * 10; data->center_freq2 = 0; + data->punct_bitmap = punct_bitmap; if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ) data->bandwidth = 80; else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ || @@ -569,7 +595,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, if (data->eht_enabled) switch (oper_chwidth) { case CONF_OPER_CHWIDTH_320MHZ: - if (!(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] & + if (eht_cap && + !(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] & EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) { wpa_printf(MSG_ERROR, "320 MHz channel width is not supported in 5 or 6 GHz"); @@ -968,7 +995,7 @@ int chan_pri_allowed(const struct hostapd_channel_data *chan) } -/* IEEE P802.11be/D3.0, Table 36-30 - Definition of the Punctured Channel +/* IEEE Std 802.11be-2024, Table 36-30 - Definition of the Punctured Channel * Information field in the U-SIG for an EHT MU PPDU using non-OFDMA * transmissions */ static const u16 punct_bitmap_80[] = { 0xF, 0xE, 0xD, 0xB, 0x7 }; @@ -1033,3 +1060,18 @@ bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap) return false; } + + +bool chan_in_current_hw_info(struct hostapd_multi_hw_info *current_hw_info, + struct hostapd_channel_data *chan) +{ + /* Assuming that if current_hw_info is not set full + * iface->current_mode->channels[] can be used to scan for channels, + * hence we return true. + */ + if (!current_hw_info) + return true; + + return current_hw_info->start_freq <= chan->freq && + current_hw_info->end_freq >= chan->freq; +} diff --git a/contrib/wpa/src/common/hw_features_common.h b/contrib/wpa/src/common/hw_features_common.h index e791c33f1736..80e33adf2d5c 100644 --- a/contrib/wpa/src/common/hw_features_common.h +++ b/contrib/wpa/src/common/hw_features_common.h @@ -58,5 +58,7 @@ int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, int ht40_plus, int pri); int chan_pri_allowed(const struct hostapd_channel_data *chan); bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap); +bool chan_in_current_hw_info(struct hostapd_multi_hw_info *current_hw_info, + struct hostapd_channel_data *chan); #endif /* HW_FEATURES_COMMON_H */ diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index ba1cb5257b65..8ad201ef763a 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -111,11 +111,6 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->hs20 = pos; elems->hs20_len = elen; break; - case HS20_OSEN_OUI_TYPE: - /* Hotspot 2.0 OSEN */ - elems->osen = pos; - elems->osen_len = elen; - break; case MBO_OUI_TYPE: /* MBO-OCE */ elems->mbo = pos; @@ -140,6 +135,46 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->sae_pk = pos + 4; elems->sae_pk_len = elen - 4; break; + case WFA_CAPA_OUI_TYPE: + elems->wfa_capab = pos + 4; + elems->wfa_capab_len = elen - 4; + break; + case WFA_RSNE_OVERRIDE_OUI_TYPE: + elems->rsne_override = pos; + elems->rsne_override_len = elen; + break; + case WFA_RSNE_OVERRIDE_2_OUI_TYPE: + elems->rsne_override_2 = pos; + elems->rsne_override_2_len = elen; + break; + case WFA_RSNXE_OVERRIDE_OUI_TYPE: + elems->rsnxe_override = pos; + elems->rsnxe_override_len = elen; + break; + case WFA_RSN_SELECTION_OUI_TYPE: + if (elen < 4 + 1) { + wpa_printf(MSG_DEBUG, + "Too short RSN Selection element ignored"); + return -1; + } + elems->rsn_selection = pos + 4; + elems->rsn_selection_len = elen - 4; + break; + case P2P2_OUI_TYPE: + /* Wi-Fi Alliance - P2P2 IE */ + elems->p2p2_ie = pos; + elems->p2p2_ie_len = elen; + break; + case PR_OUI_TYPE: + /* Wi-Fi Alliance - Proximity Ranging element */ + elems->proximity_ranging = pos; + elems->proximity_ranging_len = elen; + break; + case NAN_SDF_OUI_TYPE: + /* Wi-Fi Alliance - NAN IE */ + elems->nan_ie = pos; + elems->nan_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -320,6 +355,7 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, break; elems->key_delivery = pos; elems->key_delivery_len = elen; + total_len = &elems->key_delivery_len; break; case WLAN_EID_EXT_WRAPPED_DATA: elems->wrapped_data = pos; @@ -332,10 +368,11 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->fils_pk = pos; elems->fils_pk_len = elen; break; - case WLAN_EID_EXT_FILS_NONCE: - if (elen != FILS_NONCE_LEN) + case WLAN_EID_EXT_NONCE: + if (elen < NONCE_LEN) break; - elems->fils_nonce = pos; + elems->nonce = pos; + elems->nonce_len = elen; break; case WLAN_EID_EXT_OWE_DH_PARAM: if (elen < 2) @@ -348,10 +385,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->password_id_len = elen; break; case WLAN_EID_EXT_HE_CAPABILITIES: + if (elen < HE_CAPABILITIES_ELEM_MIN_LEN) + break; elems->he_capabilities = pos; elems->he_capabilities_len = elen; break; case WLAN_EID_EXT_HE_OPERATION: + if (elen < HE_OPERATION_ELEM_MIN_LEN) + break; elems->he_operation = pos; elems->he_operation_len = elen; break; @@ -373,10 +414,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->pasn_params_len = elen; break; case WLAN_EID_EXT_EHT_CAPABILITIES: + if (elen < EHT_CAPABILITIES_ELEM_MIN_LEN) + break; elems->eht_capabilities = pos; elems->eht_capabilities_len = elen; break; case WLAN_EID_EXT_EHT_OPERATION: + if (elen < EHT_OPERATION_ELEM_MIN_LEN) + break; elems->eht_operation = pos; elems->eht_operation_len = elen; break; @@ -391,6 +436,22 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->mbssid_known_bss = pos; elems->mbssid_known_bss_len = elen; break; + case WLAN_EID_EXT_PASN_ENCRYPTED_DATA: + elems->pasn_encrypted_data = pos; + elems->pasn_encrypted_data_len = elen; + break; + case WLAN_EID_EXT_AKM_SUITE_SELECTOR: + if (elen < RSN_SELECTOR_LEN) + break; + elems->akm_suite_selector = pos; + elems->akm_suite_selector_len = elen; + break; + case WLAN_EID_EXT_SUPPORTED_GROUPS: + if (elen < 2 || elen % 2 != 0) + break; + elems->supported_groups = pos; + elems->supported_groups_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -585,8 +646,12 @@ static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len, case WLAN_EID_MIC: elems->mic = pos; elems->mic_len = elen; - /* after mic everything is encrypted, so stop. */ - goto done; + if (elems->stop_at_mic) { + /* After MIC everything is encrypted, so stop. + */ + goto done; + } + break; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, @@ -699,6 +764,13 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, } +ParseRes ieee802_11_parse_elems_ctrl(const u8 *start, size_t len, + struct ieee802_11_elems *elems) +{ + return __ieee802_11_parse_elems(start, len, elems, elems->show_errors); +} + + /** * ieee802_11_elems_clear_ids - Clear the data for the given element IDs * @ids: Array of element IDs for which data should be cleared. @@ -899,8 +971,8 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems, elems->fils_pk = NULL; elems->fils_pk_len = 0; break; - case WLAN_EID_EXT_FILS_NONCE: - elems->fils_nonce = NULL; + case WLAN_EID_EXT_NONCE: + elems->nonce = NULL; break; case WLAN_EID_EXT_OWE_DH_PARAM: elems->owe_dh = NULL; @@ -959,14 +1031,15 @@ void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems, } -ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len, - struct ieee802_11_elems *elems, - struct wpabuf *mlbuf, - u8 link_id, bool show_errors) +static ParseRes ieee802_11_parse_link_profile(struct ieee802_11_elems *elems, + struct wpabuf *mlbuf, + u8 link_id, bool show_errors, + bool is_assoc_resp) { const struct ieee80211_eht_ml *ml; const u8 *pos; ParseRes res = ParseFailed; + size_t len; pos = wpabuf_head(mlbuf); len = wpabuf_len(mlbuf); @@ -987,14 +1060,26 @@ ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len, pos += sizeof(*ml) + pos[sizeof(*ml)]; while (len > 2) { - size_t sub_elem_len = *(pos + 1); - size_t sta_info_len; + size_t sub_elem_len, sta_info_len; u16 link_info_control; const u8 *non_inherit; + int num_frag_subelems; + + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &sub_elem_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse MLE subelem"); + goto out; + } + if ((size_t) num_frag_subelems * 2 > len) + goto out; + len -= num_frag_subelems * 2; wpa_printf(MSG_DEBUG, - "MLD: sub element: len=%zu, sub_elem_len=%zu", - len, sub_elem_len); + "MLD: sub element: len=%zu, sub_elem_len=%zu, Fragment subelems=%u", + len, sub_elem_len, num_frag_subelems); if (2 + sub_elem_len > len) { if (show_errors) @@ -1048,6 +1133,28 @@ ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len, pos += 2; sub_elem_len -= 2; + /* For association response, check status code */ + if (is_assoc_resp) { + u16 status_code; + + if (sub_elem_len < 2) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: missing status code"); + goto out; + } + + status_code = WPA_GET_LE16(pos); + if (status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "MLD: status code %u", status_code); + goto out; + } + + pos += 2; + sub_elem_len -= 2; + } + /* Handle non-inheritance */ non_inherit = get_ie_ext(pos, sub_elem_len, WLAN_EID_EXT_NON_INHERITANCE); @@ -1107,6 +1214,26 @@ out: } +ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems, + struct wpabuf *mlbuf, + u8 link_id, bool show_errors) +{ + return ieee802_11_parse_link_profile(elems, mlbuf, link_id, + show_errors, false); +} + + +ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems, + struct wpabuf *mlbuf, + u8 link_id, bool show_errors) +{ + /* ieee802_11_defrag_mle_subelem() handles subelement defragmentation + * in-place within mlbuf */ + return ieee802_11_parse_link_profile(elems, mlbuf, link_id, + show_errors, true); +} + + int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { const struct element *elem; @@ -1524,7 +1651,11 @@ ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; *channel = (freq - 5000) / 5; - *op_class = 0; /* TODO */ + if (vht_opclass) + *op_class = vht_opclass; + else + *op_class = 0; + return HOSTAPD_MODE_IEEE80211A; } @@ -2249,13 +2380,13 @@ const char * status2str(u16 status) S2S(REQUEST_DECLINED) S2S(INVALID_PARAMETERS) S2S(REJECTED_WITH_SUGGESTED_CHANGES) - S2S(INVALID_IE) - S2S(GROUP_CIPHER_NOT_VALID) - S2S(PAIRWISE_CIPHER_NOT_VALID) - S2S(AKMP_NOT_VALID) - S2S(UNSUPPORTED_RSN_IE_VERSION) - S2S(INVALID_RSN_IE_CAPAB) - S2S(CIPHER_REJECTED_PER_POLICY) + S2S(INVALID_ELEMENT) + S2S(INVALID_GROUP_CIPHER) + S2S(INVALID_PAIRWISE_CIPHER) + S2S(INVALID_AKMP) + S2S(UNSUPPORTED_RSNE_VERSION) + S2S(INVALID_RSNE_CAPABILITIES) + S2S(CIPHER_OUT_OF_POLICY) S2S(TS_NOT_CREATED) S2S(DIRECT_LINK_NOT_ALLOWED) S2S(DEST_STA_NOT_PRESENT) @@ -2263,8 +2394,8 @@ const char * status2str(u16 status) S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE) S2S(INVALID_FT_ACTION_FRAME_COUNT) S2S(INVALID_PMKID) - S2S(INVALID_MDIE) - S2S(INVALID_FTIE) + S2S(INVALID_MDE) + S2S(INVALID_FTE) S2S(REQUESTED_TCLAS_NOT_SUPPORTED) S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES) S2S(TRY_ANOTHER_BSS) @@ -2277,7 +2408,7 @@ const char * status2str(u16 status) S2S(ADV_SRV_UNREACHABLE) S2S(REQ_REFUSED_SSPN) S2S(REQ_REFUSED_UNAUTH_ACCESS) - S2S(INVALID_RSNIE) + S2S(INVALID_RSNE) S2S(U_APSD_COEX_NOT_SUPPORTED) S2S(U_APSD_COEX_MODE_NOT_SUPPORTED) S2S(BAD_INTERVAL_WITH_U_APSD_COEX) @@ -2435,7 +2566,7 @@ const struct oper_class_map global_op_class[] = { { HOSTAPD_MODE_IEEE80211A, 135, 1, 233, 16, BW80P80, NO_P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 136, 2, 2, 4, BW20, NO_P2P_SUPP }, - /* IEEE P802.11be/D5.0, Table E-4 (Global operating classes) */ + /* IEEE Std 802.11be-2024, Table E-4 (Global operating classes) */ { HOSTAPD_MODE_IEEE80211A, 137, 31, 191, 32, BW320, NO_P2P_SUPP }, /* @@ -2475,8 +2606,10 @@ static enum phy_type ieee80211_phy_type_by_freq(int freq) /* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */ -enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht) +enum phy_type ieee80211_get_phy_type(int freq, bool ht, bool vht, bool he) { + if (he) + return PHY_TYPE_HE; if (vht) return PHY_TYPE_VHT; if (ht) @@ -2541,6 +2674,9 @@ const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) { const struct element *elem; + if (!ies) + return NULL; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { if (elem->datalen >= 4 && vendor_type == WPA_GET_BE32(elem->data)) @@ -2600,14 +2736,14 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len, } else { wpa_printf(MSG_DEBUG, "Multi-AP invalid Multi-AP subelement"); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } break; case MULTI_AP_PROFILE_SUB_ELEM_TYPE: if (elen < 1) { wpa_printf(MSG_DEBUG, "Multi-AP IE invalid Multi-AP profile subelement"); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } multi_ap->profile = *pos; @@ -2622,12 +2758,12 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len, if (multi_ap->profile < MULTI_AP_PROFILE_2) { wpa_printf(MSG_DEBUG, "Multi-AP IE invalid profile to read VLAN IE"); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } if (elen < 2) { wpa_printf(MSG_DEBUG, "Multi-AP IE invalid Multi-AP VLAN subelement"); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } vlan_id = WPA_GET_LE16(pos); @@ -2635,7 +2771,7 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len, wpa_printf(MSG_INFO, "Multi-AP IE invalid Multi-AP VLAN ID %d", vlan_id); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } multi_ap->vlanid = vlan_id; break; @@ -2657,7 +2793,7 @@ u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len, if (!ext_present) { wpa_printf(MSG_DEBUG, "Multi-AP element without Multi-AP Extension subelement"); - return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_INVALID_ELEMENT; } return WLAN_STATUS_SUCCESS; @@ -2884,6 +3020,18 @@ int oper_class_bw_to_int(const struct oper_class_map *map) } +bool is_24ghz_freq(int freq) +{ + return freq >= 2400 && freq <= 2484; +} + + +bool is_5ghz_freq(int freq) +{ + return freq >= 5150 && freq <= 5885; +} + + int center_idx_to_bw_6ghz(u8 idx) { /* Channel: 2 */ @@ -3104,27 +3252,38 @@ bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len, { const u8 *end; size_t flen, i; - u32 capabs = 0; + u64 capabs = 0; if (!rsnxe || rsnxe_len == 0) return false; + + if (capab > 63) { + wpa_printf(MSG_INFO, "%s: Unsupported capab=%u", + __func__, capab); + return false; + } + end = rsnxe + rsnxe_len; flen = (rsnxe[0] & 0x0f) + 1; if (rsnxe + flen > end) return false; - if (flen > 4) - flen = 4; + if (flen > 8) + flen = 8; for (i = 0; i < flen; i++) - capabs |= rsnxe[i] << (8 * i); + capabs |= (u64) rsnxe[i] << (8 * i); - return !!(capabs & BIT(capab)); + return !!(capabs & BIT_ULL(capab)); } bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab) { - return ieee802_11_rsnx_capab_len(rsnxe ? rsnxe + 2 : NULL, - rsnxe ? rsnxe[1] : 0, capab); + if (!rsnxe) + return false; + if (rsnxe[0] == WLAN_EID_VENDOR_SPECIFIC && rsnxe[1] >= 4 + 1) + return ieee802_11_rsnx_capab_len(rsnxe + 2 + 4, rsnxe[1] - 4, + capab); + return ieee802_11_rsnx_capab_len(rsnxe + 2, rsnxe[1], capab); } @@ -3359,10 +3518,31 @@ int chwidth_freq2_to_ch_width(int chwidth, int freq2) } +enum oper_chan_width chan_width_to_oper_chwidth(enum chan_width chan_width) +{ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + return CONF_OPER_CHWIDTH_USE_HT; + case CHAN_WIDTH_80: + return CONF_OPER_CHWIDTH_80MHZ; + case CHAN_WIDTH_80P80: + return CONF_OPER_CHWIDTH_80P80MHZ; + case CHAN_WIDTH_160: + return CONF_OPER_CHWIDTH_160MHZ; + case CHAN_WIDTH_320: + return CONF_OPER_CHWIDTH_320MHZ; + default: + return CONF_OPER_CHWIDTH_USE_HT; + } +} + + struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem) { struct wpabuf *buf; - const u8 *pos, *end = data + len; + const u8 *pos, *end; size_t min_defrag_len = ext_elem ? 255 : 256; if (!data || !len) @@ -3376,6 +3556,7 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem) return NULL; pos = &data[min_defrag_len - 1]; + end = data + len; len -= min_defrag_len - 1; while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) { int ret; @@ -3399,6 +3580,72 @@ struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem) } +/** + * ieee802_11_defrag_mle_subelem - Defragment Multi-Link element subelements + * @mlbuf: Defragmented mlbuf (defragmented using ieee802_11_defrag()) + * @parent_subelem: Pointer to the subelement which may be fragmented + * @defrag_len: Defragmented length of the subelement + * Returns: Number of Fragment subelements parsed on success, -1 otherwise + * + * This function defragments a subelement present inside an Multi-Link element. + * It should be called individually for each subelement. + * + * Subelements can use the Fragment subelement if they pack more than 255 bytes + * of data, see IEEE Std 802.11be-2024, Figure 35-4 - Per-STA Profile subelement + * fragmentation within a fragmented Multi-Link element. + */ +ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf, + const u8 *parent_subelem, + size_t *defrag_len) +{ + u8 *buf, *pos, *end; + size_t len, subelem_len; + const size_t min_defrag_len = 255; + int num_frag_subelems = 0; + + if (!mlbuf || !parent_subelem) + return -1; + + buf = wpabuf_mhead_u8(mlbuf); + len = wpabuf_len(mlbuf); + end = buf + len; + + *defrag_len = parent_subelem[1]; + if (parent_subelem[1] < min_defrag_len) + return 0; + + pos = (u8 *) parent_subelem; + if (2 + parent_subelem[1] > end - pos) + return -1; + pos += 2 + parent_subelem[1]; + subelem_len = parent_subelem[1]; + + while (end - pos > 2 && + pos[0] == MULTI_LINK_SUB_ELEM_ID_FRAGMENT && pos[1]) { + size_t elen = 2 + pos[1]; + + /* This Multi-Link parent subelement has more data and is + * fragmented. */ + num_frag_subelems++; + + if (elen > (size_t) (end - pos)) + return -1; + + os_memmove(pos, pos + 2, end - (pos + 2)); + end -= 2; + mlbuf->used -= 2; + pos += elen - 2; + subelem_len += elen - 2; + + /* Deduct Fragment subelement header */ + len -= 2; + } + + *defrag_len = subelem_len; + return num_frag_subelems; +} + + const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type) { const struct element *elem; @@ -3433,3 +3680,637 @@ const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len) return &buf[mld_addr_pos]; } + + +const u8 * get_basic_mle_eml_capa(const u8 *buf, size_t len) +{ + const struct ieee80211_eht_ml *ml = + (const struct ieee80211_eht_ml *) buf; + u16 ctrl; + size_t eml_capa_pos = + MULTI_LINK_CONTROL_LEN + /* Multi-Link Control field */ + 1 + /* Common Info Length field (Basic) */ + ETH_ALEN; /* MLD MAC Address field (Basic) */ + size_t common_info_limit; + u8 common_info_len; + + if (len < MULTI_LINK_CONTROL_LEN) + return NULL; + + ctrl = le_to_host16(ml->ml_control); + if ((ctrl & MULTI_LINK_CONTROL_TYPE_MASK) != + MULTI_LINK_CONTROL_TYPE_BASIC) + return NULL; + if (!(ctrl & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)) + return NULL; + + /* Validate Common Info Length against available data */ + common_info_len = buf[MULTI_LINK_CONTROL_LEN]; + if (len < (size_t) MULTI_LINK_CONTROL_LEN + common_info_len) + return NULL; + common_info_limit = MULTI_LINK_CONTROL_LEN + common_info_len; + + if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) + eml_capa_pos += EHT_ML_LINK_ID_LEN; + + if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) + eml_capa_pos++; + + if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) + eml_capa_pos += 2; + + /* Ensure EML Capabilities field fits within the declared Common Info */ + if (eml_capa_pos + EHT_ML_EML_CAPA_LEN > common_info_limit) + return NULL; + + return &buf[eml_capa_pos]; +} + + +int get_basic_mle_link_id(const u8 *buf, size_t len) +{ + struct ieee80211_eht_ml *ml = (struct ieee80211_eht_ml *) buf; + u16 ctrl; + size_t link_id_pos = + MULTI_LINK_CONTROL_LEN + /* Multi-Link Control field */ + 1 + /* Common Info Length field (Basic) */ + ETH_ALEN; /* MLD MAC Address field (Basic) */ + size_t common_info_limit; + u8 common_info_len; + u8 link_id; + + if (len < MULTI_LINK_CONTROL_LEN) + return -1; + + ctrl = le_to_host16(ml->ml_control); + if ((ctrl & MULTI_LINK_CONTROL_TYPE_MASK) != + MULTI_LINK_CONTROL_TYPE_BASIC) + return -1; + + /* Validate Common Info Length against available data */ + common_info_len = buf[MULTI_LINK_CONTROL_LEN]; + if (len < (size_t) MULTI_LINK_CONTROL_LEN + common_info_len) + return -1; + common_info_limit = MULTI_LINK_CONTROL_LEN + common_info_len; + + if (!(ctrl & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID)) + return -1; + + if (link_id_pos + EHT_ML_LINK_ID_LEN > common_info_limit) + return -1; + + link_id = buf[link_id_pos] & BASIC_MLE_STA_CTRL_LINK_ID_MASK; + if (link_id >= MAX_NUM_MLD_LINKS) + return -1; + + return link_id; +} + + +/* Parse HT capabilities to get maximum number of supported spatial streams */ +static int +parse_ht_mcs_set_for_max_nss(const struct ieee80211_ht_capabilities *htcaps, + bool parse_for_rx) +{ + int i, max_nss_rx = 1; + u8 supported_tx_mcs_set, tx_mcs_set_defined, tx_rx_mcs_set_not_equal; + + if (!htcaps) + return max_nss_rx; + + for (i = 4; i >= 1; i--) { + if (htcaps->supported_mcs_set[i - 1] > 0) { + max_nss_rx = i; + break; + } + } + if (parse_for_rx) + return max_nss_rx; + + supported_tx_mcs_set = htcaps->supported_mcs_set[12]; + tx_mcs_set_defined = supported_tx_mcs_set & 0x1; + tx_rx_mcs_set_not_equal = (supported_tx_mcs_set >> 1) & 0x1; + if (tx_mcs_set_defined && tx_rx_mcs_set_not_equal) { + u8 max_nss_tx_field_value = (supported_tx_mcs_set >> 2) & 0x3; + + /* + * The maximum number of Tx streams is 1 more than the field + * value. + */ + return max_nss_tx_field_value + 1; + } + + return max_nss_rx; +} + + +/* Parse MCS map to get maximum number of supported spatial streams */ +static unsigned int parse_mcs_map_for_max_nss(u16 mcs_map, + unsigned int max_streams_allowed) +{ + unsigned int i, max_nss = 1; + + for (i = max_streams_allowed; i >= 1; i--) { + unsigned int stream_map = (mcs_map >> ((i - 1) * 2)) & 0x3; + + /* 3 means unsupported */ + if (stream_map != 3) { + max_nss = i; + break; + } + } + + return max_nss; +} + + +/* Parse capabilities elements to get maximum number of supported spatial + * streams */ +unsigned int get_max_nss_capability(struct ieee802_11_elems *elems, + bool parse_for_rx, enum chan_width bw) +{ + unsigned int max_nss = 1; + struct ieee80211_ht_capabilities *htcaps = + (struct ieee80211_ht_capabilities *) elems->ht_capabilities; + struct ieee80211_vht_capabilities *vhtcaps = + (struct ieee80211_vht_capabilities *) elems->vht_capabilities; + struct ieee80211_he_capabilities *hecaps = + (struct ieee80211_he_capabilities *) elems->he_capabilities; + le16 mcs_map; + + if (hecaps) { + unsigned int max_nss_he; + const u8 *optional = hecaps->optional; + + if (bw == CHAN_WIDTH_160) { + mcs_map = host_to_le16( + WPA_GET_LE16(parse_for_rx ? + &optional[0] : &optional[2])); + } else if (bw == CHAN_WIDTH_80P80) { + mcs_map = host_to_le16( + WPA_GET_LE16(parse_for_rx ? + &optional[4] : &optional[6])); + } else { + mcs_map = parse_for_rx ? + hecaps->he_basic_supported_mcs_set.rx_map : + hecaps->he_basic_supported_mcs_set.tx_map; + } + + max_nss_he = parse_mcs_map_for_max_nss( + le_to_host16(mcs_map), HE_NSS_MAX_STREAMS); + if (max_nss_he > max_nss) + max_nss = max_nss_he; + } else if (vhtcaps) { + unsigned int max_nss_vht; + + mcs_map = parse_for_rx ? + vhtcaps->vht_supported_mcs_set.rx_map : + vhtcaps->vht_supported_mcs_set.tx_map; + max_nss_vht = parse_mcs_map_for_max_nss( + le_to_host16(mcs_map), VHT_RX_NSS_MAX_STREAMS); + if (max_nss_vht > max_nss) + max_nss = max_nss_vht; + } else if (htcaps) { + unsigned int max_nss_ht; + + max_nss_ht = parse_ht_mcs_set_for_max_nss(htcaps, parse_for_rx); + if (max_nss_ht > max_nss) + max_nss = max_nss_ht; + } + + return max_nss; +} + + +/* Parse VHT/HE capabilities elements to get supported channel width */ +struct supported_chan_width +get_supported_channel_width(struct ieee802_11_elems *elems, int freq) +{ + struct supported_chan_width supported_width; + struct ieee80211_ht_capabilities *htcaps; + struct ieee80211_vht_capabilities *vhtcaps; + struct ieee80211_he_capabilities *hecaps; + struct ieee80211_eht_capabilities *ehtcaps; + + supported_width.is_40_supported = false; + supported_width.is_160_supported = false; + supported_width.is_80p80_supported = false; + supported_width.is_320_supported = false; + if (!elems) + return supported_width; + + htcaps = (struct ieee80211_ht_capabilities *) elems->ht_capabilities; + vhtcaps = (struct ieee80211_vht_capabilities *) elems->vht_capabilities; + hecaps = (struct ieee80211_he_capabilities *) elems->he_capabilities; + ehtcaps = (struct ieee80211_eht_capabilities *) elems->eht_capabilities; + + if (htcaps && + (le_to_host16(htcaps->ht_capabilities_info) & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + supported_width.is_40_supported = true; + + if (vhtcaps) { + u32 vht_capabilities_info = + le_to_host32(vhtcaps->vht_capabilities_info); + + if (vht_capabilities_info & VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) + supported_width.is_160_supported = true; + if (vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { + supported_width.is_160_supported = true; + supported_width.is_80p80_supported = true; + } + } + + if (hecaps) { + u8 channel_width_set = hecaps->he_phy_capab_info[ + HE_PHYCAP_CHANNEL_WIDTH_SET_IDX]; + + /* + * The 40 MHz capability is band specific in HE: a bit for 2.4 + * GHz vs. the shared 40/80 MHz bit for 5 GHz. + */ + if (is_24ghz_freq(freq)) { + if (channel_width_set & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) + supported_width.is_40_supported = true; + } else if (channel_width_set & + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) { + supported_width.is_40_supported = true; + } + + if (channel_width_set & + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + supported_width.is_160_supported = true; + if (channel_width_set & + HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G) + supported_width.is_80p80_supported = true; + } + + if (ehtcaps) { + if (ehtcaps->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] & + EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK) + supported_width.is_320_supported = true; + } + + return supported_width; +} + + +/* + * Parse VHT operation info fields to get operation channel width + * note that VHT operation info fields could come from the VHT Operation element + * or from the HE Operation element. + */ +static enum chan_width get_vht_operation_channel_width( + const struct ieee80211_vht_operation *vht_oper_info) +{ + enum chan_width channel_width = CHAN_WIDTH_UNKNOWN; + u8 seg0, seg1; + + switch (vht_oper_info->vht_op_info_chwidth) { + case 1: + seg0 = vht_oper_info->vht_op_info_chan_center_freq_seg0_idx; + seg1 = vht_oper_info->vht_op_info_chan_center_freq_seg1_idx; + if (seg1 && abs(seg1 - seg0) == 8) + channel_width = CHAN_WIDTH_160; + else if (seg1) + channel_width = CHAN_WIDTH_80P80; + else + channel_width = CHAN_WIDTH_80; + break; + case 2: + channel_width = CHAN_WIDTH_160; + break; + case 3: + channel_width = CHAN_WIDTH_80P80; + break; + } + + return channel_width; +} + + +/* Parse 6 GHz operation info fields to get operation channel width */ +static enum chan_width get_6ghz_operation_channel_width( + const struct ieee80211_he_6ghz_oper_info *six_ghz_oper_info) +{ + enum chan_width channel_width = CHAN_WIDTH_UNKNOWN; + u8 seg0, seg1; + + switch (six_ghz_oper_info->control & + HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK) { + case 0: + channel_width = CHAN_WIDTH_20; + break; + case 1: + channel_width = CHAN_WIDTH_40; + break; + case 2: + channel_width = CHAN_WIDTH_80; + break; + case 3: + seg0 = six_ghz_oper_info->chan_center_freq_seg0; + seg1 = six_ghz_oper_info->chan_center_freq_seg1; + if (abs(seg1 - seg0) == 8) + channel_width = CHAN_WIDTH_160; + else + channel_width = CHAN_WIDTH_80P80; + break; + } + + return channel_width; +} + + +/* Parse HE Operation element to get HE operation channel width */ +static enum chan_width get_he_operation_channel_width( + const struct ieee80211_he_operation *he_oper, size_t he_oper_len) +{ + enum chan_width channel_width = CHAN_WIDTH_UNKNOWN; + const u8 *he_oper_u8 = (const u8 *) he_oper; + bool is_6ghz_info_present, is_vht_info_present, is_cohosted_bss_present; + size_t expected_len; + + if (he_oper_len < HE_OPERATION_ELEM_MIN_LEN) + return channel_width; + + is_6ghz_info_present = + he_oper->he_oper_params & HE_OPERATION_6GHZ_OPER_INFO; + is_vht_info_present = + he_oper->he_oper_params & HE_OPERATION_VHT_OPER_INFO; + is_cohosted_bss_present = + he_oper->he_oper_params & HE_OPERATION_COHOSTED_BSS; + expected_len = HE_OPERATION_ELEM_MIN_LEN + + (is_6ghz_info_present ? HE_OPERATION_6GHZ_OPER_INFO_LEN : 0) + + (is_vht_info_present ? HE_OPERATION_VHT_OPER_INFO_LEN : 0) + + (is_cohosted_bss_present ? + HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0); + + if (he_oper_len < expected_len) + return channel_width; + + if (is_6ghz_info_present) { + struct ieee80211_he_6ghz_oper_info *six_ghz_oper_info = + (struct ieee80211_he_6ghz_oper_info *) + (he_oper_u8 + HE_OPERATION_ELEM_MIN_LEN + + (is_vht_info_present ? + HE_OPERATION_VHT_OPER_INFO_LEN : 0) + + (is_cohosted_bss_present ? + HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN : 0)); + + channel_width = + get_6ghz_operation_channel_width(six_ghz_oper_info); + } + + if (channel_width == CHAN_WIDTH_UNKNOWN && is_vht_info_present) { + struct ieee80211_vht_operation *vht_oper_info = + (struct ieee80211_vht_operation *) + (he_oper_u8 + HE_OPERATION_ELEM_MIN_LEN); + + channel_width = get_vht_operation_channel_width(vht_oper_info); + } + + return channel_width; +} + + +/* Parse EHT Operation element to get EHT operation channel width */ +static enum chan_width get_eht_operation_channel_width( + const struct ieee80211_eht_operation *eht_oper, size_t eht_oper_len) +{ + if (eht_oper_len < EHT_OPERATION_ELEM_MIN_LEN + EHT_OPER_INFO_MIN_LEN || + !(eht_oper->oper_params & EHT_OPER_INFO_PRESENT)) + return CHAN_WIDTH_UNKNOWN; + + switch (eht_oper->oper_info.control & EHT_OPER_CHANNEL_WIDTH_MASK) { + case EHT_OPER_CHANNEL_WIDTH_20MHZ: + return CHAN_WIDTH_20; + case EHT_OPER_CHANNEL_WIDTH_40MHZ: + return CHAN_WIDTH_40; + case EHT_OPER_CHANNEL_WIDTH_80MHZ: + return CHAN_WIDTH_80; + case EHT_OPER_CHANNEL_WIDTH_160MHZ: + return CHAN_WIDTH_160; + case EHT_OPER_CHANNEL_WIDTH_320MHZ: + return CHAN_WIDTH_320; + default: + return CHAN_WIDTH_UNKNOWN; + } +} + + +/* Parse HT/VHT/HE operation elements to get operation channel width */ +enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems) +{ + enum chan_width channel_width = CHAN_WIDTH_UNKNOWN; + struct ieee80211_ht_operation *ht_oper; + struct ieee80211_vht_operation *vht_oper_info; + struct ieee80211_he_operation *he_oper; + struct ieee80211_eht_operation *eht_oper; + + if (!elems) + return channel_width; + + ht_oper = (struct ieee80211_ht_operation *) elems->ht_operation; + vht_oper_info = (struct ieee80211_vht_operation *) elems->vht_operation; + he_oper = (struct ieee80211_he_operation *) elems->he_operation; + eht_oper = (struct ieee80211_eht_operation *) elems->eht_operation; + + if (eht_oper) + channel_width = get_eht_operation_channel_width( + eht_oper, elems->eht_operation_len); + + if (channel_width == CHAN_WIDTH_UNKNOWN && he_oper) + channel_width = get_he_operation_channel_width( + he_oper, elems->he_operation_len); + + if (channel_width == CHAN_WIDTH_UNKNOWN && vht_oper_info) + channel_width = get_vht_operation_channel_width(vht_oper_info); + + if (channel_width == CHAN_WIDTH_UNKNOWN && ht_oper) { + u8 sec_chan_offset = ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + + channel_width = sec_chan_offset == 0 ? + CHAN_WIDTH_20 : CHAN_WIDTH_40; + } + + return channel_width; +} + + +/* + * Get STA operation channel width from AP's operation channel width and + * STA's supported channel width + */ +enum chan_width get_sta_operation_chan_width( + enum chan_width ap_operation_chan_width, + struct supported_chan_width sta_supported_chan_width) +{ + if (ap_operation_chan_width == CHAN_WIDTH_320 && + sta_supported_chan_width.is_320_supported) + return CHAN_WIDTH_320; + + if (ap_operation_chan_width == CHAN_WIDTH_160 || + ap_operation_chan_width == CHAN_WIDTH_320) + return sta_supported_chan_width.is_160_supported ? + CHAN_WIDTH_160 : CHAN_WIDTH_80; + + if (ap_operation_chan_width == CHAN_WIDTH_80P80) + return sta_supported_chan_width.is_80p80_supported ? + CHAN_WIDTH_80P80 : CHAN_WIDTH_80; + + if (ap_operation_chan_width == CHAN_WIDTH_40) + return sta_supported_chan_width.is_40_supported ? + CHAN_WIDTH_40 : CHAN_WIDTH_20; + + return ap_operation_chan_width; +} + + +static const u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 }; +static const u8 channels_160mhz[] = { 50, 114, 163 }; + + +static u8 op_class_idx_to_chan_vht(u8 op_class, u8 idx) +{ + const u8 *chans_array; + unsigned int size; + + if (op_class == 128 || op_class == 130) { + chans_array = channels_80mhz; + size = ARRAY_SIZE(channels_80mhz); + } else if (op_class == 129) { + chans_array = channels_160mhz; + size = ARRAY_SIZE(channels_160mhz); + } else { + return 0; + } + + if (idx >= size) + return 0; + + return chans_array[idx]; +} + + +/** + * op_class_idx_to_chan - Channel index in the operating class to channel number + * @op: A pointer to the operating class object + * @idx: The channel index within the operating class. The channels are ordered + * from the lowest number to the highest, index starting from 0. + */ +u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx) +{ + u8 chan; + + if (op->bw == BW80 || op->bw == BW80P80 || op->bw == BW160) + return op_class_idx_to_chan_vht(op->op_class, idx); + + chan = op->min_chan + idx * op->inc; + if (chan > op->max_chan) + return 0; + + return chan; +} + + +static int op_class_chan_to_idx_vht(u8 op_class, u8 chan) +{ + const u8 *chans_array; + unsigned int i, size; + + if (op_class == 128 || op_class == 130) { + chans_array = channels_80mhz; + size = ARRAY_SIZE(channels_80mhz); + } else if (op_class == 129) { + chans_array = channels_160mhz; + size = ARRAY_SIZE(channels_160mhz); + } else { + return -1; + } + + for (i = 0; i < size; i++) + if (chan == chans_array[i]) + return i; + + return -1; +} + + +/** + * op_class_chan_to_idx - Channel number to channel index in the operating class + * @op: A pointer to the operating class object + * @chan: The channel number + * Returns: Channel index or -1 if channel not found + */ +int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan) +{ + if (op->bw == BW80 || op->bw == BW80P80 || op->bw == BW160) + return op_class_chan_to_idx_vht(op->op_class, chan); + + if (chan < op->min_chan || chan > op->max_chan || + (chan - op->min_chan) % op->inc) + return -1; + + return (chan - op->min_chan) / op->inc; +} + + +static int get_center_freq_80mhz(int ctrl_freq) +{ + static const int center_freqs[] = + { 5210, 5290, 5530, 5610, 5690, 5775 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(center_freqs); i++) { + if (ctrl_freq >= center_freqs[i] - 30 && + ctrl_freq <= center_freqs[i] + 30) + return center_freqs[i]; + } + + return 0; +} + + +static int get_center_freq_160mhz(int ctrl_freq) +{ + static const int center_freqs[] = { 5250, 5570 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(center_freqs); i++) { + if (ctrl_freq >= center_freqs[i] - 70 && + ctrl_freq <= center_freqs[i] + 70) + return center_freqs[i]; + } + + return 0; +} + + +/** + * ieee80211_get_center_freq - Get center frequency based on control + * frequency and operating class information + * + * @ctrl_freq: Control frequency in MHz + * @bw: The bandwidth as defined in struct oper_class_map + */ +int ieee80211_get_center_freq(int ctrl_freq, u32 bw) +{ + switch (bw) { + case BW20: + return ctrl_freq; + case BW40PLUS: + return ctrl_freq + 10; + case BW40MINUS: + return ctrl_freq - 10; + case BW80: + case BW80P80: + return get_center_freq_80mhz(ctrl_freq); + case BW160: + return get_center_freq_160mhz(ctrl_freq); + default: + return -1; + } +} diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index 9e9684c113f5..2e8565db4506 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -38,6 +38,11 @@ struct multi_ap_params { /* Parsed Information Elements */ struct ieee802_11_elems { + /* Control of parsing operations */ + bool show_errors; + bool stop_at_mic; /* Whether to stop parsing after MIC element */ + + /* Parsed data */ const u8 *ssid; const u8 *supp_rates; const u8 *ds_params; @@ -65,6 +70,8 @@ struct ieee802_11_elems { const u8 *vendor_ht_cap; const u8 *vendor_vht; const u8 *p2p; + const u8 *p2p2_ie; + const u8 *pasn_encrypted_data; const u8 *wfd; const u8 *link_id; const u8 *interworking; @@ -73,7 +80,6 @@ struct ieee802_11_elems { const u8 *ext_capab; const u8 *bss_max_idle_period; const u8 *ssid_list; - const u8 *osen; const u8 *mbo; const u8 *ampe; const u8 *mic; @@ -93,7 +99,7 @@ struct ieee802_11_elems { const u8 *key_delivery; const u8 *wrapped_data; const u8 *fils_pk; - const u8 *fils_nonce; + const u8 *nonce; const u8 *owe_dh; const u8 *power_capab; const u8 *roaming_cons_sel; @@ -116,6 +122,15 @@ struct ieee802_11_elems { const u8 *prior_access_mle; const u8 *mbssid_known_bss; const u8 *mbssid; + const u8 *rsne_override; + const u8 *rsne_override_2; + const u8 *rsnxe_override; + const u8 *rsn_selection; + const u8 *wfa_capab; + const u8 *proximity_ranging; + const u8 *akm_suite_selector; + const u8 *supported_groups; + const u8 *nan_ie; u8 ssid_len; u8 supp_rates_len; @@ -136,13 +151,14 @@ struct ieee802_11_elems { u8 vendor_ht_cap_len; u8 vendor_vht_len; u8 p2p_len; + u8 p2p2_ie_len; + u8 pasn_encrypted_data_len; u8 wfd_len; u8 interworking_len; u8 qos_map_set_len; u8 hs20_len; u8 ext_capab_len; u8 ssid_list_len; - u8 osen_len; u8 mbo_len; u8 ampe_len; u8 mic_len; @@ -156,9 +172,10 @@ struct ieee802_11_elems { u8 fils_key_confirm_len; size_t fils_hlp_len; u8 fils_ip_addr_assign_len; - u8 key_delivery_len; + size_t key_delivery_len; size_t wrapped_data_len; u8 fils_pk_len; + u8 nonce_len; u8 owe_dh_len; u8 power_capab_len; u8 roaming_cons_sel_len; @@ -179,6 +196,15 @@ struct ieee802_11_elems { size_t prior_access_mle_len; u8 mbssid_known_bss_len; u8 mbssid_len; + size_t rsne_override_len; + size_t rsne_override_2_len; + size_t rsnxe_override_len; + size_t rsn_selection_len; + u8 wfa_capab_len; + size_t proximity_ranging_len; + u8 akm_suite_selector_len; + u8 supported_groups_len; + size_t nan_len; struct mb_ies_info mb_ies; @@ -196,14 +222,18 @@ typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors); +ParseRes ieee802_11_parse_elems_ctrl(const u8 *start, size_t len, + struct ieee802_11_elems *elems); void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems, const u8 *ids, size_t num); void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems, const u8 *ids, size_t num); -ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len, - struct ieee802_11_elems *elems, +ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems, struct wpabuf *mlbuf, u8 link_id, bool show_errors); +ParseRes ieee802_11_parse_link_assoc_resp(struct ieee802_11_elems *elems, + struct wpabuf *mlbuf, + u8 link_id, bool show_errors); int ieee802_11_ie_count(const u8 *ies, size_t ies_len); struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type); @@ -244,7 +274,7 @@ int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, u16 num_modes); int is_dfs_global_op_class(u8 op_class); bool is_80plus_op_class(u8 op_class); -enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); +enum phy_type ieee80211_get_phy_type(int freq, bool ht, bool vht, bool he); int supp_rates_11b_only(struct ieee802_11_elems *elems); int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, @@ -289,6 +319,8 @@ u8 country_to_global_op_class(const char *country, u8 op_class); const struct oper_class_map * get_oper_class(const char *country, u8 op_class); int oper_class_bw_to_int(const struct oper_class_map *map); +bool is_24ghz_freq(int freq); +bool is_5ghz_freq(int freq); int center_idx_to_bw_6ghz(u8 idx); bool is_6ghz_freq(int freq); bool is_6ghz_op_class(u8 op_class); @@ -299,6 +331,10 @@ bool is_same_band(int freq1, int freq2); #define IS_2P4GHZ(n) (n >= 2412 && n <= 2484) #define IS_5GHZ(n) (n > 4000 && n < 5895) +u8 op_class_idx_to_chan(const struct oper_class_map *op, u8 idx); +int op_class_chan_to_idx(const struct oper_class_map *op, u8 chan); +int ieee80211_get_center_freq(int ctrl_freq, u32 bw); + int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len); @@ -309,6 +345,7 @@ bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab); int op_class_to_bandwidth(u8 op_class); enum oper_chan_width op_class_to_ch_width(u8 op_class); int chwidth_freq2_to_ch_width(int chwidth, int freq2); +enum oper_chan_width chan_width_to_oper_chwidth(enum chan_width chan_width); /* element iteration helpers */ #define for_each_element(_elem, _data, _datalen) \ @@ -367,7 +404,31 @@ int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed, struct ieee80211_edmg_config requested); struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem); +ssize_t ieee802_11_defrag_mle_subelem(struct wpabuf *mlbuf, + const u8 *parent_subelem, + size_t *defrag_len); const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type); const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len); +const u8 * get_basic_mle_eml_capa(const u8 *buf, size_t len); +int get_basic_mle_link_id(const u8 *buf, size_t len); + +unsigned int get_max_nss_capability(struct ieee802_11_elems *elems, + bool parse_for_rx, enum chan_width bw); + +struct supported_chan_width { + bool is_40_supported; + bool is_160_supported; + bool is_80p80_supported; + bool is_320_supported; +}; + +struct supported_chan_width +get_supported_channel_width(struct ieee802_11_elems *elems, int freq); + +enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems); + +enum chan_width get_sta_operation_chan_width( + enum chan_width ap_operation_chan_width, + struct supported_chan_width sta_supported_width); #endif /* IEEE802_11_COMMON_H */ diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h index 0fe63102603f..2d3b4677447b 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -21,7 +21,7 @@ #define WLAN_FC_RETRY 0x0800 #define WLAN_FC_PWRMGT 0x1000 #define WLAN_FC_MOREDATA 0x2000 -#define WLAN_FC_ISWEP 0x4000 +#define WLAN_FC_PROTECTED 0x4000 #define WLAN_FC_HTC 0x8000 #define WLAN_FC_S1G_BEACON_NEXT_TBTT 0x0100 @@ -98,28 +98,39 @@ #define WLAN_AUTH_FILS_SK_PFS 5 #define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_PASN 7 +#define WLAN_AUTH_802_1X 8 +#define WLAN_AUTH_EPPKE 9 #define WLAN_AUTH_LEAP 128 +/* Authentication transaction sequence number */ +#define WLAN_AUTH_TR_SEQ_SAE_COMMIT 1 +#define WLAN_AUTH_TR_SEQ_SAE_CONFIRM 2 + +#define WLAN_AUTH_TR_SEQ_PASN_AUTH1 1 +#define WLAN_AUTH_TR_SEQ_PASN_AUTH2 2 +#define WLAN_AUTH_TR_SEQ_PASN_AUTH3 3 + #define WLAN_AUTH_CHALLENGE_LEN 128 +/* IEEE Std 802.11-2024, 9.4.1.4 (Capability Information field) */ #define WLAN_CAPABILITY_ESS BIT(0) #define WLAN_CAPABILITY_IBSS BIT(1) -#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) -#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +/* bit 2 is reserved (was WLAN_CAPABILITY_CF_POLLABLE) */ +/* bit 3 is reserved (was WLAN_CAPABILITY_CF_POLL_REQUEST) */ #define WLAN_CAPABILITY_PRIVACY BIT(4) #define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) -#define WLAN_CAPABILITY_PBCC BIT(6) -#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) +/* bit 6 is reserved (was WLAN_CAPABILITY_PBCC) */ +/* bit 7 is reserved (was WLAN_CAPABILITY_CHANNEL_AGILITY) */ #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) #define WLAN_CAPABILITY_QOS BIT(9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) #define WLAN_CAPABILITY_APSD BIT(11) #define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12) -#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) -#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) -#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) +#define WLAN_CAPABILITY_EPD BIT(13) +/* bit 14 is reserved (was WLAN_CAPABILITY_DELAYED_BLOCK_ACK) */ +/* bit 15 is reserved (was WLAN_CAPABILITY_IMM_BLOCK_ACK) */ -/* Status codes (IEEE Std 802.11-2020, 9.4.1.9, Table 9-50) */ +/* Status codes (IEEE Std 802.11-2024, 9.4.1.9, Table 9-80) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 @@ -153,13 +164,13 @@ #define WLAN_STATUS_REQUEST_DECLINED 37 #define WLAN_STATUS_INVALID_PARAMETERS 38 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39 -#define WLAN_STATUS_INVALID_IE 40 -#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 -#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 -#define WLAN_STATUS_AKMP_NOT_VALID 43 -#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 -#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 -#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 +#define WLAN_STATUS_INVALID_ELEMENT 40 +#define WLAN_STATUS_INVALID_GROUP_CIPHER 41 +#define WLAN_STATUS_INVALID_PAIRWISE_CIPHER 42 +#define WLAN_STATUS_INVALID_AKMP 43 +#define WLAN_STATUS_UNSUPPORTED_RSNE_VERSION 44 +#define WLAN_STATUS_INVALID_RSNE_CAPABILITIES 45 +#define WLAN_STATUS_CIPHER_OUT_OF_POLICY 46 #define WLAN_STATUS_TS_NOT_CREATED 47 #define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 @@ -167,8 +178,8 @@ #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 #define WLAN_STATUS_INVALID_PMKID 53 -#define WLAN_STATUS_INVALID_MDIE 54 -#define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_INVALID_MDE 54 +#define WLAN_STATUS_INVALID_FTE 55 #define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56 #define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57 #define WLAN_STATUS_TRY_ANOTHER_BSS 58 @@ -181,7 +192,7 @@ #define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 -#define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_INVALID_RSNE 72 #define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73 #define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74 #define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75 @@ -233,6 +244,8 @@ #define WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED 139 #define WLAN_STATUS_EPCS_DENIED_VERIFICATION_FAILURE 140 #define WLAN_STATUS_DENIED_OPERATION_PARAMETER_UPDATE 141 +#define WLAN_STATUS_802_1_X_AUTH_FAILED 152 +#define WLAN_STATUS_802_1_X_AUTH_SUCCESS 153 /* Reason codes (IEEE Std 802.11-2020, 9.4.1.7, Table 9-90) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -495,7 +508,7 @@ #define WLAN_EID_EXT_EXTENDED_REQUEST 10 #define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 #define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 -#define WLAN_EID_EXT_FILS_NONCE 13 +#define WLAN_EID_EXT_NONCE 13 #define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 #define WLAN_EID_EXT_OWE_DH_PARAM 32 #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 @@ -504,6 +517,7 @@ #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38 #define WLAN_EID_EXT_SPATIAL_REUSE 39 #define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42 +#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52 #define WLAN_EID_EXT_OCV_OCI 54 #define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55 #define WLAN_EID_EXT_NON_INHERITANCE 56 @@ -524,6 +538,10 @@ #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110 #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114 +#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_SUPPORTED_GROUPS 161 /* Extended Capabilities field */ #define WLAN_EXT_CAPAB_20_40_COEX 0 @@ -606,6 +624,7 @@ #define WLAN_EXT_CAPAB_BEACON_PROTECTION 84 #define WLAN_EXT_CAPAB_MSCS 85 #define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88 +#define WLAN_EXT_CAPAB_KNOWN_STA_IDENTIFICATION 102 /* Extended RSN Capabilities */ /* bits 0-3: Field length (n-1) */ @@ -615,8 +634,15 @@ #define WLAN_RSNX_CAPAB_SECURE_LTF 8 #define WLAN_RSNX_CAPAB_SECURE_RTT 9 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10 +#define WLAN_RSNX_CAPAB_SPP_A_MSDU 14 #define WLAN_RSNX_CAPAB_URNM_MFPR 15 +#define WLAN_RSNX_CAPAB_KEK_IN_PASN 18 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21 +#define WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION 27 +#define WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES 28 +#define WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY 29 +#define WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE 34 +#define WLAN_RSNX_CAPAB_UNAUTH_EPPKE 36 /* Multiple BSSID element subelements */ #define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0 @@ -700,7 +726,7 @@ #define WLAN_PA_FILS_DISCOVERY 34 #define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47 -/* HT Action field values (IEEE P802.11-REVme/D4.0, 9.6.11.1, Table 9-491) */ +/* HT Action field values (IEEE Std 802.11-2024, 9.6.11.1, Table 9-517) */ #define WLAN_HT_ACTION_NOTIFY_CHANWIDTH 0 #define WLAN_HT_ACTION_SMPS 1 #define WLAN_HT_ACTION_CSI 4 @@ -708,7 +734,7 @@ #define WLAN_HT_ACTION_COMPRESSED_BF 6 #define WLAN_HT_ACTION_ASEL_IDX_FEEDBACK 7 -/* VHT Action field values (IEEE P802.11-REVme/D4.0, 9.6.22.1, Table 9-579) */ +/* VHT Action field values (IEEE Std 802.11-2024, 9.6.22.1, Table 9-605) */ #define WLAN_VHT_ACTION_COMPRESSED_BF 0 #define WLAN_VHT_ACTION_GROUP_ID_MGMT 1 #define WLAN_VHT_ACTION_OPMODE_NOTIF 2 @@ -774,6 +800,21 @@ #define WLAN_PROT_FTM 2 #define WLAN_PROT_FTM_REPORT 3 +/* Protected EHT Action field values (IEEE Std 802.11be-2024, Table 9-658d) */ +#define WLAN_PROT_EHT_T2L_MAPPING_REQUEST 0 +#define WLAN_PROT_EHT_T2L_MAPPING_RESPONSE 1 +#define WLAN_PROT_EHT_T2L_MAPPING_TEARDOWN 2 +#define WLAN_PROT_EHT_EPCS_ENABLE_REQUEST 3 +#define WLAN_PROT_EHT_EPCS_ENABLE_RESPONSE 4 +#define WLAN_PROT_EHT_EPCS_ENABLE_TEARDOWN 5 +#define WLAN_PROT_EHT_EML_OPMODE_NOTIF 6 +#define WLAN_PROT_EHT_LINK_RECOMMENDATION 7 +#define WLAN_PROT_EHT_MLO_UPDATE_REQUEST 8 +#define WLAN_PROT_EHT_MLO_UPDATE_RESPONSE 9 +#define WLAN_PROT_EHT_LINK_RECONFIG_NOTIFY 10 +#define WLAN_PROT_EHT_LINK_RECONFIG_REQUEST 11 +#define WLAN_PROT_EHT_LINK_RECONFIG_RESPONSE 12 + /* Radio Measurement capabilities (from RM Enabled Capabilities element) * IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */ /* byte 1 (out of 5) */ @@ -786,6 +827,7 @@ #define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) /* byte 5 (out of 5) */ #define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2) +#define WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT BIT(3) /* * IEEE Std 802.11-2020, 9.4.2.20.19 (Fine Timing Measurement Range @@ -941,7 +983,7 @@ enum lci_req_subelem { LCI_REQ_SUBELEM_MAX_AGE = 4, }; -#define FILS_NONCE_LEN 16 +#define NONCE_LEN 16 #define FILS_SESSION_LEN 8 #define FILS_CACHE_ID_LEN 2 #define FILS_MAX_KEY_AUTH_LEN 48 @@ -1160,12 +1202,40 @@ struct ieee80211_mgmt { u8 dialog_token; u8 variable[]; } STRUCT_PACKED rrm; + struct { + u8 action; /* Protected EHT - 11 */ + u8 dialog_token; + /* Reconfiguration Multi-Link element, + * OCI element (optional) */ + u8 variable[]; + } STRUCT_PACKED link_reconf_req; + struct { + u8 action; /* Protected EHT - 12 */ + u8 dialog_token; + u8 count; + /* Reconfiguration status list, + * Group Key Data (optional), + * OCI element (optional), + * Basic Multi-Link element (optional) */ + u8 variable[]; + } STRUCT_PACKED link_reconf_resp; + struct { + u8 action; + u8 oui[3]; + u8 oui_type; + u8 subtype; + u8 variable[0]; + } STRUCT_PACKED naf; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +#define IEEE80211_MIN_ACTION_LEN(type) \ + (offsetof(struct ieee80211_mgmt, u.action.u.type) + \ + sizeof(((struct ieee80211_mgmt *) 0)->u.action.u.type)) + #define IEEE80211_MAX_MMPDU_SIZE 2304 /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ @@ -1343,6 +1413,7 @@ struct ieee80211_ampe_ie { #define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11)) /* B36..B39 - Reserved */ +#define BSS_MEMBERSHIP_SELECTOR_EHT_PHY 121 #define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122 #define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123 #define BSS_MEMBERSHIP_SELECTOR_EPD 124 @@ -1425,15 +1496,16 @@ struct ieee80211_ampe_ie { #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define P2P2_IE_VENDOR_TYPE 0x506f9a28 #define WFD_IE_VENDOR_TYPE 0x506f9a0a #define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 -#define OSEN_IE_VENDOR_TYPE 0x506f9a12 #define NAN_IE_VENDOR_TYPE 0x506f9a13 #define NAN_SDF_VENDOR_TYPE 0x506f9a13 -#define NAN_OUI_TYPE 0x13 +#define NAN_SDF_OUI_TYPE 0x13 #define MBO_IE_VENDOR_TYPE 0x506f9a16 #define NAN_NAF_VENDOR_TYPE 0x506f9a18 +#define NAN_NAF_OUI_TYPE 0x18 #define MBO_OUI_TYPE 22 #define OWE_IE_VENDOR_TYPE 0x506f9a1c #define OWE_OUI_TYPE 28 @@ -1446,6 +1518,17 @@ struct ieee80211_ampe_ie { #define QM_IE_OUI_TYPE 0x22 #define WFA_CAPA_IE_VENDOR_TYPE 0x506f9a23 #define WFA_CAPA_OUI_TYPE 0x23 +#define WFA_RSNE_OVERRIDE_OUI_TYPE 0x29 +#define WFA_RSNE_OVERRIDE_2_OUI_TYPE 0x2a +#define WFA_RSNXE_OVERRIDE_OUI_TYPE 0x2b +#define WFA_RSN_SELECTION_OUI_TYPE 0x2c +#define RSNE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a29 +#define RSNE_OVERRIDE_2_IE_VENDOR_TYPE 0x506f9a2a +#define RSNXE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a2b +#define RSN_SELECTION_IE_VENDOR_TYPE 0x506f9a2c +/* Proximity Ranging (PR) */ +#define PR_IE_VENDOR_TYPE 0x506f9a2e +#define PR_OUI_TYPE 0x2e #define MULTI_AP_SUB_ELEM_TYPE 0x06 #define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07 @@ -1579,7 +1662,6 @@ enum wmm_ac { #define HS20_INDICATION_OUI_TYPE 16 #define HS20_ANQP_OUI_TYPE 17 -#define HS20_OSEN_OUI_TYPE 18 #define HS20_ROAMING_CONS_SEL_OUI_TYPE 29 #define HS20_STYPE_QUERY_LIST 1 #define HS20_STYPE_CAPABILITY_LIST 2 @@ -1588,11 +1670,6 @@ enum wmm_ac { #define HS20_STYPE_CONNECTION_CAPABILITY 5 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6 #define HS20_STYPE_OPERATING_CLASS 7 -#define HS20_STYPE_OSU_PROVIDERS_LIST 8 -#define HS20_STYPE_ICON_REQUEST 10 -#define HS20_STYPE_ICON_BINARY_FILE 11 -#define HS20_STYPE_OPERATOR_ICON_METADATA 12 -#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13 #define HS20_DGAF_DISABLED 0x01 #define HS20_PPS_MO_ID_PRESENT 0x02 @@ -1602,7 +1679,6 @@ enum wmm_ac { #endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ -#define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 #define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2 #define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3 @@ -1710,6 +1786,7 @@ enum mbo_transition_reject_reason { /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 +#define P2P2_OUI_TYPE 0x28 enum p2p_attr_id { P2P_ATTR_STATUS = 0, @@ -1740,6 +1817,13 @@ enum p2p_attr_id { P2P_ATTR_SESSION_ID = 26, P2P_ATTR_FEATURE_CAPABILITY = 27, P2P_ATTR_PERSISTENT_GROUP = 28, + P2P_ATTR_CAPABILITY_EXTENSION = 29, + P2P_ATTR_WLAN_AP_INFORMATION = 30, + P2P_ATTR_DEVICE_IDENTITY_KEY = 31, + P2P_ATTR_DEVICE_IDENTITY_RESOLUTION = 32, + P2P_ATTR_PAIRING_AND_BOOTSTRAPPING = 33, + P2P_ATTR_PASSWORD = 34, + P2P_ATTR_ACTION_FRAME_WRAPPER = 35, P2P_ATTR_VENDOR_SPECIFIC = 221 }; @@ -1764,6 +1848,31 @@ enum p2p_attr_id { #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) +/* P2P Capability Extension attribute - Capability info */ +#define P2P_PCEA_LEN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#define P2P_PCEA_6GHZ BIT(4) +#define P2P_PCEA_REG_INFO BIT(5) +#define P2P_PCEA_DFS_OWNER BIT(6) +#define P2P_PCEA_CLI_REQ_CS BIT(7) +#define P2P_PCEA_PAIRING_CAPABLE BIT(8) +#define P2P_PCEA_PAIRING_SETUP_ENABLED BIT(9) +#define P2P_PCEA_PMK_CACHING BIT(10) +#define P2P_PCEA_PASN_TYPE BIT(11) +#define P2P_PCEA_TWT_POWER_MGMT BIT(12) + +/* P2P Pairing Bootstrapping Method attribute - Bootstrapping Method */ +#define P2P_PBMA_OPPORTUNISTIC BIT(0) +#define P2P_PBMA_PIN_CODE_DISPLAY BIT(1) +#define P2P_PBMA_PASSPHRASE_DISPLAY BIT(2) +#define P2P_PBMA_QR_DISPLAY BIT(3) +#define P2P_PBMA_NFC_TAG BIT(4) +#define P2P_PBMA_PIN_CODE_KEYPAD BIT(5) +#define P2P_PBMA_PASSPHRASE_KEYPAD BIT(6) +#define P2P_PBMA_QR_SCAN BIT(7) +#define P2P_PBMA_NFC_READER BIT(8) +#define P2P_PBMA_SERVICE_MANAGED BIT(14) +#define P2P_PBMA_HANDSHAKE_SKIP BIT(15) + /* P2PS Coordination Protocol Transport Bitmap */ #define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) #define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) @@ -1795,6 +1904,7 @@ enum p2p_status_code { P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, P2P_SC_FAIL_REJECTED_BY_USER = 11, P2P_SC_SUCCESS_DEFERRED = 12, + P2P_SC_COMEBACK = 13, }; enum p2p_role_indication { @@ -1989,6 +2099,7 @@ enum bss_trans_mgmt_reason { #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 #define WNM_NEIGHBOR_VHT_CAPAB 191 #define WNM_NEIGHBOR_VHT_OPER 192 +#define WNM_NEIGHBOR_MULTI_LINK 201 /* QoS action */ enum qos_action { @@ -2322,9 +2433,10 @@ enum phy_type { PHY_TYPE_S1G = 11, PHY_TYPE_CDMG = 12, PHY_TYPE_CMMG = 13, + PHY_TYPE_HE = 14, }; -/* IEEE Std 802.11-2020, 9.4.2.36 - Neighbor Report element */ +/* IEEE Std 802.11-2024, 9.4.2.35 - Neighbor Report element */ /* BSSID Information Field */ #define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0) #define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1) @@ -2335,14 +2447,21 @@ enum phy_type { #define NEI_REP_BSSID_INFO_QOS BIT(5) #define NEI_REP_BSSID_INFO_APSD BIT(6) #define NEI_REP_BSSID_INFO_RM BIT(7) -#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8) -#define NEI_REP_BSSID_INFO_IMM_BA BIT(9) +/* bit 8 is reserved (was NEI_REP_BSSID_INFO_DELAYED_BA) */ +/* bit 9 is reserved (was NEI_REP_BSSID_INFO_IMM_BA) */ #define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10) #define NEI_REP_BSSID_INFO_HT BIT(11) #define NEI_REP_BSSID_INFO_VHT BIT(12) #define NEI_REP_BSSID_INFO_FTM BIT(13) #define NEI_REP_BSSID_INFO_HE BIT(14) +#define NEI_REP_BSSID_INFO_ER_BSS BIT(15) +#define NEI_REP_BSSID_INFO_COLOCATED_AP BIT(16) +#define NEI_REP_BSSID_INFO_UNSOLIC_PROBE_RESP BIT(17) +#define NEI_REP_BSSID_INFO_MEMBER_OF_ESS_WITH_COLOC_AP BIT(18) +#define NEI_REP_BSSID_INFO_OCT_WITH_REPORTING_AP BIT(19) +#define NEI_REP_BSSID_INFO_COLOCATED_WITH_6GHZ BIT(20) #define NEI_REP_BSSID_INFO_EHT BIT(21) +#define NEI_REP_BSSID_INFO_DMG_POSITIONING BIT(22) /* * IEEE Std 802.11-2020, Table 9-175 - HT/VHT Operation Information @@ -2360,9 +2479,16 @@ enum nr_chan_width { struct ieee80211_he_capabilities { u8 he_mac_capab_info[6]; u8 he_phy_capab_info[11]; - /* Followed by 4, 8, or 12 octets of Supported HE-MCS And NSS Set field - * and optional variable length PPE Thresholds field. */ - u8 optional[37]; + /* Supported HE-MCS And NSS Set field */ + struct { + le16 rx_map; /* Rx HE-MCS Map <= 80 MHz */ + le16 tx_map; /* Tx HE-MCS Map <= 80 MHz */ + /* Followed by 0, 4, or 8 octets of optional Rx/Tx HE-MCS maps + * for 160 MHz and 80+80 MHz. These are included in the + * optional[] below. */ + } he_basic_supported_mcs_set; + /* Followed by optional variable length PPE Thresholds field. */ + u8 optional[33]; } STRUCT_PACKED; #define IEEE80211_HE_CAPAB_MIN_LEN (6 + 11) @@ -2439,6 +2565,8 @@ struct ieee80211_spatial_reuse { u8 params[19]; } STRUCT_PACKED; +#define HE_CAPABILITIES_ELEM_MIN_LEN 21 + /* HE Capabilities Information defines */ #define HE_MACCAP_TWT_RESPONDER ((u8) BIT(2)) @@ -2490,10 +2618,17 @@ struct ieee80211_spatial_reuse { #define HE_OPERATION_BSS_COLOR_OFFSET 24 #define HE_OPERATION_BSS_COLOR_MAX 64 +/* HE operation fields length */ +#define HE_OPERATION_ELEM_MIN_LEN 6 +#define HE_OPERATION_VHT_OPER_INFO_LEN 3 +#define HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN 1 +#define HE_OPERATION_6GHZ_OPER_INFO_LEN 5 + /** * enum he_reg_info_6ghz_ap_type - Allowed Access Point types for 6 GHz Band * - * IEEE P802.11-REVme/D4.0, Table E-12 (Regulatory Info subfield encoding) + * IEEE Std 802.11-2024, Table E-12 (Regulatory Info subfield interpretation by + * non-AP STAs with dot11ExtendedRegInfoSupport not set to true) */ enum he_reg_info_6ghz_ap_type { HE_REG_INFO_6GHZ_AP_TYPE_INDOOR = 0, @@ -2583,7 +2718,7 @@ struct ieee80211_he_mu_edca_parameter_set { * transmit power is specified for the corresponding 20 MHz channel. */ #define RNR_20_MHZ_PSD_MAX_TXPOWER 127 -/* IEEE P802.11be/D5.0, Figure 9-704c - MLD Parameters subfield format */ +/* IEEE Std 802.11be-2024, Figure 9-734c - MLD Parameters subfield format */ /* B0..B7: AP MLD ID */ /* B8..B11: Link ID */ /* B12..B19: BSS Parameters Change Count */ @@ -2592,24 +2727,30 @@ struct ieee80211_he_mu_edca_parameter_set { /* B21: Disabled Link Indication */ #define RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED 0x20 -/* IEEE P802.11be/D2.3, 9.4.2.311 - EHT Operation element */ +/* IEEE Std 802.11be-2024, 9.4.2.321 - EHT Operation element */ +#define EHT_OPERATION_ELEM_MIN_LEN 5 -/* Figure 9-1002b: EHT Operation Parameters field subfields */ +/* Figure 9-1074j: EHT Operation Parameters field format */ #define EHT_OPER_INFO_PRESENT BIT(0) #define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1) #define EHT_OPER_DEFAULT_PE_DURATION BIT(2) #define EHT_OPER_GROUP_ADDR_BU_INDICATION_LIMIT BIT(3) #define EHT_OPER_GROUP_ADDR_BU_INDICATION_EXPONENT (BIT(4) | BIT(5)) +#define EHT_OPER_MCS15_DISABLE BIT(6) #define EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE 2 -/* Control subfield: Channel Width subfield; see Table 9-401b */ +/* Control subfield: Channel Width subfield; see Table 9-417e (Channel width, + * CCFS0, and CCFS1 subfields) */ +#define EHT_OPER_CHANNEL_WIDTH_MASK 0x7 #define EHT_OPER_CHANNEL_WIDTH_20MHZ 0 #define EHT_OPER_CHANNEL_WIDTH_40MHZ 1 #define EHT_OPER_CHANNEL_WIDTH_80MHZ 2 #define EHT_OPER_CHANNEL_WIDTH_160MHZ 3 #define EHT_OPER_CHANNEL_WIDTH_320MHZ 4 -/* Figure 9-1002c: EHT Operation Information field format */ +/* Figure 9-1074k: EHT Operation Information field format */ +#define EHT_OPER_INFO_MIN_LEN 3 + struct ieee80211_eht_oper_info { u8 control; /* B0..B2: Channel Width */ u8 ccfs0; @@ -2617,16 +2758,17 @@ struct ieee80211_eht_oper_info { le16 disabled_chan_bitmap; /* 0 or 2 octets */ } STRUCT_PACKED; -/* Figure 9-1002a: EHT Operation element format */ +/* Figure 9-1074i: EHT Operation element format */ struct ieee80211_eht_operation { u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */ u8 basic_eht_mcs_nss_set[4]; struct ieee80211_eht_oper_info oper_info; /* 0 or 3 or 5 octets */ } STRUCT_PACKED; -/* IEEE P802.11be/D1.5, 9.4.2.313 - EHT Capabilities element */ +/* IEEE Std 802.11be-2024, 9.4.2.323 - EHT Capabilities element */ +#define EHT_CAPABILITIES_ELEM_MIN_LEN 11 -/* Figure 9-1002af: EHT MAC Capabilities Information field */ +/* Figure 9-1074ap: EHT MAC Capabilities Information field format */ #define EHT_MACCAP_EPCS_PRIO BIT(0) #define EHT_MACCAP_OM_CONTROL BIT(1) #define EHT_MACCAP_TRIGGERED_TXOP_MODE1 BIT(2) @@ -2639,7 +2781,7 @@ struct ieee80211_eht_operation { #define EHT_MACCAP_MAX_MPDU_LEN_11454 BIT(7) #define EHT_MACCAP_MAX_AMPDU_LEN_EXP_EXT BIT(8) -/* Figure 9-1002ag: EHT PHY Capabilities Information field format +/* Figure 9-1074aq: EHT PHY Capabilities Information field format * _IDX indicates the octet index within the field */ #define EHT_PHY_CAPAB_LEN 9 @@ -2662,20 +2804,20 @@ struct ieee80211_eht_operation { EHT_PHYCAP_MU_BEAMFORMER_160MHZ | \ EHT_PHYCAP_MU_BEAMFORMER_320MHZ) -/* Figure 9-1002ah: Supported EHT-MCS and NSS Set field format */ +/* Figure 9-1074ar: Supported EHT-MCS and NSS Set field format */ #define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY 4 #define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS 3 #define EHT_MCS_NSS_CAPAB_LEN 9 /* - * Figure 9-1002ak: EHT PPE Thresholds field format + * Figure 9-1074au: EHT PPE Thresholds field format * Maximum PPE threshold length: 62 octets * NSS: 4 bits (maximum NSS: 16), RU index: 5 bits, each pair: 6 bits * 4 + 5 + 5 * 16 * 6 = 489 bits, Padding: 7 bits */ #define EHT_PPE_THRESH_CAPAB_LEN 62 -/* 9.4.2.313.5: EHT PPE Thresholds field */ +/* 9.4.2.323.5: EHT PPE Thresholds field */ #define EHT_PPE_THRES_NSS_SHIFT 0 #define EHT_PPE_THRES_NSS_MASK ((u8) (BIT(0) | BIT(1) | \ BIT(2) | BIT(3))) @@ -2686,7 +2828,7 @@ struct ieee80211_eht_operation { #define EHT_NSS_MAX_STREAMS 8 -/* Figure 9-1002ae: EHT Capabilities element format */ +/* Figure 9-1074ao: EHT Capabilities element format */ struct ieee80211_eht_capabilities { /* EHT MAC Capabilities Information */ le16 mac_cap; @@ -2698,13 +2840,13 @@ struct ieee80211_eht_capabilities { #define IEEE80211_EHT_CAPAB_MIN_LEN (2 + 9) -/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */ +/* IEEE Std P802.11be-2024, 9.4.2.322 - Multi-Link element */ -/* Figure 9-1002f: Multi-Link Control field */ +/* Figure 9-1074n: Multi-Link Control field */ #define MULTI_LINK_CONTROL_TYPE_MASK 0x07 #define MULTI_LINK_CONTROL_LEN 2 -/* Table 9-401c: Mult-Link element Type subfield encoding */ +/* Table 9-417f: [Multi-Link element] Type subfield encoding */ #define MULTI_LINK_CONTROL_TYPE_BASIC 0 #define MULTI_LINK_CONTROL_TYPE_PROBE_REQ 1 #define MULTI_LINK_CONTROL_TYPE_RECONF 2 @@ -2712,26 +2854,28 @@ struct ieee80211_eht_capabilities { #define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS 4 /* - * IEEE P802.11be/D2.2, Table 9-401c: Optional subelement IDs for Link Info - * field of the Multi-Link element + * Table 9-417g: Optional subelement IDs for Link Info field of the Multi-Link + * element */ #define MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE 0 #define MULTI_LINK_SUB_ELEM_ID_VENDOR 221 #define MULTI_LINK_SUB_ELEM_ID_FRAGMENT 254 -/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */ +/* IEEE Std 802.11be-2024, 9.4.2.322.2 - Basic Multi-Link element */ -/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */ +/* Figure 9-1074o: Presence Bitmap subfield of the Basic Multi-Link element */ #define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID 0x0010 #define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT 0x0020 #define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO 0x0040 #define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0080 #define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0100 #define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID 0x0200 +#define BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP 0x0400 /* * STA Control field definitions of Per-STA Profile subelement in Basic - * Multi-Link element as described in Figure 9-1002n: STA Control field format. + * Multi-Link element as described in Figure 9-1074v: STA Control field format + * of the Basic Multi-Link element. */ #define BASIC_MLE_STA_CTRL_LEN 2 #define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F @@ -2746,7 +2890,7 @@ struct ieee80211_eht_capabilities { #define BASIC_MLE_STA_PROF_STA_MAC_IDX 3 -/* IEEE P802.11be/D2.2, 9.4.2.312.2.3 - Common Info field of the Basic +/* IEEE Std 802.11be-2024, 9.4.2.322.2.3 - Common Info field of the Basic * Multi-Link element */ struct eht_ml_basic_common_info { u8 len; @@ -2762,10 +2906,17 @@ struct eht_ml_basic_common_info { * EML Capabilities: 2 octets * MLD Capabilities and Operations: 2 octets * AP MLD ID: 1 octet + * Extended MLD Capabilities And Operations: 2 octets */ u8 variable[]; } STRUCT_PACKED; +/* Length of the EML Capabilities field in the Common Info field (in octets) */ +#define EHT_ML_EML_CAPA_LEN 2 + +/* Length of the Link ID Info field in the Common Info field (in octets) */ +#define EHT_ML_LINK_ID_LEN 1 + #define EHT_ML_LINK_ID_MSK 0x0f #define EHT_ML_MEDIUM_SYNC_DELAY_DURATION 0x00ff @@ -2776,39 +2927,29 @@ struct eht_ml_basic_common_info { #define EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK 0x000e #define EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK 0x0070 #define EHT_ML_EML_CAPA_EMLMR_SUPP 0x0080 -#define EHT_ML_EML_CAPA_EMLMR_DELAY_MASK 0x0700 #define EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK 0x7800 +#define EHT_ML_EML_CAPA_RESERVED 0x8700 #define EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK 0x000f #define EHT_ML_MLD_CAPA_SRS_SUPP 0x0010 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL 0x0020 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE 0x0040 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK 0x0060 +#define EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK 0x0080 #define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK 0x0f80 #define EHT_ML_MLD_CAPA_AAR_SUPP 0x1000 +#define EHT_ML_MLD_CAPA_LINK_RECONF_OP_SUPPORT 0x2000 +#define EHT_ML_MLD_CAPA_ALIGNED_TWT_SUPPORT 0x4000 +#define EHT_ML_MLD_CAPA_RESERVED 0x8000 -#define EHT_PER_STA_CTRL_LINK_ID_MSK 0x000f -#define EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK 0x0010 -#define EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK 0x0020 -#define EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK 0x0040 -#define EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK 0x0080 -#define EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK 0x0100 -#define EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK 0x0200 -#define EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK 0x0400 -#define EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK 0x0800 +#define EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE 0x0001 +#define EHT_ML_EXT_MLD_CAPA_OP_RECO_MAX_LINKS_MASK 0x001e +#define EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 +#define EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ONE_LINK 0x0040 +#define EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 -/* IEEE P802.11be/D4.1, Figure 9-1001x - STA Control field format for the - * Reconfiguration Multi-Link element */ -#define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f -#define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010 -#define EHT_PER_STA_RECONF_CTRL_MAC_ADDR 0x0020 -#define EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER 0x0040 -#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780 -#define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800 -#define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000 -#define EHT_PER_STA_RECONF_CTRL_NSTR_INDIC_BITMAP 0x2000 - -/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */ +/* IEEE Std 802.11be-2024, Figure 9-1074u - Per-STA Profile subelement format + * of the Basic Multi-Link element*/ struct ieee80211_eht_per_sta_profile { le16 sta_control; @@ -2816,11 +2957,14 @@ struct ieee80211_eht_per_sta_profile { u8 variable[]; } STRUCT_PACKED; -/* IEEE P802.11be/D4.0, 9.4.2.312.3 - Probe Request Multi-Link element +/* IEEE Std 802.11be-2024, 9.4.2.322.3 - Probe Request Multi-Link element + * Figure 9-1074y - Presence Bitmap field format of the Probe Request + * Multi-Link element * Presence Bitmap field is B4..B15 of the Multi-Link Control field, i.e., * B0 in the presence bitmap is B4 in the control field. */ #define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0010 +#define EHT_ML_PRES_BM_PROBE_REQ_MLD_MAC_ADDR 0x0020 struct eht_ml_probe_req_common_info { u8 len; @@ -2830,13 +2974,17 @@ struct eht_ml_probe_req_common_info { * bitmap * * AP MLD ID: 1 octet + * MLD MAC Address: 6 cotets */ u8 variable[]; } STRUCT_PACKED; -/* IEEE P802.11be/D4.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */ +/* IEEE Std 802.11be-2024, 9.4.2.322.4 - Reconfiguration Multi-Link element */ #define RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR 0x0001 +#define RECONF_MULTI_LINK_CTRL_PRES_EML_CAPA 0x0002 +#define RECONF_MULTI_LINK_CTRL_PRES_MLD_CAPA 0x0004 +#define RECONF_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP 0x0008 #define EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK 0x000f #define EHT_PER_STA_RECONF_CTRL_COMPLETE_PROFILE 0x0010 @@ -2845,8 +2993,54 @@ struct eht_ml_probe_req_common_info { #define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK 0x0780 #define EHT_PER_STA_RECONF_CTRL_OP_PARAMS 0x0800 #define EHT_PER_STA_RECONF_CTRL_NSTR_BITMAP_SIZE 0x1000 +#define EHT_PER_STA_RECONF_CTRL_NSTR_INDICATION 0x2000 -/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */ +#define EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_VAL(x) ((x) >> 7) + +/* IEEE Std 802.11be-2024, Figure 9-1074ad - Common Info field format of the + * Reconfiguration Multi-Link element */ +struct eht_ml_reconf_common_info { + u8 len; + + /* + * Followed by optional fields based on the multi link reconf presence + * bitmap + * + * MLD MAC Address: 6 octets + * EML Capabilities: 2 octets + * MLD Capabilities and Operations: 2 octets + * Extended MLD Capabilities and Operations: 2 octets + */ + u8 variable[]; +} STRUCT_PACKED; + + +/* IEEE Std 802.11be-2024, Table 9-417p - Reconfiguration Operation Type + * subfield encoding */ +enum ieee80211_eht_reconf_mle_op_type { + EHT_RECONF_TYPE_AP_REMOVAL = 0, + EHT_RECONF_TYPE_OP_PARAM_UPDATE = 1, + EHT_RECONF_TYPE_ADD_LINK = 2, + EHT_RECONF_TYPE_DELETE_LINK = 3, + EHT_RECONF_TYPE_NSTR_STATUS_UPDATE = 4, +}; + +/* IEEE Std 802.11be-2024, Figure 9-1074ag — STA Info field format for the + * Reconfiguration Multi-Link element */ +struct eht_ml_reconf_sta_info { + u8 len; + + /* Followed by optional fields based on STA control presence bitmap. + * + * STA MAC Address: 6 octets + * AP Removal Timer: 2 octets + * Operation Parameters: 3 octets + * NSTR Indication Bitmap: 1 or 2 octets + */ + u8 variable[]; +} STRUCT_PACKED; + +/* IEEE Std 802.11be-2024, 9.4.2.322.1 - Multi-Link element / General */ struct ieee80211_eht_ml { le16 ml_control; @@ -2855,13 +3049,32 @@ struct ieee80211_eht_ml { u8 variable[]; } STRUCT_PACKED; -/* Table 9-401c - Optional subelement IDs for Link Info field of the - * Multi-Link element */ -enum ieee80211_eht_ml_sub_elem { - EHT_ML_SUB_ELEM_PER_STA_PROFILE = 0, - EHT_ML_SUB_ELEM_VENDOR = 221, - EHT_ML_SUB_ELEM_FRAGMENT = 254, -}; +/* IEEE Std 802.11be-2024, 9.4.2.329 (Bandwidth Indication element) defines the + * Bandwidth Indication Information field to have the same definition as the + * EHT Operation Information field in the EHT Operation element. + */ +struct ieee80211_bw_ind_info { + u8 control; /* B0..B2: Channel Width */ + u8 ccfs0; + u8 ccfs1; + le16 disabled_chan_bitmap; /* 0 or 2 octets */ +} STRUCT_PACKED; + +/* Control subfield: Channel Width subfield; see Table 9-417e (Channel width, + * CCFS0, and CCFS1 subfields) in IEEE Std 802.11be-2024. */ +#define BW_IND_CHANNEL_WIDTH_20MHZ EHT_OPER_CHANNEL_WIDTH_20MHZ +#define BW_IND_CHANNEL_WIDTH_40MHZ EHT_OPER_CHANNEL_WIDTH_40MHZ +#define BW_IND_CHANNEL_WIDTH_80MHZ EHT_OPER_CHANNEL_WIDTH_80MHZ +#define BW_IND_CHANNEL_WIDTH_160MHZ EHT_OPER_CHANNEL_WIDTH_160MHZ +#define BW_IND_CHANNEL_WIDTH_320MHZ EHT_OPER_CHANNEL_WIDTH_320MHZ + +/* IEEE Std 802.11be-2024, 9.4.2.329 (Bandwidth Indication element) */ +struct ieee80211_bw_ind_element { + u8 bw_ind_params; /* Bandwidth Indication Parameters */ + struct ieee80211_bw_ind_info bw_ind_info; /* 3 or 5 octets */ +} STRUCT_PACKED; + +#define BW_IND_PARAMETER_DISABLED_SUBCHAN_BITMAP_PRESENT BIT(1) /* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */ #define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6 @@ -2915,9 +3128,20 @@ enum scs_request_type { SCS_REQ_CHANGE = 2, }; +/* IEEE Std 802.11be-2024, 9.4.2.324 (TID-To-Link Mapping element) + * Figure 9-1074ax (TID-TO-Link Mapping Control field format) */ +#define EHT_TID_TO_LINK_MAP_DIRECTION_DOWN 0 +#define EHT_TID_TO_LINK_MAP_DIRECTION_UP 1 +#define EHT_TID_TO_LINK_MAP_DIRECTION_BOTH 2 +#define EHT_TID_TO_LINK_MAP_DIRECTION_MSK 0x03 +#define EHT_TID_TO_LINK_MAP_DEFAULT 0x04 +#define EHT_TID_TO_LINK_MAP_SWITCH_TIME_PRESENT 0x08 +#define EHT_TID_TO_LINK_MAP_EXPECT_DUR_PRESENT 0x10 +#define EHT_TID_TO_LINK_MAP_LINK_MAPPING_SIZE 0x20 + /* - * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, - * Table 9-404s (Direction subfield encoding) + * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element, + * Table 9-417w (Direction subfield encoding) */ enum scs_direction { SCS_DIRECTION_UP = 0, @@ -2926,8 +3150,8 @@ enum scs_direction { }; /* - * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, - * Figure 9-1001av (Control Info field format) + * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element, + * Figure 9-1074bd (Control Info field format) */ #define EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET 0 #define EHT_QOS_CONTROL_INFO_TID_OFFSET 2 @@ -2936,7 +3160,7 @@ enum scs_direction { #define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25 /* - * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, + * IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics element, * Presence Bitmap Of Additional Parameters */ #define SCS_QOS_BIT_MAX_MSDU_SIZE ((u16) BIT(0)) @@ -3040,6 +3264,15 @@ enum dscp_policy_request_type { #define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1) #define WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC BIT(2) +/* Wi-Fi Alliance Capabilities attributes */ +enum wfa_capa_attr_id { + WFA_CAPA_ATTR_GENERATIONAL_CAPAB = 1, + WFA_CAPA_ATTR_VENDOR_SPECIFIC = 221, +}; + +/* Wi-Fi Alliance Capabilities frame */ +#define WFA_CAPAB_VENDOR_TYPE 0x506f9a1b + struct ieee80211_neighbor_ap_info { u8 tbtt_info_hdr; u8 tbtt_info_len; diff --git a/contrib/wpa/src/common/nan.h b/contrib/wpa/src/common/nan.h deleted file mode 100644 index 19ab7468711e..000000000000 --- a/contrib/wpa/src/common/nan.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * NAN (Wi-Fi Aware) definitions - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef NAN_H -#define NAN_H - -enum nan_attr_id { - NAN_ATTR_MASTER_INDICATION = 0x00, - NAN_ATTR_CLUSTER = 0x01, - NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST = 0x02, - NAN_ATTR_SDA = 0x03, /* Service Descriptor attribute */ - NAN_ATTR_CONN_CAPA = 0x04, - NAN_ATTR_WLAN_INFRA = 0x05, - NAN_ATTR_P2P_OPER = 0x06, - NAN_ATTR_IBSS = 0x07, - NAN_ATTR_MESH = 0x08, - NAN_ATTR_FURTHER_NAN_SD = 0x09, - NAN_ATTR_FURTHER_AVAIL_MAP = 0x0A, - NAN_ATTR_COUNTRY_CODE = 0x0B, - NAN_ATTR_RANGIN = 0x0C, - NAN_ATTR_CLUSTER_DISCOVERY = 0x0D, - NAN_ATTR_SDEA = 0x0E, /* Service Descriptor Extension attribute */ - NAN_ATTR_DEVICE_CAPABILITY = 0x0F, - NAN_ATTR_NDP = 0x10, - NAN_ATTR_NAN_AVAILABILITY = 0x12, - NAN_ATTR_NDC = 0x13, - NAN_ATTR_NDL = 0x14, - NAN_ATTR_NDL_QOS = 0x15, - NAN_ATTR_UNALIGNED_SCHEDULE = 0x17, - NAN_ATTR_RANGING_INFO = 0x1A, - NAN_ATTR_RANGING_SETUP = 0x1B, - NAN_ATTR_FTM_RANGING_REPORT = 0x1C, - NAN_ATTR_ELEM_CONTAINER = 0x1D, - NAN_ATTR_EXT_WLAN_INFRA = 0x1E, - NAN_ATTR_EXT_P2P_OPER = 0x1FE, - NAN_ATTR_EXT_IBSS = 0x20, - NAN_ATTR_EXT_MESH = 0x21, - NAN_ATTR_CSIA = 0x22, /* Cipher Suite Info attribute */ - NAN_ATTR_SCIA = 0x23, /* Security Context Info attribute */ - NAN_ATTR_SHARED_KEY_DESCR = 0x24, - NAN_ATTR_PUBLIC_AVAILABILITY = 0x27, - NAN_ATTR_SUBSC_SERVICE_ID_LIST = 0x28, - NAN_ATTR_NDP_EXT = 0x29, - NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */ - NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */ - NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */ - NAN_ATTR_S3 = 0x2D, - NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */ - NAN_ATTR_VENDOR_SPECIFIC = 0xDD, -}; - -/* Service Descriptor attribute (SDA) */ - -/* Service Control field */ -#define NAN_SRV_CTRL_TYPE_MASK (BIT(0) | BIT(1)) -#define NAN_SRV_CTRL_MATCHING_FILTER BIT(2) -#define NAN_SRV_CTRL_RESP_FILTER BIT(3) -#define NAN_SRV_CTRL_SRV_INFO BIT(4) -#define NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED BIT(5) -#define NAN_SRV_CTRL_BINDING_BITMAP BIT(6) - -enum nan_service_control_type { - NAN_SRV_CTRL_PUBLISH = 0, - NAN_SRV_CTRL_SUBSCRIBE = 1, - NAN_SRV_CTRL_FOLLOW_UP = 2, -}; - -/* Service Descriptor Extension attribute (SDEA) */ - -/* SDEA Control field */ -#define NAN_SDEA_CTRL_FSD_REQ BIT(0) -#define NAN_SDEA_CTRL_FSD_GAS BIT(1) -#define NAN_SDEA_CTRL_DATA_PATH_REQ BIT(2) -#define NAN_SDEA_CTRL_DATA_PATH_TYPE BIT(3) -#define NAN_SDEA_CTRL_QOS_REQ BIT(5) -#define NAN_SDEA_CTRL_SECURITY_REQ BIT(6) -#define NAN_SDEA_CTRL_RANGING_REQ BIT(7) -#define NAN_SDEA_CTRL_RANGE_LIMIT BIT(8) -#define NAN_SDEA_CTRL_SRV_UPD_INDIC BIT(9) -#define NAN_SDEA_CTRL_GTK_REQ BIT(10) - -enum nan_service_protocol_type { - NAN_SRV_PROTO_BONJOUR = 1, - NAN_SRV_PROTO_GENERIC = 2, - NAN_SRV_PROTO_CSA_MATTER = 3, -}; - -#define NAN_ATTR_HDR_LEN 3 -#define NAN_SERVICE_ID_LEN 6 - -#define NAN_USD_DEFAULT_FREQ 2437 - -#endif /* NAN_H */ diff --git a/contrib/wpa/src/common/nan_de.c b/contrib/wpa/src/common/nan_de.c index 12fad3112bdc..42a61c49bc76 100644 --- a/contrib/wpa/src/common/nan_de.c +++ b/contrib/wpa/src/common/nan_de.c @@ -1,6 +1,6 @@ /* * NAN Discovery Engine - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,22 +10,29 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "utils/crc32.h" +#include "utils/list.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "ieee802_11_defs.h" -#include "nan.h" +#include "nan/nan.h" +#include "nan_defs.h" #include "nan_de.h" -static const u8 nan_network_id[ETH_ALEN] = +const u8 nan_network_id[ETH_ALEN] = { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 }; -static const u8 wildcard_bssid[ETH_ALEN] = -{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; enum nan_de_service_type { NAN_DE_PUBLISH, NAN_DE_SUBSCRIBE, }; +const u8 p2p_network_id[ETH_ALEN] = +{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 }; + +static const u8 wildcard_bssid[ETH_ALEN] = +{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct nan_de_service { int id; enum nan_de_service_type type; @@ -40,11 +47,16 @@ struct nan_de_service { struct os_reltime end_time; struct os_reltime last_multicast; struct os_reltime first_discovered; - struct os_reltime last_followup; bool needs_fsd; unsigned int freq; unsigned int default_freq; int *freq_list; + u8 a3[ETH_ALEN]; + bool a3_set; + + /* Source MAC address for this service (optional) */ + u8 forced_addr[ETH_ALEN]; + bool forced_addr_set; /* pauseState information for Publish function */ struct os_reltime pause_state_end; @@ -58,11 +70,62 @@ struct nan_de_service { struct os_reltime next_publish_state; struct os_reltime next_publish_chan; unsigned int next_publish_duration; + bool is_p2p; + bool is_pr; + bool listen_stopped; + bool sync; + + /* Filters */ + struct wpabuf *matching_filter_tx; + struct wpabuf *matching_filter_rx; + + bool srf_include; + bool srf_type_bloom_filter; + u8 srf_bf_idx; + struct wpabuf *srf; + bool close_proximity; + bool gtk_required; + bool data_path; + bool security_required; + + /* Bootstrapping methods */ + u16 pbm; + + /* For Publish - int_array of supported cipher suites */ + int *cipher_suites_list; + + /* Bitmap of NAN_CS_INFO_CAPA_* */ + u8 security_capab; + + /* PMKID list for this service */ + struct dl_list pmkid_list; +}; + +#define NAN_DE_N_MIN 5 +#define NAN_DE_N_MAX 10 + +#define NAN_DE_RSSI_CLOSE_PROXIMITY (-70) /* dBm */ + +struct nan_de_tracked_tx { + struct dl_list list; + u8 dst[ETH_ALEN]; + u32 cookie; + u8 digest[SHA256_MAC_LEN]; + bool with_wait; +}; + +enum nan_de_flush_tracked_tx_reason { + NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL, + NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED, }; struct nan_de { u8 nmi[ETH_ALEN]; + u8 cluster_id[ETH_ALEN]; + bool cluster_id_set; + bool offload; bool ap; + unsigned int max_listen; struct nan_callbacks cb; struct nan_de_service *service[NAN_DE_MAX_SERVICE]; @@ -74,10 +137,47 @@ struct nan_de { unsigned int listen_freq; unsigned int tx_wait_status_freq; unsigned int tx_wait_end_freq; + + struct nan_de_cfg cfg; + struct os_reltime suspend_cycle_start; + + int dw_freq; + + /* RSSI threshold for close proximity, or zero if not limited */ + int rssi_threshold; + + /* + * List of transmit requests (struct nan_de_tracked_tx::list) for which + * the caller requested status indicating whether the frame was + * acknowledged + */ + struct dl_list tracked_tx; + +#ifdef CONFIG_TESTING_OPTIONS + /* + * When set, multicast follow-up SDFs will be sent as Protected Dual of + * Public Action frames. This can be used to test protection of NAN + * multicast Management frames. + */ + bool tx_mcast_follow_up_prot; +#endif /* CONFIG_TESTING_OPTIONS */ }; -struct nan_de * nan_de_init(const u8 *nmi, bool ap, +bool nan_de_is_nan_network_id(const u8 *addr) +{ + return ether_addr_equal(addr, nan_network_id); +} + + +bool nan_de_is_p2p_network_id(const u8 *addr) +{ + return ether_addr_equal(addr, p2p_network_id); +} + + +struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap, + unsigned int max_listen, const struct nan_callbacks *cb) { struct nan_de *de; @@ -87,9 +187,17 @@ struct nan_de * nan_de_init(const u8 *nmi, bool ap, return NULL; os_memcpy(de->nmi, nmi, ETH_ALEN); + de->offload = offload; de->ap = ap; + de->max_listen = max_listen ? max_listen : 1000; os_memcpy(&de->cb, cb, sizeof(*cb)); + de->cfg.n_min = NAN_DE_N_MIN; + de->cfg.n_max = NAN_DE_N_MAX; + + de->rssi_threshold = NAN_DE_RSSI_CLOSE_PROXIMITY; + dl_list_init(&de->tracked_tx); + return de; } @@ -99,7 +207,14 @@ static void nan_de_service_free(struct nan_de_service *srv) os_free(srv->service_name); wpabuf_free(srv->ssi); wpabuf_free(srv->elems); + wpabuf_free(srv->matching_filter_tx); + wpabuf_free(srv->matching_filter_rx); + wpabuf_free(srv->srf); os_free(srv->freq_list); + os_free(srv->cipher_suites_list); +#ifdef CONFIG_NAN + nan_crypto_clear_pmkid_list(&srv->pmkid_list); +#endif /* CONFIG_NAN */ os_free(srv); } @@ -117,14 +232,136 @@ static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv, } +static void nan_de_flush_tracked_tx(struct nan_de *de, + enum nan_de_flush_tracked_tx_reason reason) +{ + struct nan_de_tracked_tx *tx, *tmp; + + dl_list_for_each_safe(tx, tmp, &de->tracked_tx, + struct nan_de_tracked_tx, list) { + if (reason == NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED && + !tx->with_wait) + continue; + + de->cb.transmit_req_status(de->cb.ctx, tx->cookie, false); + dl_list_del(&tx->list); + os_free(tx); + } +} + + static void nan_de_clear_pending(struct nan_de *de) { + nan_de_flush_tracked_tx(de, NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL); + de->listen_freq = 0; de->tx_wait_status_freq = 0; de->tx_wait_end_freq = 0; } +static int nan_de_track_tx_digest(const u8 *data, size_t len, u8 *digest) +{ + return sha256_vector(1, &data, &len, digest); +} + + +static struct nan_de_tracked_tx * +nan_de_add_tracked_tx(struct nan_de *de, const u8 *dst, bool with_wait, + u32 cookie, const struct wpabuf *buf) +{ + struct nan_de_tracked_tx *tx; + u8 digest[SHA256_MAC_LEN]; + + if (!de->cb.transmit_req_status) { + wpa_printf(MSG_DEBUG, + "NAN: No tx_status callback, cannot track Tx"); + return NULL; + } + + if (!cookie) { + wpa_printf(MSG_DEBUG, "NAN: Invalid cookie for Tx tracking"); + return NULL; + } + + if (nan_de_track_tx_digest(wpabuf_head(buf), wpabuf_len(buf), digest)) { + wpa_printf(MSG_INFO, "NAN: Failed to compute Tx digest"); + return NULL; + } + + dl_list_for_each(tx, &de->tracked_tx, struct nan_de_tracked_tx, list) { + if (!ether_addr_equal(tx->dst, dst)) + continue; + + if (tx->cookie == cookie) { + wpa_printf(MSG_DEBUG, + "NAN: Already tracking Tx cookie %u to " + MACSTR, cookie, MAC2STR(dst)); + return NULL; + } + + if (os_memcmp(tx->digest, digest, SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "NAN: Already tracking identical payload to " + MACSTR " (cookie %u)", + MAC2STR(dst), tx->cookie); + return NULL; + } + } + + tx = os_zalloc(sizeof(*tx)); + if (!tx) + return NULL; + + os_memcpy(tx->dst, dst, ETH_ALEN); + tx->cookie = cookie; + tx->with_wait = with_wait; + os_memcpy(tx->digest, digest, SHA256_MAC_LEN); + + dl_list_add(&de->tracked_tx, &tx->list); + + wpa_printf(MSG_DEBUG, "NAN: Track Tx cookie %u", tx->cookie); + wpa_hexdump(MSG_DEBUG, "NAN: Track Tx digest", + tx->digest, SHA256_MAC_LEN); + + return tx; +} + + +static void nan_de_tx_status_match(struct nan_de *de, const u8 *data, + size_t len, u8 acked) +{ + struct nan_de_tracked_tx *tx; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + const u8 *pos = (const u8 *) &mgmt->u.action; + u8 digest[SHA256_MAC_LEN]; + + if (len <= offsetof(struct ieee80211_mgmt, u.action)) + return; + + len = data + len - pos; + + if (nan_de_track_tx_digest(pos, len, digest)) + return; + + dl_list_for_each(tx, &de->tracked_tx, + struct nan_de_tracked_tx, list) { + if (!ether_addr_equal(tx->dst, mgmt->da) || + os_memcmp(tx->digest, digest, SHA256_MAC_LEN) != 0) + continue; + + wpa_printf(MSG_DEBUG, "NAN: Tx status for cookie=%u ack=%u", + tx->cookie, acked); + + de->cb.transmit_req_status(de->cb.ctx, tx->cookie, acked); + dl_list_del(&tx->list); + os_free(tx); + return; + } +} + + void nan_de_flush(struct nan_de *de) { unsigned int i; @@ -149,6 +386,9 @@ static void nan_de_pause_state(struct nan_de_service *srv, const u8 *peer_addr, wpa_printf(MSG_DEBUG, "NAN: Start pauseState"); os_get_reltime(&srv->pause_state_end); srv->pause_state_end.sec += 60; + if (os_reltime_initialized(&srv->end_time) && + os_reltime_before(&srv->end_time, &srv->pause_state_end)) + srv->pause_state_end = srv->end_time; os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN); srv->sel_peer_id = peer_id; } @@ -164,13 +404,29 @@ static void nan_de_unpause_state(struct nan_de_service *srv) } -static struct wpabuf * nan_de_alloc_sdf(size_t len) +static struct wpabuf * nan_de_alloc_sdf(struct nan_de *de, const u8 *dst, + size_t len, + enum nan_service_control_type type) { struct wpabuf *buf; + u8 category = WLAN_ACTION_PUBLIC; + + if (de->cb.is_peer_paired && de->cb.is_peer_paired(de->cb.ctx, dst)) + category = WLAN_ACTION_PROTECTED_DUAL; + +#ifdef CONFIG_TESTING_OPTIONS + if (de->tx_mcast_follow_up_prot && + is_multicast_ether_addr(dst) && + type == NAN_SRV_CTRL_FOLLOW_UP) { + wpa_printf(MSG_DEBUG, + "NAN: Send multicast follow-up as protected"); + category = WLAN_ACTION_PROTECTED_DUAL; + } +#endif /* CONFIG_TESTING_OPTIONS */ buf = wpabuf_alloc(2 + 4 + len); if (buf) { - wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, category); wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); wpabuf_put_be32(buf, NAN_SDF_VENDOR_TYPE); } @@ -182,16 +438,29 @@ static struct wpabuf * nan_de_alloc_sdf(size_t len) static int nan_de_tx(struct nan_de *de, unsigned int freq, unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, - const struct wpabuf *buf) + const struct wpabuf *buf, u32 *cookie) { + struct nan_de_tracked_tx *tracked_tx = NULL; int res; if (!de->cb.tx) return -1; + if (cookie) { + tracked_tx = nan_de_add_tracked_tx(de, dst, !!wait_time, + *cookie, buf); + if (!tracked_tx) + return -1; + } + res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf); - if (res < 0) + if (res < 0) { + if (tracked_tx) { + dl_list_del(&tracked_tx->list); + os_free(tracked_tx); + } return res; + } de->tx_wait_status_freq = freq; de->tx_wait_end_freq = wait_time ? freq : 0; @@ -200,19 +469,57 @@ static int nan_de_tx(struct nan_de *de, unsigned int freq, } +static void nan_buf_add_npba(const struct nan_de *de, + const struct nan_de_service *srv, + struct wpabuf *buf) +{ + u8 type_and_status = NAN_PBA_TYPE_ADVERTISE | + (NAN_PBA_STATUS_ACCEPTED << NAN_PBA_STATUS_POS); + + wpa_printf(MSG_DEBUG, "NAN: Add NPBA"); + + wpabuf_put_u8(buf, NAN_ATTR_NPBA); + wpabuf_put_le16(buf, 5); + + /* Dialog token is reserved (0) for advertise */ + wpabuf_put_u8(buf, 0); + wpabuf_put_u8(buf, type_and_status); + wpabuf_put_u8(buf, NAN_REASON_RESERVED); + wpabuf_put_le16(buf, srv->pbm); +} + + static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, unsigned int wait_time, enum nan_service_control_type type, - const u8 *dst, u8 req_instance_id, - const struct wpabuf *ssi) + const u8 *dst, const u8 *a3, u8 req_instance_id, + const struct wpabuf *ssi, + const struct wpabuf *attrs, u32 *cookie) { struct wpabuf *buf; size_t len = 0, sda_len, sdea_len; u8 ctrl = type; u16 sdea_ctrl = 0; + const u8 *forced_addr; + size_t cs_num = int_array_len(srv->cipher_suites_list); /* Service Descriptor attribute */ sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1; + if (srv->matching_filter_tx && wpabuf_len(srv->matching_filter_tx)) { + sda_len += wpabuf_len(srv->matching_filter_tx) + 1; + ctrl |= NAN_SRV_CTRL_MATCHING_FILTER; + } + + if (srv->srf && wpabuf_len(srv->srf)) { + /* SRF length + SRF control */ + sda_len += 1 + 1 + wpabuf_len(srv->srf); + ctrl |= NAN_SRV_CTRL_RESP_FILTER; + } + + if ((srv->type == NAN_DE_SUBSCRIBE || srv->type == NAN_DE_PUBLISH) && + srv->close_proximity) + ctrl |= NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED; + len += NAN_ATTR_HDR_LEN + sda_len; /* Service Descriptor Extension attribute */ @@ -225,7 +532,32 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, if (srv->elems) len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems); - buf = nan_de_alloc_sdf(len); + /* NPBA (dialog token, type and status, reason, pbm) */ + if (srv->pbm && type != NAN_SRV_CTRL_FOLLOW_UP) + len += NAN_ATTR_HDR_LEN + 1 + 1 + 1 + 2; + + /* Reserve some additional space for extra attributes */ + if (de->cb.add_extra_attrs) + len += 256; + + len += attrs ? wpabuf_len(attrs) : 0; + + /* Cipher Suite Information Attribute */ + if (srv->type == NAN_DE_PUBLISH && srv->cipher_suites_list) { + len += NAN_ATTR_HDR_LEN + sizeof(struct nan_cipher_suite_info) + + cs_num * sizeof(struct nan_cipher_suite); + } + + /* Security Context Information Attribute */ + if (srv->type == NAN_DE_PUBLISH && !dl_list_empty(&srv->pmkid_list)) { + unsigned int list_len = dl_list_len(&srv->pmkid_list); + + /* Each entry: sizeof(nan_sec_ctxt) + PMKID_LEN */ + len += NAN_ATTR_HDR_LEN + + list_len * (sizeof(struct nan_sec_ctxt) + PMKID_LEN); + } + + buf = nan_de_alloc_sdf(de, dst, len, type); if (!buf) return; @@ -237,23 +569,55 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, wpabuf_put_u8(buf, req_instance_id); /* Requestor Instance ID */ wpabuf_put_u8(buf, ctrl); + if (ctrl & NAN_SRV_CTRL_MATCHING_FILTER) { + wpabuf_put_u8(buf, wpabuf_len(srv->matching_filter_tx)); + wpabuf_put_buf(buf, srv->matching_filter_tx); + } + + if (ctrl & NAN_SRV_CTRL_RESP_FILTER) { + u8 srf_ctrl = 0; + + if (srv->srf_type_bloom_filter) + srf_ctrl = NAN_SRF_CTRL_BF; + + if (srv->srf_include) + srf_ctrl |= NAN_SRF_CTRL_INCLUDE; + + srf_ctrl |= (srv->srf_bf_idx & NAN_SRF_CTRL_BF_IDX_MSK) << + NAN_SRF_CTRL_BF_IDX_POS; + wpabuf_put_u8(buf, wpabuf_len(srv->srf) + 1); + wpabuf_put_u8(buf, srf_ctrl); + wpabuf_put_buf(buf, srv->srf); + } + /* Service Descriptor Extension attribute */ if (srv->type == NAN_DE_PUBLISH || ssi) { - wpabuf_put_u8(buf, NAN_ATTR_SDEA); - wpabuf_put_le16(buf, sdea_len); - wpabuf_put_u8(buf, srv->id); /* Instance ID */ if (srv->type == NAN_DE_PUBLISH) { if (srv->publish.fsd) sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ; if (srv->publish.fsd_gas) sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS; + if (srv->gtk_required) + sdea_ctrl |= NAN_SDEA_CTRL_GTK_REQ; + if (srv->data_path) { + sdea_ctrl |= NAN_SDEA_CTRL_DATA_PATH_REQ; + if (srv->cipher_suites_list && + srv->security_required) + sdea_ctrl |= NAN_SDEA_CTRL_SECURITY_REQ; + } } - wpabuf_put_le16(buf, sdea_ctrl); - if (ssi) { - wpabuf_put_le16(buf, 4 + wpabuf_len(ssi)); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, srv->srv_proto_type); - wpabuf_put_buf(buf, ssi); + + if (sdea_ctrl || ssi) { + wpabuf_put_u8(buf, NAN_ATTR_SDEA); + wpabuf_put_le16(buf, sdea_len); + wpabuf_put_u8(buf, srv->id); /* Instance ID */ + wpabuf_put_le16(buf, sdea_ctrl); + if (ssi) { + wpabuf_put_le16(buf, 4 + wpabuf_len(ssi)); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, srv->srv_proto_type); + wpabuf_put_buf(buf, ssi); + } } } @@ -265,10 +629,53 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv, wpabuf_put_buf(buf, srv->elems); } - /* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD, - * but there is no synchronization in USD as as such, no NAN Cluster - * either. Use Wildcard BSSID instead. */ - nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, wildcard_bssid, buf); + /* Use per-service source address if configured, otherwise use NMI */ + forced_addr = srv->forced_addr_set ? srv->forced_addr : de->nmi; + + if (srv->pbm && type != NAN_SRV_CTRL_FOLLOW_UP) + nan_buf_add_npba(de, srv, buf); + + if (de->cb.add_extra_attrs) + de->cb.add_extra_attrs(de->cb.ctx, buf); + + if (attrs) { + wpa_printf(MSG_DEBUG, "NAN: Add extra NAN attributes"); + wpabuf_put_buf(buf, attrs); + } + + if (srv->type == NAN_DE_PUBLISH && srv->cipher_suites_list) { + size_t i; + + wpabuf_put_u8(buf, NAN_ATTR_CSIA); + wpabuf_put_le16(buf, sizeof(struct nan_cipher_suite_info) + + cs_num * sizeof(struct nan_cipher_suite)); + wpabuf_put_u8(buf, srv->security_capab); + for (i = 0; i < cs_num; i++) { + wpabuf_put_u8(buf, (u8) srv->cipher_suites_list[i]); + wpabuf_put_u8(buf, srv->id); + } + } + + if (srv->type == NAN_DE_PUBLISH && !dl_list_empty(&srv->pmkid_list)) { + struct nan_de_pmkid *pmkid; + u8 *len_ptr; + + wpabuf_put_u8(buf, NAN_ATTR_SCIA); + len_ptr = wpabuf_put(buf, 2); /* length filled later */ + + dl_list_for_each(pmkid, &srv->pmkid_list, struct nan_de_pmkid, + list) { + wpabuf_put_le16(buf, PMKID_LEN); + wpabuf_put_u8(buf, NAN_SEC_CTX_TYPE_ND_PMKID); + wpabuf_put_u8(buf, srv->id); + wpabuf_put_data(buf, pmkid->pmkid, PMKID_LEN); + } + + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + } + + nan_de_tx(de, srv->sync ? 0 : srv->freq, srv->sync ? 0 : wait_time, + dst, forced_addr, a3, buf, cookie); wpabuf_free(buf); } @@ -332,25 +739,48 @@ static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv, { enum nan_service_control_type type; unsigned int wait_time = 100; + const u8 *network_id; + const u8 *bssid; if (srv->type == NAN_DE_PUBLISH) { int ms; type = NAN_SRV_CTRL_PUBLISH; - nan_de_check_chan_change(srv); - ms = nan_de_time_to_next_chan_change(srv); - if (ms < 100) - ms = 100; - wait_time = ms; + if (!srv->sync) { + nan_de_check_chan_change(srv); + ms = nan_de_time_to_next_chan_change(srv); + if (ms < 100) + ms = 100; + wait_time = ms; + } } else if (srv->type == NAN_DE_SUBSCRIBE) { type = NAN_SRV_CTRL_SUBSCRIBE; } else { return; } - nan_de_tx_sdf(de, srv, wait_time, type, nan_network_id, - req_instance_id, srv->ssi); + if (srv->is_p2p) { + network_id = p2p_network_id; + bssid = wildcard_bssid; + } else { + network_id = nan_network_id; + bssid = nan_network_id; + } + + if (srv->sync) { + if (!de->cluster_id_set || !de->dw_freq) { + wpa_printf(MSG_DEBUG, + "NAN: Cluster ID or DW frequency are not set - skip sync TX"); + return; + } + + wait_time = 0; + bssid = de->cluster_id; + } + + nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid, + req_instance_id, srv->ssi, NULL, NULL); os_get_reltime(&srv->last_multicast); } @@ -395,11 +825,6 @@ static bool nan_de_srv_expired(struct nan_de_service *srv, return false; if (!srv->publish.fsd) return true; - if (os_reltime_initialized(&srv->last_followup) && - !os_reltime_expired(now, &srv->last_followup, 1)) - return false; - if (os_reltime_expired(now, &srv->last_multicast, 1)) - return true; } if (srv->type == NAN_DE_SUBSCRIBE) { @@ -409,11 +834,6 @@ static bool nan_de_srv_expired(struct nan_de_service *srv, return false; if (!srv->needs_fsd) return true; - if (os_reltime_initialized(&srv->last_followup) && - !os_reltime_expired(now, &srv->last_followup, 1)) - return false; - if (os_reltime_expired(now, &srv->first_discovered, 1)) - return true; } return false; @@ -478,6 +898,16 @@ static int nan_de_srv_time_to_next(struct nan_de *de, next = tmp; } + if (srv->type == NAN_DE_PUBLISH && + srv->publish.fsd && + os_reltime_initialized(&srv->pause_state_end)) { + os_reltime_sub(&srv->pause_state_end, now, &diff); + tmp = os_reltime_in_ms(&diff); + if (next == -1 || tmp < next) + next = tmp; + return next; + } + tmp = nan_de_next_multicast(de, srv, now); if (tmp >= 0 && (next == -1 || tmp < next)) next = tmp; @@ -513,19 +943,22 @@ static int nan_de_srv_time_to_next(struct nan_de *de, } -static void nan_de_start_new_publish_state(struct nan_de_service *srv, +static void nan_de_start_new_publish_state(struct nan_de *de, + struct nan_de_service *srv, bool force_single) { unsigned int n; + if (srv->sync) + return; + if (force_single || !srv->freq_list || srv->freq_list[0] == 0) srv->in_multi_chan = false; else srv->in_multi_chan = !srv->in_multi_chan; - /* Use hardcoded Nmin=5 and Nmax=10 and pick a random N from that range. - * Use same values for M. */ - n = 5 + os_random() % 5; + /* Use same values for N and M. */ + n = de->cfg.n_min + os_random() % (de->cfg.n_max - de->cfg.n_min); srv->next_publish_duration = n * 100; nan_de_set_publish_times(srv); @@ -550,6 +983,26 @@ static void nan_de_start_new_publish_state(struct nan_de_service *srv, } +static u32 nan_de_listen_duration(struct nan_de *de, struct nan_de_service *srv) +{ + u32 duration = 1000; + u32 max_duration = de->max_listen; + + /* Limit the listen duration based on the maximal 'N' value */ + if (de->cfg.n_max && de->cfg.n_max * 100 < max_duration) + max_duration = de->cfg.n_max * 100; + + if (srv->type == NAN_DE_PUBLISH) { + nan_de_check_chan_change(srv); + duration = nan_de_time_to_next_chan_change(srv); + if (duration < 150) + duration = 150; + } + + return MIN(duration, max_duration); +} + + static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) { struct nan_de *de = eloop_ctx; @@ -560,6 +1013,55 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) os_get_reltime(&now); + /* Based on the USD specification, the device should always be either on + * the default channel or one of the configured channels. However, to + * allow operation of other interfaces, suspend the USD functionality + * based on the cycle and suspend parameters. This would lower the + * probability of service discovery, but would allow functionality of + * other interfaces. + */ + if (!de->listen_freq && de->cfg.cycle) { + u32 diff_ms; + + if (os_reltime_initialized(&de->suspend_cycle_start)) { + struct os_reltime diff; + + os_reltime_sub(&now, &de->suspend_cycle_start, &diff); + diff_ms = os_reltime_in_ms(&diff); + } else { + /* We want to start a new cycle */ + diff_ms = de->cfg.cycle; + } + + if (diff_ms < de->cfg.suspend) { + wpa_printf(MSG_DEBUG, + "NAN: USD: Suspend in progress: diff_ms=%u", + diff_ms); + + /* Set the timer to fire at the end of the suspend */ + diff_ms = de->cfg.suspend - diff_ms; + } else if (diff_ms >= de->cfg.cycle) { + wpa_printf(MSG_DEBUG, + "NAN: Suspend USD for %u ms (passed=%u ms)", + de->cfg.suspend, diff_ms); + de->suspend_cycle_start = now; + + /* Set the timer to fire at the end of the suspend */ + diff_ms = de->cfg.suspend; + } else { + diff_ms = 0; + } + + if (diff_ms) { + wpa_printf(MSG_DEBUG, "NAN: diff_ms=%u ms", diff_ms); + + eloop_register_timeout(diff_ms / 1000, + (diff_ms % 1000) * 1000, + nan_de_timer, de, NULL); + return; + } + } + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { struct nan_de_service *srv = de->service[i]; int srv_next; @@ -570,27 +1072,65 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) if (nan_de_srv_expired(srv, &now)) { wpa_printf(MSG_DEBUG, "NAN: Service id %d expired", srv->id); + if (srv->type == NAN_DE_PUBLISH && + de->cb.offload_cancel_publish) + de->cb.offload_cancel_publish(de->cb.ctx, + srv->id); + if (srv->type == NAN_DE_SUBSCRIBE && + de->cb.offload_cancel_subscribe) + de->cb.offload_cancel_subscribe(de->cb.ctx, + srv->id); nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT); continue; } + if (srv->sync) + continue; + if (os_reltime_initialized(&srv->next_publish_state) && os_reltime_before(&srv->next_publish_state, &now)) - nan_de_start_new_publish_state(srv, false); + nan_de_start_new_publish_state(de, srv, false); if (srv->type == NAN_DE_PUBLISH && os_reltime_initialized(&srv->pause_state_end) && - (os_reltime_before(&srv->pause_state_end, &now) || - (srv->publish.fsd && - os_reltime_initialized(&srv->last_followup) && - os_reltime_expired(&now, &srv->last_followup, 1)))) + (os_reltime_before(&srv->pause_state_end, &now))) nan_de_unpause_state(srv); srv_next = nan_de_srv_time_to_next(de, srv, &now); if (srv_next >= 0 && (next == -1 || srv_next < next)) next = srv_next; - if (srv_next == 0 && !started && + if (srv->type == NAN_DE_PUBLISH && + srv->publish.fsd && + os_reltime_initialized(&srv->pause_state_end) && + de->tx_wait_end_freq == 0 && + de->listen_freq == 0 && de->ext_listen_freq == 0) { + struct os_reltime diff; + int duration; + + os_reltime_sub(&srv->pause_state_end, &now, &diff); + duration = os_reltime_in_ms(&diff); + if (duration < 0) + continue; + if (srv->listen_stopped) { + wpa_printf(MSG_DEBUG, + "NAN: Publisher listen stopped temporarily - do not start driver listen operation"); + continue; + } + if ((unsigned int) duration > de->max_listen) + duration = de->max_listen; + if (de->cb.listen(de->cb.ctx, srv->freq, duration, + srv->forced_addr_set ? + srv->forced_addr : NULL) == 0) { + wpa_printf(MSG_DEBUG, + "NAN: Publisher in pauseState - started listen on %u MHz", + srv->freq); + de->listen_freq = srv->freq; + return; + } + } + + if (srv_next == 0 && !started && !de->offload && de->listen_freq == 0 && de->ext_listen_freq == 0 && de->tx_wait_end_freq == 0 && nan_de_next_multicast(de, srv, &now) == 0) { @@ -598,24 +1138,27 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) nan_de_tx_multicast(de, srv, 0); } - if (!started && de->cb.listen && + if (!started && !de->offload && de->cb.listen && de->listen_freq == 0 && de->ext_listen_freq == 0 && de->tx_wait_end_freq == 0 && ((srv->type == NAN_DE_PUBLISH && !srv->publish.unsolicited && srv->publish.solicited) || (srv->type == NAN_DE_SUBSCRIBE && !srv->subscribe.active))) { - int duration = 1000; + u32 duration; - if (srv->type == NAN_DE_PUBLISH) { - nan_de_check_chan_change(srv); - duration = nan_de_time_to_next_chan_change(srv); - if (duration < 150) - duration = 150; + if (srv->listen_stopped) { + wpa_printf(MSG_DEBUG, + "NAN: Listen stopped temporarily - do not start driver listen operation"); + continue; } + duration = nan_de_listen_duration(de, srv); + started = true; - if (de->cb.listen(de->cb.ctx, srv->freq, duration) == 0) + if (de->cb.listen(de->cb.ctx, srv->freq, duration, + srv->forced_addr_set ? + srv->forced_addr : NULL) == 0) de->listen_freq = srv->freq; } @@ -626,7 +1169,7 @@ static void nan_de_timer(void *eloop_ctx, void *timeout_ctx) if (next == 0) next = 1; - wpa_printf(MSG_DEBUG, "NAN: Next timer in %u ms", next); + eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer, de, NULL); } @@ -667,15 +1210,32 @@ void nan_de_listen_ended(struct nan_de *de, unsigned int freq) } -void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst) +void nan_de_update_nmi(struct nan_de *de, const u8 *nmi) +{ + if (de) + os_memcpy(de->nmi, nmi, ETH_ALEN); +} + + +void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst, + const u8 *data, size_t data_len, bool ack) { if (freq == de->tx_wait_status_freq) de->tx_wait_status_freq = 0; + + nan_de_tx_status_match(de, data, data_len, ack); } void nan_de_tx_wait_ended(struct nan_de *de) { + if (de->tx_wait_end_freq) + wpa_printf(MSG_DEBUG, + "NAN: TX wait for response ended (freq=%u)", + de->tx_wait_end_freq); + + nan_de_flush_tracked_tx(de, NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED); + de->tx_wait_end_freq = 0; nan_de_run_timer(de); } @@ -774,40 +1334,209 @@ static void nan_de_get_sdea(const u8 *buf, size_t len, u8 instance_id, } -static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv, - const u8 *peer_addr, u8 instance_id, - u8 req_instance_id, u16 sdea_control, - enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len) +static unsigned int nan_de_parse_csia(const u8 *buf, size_t len, u8 instance_id, + u8 *cipher_suites, + unsigned int max_cipher_suites, + u8 *capabilities) { - /* Subscribe function processing of a receive Publish message */ - if (!os_reltime_initialized(&srv->first_discovered)) { - os_get_reltime(&srv->first_discovered); - srv->needs_fsd = sdea_control & NAN_SDEA_CTRL_FSD_REQ; - nan_de_run_timer(de); + const u8 *csia, *pos, *end; + u16 csia_len; + unsigned int cs_count = 0; + const struct nan_cipher_suite_info *cs_info; + + csia = nan_de_get_attr(buf, len, NAN_ATTR_CSIA, 0); + if (!csia) + return 0; + + csia++; + csia_len = WPA_GET_LE16(csia); + csia += 2; + + if (csia_len < 1) + return 0; + + wpa_printf(MSG_DEBUG, + "NAN: Parsing Cipher Suite Information attribute (len=%u)", + csia_len); + + cs_info = (const struct nan_cipher_suite_info *) csia; + + if (capabilities) + *capabilities = cs_info->capab; + + pos = cs_info->cs; + end = csia + csia_len; + + /* Parse cipher suite list. Each entry is 2 bytes (csid + publish_id) */ + while (end - pos >= 2 && cs_count < max_cipher_suites) { + u8 csid = *pos++; + u8 publish_id = *pos++; + + if (csid == NAN_CS_NONE || csid >= NAN_CS_MAX) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid cipher suite ID %u for publish ID %u", + csid, publish_id); + continue; + } + + wpa_printf(MSG_DEBUG, + "NAN: Cipher suite ID %u for publish ID %u", + csid, publish_id); + + /* Only include cipher suites for the matching publish ID */ + if (publish_id == instance_id) { + cipher_suites[cs_count++] = csid; + wpa_printf(MSG_DEBUG, + "NAN: Added cipher suite %u for matching publish ID %u", + csid, instance_id); + } } - if (srv->subscribe.active && req_instance_id == 0) { - /* Active subscriber replies with a Subscribe message if it - * received a matching unsolicited Publish message. */ - nan_de_tx_multicast(de, srv, instance_id); + wpa_printf(MSG_DEBUG, + "NAN: Parsed %u cipher suites from CSIA for publish ID %u", + cs_count, instance_id); + + return cs_count; +} + + +static unsigned int nan_de_parse_scia(const u8 *buf, size_t len, u8 instance_id, + u8 *pmkid_list, unsigned int max_pmkids) +{ + const u8 *scia, *end; + u16 scia_len; + unsigned int pmkid_count = 0; + + scia = nan_de_get_attr(buf, len, NAN_ATTR_SCIA, 0); + if (!scia) + return 0; + + scia++; + scia_len = WPA_GET_LE16(scia); + scia += 2; + + end = scia + scia_len; + + wpa_printf(MSG_DEBUG, + "NAN: Parsing Security Context Information attribute (len=%u)", + scia_len); + + /* Parse list of Security Context Identifiers */ + while ((size_t) (end - scia) >= sizeof(struct nan_sec_ctxt)) { + const struct nan_sec_ctxt *sec_ctx = + (const struct nan_sec_ctxt *) scia; + u16 scid_len = le_to_host16(sec_ctx->len); + + if (scid_len + sizeof(*sec_ctx) > (size_t) (end - scia)) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid SCID length %u (remaining %zu)", + scid_len, (size_t) (end - scia)); + break; + } + + /* Check if this is for our instance_id and is a PMKID type */ + if (sec_ctx->scid == NAN_SEC_CTX_TYPE_ND_PMKID && + sec_ctx->instance_id == instance_id) { + if (scid_len == PMKID_LEN && pmkid_count < max_pmkids) { + os_memcpy(&pmkid_list[pmkid_count * PMKID_LEN], + sec_ctx->ctxt, PMKID_LEN); + pmkid_count++; + wpa_hexdump(MSG_DEBUG, "NAN: Parsed PMKID", + sec_ctx->ctxt, PMKID_LEN); + } else { + wpa_printf(MSG_DEBUG, + "NAN: Unexpected SCID length %u or max PMKIDs reached", + scid_len); + } + } + + scia += scid_len + sizeof(*sec_ctx); } - if (!srv->subscribe.active && req_instance_id == 0) { - /* Passive subscriber replies with a Follow-up message without - * Service Specific Info field if it received a matching - * unsolicited Publish message. */ - nan_de_transmit(de, srv->id, NULL, NULL, peer_addr, - instance_id); + wpa_printf(MSG_DEBUG, "NAN: Parsed %u PMKIDs from SCIA", pmkid_count); + + return pmkid_count; +} + + +static void nan_de_process_elem_container(struct nan_de *de, const u8 *buf, + size_t len, const u8 *peer_addr, + unsigned int freq, bool p2p, bool pr) +{ + const u8 *elem; + u16 elem_len; + + elem = nan_de_get_attr(buf, len, NAN_ATTR_ELEM_CONTAINER, 0); + if (!elem) + return; + + elem++; + elem_len = WPA_GET_LE16(elem); + elem += 2; + /* Skip the attribute if there is not enough froom for an element. */ + if (elem_len < 1 + 2) + return; + + /* Skip Map ID */ + elem++; + elem_len--; + + if (p2p && de->cb.process_p2p_usd_elems) + de->cb.process_p2p_usd_elems(de->cb.ctx, elem, elem_len, + peer_addr, freq); + if (pr && de->cb.process_pr_usd_elems) + de->cb.process_pr_usd_elems(de->cb.ctx, elem, elem_len, + peer_addr, freq); +} + + +static void nan_de_parse_dcea(const u8 *buf, size_t len, bool *pairing_setup, + bool *npk_nik_caching) +{ + const u8 *dcea; + u16 dcea_len; + + *pairing_setup = false; + *npk_nik_caching = false; + + dcea = nan_de_get_attr(buf, len, NAN_ATTR_DCEA, 0); + if (!dcea) + return; + + dcea_len = WPA_GET_LE16(dcea + 1); + if (dcea_len < 2) { + wpa_printf(MSG_DEBUG, "NAN: DCEA length=%u too short", + dcea_len); + return; } - if (de->cb.discovery_result) - de->cb.discovery_result( - de->cb.ctx, srv->id, srv_proto_type, - ssi, ssi_len, instance_id, - peer_addr, - sdea_control & NAN_SDEA_CTRL_FSD_REQ, - sdea_control & NAN_SDEA_CTRL_FSD_GAS); + *pairing_setup = !!(dcea[4] & NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP); + *npk_nik_caching = !!(dcea[4] & + NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING); +} + + +static u16 nan_de_get_advertise_pbm(const u8 *buf, size_t len) +{ + const u8 *npba; + u16 npba_len; + + npba = nan_de_get_attr(buf, len, NAN_ATTR_NPBA, 0); + if (!npba) + return 0; + + npba_len = WPA_GET_LE16(npba + 1); + if (npba_len < 5) { + wpa_printf(MSG_DEBUG, "NAN: Invalid NPBA length %u", npba_len); + return 0; + } + + /* Skip the attribute ID and length */ + npba += NAN_ATTR_HDR_LEN; + if ((npba[1] & NAN_PBA_TYPE_MASK) != NAN_PBA_TYPE_ADVERTISE) + return 0; + + return WPA_GET_LE16(npba + 3); } @@ -815,139 +1544,281 @@ static bool nan_de_filter_match(struct nan_de_service *srv, const u8 *matching_filter, size_t matching_filter_len) { - const u8 *pos, *end; + const u8 *spos, *spos_end, *ppos, *ppos_end; + const u8 *publish_filter = NULL, *subscribe_filter = NULL; + u8 publish_filter_len = 0, subscribe_filter_len = 0; - /* Since we do not currently support matching_filter_rx values for the - * local Publish function, any matching filter with at least one - * pair with length larger than zero implies a mismatch. - */ + wpa_printf(MSG_DEBUG, + "NAN: Check matching filter for service id %d type %d", + srv->id, srv->type); - if (!matching_filter) + if (srv->type == NAN_DE_PUBLISH) { + if (srv->matching_filter_rx) { + publish_filter = + wpabuf_head_u8(srv->matching_filter_rx); + publish_filter_len = + wpabuf_len(srv->matching_filter_rx); + } + subscribe_filter = matching_filter; + subscribe_filter_len = matching_filter_len; + } else if (srv->type == NAN_DE_SUBSCRIBE) { + if (srv->matching_filter_rx) { + subscribe_filter = + wpabuf_head_u8(srv->matching_filter_rx); + subscribe_filter_len = + wpabuf_len(srv->matching_filter_rx); + } + publish_filter = matching_filter; + publish_filter_len = matching_filter_len; + } else { + wpa_printf(MSG_DEBUG, + "NAN: Unsupported service type %d for matching filter", + srv->type); + return false; + } + + if (!subscribe_filter) return true; - pos = matching_filter; - end = matching_filter + matching_filter_len; + spos = subscribe_filter; + spos_end = subscribe_filter + subscribe_filter_len; - while (pos < end) { - u8 len; + ppos = publish_filter; + ppos_end = publish_filter ? publish_filter + publish_filter_len : NULL; - len = *pos++; - if (len > end - pos) - break; - if (len) { - /* A non-empty Matching Filter entry: no match since - * there is no local matching_filter_rx. */ + wpa_hexdump(MSG_DEBUG, "NAN: subscribe filter", + spos, spos_end - spos); + if (ppos) + wpa_hexdump(MSG_DEBUG, "NAN: publish filter", + ppos, ppos_end - ppos); + + while (spos < spos_end) { + u8 slen, plen = 0; + + slen = *spos++; + + /* Invalid filter length - do not match */ + if (slen > spos_end - spos) return false; + + /* Read publish filter */ + if (ppos && ppos < ppos_end) { + plen = *ppos++; + if (plen > ppos_end - ppos) + return false; } + + if (slen > 0) { + if (!ppos) + return false; + + /* For non zero filters, compare */ + if (plen && + (plen != slen || os_memcmp(spos, ppos, plen) != 0)) + return false; + + /* Filter matches */ + } + + spos += slen; + + /* + * If ppos is NULL we can still have match if the subscribe + * filter is <0><0>... + */ + if (!ppos) + continue; + + ppos += plen; + + /* Publish filter is over */ + if (ppos >= ppos_end && spos < spos_end) + return false; } return true; } -static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv, - const u8 *peer_addr, u8 instance_id, +static bool nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, u8 instance_id, + const u8 *matching_filter, + size_t matching_filter_len, + u8 req_instance_id, u16 sdea_control, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len, + bool range_limit, int rssi, + const u8 *buf, size_t buf_len) +{ + struct nan_discovery_result res; + + /* The SCIA can potentially contain a PMKID for each cipher suite */ + u8 pmkid_list[(NAN_CS_MAX - 1) * PMKID_LEN]; + unsigned int pmkid_count = 0; + /* Cipher suites from CSIA */ + u8 cipher_suites[NAN_CS_MAX - 1]; + unsigned int cipher_suite_count = 0; + + if (!nan_de_filter_match(srv, matching_filter, matching_filter_len)) + return false; + + /* Skip USD logic */ + if (srv->sync) + goto send_event; + + if ((range_limit || srv->close_proximity) && + de->rssi_threshold && rssi) { + if (rssi < de->rssi_threshold) { + wpa_printf(MSG_DEBUG, + "NAN: Discard SDA with RSSI %d below threshold %d", + rssi, de->rssi_threshold); + return false; + } + } + + /* Subscribe function processing of a receive Publish message */ + if (!os_reltime_initialized(&srv->first_discovered)) { + os_get_reltime(&srv->first_discovered); + srv->needs_fsd = sdea_control & NAN_SDEA_CTRL_FSD_REQ; + nan_de_run_timer(de); + } + + if (!de->offload && srv->subscribe.active && req_instance_id == 0) { + /* Active subscriber replies with a Subscribe message if it + * received a matching unsolicited Publish message. */ + nan_de_tx_multicast(de, srv, instance_id); + } + + if (!de->offload && !srv->subscribe.active && req_instance_id == 0) { + /* Passive subscriber replies with a Follow-up message without + * Service Specific Info field if it received a matching + * unsolicited Publish message. */ + nan_de_transmit(de, srv->id, NULL, NULL, peer_addr, + instance_id, NULL, NULL); + } + +send_event: + os_memset(&res, 0, sizeof(res)); + if (buf && buf_len > 0) { + /* Parse Cipher Suite Information Attribute */ + cipher_suite_count = nan_de_parse_csia( + buf, buf_len, instance_id, cipher_suites, + ARRAY_SIZE(cipher_suites), NULL); + + /* Parse Security Context Information attribute */ + pmkid_count = nan_de_parse_scia(buf, buf_len, instance_id, + pmkid_list, + sizeof(pmkid_list) / PMKID_LEN); + + /* + * Parse Device Capability Extension attribute for pairing + * setup and NPK/NIK caching support + */ + nan_de_parse_dcea(buf, buf_len, + &res.pairing_setup_supp, + &res.npk_nik_caching_supp); + + /* Get the bootstrapping methods */ + res.pbm = nan_de_get_advertise_pbm(buf, buf_len); + } + + res.subscribe_id = srv->id; + res.srv_proto_type = srv_proto_type; + res.ssi = ssi; + res.ssi_len = ssi_len; + res.peer_publish_id = instance_id; + res.peer_addr = peer_addr; + res.fsd = !!(sdea_control & NAN_SDEA_CTRL_FSD_REQ); + res.fsd_gas = !!(sdea_control & NAN_SDEA_CTRL_FSD_GAS); + res.data_path = !!(sdea_control & NAN_SDEA_CTRL_DATA_PATH_REQ); + res.security_required = !!(sdea_control & NAN_SDEA_CTRL_SECURITY_REQ); + res.cipher_suites = cipher_suite_count > 0 ? cipher_suites : NULL; + res.n_cipher_suites = cipher_suite_count; + res.pmkid_list = pmkid_count > 0 ? pmkid_list : NULL; + res.pmkid_count = pmkid_count; + + if (de->cb.discovery_result) + de->cb.discovery_result(de->cb.ctx, &res); + + return true; +} + + +static bool nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, + u8 instance_id, const u8 *matching_filter, size_t matching_filter_len, enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len) + const u8 *ssi, size_t ssi_len, + bool range_limit, int rssi) { - struct wpabuf *buf; - size_t len = 0, sda_len, sdea_len; - u8 ctrl = 0; - u16 sdea_ctrl = 0; + const u8 *network_id; /* Publish function processing of a receive Subscribe message */ if (!nan_de_filter_match(srv, matching_filter, matching_filter_len)) - return; + return false; + + if ((range_limit || srv->close_proximity) && + de->rssi_threshold && rssi) { + if (rssi < de->rssi_threshold) { + wpa_printf(MSG_DEBUG, + "NAN: Discard SDA with RSSI %d below threshold %d", + rssi, de->rssi_threshold); + return false; + } + } if (!srv->publish.solicited) - return; + return false; if (os_reltime_initialized(&srv->pause_state_end) && (!ether_addr_equal(peer_addr, srv->sel_peer_addr) || instance_id != srv->sel_peer_id)) { wpa_printf(MSG_DEBUG, "NAN: In pauseState - ignore Subscribe message from another subscriber"); - return; + return false; } + if (de->offload) + goto offload; + /* Reply with a solicited Publish message */ - /* Service Descriptor attribute */ - sda_len = NAN_SERVICE_ID_LEN + 1 + 1 + 1; - len += NAN_ATTR_HDR_LEN + sda_len; - /* Service Descriptor Extension attribute */ - sdea_len = 1 + 2; - if (srv->ssi) - sdea_len += 2 + 4 + wpabuf_len(srv->ssi); - len += NAN_ATTR_HDR_LEN + sdea_len; + if (srv->is_p2p) + network_id = p2p_network_id; + else + network_id = nan_network_id; - /* Element Container attribute */ - if (srv->elems) - len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems); + if (srv->sync && de->cluster_id_set) + a3 = de->cluster_id; + else if (srv->publish.solicited_multicast || !a3) + a3 = network_id; + else if (srv->is_p2p) + a3 = de->nmi; - buf = nan_de_alloc_sdf(len); - if (!buf) - return; + nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_PUBLISH, + srv->publish.solicited_multicast ? + network_id : peer_addr, a3, instance_id, srv->ssi, NULL, + NULL); - /* Service Descriptor attribute */ - wpabuf_put_u8(buf, NAN_ATTR_SDA); - wpabuf_put_le16(buf, sda_len); - wpabuf_put_data(buf, srv->service_id, NAN_SERVICE_ID_LEN); - wpabuf_put_u8(buf, srv->id); /* Instance ID */ - wpabuf_put_u8(buf, instance_id); /* Requestor Instance ID */ - ctrl |= NAN_SRV_CTRL_PUBLISH; - wpabuf_put_u8(buf, ctrl); - - /* Service Descriptor Extension attribute */ - if (srv->type == NAN_DE_PUBLISH || srv->ssi) { - wpabuf_put_u8(buf, NAN_ATTR_SDEA); - wpabuf_put_le16(buf, sdea_len); - wpabuf_put_u8(buf, srv->id); /* Instance ID */ - if (srv->type == NAN_DE_PUBLISH) { - if (srv->publish.fsd) - sdea_ctrl |= NAN_SDEA_CTRL_FSD_REQ; - if (srv->publish.fsd_gas) - sdea_ctrl |= NAN_SDEA_CTRL_FSD_GAS; - } - wpabuf_put_le16(buf, sdea_ctrl); - if (srv->ssi) { - wpabuf_put_le16(buf, 4 + wpabuf_len(srv->ssi)); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, srv->srv_proto_type); - wpabuf_put_buf(buf, srv->ssi); - } - } - - /* Element Container attribute */ - if (srv->elems) { - wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER); - wpabuf_put_le16(buf, 1 + wpabuf_len(srv->elems)); - wpabuf_put_u8(buf, 0); /* Map ID */ - wpabuf_put_buf(buf, srv->elems); - } - - /* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD, - * but there is no synchronization in USD as as such, no NAN Cluster - * either. Use Wildcard BSSID instead. */ - nan_de_tx(de, srv->freq, 100, - srv->publish.solicited_multicast ? nan_network_id : peer_addr, - de->nmi, wildcard_bssid, buf); - wpabuf_free(buf); - - nan_de_pause_state(srv, peer_addr, instance_id); + if (!srv->is_p2p && !srv->sync) + nan_de_pause_state(srv, peer_addr, instance_id); +offload: if (!srv->publish.disable_events && de->cb.replied) de->cb.replied(de->cb.ctx, srv->id, peer_addr, instance_id, srv_proto_type, ssi, ssi_len); + + return true; } -static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv, - const u8 *peer_addr, u8 instance_id, - const u8 *ssi, size_t ssi_len) +static bool nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv, + const u8 *peer_addr, const u8 *a3, + u8 instance_id, const u8 *ssi, size_t ssi_len, + const u8 *buf, size_t len) { /* Follow-up function processing of a receive Follow-up message for a * Subscribe or Publish instance */ @@ -959,23 +1830,84 @@ static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv, !ssi)) { wpa_printf(MSG_DEBUG, "NAN: In pauseState - ignore Follow-up message from another subscriber or without ssi"); - return; + return false; } - os_get_reltime(&srv->last_followup); - - if (srv->type == NAN_DE_PUBLISH && !ssi) + if (srv->type == NAN_DE_PUBLISH && !ssi && !srv->sync) nan_de_pause_state(srv, peer_addr, instance_id); + os_memcpy(srv->a3, a3, ETH_ALEN); + srv->a3_set = true; + if (de->cb.receive) de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len, - peer_addr); + peer_addr, buf, len); + + return true; } -static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, +static bool nan_check_bloom_filter(const u8 *nmi, const u8 *bf, + size_t bf_len, u8 bf_idx) +{ + u8 a_j_x[1 + ETH_ALEN]; + int j; + u32 crc; + + for (j = 4 * bf_idx; j < 4 * (bf_idx + 1); j++) { + a_j_x[0] = j; + os_memcpy(&a_j_x[1], nmi, ETH_ALEN); + crc = (~ieee80211_crc32(a_j_x, 1 + ETH_ALEN)) & 0xFFFF; + crc %= bf_len * 8; + if (!(bf[crc / 8] & BIT(crc % 8))) + return false; + } + + return true; +} + + +static bool nan_srf_match(struct nan_de *de, const u8 *srf, size_t srf_len) +{ + u8 srf_ctrl; + bool srf_type_bf; + bool include; + u8 srf_bf_idx; + + if (srf_len < 1) + return false; + + srf_ctrl = *srf++; + srf_len--; + + srf_type_bf = !!(srf_ctrl & NAN_SRF_CTRL_BF); + include = !!(srf_ctrl & NAN_SRF_CTRL_INCLUDE); + srf_bf_idx = (srf_ctrl >> NAN_SRF_CTRL_BF_IDX_POS) & + NAN_SRF_CTRL_BF_IDX_MSK; + + if (srf_type_bf) { + if (srf_len == 0) + return false; + if (nan_check_bloom_filter(de->nmi, srf, srf_len, srf_bf_idx)) + return include; + } else { + /* MAC Address filter */ + while (srf_len >= ETH_ALEN) { + if (ether_addr_equal(srf, de->nmi)) + return include; + + srf += ETH_ALEN; + srf_len -= ETH_ALEN; + } + } + + return !include; +} + + +static bool nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3, unsigned int freq, const u8 *buf, size_t len, - const u8 *sda, size_t sda_len) + const u8 *sda, size_t sda_len, int rssi) { const u8 *service_id; u8 instance_id, req_instance_id, ctrl; @@ -989,16 +1921,17 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *end; const u8 *matching_filter = NULL; size_t matching_filter_len = 0; + bool ret = false; if (sda_len < NAN_SERVICE_ID_LEN + 1 + 1 + 1) - return; + return false; end = sda + sda_len; service_id = sda; sda += NAN_SERVICE_ID_LEN; instance_id = *sda++; req_instance_id = *sda++; - ctrl = *sda; + ctrl = *sda++; type = ctrl & NAN_SRV_CTRL_TYPE_MASK; wpa_printf(MSG_DEBUG, "NAN: SDA - Service ID %02x%02x%02x%02x%02x%02x Instance ID %u Requestor Instance ID %u Service Control 0x%x (Service Control Type %u)", @@ -1010,12 +1943,12 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, wpa_printf(MSG_DEBUG, "NAN: Discard SDF with unknown Service Control Type %u", type); - return; + return false; } if (ctrl & NAN_SRV_CTRL_BINDING_BITMAP) { if (end - sda < 2) - return; + return false; sda += 2; } @@ -1023,10 +1956,10 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, u8 flen; if (end - sda < 1) - return; + return false; flen = *sda++; if (end - sda < flen) - return; + return false; matching_filter = sda; matching_filter_len = flen; sda += flen; @@ -1036,10 +1969,17 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, u8 flen; if (end - sda < 1) - return; + return false; flen = *sda++; if (end - sda < flen) - return; + return false; + + if (!nan_srf_match(de, sda, flen)) { + wpa_printf(MSG_DEBUG, + "NAN: Discard SDA with non-matching SRF"); + return false; + } + sda += flen; } @@ -1047,16 +1987,19 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, u8 flen; if (end - sda < 1) - return; + return false; flen = *sda++; if (end - sda < flen) - return; - if (flen >= 4 && WPA_GET_BE24(sda) == OUI_WFA) { - srv_proto_type = sda[3]; - ssi = sda + 4; - ssi_len = flen - 4; - wpa_printf(MSG_DEBUG, "NAN: Service Protocol Type %d", - srv_proto_type); + return false; + + if (flen) { + /* This case of SSI in SDA does not have an explicit + * indication of a service protocol type unlike the + * SDEA case. For now, leave srv_proto_type to 0 for + * this SDA case since that is a reserved value for the + * SDEA cases. */ + ssi = sda; + ssi_len = flen; wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len); } sda += flen; @@ -1094,43 +2037,55 @@ static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len); } + nan_de_process_elem_container(de, buf, len, peer_addr, + freq, srv->is_p2p, + srv->is_pr); } switch (type) { case NAN_SRV_CTRL_PUBLISH: - nan_de_rx_publish(de, srv, peer_addr, instance_id, - req_instance_id, - sdea_control, srv_proto_type, - ssi, ssi_len); + ret |= nan_de_rx_publish( + de, srv, peer_addr, a3, instance_id, + matching_filter, matching_filter_len, + req_instance_id, sdea_control, srv_proto_type, + ssi, ssi_len, + ctrl & NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED, + rssi, buf, len); break; case NAN_SRV_CTRL_SUBSCRIBE: - nan_de_rx_subscribe(de, srv, peer_addr, instance_id, - matching_filter, - matching_filter_len, - srv_proto_type, - ssi, ssi_len); + ret |= nan_de_rx_subscribe( + de, srv, peer_addr, a3, instance_id, + matching_filter, matching_filter_len, + srv_proto_type, ssi, ssi_len, + ctrl & NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED, + rssi); break; case NAN_SRV_CTRL_FOLLOW_UP: - nan_de_rx_follow_up(de, srv, peer_addr, instance_id, - ssi, ssi_len); + ret |= nan_de_rx_follow_up(de, srv, peer_addr, a3, + instance_id, ssi, ssi_len, + buf, len); break; } } + + return ret; } -void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq, - const u8 *buf, size_t len) +bool nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3, + unsigned int freq, const u8 *buf, size_t len, int rssi) { const u8 *sda; u16 sda_len; unsigned int skip; + bool ret = false; if (!de->num_service) - return; + return false; - wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR " freq=%u len=%zu", - MAC2STR(peer_addr), freq, len); + wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR + " freq=%u len=%zu rssi=%d", + MAC2STR(peer_addr), freq, len, rssi); wpa_hexdump(MSG_MSGDUMP, "NAN: SDF payload", buf, len); @@ -1142,8 +2097,11 @@ void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq, sda++; sda_len = WPA_GET_LE16(sda); sda += 2; - nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len); + ret |= nan_de_rx_sda(de, peer_addr, a3, freq, buf, len, + sda, sda_len, rssi); } + + return ret; } @@ -1196,15 +2154,29 @@ static int nan_de_derive_service_id(struct nan_de_service *srv) } +const u8 * nan_de_get_service_id(struct nan_de *de, int id) +{ + struct nan_de_service *srv; + + if (id < 1 || id > NAN_DE_MAX_SERVICE) + return NULL; + srv = de->service[id - 1]; + if (!srv) + return NULL; + return srv->service_id; +} + + int nan_de_publish(struct nan_de *de, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, const struct wpabuf *elems, - struct nan_publish_params *params) + struct nan_publish_params *params, bool p2p, + const u8 *addr) { int publish_id; struct nan_de_service *srv; - if (!service_name) { + if (!service_name && !params->proximity_ranging) { wpa_printf(MSG_DEBUG, "NAN: Publish() - no service_name"); return -1; } @@ -1215,6 +2187,24 @@ int nan_de_publish(struct nan_de *de, const char *service_name, return -1; } + if (params->proximity_ranging && params->solicited && !elems) { + wpa_printf(MSG_INFO, + "NAN: Unable to fetch proximity ranging params"); + return -1; + } + + if (params->sync && !de->cluster_id_set) { + wpa_printf(MSG_DEBUG, + "NAN: Publish() - can't publish sync, cluster id is not set"); + return -1; + } + + if (p2p && params->sync) { + wpa_printf(MSG_DEBUG, + "NAN: Publish() - P2P is not supported with sync"); + return -1; + } + publish_id = nan_de_get_handle(de); if (publish_id < 1) return -1; @@ -1224,11 +2214,18 @@ int nan_de_publish(struct nan_de *de, const char *service_name, return -1; srv->type = NAN_DE_PUBLISH; srv->freq = srv->default_freq = params->freq; - srv->service_name = os_strdup(service_name); - if (!srv->service_name) - goto fail; - if (nan_de_derive_service_id(srv) < 0) + + if (service_name) { + srv->service_name = os_strdup(service_name); + if (!srv->service_name) + goto fail; + } + + if (params->proximity_ranging && !service_name) + os_memset(srv->service_id, 0, NAN_SERVICE_ID_LEN); + else if (nan_de_derive_service_id(srv) < 0) goto fail; + os_memcpy(&srv->publish, params, sizeof(*params)); if (params->freq_list) { @@ -1253,14 +2250,82 @@ int nan_de_publish(struct nan_de *de, const char *service_name, goto fail; } + if (params->match_filter_rx) { + srv->matching_filter_rx = + wpabuf_parse_bin(params->match_filter_rx); + if (!srv->matching_filter_rx || + wpabuf_len(srv->matching_filter_rx) > 255) { + wpa_printf(MSG_INFO, + "NAN: Failed to parse RX matching filter"); + goto fail; + } + } + + if (params->match_filter_tx) { + srv->matching_filter_tx = + wpabuf_parse_bin(params->match_filter_tx); + if (!srv->matching_filter_tx || + wpabuf_len(srv->matching_filter_tx) > 255) { + wpa_printf(MSG_INFO, + "NAN: Failed to parse TX matching filter"); + goto fail; + } + } + + srv->sync = params->sync; + + if (addr && params->forced_addr) { + os_memcpy(srv->forced_addr, addr, ETH_ALEN); + srv->forced_addr_set = true; + wpa_printf(MSG_DEBUG, "NAN: Using source address " MACSTR + " for publish service", MAC2STR(srv->forced_addr)); + } + + srv->security_capab = params->security_capab; + + if (params->cipher_suites_list) { + int i = 0; + + while (params->cipher_suites_list[i] && i < NAN_CS_MAX) { + if (params->cipher_suites_list[i] >= NAN_CS_MAX) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid cipher suite %d in publish", + params->cipher_suites_list[i]); + goto fail; + } + + i++; + } + + srv->cipher_suites_list = + int_array_dup(params->cipher_suites_list); + if (!srv->cipher_suites_list) + goto fail; + } + + dl_list_init(&srv->pmkid_list); +#ifdef CONFIG_NAN + if (nan_crypto_pmkid_list(&srv->pmkid_list, de->nmi, srv->service_id, + srv->cipher_suites_list, params->nd_pmk) < 0) + goto fail; +#endif /* CONFIG_NAN */ + /* Prepare for single and multi-channel states; starting with * single channel */ srv->first_multi_chan = true; - nan_de_start_new_publish_state(srv, true); + nan_de_start_new_publish_state(de, srv, true); wpa_printf(MSG_DEBUG, "NAN: Assigned new publish handle %d for %s", - publish_id, service_name); + publish_id, service_name ? service_name : "Ranging"); srv->id = publish_id; + srv->is_p2p = p2p; + srv->is_pr = params->proximity_ranging && params->solicited; + srv->close_proximity = params->close_proximity; + srv->pbm = params->pbm; + srv->gtk_required = params->gtk_required; + srv->data_path = params->data_path; + srv->security_required = params->security_required; + nan_de_add_srv(de, srv); nan_de_run_timer(de); return publish_id; @@ -1309,19 +2374,125 @@ int nan_de_update_publish(struct nan_de *de, int publish_id, } +int nan_de_unpause_publish(struct nan_de *de, int publish_id, + u8 peer_instance_id, const u8 *peer_addr) +{ + struct nan_de_service *srv; + + wpa_printf(MSG_DEBUG, + "NAN: UnpausePublish(publish_id=%d, peer_instance_id=%d peer_addr=" + MACSTR ")", + publish_id, peer_instance_id, MAC2STR(peer_addr)); + + if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE) + return -1; + srv = de->service[publish_id - 1]; + if (!srv || srv->type != NAN_DE_PUBLISH) + return -1; + + if (srv->sel_peer_id != peer_instance_id || + !ether_addr_equal(peer_addr, srv->sel_peer_addr) || + !os_reltime_initialized(&srv->pause_state_end)) + return -1; + + nan_de_unpause_state(srv); + return 0; +} + + +static void bloom_filter_add(u8 *bf, u8 bf_idx, u8 bf_len, const u8 *mac) +{ + u8 a_j_x[1 + ETH_ALEN]; + int j; + u32 crc; + + for (j = 4 * bf_idx; j < 4 * (bf_idx + 1); j++) { + a_j_x[0] = j; + os_memcpy(&a_j_x[1], mac, ETH_ALEN); + crc = (~ieee80211_crc32(a_j_x, 1 + ETH_ALEN)) & 0xFFFF; + crc %= bf_len * 8; + bf[crc / 8] |= 1 << (crc % 8); + } +} + + +static struct wpabuf * nan_build_bloom_filter(const char *srf_mac_list, + u8 srf_bf_len, u8 srf_bf_idx) +{ + struct wpabuf *srf; + int i, n; + u8 mac[ETH_ALEN]; + u8 *bf; + + if (srf_bf_idx > 3) + return NULL; + + if (os_strlen(srf_mac_list) % (ETH_ALEN * 2)) { + wpa_printf(MSG_INFO, + "NAN: Invalid SRF MAC list length %zu", + os_strlen(srf_mac_list)); + return NULL; + } + + n = os_strlen(srf_mac_list) / (ETH_ALEN * 2); + + srf = wpabuf_alloc(srf_bf_len); + if (!srf) + return NULL; + + bf = wpabuf_put(srf, srf_bf_len); + + for (i = 0; i < n; i++) { + if (hexstr2bin(srf_mac_list + i * 2 * ETH_ALEN, mac, ETH_ALEN)) + { + wpa_printf(MSG_INFO, + "NAN: Invalid SRF MAC address %s", + srf_mac_list + i * 2 * ETH_ALEN); + goto out; + } + + bloom_filter_add(bf, srf_bf_idx, srf_bf_len, mac); + } + + return srf; +out: + wpabuf_free(srf); + return NULL; +} + + int nan_de_subscribe(struct nan_de *de, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, const struct wpabuf *elems, - struct nan_subscribe_params *params) + struct nan_subscribe_params *params, bool p2p, + const u8 *addr) { int subscribe_id; struct nan_de_service *srv; - if (!service_name) { + if (!service_name && !params->proximity_ranging) { wpa_printf(MSG_DEBUG, "NAN: Subscribe() - no service_name"); return -1; } + if (params->proximity_ranging && params->active && !elems) { + wpa_printf(MSG_INFO, + "NAN: Unable to fetch proximity ranging params"); + return -1; + } + + if (params->sync && !de->cluster_id_set) { + wpa_printf(MSG_DEBUG, + "NAN: Subscribe() - can't publish sync, cluster id is not set"); + return -1; + } + + if (p2p && params->sync) { + wpa_printf(MSG_DEBUG, + "NAN: Subscribe() - P2P is not supported with sync"); + return -1; + } + subscribe_id = nan_de_get_handle(de); if (subscribe_id < 1) return -1; @@ -1331,12 +2502,30 @@ int nan_de_subscribe(struct nan_de *de, const char *service_name, return -1; srv->type = NAN_DE_SUBSCRIBE; srv->freq = params->freq; - srv->service_name = os_strdup(service_name); - if (!srv->service_name) - goto fail; - if (nan_de_derive_service_id(srv) < 0) + + if (service_name) { + srv->service_name = os_strdup(service_name); + if (!srv->service_name) + goto fail; + } + + if (params->proximity_ranging && !service_name) + os_memset(srv->service_id, 0, NAN_SERVICE_ID_LEN); + else if (nan_de_derive_service_id(srv) < 0) goto fail; + os_memcpy(&srv->subscribe, params, sizeof(*params)); + + if (params->freq_list) { + size_t len; + + len = (int_array_len(params->freq_list) + 1) * sizeof(int); + srv->freq_list = os_memdup(params->freq_list, len); + if (!srv->freq_list) + goto fail; + } + srv->subscribe.freq_list = NULL; + srv->srv_proto_type = srv_proto_type; if (ssi) { srv->ssi = wpabuf_dup(ssi); @@ -1349,9 +2538,72 @@ int nan_de_subscribe(struct nan_de *de, const char *service_name, goto fail; } + if (params->match_filter_rx) { + srv->matching_filter_rx = + wpabuf_parse_bin(params->match_filter_rx); + if (!srv->matching_filter_rx || + wpabuf_len(srv->matching_filter_rx) > 255) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to parse RX matching filter"); + goto fail; + } + } + + if (params->match_filter_tx) { + srv->matching_filter_tx = + wpabuf_parse_bin(params->match_filter_tx); + if (!srv->matching_filter_tx || + wpabuf_len(srv->matching_filter_tx) > 255) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to parse TX matching filter"); + goto fail; + } + } + + if (params->srf_mac_list) { + if (params->srf_bf_len) { + srv->srf = nan_build_bloom_filter(params->srf_mac_list, + params->srf_bf_len, + params->srf_bf_idx); + srv->srf_type_bloom_filter = true; + srv->srf_bf_idx = params->srf_bf_idx; + } else { + srv->srf = wpabuf_parse_bin(params->srf_mac_list); + if (wpabuf_len(srv->srf) % ETH_ALEN) { + wpa_printf(MSG_INFO, + "NAN: Invalid SRF MAC list length"); + goto fail; + } + } + + if (!srv->srf || wpabuf_len(srv->srf) > 254) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to parse SRF MAC list"); + goto fail; + } + + srv->srf_include = params->srf_include; + } + + if (addr && params->forced_addr) { + os_memcpy(srv->forced_addr, addr, ETH_ALEN); + srv->forced_addr_set = true; + wpa_printf(MSG_DEBUG, "NAN: Using source address " MACSTR + " for subscribe service", MAC2STR(srv->forced_addr)); + } + + dl_list_init(&srv->pmkid_list); + wpa_printf(MSG_DEBUG, "NAN: Assigned new subscribe handle %d for %s", - subscribe_id, service_name); + subscribe_id, service_name ? service_name : "Ranging"); srv->id = subscribe_id; + srv->is_p2p = p2p; + srv->is_pr = params->proximity_ranging && params->active; + srv->sync = params->sync; + srv->close_proximity = params->close_proximity; + srv->pbm = params->pbm; + srv->gtk_required = params->gtk_required; + nan_de_add_srv(de, srv); nan_de_run_timer(de); return subscribe_id; @@ -1376,7 +2628,47 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id) int nan_de_transmit(struct nan_de *de, int handle, const struct wpabuf *ssi, const struct wpabuf *elems, - const u8 *peer_addr, u8 req_instance_id) + const u8 *peer_addr, u8 req_instance_id, + const struct wpabuf *nan_attrs, u32 *cookie) +{ + struct nan_de_service *srv; + const u8 *a3; + const u8 *network_id; + + if (handle < 1 || handle > NAN_DE_MAX_SERVICE) + return -1; + + srv = de->service[handle - 1]; + if (!srv) + return -1; + + if (srv->sync && !de->cluster_id_set) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot transmit Follow-up, cluster ID not set"); + return -1; + } + + if (srv->is_p2p) + network_id = p2p_network_id; + else if (srv->sync) + network_id = de->cluster_id; + else + network_id = nan_network_id; + + if (srv->a3_set) + a3 = srv->a3; + else + a3 = network_id; + nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP, + peer_addr, a3, req_instance_id, ssi, nan_attrs, + cookie); + + srv->listen_stopped = false; + return 0; +} + + +int nan_de_stop_listen(struct nan_de *de, int handle) { struct nan_de_service *srv; @@ -1386,10 +2678,218 @@ int nan_de_transmit(struct nan_de *de, int handle, srv = de->service[handle - 1]; if (!srv) return -1; - - nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP, - peer_addr, req_instance_id, ssi); - - os_get_reltime(&srv->last_followup); + srv->listen_stopped = true; return 0; } + + +int nan_de_config(struct nan_de *de, struct nan_de_cfg *cfg) +{ + if (!de || !cfg) + return -1; + + /* No change in configuration */ + if (de->cfg.n_min == cfg->n_min && de->cfg.n_max == cfg->n_max && + de->cfg.cycle == cfg->cycle && de->cfg.suspend == cfg->suspend) + return 0; + + wpa_printf(MSG_DEBUG, + "NAN: Configuring NAN DE: n=(%u, %u), suspend=%u, cycle=%u", + cfg->n_min, cfg->n_max, cfg->suspend, cfg->cycle); + + if (!cfg->n_min && !cfg->n_max) { + cfg->n_min = NAN_DE_N_MIN; + cfg->n_max = NAN_DE_N_MAX; + } else if (cfg->n_min < 1 || cfg->n_max < cfg->n_min) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid configuration parameters: N"); + return -1; + } + + if (((!!cfg->suspend) ^ (!!cfg->cycle)) || + (cfg->cycle && cfg->suspend >= cfg->cycle)) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid configuration parameters: cycle"); + return -1; + } + + de->cfg = *cfg; + + os_memset(&de->suspend_cycle_start, 0, sizeof(de->suspend_cycle_start)); + + if (!de->listen_freq) + nan_de_run_timer(de); + + return 0; +} + + +void nan_de_dw_trigger(struct nan_de *de, int freq) +{ + int i; + struct os_reltime now; + + de->dw_freq = freq; + + if (!de->cluster_id_set) { + wpa_printf(MSG_DEBUG, "NAN: Skip DW, cluster ID not set"); + return; + } + + os_get_reltime(&now); + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { + struct nan_de_service *srv = de->service[i]; + + if (!srv || !srv->sync) + continue; + + if (nan_de_srv_expired(srv, &now)) { + nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT); + continue; + } + + if ((srv->type == NAN_DE_PUBLISH && + srv->publish.unsolicited) || + (srv->type == NAN_DE_SUBSCRIBE && srv->subscribe.active)) { + nan_de_tx_multicast(de, srv, 0); + } + } +} + + +void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id) +{ + if (cluster_id) { + os_memcpy(de->cluster_id, cluster_id, ETH_ALEN); + de->cluster_id_set = true; + } else { + de->cluster_id_set = false; + } +} + + +bool nan_de_is_valid_instance_id(struct nan_de *de, int handle, + bool publish, u8 *service_id) +{ + struct nan_de_service *srv; + + if (handle < 1 || handle > NAN_DE_MAX_SERVICE) + return false; + + srv = de->service[handle - 1]; + if (!srv) + return false; + + if (publish && srv->type != NAN_DE_PUBLISH) + return false; + if (!publish && srv->type != NAN_DE_SUBSCRIBE) + return false; + + os_memcpy(service_id, srv->service_id, NAN_SERVICE_ID_LEN); + return true; +} + + +u16 nan_de_get_service_bootstrap_methods(struct nan_de *de, int handle) +{ + struct nan_de_service *srv; + + if (handle < 1 || handle > NAN_DE_MAX_SERVICE) + return 0; + + srv = de->service[handle - 1]; + if (!srv) + return 0; + + return srv->pbm; +} + + +bool nan_de_service_supports_csid(struct nan_de *de, int handle, int csid) +{ + struct nan_de_service *srv; + + if (handle < 1 || handle > NAN_DE_MAX_SERVICE) + return false; + + srv = de->service[handle - 1]; + if (!srv) + return false; + + /* If cipher_suites_list is not set, all CSIDs are allowed */ + if (!srv->cipher_suites_list) + return true; + + /* Open is allowed only if security is not required */ + if (csid == NAN_CS_NONE) + return !srv->security_required; + + /* Check if the CSID is in the service's cipher suite list */ + return int_array_includes(srv->cipher_suites_list, csid); +} + + +static const char * nan_de_service_type2str(enum nan_de_service_type type) +{ + switch (type) { + case NAN_DE_PUBLISH: + return "publish"; + case NAN_DE_SUBSCRIBE: + return "subscribe"; + } + + return "unknown"; +} + + +int nan_de_get_status(struct nan_de *de, char *buf, size_t buflen) +{ + char *pos, *end; + unsigned int i; + int ret; + + if (!de) + return -1; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "num_services=%u\n", + de->num_service); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (i = 0; i < NAN_DE_MAX_SERVICE; i++) { + struct nan_de_service *srv = de->service[i]; + + if (!srv) + continue; + + ret = os_snprintf(pos, end - pos, + "service=%u type=%s name=%s sync=%d\n", + srv->id, + nan_de_service_type2str(srv->type), + srv->service_name ? srv->service_name : "", + srv->sync); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +#ifdef CONFIG_TESTING_OPTIONS + +void nan_de_set_tx_mcast_follow_up_prot(struct nan_de *de, bool prot) +{ + wpa_printf(MSG_DEBUG, + "NAN: Set tx_mcast_follow_up_dual_prot: %u->%u", + de->tx_mcast_follow_up_prot, prot); + + de->tx_mcast_follow_up_prot = prot; +} + +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/common/nan_de.h b/contrib/wpa/src/common/nan_de.h index 62235064b075..0599a808ef45 100644 --- a/contrib/wpa/src/common/nan_de.h +++ b/contrib/wpa/src/common/nan_de.h @@ -1,6 +1,6 @@ /* * NAN Discovery Engine - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,7 +9,7 @@ #ifndef NAN_DE_H #define NAN_DE_H -#include "nan.h" +#include "nan_defs.h" /* Maximum number of active local publish and subscribe instances */ #ifndef NAN_DE_MAX_SERVICE @@ -24,20 +24,37 @@ enum nan_de_reason { NAN_DE_REASON_FAILURE, }; +struct nan_discovery_result { + int subscribe_id; + enum nan_service_protocol_type srv_proto_type; + const u8 *ssi; + size_t ssi_len; + int peer_publish_id; + const u8 *peer_addr; + bool fsd; + bool fsd_gas; + bool data_path; + bool security_required; + const u8 *pmkid_list; + unsigned int pmkid_count; + const u8 *cipher_suites; + unsigned int n_cipher_suites; + bool pairing_setup_supp; + bool npk_nik_caching_supp; + u16 pbm; +}; + struct nan_callbacks { void *ctx; int (*tx)(void *ctx, unsigned int freq, unsigned int wait_time, const u8 *dst, const u8 *src, const u8 *bssid, const struct wpabuf *buf); - int (*listen)(void *ctx, unsigned int freq, unsigned int duration); + int (*listen)(void *ctx, unsigned int freq, unsigned int duration, + const u8 *forced_addr); /* NAN DE Events */ - void (*discovery_result)(void *ctx, int subscribe_id, - enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len, - int peer_publish_id, - const u8 *peer_addr, bool fsd, bool fsd_gas); + void (*discovery_result)(void *ctx, struct nan_discovery_result *res); void (*replied)(void *ctx, int publish_id, const u8 *peer_addr, int peer_subscribe_id, @@ -50,12 +67,33 @@ struct nan_callbacks { void (*subscribe_terminated)(void *ctx, int subscribe_id, enum nan_de_reason reason); + void (*offload_cancel_publish)(void *ctx, int publish_id); + void (*offload_cancel_subscribe)(void *ctx, int subscribe_id); + void (*receive)(void *ctx, int id, int peer_instance_id, const u8 *ssi, size_t ssi_len, - const u8 *peer_addr); + const u8 *peer_addr, + const u8 *buf, size_t len); + + void (*process_p2p_usd_elems)(void *ctx, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq); + + void (*process_pr_usd_elems)(void *ctx, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq); + void (*add_extra_attrs)(void *ctx, struct wpabuf *buf); + bool (*is_peer_paired)(void *ctx, const u8 *addr); + void (*transmit_req_status)(void *ctx, u32 cookie, bool ack); }; -struct nan_de * nan_de_init(const u8 *nmi, bool ap, +extern const u8 nan_network_id[ETH_ALEN]; +extern const u8 p2p_network_id[ETH_ALEN]; + +bool nan_de_is_nan_network_id(const u8 *addr); +bool nan_de_is_p2p_network_id(const u8 *addr); +struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap, + unsigned int max_listen, const struct nan_callbacks *cb); void nan_de_flush(struct nan_de *de); void nan_de_deinit(struct nan_de *de); @@ -63,11 +101,14 @@ void nan_de_deinit(struct nan_de *de); void nan_de_listen_started(struct nan_de *de, unsigned int freq, unsigned int duration); void nan_de_listen_ended(struct nan_de *de, unsigned int freq); -void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst); +void nan_de_update_nmi(struct nan_de *de, const u8 *nmi); +void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst, + const u8 *data, size_t data_len, bool ack); void nan_de_tx_wait_ended(struct nan_de *de); -void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq, - const u8 *buf, size_t len); +bool nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3, + unsigned int freq, const u8 *buf, size_t len, int rssi); +const u8 * nan_de_get_service_id(struct nan_de *de, int id); struct nan_publish_params { /* configuration_parameters */ @@ -99,19 +140,68 @@ struct nan_publish_params { /* Announcement period in ms; 0 = use default */ unsigned int announcement_period; + + /* Proximity ranging flag */ + bool proximity_ranging; + + /* Synchronized discovery */ + bool sync; + + /* + * Null-terminated string containing the hex-encoded + * representation of the matching filters. + */ + const char *match_filter_tx; + const char *match_filter_rx; + + /* RSSI range limit */ + bool close_proximity; + + /* Source MAC address for this service (optional) */ + const u8 *forced_addr; + + /* + * Pairing Bootstrapping Methods as defined in Wi-Fi Aware spec v4.0, + * Table 128 + */ + u16 pbm; + + /* int_array of cipher suites */ + const int *cipher_suites_list; + + /* Bitmap of NAN_CS_INFO_CAPA_* */ + u8 security_capab; + + /* ND-PMK to use for creating a list of PMKIDs for the service */ + const u8 *nd_pmk; + + /* + * GTK protection required for group-addressed Data frames transmitted + * and received for the service + */ + bool gtk_required; + + /* Request NAN Data Path */ + bool data_path; + + bool security_required; }; /* Returns -1 on failure or >0 publish_id */ int nan_de_publish(struct nan_de *de, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, const struct wpabuf *elems, - struct nan_publish_params *params); + struct nan_publish_params *params, bool p2p, + const u8 *addr); void nan_de_cancel_publish(struct nan_de *de, int publish_id); int nan_de_update_publish(struct nan_de *de, int publish_id, const struct wpabuf *ssi); +int nan_de_unpause_publish(struct nan_de *de, int publish_id, + u8 peer_instance_id, const u8 *peer_addr); + struct nan_subscribe_params { /* configuration_parameters */ @@ -124,15 +214,62 @@ struct nan_subscribe_params { /* Selected frequency */ unsigned int freq; + /* Multi-channel frequencies (publishChannelList) */ + const int *freq_list; + /* Query period in ms; 0 = use default */ unsigned int query_period; + + /* Proximity ranging flag */ + bool proximity_ranging; + + /* Synchronized discovery */ + bool sync; + + /* + * Null-terminated string containing the hex-encoded + * representation of the matching filters. + */ + const char *match_filter_tx; + const char *match_filter_rx; + + /* Service response filter include flag */ + bool srf_include; + + /* Service response filter MAC list */ + const char *srf_mac_list; + + /* Bloom filter length in octets. If 0, MAC list is used instead */ + u8 srf_bf_len; + + /* Bloom filter index (0-3) */ + u8 srf_bf_idx; + + /* RSSI range limit */ + bool close_proximity; + + /* Source MAC address for this service (optional) */ + const u8 *forced_addr; + + /* + * Pairing Bootstrapping Methods as defined in Wi-Fi Aware spec v4.0, + * Table 128 + */ + u16 pbm; + + /* + * GTK protection required for group-addressed Data frames transmitted + * and received for the service + */ + bool gtk_required; }; /* Returns -1 on failure or >0 subscribe_id */ int nan_de_subscribe(struct nan_de *de, const char *service_name, enum nan_service_protocol_type srv_proto_type, const struct wpabuf *ssi, const struct wpabuf *elems, - struct nan_subscribe_params *params); + struct nan_subscribe_params *params, bool p2p, + const u8 *addr); void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id); @@ -140,6 +277,30 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id); * req_instance_id = peer publish_id or subscribe_id */ int nan_de_transmit(struct nan_de *de, int handle, const struct wpabuf *ssi, const struct wpabuf *elems, - const u8 *peer_addr, u8 req_instance_id); + const u8 *peer_addr, u8 req_instance_id, + const struct wpabuf *nan_attrs, u32 *cookie); + +void nan_de_dw_trigger(struct nan_de *de, int freq); +void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id); +bool nan_de_is_valid_instance_id(struct nan_de *de, int handle, + bool publish, u8 *service_id); +u16 nan_de_get_service_bootstrap_methods(struct nan_de *de, int handle); +bool nan_de_service_supports_csid(struct nan_de *de, int handle, int csid); +void nan_de_set_tx_mcast_follow_up_prot(struct nan_de *de, bool prot); +int nan_de_get_status(struct nan_de *de, char *buf, size_t buflen); + +int nan_de_stop_listen(struct nan_de *de, int handle); + +struct nan_de_cfg { + /* N and M minimal and maximal values */ + u32 n_min, n_max; + + /* When not in pause state, stop the DE radio usage for 'suspend' ms + * every 'cycle' ms. + */ + u32 suspend, cycle; +}; + +int nan_de_config(struct nan_de *de, struct nan_de_cfg *cfg); #endif /* NAN_DE_H */ diff --git a/contrib/wpa/src/common/nan_defs.h b/contrib/wpa/src/common/nan_defs.h new file mode 100644 index 000000000000..88b46eaf3ce0 --- /dev/null +++ b/contrib/wpa/src/common/nan_defs.h @@ -0,0 +1,663 @@ +/* + * NAN (Wi-Fi Aware) definitions + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * Copyright (c) 2026, Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_DEFS_H +#define NAN_DEFS_H + +/* Wi-Fi Aware spec v4.0, Table 35 (NAI Action frame subtypes) */ +enum nan_subtype { + NAN_SUBTYPE_INVALID = 0, + NAN_SUBTYPE_RANGING_REQUEST = 1, + NAN_SUBTYPE_RANGING_RESPONSE = 2, + NAN_SUBTYPE_RANGING_TERMINATION = 3, + NAN_SUBTYPE_RANGING_REPORT = 4, + NAN_SUBTYPE_DATA_PATH_REQUEST = 5, + NAN_SUBTYPE_DATA_PATH_RESPONSE = 6, + NAN_SUBTYPE_DATA_PATH_CONFIRM = 7, + NAN_SUBTYPE_DATA_PATH_KEY_INSTALL = 8, + NAN_SUBTYPE_DATA_PATH_TERMINATION = 9, + NAN_SUBTYPE_SCHEDULE_REQUEST = 10, + NAN_SUBTYPE_SCHEDULE_RESPONSE = 11, + NAN_SUBTYPE_SCHEDULE_CONFIRM = 12, + NAN_SUBTYPE_SCHEDULE_UPDATE_NOTIF = 13, +}; + +enum nan_attr_id { + NAN_ATTR_MASTER_INDICATION = 0x00, + NAN_ATTR_CLUSTER = 0x01, + NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST = 0x02, + NAN_ATTR_SDA = 0x03, /* Service Descriptor attribute */ + NAN_ATTR_CONN_CAPA = 0x04, + NAN_ATTR_WLAN_INFRA = 0x05, + NAN_ATTR_P2P_OPER = 0x06, + NAN_ATTR_IBSS = 0x07, + NAN_ATTR_MESH = 0x08, + NAN_ATTR_FURTHER_NAN_SD = 0x09, + NAN_ATTR_FURTHER_AVAIL_MAP = 0x0A, + NAN_ATTR_COUNTRY_CODE = 0x0B, + NAN_ATTR_RANGING = 0x0C, + NAN_ATTR_CLUSTER_DISCOVERY = 0x0D, + NAN_ATTR_SDEA = 0x0E, /* Service Descriptor Extension attribute */ + NAN_ATTR_DEVICE_CAPABILITY = 0x0F, + NAN_ATTR_NDP = 0x10, + NAN_ATTR_NAN_AVAILABILITY = 0x12, + NAN_ATTR_NDC = 0x13, + NAN_ATTR_NDL = 0x14, + NAN_ATTR_NDL_QOS = 0x15, + NAN_ATTR_UNALIGNED_SCHEDULE = 0x17, + NAN_ATTR_RANGING_INFO = 0x1A, + NAN_ATTR_RANGING_SETUP = 0x1B, + NAN_ATTR_FTM_RANGING_REPORT = 0x1C, + NAN_ATTR_ELEM_CONTAINER = 0x1D, + NAN_ATTR_EXT_WLAN_INFRA = 0x1E, + NAN_ATTR_EXT_P2P_OPER = 0x1F, + NAN_ATTR_EXT_IBSS = 0x20, + NAN_ATTR_EXT_MESH = 0x21, + NAN_ATTR_CSIA = 0x22, /* Cipher Suite Info attribute */ + NAN_ATTR_SCIA = 0x23, /* Security Context Info attribute */ + NAN_ATTR_SHARED_KEY_DESCR = 0x24, + NAN_ATTR_PUBLIC_AVAILABILITY = 0x27, + NAN_ATTR_SUBSC_SERVICE_ID_LIST = 0x28, + NAN_ATTR_NDP_EXT = 0x29, + NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */ + NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */ + NAN_ATTR_NPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */ + NAN_ATTR_S3 = 0x2D, + NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */ + NAN_ATTR_VENDOR_SPECIFIC = 0xDD, +}; + +/* Wi-Fi Aware spec v4.0, Table 43 (Reason Code field) */ +enum nan_reason { + NAN_REASON_RESERVED = 0, + NAN_REASON_UNSPECIFIED_REASON = 1, + NAN_REASON_RESOURCE_LIMITATION = 2, + NAN_REASON_INVALID_PARAMETERS = 3, + NAN_REASON_FTM_PARAMETERS_INCAPABLE = 4, + NAN_REASON_NO_MOVEMENT = 5, + NAN_REASON_INVALID_AVAILABILITY = 6, + NAN_REASON_IMMUTABLE_UNACCEPTABLE = 7, + NAN_REASON_SECURITY_POLICY = 8, + NAN_REASON_QOS_UNACCEPTABLE = 9, + NAN_REASON_NDP_REJECTED = 10, + NAN_REASON_NDL_UNACCEPTABLE = 11, + NAN_REASON_RANGING_SCHED_NOT_ACCEPT = 12, + NAN_REASON_PAIR_BOOTSTRAP_REJECTED = 13, +}; + +/* Service Descriptor attribute (SDA) */ + +/* Service Control field */ +#define NAN_SRV_CTRL_TYPE_MASK (BIT(0) | BIT(1)) +#define NAN_SRV_CTRL_MATCHING_FILTER BIT(2) +#define NAN_SRV_CTRL_RESP_FILTER BIT(3) +#define NAN_SRV_CTRL_SRV_INFO BIT(4) +#define NAN_SRV_CTRL_DISCOVERY_RANGE_LIMITED BIT(5) +#define NAN_SRV_CTRL_BINDING_BITMAP BIT(6) + +enum nan_service_control_type { + NAN_SRV_CTRL_PUBLISH = 0, + NAN_SRV_CTRL_SUBSCRIBE = 1, + NAN_SRV_CTRL_FOLLOW_UP = 2, +}; + +/* Service Descriptor Extension attribute (SDEA) */ + +/* SDEA Control field */ +#define NAN_SDEA_CTRL_FSD_REQ BIT(0) +#define NAN_SDEA_CTRL_FSD_GAS BIT(1) +#define NAN_SDEA_CTRL_DATA_PATH_REQ BIT(2) +#define NAN_SDEA_CTRL_DATA_PATH_TYPE BIT(3) +#define NAN_SDEA_CTRL_QOS_REQ BIT(5) +#define NAN_SDEA_CTRL_SECURITY_REQ BIT(6) +#define NAN_SDEA_CTRL_RANGING_REQ BIT(7) +#define NAN_SDEA_CTRL_RANGE_LIMIT BIT(8) +#define NAN_SDEA_CTRL_SRV_UPD_INDIC BIT(9) +#define NAN_SDEA_CTRL_GTK_REQ BIT(10) + +enum nan_service_protocol_type { + NAN_SRV_PROTO_BONJOUR = 1, + NAN_SRV_PROTO_GENERIC = 2, + NAN_SRV_PROTO_CSA_MATTER = 3, +}; + +/* SRF Control field */ +/* bit 0 = 0: Address Set is a sequence of MAC Addresses + * bit 0 = 1: Address Set is a Bloom filter */ +#define NAN_SRF_CTRL_BF BIT(0) +#define NAN_SRF_CTRL_INCLUDE BIT(1) + +/* bit 2-3: Bloom Filter Index */ +#define NAN_SRF_CTRL_BF_IDX_MSK (BIT(0) | BIT(1)) +#define NAN_SRF_CTRL_BF_IDX_POS 2 + +/* Wi-Fi Aware spec v4.0, Table 128 (NPBA format) */ +#define NAN_PBA_TYPE_MASK 0xf +#define NAN_PBA_STATUS_MASK 0xf +#define NAN_PBA_STATUS_POS 4 + +enum nan_pba_type { + NAN_PBA_TYPE_ADVERTISE = 0, + NAN_PBA_TYPE_REQUEST = 1, + NAN_PBA_TYPE_RESPONSE = 2, +}; + +enum nan_pba_status { + NAN_PBA_STATUS_ACCEPTED = 0, + NAN_PBA_STATUS_REJECTED = 1, + NAN_PBA_STATUS_COMEBACK = 2, +}; + +enum nan_pairing_bootstrapping_method { + NAN_PBA_METHOD_OPPORTUNISTIC = BIT(0), + NAN_PBA_METHOD_PIN_DISPLAY = BIT(1), + NAN_PBA_METHOD_PASSPHRASE_DISPLAY = BIT(2), + NAN_PBA_METHOD_QR_DISPLAY = BIT(3), + NAN_PBA_METHOD_NFC_TAG = BIT(4), + NAN_PBA_METHOD_PIN_KEYPAD = BIT(5), + NAN_PBA_METHOD_PASSPHRASE_KEYPAD = BIT(6), + NAN_PBA_METHOD_QR_SCAN = BIT(7), + NAN_PBA_METHOD_NFC_READER = BIT(8), + NAN_PBA_METHOD_SERVICE_MANAGED = BIT(14), + NAN_PBA_METHOD_HANDSHAKE_SKIPPED = BIT(15), +}; + +#define NAN_ATTR_HDR_LEN 3 +#define NAN_SERVICE_ID_LEN 6 + +#define NAN_USD_DEFAULT_FREQ 2437 + +/* Map ID + * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */ +#define NAN_DEV_CAPA_MAP_ID_DONT_APPLY_ALL BIT(0) +#define NAN_DEV_CAPA_MAP_ID_POS 1 +#define NAN_DEV_CAPA_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) \ + BIT(4)) + +/* Supported Bands: + * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */ +#define NAN_DEV_CAPA_SBAND_SUB_1G BIT(1) +#define NAN_DEV_CAPA_SBAND_2G BIT(2) +#define NAN_DEV_CAPA_SBAND_5G BIT(4) +#define NAN_DEV_CAPA_SBAND_6G BIT(7) + +/* Wi-Fi Aware spec v4.0, Table 80 (Committed DW Information field format) */ +#define NAN_CDW_INFO_2G_POS 0 +#define NAN_CDW_INFO_2G_MASK (BIT(0) | BIT(1) | BIT(2)) +#define NAN_CDW_INFO_5G_POS 3 +#define NAN_CDW_INFO_5G_MASK (BIT(3) | BIT(4) | BIT(5)) +#define NAN_CDW_INFO_2G_OVERRIDE_POS 6 +#define NAN_CDW_INFO_2G_OVERRIDE_MASK (BIT(6) | BIT(7) | BIT(8) | BIT(9)) +#define NAN_CDW_INFO_5G_OVERRIDE_POS 10 +#define NAN_CDW_INFO_5G_OVERRIDE_MASK (BIT(10) | BIT(11) | BIT(12) | BIT(13)) + +/* Wi-Fi Aware spec v4.0, Table 81 (Operation Mode field format) */ +#define NAN_DEV_CAPA_OP_MODE_PHY_MODE (BIT(0) | BIT(4)) +#define NAN_DEV_CAPA_OP_MODE_PHY_MODE_VHT BIT(0) +#define NAN_DEV_CAPA_OP_MODE_HE_VHT_80P80 BIT(1) +#define NAN_DEV_CAPA_OP_MODE_HE_VHT_160 BIT(2) +#define NAN_DEV_CAPA_OP_MODE_PHY_MODE_HE BIT(4) + +/* Number of Antennas + * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */ +#define NAN_DEV_CAPA_TX_ANT_POS 0 +#define NAN_DEV_CAPA_TX_ANT_MASK 0x0f +#define NAN_DEV_CAPA_RX_ANT_POS 4 +#define NAN_DEV_CAPA_RX_ANT_MASK 0xf0 + +/* Capabilities + * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */ +#define NAN_DEV_CAPA_DFS_MASTER BIT(0) +#define NAN_DEV_CAPA_EXT_KEY_ID BIT(1) +#define NAN_DEV_CAPA_SIM_NDP_RX BIT(2) +#define NAN_DEV_CAPA_NDPE_ATTR_SUPP BIT(3) +#define NAN_DEV_CAPA_S3 BIT(4) + +/* Device Capability attribute + * Wi-Fi Aware spec v4.0, Table 79 (Device Capability attribute format) */ +struct nan_device_capa { + u8 map_id; /* Map ID */ + le16 cdw_info; /* Committed DW Info */ + u8 supported_bands; /* Supported Bands */ + u8 op_mode; /* Operation Mode */ + u8 ant; /* Number of Antennas */ + le16 channel_switch_time; /* Max Channel Switch Time */ + u8 capa; /* Capabilities */ +} STRUCT_PACKED; + +/* NAN Data Path (NDP) attribute + * Wi-Fi Aware spec v4.0, Table 82 (NDP attribute format) */ +#define NAN_NDP_TYPE_POS 0 +#define NAN_NDP_TYPE_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#define NAN_NDP_STATUS_POS 4 +#define NAN_NDP_STATUS_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7)) + +/* NDP type (in Type and Status field) */ +enum nan_ndp_type { + NAN_NDP_TYPE_REQUEST = 0, + NAN_NDP_TYPE_RESPONSE = 1, + NAN_NDP_TYPE_CONFIRM = 2, + NAN_NDP_TYPE_SECURITY_INSTALL = 3, + NAN_NDP_TYPE_TERMINATE = 4, +}; + +/* NDP status (in Type and Status field) */ +enum nan_ndp_status { + NAN_NDP_STATUS_CONTINUED = 0, + NAN_NDP_STATUS_ACCEPTED = 1, + NAN_NDP_STATUS_REJECTED = 2, +}; + +/* Wi-Fi Aware spec v4.0, Table 84 (NDP Control field) and Table 87 (Subfields + * of NDPE Control field format). + * Bits 0-4 are identical for NDP and NDPE. The only differences are in + * Bit 5. + */ +#define NAN_NDP_CTRL_CONFIRM_REQUIRED BIT(0) +#define NAN_NDP_CTRL_SECURITY_PRESENT BIT(2) +#define NAN_NDP_CTRL_PUBLISH_ID_PRESENT BIT(3) +#define NAN_NDP_CTRL_RESPONDER_NDI_PRESENT BIT(4) + +#define NAN_NDP_CTRL_SPEC_INFO_PRESENT BIT(5) +#define NAN_NDPE_CTRL_GTK_REQUIRED BIT(5) + +/* Wi-Fi Aware spec v4.0, Table 82 (NDP attribute format) and + * Table 86 (NAN Data Path Extension attribute format) + * Note: The structure does not include the Attribute ID and Length fields. */ +struct ieee80211_ndp { + u8 dialog_token; /* Dialog Token */ + u8 type_and_status; /* Type and Status */ + u8 reason_code; /* Reason Code */ + u8 initiator_ndi[ETH_ALEN]; /* Initiator NDI */ + u8 ndp_id; /* NDP ID */ + u8 ndp_ctrl; /* NDP Control */ + + /* followed by optional fields based on ndp_ctrl */ + u8 optional[0]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 89 (List of TLV types for the NDPE attribute) */ +enum nan_ndpe_tlv_type { + NAN_NDPE_TLV_IPV6_LINK_LOCAL = 0, + NAN_NDPE_TLV_SRV_INFO = 1, +}; + +#define NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN 8 + +/* Wi-Fi Aware spec v4.0, Table 97 (Time Bitmap Control field format for the + * NAN Availability attribute) */ +#define NAN_TIME_BM_CTRL_BIT_DURATION_POS 0 +#define NAN_TIME_BM_CTRL_BIT_DURATION_MASK (BIT(0) | BIT(1) | BIT(2)) +#define NAN_TIME_BM_CTRL_BIT_DURATION_16_TU 0 +#define NAN_TIME_BM_CTRL_BIT_DURATION_32_TU 1 +#define NAN_TIME_BM_CTRL_BIT_DURATION_64_TU 2 +#define NAN_TIME_BM_CTRL_BIT_DURATION_128_TU 3 + +#define NAN_TIME_BM_CTRL_PERIOD_POS 3 +#define NAN_TIME_BM_CTRL_PERIOD_MASK (BIT(3) | BIT(4) | BIT(5)) +#define NAN_TIME_BM_CTRL_PERIOD_NONE 0 + +#define NAN_TIME_BM_CTRL_START_OFFSET_POS 6 +#define NAN_TIME_BM_CTRL_START_OFFSET_MASK (BIT(6) | BIT(7) | BIT(8) | \ + BIT(9) | BIT(10) | BIT(11) | \ + BIT(12) | BIT(13) | BIT(14)) + +/* Wi-Fi Aware spec v4.0, Table 96 (Entry Control field format for the + * NAN Availability attribute) */ +#define NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED BIT(0) +#define NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL BIT(1) +#define NAN_AVAIL_ENTRY_CTRL_TYPE_COND BIT(2) +#define NAN_AVAIL_ENTRY_CTRL_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) + +#define NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS 3 +#define NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_MASK (BIT(3) | BIT(4)) +#define NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN 7 +#define NAN_AVAIL_ENTRY_CTRL_UTIL_MAX 5 +#define NAN_AVAIL_ENTRY_CTRL_UTIL_POS 5 +#define NAN_AVAIL_ENTRY_CTRL_UTIL_MASK (BIT(5) | BIT(6) | BIT(7)) +#define NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS 8 +#define NAN_AVAIL_ENTRY_CTRL_RX_NSS_MASK (BIT(8) | BIT(9) | BIT(10) | \ + BIT(11)) +#define NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT BIT(12) + +/* Wi-Fi Aware spec v4.0, Table 99 (List of Band Entries) */ +enum nan_band_entry { + NAN_BAND_ENTRY_SUB_1G = 1, + NAN_BAND_ENTRY_2G = 2, + NAN_BAND_ENTRY_5G = 4, + NAN_BAND_ENTRY_6G = 7, +}; + +/* Wi-Fi Aware spec v4.0, Table 100 (Channel Entry format for the + * NAN Availability attribute) */ +struct nan_chan_entry { + u8 op_class; /* Operating Class */ + le16 chan_bitmap; /* Channel Bitmap */ + u8 pri_chan_bitmap; /* Primary Channel Bitmap */ + + /* This field is optional. It is present only if + * NAN_BAND_CHAN_CTRL_NON_CONT_BW is set + */ + le16 aux_chan_bitmap; /* Auxiliary Channel Bitmap */ +} STRUCT_PACKED; + +/* Channel entry only contains the aux_chan_bitmap field for 80+80 MHz operating + * class. See Table 100. */ +#define NAN_CHAN_ENTRY_MIN_LEN 4 +#define NAN_CHAN_ENTRY_80P80_LEN 6 + +/* Wi-Fi Aware spec v4.0, Table 98 (Band/Channel Entries List field format for + * the NAN Availability attribute) */ +#define NAN_BAND_CHAN_CTRL_TYPE BIT(0) +#define NAN_BAND_CHAN_CTRL_NON_CONT_BW BIT(1) +#define NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS 4 +#define NAN_BAND_CHAN_CTRL_NUM_ENTRIES_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7)) + +struct nan_band_chan_list { + u8 ctrl; + u8 entries[0]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 95 (Availability Entry field format for the + * NAN Availability attribute) + */ +struct nan_avail_ent { + le16 len; + le16 ctrl; /* Entry Control */ + + /* followed by optional fields based on ctrl. Note that this also + * includes the inclusion of Time Bitmap Control and Length fields + */ + u8 optional[0]; +} STRUCT_PACKED; + +#define MIN_AVAIL_ENTRY_LEN 2 + +/* This structure represents a time bitmap related fields in the NAN + * Availability entry. */ +struct nan_tbm { + le16 ctrl; + u8 len; + u8 bitmap[0]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 94 (Attribute Control field format for the + * NAN Availability attribute) */ +#define NAN_AVAIL_CTRL_MAP_ID_POS 0 +#define NAN_AVAIL_CTRL_MAP_ID_MASK 0xf +#define NAN_AVAIL_CTRL_COMMITTED_CHANGED BIT(4) +#define NAN_AVAIL_CTRL_POTENTIAL_CHANGED BIT(5) +#define NAN_AVAIL_CTRL_PUB_AVAIL_ATTR_CHANGED BIT(6) +#define NAN_AVAIL_CTRL_NDC_ATTR_CHANGED BIT(7) + +/* Wi-Fi Aware spec v4.0, Table 93 (NAN Availability attribute format). + * Attribute ID and Length fields not included. */ +struct nan_avail { + u8 seq_id; /* Sequence ID */ + le16 ctrl; /* Attribute Control */ + + /* followed by Availability Entry List */ + u8 optional[0]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 110 (Attribute Control field format for the + * Unaligned Schedule attribute) + */ +#define NAN_UNALIGNED_SCHED_CTRL_SCHED_ID_MASK (BIT(0) | BIT(1) | BIT(2) | \ + BIT(3)) + +/* Wi-Fi Aware spec v4.0, Table 111 (ULW Overwrite field format) */ +#define NAN_ULW_OVERWRITE_ALL BIT(0) +#define NAN_ULW_OVERWRITE_MAP_ID_POS 1 +#define NAN_ULW_OVERWRITE_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) | BIT(4)) + +/* Wi-Fi Aware spec v4.0, Table 112 (ULW Control field format) */ +#define NAN_ULW_CTRL_TYPE_POS 0 +#define NAN_ULW_CTRL_TYPE_MASK (BIT(0) | BIT(1)) +#define NAN_ULW_CTRL_TYPE_BAND_ID 0 +#define NAN_ULW_CTRL_TYPE_CHAN_ENTRY 1 +#define NAN_ULW_CTRL_TYPE_CHAN_ENTRY_WITH_AUX 2 +#define NAN_ULW_CTRL_CHAN_AVAIL BIT(2) +#define NAN_ULW_CTRL_RX_NSS_POS 3 +#define NAN_ULW_CTRL_RX_NSS_MASK (BIT(3) | BIT(4) | BIT(5) | BIT(6)) + +/* Wi-Fi Aware spec v4.0, Table 109 (Unaligned Schedule attribute format). + * ID and length are not included. + */ +struct nan_unaligned_sched { + u8 sched_id; + u8 seq_id; + le32 start_time; + le32 duration; + le32 period; + u8 count_down; + u8 ulw_overwrite; + + /* followed by optional fields (ULW Control and Band ID or Channel + * Entry) */ + u8 optional[0]; +} STRUCT_PACKED; + +#define NAN_SCHED_ENTRY_MAP_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +/* Wi-Fi Aware spec v4.0, Table 104 (Schedule Entry format for the NDC + * attribute) */ +struct nan_sched_entry { + u8 map_id; /* Map ID */ + le16 control; /* Time Bitmap Control */ + u8 len; /* Time Bitmap Length */ + u8 bm[0]; /* Time Bitmap */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 103 (Attribute Control field format for the NDC + * attribute) */ +#define NAN_NDC_CTRL_SELECTED BIT(0) + +/* Wi-Fi Aware spec v4.0, Table 102 (NDC attribute format). + * Attribute ID and Length fields not included */ +struct ieee80211_ndc { + u8 ndc_id[ETH_ALEN]; /* NDC ID */ + u8 ctrl; /* Attribute Control */ + u8 sched_entries[0]; /* Schedule Entry List */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 105 (NDL attribute format) */ +#define NAN_NDL_TYPE_POS 0 +#define NAN_NDL_TYPE_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#define NAN_NDL_STATUS_POS 4 +#define NAN_NDL_STATUS_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7)) + +enum nan_ndl_type { + NAN_NDL_TYPE_REQUEST = 0, + NAN_NDL_TYPE_RESPONSE = 1, + NAN_NDL_TYPE_CONFIRM = 2, +}; + +enum nan_ndl_status { + NAN_NDL_STATUS_CONTINUED = 0, + NAN_NDL_STATUS_ACCEPTED = 1, + NAN_NDL_STATUS_REJECTED = 2, +}; + +/* Wi-Fi Aware spec v4.0, Table 107 (NDL Control field format) */ +#define NAN_NDL_CTRL_PEER_ID_PRESENT BIT(0) +#define NAN_NDL_CTRL_IMMUT_SCHED_PRESENT BIT(1) +#define NAN_NDL_CTRL_NDC_ATTR_PRESENT BIT(2) +#define NAN_NDL_CTRL_NDL_QOS_ATTR_PRESENT BIT(3) +#define NAN_NDL_CTRL_MAX_IDLE_PERIOD_PRESENT BIT(4) +#define NAN_NDL_CTRL_NDL_TYPE BIT(5) +#define NAN_NDL_CTRL_NDL_SETUP_REASON_POS 6 +#define NAN_NDL_CTRL_NDL_SETUP_REASON_MASK (BIT(6) | BIT(7)) + +#define NAN_NDL_CTRL_NDL_SETUP_REASON_NDP 0x0 +#define NAN_NDL_CTRL_NDL_SETUP_REASON_FSD_GAS 0x1 + +/* Wi-Fi Aware spec v4.0, Table 105 (NDL attribute format) */ +struct ieee80211_ndl { + u8 dialog_token; /* Dialog Token */ + u8 type_and_status; /* Type and Status */ + u8 reason_code; /* Reason Code */ + u8 ctrl; /* NDL Control */ + + /* followed by optional fields based on ndl_ctrl */ + u8 optional[0]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 130 (Element Container attribute format) */ +#define NAN_ELEMENT_CONTAINER_MAP_ID_VALID_POS 0 +#define NAN_ELEMENT_CONTAINER_MAP_ID_VALID_MASK BIT(0) +#define NAN_ELEMENT_CONTAINER_MAP_ID_POS 1 +#define NAN_ELEMENT_CONTAINER_MAP_ID_MASK (BIT(1) | BIT(2) | BIT(3) |\ + BIT(4)) + +struct ieee80211_elemc { + u8 map_id; /* Map ID */ + u8 variable[0]; /* Elements */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 108 (NDL QoS attribute format) */ +struct ieee80211_nan_qos { + u8 min_slots; /* Minimum time slots */ + le16 max_latency; /* Maximum latency */ +} STRUCT_PACKED; + +#define NAN_QOS_MIN_SLOTS_NO_PREF 0 +#define NAN_QOS_MAX_LATENCY_NO_PREF 0xffff + +#define NAN_AUTH_TOKEN_LEN 16 +#define NAN_KEY_MIC_LEN 16 +#define NAN_KEY_MIC_24_LEN 24 + +/* Wi-Fi Aware spec v4.0, Table 121 (Cipher Suite attribute field format) */ +enum nan_cipher_suite_id { + NAN_CS_NONE = 0, + NAN_CS_SK_CCM_128 = 1, + NAN_CS_SK_GCM_256 = 2, + NAN_CS_PK_2WDH_128 = 3, + NAN_CS_PK_2WDH_256 = 4, + NAN_CS_GTK_CCMP_128 = 5, + NAN_CS_GTK_GCMP_256 = 6, + NAN_CS_PK_PASN_128 = 7, + NAN_CS_PK_PASN_256 = 8, + /* Keep last */ + NAN_CS_MAX +}; + +/* Helper macros to check CSID properties */ +#define NAN_CS_IS_128(csid) \ + ((csid) == NAN_CS_SK_CCM_128 || (csid) == NAN_CS_PK_PASN_128) +#define NAN_CS_IS_256(csid) \ + ((csid) == NAN_CS_SK_GCM_256 || (csid) == NAN_CS_PK_PASN_256) +#define NAN_CS_IS_VALID_NDP(csid) \ + (NAN_CS_IS_128(csid) || NAN_CS_IS_256(csid)) +#define NAN_CS_IS_PASN(csid) \ + ((csid) == NAN_CS_PK_PASN_128 || (csid) == NAN_CS_PK_PASN_256) + +struct nan_cipher_suite { + u8 csid; /* Cipher Suite ID */ + u8 instance_id; /* Publish ID */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 122 (Cipher Suite Information attribute (CSIA) + * field format) */ +#define NAN_CS_INFO_CAPA_16_ND_TKSA_REPLAY_COUNTERS BIT(0) +#define NAN_CS_INFO_CAPA_GTK_SUPP_POS 1 +#define NAN_CS_INFO_CAPA_GTK_SUPP_MASK (BIT(1) | BIT(2)) +#define NAN_CS_INFO_CAPA_GTK_SUPP_NONE 0 +#define NAN_CS_INFO_CAPA_GTK_SUPP_NO_BIGTK 1 +#define NAN_CS_INFO_CAPA_GTK_SUPP_ALL 2 +#define NAN_CS_INFO_CAPA_16_REPLAY_COUNTERS BIT(3) +#define NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256 BIT(4) + +struct nan_cipher_suite_info { + u8 capab; /* Capabilities */ + u8 cs[0]; /* Cipher Suite List */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 123 (Security Context Identifier (SCID) field + * format) */ +enum nan_sec_ctx_type { + NAN_SEC_CTX_TYPE_ND_PMKID = 1, +}; + +struct nan_sec_ctxt { + le16 len; /* Security Context Identifier Type Length */ + u8 scid; /* Security Context Identifier Type */ + u8 instance_id; /* Publish ID */ + u8 ctxt[0]; /* Security Context Identifier */ +} STRUCT_PACKED; + +/* Only key descriptor type 2 is supported */ +#define NAN_KEY_DESC 2 + +/* Wi-Fi Aware spec v4.0, Table 125 (NAN Shared Key Descriptor attribute field + * format) */ +struct nan_shared_key { + u8 publish_id; /* Publish ID */ + + /* + * The format of the key is as defined in the IEEE 802.11 standard, + * starting with the Descriptor Type field. See struct wpa_eapol_key. + */ + u8 key[0]; /* IEEE 802.11 RSNA Key Descriptor */ +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Table 141 (Capability Info field) */ +#define NAN_DEV_CAPA_EXT_INFO_0_REG_VALID BIT(0) +#define NAN_DEV_CAPA_EXT_INFO_0_AP_TYPE_POS 1 +#define NAN_DEV_CAPA_EXT_INFO_0_AP_TYPE_MASK (BIT(1) | BIT(2) | BIT(3)) +#define NAN_DEV_CAPA_EXT_INFO_INDOOR_AP 0 +#define NAN_DEV_CAPA_EXT_INFO_SP_AP 1 +#define NAN_DEV_CAPA_EXT_INFO_VLP_AP 2 +#define NAN_DEV_CAPA_EXT_INFO_INDOOR_ENABLED_AP 3 +#define NAN_DEV_CAPA_EXT_INFO_INDOOR_SP_AP 4 + +#define NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP BIT(0) +#define NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING BIT(1) + +/* Wi-Fi Aware spec v4.0, Table 22 (NIRA Cryptographic Parameters and Methods) + */ +#define NAN_NIRA_NONCE_LEN 8 +#define NAN_NIRA_TAG_LEN 8 +#define NAN_NIRA_STR "NIR" +#define NAN_NIRA_STR_LEN 3 +#define NAN_NIRA_CIPHER_VER_128 0 +#define NAN_NIK_LEN 16 + +#define NAN_PASN_SSID "516F9A010000" + +/* Wi-Fi Aware spec v4.0, Table 126 (NAN KDE field format) */ +#define NAN_KEY_DATA_NIK RSN_SELECTOR(0x50, 0x6f, 0x9a, 36) +#define NAN_KEY_DATA_LIFETIME RSN_SELECTOR(0x50, 0x6f, 0x9a, 37) + +/* Wi-Fi Aware spec v4.0, Figure 61 (NIK KDE format) + * Note: The NIK field is defined with a variable length in Figure 61, but + * since only one cipher version is defined for now (which determines the + * length), use a fixed length here. + */ +struct nan_nik_kde { + u8 cipher_ver; + u8 nik[NAN_NIK_LEN]; +} STRUCT_PACKED; + +/* Wi-Fi Aware spec v4.0, Figure 63 (Key Bitmap format) */ +#define NAN_KEY_LIFETIME_GTK BIT(0) +#define NAN_KEY_LIFETIME_IGTK BIT(1) +#define NAN_KEY_LIFETIME_BIGTK BIT(2) +#define NAN_KEY_LIFETIME_NIK BIT(3) +#define NAN_KEY_LIFETIME_ND_TK BIT(4) +#define NAN_KEY_LIFETIME_NM_TK BIT(5) + +/* Wi-Fi Aware spec v4.0, Figure 62 (NAN Key Lifetime KDE format) */ +struct nan_key_lifetime_kde { + le16 key_bitmap; + be32 lifetime_sec; +} STRUCT_PACKED; + +#endif /* NAN_DEFS_H */ diff --git a/contrib/wpa/src/common/proc_coord.c b/contrib/wpa/src/common/proc_coord.c new file mode 100644 index 000000000000..ac9157878820 --- /dev/null +++ b/contrib/wpa/src/common/proc_coord.c @@ -0,0 +1,817 @@ +/* + * Coordination of operations between processes + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "proc_coord.h" + + +struct proc_coord_msg_header { + u32 msg_type; /* enum proc_coord_message_types */ + u32 cmd; /* proc_coord_commands */ + u32 seq; +}; + +enum proc_coord_peer_state { + PROC_COORD_PEER_WAITING, + PROC_COORD_PEER_ACTIVE, + PROC_COORD_PEER_TIMED_OUT, +}; + +struct proc_coord_peer { + struct dl_list list; + pid_t pid; + enum proc_coord_peer_state state; + struct os_reltime last_rx; +}; + +struct proc_coord_pending_request { + struct dl_list list; + struct proc_coord_peer *peer; + enum proc_coord_commands cmd; + u32 seq; + proc_coord_response_cb cb; + void *cb_ctx; + struct os_reltime timeout; +}; + +struct proc_coord_handler { + struct dl_list list; + proc_coord_cb cb; + void *cb_ctx; +}; + +struct proc_coord { + pid_t pid; + char *dir; + char *own_sock; + int sock; + u32 next_seq; + struct dl_list handlers; /* struct proc_coord_handler::list */ + struct dl_list peers; /* struct proc_coord_peer::list */ + struct dl_list requests; /* struct proc_coord_pending_request::list */ +}; + + +static void proc_coord_set_req_expire_timer(struct proc_coord *pc); + + +static struct proc_coord_peer * proc_coord_get_peer(struct proc_coord *pc, + pid_t pid) +{ + struct proc_coord_peer *peer; + + dl_list_for_each(peer, &pc->peers, struct proc_coord_peer, list) { + if (peer->pid == pid) + return peer; + } + + return NULL; +} + + +static struct proc_coord_peer * proc_coord_add_peer(struct proc_coord *pc, + pid_t pid) +{ + struct proc_coord_peer *peer; + + peer = os_zalloc(sizeof(*peer)); + if (!peer) + return NULL; + + peer->pid = pid; + peer->state = PROC_COORD_PEER_WAITING; + dl_list_add(&pc->peers, &peer->list); + + return peer; +} + + +static void proc_coord_remove_req(struct proc_coord_pending_request *req) +{ + dl_list_del(&req->list); + if (req->cb) + req->cb(req->cb_ctx, req->peer->pid, NULL); + os_free(req); +} + + +static void proc_coord_remove_peer(struct proc_coord *pc, + struct proc_coord_peer *peer) +{ + struct proc_coord_pending_request *req, *tmp; + + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (req->peer == peer) + proc_coord_remove_req(req); + } + + dl_list_del(&peer->list); + os_free(peer); +} + + +static void proc_coord_expire_requests(void *eloop_ctx, void *timeout_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct proc_coord_pending_request *req, *tmp; + struct os_reltime now; + + os_get_reltime(&now); + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (os_reltime_before(&req->timeout, &now)) { + wpa_printf(MSG_DEBUG, + "proc_coord: Pending request peer=%u cmd=%d seq=%u timed out", + req->peer->pid, req->cmd, req->seq); + proc_coord_remove_req(req); + } + } + + proc_coord_set_req_expire_timer(pc); +} + + +void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb, + void *cb_ctx) +{ + struct proc_coord_pending_request *req, *tmp; + + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (req->cb == cb && req->cb_ctx == cb_ctx) { + req->cb = NULL; + req->cb_ctx = NULL; + proc_coord_remove_req(req); + } + } + + proc_coord_set_req_expire_timer(pc); +} + + +static void proc_coord_set_req_expire_timer(struct proc_coord *pc) +{ + struct proc_coord_pending_request *req; + struct os_reltime *first = NULL; + + eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL); + dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request, + list) { + if (!first || os_reltime_before(&req->timeout, first)) + first = &req->timeout; + } + + if (first) { + struct os_reltime now, res; + unsigned int ms; + + os_get_reltime(&now); + if (os_reltime_before(first, &now)) { + ms = 0; + } else { + os_reltime_sub(first, &now, &res); + ms = os_reltime_in_ms(&res); + } + eloop_register_timeout(ms / 1000, (ms % 1000) * 1000, + proc_coord_expire_requests, pc, NULL); + } +} + + +static struct proc_coord_pending_request * +proc_coord_get_request(struct proc_coord *pc, struct proc_coord_peer *peer, + enum proc_coord_commands cmd, u32 seq) +{ + struct proc_coord_pending_request *req; + + dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request, + list) { + if (req->peer == peer && req->cmd == cmd && req->seq == seq) + return req; + } + + return NULL; +} + + +static int proc_coord_send_msg(struct proc_coord *pc, + struct proc_coord_peer *peer, + enum proc_coord_message_types msg_type, + enum proc_coord_commands cmd, + u32 seq, const struct wpabuf *msg) +{ + struct proc_coord_msg_header hdr; + ssize_t res; + struct sockaddr_un addr; + struct msghdr mh; + struct iovec io[2]; + + wpa_printf(MSG_DEBUG, + "proc_coord: Send message to %d (msg_type=%u cmd=%u seq=%u)", + peer->pid, msg_type, cmd, seq); + + os_memset(&hdr, 0, sizeof(hdr)); + hdr.msg_type = msg_type; + hdr.cmd = cmd; + hdr.seq = seq; + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%u", + pc->dir, peer->pid); + + io[0].iov_base = &hdr; + io[0].iov_len = sizeof(hdr); + if (msg) { + io[1].iov_base = (void *) wpabuf_head(msg); + io[1].iov_len = wpabuf_len(msg); + } + + os_memset(&mh, 0, sizeof(mh)); + mh.msg_iov = io; + mh.msg_iovlen = msg ? 2 : 1; + mh.msg_name = (void *) &addr; + mh.msg_namelen = sizeof(addr); + + res = sendmsg(pc->sock, &mh, MSG_DONTWAIT); + if (res < 0) { + int err = errno; + + wpa_printf(MSG_INFO, "proc_coord: sendmsg: %s", + strerror(errno)); + if (err == ENOENT || err == ECONNREFUSED) { + wpa_printf(MSG_INFO, + "proc_coord: Remove peer %u due to connection being refused", + peer->pid); + proc_coord_remove_peer(pc, peer); + } + return -1; + } + return 0; +} + + +static void proc_coord_rx_starting(struct proc_coord *pc, pid_t pid) +{ + wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STARTING", pid); +} + + +static void proc_coord_rx_stopping(struct proc_coord *pc, pid_t pid) +{ + struct proc_coord_peer *peer; + + wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STOPPING", pid); + + peer = proc_coord_get_peer(pc, pid); + if (peer) { + wpa_printf(MSG_INFO, + "proc_coord: Remove peer %u due to STOPPING event", + pid); + proc_coord_remove_peer(pc, peer); + } +} + + +static void proc_coord_rx_ping(struct proc_coord *pc, pid_t pid, + enum proc_coord_message_types msg_type, u32 seq) +{ + if (msg_type != PROC_COORD_MSG_REQUEST) + return; + + wpa_printf(MSG_DEBUG, "proc_coord: Reply to peer %u PING", pid); + proc_coord_send_response(pc, pid, PROC_COORD_CMD_PING, seq, NULL); +} + + +static void proc_coord_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct sockaddr_un from; + struct wpabuf *msg; + size_t msg_len; + ssize_t res; + struct proc_coord_msg_header hdr; + pid_t pid; + char tmp[20], *pos, *end; + struct proc_coord_peer *peer; + struct msghdr mh; + struct iovec io[2]; + struct proc_coord_handler *handler, *h_tmp; + + res = recv(sock, tmp, 0, MSG_PEEK | MSG_TRUNC); + if (res < 0) { + wpa_printf(MSG_ERROR, "proc_coord: recv: %s", + strerror(errno)); + return; + } + msg_len = res; + if (msg_len < sizeof(hdr)) + return; + msg_len -= sizeof(hdr); + + msg = wpabuf_alloc(msg_len); + if (!msg) + return; + + io[0].iov_base = &hdr; + io[0].iov_len = sizeof(hdr); + io[1].iov_base = wpabuf_mhead(msg); + io[1].iov_len = msg_len; + + os_memset(&mh, 0, sizeof(mh)); + mh.msg_iov = io; + mh.msg_iovlen = 2; + mh.msg_name = (void *) &from; + mh.msg_namelen = sizeof(from); + + res = recvmsg(sock, &mh, MSG_DONTWAIT); + if (res < 0) { + wpa_printf(MSG_ERROR, "proc_coord: recvmsg: %s", + strerror(errno)); + goto out; + } + if ((size_t) res < sizeof(hdr)) + goto out; + + wpa_printf(MSG_DEBUG, "proc_coord: Received message from %s", + from.sun_path); + if ((size_t) res > sizeof(hdr)) { + wpabuf_put(msg, res - sizeof(hdr)); + wpa_hexdump_buf(MSG_MSGDUMP, + "proc_coord: Received message payload", msg); + } + + end = ((char *) &from) + mh.msg_namelen; + + /* Require same directory for client socket */ + if ((size_t) (end - from.sun_path) < os_strlen(pc->dir) || + os_strncmp(pc->dir, from.sun_path, os_strlen(pc->dir)) != 0) + goto out; + + /* Find the peer PID from the socket name */ + pos = end - 1; + while (pos > from.sun_path) { + if (*pos == '/') + break; + pos--; + } + if (pos == from.sun_path) + goto out; + pos++; + os_memcpy(tmp, pos, end - pos); + pid = atoi(tmp); + + wpa_printf(MSG_DEBUG, "proc_coord: pid=%u msg_type=%u cmd=%u seq=%u", + pid, hdr.msg_type, hdr.cmd, hdr.seq); + + peer = proc_coord_get_peer(pc, pid); + + switch (hdr.msg_type) { + case PROC_COORD_MSG_REQUEST: + break; + case PROC_COORD_MSG_RESPONSE: { + struct proc_coord_pending_request *req; + + if (!peer) { + wpa_printf(MSG_DEBUG, + "proc_coord: Discard msg_type=RESPONSE from %u since there is no peer entry for it", + pid); + goto out; + } + + req = proc_coord_get_request(pc, peer, hdr.cmd, hdr.seq); + if (!req) { + wpa_printf(MSG_DEBUG, + "proc_coord: Discard msg_type=RESPONSE from %u since there is no pending request for it", + pid); + goto out; + } + if (req->cb) + req->cb(req->cb_ctx, peer->pid, msg); + dl_list_del(&req->list); + os_free(req); + break; + } + case PROC_COORD_MSG_EVENT: + break; + default: + wpa_printf(MSG_DEBUG, + "proc_coord: Discard unknown msg_type=%u from %u", + hdr.msg_type, pid); + goto out; + } + + if (!peer) { + wpa_printf(MSG_DEBUG, + "proc_coord: Add new peer entry for %d based on received message", + pid); + peer = proc_coord_add_peer(pc, pid); + if (!peer) { + wpa_printf(MSG_ERROR, + "proc_coord: Could not add peer entry for %u", + pid); + goto out; + } + } + + if (peer->state != PROC_COORD_PEER_ACTIVE) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d active due to received message", + pid); + peer->state = PROC_COORD_PEER_ACTIVE; + } + + os_get_reltime(&peer->last_rx); + + switch (hdr.cmd) { + case PROC_COORD_CMD_STARTING : + proc_coord_rx_starting(pc, pid); + break; + case PROC_COORD_CMD_STOPPING: + proc_coord_rx_stopping(pc, pid); + break; + case PROC_COORD_CMD_PING: + proc_coord_rx_ping(pc, pid, hdr.msg_type, hdr.seq); + break; + default: + if (hdr.msg_type == PROC_COORD_MSG_RESPONSE) + break; + + dl_list_for_each_safe(handler, h_tmp, &pc->handlers, + struct proc_coord_handler, list) { + if (handler->cb(handler->cb_ctx, pid, hdr.msg_type, + hdr.cmd, hdr.seq, msg)) + break; + } + break; + } + +out: + wpabuf_free(msg); +} + + +static void proc_coord_send_starting(struct proc_coord *pc) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + + if (dl_list_empty(&pc->peers)) + return; + + wpa_printf(MSG_DEBUG, "proc_coord: Send STARTING event to all peers"); + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, + PROC_COORD_CMD_STARTING, 0, NULL) == 0) + count++; + } + wpa_printf(MSG_DEBUG, "proc_coord: STARTING sent to %d peer(s)", count); +} + + +static void proc_coord_send_stopping(struct proc_coord *pc) +{ + if (dl_list_empty(&pc->peers)) + return; + + wpa_printf(MSG_DEBUG, + "proc_coord: Send STOPPING event to all active peers"); + proc_coord_send_event(pc, 0, PROC_COORD_CMD_STOPPING, NULL); +} + + +static void proc_coord_cb_ping(void *ctx, int pid, const struct wpabuf *msg) +{ + struct proc_coord *pc = ctx; + struct proc_coord_peer *peer; + + peer = proc_coord_get_peer(pc, pid); + if (!peer) + return; + if (msg) { + if (peer->state != PROC_COORD_PEER_ACTIVE) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d active due to response to PING", + pid); + peer->state = PROC_COORD_PEER_ACTIVE; + } + } else { + if (peer->state != PROC_COORD_PEER_TIMED_OUT) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d timed out due to no response to PING", + pid); + peer->state = PROC_COORD_PEER_TIMED_OUT; + } + } +} + + +static void proc_coord_update_peers_from_dir(struct proc_coord *pc) +{ + DIR *dir; + struct dirent *de; + struct proc_coord_peer *peer, *tmp; + + /* Remove peers that do not have a socket file */ + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + char fname[256]; + + os_snprintf(fname, sizeof(fname), "%s/%d", pc->dir, peer->pid); + if (!os_file_exists(fname)) { + wpa_printf(MSG_DEBUG, + "proc_coord: Remove peer %d due to socket file not present", + peer->pid); + proc_coord_remove_peer(pc, peer); + } + } + + /* Add peer entries for all new sockets in the directory */ + dir = opendir(pc->dir); + if (!dir) { + wpa_printf(MSG_ERROR, "proc_coord: opendir: %s", + strerror(errno)); + return; + } + + while ((de = readdir(dir))) { + int pid = atoi(de->d_name); + + if (pid <= 0 || pid == pc->pid) + continue; + + peer = proc_coord_get_peer(pc, pid); + if (peer) + continue; + + wpa_printf(MSG_DEBUG, "proc_coord: Add new peer entry for %u", + pid); + peer = proc_coord_add_peer(pc, pid); + if (!peer) { + wpa_printf(MSG_ERROR, + "proc_coord: Could not add peer entry for %u", + pid); + continue; + } + + wpa_printf(MSG_DEBUG, "proc_coord: Ping new peer %u", pid); + proc_coord_send_request(pc, pid, PROC_COORD_CMD_PING, NULL, + 10000, proc_coord_cb_ping, pc); + } + + closedir(dir); +} + + +static void proc_coord_update_peers(void *eloop_ctx, void *timeout_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct proc_coord_peer *peer, *tmp; + struct os_reltime now; + + proc_coord_update_peers_from_dir(pc); + + os_get_reltime(&now); + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (!os_reltime_expired(&now, &peer->last_rx, 60)) + continue; + proc_coord_send_request(pc, peer->pid, PROC_COORD_CMD_PING, + NULL, 10000, NULL, NULL); + } + + eloop_register_timeout(10, 0, proc_coord_update_peers, pc, NULL); +} + + +struct proc_coord * proc_coord_init(const char *dir) +{ + struct proc_coord *pc; + struct sockaddr_un addr; + size_t len; + int flags; + + pc = os_zalloc(sizeof(*pc)); + if (!pc) + return NULL; + + dl_list_init(&pc->peers); + dl_list_init(&pc->requests); + dl_list_init(&pc->handlers); + pc->sock = -1; + + pc->dir = os_strdup(dir); + if (!pc->dir) + goto fail; + + len = os_strlen(dir) + 20; + pc->own_sock = os_zalloc(len); + if (!pc->own_sock) + goto fail; + pc->pid = getpid(); + os_snprintf(pc->own_sock, len, "%s/%d", dir, pc->pid); + wpa_printf(MSG_DEBUG, "proc_coord: Own socket at %s", pc->own_sock); + unlink(pc->own_sock); + + pc->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (pc->sock < 0) { + wpa_printf(MSG_ERROR, "proc_coord: socket(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, pc->own_sock, sizeof(addr.sun_path)); + if (bind(pc->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "proc_coord: bind(PF_UNIX) failed: %s", + strerror(errno)); + goto fail; + } + + flags = fcntl(pc->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(pc->sock, F_SETFL, flags) < 0) { + wpa_printf(MSG_INFO, + "proc_coord: fcntl(O_NONBLOCK): %s", + strerror(errno)); + /* Not fatal, continue on.*/ + } + } + + eloop_register_read_sock(pc->sock, proc_coord_receive, pc, NULL); + + proc_coord_update_peers_from_dir(pc); + + /* Start periodic updates quickly to recover from potential race + * conditions if the processes are started at the same time. */ + eloop_register_timeout(1, 0, proc_coord_update_peers, pc, NULL); + + proc_coord_send_starting(pc); + + return pc; + +fail: + proc_coord_deinit(pc); + return NULL; +} + + +void proc_coord_deinit(struct proc_coord *pc) +{ + struct proc_coord_peer *peer, *tmp; + struct proc_coord_pending_request *req, *tmp2; + + if (!pc) + return; + + eloop_cancel_timeout(proc_coord_update_peers, pc, NULL); + eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL); + + if (pc->sock >= 0) { + proc_coord_send_stopping(pc); + eloop_unregister_read_sock(pc->sock); + close(pc->sock); + unlink(pc->own_sock); + } + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) + proc_coord_remove_peer(pc, peer); + + dl_list_for_each_safe(req, tmp2, &pc->requests, + struct proc_coord_pending_request, list) { + dl_list_del(&req->list); + os_free(req); + } + + os_free(pc->dir); + os_free(pc->own_sock); + os_free(pc); +} + + +int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx) +{ + struct proc_coord_handler *handler; + + handler = os_zalloc(sizeof(*handler)); + if (!handler) + return -1; + + handler->cb = cb; + handler->cb_ctx = cb_ctx; + dl_list_add(&pc->handlers, &handler->list); + return 0; +} + + +void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx) +{ + struct proc_coord_handler *handler; + + dl_list_for_each(handler, &pc->handlers, struct proc_coord_handler, + list) { + if (handler->cb == cb && handler->cb_ctx == cb_ctx) { + dl_list_del(&handler->list); + os_free(handler); + break; + } + } +} + + +int proc_coord_send_event(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + u32 seq = 0; + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (dst && peer->pid != dst) + continue; + if (!dst && peer->state != PROC_COORD_PEER_ACTIVE) + continue; + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, cmd, + seq, msg) == 0) + count++; + } + + return count; +} + + +int proc_coord_send_request(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg, + unsigned int timeout_ms, + proc_coord_response_cb cb, + void *cb_ctx) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + struct proc_coord_pending_request *req; + + pc->next_seq++; + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (dst && peer->pid != dst) + continue; + if (!dst && peer->state != PROC_COORD_PEER_ACTIVE) + continue; + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_REQUEST, cmd, + pc->next_seq, msg) < 0) + continue; + count++; + + req = os_zalloc(sizeof(*req)); + if (!req) + break; + req->peer = peer; + req->cmd = cmd; + req->seq = pc->next_seq; + req->cb = cb; + req->cb_ctx = cb_ctx; + os_get_reltime(&req->timeout); + os_reltime_add_ms(&req->timeout, timeout_ms); + dl_list_add(&pc->requests, &req->list); + } + proc_coord_set_req_expire_timer(pc); + + return count; +} + + +int proc_coord_send_response(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, u32 seq, + const struct wpabuf *msg) +{ + struct proc_coord_peer *peer = proc_coord_get_peer(pc, dst); + + if (!peer) + return -1; + return proc_coord_send_msg(pc, peer, PROC_COORD_MSG_RESPONSE, cmd, + seq, msg); +} diff --git a/contrib/wpa/src/common/proc_coord.h b/contrib/wpa/src/common/proc_coord.h new file mode 100644 index 000000000000..8eae443444f2 --- /dev/null +++ b/contrib/wpa/src/common/proc_coord.h @@ -0,0 +1,139 @@ +/* + * Coordination of operations between processes + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PROC_COORD_H +#define PROC_COORD_H + +struct proc_coord; + +enum proc_coord_message_types { + PROC_COORD_MSG_REQUEST = 0, + PROC_COORD_MSG_RESPONSE = 1, + PROC_COORD_MSG_EVENT = 2, +}; + +enum proc_coord_commands { + PROC_COORD_CMD_STARTING = 0, + PROC_COORD_CMD_STOPPING = 1, + PROC_COORD_CMD_PING = 2, + PROC_COORD_CMD_TEST = 3, +}; + +/** + * proc_coord_init - Initialize process coordinations + * @dir: Access controlled directory for process coordination + * Returns: Context pointer on success or %NULL on failure + * + * The returned context must be released with a call to proc_coord_deinit(). + */ +struct proc_coord * proc_coord_init(const char *dir); + +/** + * proc_coord_deinit - Deinitialize process coordinations + * @pc: Process coordination context from proc_coord_init() + */ +void proc_coord_deinit(struct proc_coord *pc); + +typedef bool (*proc_coord_cb)(void *ctx, int src, + enum proc_coord_message_types msg_type, + enum proc_coord_commands cmd, + u32 seq, const struct wpabuf *msg); + +/** + * proc_coord_register_handler - Register a handler for process coordination + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function + * @cb_ctx: Context for the callback function + * Returns: 0 on success or -1 on failure + * + * The registered handler will be called for received request and event + * messages. Received request messages are delivered to the separate handler + * registered with proc_coord_send_request(). + * + * The handler function can return true to stop iteration of handler functions + * or false to allow the iteration to continue reporting the message to other + * registered handler functions, if any. + */ +int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx); + +/** + * proc_coord_unregister_handler - Unregister a handler for process coordination + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function + * @cb_ctx: Context for the callback function + */ +void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx); + +/** + * proc_coord_send_event - Send an event message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) or 0 for all active peers + * @cmd: The command ID for the message + * @msg: Payload of the message + * Returns: The number of peers the message was sent to + */ +int proc_coord_send_event(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg); + +typedef void (*proc_coord_response_cb)(void *ctx, int pid, + const struct wpabuf *msg); + +/** + * proc_coord_send_request - Send a request message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) or 0 for all active peers + * @cmd: The command ID for the message + * @msg: Payload of the message + * @timeout_ms: Timeout for receiving a response + * @cb: Callback function to report the responses or %NULL for no callback + * @cb_ctx: Context for the callback function + * Returns: The number of peers the message was sent to + * + * If a response is received from a peer, the response is reported to the + * callback function. If no response is received within the specified timeout, + * the callback function is called with msg == NULL. The specified @cb_ctx has + * to remain valid until all the pending responses have been reported or until + * proc_coord_cancel_wait() has been used to cancel any pending wait. + */ +int proc_coord_send_request(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg, + unsigned int timeout_ms, + proc_coord_response_cb cb, + void *cb_ctx); + +/** + * proc_coord_cancel_wait - Cancel wait for a pending response message + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function registered with proc_coord_send_request() + * @cb_ctx: Context for the callback function + */ +void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb, + void *cb_ctx); + +/** + * proc_coord_send_event - Send a response message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) + * @cmd: The command ID for the message + * @seq: The sequence number from the received request message + * @msg: Payload of the message + * Returns: 0 on success or -1 on failure + * + * This is used to send a response to a request message that was reported + * through a call to the handler function that was registered with + * proc_coord_register_handler(). + */ +int proc_coord_send_response(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, u32 seq, + const struct wpabuf *msg); + +#endif /* PROC_COORD_H */ diff --git a/contrib/wpa/src/common/proximity_ranging.c b/contrib/wpa/src/common/proximity_ranging.c new file mode 100644 index 000000000000..a230043f56e8 --- /dev/null +++ b/contrib/wpa/src/common/proximity_ranging.c @@ -0,0 +1,2707 @@ +/* + * Proxmity Ranging + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/sha256.h" +#include "pasn/pasn_common.h" +#include "proximity_ranging.h" + + +static bool valid_country_ch(char c) +{ + return c >= 'A' && c <= 'Z'; +} + + +static void pr_device_free(struct pr_data *pr, struct pr_device *dev) +{ +#ifdef CONFIG_PASN + wpabuf_free(dev->ranging_wrapper); + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + pasn_data_deinit(dev->pasn); + } +#endif /* CONFIG_PASN */ + os_free(dev); +} + + +static struct pr_device * pr_get_device(struct pr_data *pr, const u8 *addr) +{ + struct pr_device *dev; + + dl_list_for_each(dev, &pr->devices, struct pr_device, list) { + if (ether_addr_equal(dev->pr_device_addr, addr)) + return dev; + } + return NULL; +} + + +static struct pr_device * pr_create_device(struct pr_data *pr, const u8 *addr) +{ + struct pr_device *dev, *oldest = NULL; + size_t count = 0; + + dev = pr_get_device(pr, addr); + if (dev) + return dev; + + dl_list_for_each(dev, &pr->devices, struct pr_device, list) { + count++; + if (!oldest || + os_reltime_before(&dev->last_seen, &oldest->last_seen)) + oldest = dev; + } + if (count + 1 > PR_MAX_PEER && oldest) { + wpa_printf(MSG_DEBUG, + "PR: Remove oldest peer entry to make room for a new peer " + MACSTR, MAC2STR(oldest->pr_device_addr)); + dl_list_del(&oldest->list); + pr_device_free(pr, oldest); + } + + dev = os_zalloc(sizeof(*dev)); + if (!dev) + return NULL; + + dl_list_add(&pr->devices, &dev->list); + os_memcpy(dev->pr_device_addr, addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "PR: New Proximity Ranging device " MACSTR + " added to list", MAC2STR(addr)); + + return dev; +} + + +struct pr_data * pr_init(const struct pr_config *cfg) +{ + struct pr_data *pr; + + pr = os_zalloc(sizeof(*pr) + sizeof(*cfg)); + if (!pr) + return NULL; + + pr->cfg = (struct pr_config *) (pr + 1); + os_memcpy(pr->cfg, cfg, sizeof(*cfg)); + if (cfg->dev_name) + pr->cfg->dev_name = os_strdup(cfg->dev_name); + else + pr->cfg->dev_name = NULL; + + dl_list_init(&pr->devices); + dl_list_init(&pr->dev_iks); + +#ifdef CONFIG_PASN + pr->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + pr->responder_pmksa = pasn_responder_pmksa_cache_init(); +#endif /* CONFIG_PASN */ + + return pr; +} + + +static void pr_deinit_dev_iks(struct pr_data *pr) +{ + struct pr_dev_ik *dev_ik, *prev_dev_ik; + + dl_list_for_each_safe(dev_ik, prev_dev_ik, &pr->dev_iks, + struct pr_dev_ik, list) { + dl_list_del(&dev_ik->list); + os_free(dev_ik); + } +} + + +void pr_flush(struct pr_data *pr) +{ +#ifdef CONFIG_PASN + if (pr->initiator_pmksa) + pasn_initiator_pmksa_cache_flush(pr->initiator_pmksa); + if (pr->responder_pmksa) + pasn_responder_pmksa_cache_flush(pr->responder_pmksa); +#endif /* CONFIG_PASN */ +} + +void pr_deinit(struct pr_data *pr) +{ + struct pr_device *dev, *prev; + + if (!pr) + return; + + os_free(pr->cfg->dev_name); + + dl_list_for_each_safe(dev, prev, &pr->devices, struct pr_device, list) { + dl_list_del(&dev->list); + pr_device_free(pr, dev); + } + + pr_deinit_dev_iks(pr); + +#ifdef CONFIG_PASN + os_free(pr->pr_pasn_params); + pr->pr_pasn_params = NULL; + pr->ranging_final_received = false; + + pasn_initiator_pmksa_cache_deinit(pr->initiator_pmksa); + pasn_responder_pmksa_cache_deinit(pr->responder_pmksa); +#endif /* CONFIG_PASN */ + + os_free(pr); + wpa_printf(MSG_DEBUG, "PR: Deinit done"); +} + + +void pr_set_dev_addr(struct pr_data *pr, const u8 *addr) +{ + if (pr && addr) + os_memcpy(pr->cfg->dev_addr, addr, ETH_ALEN); +} + + +void pr_clear_dev_iks(struct pr_data *pr) +{ + struct pr_device *dev; + + pr->cfg->dik_len = 0; + os_memset(pr->cfg->dik_data, 0, DEVICE_IDENTITY_KEY_LEN); + pr->cfg->global_password_valid = false; + os_memset(pr->cfg->global_password, 0, + sizeof(pr->cfg->global_password)); + + dl_list_for_each(dev, &pr->devices, struct pr_device, list) { + dev->password_valid = false; + os_memset(dev->password, 0, sizeof(dev->password)); + dev->dik_valid = false; + os_memset(dev->dik, 0, DEVICE_IDENTITY_KEY_LEN); + } + + pr_deinit_dev_iks(pr); +} + + +void pr_add_dev_ik(struct pr_data *pr, const u8 *dik, const char *password, + const u8 *pmk, size_t pmk_len, bool own) +{ + struct pr_dev_ik *dev_ik; + + if (own) { + os_memcpy(pr->cfg->dik_data, dik, DEVICE_IDENTITY_KEY_LEN); + pr->cfg->dik_len = DEVICE_IDENTITY_KEY_LEN; + if (password) { + os_strlcpy(pr->cfg->global_password, password, + sizeof(pr->cfg->global_password)); + pr->cfg->global_password_valid = true; + } + return; + } + + if (pmk && (pmk_len != 32 && pmk_len != 48 && pmk_len != 64)) { + wpa_printf(MSG_INFO, "PR: Unexpected PMK length %zu", pmk_len); + return; + } + + dl_list_for_each(dev_ik, &pr->dev_iks, struct pr_dev_ik, list) { + if (os_memcmp(dik, dev_ik->dik, DEVICE_IDENTITY_KEY_LEN) == 0) { + dl_list_del(&dev_ik->list); + os_free(dev_ik); + break; + } + } + + dev_ik = os_zalloc(sizeof(*dev_ik)); + if (!dev_ik) + return; + + dl_list_add(&pr->dev_iks, &dev_ik->list); + os_memcpy(dev_ik->dik, dik, DEVICE_IDENTITY_KEY_LEN); + if (password) { + os_strlcpy(dev_ik->password, password, + sizeof(dev_ik->password)); + dev_ik->password_valid = true; + } + if (pmk) { + os_memcpy(dev_ik->pmk, pmk, pmk_len); + dev_ik->pmk_len = pmk_len; + dev_ik->pmk_valid = true; + } + + wpa_printf(MSG_DEBUG, "PR: New Device Identity added to list"); +} + + +int pr_set_peer_credentials(struct pr_data *pr, const u8 *addr, + const u8 *pmk, size_t pmk_len, + const char *password) +{ + struct pr_device *dev; + + if (!pr || !addr) + return -1; + + dev = pr_get_device(pr, addr); + if (!dev) { + wpa_printf(MSG_DEBUG, "PR: set_peer_credentials: " MACSTR + " not found", MAC2STR(addr)); + return -1; + } + + if (pmk && pmk_len) { + if (pmk_len > PMK_LEN_MAX) + return -1; + os_memcpy(dev->pmk, pmk, pmk_len); + dev->pmk_len = pmk_len; + dev->pmk_valid = true; + wpa_printf(MSG_DEBUG, "PR: PMK set for " MACSTR, MAC2STR(addr)); + } + + if (password) { + if (os_strlen(password) >= sizeof(dev->password)) + return -1; + os_strlcpy(dev->password, password, sizeof(dev->password)); + dev->password_valid = true; + wpa_printf(MSG_DEBUG, "PR: password set for " MACSTR, + MAC2STR(addr)); + } + + return 0; +} + + +static struct wpabuf * pr_encaps_elem(const struct wpabuf *subelems, + u32 ie_type) +{ + struct wpabuf *ie = NULL; + const u8 *pos, *end; + size_t len = 0; + + if (!subelems) + return NULL; + + len = wpabuf_len(subelems) + 1000; + ie = wpabuf_alloc(len); + if (!ie) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, ie_type); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + return ie; +} + + +static void pr_get_ranging_capabilities(struct pr_data *pr, + struct pr_capabilities *capab) +{ + os_memset(capab, 0, sizeof(struct pr_capabilities)); + + if (pr->cfg->dev_name) + os_strlcpy(capab->device_name, pr->cfg->dev_name, + sizeof(capab->device_name)); + + if (pr->cfg->edca_ista_support || pr->cfg->edca_rsta_support) + capab->edca_support = true; + + if (pr->cfg->ntb_ista_support || pr->cfg->ntb_rsta_support) + capab->ntb_support = true; + + capab->secure_he_ltf = pr->cfg->secure_he_ltf; + capab->pasn_type = pr->cfg->pasn_type; + capab->support_6ghz = pr->cfg->support_6ghz; +} + + +static void pr_get_edca_capabilities(struct pr_data *pr, + struct edca_capabilities *capab) +{ + u16 edca_hw_caps = 0; + + os_memset(capab, 0, sizeof(struct edca_capabilities)); + capab->ista_support = pr->cfg->edca_ista_support; + capab->rsta_support = pr->cfg->edca_rsta_support; + os_memcpy(capab->country, pr->cfg->country, 3); + + edca_hw_caps |= (pr->cfg->edca_format_and_bw & EDCA_FORMAT_AND_BW_MASK) + << EDCA_FORMAT_AND_BW; + edca_hw_caps |= (pr->cfg->max_tx_antenna & EDCA_MAX_TX_ANTENNA_MASK) << + EDCA_MAX_TX_ANTENNA; + edca_hw_caps |= (pr->cfg->max_rx_antenna & EDCA_MAX_RX_ANTENNA_MASK) << + EDCA_MAX_RX_ANTENNA; + + capab->edca_hw_caps = edca_hw_caps; + os_memcpy(&capab->channels, &pr->cfg->edca_channels, + sizeof(struct pr_channels)); +} + + +static void pr_get_ntb_capabilities(struct pr_data *pr, + struct ntb_capabilities *capab) +{ + u32 ntb_hw_caps = 0; + + os_memset(capab, 0, sizeof(struct ntb_capabilities)); + capab->ista_support = pr->cfg->ntb_ista_support; + capab->rsta_support = pr->cfg->ntb_rsta_support; + os_memcpy(capab->country, pr->cfg->country, 3); + capab->secure_he_ltf = pr->cfg->secure_he_ltf; + + ntb_hw_caps |= (pr->cfg->ntb_format_and_bw & NTB_FORMAT_AND_BW_MASK) << + NTB_FORMAT_AND_BW; + ntb_hw_caps |= (pr->cfg->max_tx_ltf_repetations & + MAX_TX_LTF_REPETATIONS_MASK) << MAX_TX_LTF_REPETATIONS; + ntb_hw_caps |= (pr->cfg->max_rx_ltf_repetations & + MAX_RX_LTF_REPETATIONS_MASK) << MAX_RX_LTF_REPETATIONS; + + ntb_hw_caps |= (pr->cfg->max_rx_ltf_total & MAX_RX_LTF_TOTAL_MASK) << + MAX_RX_LTF_TOTAL; + ntb_hw_caps |= (pr->cfg->max_tx_ltf_total & MAX_TX_LTF_TOTAL_MASK) << + MAX_TX_LTF_TOTAL; + + ntb_hw_caps |= (pr->cfg->max_rx_sts_le_80 & MAX_RX_STS_LE_80_MASK) << + MAX_RX_STS_LE_80; + ntb_hw_caps |= (pr->cfg->max_rx_sts_gt_80 & MAX_RX_STS_GT_80_MASK) << + MAX_RX_STS_GT_80; + + ntb_hw_caps |= (pr->cfg->max_tx_sts_le_80 & MAX_TX_STS_LE_80_MASK) << + MAX_TX_STS_LE_80; + ntb_hw_caps |= (pr->cfg->max_tx_sts_gt_80 & MAX_TX_STS_GT_80_MASK) << + MAX_TX_STS_GT_80; + + capab->ntb_hw_caps = ntb_hw_caps; + os_memcpy(&capab->channels, &pr->cfg->ntb_channels, + sizeof(struct pr_channels)); +} + + +static int pr_derive_dira(struct pr_data *pr, const u8 *src_addr, + struct pr_dira *dira) +{ + u8 nonce[DEVICE_IDENTITY_NONCE_LEN]; + u8 tag[DEVICE_MAX_HASH_LEN]; + u8 data[DIR_STR_LEN + ETH_ALEN + DEVICE_IDENTITY_NONCE_LEN]; + + if (pr->cfg->dik_cipher != DIRA_CIPHER_VERSION_128) { + wpa_printf(MSG_INFO, "PR: Unsupported DIRA Cipher version %d", + pr->cfg->dik_cipher); + return -1; + } + + if (pr->cfg->dik_len != DEVICE_IDENTITY_KEY_LEN) { + wpa_printf(MSG_INFO, "PR: Invalid DIK length %zu", + pr->cfg->dik_len); + return -1; + } + + os_memset(data, 0, sizeof(data)); + + if (os_get_random(nonce, DEVICE_IDENTITY_NONCE_LEN) < 0) { + wpa_printf(MSG_INFO, "PR: Failed to generate DIRA nonce"); + return -1; + } + + /* Tag = Truncate-64(HMAC-SHA-256(DevIK, "DIR" || PR Device Address || + * Nonce)) + */ + os_memcpy(data, "DIR", DIR_STR_LEN); + os_memcpy(&data[DIR_STR_LEN], src_addr, ETH_ALEN); + os_memcpy(&data[DIR_STR_LEN + ETH_ALEN], nonce, + DEVICE_IDENTITY_NONCE_LEN); + + if (hmac_sha256(pr->cfg->dik_data, pr->cfg->dik_len, data, sizeof(data), + tag) < 0) { + wpa_printf(MSG_ERROR, "PR: Could not derive DIRA tag"); + return -1; + } + + os_memset(dira, 0, sizeof(struct pr_dira)); + dira->cipher_version = pr->cfg->dik_cipher; + dira->nonce_len = DEVICE_IDENTITY_NONCE_LEN; + os_memcpy(dira->nonce, nonce, DEVICE_IDENTITY_NONCE_LEN); + dira->tag_len = DEVICE_IDENTITY_TAG_LEN; + os_memcpy(dira->tag, tag, DEVICE_IDENTITY_TAG_LEN); + + wpa_hexdump_key(MSG_DEBUG, "PR: DIK", pr->cfg->dik_data, + pr->cfg->dik_len); + wpa_hexdump(MSG_DEBUG, "PR: DIRA-NONCE", dira->nonce, dira->nonce_len); + wpa_hexdump(MSG_DEBUG, "PR: DIRA-TAG", dira->tag, dira->tag_len); + + return 0; +} + + +static int pr_validate_dira(struct pr_data *pr, struct pr_device *dev, + const u8 *dira, u16 dira_len) +{ + int ret; + size_t len[3]; + const u8 *addr[3]; + struct pr_dev_ik *dev_ik; + u8 tag[DEVICE_MAX_HASH_LEN]; + const char *label = "DIR"; + const u8 *dira_nonce, *dira_tag; + + /* Reset DevIK state - set only if DIRA verification succeeds */ + os_memset(dev->dik, 0, DEVICE_IDENTITY_KEY_LEN); + dev->dik_valid = false; + + if (dira_len < 1 + DEVICE_IDENTITY_NONCE_LEN + DEVICE_IDENTITY_TAG_LEN) + { + wpa_printf(MSG_DEBUG, "PR: Truncated DIRA (length %u)", + dira_len); + return -1; + } + + /* Cipher Version */ + if (dira[0] != DIRA_CIPHER_VERSION_128) { + wpa_printf(MSG_DEBUG, "PR: Unsupported DIRA cipher version %d", + dira[0]); + return -1; + } + + /* Nonce */ + dira_nonce = &dira[1]; + + /* Tag */ + dira_tag = &dira[1 + DEVICE_IDENTITY_NONCE_LEN]; + + /* Tag = Truncate-64(HMAC-SHA-256(DevIK, "DIR" || Device Address || + * Nonce)) */ + addr[0] = (const u8 *) label; + len[0] = DIR_STR_LEN; + addr[1] = dev->pr_device_addr; + len[1] = ETH_ALEN; + addr[2] = dira_nonce; + len[2] = DEVICE_IDENTITY_NONCE_LEN; + + dl_list_for_each(dev_ik, &pr->dev_iks, struct pr_dev_ik, list) { + ret = hmac_sha256_vector(dev_ik->dik, DEVICE_IDENTITY_KEY_LEN, + 3, addr, len, tag); + if (ret < 0) { + wpa_printf(MSG_INFO, + "PR: Failed to derive DIRA Tag"); + return -1; + } + + if (os_memcmp(tag, dira_tag, DEVICE_IDENTITY_TAG_LEN) == 0) { + wpa_printf(MSG_DEBUG, "PR: DIRA Tag matched"); + if (dev_ik->password_valid) { + os_strlcpy(dev->password, dev_ik->password, + sizeof(dev->password)); + dev->password_valid = true; + } + if (dev_ik->pmk_valid) { + os_memcpy(dev->pmk, dev_ik->pmk, + dev_ik->pmk_len); + dev->pmk_len = dev_ik->pmk_len; + dev->pmk_valid = true; + } + os_memcpy(dev->dik, dev_ik->dik, + DEVICE_IDENTITY_KEY_LEN); + dev->dik_valid = true; + return 0; + } + } + + return -1; +} + + +#ifdef CONFIG_PASN + +static void pr_copy_channels(struct pr_channels *dst, + const struct pr_channels *src, bool allow_6ghz) +{ + size_t i, j; + + if (allow_6ghz) { + os_memcpy(dst, src, sizeof(struct pr_channels)); + return; + } + + for (i = 0, j = 0; i < src->op_classes; i++) { + if (is_6ghz_op_class(src->op_class[i].op_class)) + continue; + os_memcpy(&dst->op_class[j], &src->op_class[i], + sizeof(struct pr_op_class)); + j++; + } + dst->op_classes = j; +} + + +static void pr_op_class_intersect(const struct pr_op_class *a, + const struct pr_op_class *b, + struct pr_op_class *res) +{ + size_t i, j; + + res->op_class = a->op_class; + for (i = 0; i < a->channels; i++) { + for (j = 0; j < b->channels; j++) { + if (a->channel[i] != b->channel[j]) + continue; + res->channel[res->channels] = a->channel[i]; + res->channels++; + if (res->channels == PR_MAX_OP_CLASS_CHANNELS) + return; + } + } +} + + +/** + * pr_channels_intersect - Intersection of supported channel lists + * @a: First set of supported channels + * @b: Second set of supported channels + * @res: Data structure for returning the intersection of supported channels + * + * This function can be used to find a common set of supported channels. Both + * input channel sets are assumed to use the global operating classes or the + * same country code. If different country codes are used without using the + * global operating classes, the operating class numbers may not be matched + * correctly and results are undefined. + */ +static void pr_channels_intersect(const struct pr_channels *a, + const struct pr_channels *b, + struct pr_channels *res) +{ + size_t i, j; + const struct pr_op_class *a_op; + const struct pr_op_class *b_op; + + os_memset(res, 0, sizeof(*res)); + + for (i = 0; i < a->op_classes; i++) { + a_op = &a->op_class[i]; + for (j = 0; j < b->op_classes; j++) { + b_op = &b->op_class[j]; + if (a_op->op_class != b_op->op_class) + continue; + pr_op_class_intersect(a_op, b_op, + &res->op_class[res->op_classes]); + if (res->op_class[res->op_classes].channels) { + res->op_classes++; + if (res->op_classes == PR_MAX_OP_CLASSES) + return; + } + } + } +} + +#endif /* CONFIG_PASN */ + + +static void pr_buf_add_channel_list(struct wpabuf *buf, const char *country, + const struct pr_channels *chan) +{ + size_t i; + + wpabuf_put_data(buf, country, 3); /* Country String */ + wpabuf_put_u8(buf, chan->op_classes); /* Number of Channel Entries */ + + /* Channel Entry List */ + for (i = 0; i < chan->op_classes; i++) { + const struct pr_op_class *c = &chan->op_class[i]; + + wpabuf_put_u8(buf, c->op_class); + wpabuf_put_u8(buf, c->channels); + wpabuf_put_data(buf, c->channel, c->channels); + } +} + + +static void pr_buf_add_ranging_capa_info(struct wpabuf *buf, + const struct pr_capabilities *capab) +{ + u8 *len; + u8 capa_6g = 0; + u8 protocol_type = 0; + size_t _len; + + /* Proximity Ranging Capability Attribute */ + wpabuf_put_u8(buf, PR_ATTR_RANGING_CAPABILITY); + len = wpabuf_put(buf, 2); /* Attribute length to be filled */ + + /* Ranging Protocol Type */ + if (capab->edca_support) + protocol_type |= PR_EDCA_BASED_RANGING; + if (capab->ntb_support && capab->secure_he_ltf) + protocol_type |= PR_NTB_SECURE_LTF_BASED_RANGING; + if (capab->ntb_support) + protocol_type |= PR_NTB_OPEN_BASED_RANGING; + wpabuf_put_u8(buf, protocol_type); + + /* PASN Type */ + wpabuf_put_u8(buf, capab->pasn_type); + + /* 6GHz band */ + if (capab->support_6ghz) + capa_6g |= BIT(0); + + wpabuf_put_u8(buf, capa_6g); + + /* Device Name */ + wpabuf_put_data(buf, capab->device_name, WPS_DEV_NAME_MAX_LEN); + wpa_printf(MSG_DEBUG, "PR: Device name: %s", capab->device_name); + + _len = (u8 *) wpabuf_put(buf, 0) - len - 2; + WPA_PUT_LE16(len, _len); + wpa_hexdump(MSG_DEBUG, "PR: * Capability Attribute", len + 2, _len); +} + + +static void pr_buf_add_edca_capa_info(struct wpabuf *buf, + const struct edca_capabilities *edca_data) +{ + u8 *len; + u8 ranging_role = 0; + size_t _len; + + /* Proximity Ranging EDCA Capability Attribute */ + wpabuf_put_u8(buf, PR_ATTR_EDCA_CAPABILITY); + len = wpabuf_put(buf, 2); /* Attribute length to be filled */ + + /* Ranging Role */ + if (edca_data->ista_support) + ranging_role |= PR_ISTA_SUPPORT; + if (edca_data->rsta_support) + ranging_role |= PR_RSTA_SUPPORT; + wpabuf_put_u8(buf, ranging_role); + + /* Ranging Parameters */ + wpabuf_put_le16(buf, edca_data->edca_hw_caps); + + pr_buf_add_channel_list(buf, edca_data->country, &edca_data->channels); + + _len = (u8 *) wpabuf_put(buf, 0) - len - 2; + WPA_PUT_LE16(len, _len); + wpa_hexdump(MSG_DEBUG, "PR: * EDCA Capability Attribute", + len + 2, _len); +} + + +static void pr_buf_add_ntb_capa_info(struct wpabuf *buf, + const struct ntb_capabilities *ntb_data) +{ + u8 *len; + u8 ranging_role = 0; + size_t _len; + + /* Proximity Ranging 11az NTB Capability Attribute */ + wpabuf_put_u8(buf, PR_ATTR_NTB_CAPABILITY); + len = wpabuf_put(buf, 2); + + /* Ranging Role */ + if (ntb_data->ista_support) + ranging_role |= PR_ISTA_SUPPORT; + if (ntb_data->rsta_support) + ranging_role |= PR_RSTA_SUPPORT; + wpabuf_put_u8(buf, ranging_role); + + /* Ranging Parameter */ + wpabuf_put_le32(buf, ntb_data->ntb_hw_caps); + + pr_buf_add_channel_list(buf, ntb_data->country, &ntb_data->channels); + + _len = (u8 *) wpabuf_put(buf, 0) - len - 2; + WPA_PUT_LE16(len, _len); + wpa_hexdump(MSG_DEBUG, "PR: * NTB Capability Attribute", len + 2, _len); +} + + +static void pr_buf_add_dira(struct wpabuf *buf, const struct pr_dira *dira) +{ + u8 *len; + size_t _len; + + /* Proximity Ranging Device Identity Resolution attribute */ + wpabuf_put_u8(buf, PR_ATTR_DEVICE_IDENTITY_RESOLUTION); + + /* Length to be filled */ + len = wpabuf_put(buf, 2); + + wpabuf_put_u8(buf, dira->cipher_version); + wpabuf_put_data(buf, dira->nonce, dira->nonce_len); + wpabuf_put_data(buf, dira->tag, dira->tag_len); + + /* Update attribute length */ + _len = (u8 *) wpabuf_put(buf, 0) - len - 2; + WPA_PUT_LE16(len, _len); + + wpa_printf(MSG_DEBUG, "PR: * DIRA"); +} + + +struct wpabuf * pr_prepare_usd_elems(struct pr_data *pr, const u8 *src_addr) +{ + u32 ie_type; + struct wpabuf *buf, *buf2; + struct pr_capabilities pr_caps; + struct pr_dira dira; + + buf = wpabuf_alloc(1000); + if (!buf) + return NULL; + + pr_get_ranging_capabilities(pr, &pr_caps); + pr_buf_add_ranging_capa_info(buf, &pr_caps); + + if (pr->cfg->edca_ista_support || pr->cfg->edca_rsta_support) { + struct edca_capabilities edca_caps; + + pr_get_edca_capabilities(pr, &edca_caps); + pr_buf_add_edca_capa_info(buf, &edca_caps); + } + + if (pr->cfg->ntb_ista_support || pr->cfg->ntb_rsta_support) { + struct ntb_capabilities ntb_caps; + + pr_get_ntb_capabilities(pr, &ntb_caps); + pr_buf_add_ntb_capa_info(buf, &ntb_caps); + } + + if (!pr_derive_dira(pr, src_addr, &dira)) + pr_buf_add_dira(buf, &dira); + + ie_type = (OUI_WFA << 8) | PR_OUI_TYPE; + buf2 = pr_encaps_elem(buf, ie_type); + wpabuf_free(buf); + + return buf2; +} + + +static int pr_parse_attribute(u8 id, const u8 *data, u16 len, + struct pr_message *msg) +{ + switch (id) { + case PR_ATTR_STATUS: + if (len < 1) { + wpa_printf(MSG_INFO, + "PR: Invalid Proximity Ranging Status Attribute (length %d)", + len); + return -1; + } + msg->status_ie = data; + msg->status_ie_len = len; + wpa_printf(MSG_DEBUG, "PR: Status Code %u", data[0]); + break; + case PR_ATTR_RANGING_CAPABILITY: + if (len < 35) { + wpa_printf(MSG_INFO, + "PR: Too short Proximity Ranging Capability Attribute (length %d)", + len); + return -1; + } + msg->pr_capability = data; + msg->pr_capability_len = len; + wpa_printf(MSG_DEBUG, + "PR: Ranging Protocol Type: %02x PASN Type: %02x", + data[0], data[1]); + break; + case PR_ATTR_EDCA_CAPABILITY: + if (len < 10) { + wpa_printf(MSG_INFO, + "PR: Too short Proximity Ranging EDCA Capability Attribute (length %d)", + len); + return -1; + } + msg->edca_capability = data; + msg->edca_capability_len = len; + wpa_printf(MSG_DEBUG, + "PR: EDCA Ranging Role %02x, Ranging Parameters %04x", + data[0], WPA_GET_LE16(data + 1)); + break; + case PR_ATTR_NTB_CAPABILITY: + if (len < 12) { + wpa_printf(MSG_INFO, + "PR: Too short Proximity Ranging 11az NTB Capability Attribute (length %d)", + len); + return -1; + } + msg->ntb_capability = data; + msg->ntb_capability_len = len; + wpa_printf(MSG_DEBUG, + "PR: NTB Ranging Role %02x, Ranging Parameter %04x", + data[0], WPA_GET_LE32(data + 1)); + break; + case PR_ATTR_OPERATION_MODE: + if (len < 9) { + wpa_printf(MSG_INFO, + "PR: Invalid Proximity Ranging Operation Mode Attribute (length %d)", + len); + return -1; + } + msg->op_mode = data; + msg->op_mode_len = len; + break; + case PR_ATTR_DEVICE_IDENTITY_RESOLUTION: + if (len < 1 + DEVICE_IDENTITY_NONCE_LEN + + DEVICE_IDENTITY_TAG_LEN) { + wpa_printf(MSG_INFO, "PR: Too short DIRA (length %d)", + len); + return -1; + } + msg->dira = data; + msg->dira_len = len; + wpa_printf(MSG_DEBUG, "PR: DIRA cipher version %u", data[0]); + break; + default: + wpa_printf(MSG_DEBUG, + "PR: Skipped unknown attribute %d (length %d)", + id, len); + break; + } + + return 0; +} + + +/** + * pr_parse_proximity_ranging_element - Parse Proximity Ranging element + * @buf: Concatenated PR element(s) payload + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: The caller is responsible for clearing the msg data structure before + * calling this function. + */ +static int pr_parse_proximity_ranging_element(const struct wpabuf *buf, + struct pr_message *msg) +{ + const u8 *pos = wpabuf_head_u8(buf); + const u8 *end = pos + wpabuf_len(buf); + + wpa_printf(MSG_DEBUG, "PR: Parsing Proximity Ranging element"); + + while (pos < end) { + u16 attr_len; + u8 id; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "PR: Invalid PR attribute"); + return -1; + } + id = *pos++; + attr_len = WPA_GET_LE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "PR: Attribute %d length %u", + id, attr_len); + if (attr_len > end - pos) { + wpa_printf(MSG_DEBUG, + "PR: Attribute underflow (len=%u left=%d)", + attr_len, (int) (end - pos)); + wpa_hexdump(MSG_MSGDUMP, "PR: Data", pos, end - pos); + return -1; + } + if (pr_parse_attribute(id, pos, attr_len, msg)) + return -1; + pos += attr_len; + } + + return 0; +} + + +static void pr_parse_free(struct pr_message *msg) +{ + wpabuf_free(msg->pr_attributes); + msg->pr_attributes = NULL; +} + + +/** + * pr_parse_elements - Parse Proximity Ranging element(s) + * @data: Elements from the message + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: The caller is responsible for clearing the msg data structure before + * calling this function. + * + * Note: The caller must free temporary memory allocations by calling + * pr_parse_free() when the parsed data is not needed anymore. + */ +static int pr_parse_elements(const u8 *data, size_t len, struct pr_message *msg) +{ + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(data, len, &elems, true) == ParseFailed) + return -1; + + msg->pr_attributes = ieee802_11_vendor_ie_concat(data, len, + PR_IE_VENDOR_TYPE); + if (msg->pr_attributes && + pr_parse_proximity_ranging_element(msg->pr_attributes, msg)) { + wpa_printf(MSG_INFO, + "PR: Failed to parse Proximity Ranging element data"); + if (msg->pr_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, + "PR: Proximity Ranging element payload", + msg->pr_attributes); + pr_parse_free(msg); + return -1; + } + return 0; +} + + +static int pr_process_channels(const u8 *channel_list, size_t channel_list_len, + struct pr_channels *ch) +{ + u8 channels; + const u8 *pos, *end; + u8 op_class_count; + + if (channel_list_len < 1) + return -1; + + pos = channel_list; + end = channel_list + channel_list_len; + + /* Number of Channel Entries */ + /* Get total count of the operational classes */ + op_class_count = pos[0]; + wpa_printf(MSG_DEBUG, "PR: Total operational classes: %u", + op_class_count); + pos++; + + /* Channel Entry List */ + ch->op_classes = 0; + while (end - pos > 2 && (ch->op_classes <= op_class_count)) { + struct pr_op_class *cl = &ch->op_class[ch->op_classes]; + + cl->op_class = *pos++; /* Operating Class */ + channels = *pos++; /* Number of Channels */ + + /* Channel List */ + if (channels > end - pos) { + wpa_printf(MSG_INFO, + "PR: Invalid channel list channel %d, size: %ld", + channels, end - pos); + return -1; + } + cl->channels = channels > PR_MAX_OP_CLASS_CHANNELS ? + PR_MAX_OP_CLASS_CHANNELS : channels; + os_memcpy(cl->channel, pos, cl->channels); + pos += channels; + ch->op_classes++; + if (ch->op_classes == PR_MAX_OP_CLASSES) + break; + } + + if (ch->op_classes != op_class_count && + ch->op_classes < PR_MAX_OP_CLASSES) { + wpa_printf(MSG_INFO, + "PR: Channel list count mismatch %lu != %d", + ch->op_classes, op_class_count); + return -1; + } + + return 0; +} + + +static void pr_process_ranging_capabilities(const u8 *caps, size_t caps_len, + struct pr_capabilities *pr_caps) +{ + const u8 *pos; + + if (!caps || caps_len < 3 + WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_INFO, + "PR: Invalid Proximity Ranging Capability Attribute"); + return; + } + + pos = caps; + + /* Ranging Protocol Type */ + if (*pos & PR_EDCA_BASED_RANGING) + pr_caps->edca_support = true; + if (*pos & PR_NTB_SECURE_LTF_BASED_RANGING) { + pr_caps->secure_he_ltf = true; + pr_caps->ntb_support = true; + } + if (*pos & PR_NTB_OPEN_BASED_RANGING) + pr_caps->ntb_support = true; + + pos++; + /* PASN Type */ + pr_caps->pasn_type = *pos; + + pos++; + /* 6GHz band */ + pr_caps->support_6ghz = *pos & BIT(0); + + pos++; + /* Device Name */ + os_memset(pr_caps->device_name, 0, WPS_DEV_NAME_MAX_LEN + 1); + os_memcpy(pr_caps->device_name, pos, WPS_DEV_NAME_MAX_LEN); + + wpa_printf(MSG_DEBUG, + "PR: Device name=%s, edca capability=%x, ntb capability=%x, secure LTF capability=%u, 6GHz=%u", + pr_caps->device_name, pr_caps->edca_support, + pr_caps->ntb_support, pr_caps->secure_he_ltf, + pr_caps->support_6ghz); +} + + +static void pr_process_edca_capabilities(const u8 *caps, size_t caps_len, + struct edca_capabilities *edca_caps) +{ + const u8 *pos, *end; + + if (caps_len < 7) + return; + + pos = caps; + end = caps + caps_len; + + /* Ranging Role */ + if (*pos & PR_ISTA_SUPPORT) + edca_caps->ista_support = true; + if (*pos & PR_RSTA_SUPPORT) + edca_caps->rsta_support = true; + pos++; + + /* Ranging Parameters */ + edca_caps->edca_hw_caps = WPA_GET_LE16(pos); + pos += 2; + + /* Country String */ + os_memcpy(edca_caps->country, pos, 3); + pos += 3; + + pr_process_channels(pos, end - pos, &edca_caps->channels); + + wpa_printf(MSG_DEBUG, + "PR: EDCA ISTA support=%u, EDCA RSTA support=%u, op classes count=%lu, country=%c%c", + edca_caps->ista_support, edca_caps->rsta_support, + edca_caps->channels.op_classes, + valid_country_ch(edca_caps->country[0]) ? + edca_caps->country[0] : '_', + valid_country_ch(edca_caps->country[1]) ? + edca_caps->country[1] : '_'); +} + + +static void pr_process_ntb_capabilities(const u8 *caps, size_t caps_len, + struct ntb_capabilities *ntb_caps, + bool secure_ltf) +{ + const u8 *pos, *end; + + if (caps_len < 9) + return; + + pos = caps; + end = caps + caps_len; + + /* Ranging Role */ + if (*pos & PR_ISTA_SUPPORT) + ntb_caps->ista_support = true; + if (*pos & PR_RSTA_SUPPORT) + ntb_caps->rsta_support = true; + if (secure_ltf) + ntb_caps->secure_he_ltf = true; + pos++; + + /* Ranging Parameter */ + ntb_caps->ntb_hw_caps = WPA_GET_LE32(pos); + pos += 4; + + /* Country String */ + os_memcpy(ntb_caps->country, pos, 3); + pos += 3; + + pr_process_channels(pos, end - pos, &ntb_caps->channels); + + wpa_printf(MSG_DEBUG, + "PR: NTB ISTA support=%u, NTB RSTA support=%u, op classes count=%lu, secure HE-LTF=%u, country=%c%c", + ntb_caps->ista_support, ntb_caps->rsta_support, + ntb_caps->channels.op_classes, + ntb_caps->secure_he_ltf, + valid_country_ch(ntb_caps->country[0]) ? + ntb_caps->country[0] : '_', + valid_country_ch(ntb_caps->country[1]) ? + ntb_caps->country[1] : '_'); +} + + +void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len, + const u8 *peer_addr, unsigned int freq) +{ + struct pr_device *dev; + struct pr_message msg; + + os_memset(&msg, 0, sizeof(msg)); + os_memcpy(msg.pr_device_addr, peer_addr, ETH_ALEN); + + if (pr_parse_elements(ies, ies_len, &msg)) { + wpa_printf(MSG_INFO, + "PR: Failed to parse Proximity Ranging element(s)"); + pr_parse_free(&msg); + return; + } + + if (!msg.pr_capability) { + wpa_printf(MSG_DEBUG, + "PR: Ranging caps not present, ignoring proximity device " + MACSTR, MAC2STR(peer_addr)); + pr_parse_free(&msg); + return; + } + + if (!msg.edca_capability && !msg.ntb_capability) { + wpa_printf(MSG_DEBUG, + "PR: Neither EDCA nor NTB capabilities are present, ignoring proximity device " + MACSTR, MAC2STR(peer_addr)); + pr_parse_free(&msg); + return; + } + + dev = pr_create_device(pr, peer_addr); + if (!dev) { + pr_parse_free(&msg); + wpa_printf(MSG_INFO, "PR: Failed to create a device"); + return; + } + + os_get_reltime(&dev->last_seen); + dev->listen_freq = freq; + dev->discovery_type = PR_DISCOVERY_TYPE_USD; + + pr_process_ranging_capabilities(msg.pr_capability, + msg.pr_capability_len, &dev->pr_caps); + + if (dev->pr_caps.edca_support && msg.edca_capability) + pr_process_edca_capabilities(msg.edca_capability, + msg.edca_capability_len, + &dev->edca_caps); + + if (dev->pr_caps.ntb_support && msg.ntb_capability) + pr_process_ntb_capabilities(msg.ntb_capability, + msg.ntb_capability_len, + &dev->ntb_caps, + dev->pr_caps.secure_he_ltf); + + if (msg.dira && msg.dira_len) + pr_validate_dira(pr, dev, msg.dira, msg.dira_len); + + if (pr->cfg->device_found) + pr->cfg->device_found(pr->cfg->cb_ctx, dev); + + pr_parse_free(&msg); +} + + +/** + * pr_ensure_oob_peer - Add a minimal OOB peer entry if not already present + */ +int pr_ensure_oob_peer(struct pr_data *pr, const u8 *addr, int freq) +{ + struct pr_device *dev; + + if (!pr || !addr) + return -1; + + dev = pr_get_device(pr, addr); + if (dev) + return 0; + + dev = pr_create_device(pr, addr); + if (!dev) { + wpa_printf(MSG_INFO, "PR: Failed to create OOB peer " MACSTR, + MAC2STR(addr)); + return -1; + } + + dev->discovery_type = PR_DISCOVERY_TYPE_OOB; + if (freq) + dev->listen_freq = freq; + + wpa_printf(MSG_DEBUG, "PR: OOB peer " MACSTR " created at PASN_START", + MAC2STR(addr)); + return 0; +} + + +#ifdef CONFIG_PASN + +static bool pr_eq_ranging_capa_params(const struct pr_device *dev, + const struct pr_capabilities *caps) +{ + if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB) + return true; + + return dev->pr_caps.edca_support == caps->edca_support && + dev->pr_caps.ntb_support == caps->ntb_support && + dev->pr_caps.pasn_type == caps->pasn_type && + dev->pr_caps.secure_he_ltf == caps->secure_he_ltf && + dev->pr_caps.support_6ghz == caps->support_6ghz && + os_strcmp(dev->pr_caps.device_name, caps->device_name) == 0; +} + + +static bool pr_eq_edca_params(const struct pr_device *dev, + const struct edca_capabilities *edca_caps) +{ + if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB) + return true; + + return dev->edca_caps.ista_support == edca_caps->ista_support && + dev->edca_caps.rsta_support == edca_caps->rsta_support && + dev->edca_caps.edca_hw_caps == edca_caps->edca_hw_caps && + os_memcmp(dev->edca_caps.country, edca_caps->country, 3) == 0; +} + + +static bool pr_eq_ntb_params(const struct pr_device *dev, + const struct ntb_capabilities *ntb_caps) +{ + if (dev->discovery_type == PR_DISCOVERY_TYPE_OOB) + return true; + + return dev->ntb_caps.ista_support == ntb_caps->ista_support && + dev->ntb_caps.rsta_support == ntb_caps->rsta_support && + dev->ntb_caps.ntb_hw_caps == ntb_caps->ntb_hw_caps && + os_memcmp(dev->ntb_caps.country, ntb_caps->country, 3) == 0; +} + + +static void pr_buf_add_operation_mode(struct wpabuf *buf, + struct operation_mode *mode) +{ + u8 *len; + size_t _len; + + /* Proximity Ranging Operation Mode Attribute */ + wpabuf_put_u8(buf, PR_ATTR_OPERATION_MODE); + /* Length to be filled */ + len = wpabuf_put(buf, 2); + + /* Ranging Protocol Type */ + wpabuf_put_u8(buf, mode->protocol_type); + + /* Ranging Role */ + wpabuf_put_u8(buf, mode->role); + + pr_buf_add_channel_list(buf, mode->country, &mode->channels); + + /* Update attribute length */ + _len = (u8 *) wpabuf_put(buf, 0) - len - 2; + WPA_PUT_LE16(len, _len); + wpa_hexdump(MSG_DEBUG, "PR: * Operation Mode", len + 2, _len); +} + + +static void pr_buf_add_ranging_neg_status(struct wpabuf *buf, u8 status) +{ + /* Proximity Ranging Status Attribute */ + wpabuf_put_u8(buf, PR_ATTR_STATUS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, status); + wpa_printf(MSG_DEBUG, "PR: * Role negotiation status %u", status); +} + + +static void pr_process_op_mode(const u8 *caps, size_t caps_len, + struct operation_mode *op_mode) +{ + const u8 *pos, *end; + + if (!caps || caps_len < 6) + return; + + pos = caps; + end = caps + caps_len; + + /* Ranging Protocol Type */ + op_mode->protocol_type = *pos; + pos++; + + /* Ranging Role */ + op_mode->role = *pos; + pos++; + + /* Country String */ + os_memcpy(op_mode->country, pos, 3); + pos += 3; + + pr_process_channels(pos, end - pos, &op_mode->channels); + + wpa_printf(MSG_DEBUG, + "PR: protocol type=%u, role type=%u, country=%c%c, channel size=%lu", + op_mode->protocol_type, op_mode->role, + valid_country_ch(op_mode->country[0]) ? + op_mode->country[0] : '_', + valid_country_ch(op_mode->country[1]) ? + op_mode->country[1] : '_', + op_mode->channels.op_classes); +} + + +static void pr_choose_best_channel(struct pr_channels *common_channel, + u8 *op_class, u8 *op_channel) +{ + int bw; + int max_bw = 0; + const struct oper_class_map *map; + size_t i; + + if (!common_channel || !common_channel->op_classes || + !common_channel->op_class[0].channels) { + wpa_printf(MSG_INFO, + "PR: Empty channel list, cannot get best channel"); + *op_class = 0; + *op_channel = 0; + return; + } + + for (i = 0; i < common_channel->op_classes; i++) { + map = get_oper_class(NULL, + common_channel->op_class[i].op_class); + if (!map) + continue; + bw = oper_class_bw_to_int(map); + if (bw > max_bw) { + *op_class = common_channel->op_class[i].op_class; + *op_channel = common_channel->op_class[i].channel[0]; + max_bw = bw; + } + } + + wpa_printf(MSG_DEBUG, + "PR: Choose the operating class: %u, operating channel: %u", + *op_class, *op_channel); +} + + +static u8 pr_pasn_get_best_op_mode(struct pr_data *pr, u8 peer_supp_roles, + struct operation_mode *op_mode, + struct operation_mode *res_op_mode) +{ + u8 ranging_type = 0; + struct pr_channels common_chan; + struct pr_channels *own_channels = NULL; + int status = PR_NEGOTIATION_FAIL; + u8 op_class = 0, op_channel = 0; + bool own_ista_support = false, own_rsta_support = false; + + if (op_mode->protocol_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) { + if (op_mode->protocol_type == PR_NTB_SECURE_LTF_BASED_RANGING && + !pr->cfg->secure_he_ltf) { + wpa_printf(MSG_INFO, + "PR PASN: Secure HE-LTF not supported"); + return PR_NEGOTIATION_FAIL; + } + + if ((op_mode->protocol_type & + PR_NTB_SECURE_LTF_BASED_RANGING) && pr->cfg->secure_he_ltf) + ranging_type = PR_NTB_SECURE_LTF_BASED_RANGING; + else + ranging_type = PR_NTB_OPEN_BASED_RANGING; + + own_ista_support = pr->cfg->ntb_ista_support; + own_rsta_support = pr->cfg->ntb_rsta_support; + own_channels = &pr->cfg->ntb_channels; + wpa_printf(MSG_DEBUG, "PR PASN: Choose NTB Ranging Protocol"); + } else if (op_mode->protocol_type & PR_EDCA_BASED_RANGING) { + ranging_type = PR_EDCA_BASED_RANGING; + + own_ista_support = pr->cfg->edca_ista_support; + own_rsta_support = pr->cfg->edca_rsta_support; + own_channels = &pr->cfg->edca_channels; + wpa_printf(MSG_DEBUG, "PR PASN: Choose EDCA Ranging Protocol"); + } else { + wpa_printf(MSG_DEBUG, + "PR PASN: Invalid Ranging Protocol, proposed type 0x%x", + op_mode->protocol_type); + return PR_NEGOTIATION_FAIL; + } + + if (!own_ista_support && !own_rsta_support) { + wpa_printf(MSG_DEBUG, + "PR PASN: Device can't fulfill any requested Ranging Role"); + return PR_NEGOTIATION_FAIL; + } + + if (own_channels == NULL || !own_channels->op_classes) { + wpa_printf(MSG_DEBUG, + "PR PASN: Invalid or empty channel list to negotiate"); + return PR_NEGOTIATION_FAIL; + } + + pr_channels_intersect(own_channels, &op_mode->channels, &common_chan); + if (!common_chan.op_classes) { + wpa_printf(MSG_DEBUG, + "PR PASN: No common channels to perform ranging"); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support && + !(peer_supp_roles & PR_RSTA_SUPPORT)) { + wpa_printf(MSG_DEBUG, + "PR PASN: Device cannot act as RSTA and cannot update role"); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_RSTA_SUPPORT && !own_ista_support && + !(peer_supp_roles & PR_ISTA_SUPPORT)) { + wpa_printf(MSG_DEBUG, + "PR PASN: Device cannot act as ISTA and cannot update role"); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_ISTA_SUPPORT && own_rsta_support) { + res_op_mode->role = PR_RSTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + } else if (op_mode->role == PR_RSTA_SUPPORT && own_ista_support) { + res_op_mode->role = PR_ISTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + } else if ((op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support) && + ((peer_supp_roles & PR_RSTA_SUPPORT) && own_ista_support)) { + res_op_mode->role = PR_ISTA_SUPPORT; + status = PR_NEGOTIATION_UPDATE; + } else if ((op_mode->role == PR_RSTA_SUPPORT && !own_ista_support) && + ((peer_supp_roles & PR_ISTA_SUPPORT) && own_rsta_support)) { + res_op_mode->role = PR_RSTA_SUPPORT; + status = PR_NEGOTIATION_UPDATE; + } else if (op_mode->role == (PR_RSTA_SUPPORT | PR_ISTA_SUPPORT)) { + if ((pr->cfg->preferred_ranging_role || !own_ista_support) && + own_rsta_support) { + res_op_mode->role = PR_RSTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + wpa_printf(MSG_DEBUG, "PR PASN: Choose RSTA role"); + } else if ((!pr->cfg->preferred_ranging_role || + !own_rsta_support) && own_ista_support) { + res_op_mode->role = PR_ISTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + wpa_printf(MSG_DEBUG, "PR PASN: Choose ISTA role"); + } + } else { + wpa_printf(MSG_DEBUG, "PR PASN: Failed to choose device role"); + return PR_NEGOTIATION_FAIL; + } + + res_op_mode->protocol_type = ranging_type; + os_memcpy(res_op_mode->country, pr->cfg->country, 3); + + if (res_op_mode->role == PR_RSTA_SUPPORT) { + pr_copy_channels(&res_op_mode->channels, &common_chan, + pr->cfg->support_6ghz); + } else { + pr_choose_best_channel(&common_chan, &op_class, &op_channel); + if (!op_class || !op_channel) { + wpa_printf(MSG_DEBUG, + "PR: Couldn't choose a common channel for ranging in ISTA role"); + return PR_NEGOTIATION_FAIL; + } + res_op_mode->channels.op_classes = 1; + res_op_mode->channels.op_class[0].channels = 1; + res_op_mode->channels.op_class[0].channel[0] = op_channel; + res_op_mode->channels.op_class[0].op_class = op_class; + wpa_printf(MSG_DEBUG, + "PR: Choose operating class %u, channel %u", + op_class, op_channel); + } + + wpa_printf(MSG_DEBUG, + "PR PASN: Ranging Protocol=%u, Role=%u, country=%c%c, status=%d, op class size=%lu", + res_op_mode->protocol_type, res_op_mode->role, + valid_country_ch(res_op_mode->country[0]) ? + res_op_mode->country[0] : '_', + valid_country_ch(res_op_mode->country[1]) ? + res_op_mode->country[1] : '_', + status, res_op_mode->channels.op_classes); + + return status; +} + + +static u8 pr_pasn_get_final_op_mode(struct pr_data *pr, u8 supp_roles, + struct operation_mode *op_mode, + struct operation_mode *res_op_mode) +{ + u8 ranging_type = 0; + struct pr_channels common_chan; + struct pr_channels *own_channels = NULL; + int status = PR_NEGOTIATION_FAIL; + u8 op_class = 0, op_channel = 0; + bool own_ista_support = false, own_rsta_support = false; + + if (op_mode->protocol_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) { + if (op_mode->protocol_type == PR_NTB_SECURE_LTF_BASED_RANGING && + !pr->cfg->secure_he_ltf) { + wpa_printf(MSG_INFO, + "PR PASN: Secure HE-LTF not supported"); + return PR_NEGOTIATION_FAIL; + } + + if ((op_mode->protocol_type & + PR_NTB_SECURE_LTF_BASED_RANGING) && pr->cfg->secure_he_ltf) + ranging_type = PR_NTB_SECURE_LTF_BASED_RANGING; + else + ranging_type = PR_NTB_OPEN_BASED_RANGING; + + own_ista_support = pr->cfg->ntb_ista_support; + own_rsta_support = pr->cfg->ntb_rsta_support; + own_channels = &pr->cfg->ntb_channels; + wpa_printf(MSG_DEBUG, "PR PASN: Choose NTB Ranging Protocol"); + } else if (op_mode->protocol_type & PR_EDCA_BASED_RANGING) { + ranging_type = PR_EDCA_BASED_RANGING; + + own_ista_support = pr->cfg->edca_ista_support; + own_rsta_support = pr->cfg->edca_rsta_support; + own_channels = &pr->cfg->edca_channels; + wpa_printf(MSG_DEBUG, "PR PASN: Choose EDCA Ranging Protocol"); + } else { + wpa_printf(MSG_INFO, + "PR PASN: Invalid Ranging Protocol, proposed type 0x%x", + op_mode->protocol_type); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_ISTA_SUPPORT && !own_rsta_support) { + wpa_printf(MSG_INFO, "PR: Device cannot act as RSTA"); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_RSTA_SUPPORT && !own_ista_support) { + wpa_printf(MSG_INFO, "PR: Device cannot act as ISTA"); + return PR_NEGOTIATION_FAIL; + } + + if (op_mode->role == PR_ISTA_SUPPORT) { + res_op_mode->role = PR_RSTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + } else if (op_mode->role == PR_RSTA_SUPPORT) { + res_op_mode->role = PR_ISTA_SUPPORT; + status = PR_NEGOTIATION_SUCCESS; + } else { + wpa_printf(MSG_INFO, "PR: Invalid Ranging Role proposed"); + return PR_NEGOTIATION_FAIL; + } + + pr_channels_intersect(own_channels, &op_mode->channels, &common_chan); + if (!common_chan.op_classes) { + wpa_printf(MSG_INFO, + "PR: No common channels to perform ranging"); + return PR_NEGOTIATION_FAIL; + } + + pr_choose_best_channel(&common_chan, &op_class, &op_channel); + if (!op_class || !op_channel) { + wpa_printf(MSG_INFO, + "PR: Couldn't choose a common channel for ranging"); + return PR_NEGOTIATION_FAIL; + } + + res_op_mode->protocol_type = ranging_type; + os_memcpy(res_op_mode->country, pr->cfg->country, 3); + res_op_mode->channels.op_classes = 1; + res_op_mode->channels.op_class[0].channels = 1; + res_op_mode->channels.op_class[0].channel[0] = op_channel; + res_op_mode->channels.op_class[0].op_class = op_class; + wpa_printf(MSG_DEBUG, "PR: Choose operating class %u, channel %u", + op_class, op_channel); + + return status; +} + + +static int pr_prepare_pasn_pr_elem(struct pr_data *pr, struct wpabuf *extra_ies, + bool add_dira, u8 ranging_role, + u8 ranging_type, int forced_pr_freq) +{ + u32 ie_type; + struct wpabuf *buf, *buf2; + struct pr_dira dira; + struct pr_capabilities pr_caps; + struct edca_capabilities edca_caps; + struct ntb_capabilities ntb_caps; + struct operation_mode op_mode; + struct pr_channels op_channels; + u8 forced_op_class = 0, forced_op_channel = 0; + enum hostapd_hw_mode hw_mode; + + buf = wpabuf_alloc(1000); + if (!buf) + return -1; + + pr_get_ranging_capabilities(pr, &pr_caps); + pr_buf_add_ranging_capa_info(buf, &pr_caps); + + if (ranging_type & PR_EDCA_BASED_RANGING) { + pr_get_edca_capabilities(pr, &edca_caps); + pr_buf_add_edca_capa_info(buf, &edca_caps); + pr_copy_channels(&op_channels, &edca_caps.channels, + pr->cfg->support_6ghz); + } else if (ranging_type & PR_NTB_OPEN_BASED_RANGING || + ranging_type & PR_NTB_SECURE_LTF_BASED_RANGING) { + pr_get_ntb_capabilities(pr, &ntb_caps); + pr_buf_add_ntb_capa_info(buf, &ntb_caps); + pr_copy_channels(&op_channels, &ntb_caps.channels, + pr->cfg->support_6ghz); + } else { + wpa_printf(MSG_INFO, "PR: Unsupported ranging_type 0x%x", + ranging_type); + wpabuf_free(buf); + return -1; + } + + os_memset(&op_mode, 0, sizeof(struct operation_mode)); + op_mode.role = ranging_role; + op_mode.protocol_type = ranging_type; + os_memcpy(op_mode.country, pr->cfg->country, 3); + + if (forced_pr_freq) { + hw_mode = ieee80211_freq_to_channel_ext(forced_pr_freq, 0, 0, + &forced_op_class, + &forced_op_channel); + if (hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_INFO, "PR: Invalid forced_pr_freq"); + wpabuf_free(buf); + return -1; + } + + op_mode.channels.op_classes = 1; + op_mode.channels.op_class[0].channels = 1; + op_mode.channels.op_class[0].channel[0] = forced_op_channel; + op_mode.channels.op_class[0].op_class = forced_op_class; + } else { + pr_copy_channels(&op_mode.channels, &op_channels, + pr->cfg->support_6ghz); + } + + pr_buf_add_operation_mode(buf, &op_mode); + + /* PR Device Identity Resolution attribute */ + if (!pr_derive_dira(pr, pr->cfg->dev_addr, &dira)) + pr_buf_add_dira(buf, &dira); + + ie_type = (OUI_WFA << 8) | PR_OUI_TYPE; + buf2 = pr_encaps_elem(buf, ie_type); + wpabuf_free(buf); + + if (wpabuf_tailroom(extra_ies) < wpabuf_len(buf2)) { + wpa_printf(MSG_INFO, + "PR: Not enough room for PR element in PASN Frame"); + wpabuf_free(buf2); + return -1; + } + wpabuf_put_buf(extra_ies, buf2); + wpabuf_free(buf2); + + return 0; +} + + +static struct wpabuf * pr_pasn_generate_rsnxe(struct pr_data *pr, int akmp) +{ + u32 capab; + size_t flen = 0; + struct wpabuf *buf; + + capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + + if (wpa_key_mgmt_sae(akmp)) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + if (pr->cfg->secure_he_ltf) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); + + while (capab >> flen * 8) + flen++; + + buf = wpabuf_alloc(2 + flen); + if (!buf) + return NULL; + + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + wpa_printf(MSG_DEBUG, "PR: RSNXE capabilities: %04x", capab); + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, flen); + while (flen--) { + wpabuf_put_u8(buf, capab & 0xff); + capab = capab >> 8; + } + + return buf; +} + + +/* SSID used for deriving SAE pt for PR security */ +#define PR_PASN_SSID "516F9A010000" + +static void pr_pasn_set_password(struct pasn_data *pasn, u8 pasn_type, + const char *passphrase) +{ + int pasn_groups[4] = { 0, 0, 0, 0 }; + size_t len; + + if (!passphrase) + return; + + len = os_strlen(passphrase); + + if ((pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) && + (pasn_type & (PR_PASN_DH19_UNAUTH | PR_PASN_DH19_AUTH))) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) PR_PASN_SSID, + os_strlen(PR_PASN_SSID), + (const u8 *) passphrase, len, NULL, 0); +} + + +static int pr_pasn_initialize(struct pr_data *pr, struct pr_device *dev, + const u8 *addr, u8 auth_mode, int freq, + u8 ranging_type, const u8 *pmkid) +{ + struct wpabuf *rsnxe; + struct pasn_data *pasn; + + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + } else { + dev->pasn = pasn_data_init(); + if (!dev->pasn) + return -1; + } + + pasn = dev->pasn; + os_memcpy(pasn->own_addr, pr->cfg->dev_addr, ETH_ALEN); + os_memcpy(pasn->peer_addr, addr, ETH_ALEN); + + if (dev->pasn_role == PR_ROLE_PASN_INITIATOR) { + pasn->pmksa = pr->initiator_pmksa; + os_memcpy(pasn->bssid, pasn->peer_addr, ETH_ALEN); + } else { + pasn->pmksa = pr->responder_pmksa; + os_memcpy(pasn->bssid, pasn->own_addr, ETH_ALEN); + } + + pasn->noauth = 1; + + /* As specified in Proximity Ranging Implementation Considerations for + * P2P Operation D1.8, unauthenticated mode PASN with DH group 19 + * should be supported by all P2P proximity ranging devices. Skip + * this check for OOB peers whose capabilities are not known. + */ + if (dev->discovery_type != PR_DISCOVERY_TYPE_OOB && + (!(pr->cfg->pasn_type & BIT(0)) || + !(dev->pr_caps.pasn_type & BIT(0)))) { + wpa_printf(MSG_DEBUG, + "PR PASN: Unauthenticated DH group 19 NOT supported, PASN type of self 0x%x, peer 0x%x", + pr->cfg->pasn_type, dev->pr_caps.pasn_type); + return -1; + } + + /* As specified in Proximity Ranging Implementation Considerations for + * P2P Operation D1.8, EDCA based ranging is only supported with + * unauthenticated mode PASN with DH group 19. */ + if (((pr->cfg->pasn_type & 0xc) && (dev->pr_caps.pasn_type & 0xc)) && + ranging_type != PR_EDCA_BASED_RANGING) { + pasn->group = 20; + pasn->cipher = WPA_CIPHER_GCMP_256; + } else { + pasn->group = 19; + pasn->cipher = WPA_CIPHER_CCMP; + } + + if (pr->cfg->secure_he_ltf && + ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING) { + pasn->secure_ltf = true; + pasn_enable_kdk_derivation(pasn); + } else { + pasn_disable_kdk_derivation(pasn); + } + wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len); + + if (auth_mode == PR_PASN_AUTH_MODE_SAE) { + pasn->akmp = WPA_KEY_MGMT_SAE; + if (dev->password_valid) { + pr_pasn_set_password(pasn, pr->cfg->pasn_type, + dev->password); + } else if (pr->cfg->global_password_valid) { + pr_pasn_set_password(pasn, pr->cfg->pasn_type, + pr->cfg->global_password); + } else { + wpa_printf(MSG_INFO, "PR PASN: Password not available"); + return -1; + } + } else if (auth_mode == PR_PASN_AUTH_MODE_PMK && dev->pmk_valid) { + if (!dev->pmk_valid) { + wpa_printf(MSG_INFO, "PR PASN: PMK not available"); + return -1; + } + if (dev->pasn_role == PR_ROLE_PASN_INITIATOR) + pasn_initiator_pmksa_cache_add(pr->initiator_pmksa, + pasn->own_addr, + pasn->peer_addr, + dev->pmk, + dev->pmk_len, + pmkid, + WPA_KEY_MGMT_SAE); + else + pasn_responder_pmksa_cache_add(pr->responder_pmksa, + pasn->own_addr, + pasn->peer_addr, + dev->pmk, + dev->pmk_len, + pmkid, + WPA_KEY_MGMT_SAE); + pasn->akmp = WPA_KEY_MGMT_SAE; + } else { + pasn->akmp = WPA_KEY_MGMT_PASN; + } + + pasn->rsn_pairwise = pasn->cipher; + pasn->wpa_key_mgmt = pasn->akmp; + + rsnxe = pr_pasn_generate_rsnxe(pr, pasn->akmp); + if (rsnxe) { + os_free(pasn->rsnxe_ie); + pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe), + wpabuf_len(rsnxe)); + wpabuf_free(rsnxe); + if (!pasn->rsnxe_ie) + return -1; + } + + pasn->cb_ctx = pr->cfg->cb_ctx; + pasn->send_mgmt = pr->cfg->pasn_send_mgmt; + pasn->freq = freq; + return 0; +} + + +static int pr_validate_pasn_request(struct pr_data *pr, struct pr_device *dev, + u8 auth_mode, u8 ranging_role, + u8 ranging_type) +{ + if (!ranging_role || !ranging_type) + return -1; + + if (auth_mode == PR_PASN_AUTH_MODE_PASN) { + if (!(pr->cfg->pasn_type & + (PR_PASN_DH19_UNAUTH | PR_PASN_DH20_UNAUTH)) || + !(dev->pr_caps.pasn_type & + (PR_PASN_DH19_UNAUTH | PR_PASN_DH20_UNAUTH))) { + wpa_printf(MSG_INFO, + "PR: Dev/Peer doesn't support PASN-UNAUTH"); + return -1; + } + } else if (auth_mode == PR_PASN_AUTH_MODE_PMK || + auth_mode == PR_PASN_AUTH_MODE_SAE) { + if (!(pr->cfg->pasn_type & + (PR_PASN_DH19_AUTH | PR_PASN_DH20_AUTH)) || + !(dev->pr_caps.pasn_type & + (PR_PASN_DH19_AUTH | PR_PASN_DH20_AUTH))) { + wpa_printf(MSG_INFO, "PR: Dev/Peer doesn't support PASN-SAE/PMK"); + return -1; + } + } + + if (ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING || + ranging_type == PR_NTB_OPEN_BASED_RANGING) { + if (ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING && + (!pr->cfg->secure_he_ltf || !dev->ntb_caps.secure_he_ltf)) { + wpa_printf(MSG_INFO, + "PR: Dev/Peer doesn't support HE-LTF"); + return -1; + } + + if (ranging_role == PR_ISTA_SUPPORT && + !pr->cfg->ntb_ista_support) { + wpa_printf(MSG_INFO, + "PR: Device doesn't support NTB ISTA role"); + return -1; + } + + if (ranging_role == PR_RSTA_SUPPORT && + !pr->cfg->ntb_rsta_support) { + wpa_printf(MSG_INFO, + "PR: Device doesn't support NTB RSTA role"); + return -1; + } + + if (ranging_role == PR_ISTA_SUPPORT && + !dev->ntb_caps.rsta_support && + !pr->cfg->ntb_rsta_support) { + wpa_printf(MSG_INFO, + "PR: Device and Peer doesn't support NTB RSTA role, no possiblity for negotiation update"); + return -1; + } + + if (ranging_role == PR_RSTA_SUPPORT && + !dev->ntb_caps.ista_support && + !pr->cfg->ntb_ista_support) { + wpa_printf(MSG_INFO, + "PR: Device and Peer doesn't support NTB ISTA role, no possiblity for negotiation update"); + return -1; + } + } else if (ranging_type == PR_EDCA_BASED_RANGING) { + if (ranging_role == PR_ISTA_SUPPORT && + !pr->cfg->edca_ista_support) { + wpa_printf(MSG_INFO, + "PR: Device doesn't support EDCA ISTA role"); + return -1; + } + + if (ranging_role == PR_RSTA_SUPPORT && + !pr->cfg->edca_rsta_support) { + wpa_printf(MSG_INFO, + "PR: Device doesn't support EDCA RSTA role"); + return -1; + } + + if (ranging_role == PR_ISTA_SUPPORT && + !dev->edca_caps.rsta_support && + !pr->cfg->edca_rsta_support) { + wpa_printf(MSG_INFO, + "PR: Device and Peer doesn't support EDCA RSTA role, no possiblity for negotiation update"); + return -1; + } + + if (ranging_role == PR_RSTA_SUPPORT && + !dev->edca_caps.ista_support && + !pr->cfg->edca_ista_support) { + wpa_printf(MSG_INFO, + "PR: Device and Peer doesn't support EDCA ISTA role, no possiblity for negotiation update"); + return -1; + } + } + + return 0; +} + + +int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq, + u8 auth_mode, u8 ranging_role, u8 ranging_type, + int forced_pr_freq) +{ + int ret = 0; + struct pasn_data *pasn; + struct pr_device *dev; + u8 pmkid[PMKID_LEN]; + struct wpabuf *extra_ies; + + if (!addr) { + wpa_printf(MSG_DEBUG, "PR PASN: Peer address NULL"); + return -1; + } + + dev = pr_get_device(pr, addr); + if (!dev) { + wpa_printf(MSG_DEBUG, "PR PASN: Peer not known"); + return -1; + } + + if (dev->discovery_type != PR_DISCOVERY_TYPE_OOB && + pr_validate_pasn_request(pr, dev, auth_mode, ranging_role, + ranging_type) < 0) { + wpa_printf(MSG_INFO, + "PR PASN: Invalid parameters to initiate authentication"); + return -1; + } + + if (freq == 0) + freq = dev->listen_freq; + + dev->pasn_role = PR_ROLE_PASN_INITIATOR; + + if (auth_mode == PR_PASN_AUTH_MODE_PMK && dev->pmk_valid && + os_get_random(pmkid, PMKID_LEN) < 0) + return -1; + + if (pr_pasn_initialize(pr, dev, addr, auth_mode, freq, ranging_type, + pmkid)) { + wpa_printf(MSG_INFO, "PR PASN: Initialization failed"); + return -1; + } + pasn = dev->pasn; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies) + return -1; + + if (pr_prepare_pasn_pr_elem(pr, extra_ies, false, ranging_role, + ranging_type, forced_pr_freq)) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to prepare extra elements"); + ret = -1; + goto out; + } + + pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + + if (auth_mode == PR_PASN_AUTH_MODE_PMK) { + ret = wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, + pasn->bssid, pasn->akmp, pasn->cipher, + pasn->group, pasn->freq, NULL, 0, NULL, 0, + NULL); + } else { + ret = wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, + pasn->bssid, pasn->akmp, pasn->cipher, + pasn->group, pasn->freq, NULL, 0, NULL, 0, + NULL); + } + if (ret) { + wpa_printf(MSG_INFO, "PR PASN: Failed to start PASN"); + } else { + /* M1 sent successfully - notify that negotiation has started */ + if (pr->cfg->negotiation_started) + pr->cfg->negotiation_started(pr->cfg->cb_ctx, addr, + ranging_role, + ranging_type); + } + +out: + wpabuf_free(extra_ies); + return ret; +} + + +int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len, + bool acked) +{ + int ret = 0; + struct pr_device *dev; + struct pasn_data *pasn; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + u8 self_format_bw, peer_format_bw; + + if (!pr) + return -1; + + dev = pr_get_device(pr, mgmt->da); + if (!dev || !dev->pasn) { + wpa_printf(MSG_INFO, "PR PASN: Peer not found " MACSTR, + MAC2STR(mgmt->da)); + return -1; + } + + pasn = dev->pasn; + wpa_printf(MSG_DEBUG, "PR PASN: TX status from " MACSTR " ack=%d", + MAC2STR(mgmt->da), acked); + + ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked); + + /* + * Authentication frame 1 was not acked; return to caller to schedule a + * retransmission. Preserve pasn->frame for the retry. + */ + if (ret == 2) + return ret; + + if (ret == 1 && acked && pr->cfg->pasn_result) + pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role, + dev->protocol_type, dev->final_op_class, + dev->final_op_channel, pr->cfg->country); + + if (ret == 1 && acked && pr->cfg->get_ranging_params) { + if (dev->protocol_type & PR_EDCA_BASED_RANGING) { + self_format_bw = pr->cfg->edca_format_and_bw; + peer_format_bw = dev->edca_caps.edca_hw_caps & + EDCA_FORMAT_AND_BW_MASK; + } else if ((dev->protocol_type & + PR_NTB_SECURE_LTF_BASED_RANGING) || + (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) { + self_format_bw = pr->cfg->ntb_format_and_bw; + peer_format_bw = dev->ntb_caps.ntb_hw_caps & + NTB_FORMAT_AND_BW_MASK; + } else { + wpa_printf(MSG_INFO, + "PR PASN: Invalid protocol type: %u", + dev->protocol_type); + goto out; + } + + pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr, + dev->pr_device_addr, + dev->ranging_role, + dev->protocol_type, + dev->final_op_class, + dev->final_op_channel, + self_format_bw, + peer_format_bw); + pr_flush(pr); + } + +out: + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + return ret; +} + + +int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr) +{ + struct pr_device *dev; + struct pasn_data *pasn; + + dev = pr_get_device(pr, addr); + if (!dev || !dev->pasn || !dev->pasn->frame) + return -1; + + pasn = dev->pasn; + wpa_printf(MSG_DEBUG, "PR PASN: retransmit Authentication frame 1 to " + MACSTR, MAC2STR(addr)); + return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame), + wpabuf_len(pasn->frame), 0, pasn->freq, 1000); +} + + +static int pr_process_pasn_ranging_wrapper(struct pr_data *pr, + struct pr_device *dev, + const struct ieee80211_mgmt *mgmt, + size_t len, int trans_seq) +{ + u32 ie_type; + const u8 *ies; + size_t ies_len; + u8 status = PR_NEGOTIATION_FAIL; + bool success = false; + struct wpabuf *buf, *buf2; + struct pr_message msg; + struct pr_capabilities caps; + struct edca_capabilities edca; + struct ntb_capabilities ntb; + struct operation_mode op_mode; + u8 supp_ranging_role = 0; + struct operation_mode res_op_mode; + bool ntb_caps_valid = false, edca_caps_valid = false; + + buf = wpabuf_alloc(1000); + if (!buf) { + wpa_printf(MSG_INFO, "PR: Memory allocation failed"); + return -1; + } + + os_memset(&msg, 0, sizeof(msg)); + ies = mgmt->u.auth.variable; + if (offsetof(struct ieee80211_mgmt, u.auth.variable) > len) + return -1; + ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + if (pr_parse_elements(ies, ies_len, &msg) || !msg.op_mode) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to parse PR element in Auth1"); + goto end; + } + + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + if (!msg.status_ie || !msg.status_ie_len) { + wpa_printf(MSG_DEBUG, "PR INFO: * No status attribute"); + wpabuf_free(buf); + pr_parse_free(&msg); + return -1; + } + if (*msg.status_ie == PR_NEGOTIATION_FAIL) { + wpa_printf(MSG_INFO, + "PR PASN: * Ranging Negotiation status fail"); + wpabuf_free(buf); + pr_parse_free(&msg); + return -1; + } + } + + if (!msg.op_mode || !msg.op_mode_len || + !msg.pr_capability || !msg.pr_capability_len || + ((!msg.edca_capability || !msg.edca_capability_len) && + (!msg.ntb_capability || !msg.ntb_capability_len))) + goto end; + + if (msg.dira && msg.dira_len) + pr_validate_dira(pr, dev, msg.dira, msg.dira_len); + + pr_process_op_mode(msg.op_mode, msg.op_mode_len, &op_mode); + if (!op_mode.channels.op_classes) { + wpa_printf(MSG_INFO, "PR: Received empty channel list"); + goto end; + } + + os_memset(&caps, 0, sizeof(struct pr_capabilities)); + pr_process_ranging_capabilities(msg.pr_capability, + msg.pr_capability_len, &caps); + if (!pr_eq_ranging_capa_params(dev, &caps)) { + wpa_printf(MSG_INFO, "PR: Ranging capabilities not matching"); + goto end; + } + + pr_get_ranging_capabilities(pr, &caps); + + if ((op_mode.protocol_type & PR_EDCA_BASED_RANGING) && + (!msg.edca_capability || !msg.edca_capability_len)) + goto end; + if ((op_mode.protocol_type & PR_NTB_OPEN_BASED_RANGING) && + (!msg.ntb_capability || !msg.ntb_capability_len)) + goto end; + if ((op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) && + (!msg.ntb_capability || !msg.ntb_capability_len)) + goto end; + + if (op_mode.protocol_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) { + pr_process_ntb_capabilities(msg.ntb_capability, + msg.ntb_capability_len, &ntb, + caps.secure_he_ltf); + + if (!pr_eq_ntb_params(dev, &ntb)) { + wpa_printf(MSG_INFO, + "PR: NTB capabilities not matching"); + goto end; + } + + if (dev->ntb_caps.ista_support) + supp_ranging_role |= PR_ISTA_SUPPORT; + if (dev->ntb_caps.rsta_support) + supp_ranging_role |= PR_RSTA_SUPPORT; + + pr_get_ntb_capabilities(pr, &ntb); + ntb_caps_valid = true; + } else if (op_mode.protocol_type & PR_EDCA_BASED_RANGING) { + pr_process_edca_capabilities(msg.edca_capability, + msg.edca_capability_len, &edca); + if (!pr_eq_edca_params(dev, &edca)) { + wpa_printf(MSG_INFO, + "PR: EDCA capabilities not matching"); + goto end; + } + + if (dev->edca_caps.ista_support) + supp_ranging_role |= PR_ISTA_SUPPORT; + if (dev->edca_caps.rsta_support) + supp_ranging_role |= PR_RSTA_SUPPORT; + + pr_get_edca_capabilities(pr, &edca); + edca_caps_valid = true; + } + + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) + status = pr_pasn_get_best_op_mode(pr, supp_ranging_role, + &op_mode, &res_op_mode); + else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) + status = pr_pasn_get_final_op_mode(pr, supp_ranging_role, + &op_mode, &res_op_mode); + + if (status != PR_NEGOTIATION_SUCCESS && + status != PR_NEGOTIATION_UPDATE) { + wpa_printf(MSG_DEBUG, + "PR: Couldn't derive suitable operation mode"); + goto end; + } + + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + pr_buf_add_ranging_capa_info(buf, &caps); + if (edca_caps_valid) + pr_buf_add_edca_capa_info(buf, &edca); + if (ntb_caps_valid) + pr_buf_add_ntb_capa_info(buf, &ntb); + } + + pr_buf_add_ranging_neg_status(buf, status); + pr_buf_add_operation_mode(buf, &res_op_mode); + + dev->ranging_role = res_op_mode.role; + dev->protocol_type = res_op_mode.protocol_type; + + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + dev->final_op_channel = + res_op_mode.channels.op_class[0].channel[0]; + dev->final_op_class = res_op_mode.channels.op_class[0].op_class; + } + + success = true; +end: + if (!success) + pr_buf_add_ranging_neg_status(buf, PR_NEGOTIATION_FAIL); + + ie_type = (OUI_WFA << 8) | PR_OUI_TYPE; + buf2 = pr_encaps_elem(buf, ie_type); + wpabuf_free(buf); + + wpabuf_free(dev->ranging_wrapper); + dev->ranging_wrapper = buf2; + pr_parse_free(&msg); + + return 0; +} + + +static int +pr_process_pasn_ranging_wrapper_result(struct pr_data *pr, + struct pr_device *dev, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + int ret = -1; + const u8 *ies; + size_t ies_len; + struct pr_message msg; + struct operation_mode op_mode; + struct pr_channels common_chan; + + os_memset(&msg, 0, sizeof(msg)); + ies = mgmt->u.auth.variable; + ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + if (pr_parse_elements(ies, ies_len, &msg) || !msg.op_mode) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to parse PR element in Auth3"); + goto fail; + } + + if (!msg.status_ie || !msg.status_ie_len) { + wpa_printf(MSG_INFO, "PR PASN: * No status attribute"); + goto fail; + } + + if (*msg.status_ie == PR_NEGOTIATION_FAIL) { + wpa_printf(MSG_INFO, + "PR PASN: * Ranging Negotiation status fail"); + goto fail; + } + + if (!msg.op_mode || !msg.op_mode_len) + goto fail; + + pr_process_op_mode(msg.op_mode, msg.op_mode_len, &op_mode); + if (op_mode.channels.op_classes != 1) { + wpa_printf(MSG_INFO, "PR: PASN received invalid channel list"); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "PR PASN: Frame 3: Operating mode data: Role=%u, protocol type=%u, operating class=%u, channel= %u", + op_mode.role, op_mode.protocol_type, + op_mode.channels.op_class[0].op_class, + op_mode.channels.op_class[0].channel[0]); + + if (op_mode.protocol_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) { + if ((op_mode.role & PR_ISTA_SUPPORT) && + !pr->cfg->ntb_rsta_support) + goto fail; + + if ((op_mode.role & PR_RSTA_SUPPORT) && + !pr->cfg->ntb_ista_support) + goto fail; + + if ((op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) && + !pr->cfg->secure_he_ltf) + goto fail; + + pr_channels_intersect(&pr->cfg->ntb_channels, &op_mode.channels, + &common_chan); + if (common_chan.op_classes == 0) { + wpa_printf(MSG_INFO, "PR PASN: No common channel"); + goto fail; + } + } else if (op_mode.protocol_type & PR_EDCA_BASED_RANGING) { + if ((op_mode.role & PR_ISTA_SUPPORT) && + !pr->cfg->edca_rsta_support) + goto fail; + + if ((op_mode.role & PR_RSTA_SUPPORT) && + !pr->cfg->edca_ista_support) + goto fail; + + pr_channels_intersect(&pr->cfg->edca_channels, + &op_mode.channels, &common_chan); + if (common_chan.op_classes == 0) + goto fail; + } + + if (op_mode.role & PR_RSTA_SUPPORT) + dev->ranging_role = PR_ISTA_SUPPORT; + else + dev->ranging_role = PR_RSTA_SUPPORT; + dev->protocol_type = op_mode.protocol_type; + dev->final_op_channel = op_mode.channels.op_class[0].channel[0]; + dev->final_op_class = op_mode.channels.op_class[0].op_class; + ret = 0; + +fail: + pr_parse_free(&msg); + return ret; +} + + +static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + int ret = -1; + u8 pasn_type; + u8 auth_mode = 0; + int pasn_groups[4] = { 0 }; + struct wpa_ie_data rsn_data; + struct ieee802_11_elems elems; + + pasn_type = pr->cfg->pasn_type; + if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH) && + pasn_type & (PR_PASN_DH19_UNAUTH | PR_PASN_DH19_AUTH)) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & (PR_PASN_DH20_UNAUTH | PR_PASN_DH20_AUTH)) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + + if (pr_process_pasn_ranging_wrapper(pr, dev, mgmt, len, 1)) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to handle Auth1 action wrapper"); + return -1; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PR PASN: Failed parsing elements in Auth1 frame"); + goto fail; + } + + if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn_data) == 0) { + if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE && rsn_data.num_pmkid) + auth_mode = PR_PASN_AUTH_MODE_PMK; + else if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE) + auth_mode = PR_PASN_AUTH_MODE_SAE; + else + auth_mode = PR_PASN_AUTH_MODE_PASN; + } + + dev->pasn_role = PR_ROLE_PASN_RESPONDER; + if (pr_pasn_initialize(pr, dev, mgmt->sa, auth_mode, freq, + dev->protocol_type, rsn_data.pmkid)) { + wpa_printf(MSG_INFO, "PR PASN: Initialize failed"); + goto fail; + } + + pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(dev->ranging_wrapper), + wpabuf_len(dev->ranging_wrapper)); + os_free(dev->pasn->pasn_groups); + dev->pasn->pasn_groups = int_array_dup(pasn_groups); + if (handle_auth_pasn_1(dev->pasn, pr->cfg->dev_addr, mgmt->sa, mgmt, + len, false) < 0) { + wpa_printf(MSG_INFO, "PR PASN: Handle Auth1 failed"); + goto fail; + } + + if (!(dev->protocol_type & PR_EDCA_BASED_RANGING) && + pr->cfg->set_keys && + pr->cfg->set_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr, + dev->pr_device_addr, dev->pasn->cipher, + dev->pasn->akmp, &dev->pasn->ptk) < 0) { + wpa_printf(MSG_INFO, "PR PASN: Key configuration failed"); + goto fail; + } + + /* M1 received and M2 sent - notify that negotiation has started */ + if (pr->cfg->negotiation_started) + pr->cfg->negotiation_started(pr->cfg->cb_ctx, mgmt->sa, + dev->ranging_role, + dev->protocol_type); + ret = 0; + +fail: + wpabuf_free(dev->ranging_wrapper); + dev->ranging_wrapper = NULL; + return ret; +} + + +static int pr_pasn_handle_auth_2(struct pr_data *pr, struct pr_device *dev, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + int ret = -1; + struct wpa_pasn_params_data pasn_data; + + if (dev->pasn_role != PR_ROLE_PASN_INITIATOR) { + wpa_printf(MSG_INFO, + "PR PASN: Auth2 not expected on responder"); + return -1; + } + + if (!dev->pasn) + return -1; + + if (pr_process_pasn_ranging_wrapper(pr, dev, mgmt, len, 2)) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to handle Auth2 action wrapper"); + return -1; + } + pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(dev->ranging_wrapper), + wpabuf_len(dev->ranging_wrapper)); + + if (wpa_pasn_auth_rx(dev->pasn, (const u8 *) mgmt, len, + &pasn_data) < 0) { + wpa_printf(MSG_INFO, "PR PASN: wpa_pasn_auth_rx() failed"); + dev->pasn_role = PR_ROLE_IDLE; + goto fail; + } + + if (!(dev->protocol_type & PR_EDCA_BASED_RANGING) && + pr->cfg->set_keys && + pr->cfg->set_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr, + dev->pr_device_addr, dev->pasn->cipher, + dev->pasn->akmp, &dev->pasn->ptk) < 0) { + wpa_printf(MSG_INFO, "PR PASN: Key configuration failed"); + goto fail; + } + ret = 0; + +fail: + wpabuf_free(dev->ranging_wrapper); + dev->ranging_wrapper = NULL; + return ret; +} + + +static int pr_pasn_handle_auth_3(struct pr_data *pr, struct pr_device *dev, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 self_format_bw, peer_format_bw; + + if (dev->pasn_role != PR_ROLE_PASN_RESPONDER) { + wpa_printf(MSG_INFO, + "PR PASN: Auth3 not expected on initiator"); + return -1; + } + + if (!dev->pasn) + return -1; + + if (pr_process_pasn_ranging_wrapper_result(pr, dev, mgmt, len)) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to handle Auth3 action wrapper"); + goto fail; + } + + if (handle_auth_pasn_3(dev->pasn, pr->cfg->dev_addr, mgmt->sa, mgmt, + len) < 0) { + wpa_printf(MSG_INFO, "PR PASN: Failed to handle Auth3"); + goto fail; + } + + if (pr->cfg->pasn_result) + pr->cfg->pasn_result(pr->cfg->cb_ctx, dev->ranging_role, + dev->protocol_type, dev->final_op_class, + dev->final_op_channel, pr->cfg->country); + + if (dev->protocol_type & PR_EDCA_BASED_RANGING) { + self_format_bw = pr->cfg->edca_format_and_bw; + peer_format_bw = dev->edca_caps.edca_hw_caps & + EDCA_FORMAT_AND_BW_MASK; + + } else if ((dev->protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) || + (dev->protocol_type & PR_NTB_OPEN_BASED_RANGING)) { + self_format_bw = pr->cfg->ntb_format_and_bw; + peer_format_bw = dev->ntb_caps.ntb_hw_caps & + NTB_FORMAT_AND_BW_MASK; + } else { + wpa_printf(MSG_INFO, "PR PASN: Invalid protocol type: %u", + dev->protocol_type); + goto fail; + } + + if (pr->cfg->get_ranging_params) + pr->cfg->get_ranging_params(pr->cfg->cb_ctx, pr->cfg->dev_addr, + dev->pr_device_addr, + dev->ranging_role, + dev->protocol_type, + dev->final_op_class, + dev->final_op_channel, + self_format_bw, + peer_format_bw); + pr_flush(pr); + return 0; + +fail: + /* Clear the keys as M3 processing failed */ + if (pr->cfg->clear_keys) + pr->cfg->clear_keys(pr->cfg->cb_ctx, pr->cfg->dev_addr, + dev->pr_device_addr); + return -1; +} + + +int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt, + size_t len, int freq) +{ + struct pr_device *dev; + u16 auth_alg, auth_transaction; + + dev = pr_get_device(pr, mgmt->sa); + if (!dev) { + wpa_printf(MSG_INFO, "PR: Peer not found " MACSTR, + MAC2STR(mgmt->sa)); + return -1; + } + + if (!ether_addr_equal(mgmt->da, pr->cfg->dev_addr)) { + wpa_printf(MSG_INFO, "PR PASN: Not our frame"); + return -1; + } + + if (len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -1; + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + if (auth_alg != WLAN_AUTH_PASN) { + wpa_printf(MSG_INFO, + "PR: Unexpected Authentication frame, auth_alg=%d", + auth_alg); + return -1; + } + + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) + return pr_pasn_handle_auth_1(pr, dev, mgmt, len, freq); + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) + return pr_pasn_handle_auth_2(pr, dev, mgmt, len); + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) + return pr_pasn_handle_auth_3(pr, dev, mgmt, len); + + return -1; +} + +#endif /* CONFIG_PASN */ diff --git a/contrib/wpa/src/common/proximity_ranging.h b/contrib/wpa/src/common/proximity_ranging.h new file mode 100644 index 000000000000..6ce9f69224bd --- /dev/null +++ b/contrib/wpa/src/common/proximity_ranging.h @@ -0,0 +1,683 @@ +/* + * Proxmity Ranging + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PROXIMITY_RANGING_H +#define PROXIMITY_RANGING_H + +#include "wpa_common.h" +#include "utils/list.h" +#include "wps/wps_defs.h" + +struct ieee80211_mgmt; + +#define DEVICE_IDENTITY_KEY_LEN 16 +#define DEVICE_IDENTITY_TAG_LEN 8 +#define DEVICE_IDENTITY_NONCE_LEN 8 +#define DIR_STR_LEN 3 +#define DEVICE_MAX_HASH_LEN 32 + +/* DIRA Cipher versions */ +#define DIRA_CIPHER_VERSION_128 0 + +/** + * PR_MAX_OP_CLASSES - Maximum number of operating classes + */ +#define PR_MAX_OP_CLASSES 30 + +/** + * PR_MAX_OP_CLASS_CHANNELS - Maximum number of channels per operating class + */ +#define PR_MAX_OP_CLASS_CHANNELS 60 + +/** + * PR_MAX_PEER - Maximum number of Proximity Ranging peers that device can store + */ +#define PR_MAX_PEER 100 + +/* + * Proximity Ranging negotiation status + * Proximity Ranging Implementation Considerations for P2P Operation D1.8, + * Table 5 (Proximity Ranging Status Attribute format). + */ +#define PR_NEGOTIATION_SUCCESS 0 +#define PR_NEGOTIATION_UPDATE 1 +#define PR_NEGOTIATION_FAIL 2 + +/** + * enum pr_session_end_reason - Reason codes for ranging session end + */ +enum pr_session_end_reason { + PR_SESSION_END_TIMEOUT = 0, + PR_SESSION_END_USER_ABORT = 1, + PR_SESSION_END_PEER_COMPLETE = 2, + PR_SESSION_END_NEG_FAILED = 3, +}; + +enum pr_pasn_role { + PR_ROLE_IDLE = 0, + PR_ROLE_PASN_INITIATOR, + PR_ROLE_PASN_RESPONDER, +}; + +/** + * struct pr_channels - List of supported channels + */ +struct pr_channels { + /** + * struct pr_op_class - Supported operating class + */ + struct pr_op_class { + /** + * op_class - Operating class + */ + u8 op_class; + + /** + * channel - Supported channels + */ + u8 channel[PR_MAX_OP_CLASS_CHANNELS]; + + /** + * channels - Number of channel entries in use + */ + size_t channels; + } op_class[PR_MAX_OP_CLASSES]; + + /** + * op_classes - Number of op_class entries in use + */ + size_t op_classes; +}; + +/** + * Format and Bandwidth values for EDCA based ranging with range of 10-16 + * from IEEE Std 802.11-2024, 9.4.2.166 (FTM Parameters element), Table 9-325 + * (Format And Bandwidth subfield) as specified in Proximity Ranging + * Implementation Considerations for P2P Operation, Draft 1.8, Table 8 + * (Proximity Ranging EDCA Capability Attribute format) for the the Ranging + * Parameters field B0-B3. + */ +enum edca_format_and_bw_value { + EDCA_FORMAT_AND_BW_VHT20 = 10, + EDCA_FORMAT_AND_BW_HT40 = 11, + EDCA_FORMAT_AND_BW_VHT40 = 12, + EDCA_FORMAT_AND_BW_VHT80 = 13, + EDCA_FORMAT_AND_BW_VHT80P80 = 14, + EDCA_FORMAT_AND_BW_VHT160_DUAL_LO = 15, + EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO = 16, + EDCA_FORMAT_AND_BW_INVALID +}; + +/** + * Format and Bandwidth values for NTB based ranging as from IEEE Std + * 802.11-2024, 9.4.2.300 (Ranging Parameters element), Table 9-412 (Format And + * Bandwidth subfield) as specified in Proximity Ranging Implementation + * Considerations for P2P Operation, Draft 1.8, Table 9 (Proximity Ranging 11az + * NTB Capability Attribute format) for the Ranging Parameter field B0-B2. + */ +enum ntb_format_and_bw_value { + NTB_FORMAT_AND_BW_HE20 = 0, + NTB_FORMAT_AND_BW_HE40 = 1, + NTB_FORMAT_AND_BW_HE80 = 2, + NTB_FORMAT_AND_BW_HE80P80 = 3, + NTB_FORMAT_AND_BW_HE160_DUAL_LO = 4, + NTB_FORMAT_AND_BW_HE160_SINGLE_LO = 5, + NTB_FORMAT_AND_BW_INVALID +}; + +struct pr_capabilities { + u8 pasn_type; + + char device_name[WPS_DEV_NAME_MAX_LEN + 1]; + + bool edca_support; + + bool ntb_support; + + bool secure_he_ltf; + + bool support_6ghz; +}; + +struct edca_capabilities { + bool ista_support; + + bool rsta_support; + +/** + * Ranging Parameters field for device specific EDCA capabilities + * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8, + * Table 8 (Proximity Ranging EDCA Capability Attribute format). + */ +#define EDCA_FORMAT_AND_BW 0 +#define EDCA_MAX_TX_ANTENNA 4 +#define EDCA_MAX_RX_ANTENNA 7 + +#define EDCA_FORMAT_AND_BW_MASK 0x000F +#define EDCA_MAX_TX_ANTENNA_MASK 0x0007 +#define EDCA_MAX_RX_ANTENNA_MASK 0x0007 + u16 edca_hw_caps; + + char country[3]; + + struct pr_channels channels; +}; + +struct ntb_capabilities { + bool ista_support; + + bool rsta_support; + + bool secure_he_ltf; + +/** + * Ranging Parameter field for NTB capabilities + * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8, + * Table 9 (Proximity Ranging 11az NTB Capability Attribute format). + */ +#define NTB_FORMAT_AND_BW 0 +#define MAX_TX_LTF_REPETATIONS 3 +#define MAX_RX_LTF_REPETATIONS 6 +#define MAX_RX_LTF_TOTAL 9 +#define MAX_TX_LTF_TOTAL 11 +#define MAX_RX_STS_LE_80 13 +#define MAX_RX_STS_GT_80 16 +#define MAX_TX_STS_LE_80 19 +#define MAX_TX_STS_GT_80 22 + +#define NTB_FORMAT_AND_BW_MASK 0x00000007 + +/* Max TX LTF repetations supported for non trigger based ranging */ +#define MAX_TX_LTF_REPETATIONS_MASK 0x00000007 + +/* Max RX LTF repetations supported for non trigger based ranging */ +#define MAX_RX_LTF_REPETATIONS_MASK 0x00000007 + +/* Max RX LTF total supported for non trigger based ranging */ +#define MAX_RX_LTF_TOTAL_MASK 0x00000003 + +/* Max TX LTF total supported for non trigger based ranging */ +#define MAX_TX_LTF_TOTAL_MASK 0x00000003 + +/* To configure max R2I STS for Bandwidth less than or equal to 80 MHz */ +#define MAX_RX_STS_LE_80_MASK 0x00000007 + +/* To configure max R2I STS for Bandwidth greater than 80Mz */ +#define MAX_RX_STS_GT_80_MASK 0x00000007 + +/* To configure max I2R STS for Bandwidth less than or equal to 80 MHz */ +#define MAX_TX_STS_LE_80_MASK 0x00000007 + +/* To configure max I2R STS for Bandwidth greater than 80 MHz */ +#define MAX_TX_STS_GT_80_MASK 0x00000007 + u32 ntb_hw_caps; + + char country[3]; + + struct pr_channels channels; +}; + +/* + * Proximity Ranging Attribute IDs + * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8, + * Table 4 (Proximity Ranging Attribute ID list). + */ +enum pr_attr_id { + PR_ATTR_STATUS = 0, + PR_ATTR_RANGING_CAPABILITY = 1, + PR_ATTR_EDCA_CAPABILITY = 2, + PR_ATTR_NTB_CAPABILITY = 3, + PR_ATTR_OPERATION_MODE = 4, + PR_ATTR_WLAN_AP_INFO = 5, + PR_ATTR_DEVICE_IDENTITY_RESOLUTION = 6, + PR_ATTR_VENDOR_SPECIFIC = 221, +}; + +/* + * Proximity Ranging capabilities in Ranging Protocol Type field, + * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8, + * Table 7 (Proximity Ranging Capability Attribute format). + */ +#define PR_EDCA_BASED_RANGING BIT(0) +#define PR_NTB_SECURE_LTF_BASED_RANGING BIT(1) +#define PR_NTB_OPEN_BASED_RANGING BIT(2) + +/** + * Ranging Role field in EDCA capabilities + * Proximity Ranging Implementation Considerations for P2P Operation Draft 1.8, + * Table 8 (Proximity Ranging EDCA Capability Attribute format). + */ +#define PR_ISTA_SUPPORT BIT(0) +#define PR_RSTA_SUPPORT BIT(1) + +/* + * PASN capabilities in PASN Type field + * Proximity Ranging Implementation Considerations for P2P Operation D1.8, + * Table 7 (Proximity Ranging Capability Attribute). + */ +#define PR_PASN_DH19_UNAUTH BIT(0) +#define PR_PASN_DH19_AUTH BIT(1) +#define PR_PASN_DH20_UNAUTH BIT(2) +#define PR_PASN_DH20_AUTH BIT(3) + +/* Authentication Mode */ +#define PR_PASN_AUTH_MODE_PASN 0 +#define PR_PASN_AUTH_MODE_SAE 1 +#define PR_PASN_AUTH_MODE_PMK 2 + +/* Peer discovery type */ +#define PR_DISCOVERY_TYPE_USD 0 +#define PR_DISCOVERY_TYPE_OOB 1 + +/** + * struct pr_pasn_ranging_params - Peer parameters to be used in PASN to trigger + * ranging in case PR PASN is successful. + */ +struct pr_pasn_ranging_params { + enum { + PR_PASN_AND_RANGING, + PR_STOP_RANGING, + } action; + u8 peer_addr[ETH_ALEN]; + u8 ranging_type; + u8 ranging_role; + u8 pr_pasn_status; + u8 auth_mode; + int freq; + u32 ranging_timeout; + u8 src_addr[ETH_ALEN]; + enum pr_pasn_role pasn_role; + + /** + * EDCA based ranging specific parameters + * + * @burst_period: Burst period in units of 100 milliseconds + * @num_bursts_exp: Number of bursts exponent + * @ftms_per_burst: Number of FTM frames per burst + * @ftmr_retries: Number of retries for FTM Request frame + * @burst_duration: Burst duration as defined in IEEE Std 802.11-2024, + * Table 9-322 (Burst Duration subfield encoding). + */ + u16 burst_period; + u8 num_bursts_exp; + u8 ftms_per_burst; + u8 ftmr_retries; + u8 burst_duration; + + /** + * NTB ranging specific parameters + * + * @min_time_between_measurements: Minimum time between two consecutive + * range measurements in units of 100 microseconds. + * @max_time_between_measurements: Maximum time between two consecutive + * range measurements in units of 10 milliseconds, to avoid FTM + * negotiation. + * @availability_window: Duration of the Availability Window (AW) in + * units of 1 millisecond (0-255 ms). + * @nominal_time: Nominal duration between adjacent Availability Windows + * in units of milliseconds. + */ + u32 min_time_between_measurements; + u32 max_time_between_measurements; + u8 availability_window; + u32 nominal_time; + + /** + * @request_lci: Whether to request LCI + * @request_civicloc: Whether to request civic location + */ + bool request_lci; + bool request_civicloc; + + /** + * @lmr_feedback: Negotiate LMR feedback for NTB ranging. + * Only valid when NTB ranging type is set. Required for RSTA + * to report measurement results back to the initiator. + */ + bool lmr_feedback; + + /** + * @ingress_threshold: Ingress range threshold in millimeters. + * The kernel reports a measurement result when the device + * moves into this range. + */ + u64 ingress_threshold; + + /** + * @egress_threshold: Egress range threshold in millimeters. + * The kernel reports a measurement result when the device + * moves out of this range. + */ + u64 egress_threshold; + + /** + * @pr_suppress_results: Suppress ranging results for PD requests. + * Cannot be used with range_report or lmr_feedback. + */ + bool pr_suppress_results; + + u32 continuous_ranging_session_time; + + int forced_pr_freq; + u8 ranging_op_class; + u16 channel_width; /* channel width in MHz (20/40/80/160/320) */ + u8 format_bw; + u32 center_freq1; + u32 center_freq2; + u64 cookie; + + /* Per-session credentials - if set, used instead of stored ones */ + char password[100]; + bool password_valid; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; +}; + +struct pr_dev_ik { + struct dl_list list; + u8 dik[DEVICE_IDENTITY_KEY_LEN]; + char password[100]; + bool password_valid; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + bool pmk_valid; +}; + +/** + * struct pr_device_info - Proximity ranging peer information + */ +struct pr_device { + struct dl_list list; + struct os_reltime last_seen; + int listen_freq; + + /** + * pr_device_addr - PR Device Address of the peer + */ + u8 pr_device_addr[ETH_ALEN]; + + struct pr_capabilities pr_caps; + struct edca_capabilities edca_caps; + struct ntb_capabilities ntb_caps; + + /* Password to be used in PASN-SAE by the Seeker. + * This is updated with valid password if DIRA matches for the peer. + */ + char password[100]; + bool password_valid; + + /* PMK to be used in PASN-PMK by the Seeker. + * This is updated with valid PMK if DIRA matches for the peer. + */ + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + bool pmk_valid; + + /* DevIK of the peer resolved via DIRA verification. + * Set to the matched dev_ik->dik when pr_validate_dira() succeeds. + * Cleared by pr_clear_dev_iks(). + */ + u8 dik[DEVICE_IDENTITY_KEY_LEN]; + bool dik_valid; + +#ifdef CONFIG_PASN + /* PASN data structure */ + struct pasn_data *pasn; + struct wpabuf *ranging_wrapper; + enum pr_pasn_role pasn_role; +#endif /* CONFIG_PASN */ + + u8 ranging_role; + u8 protocol_type; + u8 final_op_class; + u8 final_op_channel; + u8 discovery_type; +}; + + +/** + * struct pr_message - Proximity ranging peer information + */ +struct pr_message { + struct wpabuf *pr_attributes; + + u8 pr_device_addr[ETH_ALEN]; + + const u8 *pr_capability; + size_t pr_capability_len; + + const u8 *edca_capability; + size_t edca_capability_len; + + const u8 *ntb_capability; + size_t ntb_capability_len; + + const u8 *dira; + size_t dira_len; + + const u8 *status_ie; + size_t status_ie_len; + + const u8 *op_mode; + size_t op_mode_len; +}; + + +struct pr_config { + u8 pasn_type; + + int preferred_ranging_role; + + char country[3]; + + u8 dev_addr[ETH_ALEN]; + + /** + * dev_name - Device Name + */ + char *dev_name; + + bool edca_ista_support; + + bool edca_rsta_support; + + /* Best single format_bw value derived from pd_format_bw_bitmap */ + u8 edca_format_and_bw; + + u32 edca_min_ranging_interval; + + u8 max_tx_antenna; + + u8 max_rx_antenna; + + struct pr_channels edca_channels; + + bool ntb_ista_support; + + bool ntb_rsta_support; + + bool concurrent_ista_rsta; + + u32 pmsr_max_peers; + + u32 pr_max_peer_ista_role; + + u32 pr_max_peer_rsta_role; + + u8 max_ftms_per_burst; + + bool secure_he_ltf; + + u8 max_tx_ltf_repetations; + + u8 max_rx_ltf_repetations; + + u8 max_tx_ltf_total; + + u8 max_rx_ltf_total; + + u8 max_rx_sts_le_80; + + u8 max_rx_sts_gt_80; + + u8 max_tx_sts_le_80; + + u8 max_tx_sts_gt_80; + + /* PD ranging preamble and bandwidth bitmaps (shared by EDCA and NTB) */ + u32 pd_preamble_bitmap; + u32 pd_format_bw_bitmap; + + /* Best single format_bw value derived from pd_format_bw_bitmap */ + u8 ntb_format_and_bw; + + u32 ntb_min_ranging_interval; + + struct pr_channels ntb_channels; + + bool support_6ghz; + + /* Cipher version type */ + int dik_cipher; + + /* Buffer to hold the DevIK */ + u8 dik_data[DEVICE_IDENTITY_KEY_LEN]; + + /* Length of DevIK in octets */ + size_t dik_len; + + /* DevIK expiration */ + int expiration; + + /* Global password to be used in PASN-SAE for Advertiser */ + char global_password[100]; + + bool global_password_valid; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * pasn_send_mgmt - Function handler to transmit a Management frame + * @ctx: Callback context from cb_ctx + * @data: Frame to transmit + * @data_len: Length of frame to transmit + * @noack: No ack flag + * @freq: Frequency in MHz for the channel on which to transmit + * @wait: How many milliseconds to wait for a response frame + * Returns: 0 on success, -1 on failure + */ + int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, unsigned int wait); + + /** + * negotiation_started - Called when PASN negotiation begins + * @ctx: Callback context from cb_ctx + * @peer_addr: MAC address of the peer + * @role: Ranging role (initiator or responder) + * @protocol_type: Ranging protocol type + * + * Fired on the initiator after Auth frame 1 (M1) is sent, and on the + * responder after Auth frame 1 (M1) is received and M2 is sent back. + */ + void (*negotiation_started)(void *ctx, const u8 *peer_addr, u8 role, + u8 protocol_type); + + void (*pasn_result)(void *ctx, u8 role, u8 protocol_type, u8 op_class, + u8 op_channel, const char *country); + + int (*set_keys)(void *ctx, const u8 *own_addr, const u8 *peer_addr, + int cipher, int akmp, struct wpa_ptk *ptk); + + void (*clear_keys)(void *ctx, const u8 *own_addr, const u8 *peer_addr); + + void (*get_ranging_params)(void *ctx, const u8 *dev_addr, + const u8 *peer_addr, u8 ranging_role, + u8 protocol_type, u8 op_class, u8 op_channel, + u8 self_format_bw, u8 peer_format_bw); + + void (*device_found)(void *ctx, const struct pr_device *dev); +}; + +struct pr_data { + /** + * cfg - PR module configuration + * + * This is included in the same memory allocation with the + * struct pr_data and as such, must not be freed separately. + */ + struct pr_config *cfg; + + struct dl_list devices; + + struct dl_list dev_iks; + + /* PMKSA cache for PASN-PMK authentication */ + struct rsn_pmksa_cache *initiator_pmksa; + struct rsn_pmksa_cache *responder_pmksa; + + /* PR PASN request tracking - similar to pasn_params in wpa_supplicant + */ + struct pr_pasn_ranging_params *pr_pasn_params; + + /* Set when final measurement result received; blocks further results */ + bool ranging_final_received; +}; + +/* PR Device Identity Resolution Attribute parameters */ +struct pr_dira { + /* Cipher version type */ + int cipher_version; + /* Nonce used in DIRA attribute */ + u8 nonce[DEVICE_IDENTITY_NONCE_LEN]; + /* Length of nonce */ + size_t nonce_len; + /* Tag computed for nonce using NIK */ + u8 tag[DEVICE_IDENTITY_TAG_LEN]; + /* Length of tag in octets */ + size_t tag_len; +}; + +struct operation_mode { + /* Bitmap for Ranging Protocol type */ + u8 protocol_type; + + /* Bitmap for Role-ISTA/RSTA */ + u8 role; + + char country[3]; + + struct pr_channels channels; +}; + +struct pr_data * pr_init(const struct pr_config *cfg); +void pr_flush(struct pr_data *pr); +void pr_deinit(struct pr_data *pr); +void pr_set_dev_addr(struct pr_data *pr, const u8 *addr); +void pr_clear_dev_iks(struct pr_data *pr); +void pr_add_dev_ik(struct pr_data *pr, const u8 *dik, const char *password, + const u8 *pmk, size_t pmk_len, bool own); +int pr_set_peer_credentials(struct pr_data *pr, const u8 *addr, + const u8 *pmk, size_t pmk_len, + const char *password); +struct wpabuf * pr_prepare_usd_elems(struct pr_data *pr, const u8 *src_addr); +void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len, + const u8 *peer_addr, unsigned int freq); +int pr_ensure_oob_peer(struct pr_data *pr, const u8 *addr, int freq); +int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq, + u8 auth_mode, u8 ranging_role, u8 ranging_type, + int forced_pr_freq); +int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len, + bool acked); +int pr_pasn_auth_retransmit(struct pr_data *pr, const u8 *addr); +int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt, + size_t len, int freq); + +#endif /* PROXIMITY_RANGING_H */ diff --git a/contrib/wpa/src/common/ptksa_cache.c b/contrib/wpa/src/common/ptksa_cache.c index 918a1cc0c314..39faf0e01816 100644 --- a/contrib/wpa/src/common/ptksa_cache.c +++ b/contrib/wpa/src/common/ptksa_cache.c @@ -39,12 +39,21 @@ static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx) struct ptksa_cache *ptksa = eloop_ctx; struct ptksa_cache_entry *e, *next; struct os_reltime now; + struct dl_list expired; if (!ptksa) return; os_get_reltime(&now); + dl_list_init(&expired); + /* + * Move expired entries from the main ptksa list to a temporary + * 'expired' list. This prevents issues if the callback (e->cb) + * triggers operations like ptksa_cache_flush(), which would iterate + * over ptksa->ptksa. By removing entries first, flush operations + * will not double-process or double-free these entries. + */ dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry, list) { if (e->expiration > now.sec) @@ -53,10 +62,17 @@ static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR, MAC2STR(e->addr)); + dl_list_del(&e->list); + ptksa->n_ptksa--; + dl_list_add_tail(&expired, &e->list); + } + + dl_list_for_each_safe(e, next, &expired, + struct ptksa_cache_entry, list) { + dl_list_del(&e->list); if (e->cb && e->ctx) e->cb(e); - else - ptksa_cache_free_entry(ptksa, e); + bin_clear_free(e, sizeof(*e)); } ptksa_cache_set_expiration(ptksa); @@ -267,6 +283,7 @@ void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher) * @life_time_expiry_cb: Callback for alternative expiration handling * @ctx: Context pointer to save into e->ctx for the callback * @akmp: The key management mechanism that was used to derive the PTK + * @auth_alg: The authentication algorithm that was used to derive the PTK * Returns: Pointer to the added PTKSA cache entry or %NULL on error * * This function creates a PTKSA entry and adds it to the PTKSA cache. @@ -280,7 +297,7 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, const struct wpa_ptk *ptk, void (*life_time_expiry_cb) (struct ptksa_cache_entry *e), - void *ctx, u32 akmp) + void *ctx, u32 akmp, u16 auth_alg) { struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL; struct os_reltime now; @@ -306,6 +323,7 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, entry->cb = life_time_expiry_cb; entry->ctx = ctx; entry->akmp = akmp; + entry->auth_alg = auth_alg; if (own_addr) os_memcpy(entry->own_addr, own_addr, ETH_ALEN); @@ -374,7 +392,8 @@ int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len) struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr, u32 cipher, u32 life_time, const struct wpa_ptk *ptk, - void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp) + void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp, + u16 auth_alg) { return NULL; } diff --git a/contrib/wpa/src/common/ptksa_cache.h b/contrib/wpa/src/common/ptksa_cache.h index dd5e7db8f98c..bb0d1071a566 100644 --- a/contrib/wpa/src/common/ptksa_cache.h +++ b/contrib/wpa/src/common/ptksa_cache.h @@ -27,6 +27,7 @@ struct ptksa_cache_entry { void (*cb)(struct ptksa_cache_entry *e); void *ctx; u32 akmp; + u16 auth_alg; }; @@ -44,7 +45,8 @@ struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa, const struct wpa_ptk *ptk, void (*cb) (struct ptksa_cache_entry *e), - void *ctx, u32 akmp); + void *ctx, u32 akmp, + u16 auth_alg); void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher); #endif /* PTKSA_CACHE_H */ diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h index 5b58ffb3943c..1290e36d8d0c 100644 --- a/contrib/wpa/src/common/qca-vendor.h +++ b/contrib/wpa/src/common/qca-vendor.h @@ -2,7 +2,7 @@ * Qualcomm Atheros OUI and vendor specific assignments * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -76,6 +76,10 @@ enum qca_radiotap_vendor_ids { * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_5GHZ: Configure TX_NSS in 5 or 6 GHz band * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_5GHZ: Configure RX_NSS in 5 or 6 GHz band * + * To effectively clear or reset the NSS configuration, user-space can set the + * attribute to 0xFF or 255, allowing the driver to apply the default NSS + * configuration. + * * Global chain configuration - Applies to all bands (2.4 GHz and 5/6 GHz) * The following attributes are used to dynamically configure the number of * chains to be used for transmitting or receiving the data in the 2.4 GHz and @@ -111,6 +115,35 @@ enum qca_radiotap_vendor_ids { * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_5GHZ: The number of chains to be * used for receiving the data in the 5/6 GHz band. * + * To effectively clear or reset the chain configuration, user-space can set + * the attribute to 0xFF or 255, allowing the driver to apply the default chain + * configuration. + * + * Global chain-mask configuration - Applies to 2.4 GHz or 5/6 GHz band + * The following band specific attributes are used to dynamically configure the + * global chain masks (e.g., 0x1 for Chain 0, 0x2 for Chain 1, 0x4 for Chain 2) + * to be used for transmitting the data in the 2.4 GHz or 5/6 GHz band. + * + * These attributes can be used independently. + * This configuration is allowed when the driver is active. + * The driver/firmware will intersect (AND operation) the newly configured + * global TX chain mask with other chain configurations (e.g., the global + * chain-mask configuration, global chain configuration, and per band chain + * configuration) to determine the final active chain mask. + * + * If the intersection of these configurations results in no available chains, + * the driver/firmware will reject the configuration attempt. + * + * To effectively clear or reset the chain mask configuration, user-space + * can set the attribute to a bitmask representing all available chains + * (i.e., 0xFF or 255), allowing the driver to apply the default chain mask + * configuration. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_2GHZ: Bitmask of chains to be used + * for transmitting the data in the 2.4 GHz band. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_5GHZ: Bitmask of chains to be used + * for transmitting the data in the 5/6 GHz band. + * * The following scenarios capture how the driver process the configuration when * different TX/RX NSS and chain config attributes are used in the command. * @@ -230,7 +263,8 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features * supported by the driver. enum qca_wlan_vendor_features defines - * the possible features. + * the possible features that are encoded in + * QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS. * * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver, * which supports DFS offloading, to indicate a channel availability check @@ -1132,6 +1166,39 @@ enum qca_radiotap_vendor_ids { * Uses the attributes defined in * enum qca_wlan_vendor_attr_tdls_disc_rsp_ext. * + * @QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH: This vendor subcommand is + * used to configure and indicate the audio transport switch in both + * command and event paths. This is used when two or more audio transports + * (e.g., WLAN and Bluetooth) are available between peers. + * + * If the driver needs to perform operations like scan, connection, + * roaming, RoC, etc. and AP concurrency policy is set to either + * QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO or + * QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING, the driver sends + * audio transport switch event to userspace. Userspace application upon + * receiving the event, can try to switch to the requested audio transport. + * The userspace uses this command to send the status of transport + * switching (either confirm or reject) to the driver using this + * subcommand. The driver continues with the pending operation either upon + * receiving the command from userspace or after waiting for a timeout from + * sending the event to userspace. The driver can request userspace to + * switch to WLAN upon availability of WLAN audio transport once after the + * concurrent operations are completed. + * + * Userspace can also request audio transport switch from non-WLAN to WLAN + * using this subcommand to the driver. The driver can accept or reject + * depending on other concurrent operations in progress. The driver returns + * success if it can allow audio transport when it receives the command or + * appropriate kernel error code otherwise. Userspace indicates the audio + * transport switch from WLAN to non-WLAN using this subcommand and the + * driver can do other concurrent operations without needing to send any + * event to userspace. This subcommand is used by userspace only when the + * driver advertises support for + * QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN. + * + * The attributes used with this command are defined in enum + * qca_wlan_vendor_attr_audio_transport_switch. + * * @QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY: This vendor subcommand is used to * configure, retrieve, and report per-link transmit latency statistics. * @@ -1227,6 +1294,235 @@ enum qca_radiotap_vendor_ids { * collection of these statistics has been enabled by the command * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY. The attributes for this * event are defined in enum qca_wlan_vendor_attr_flow_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_USD: Vendor subcommand to implement unsynchronized + * service discovery (USD). Based on the type of the USD subcommand the USD + * operation to publish, subscribe, update publish, cancel publish, or + * cancel subscribe is triggered. + * + * When used as an event, the driver notifies the status of an USD command. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_usd. + * + * @QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT: This is an extension to + * %NL80211_CMD_CONNECT command. Userspace can use this to indicate + * additional information to be considered for the subsequent + * (re)association request attempts with %NL80211_CMD_CONNECT. The + * additional information sent with this command is applicable for the + * entire duration of the connection established with %NL80211_CMD_CONNECT, + * including the roams triggered by the driver internally due to other + * vendor interfaces, driver internal logic, and BTM requests from the + * connected AP. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_connect_ext. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE: Vendor subcommand to configure + * Wi-Fi Direct mode. This command sets the configuration through + * the attributes defined in the enum qca_wlan_vendor_attr_set_p2p_mode. + * It is applicable for P2P Group Owner only. This command is used before + * starting the GO. + * + * @QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ: Vendor subcommand to request + * transmission of a channel usage request. It carries channel usage + * information for BSSs that are not infrastructure BSSs or an off channel + * TDLS direct link. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_chan_usage_req. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT: Vendor subcommand that can be + * used to fetch the current snapshot of scan data stored by firmware + * during the offload scans such as PNO (Preferred Network Offload), RTT, + * and roaming scans when the Apps or host is in suspended state. This scan + * data comprises of only limited information of the scanned BSSs due to + * memory limits of the firmware. The BSS information stored in the + * firmware may not be pushed to the kernel (cfg80211) scan cache after + * Apps or host coming out from suspended state if full Beacon or Probe + * Response frame information is not available. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_fw_scan_report. + * + * @QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN: If there are no active Wi-Fi + * interfaces for a certain duration, the host driver might trigger idle + * shutdown. The host driver rejects the user space commands between start + * and completion of the idle shutdown. If a command is rejected, user + * space can use this event to determine when to retry the specific + * command. + * + * This is a wiphy specific vendor event and it indicates user space that + * the host driver has reached the idle timer and has started or completed + * idle shutdown procedure. + * + * The attributes used with this event are defined in + * enum qca_wlan_vendor_attr_idle_shutdown. + * + * @QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE: This vendor subcommand/event is + * used for primary link migration. + * + * This subcommand is used to trigger primary link migration from + * user space. Either just one ML client or a bunch of clients can + * be migrated. + * + * This subcommand is used as an event to notify user applications and + * subsystems about primary link migration once it is completed + * successfully. This event will send the MAC address of the peer for which + * the primary link has been changed and the new link ID to ensure primary + * link changes in WLAN subsystem are communicated to user applications + * and also to manage the load of that primary link in a better way. + * + * The attributes used with this subcommand/event are defined in + * &enum qca_wlan_vendor_attr_pri_link_migrate. + * + * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR and + * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID are mutually + * exclusive attributes. Migration should be requested for either one ML + * client or a bunch of ML clients. + * + * @QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG: Vendor subcommand that + * can be used to send periodic or on-demand directed Probe Response frames + * to a connected peer. + * + * This command is only applicable for AP/P2P GO mode. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg. + * + * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS: Vendor subcommand that can + * be used to notify userspace about status updates of a classified flow + * learned by the driver with + * %QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT. + * The attributes for this event are defined in + * enum qca_wlan_vendor_attr_flow_status. + * + * @QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG: Subcommand to update the + * RX MCS MAP capability. This configuration is only applicable to + * non-AP STA or non-AP MLD and allowed only in associated state and + * valid until disconnection. This command results in the driver triggering + * re-association to re-negotiate the updated RX MCS capability with the + * peer. The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_rx_mcs_map_params. + * + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE: Vendor subcommand/event + * which is used to exchange Tx powerboost operations between + * userspace and driver (kernel space). + * + * The attributes used with this command/event are defined in + * enum qca_wlan_vendor_attr_iq_data_inference. + * + * @QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA: Vendor subcommand used to + * enable/disable Notice of Absence (NoA) as a GO in a P2P connection. + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_p2p_set_noa. + * + * @QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WIPHY: Vendor subcommand/event + * which is used to get WLAN statistics from the driver to userspace + * application for the WIPHY level interfaces like device/radio. + * + * The attributes used for this subcommand are defined in + * enum qca_wlan_vendor_attr_wlan_telemetry. + * + * The attributes used for the event reply are defined in + * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply. + * + * @QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WDEV: Vendor subcommand/event + * which is used to get WLAN statistics from the driver to userspace + * application for the WDEV level interfaces like vdev/station. + * + * The attributes used for this subcommand are defined in + * enum qca_wlan_vendor_attr_wlan_telemetry. + * + * The attributes used for the event reply are defined in + * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply. + * + * @QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE: Vendor subcommand used to + * notify the userspace about the current active and inactive links + * among the affiliated MLO links, whenever there is a change in the + * state of any of the links along with the reason. + * The attributes used in this event are defined in the + * enum qca_wlan_vendor_attr_link_state_change. + * + * @QCA_NL80211_VENDOR_SUBCMD_DAR: This vendor subcommand is used to initiate + * the Dynamic Analytics Report (DAR) request or session terminate from + * the driver. The request or session terminate type is identified over + * the %QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE attribute. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_dar. + * + * @QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG: Vendor subcommand used to control + * a specific feature enablement based on the OUI data and capabilities of + * the AP. The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_feature_config. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS: Vendor subcommand used to retrieve + * Wi-Fi and other technologies (e.g., Bluetooth, UWB (Ultra-Wideband), MWS + * (Modem Wireless System) coexistence statistics from the driver. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_coex_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS: This vendor subcommand is used to + * configure airtime fairness. The attributes used with this subcommand + * are defined in enum qca_wlan_vendor_attr_atf_offload_ops. + * + * @QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG: Vendor subcommand used to get or set + * Dynamic Channel Selection (DCS) configuration parameters. This enables or + * disables different types of interference mitigation. DCS monitors + * wireless channels for periodic interference events, typically one event + * per second, and automatically switches to a cleaner channel when + * necessary. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_dcs. + * + * @QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS: Retrieve Qualcomm Sensing Hub (QSH) + * related Wi-Fi statistics from the sensor. Currently supports scan count; + * might be extended in the future. + * + * No attributes are used in the request. The response includes attributes + * defined in enum qca_wlan_vendor_attr_qsh_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS: This vendor subcommand is + * used to retrieve WLAN host driver side TX/RX statistics. + * The attributes used with this command are defined in + * enum qca_wlan_host_txrx_stats_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS: This vendor subcommand is + * used to retrieve IP Accelerator (IPA) ring statistics. + * The attributes used with this command are defined in + * enum qca_wlan_ipa_ring_stats_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH: This vendor subcommand is used to + * offload only the authentication process to wpa_supplicant in scenarios + * where the SME is implemented in the driver. + * + * Authentication frames are exchanged between the driver and + * wpa_supplicant using %NL80211_CMD_FRAME, and the transmission status + * of each frame is reported through %NL80211_CMD_FRAME_TX_STATUS. If the + * authentication process results in establishing a PTKSA (e.g., EPPKE + * authentication), wpa_supplicant stores the PTKSA in its internal cache. + * After the final Authentication frame is successfully transmitted + * (confirmed via %NL80211_CMD_FRAME_TX_STATUS), wpa_supplicant installs + * the required temporal keys into the driver using %NL80211_CMD_NEW_KEY. + * After completing the authentication process, wpa_supplicant will + * indicate the success/failure status with the same command to the driver. + * + * Upon successful authentication, the driver proceeds with association + * phase and reports the connection result via %NL80211_CMD_CONNECT. If + * association completes successfully, wpa_supplicant retrieves the KCK + * and KEK from the PTKSA cache for use in PTK/GTK rekeying operations. If + * the association fails, wpa_supplicant clears the PTKSA cache for this + * session to avoid using stale key material. + * + * wpa_supplicant advertises the authentication algorithms it supports for + * this subcommand using %QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES via + * %QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT. + * + * The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_external_auth. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -1442,7 +1738,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP = 229, QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG = 230, QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT = 231, - /* 232 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH = 232, QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY = 233, /* 234 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_SDWF_PHY_OPS = 235, @@ -1459,6 +1755,31 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT = 246, QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY = 247, QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT = 248, + QCA_NL80211_VENDOR_SUBCMD_USD = 249, + QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT = 250, + QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE = 251, + QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ = 252, + QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT = 253, + QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN = 254, + /* 255 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE = 256, + QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG = 257, + QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS = 258, + QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG = 259, + QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE = 260, + QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA = 261, + QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WIPHY = 262, + QCA_NL80211_VENDOR_SUBCMD_WLAN_TELEMETRY_WDEV = 263, + QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE = 264, + QCA_NL80211_VENDOR_SUBCMD_DAR = 265, + QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG = 266, + QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS = 267, + QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS = 268, + QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG = 269, + QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS = 270, + QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS = 271, + QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS = 272, + QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH = 273, }; /* Compatibility defines for previously used subcmd names. @@ -1485,7 +1806,11 @@ enum qca_wlan_vendor_attr { */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, - /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + /* Feature flags contained in a byte array. The feature flags are + * identified by their bit index (see &enum qca_wlan_vendor_features) + * with the first byte being the least significant one and the last one + * being the most significant one. Used by + * QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES. */ QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, QCA_WLAN_VENDOR_ATTR_TEST = 8, /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ @@ -1688,6 +2013,24 @@ enum qca_wlan_vendor_attr { */ QCA_WLAN_VENDOR_ATTR_MLO_CAPABILITY_MAX_STR_LINK_COUNT = 45, + /* An array of unsigned 32-bit values used by + * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the switch + * count for each antenna. + */ + QCA_WLAN_VENDOR_ATTR_ANT_SWITCH_COUNT = 46, + + /* An array of unsigned 32-bit values used by + * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the active + * duration (in seconds) of each antenna. + */ + QCA_WLAN_VENDOR_ATTR_ANT_DURATION = 47, + + /* An array of unsigned 32-bit values used by + * QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report the + * current RSSI of each antenna. + */ + QCA_WLAN_VENDOR_ATTR_ANT_RSSI = 48, + /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -1839,6 +2182,11 @@ enum qca_wlan_vendor_attr_roam_auth { * nested attribute are defined in enum qca_wlan_vendor_attr_mlo_links. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS = 15, + /* Flag attribute, The driver sets this flag to indicate that both the + * (Re)Association Request frame and the corresponding (Re)Association + * Response frame were transmitted encrypted over the air. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_ASSOC_ENCRYPTED = 16, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, @@ -1936,7 +2284,9 @@ enum qca_wlan_vendor_attr_p2p_listen_offload { * * @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC. * Used with command to configure channel list using an array of - * channel numbers (u8). + * channel numbers (u8). This represents the list of allowed channels for + * the primary and non-primary channel operation. Channels which are not present + * in the specified list shouldn't be used as a primary or non-primary channel. * Note: If both the driver and user-space application supports the 6 GHz band, * the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional. @@ -1968,7 +2318,10 @@ enum qca_wlan_vendor_attr_p2p_listen_offload { * * @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC. * Used with command to configure the channel list using an array of channel - * center frequencies in MHz (u32). + * center frequencies in MHz (u32). This represents the list of allowed + * frequencies for the primary and non-primary channel operation. Frequencies + * which are not present in the specified list shouldn't be used as a primary or + * non-primary channel. * Note: If both the driver and user-space application supports the 6 GHz band, * the driver first parses the frequency list and if it fails to get a frequency * list, parses the channel list specified using @@ -2031,6 +2384,15 @@ enum qca_wlan_vendor_attr_p2p_listen_offload { * Used with command to configure ACS operation for a specific link affiliated * to an AP MLD. * + * @QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY: Optional flag + * attribute. Used with command to indicate whether the driver is allowed to use + * a 6 GHz non-PSC channel as a primary channel. If this flag is indicated the + * driver shall not use 6 GHz non-PSC channels as a primary channel even if + * %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST includes 6 GHz non-PSC channels. + * However, the driver is still allowed to use 6 GHz non-PSC channels specified + * in %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST as non-primary channels. User space is + * allowed to specify this flag only when the driver indicates support for + * %QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC. */ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, @@ -2055,6 +2417,7 @@ enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19, QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME = 20, QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID = 21, + QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY = 22, /* keep last */ QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, @@ -2180,6 +2543,81 @@ enum qca_wlan_vendor_acs_hw_mode { * that the device supports enhanced audio experience over WLAN feature. * @QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER: Flag indicates that the device * in AP mode supports TWT responder mode in HT and VHT modes. + * + * @QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA: Flag indicates that the device + * supports RSNE/RSNXE overriding in STA mode. Supplicant should enable + * RSN overriding elements use only when the driver indicates this feature + * flag. For BSS selection offload to the driver case, the driver shall + * strip/modify the RSN Selection element indicated in connect request + * elements or add that element if none was provided based on the BSS + * selected by the driver. + * + * @QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD: Flag indicates that the driver + * supports Unsynchronized Service Discovery to be offloaded to it. + * + * @QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC: Flag indicates that the driver + * supports preferring 6 GHz PSC channel as a primary channel in ACS + * result. + * + * @QCA_WLAN_VENDOR_FEATURE_P2P_V2: Flag indicates that the driver supports + * P2P R2 functionality (P2P R2 Discovery, Pairing, TWT power save, etc). + * + * @QCA_WLAN_VENDOR_FEATURE_PCC_MODE: Flag indicates that the driver supports + * P2P Connection Compatibility mode in which GO allows connection + * with both P2P R1 and R2 clients. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_TX_POWER_LIMIT: Flag indicates that the + * driver supports enabling TX power limit from the userspace. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_USER_SCENARIO_TO_DSI_MAPPING: Flag indicates + * that the driver supports enabling the user scenario to DSI index mapping. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_INDOOR_CH_SCC: Flag indicates driver + * support for SCC (Single Channel Concurrency) with a STA connected + * indoor channel for P2P GO, SAP, and NAN. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_DFS_CH_SCC_P2P: Flag indicates driver + * support for SCC (Single Channel Concurrency) with a STA connected + * DFS channel for P2P. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS: Flag indicates that the + * driver supports AP assisted DFS channel operation for P2P. + * When this feature is advertised, the driver stops transmitting Data + * frames on DFS channel and initiates Channel Switch Announcement on any + * P2P GO interface to connected P2P Clients present in the P2P Group + * immediately after receiving a Channel Switch Announcement from the + * connected DFS AP on its STA interface. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GO_CANCEL_ONE_SHOT_NOA: Flag indicates + * that the device supports cancellation of firmware-initiated one-shot NoA + * schedules on a P2P GO interface. This capability allows the P2P GO to cancel + * NoA early when a local STA interface (sharing the same radio) completes a + * connection or roaming operation. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA: + * Flag indicates that the device in P2P Client mode supports staying awake + * during one-shot NoA periods instead of entering sleep. This allows the + * group client to immediately receive frames when the P2P GO cancels an NoA + * early (using QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA), without + * waiting for the originally configured NoA duration to expire. + * + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY: Flag indicates that + * the driver supports PMKSA caching privacy as defined in + * IEEE P802.11bi/D4.0, 12.16.7. Userspace should enable PMKSA caching + * privacy only when both %NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION and + * this feature flag are advertised by the driver. + * + * When both the AP and the non-AP STA support PMKSA caching privacy and + * association frame encryption, the driver shall include the PMKIDSNonce + * in the (Re)Association Request frame. Upon successful non-FT + * (re)association, indicated by %NL80211_CMD_CONNECT, %NL80211_CMD_ROAM, + * or %QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH, wpa_supplicant shall + * derive a new PMKID for the PMKSA entry used during the current + * connection. The derivation uses the PMKIDANonce from the (Re)Association + * Response frame and the PMKIDSNonce from the (Re)Association Request + * frame. PMKR0Name randomization during FT reassociation will be handled + * by the driver. + * * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -2208,6 +2646,19 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST = 22, QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN = 23, QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER = 24, + QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA = 25, + QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD = 26, + QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC = 27, + QCA_WLAN_VENDOR_FEATURE_P2P_V2 = 28, + QCA_WLAN_VENDOR_FEATURE_PCC_MODE = 29, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_TX_POWER_LIMIT = 30, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_USER_SCENARIO_TO_DSI_MAPPING = 31, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_INDOOR_CH_SCC = 32, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_STA_DFS_CH_SCC_P2P = 33, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS = 34, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GO_CANCEL_ONE_SHOT_NOA = 35, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA = 36, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY = 37, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -2627,6 +3078,9 @@ enum qca_wlan_vendor_scan_priority { * when AP is operating as MLD to specify which link is requesting the * scan or which link the scan result is for. No need of this attribute * in other cases. + * @QCA_WLAN_VENDOR_ATTR_SCAN_SKIP_CHANNEL_RECENCY_PERIOD: Optional (u32). Skip + * scanning channels which are scanned recently within configured time + * (in ms). */ enum qca_wlan_vendor_attr_scan { QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, @@ -2645,6 +3099,7 @@ enum qca_wlan_vendor_attr_scan { QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY = 13, QCA_WLAN_VENDOR_ATTR_SCAN_PAD = 14, QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID = 15, + QCA_WLAN_VENDOR_ATTR_SCAN_SKIP_CHANNEL_RECENCY_PERIOD = 16, QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SCAN_MAX = QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 @@ -3115,6 +3570,11 @@ enum qca_wlan_vendor_attr_config { * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes * when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED. + * + * This attribute shall be configured along with + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL and + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE attributes + * when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED. */ QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71, @@ -3646,6 +4106,223 @@ enum qca_wlan_vendor_attr_config { */ QCA_WLAN_VENDOR_ATTR_CONFIG_FOLLOW_AP_PREFERENCE_FOR_CNDS_SELECT = 121, + /* 16-bit unsigned value to configure P2P GO beacon interval in TUs. + * This attribute is used to update the P2P GO beacon interval + * dynamically. + * + * Updating the beacon interval while the GO continues operating the BSS + * will likely interoperability issues and is not recommended to be + * used. All the values should be multiples of the minimum used value to + * minimize risk of issues. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_P2P_GO_BEACON_INTERVAL = 122, + + /* 8-bit unsigned value. Disable DFS owner capability + * 1: disable DFS owner capability in the driver. + * 0: reset DFS owner capability to the default DFS owner capability of + * the driver. + * + * If DFS owner capability is disabled, the driver will not start AP + * mode operations on DFS channels, and all the features depending on + * DFS owner functionality will not be supported. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_OWNER_DISABLE = 123, + + /* 16-bit unsigned value. For probing RSSI on other antennas, this + * attribute specifies the number of WLAN probes. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_WLAN = 124, + + /* 16-bit unsigned value. For probing RSSI on other antennas, this + * attribute specifies the number of BT probes. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_BT = 125, + + /* 16-bit unsigned value. This attribute specifies the WLAN RSSI + * threshold. The firmware will start to probe RSSI on other antenna + * if WLAN RSSI is lower than the threshold. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_WLAN_RSSI_THRESHOLD = 126, + + /* 16-bit unsigned value. This attribute specifies the BT RSSI + * threshold. The firmware will start to probe RSSI on other antenna + * if BT RSSI is lower than the threshold. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_BT_RSSI_THRESHOLD = 127, + + /* 16-bit unsigned value. This attribute specifies the WLAN RSSI + * difference. The firmware will select a better antenna if WLAN RSSI + * difference is larger than the value. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_WLAN_RSSI_DIFF = 128, + + /* 16-bit unsigned value. This attribute specifies the BT RSSI + * difference. The firmware will select a better antenna if WLAN RSSI + * difference larger than the value. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_BT_RSSI_DIFF = 129, + + /* 8-bit unsigned value to enable/disable setup link Reconfiguration + * feature support in STA mode. + * 1 - Enable + * 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SETUP_LINK_RECONFIG_SUPPORT = 130, + + /* 8-bit unsigned value to enable/disable DFS No Wait feature support + * in AP mode. + * 1 - Enable + * 0 - Disable. + * + * DFS No Wait allows AP to be started within the subset of channel + * bandwidth that does not require DFS while waiting for CAC to + * complete on the subset that requires DFS. If no radar was detected, + * switch to the full configured channel bandwidth. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_NO_WAIT_SUPPORT = 131, + + /* Nested attribute to configure the EHT EMLSR operation and EHT MLO + * links for the EMLSR operation to the driver in STA mode. This is + * runtime configuration on STA after association and the configuration + * is valid only for the current association. + * Uses enum qca_wlan_vendor_attr_emlsr_info for values. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_EMLSR_LINKS = 132, + + /* 8-bit unsigned value + * + * This setting determines the intensity of the power-saving mode. + * The level ranges from 1 to 5, where: + * 1 represents the most aggressive (maximum power saving), and + * 5 represents the least aggressive (minimal power saving). + * + * This attribute shall be configured along with + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE and + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT + * to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED mode. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL = 133, + + /* 16-bit unsigned value to configure latency tolerance value in + * milliseconds. + * + * This attribute configures latency tolerance value for + * STA and P2P Client mode. This is used by the firmware to achieve + * optimal power consumption within latency constraints. + * + * This attribute shall be configured along with + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL and + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT + * to %QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED mode. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE = 134, + + /* 8-bit unsigned value to control transmit TX power limit. When + * this flag attribute is present, the firmware shall apply the vendor + * configured TX power limit that is stored in the Board Data File + * (BDF) for all bands and all the interfaces. + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_POWER_LIMIT_ENABLE = 135, + + /* 8-bit unsigned integer to configure the driver to enable or disable + * the A-MSDU address check validation. + * 1 - Enables A-MSDU address check for the + * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC address. + * 0 - Disables A-MSDU address check for the + * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC address + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AMSDU_ADDR_CHECK_VALIDATION = 136, + + /* 8-bit unsigned value. The TX chain mask to be configured dynamically + * for transmitting the data in the 2.4 GHz band. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_2GHZ = 137, + + /* 8-bit unsigned value. The TX chain mask to be configured dynamically + * for transmitting the data in the 5/6 GHz band. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_CHAIN_MASK_5GHZ = 138, + + /* 8-bit unsigned value to enable or disable the feature to allow + * SCC with STA connected indoor channel for P2P GO, SAP, and NAN + * to the driver in STA mode. This configuration is applicable only + * when a STA interface is in connected state. + * When the STA disconnects, any P2P_GO/SAP/NAN interface present in + * SCC with STA connected indoor channel will re-evaluate its operating + * channel and either remain on the current channel or switch to other + * valid channel, depending on regulatory and underlying driver policy. + * 1 - Enable, 0 - Disable. + * + * See + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC + * for a more granular configuration of this per peer protocol. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_INDOOR_CH_SCC = 139, + + /* 8-bit unsigned value to enable or disable the feature to allow + * SCC with STA connected DFS channel for P2P GO/Client to the driver in + * STA mode. This configuration is applicable only when a STA interface + * is in connected state and can be updated/modified only in STA + * disconnected state. + * + * When enabled, behavior on a P2P Client is following: On STA + * disconnect/roam/CSA to different channel, the P2P Client + * interface operating in SCC with STA on DFS channel will disconnect + * internally. No Deauthentication frame is sent over the air to the + * P2P GO on the DFS channel. + * + * When enabled, behavior on a P2P GO is following: On STA disconnect/ + * roam/CSA to different channel, the P2P GO interface will initiate a + * Channel Switch Announcement (CSA) to switch to a valid channel + * depending on regulatory and underlying driver policy. Beacon frames + * with CSA element will be sent in compliance with regulatories after + * STA moved away from the DFS channel due to disconnect/roam/CSA. + * + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_DFS_CH_SCC_P2P = 140, + + /* + * 8-bit unsigned value to enable or disable QSH-initiated Wi-Fi scans, + * providing a mechanism to optimize power consumption. By default, this + * feature is enabled. + * + * Valid values: + * 0 - Disable + * 1 - Enable + * + * Values other than 0 or 1 are invalid and shall be rejected. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_QSH_SCAN_CTRL = 141, + + /* 8-bit bitmap to enable the feature to allow SCC with STA connected + * indoor channel per peer protocol to the driver in STA mode. This + * configuration is applicable only when a STA interface is in + * connected state. + * When the STA disconnects, any peer protocol interface present in + * SCC with STA connected indoor channel will re-evaluate its operating + * channel and either remain on the current channel or switch to other + * valid channel, depending on regulatory and underlying driver policy. + * + * Either @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_STA_INDOOR_CH_SCC attribute + * or @QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC + * attribute can be used by userspace at a time to allow indoor channel + * SCC with STA. + * + * bit 0: Setting bit0 indicates to allow SCC with STA connected indoor + * channel for P2P + * bit 1: Setting bit1 indicates to allow SCC with STA connected indoor + * channel for NAN + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ALLOW_PEER_PROTOCOL_INDOOR_CH_STA_SCC = 142, + + /* Nested attribute to configure or report Estimated Service Parameters + * (ESP). This contains nested attributes defined in + * enum qca_wlan_vendor_attr_config_esp_param. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PARAMS = 143, + /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = @@ -5309,6 +5986,69 @@ enum qca_wlan_vendor_attr_ll_stats_results { */ QCA_WLAN_VENDOR_ATTR_LL_STATS_MLO_LINK = 93, + /* Unsigned 32 bit value. It represents the number of MSDUs sent by the + * driver that were retransmitted and eventually transmitted + * successfully. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_RETRY_MSDU_CNT = 94, + + /* Unsigned 32 bit value. It represents the number of MSDUs that were + * successfully transmitted by the driver, including those that were + * retransmitted and eventually succeeded. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_SUCC_MSDU_CNT = 95, + + /* Unsigned 32 bit value. It represents the number of MSDUs that were + * handed off by the driver for transmission but were ultimately dropped + * by the firmware. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_FW_DROP_MSDU_CNT = 96, + + /* Unsigned 32 bit value. It represents the number of MSDUs that were + * intended for transmission but were dropped by the driver before being + * handed off to the firmware. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TX_DRIVER_DROP_MSDU_CNT = 97, + + /* Unsigned 32 bit value. It represents the number of MSDUs that were + * received from hardware fast receiving rings. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RX_DRIVER_MSDU_CNT = 98, + + /* Unsigned 32 bit value. It represents the number of MPDUs that were + * received from hardware fast receiving rings. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RX_DRIVER_MPDU_CNT = 99, + + /* Nested attribute for SNR distribution statistics. This attribute + * contains a dynamic SNR distribution histogram and measurement + * duration for the interface's operating channel. + * + * SNR is measured by the target hardware and reported as a distribution + * across configurable bins. The bin range and step size are determined + * by the firmware and reported via the nested attributes defined in + * enum qca_wlan_vendor_attr_ll_stats_snr. Applications must read the + * config attributes (SNR_LOWER_BOUND, SNR_UPPER_BOUND, SNR_STEP, + * BIN_COUNT) to correctly interpret the BIN_DATA array. + * + * For non-MLO connection: + * - This attribute appears at the interface level and represents the + * SNR distribution for the single operating channel. + * + * For MLO connection: + * - This attribute ONLY appears inside + * %QCA_WLAN_VENDOR_ATTR_LL_STATS_MLO_LINK + * - Each link reports its own SNR distribution for its specific + * operating channel. + * - There is no aggregated SNR distribution at the interface level, + * as SNR distributions from different channels cannot be + * meaningfully combined. + * + * This is a nested attribute containing SNR distribution attributes + * defined in enum qca_wlan_vendor_attr_ll_stats_snr. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_SNR = 100, + /* keep last */ QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX = @@ -5327,6 +6067,64 @@ enum qca_wlan_vendor_attr_ll_stats_type { QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1, }; +/** + * enum qca_wlan_vendor_attr_ll_stats_snr - SNR distribution attributes + * + * These attributes report the distribution of received SNR samples collected + * by the target hardware. The bin configuration (range and step) is dynamic and + * determined by the firmware. Applications must use the config attributes + * (SNR_LOWER_BOUND, SNR_UPPER_BOUND, SNR_STEP, BIN_COUNT) to correctly + * interpret the BIN_DATA array. + * + * Bin layout (num_bins = ((upper_bound - lower_bound) / step) + 2): + * Bin[0]: SNR < lower_bound dB + * Bin[1]: SNR in [lower_bound, lower_bound + step) dB + * Bin[i]: SNR in [lower_bound + (i-1)*step, lower_bound + i*step) dB + * Bin[num_bins-1]: SNR >= upper_bound dB + * + * Example (lower=0, upper=100, step=10, num_bins=12): + * Bin[0]=SNR<0, Bin[1]=SNR[0,10), ..., Bin[10]=SNR[90,100), Bin[11]=SNR>=100 + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MEAS_DUR_US: u32 + * Total measurement duration in microseconds during which SNR samples + * were collected on the operating channel. + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_LOWER_BOUND: u32 + * Lower bound of the SNR measurement range in dB. + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_UPPER_BOUND: u32 + * Upper bound of the SNR measurement range in dB. + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_STEP: u32 + * Step size in dB for each SNR bin. + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_COUNT: u32 + * Total number of bins = ((upper_bound - lower_bound) / step) + 2. + * The +2 accounts for out-of-bound bins (below lower_bound and above + * upper_bound). This is also the number of u32 elements in BIN_DATA. + * + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_DATA: NLA_BINARY + * Array of u32 values, BIN_COUNT elements total. Each element is the + * count of SNR samples that fell in the corresponding bin during the + * measurement period. See bin layout description above. + * The u32 values are in host byte order; no byte-order conversion is + * required by the application. + */ +enum qca_wlan_vendor_attr_ll_stats_snr { + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MEAS_DUR_US = 1, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_LOWER_BOUND = 2, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_UPPER_BOUND = 3, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_STEP = 4, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_COUNT = 5, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_BIN_DATA = 6, /* NLA_BINARY: u32[] */ + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_SNR_AFTER_LAST - 1, +}; + /** * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for * TDLS configuration to the host driver. @@ -5434,6 +6232,12 @@ enum qca_wlan_vendor_tdls_trigger_mode { * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V3_0: Select the SAR power + * limits version 3.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V4_0: Select the SAR power + * limits version 4.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V5_0: Select the SAR power + * limits version 5.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. * * This enumerates the valid set of values that may be supplied for * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of @@ -5450,6 +6254,197 @@ enum qca_vendor_attr_sar_limits_selections { QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V3_0 = 8, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V4_0 = 9, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V5_0 = 10, +}; + +/** + * enum qca_wlan_power_scenario - Represents different power scenarios that can + * be configured through %QCA_WLAN_VENDOR_ATTR_CONFIG_POWER_SCENARIO. + * + * @QCA_WLAN_POWER_SCENARIO_VOICE_CALL: Power scenario optimized for voice + * calls, balancing connectivity with reduced interference to voice + * communications. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF: Power settings for a device held + * near head with cellular radio disabled, optimizing Wi-Fi transmission power + * to minimize SAR exposure while maintaining connectivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON: Power settings for a device held + * near head with cellular radio enabled, managing combined RF exposure from + * Wi-Fi and cellular radios to comply with regulatory SAR limits. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF: Power settings for a device + * carried on body with cellular radio disabled, optimized for body-worn + * position while ensuring regulatory compliance for body SAR limits. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON: Power settings for a device carried + * on body with cellular radio enabled, balancing Wi-Fi performance with + * cellular coexistence in body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT: Power settings for a device carried on + * body with Bluetooth active, optimizing Wi-Fi and Bluetooth coexistence in + * body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT: Power settings for a device used as + * a hotspot while held near head, optimizing tethering functionality while + * managing RF exposure near the user's head. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW: Power settings for a device + * used as a hotspot near head with millimeter wave technology active, with + * special power settings for high-frequency operation near head. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT: Power settings for a device on + * body with both cellular and Bluetooth active, managing triple-radio + * coexistence in body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT: Power settings for a device used as + * a hotspot while carried on body, optimizing tethering performance in + * body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT: Power settings for a device used + * as a hotspot on body with Bluetooth active, balancing hotspot and Bluetooth + * coexistence in body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW: Power settings for a device + * used as a hotspot on body with mmWave technology, optimizing high-frequency + * hotspot operation in body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW: Power settings for a device + * used as a hotspot on body with both Bluetooth and mmWave active, managing + * complex triple-technology coexistence in body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF_UNFOLDED: Power settings for a + * foldable device in unfolded state, held near head with cellular radio + * disabled, optimizing Wi-Fi power for a larger form factor near head. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON_UNFOLDED: Power settings for a + * foldable device in unfolded state, held near head with cellular radio + * enabled, managing combined RF exposure in unfolded configuration near head. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_UNFOLDED: Power settings for a + * foldable device in unfolded state, used as hotspot near head, optimizing + * tethering performance for unfolded configuration. + * + * @QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW_UNFOLDED: Power settings for a + * foldable device in unfolded state, used as a mmWave hotspot near head, with + * special power settings for high-frequency tethering in unfolded + * configuration. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED: Power settings for a + * foldable device in unfolded state, carried on body with cellular radio + * disabled, optimizing Wi-Fi power for unfolded body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED: Power settings for a foldable + * device in unfolded state, carried on body with Bluetooth active, managing + * Wi-Fi-Bluetooth coexistence in unfolded body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED: Power settings for a + * foldable device in unfolded state, carried on body with cellular radio + * enabled, balancing Wi-Fi and cellular performance in unfolded body-worn + * position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED: Power settings for a + * foldable device in unfolded state, on body with both cellular and Bluetooth + * active, managing triple-radio coexistence in unfolded body-worn + * configuration. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_UNFOLDED: Power settings for a + * foldable device in unfolded state, used as a hotspot while on body, + * optimizing tethering performance in unfolded body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_UNFOLDED: Power settings for a + * foldable device in unfolded state, used as a hotspot on body with Bluetooth + * active, balancing hotspot and Bluetooth operation in unfolded body-worn + * position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW_UNFOLDED: Power settings for a + * foldable device in unfolded state, used as a mmWave hotspot on body, + * optimizing high-frequency tethering in unfolded body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW_UNFOLDED: Power settings for + * a foldable device in unfolded state, used as a hotspot on body with both + * Bluetooth and mmWave active, managing complex triple-technology coexistence + * in unfolded body-worn position. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_REAR_CAMERA: Power settings for a device on + * body with rear camera active, optimizing Wi-Fi power to minimize + * interference with camera operations while maintaining connectivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED_CAP: Power settings for a + * foldable device with capacitive touch in unfolded state, on body with + * cellular radio disabled, optimizing Wi-Fi power while ensuring touch + * sensitivity is maintained. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED_CAP: Power settings for a + * foldable device with capacitive touch in unfolded state, on body with + * Bluetooth active, balancing Wi-Fi-Bluetooth coexistence while maintaining + * touch sensitivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED_CAP: Power settings for a + * foldable device with capacitive touch in unfolded state, on body with + * cellular radio enabled, managing Wi-Fi-cellular coexistence while + * maintaining touch sensitivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED_CAP: Power settings for + * a foldable device with capacitive touch in unfolded state, on body with both + * cellular and Bluetooth active, managing triple-radio coexistence while + * maintaining touch sensitivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_CAP: Power settings for a device + * with capacitive touch on body with cellular radio disabled, optimizing Wi-Fi + * power in folded state while ensuring touch sensitivity is maintained. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_CAP: Power settings for a device with + * capacitive touch on body with Bluetooth active, balancing Wi-Fi-Bluetooth + * coexistence in folded state while maintaining touch sensitivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_CAP: Power settings for a device + * with capacitive touch on body with cellular radio enabled, managing + * Wi-Fi-cellular coexistence in folded state while maintaining touch + * sensitivity. + * + * @QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_CAP: Power settings for a device + * with capacitive touch on body with both cellular and Bluetooth active, + * managing triple-radio coexistence in folded state while maintaining touch + *sensitivity. + */ +enum qca_wlan_power_scenario { + QCA_WLAN_POWER_SCENARIO_VOICE_CALL = 0, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF = 1, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON = 2, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF = 3, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON = 4, + QCA_WLAN_POWER_SCENARIO_ON_BODY_BT = 5, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT = 6, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW = 7, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT = 8, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT = 9, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT = 10, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW = 11, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW = 12, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_OFF_UNFOLDED = 13, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_CELL_ON_UNFOLDED = 14, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_UNFOLDED = 15, + QCA_WLAN_POWER_SCENARIO_ON_HEAD_HOTSPOT_MMW_UNFOLDED = 16, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED = 17, + QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED = 18, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED = 19, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED = 20, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_UNFOLDED = 21, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_UNFOLDED = 22, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_MMW_UNFOLDED = 23, + QCA_WLAN_POWER_SCENARIO_ON_BODY_HOTSPOT_BT_MMW_UNFOLDED = 24, + QCA_WLAN_POWER_SCENARIO_ON_BODY_REAR_CAMERA = 25, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_UNFOLDED_CAP = 26, + QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_UNFOLDED_CAP = 27, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_UNFOLDED_CAP = 28, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_UNFOLDED_CAP = 29, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_OFF_CAP = 30, + QCA_WLAN_POWER_SCENARIO_ON_BODY_BT_CAP = 31, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_CAP = 32, + QCA_WLAN_POWER_SCENARIO_ON_BODY_CELL_ON_BT_CAP = 33, }; /** @@ -5534,6 +6529,11 @@ enum qca_vendor_attr_sar_limits_spec_modulations { * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0. * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_USER_SCENARIO: Optional (u32) + * value to indicate SAR user scenario to map them to respective + * device state indexes. Valid values of user scenarios are enumerated + * in enum %qca_wlan_power_scenario. + * * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS. */ @@ -5547,6 +6547,7 @@ enum qca_vendor_attr_sar_limits { QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_USER_SCENARIO = 9, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX = @@ -5673,11 +6674,21 @@ enum qca_vendor_attr_scan_freq_list_scheme { * @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to * trigger the scan on all the valid frequencies to find better * candidates to roam. + * + * @QCA_ROAM_SCAN_SCHEME_NEIGHBOR_REPORT_SCAN: Indicates the driver/firmware + * to trigger the scan only on the channels obtained from the neighbor + * report (e.g., neighbor request/response, RNR, BTM request). + * + * @QCA_ROAM_SCAN_SCHEME_DEFAULT: Indicates the driver/firmware to clear any + * scan scheme configured earlier and revert to the default scan scheme + * behavior. */ enum qca_roam_scan_scheme { QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0, QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1, QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2, + QCA_ROAM_SCAN_SCHEME_NEIGHBOR_REPORT_SCAN = 3, + QCA_ROAM_SCAN_SCHEME_DEFAULT = 4, }; /* @@ -5715,6 +6726,11 @@ enum qca_roam_scan_scheme { * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered * based on the scan results obtained from an external scan (not triggered * to aim roaming). + * @QCA_ROAM_TRIGGER_REASON_WTC: Set if the roam has to be triggered + * due to Wireless to Cellular BSS Transition Management (BTM) request. + * @QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY: Set if the roam has to be triggered + * due to Bluetooth connection is established when the station is connected + * in the 2.4 GHz band. * * Set the corresponding roam trigger reason bit to consider it for roam * trigger. @@ -5735,6 +6751,8 @@ enum qca_vendor_roam_triggers { QCA_ROAM_TRIGGER_REASON_IDLE = 1 << 10, QCA_ROAM_TRIGGER_REASON_TX_FAILURES = 1 << 11, QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12, + QCA_ROAM_TRIGGER_REASON_WTC = 1 << 13, + QCA_ROAM_TRIGGER_REASON_BT_ACTIVITY = 1 << 14, }; /* @@ -6033,6 +7051,11 @@ enum qca_vendor_attr_roam_candidate_selection_criteria { * qca_roam_scan_scheme. * It's an optional attribute. If this attribute is not configured, the * driver shall proceed with default behavior. + * The validity of this configuration is controlled by + * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION. When that + * flag is present, the configuration is valid only until the next + * disconnection. When that flag is absent, the configuration persists + * across disconnections. * * @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm, * signifying the RSSI threshold of the current connected AP, indicating @@ -6187,6 +7210,10 @@ enum qca_vendor_attr_roam_candidate_selection_criteria { * QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD where the configured * threshold is used only when the connected AP asks the STA to roam * through a BTM request. + * This attribute should not be used together with + * QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS in the same command. + * When both have been configured across separate commands, the most recent + * configuration takes effect and overwrites the previous one. * * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF: Unsigned 8-bit value. * This attribute signifies the RSSI difference threshold between the @@ -6214,6 +7241,112 @@ enum qca_vendor_attr_roam_candidate_selection_criteria { * better Wi-Fi bands. E.g., STA would initially connect to a 2.4 GHz BSSID * and would migrate to 5/6 GHz when it comes closer to the AP (high RSSI * for 2.4 GHz BSS). + * + * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_2P4GHZ: Unsigned 8-bit + * value. + * Represents the weightage in percentage (%) of the total score that is + * given for the roam scan candidates present on the 2.4 GHz band. The + * configuration is valid until next disconnection. If this attribute is + * not present, the existing configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_5GHZ: Unsigned 8-bit + * value. + * Represents the weightage in percentage (%) of the total score that is + * given for the roam scan candidates present on the 5 GHz band. + * The configuration is valid until next disconnection. If this attribute + * is not present, the existing configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_6GHZ: Unsigned 8-bit + * value. + * Represents the weightage in percentage (%) of the total score that is + * given for the roam scan candidates present on the 6 GHz band. + * The configuration is valid until next disconnection. If this attribute + * is not present, the existing configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_THRESHOLD_PERCENTAGE: Unsigned 8-bit + * value. + * This attribute indicates the minimum roam score difference in + * percentage (%). The roam candidate AP will be ignored if the score + * difference percentage between the roam candidate AP and the current + * connected AP is less than current connected AP score roam score delta. + * The configuration is valid until next disconnection. + * If this attribute is not present, the existing configuration shall be + * used. + * + * @QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT: Unsigned 8-bit + * value in dB. + * This attribute indicates the RSSI decrement value from the current low + * RSSI threshold for the next low RSSI roam trigger when no candidate is + * found during the current low RSSI roam trigger. This value is applicable + * only for low RSSI roam triggers. This configuration is valid until next + * disconnection. If this attribute is not present, the existing + * configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_PERIODIC_ROAM_SCAN_INTERVAL: Unsigned 32-bit + * value in seconds. + * This attribute defines the interval after which the next roam scan will + * start if the current scan finds no candidates. The scan repeats at this + * interval until a candidate is found. + * This configuration is valid until next disconnection. If this attribute + * is not present, the existing configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_MIN_DELTA_THRESHOLD: Unsigned 32-bit + * value. + * This attribute indicates the minimum roam score difference for an AP to + * be considered as a candidate. A roam candidate AP will be ignored if + * the score difference between the roam candidate AP and the current + * connected AP is less than the sum of the current connected AP score and + * the roam score delta. + * + * The configuration is valid until next disconnection. If this attribute + * is not present, the existing configuration shall be used. + * + * @QCA_ATTR_ROAM_CONTROL_CONNECTED_BSS_RECONNECT_DISALLOW_PERIOD: Unsigned + * 32-bit value. + * This attribute specifies the duration (in seconds) of the current BSS + * connection from the last successful association, after which the + * connected BSS can be considered as a roaming candidate upon receiving + * a Deauthentication or Disassociation frame from the BSS, provided no + * alternative candidate is available. The connection timer to monitor the + * disallow period should start after each successful connection. + * + * This configuration is valid until next disconnection. If this attribute + * is not present, the existing configuration shall be used. + * 0 - Always disallow roaming to the current connected BSS when a + * Deauthentication or Disassociation frame is received from the connected + * BSS. + * Other values - Disallow roaming to the current connected BSS for the + * specified duration from the last successful connection time when a + * Deauthentication or Disassociation frame is received from the connected + * BSS. + * + * @QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS: Nested attribute to + * configure low RSSI thresholds per band. The nl80211_band enum value of + * the respective band is used as the attribute type, and the data in each + * such attribute is a signed 32-bit RSSI value in dBm, signifying the low + * RSSI threshold applicable when the STA is connected on the corresponding + * band. The STA keeps monitoring the connected AP's RSSI and will start + * scanning for neighboring APs once the RSSI falls below the threshold + * configured for the current band, triggering the roam eventually. + * When QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET and + * QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT are + * configured together with this attribute, the per-band threshold + * configured here serves as the low RSSI threshold for the respective + * band, and those attributes apply to the per-band threshold accordingly. + * The configured per-band thresholds are obtained with the same attribute + * for get. Clears the per-band thresholds in the driver when specified + * with the clear command. + * This attribute should not be used together with + * QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD in the same command. + * When both have been configured across separate commands, the most recent + * configuration takes effect and overwrites the previous one. + * + * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION: Flag + * attribute to configure the lifetime of the scan scheme set through + * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME. When this flag is present, the scan + * scheme configuration is valid only until the next disconnection. When + * this flag is absent, the scan scheme configuration persists across + * disconnections. */ enum qca_vendor_attr_roam_control { QCA_ATTR_ROAM_CONTROL_ENABLE = 1, @@ -6247,6 +7380,16 @@ enum qca_vendor_attr_roam_control { QCA_ATTR_ROAM_CONTROL_CANDIDATE_ROAM_RSSI_DIFF = 29, QCA_ATTR_ROAM_CONTROL_6GHZ_CANDIDATE_ROAM_RSSI_DIFF = 30, QCA_ATTR_ROAM_CONTROL_CONNECTED_HIGH_RSSI_OFFSET = 31, + QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_2P4GHZ = 32, + QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_5GHZ = 33, + QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_WEIGHTAGE_6GHZ = 34, + QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_THRESHOLD_PERCENTAGE = 35, + QCA_ATTR_ROAM_CONTROL_CONNECTED_LOW_RSSI_THRESHOLD_DECREMENT = 36, + QCA_ATTR_ROAM_CONTROL_PERIODIC_ROAM_SCAN_INTERVAL = 37, + QCA_ATTR_ROAM_CONTROL_CANDIDATE_SCORE_MIN_DELTA_THRESHOLD = 38, + QCA_ATTR_ROAM_CONTROL_CONNECTED_BSS_RECONNECT_DISALLOW_PERIOD = 39, + QCA_ATTR_ROAM_CONTROL_PER_BAND_LOW_RSSI_THRESHOLDS = 40, + QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_VALID_UNTIL_DISCONNECTION = 41, /* keep last */ QCA_ATTR_ROAM_CONTROL_AFTER_LAST, @@ -7417,6 +8560,10 @@ enum qca_wlan_vendor_attr_external_acs_event { * for External ACS */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AFC_CAPABILITY = 15, + /* Link ID attibute (u8) is used to identify a specific link affiliated + * to an AP MLD. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LINK_ID = 16, /* keep last */ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST, @@ -8386,6 +9533,16 @@ enum qca_wlan_vendor_hang_reason { QCA_WLAN_HANG_FLUSH_LOGS = 35, /* Host wakeup because of page fault */ QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT = 36, + /* Timeout waiting for response of unified vdev connect command from the + * firmware. This command combines peer create, vdev start, peer assoc, + * and vdev up operations to optimize link switch time. + */ + QCA_WLAN_HANG_VDEV_UNIFIED_CONNECT_RESP_TIMEOUT = 37, + /* Timeout waiting for response of unified vdev disconnect command from + * the firmware. This command combines peer delete, vdev stop, and vdev + * down operations to optimize link switch time. + */ + QCA_WLAN_HANG_VDEV_UNIFIED_DISCONNECT_RESP_TIMEOUT = 38, }; /** @@ -8940,6 +10097,22 @@ enum qca_wlan_vendor_attr_ndp_params { * frames from each other. */ QCA_WLAN_VENDOR_ATTR_NDP_GTK_REQUIRED = 33, + /* Unsigned 32-bit attribute. Indicates the maximum latency of the NAN + * data packets to be transmitted/received in milliseconds. + * This attribute is optional, and it is configured for active NDP + * session of a given NDP instance ID + * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID using the command + * %QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG. + */ + QCA_WLAN_VENDOR_ATTR_NDP_MAX_LATENCY_MS = 34, + /* 32-bit unsigned value to indicate throughput in Mbps for the NDP + * data of a given NDP session. + * This attribute is optional, and it is configured for active NDP + * session of a given NDP instance ID + * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID using the command + * %QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG. + */ + QCA_WLAN_VENDOR_ATTR_NDP_TPUT = 35, /* keep last */ QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST, @@ -8987,7 +10160,14 @@ enum qca_wlan_ndp_sub_cmd { /* Command to indicate the peer about the end request being received */ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11, /* Command to indicate the peer of schedule update */ - QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12 + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12, + /* Command to update dynamic configurations of active NDP sessions. + * %QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID is a mandatory attribute and + * at least one of the attributes + * %QCA_WLAN_VENDOR_ATTR_NDP_MAX_LATENCY_MS and + * %QCA_WLAN_VENDOR_ATTR_NDP_TPUT must be configured in this command. + */ + QCA_WLAN_VENDOR_NDP_SUB_CMD_UPDATE_CONFIG = 13, }; /** @@ -9724,6 +10904,14 @@ enum qca_wlan_vendor_attr_omi_tx { * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80 * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT20: EHT20 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40: EHT40 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40PLUS: EHT40 (ext ch +1) + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40MINUS: EHT40 (ext ch -1) + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80: EHT80 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80P80: EHT 80P80 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT160: EHT160 + * @QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT320: EHT320 */ enum qca_wlan_vendor_phy_mode { QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0, @@ -9755,6 +10943,14 @@ enum qca_wlan_vendor_phy_mode { QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27, QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT20 = 29, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40 = 30, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40PLUS = 31, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT40MINUS = 32, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80 = 33, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT80P80 = 34, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT160 = 35, + QCA_WLAN_VENDOR_PHY_MODE_11BE_EHT320 = 36, }; /* Attributes for data used by @@ -10358,6 +11554,97 @@ enum qca_wlan_vendor_attr_wifi_test_config { */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_SCS_TRAFFIC_SUPPORT = 73, + /* 8-bit unsigned value to disable or not disable the channel switch + * initiation in P2P GO mode. + * 0 - Not-disable, 1 - Disable + * + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_CHAN_SWITCH_INITIATION = 74, + + /* 8-bit unsigned value. This indicates number of random PMKIDs to be + * added in the RSNE of the (Re)Association request frames. This is + * exclusively used for the scenarios where the device is used as a test + * bed device with special functionality and not recommended for + * production. Default value is zero. If the user space configures a + * non-zero value, that remains in use until the driver is unloaded or + * the user space resets the value to zero. + * + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RSNE_ADD_RANDOM_PMKIDS = 75, + + /* 8-bit unsigned value to configure Triggered SU Beamforming Feedback + * support in the EHT capabilities of an Association Request frame. + * 1-enable, 0-disable + * + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TRIG_SU_BFORMING_FEEDBACK = 76, + + /* 8-bit unsigned value to configure the extra EHT-LTFs support in the + * EHT capabilities of an Association Request frame. + * 1-enable, 0-disable + * + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_EXTRA_LTF = 77, + + /* 8-bit unsigned integer to configure the firmware to reject AP's BSS + * Transition Management (BTM) request frame by sending a BTM response + * with error status code. + * + * 1 - STA rejects AP's BTM request frame + * 0 - STA accepts AP's BTM request frame + * + * This attribute is used for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_REJECT = 78, + + /* Nested attribute to control the response of the driver upon receiving + * a BTM request from the AP. + * Uses the enum qca_wlan_vendor_attr_btm_req_resp attributes. + * This attribute is used to configure the STA. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP = 79, + + /* 8-bit unsigned value to configure Restricted TWT feature support + * within the EHT capabilities element of the Association Request frame. + * + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_RTWT_SUPPORT = 80, + + /* 8-bit unsigned value to configure BTM MLD Recommendation For Multiple + * APs Support within the Extended MLD capability of Multi-link element + * in the Association Request frame. + * + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BTM_RECOMM_MULTI_AP_SUPPORT = 81, + + /* 8-bit unsigned value to set the reserved fields within the MLE + * starting from Multi-Link Control to STA control within Per-STA + * profile. It does not set the subsequent STA Info or HT/HE/EHT + * Capabilities elements. + * This configuration is for Probe Request variant in Multi-link Probe + * Request frames and Basic variant in Association Request frames. + * + * This attribute is used to configure the testbed device. + * 1-set the reserved fields, 0-default behavior + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_SET_MLE_RESERVED_FIELDS = 82, + + /* 8-bit unsigned value to configure the STA interface to operate in + * 20 MHz only mode. + * + * This attribute is used to configure the testbed device. + * 1-set the 20 MHz only mode, 0-default behavior + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_20MHZ_ONLY_STA = 83, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = @@ -10439,6 +11726,12 @@ enum qca_wlan_vendor_attr_wifi_test_config { * related information for example TWT required bit in AP capabilities etc. * The reason for the notification is sent using * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS. + * + * @QCA_WLAN_TWT_EARLY_TERMINATION_IND: Indicate early termination for the + * ongoing SP. Required parameters are configured through + * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refer the enum + * qca_wlan_vendor_attr_twt_early_termination_ind. + * Valid only after the TWT session is successfully setup. */ enum qca_wlan_twt_operation { QCA_WLAN_TWT_SET = 0, @@ -10453,6 +11746,7 @@ enum qca_wlan_twt_operation { QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9, QCA_WLAN_TWT_SET_PARAM = 10, QCA_WLAN_TWT_NOTIFY = 11, + QCA_WLAN_TWT_EARLY_TERMINATION_IND = 12, }; /** @@ -10467,8 +11761,9 @@ enum qca_wlan_twt_operation { * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the * parameters configured for TWT. These parameters are represented by * enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume, - * enum qca_wlan_vendor_attr_twt_set_param, or - * enum qca_wlan_vendor_attr_twt_stats based on the operation. + * enum qca_wlan_vendor_attr_twt_set_param, enum qca_wlan_vendor_attr_twt_stats, + * or enum qca_wlan_vendor_attr_twt_early_termination_ind based on the + * operation. * * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS: Size is u8, mandatory when * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION is set to QCA_WLAN_TWT_NOTIFY. @@ -10650,12 +11945,49 @@ enum qca_wlan_twt_setup_state { QCA_WLAN_TWT_SETUP_STATE_SUSPEND = 2, }; +/** + * enum qca_wlan_twt_session_suspendable: The values used with + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE. + * + * @QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE: TWT session cannot be suspended. + * @QCA_WLAN_TWT_SESSION_SUSPENDABLE: TWT session can be suspended. + */ +enum qca_wlan_twt_session_suspendable { + QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE = 0, + QCA_WLAN_TWT_SESSION_SUSPENDABLE = 1, +}; + +/** + * enum qca_wlan_twt_session_updatable: Define the values used with + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE. + * + * @QCA_WLAN_TWT_SESSION_NOT_UPDATABLE: TWT session cannot be updated. + * @QCA_WLAN_TWT_SESSION_UPDATABLE: TWT session can be updated. + */ +enum qca_wlan_twt_session_updatable { + QCA_WLAN_TWT_SESSION_NOT_UPDATABLE = 0, + QCA_WLAN_TWT_SESSION_UPDATABLE = 1, +}; + +/** + * enum qca_wlan_twt_session_implicit: Define the values used with + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT. + * + * @QCA_WLAN_TWT_SESSION_NOT_IMPLICIT: TWT session cannot be implicit. + * @QCA_WLAN_TWT_SESSION_IMPLICIT: TWT session can be implicit. + */ +enum qca_wlan_twt_session_implicit { + QCA_WLAN_TWT_SESSION_NOT_IMPLICIT = 0, + QCA_WLAN_TWT_SESSION_IMPLICIT = 1, +}; + /** * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for * TWT (Target Wake Time) setup request. These attributes are sent as part of * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by - * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT. + * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT and + * %QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ. * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute. * Disable (flag attribute not present) - Individual TWT @@ -10841,9 +12173,10 @@ enum qca_wlan_twt_setup_state { * The Broadcast TWT Recommendation subfield contains a value that indicates * recommendations on the types of frames that are transmitted by TWT * scheduled STAs and scheduling AP during the broadcast TWT SP. - * The allowed values are 0 - 3. + * The allowed values are 0 - 4. * This parameter is used for * 1. TWT SET Request + * 2. R-TWT SET Request * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8) * This attribute is used to configure Broadcast TWT Persistence. @@ -10878,6 +12211,38 @@ enum qca_wlan_twt_setup_state { * * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD: Attribute used for padding for 64-bit * alignment. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE: Optional (u8) + * This attribute indicates whether the TWT session being negotiated can be + * suspended. + * Refers the enum qca_wlan_twt_session_suspendable. + * This parameter is used for + * 1. TWT SET Response + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP: Optional (u32) + * This attribute is used to configure downlink TIDs for R-TWT scheduling. + * This attribute only applicable when requesting R-TWT schedules. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP: Optional (u32) + * This attribute is used to configure uplink TIDs for R-TWT scheduling. + * This attribute only applicable when requesting R-TWT schedules. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE: Optional (u8) + * This attribute indicates whether the parameters of the TWT session being + * negotiated (like wake interval, wake duration, etc.) can be updated after + * session setup. + * Refers the enum qca_wlan_twt_session_updatable. + * This parameter is used for + * 1. TWT SET Response + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT: Optional (u8) + * This attribute indicates whether the TWT session being negotiated is + * an implicit TWT, where the requesting STA calculates the start time of the + * next TWT service period, or an explicit TWT, where the responding STA + * calculates the start time of the next TWT service period. + * Refers the enum qca_wlan_twt_session_implicit. + * This parameter is used for + * 1. TWT SET Response */ enum qca_wlan_vendor_attr_twt_setup { QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0, @@ -10915,6 +12280,11 @@ enum qca_wlan_vendor_attr_twt_setup { QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD = 27, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE = 28, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP = 29, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP = 30, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE = 31, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT = 32, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST, @@ -10962,7 +12332,9 @@ enum qca_wlan_vendor_attr_twt_setup { * @QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS: FW rejected the TWT * setup request due to channel switch in progress. * @QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS: FW rejected the TWT setup - * request due to scan in progress. + * request due to scan in progress. This is also used in TWT_TERMINATE + * notification from the driver to indicate TWT session termination is due to + * scan in progress. * QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE: The driver requested to * terminate an existing TWT session on power save exit request from userspace. * Used on the TWT_TERMINATE notification from the driver/firmware. @@ -10970,6 +12342,22 @@ enum qca_wlan_vendor_attr_twt_setup { * required bit in its capabilities. * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED: The peer has cleared * the TWT required bit(1->0) in its capabilities. + * @QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE: FW terminated + * the TWT session due to more than one MLO link becoming active. Used on the + * TWT_TERMINATE notification from the driver/firmware. + * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED: TWT session already in + * resumed state. Used on the TWT_RESUME notification from the driver/firmware. + * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED: Requested TWT operation is + * rejected by the peer. Used on the TWT_SET notification from the + * driver/firmware. + * @QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT: Requested TWT operation has timed out. + * Used on the TWT_SET, TWT_TERMINATE notification from the driver/firmware. + * @QCA_WLAN_VENDOR_TWT_STATUS_CHAN_SWITCH_24GHZ: FW terminated the TWT + * session due to channel switch triggered to a 2.4 GHz channel. Used on the + * TWT_TERMINATE notification from the driver. + * @QCA_WLAN_VENDOR_TWT_STATUS_MLO_LINK_INACTIVE: FW terminated the TWT session + * due to the link inactivation triggered on the TWT session established + * link. Used on the TWT_TERMINATE notification from the driver. */ enum qca_wlan_vendor_twt_status { QCA_WLAN_VENDOR_TWT_STATUS_OK = 0, @@ -10997,6 +12385,12 @@ enum qca_wlan_vendor_twt_status { QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE = 22, QCA_WLAN_VENDOR_TWT_STATUS_TWT_REQUIRED = 23, QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED = 24, + QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE = 25, + QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED = 26, + QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED = 27, + QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT = 28, + QCA_WLAN_VENDOR_TWT_STATUS_CHAN_SWITCH_24GHZ = 29, + QCA_WLAN_VENDOR_TWT_STATUS_MLO_LINK_INACTIVE = 30, }; /** @@ -11070,7 +12464,7 @@ enum qca_wlan_vendor_attr_twt_resume { * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE: Required (u32) * This attribute represents the next TWT subfield size. * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits, - * and 4 for 64 bits. + * and 3 for 64 bits. * * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR: 6-byte MAC address * Represents the MAC address of the peer to which TWT Suspend and Resume is @@ -11179,6 +12573,16 @@ enum qca_wlan_vendor_attr_twt_nudge { * Status of the TWT GET STATISTICS request. * This contains status values in enum qca_wlan_vendor_twt_status * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US: Optional (u32) + * Average of duration of the early terminated TWT service periods + * in micro seconds. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT: Optional (u32) + * Number of early terminated TWT service periods observed over + * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. + * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware. */ enum qca_wlan_vendor_attr_twt_stats { QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0, @@ -11194,6 +12598,8 @@ enum qca_wlan_vendor_attr_twt_stats { QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10, QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11, QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US = 13, + QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT = 14, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST, @@ -11251,12 +12657,28 @@ enum qca_wlan_twt_capa { * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16). * Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in * enum qca_wlan_twt_capa. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL: (u32). + * Minimum tolerance limit of wake interval parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL: (u32). + * Maximum tolerance limit of wake interval parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION: (u32). + * Minimum tolerance limit of wake duration parameter in microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION: (u32). + * Maximum tolerance limit of wake duration parameter in microseconds. */ enum qca_wlan_vendor_attr_twt_capability { QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2, QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL = 4, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL = 5, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION = 6, + QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST, @@ -11325,11 +12747,58 @@ enum qca_wlan_vendor_twt_setup_resp_type { * which the STA may accept. * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any * alternate parameters than the requested ones. + * @QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING: TWT responding STA suggests TWT + * group parameters that are different from the suggested or demanded TWT + * parameters of the TWT requesting STA. + * @QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT: TWT responding STA or TWT scheduling + * AP accepts the TWT request with the TWT parameters indicated in the TWT + * element transmitted by the TWT requesting STA or TWT scheduled STA. + * @QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT: TWT responding STA or TWT + * scheduling AP suggests TWT parameters that are different from those suggested + * by the TWT requesting STA or TWT scheduled STA. + * @QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT: TWT responding STA or TWT scheduling + * dictates TWT parameters that are different from those suggested by the + * TWT requesting STA or TWT scheduled STA. + * @QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT: A TWT responding STA or TWT scheduling + * AP rejects setup or terminates an existing broadcast TWT, or a TWT scheduled + * STA terminates its membership in a broadcast TWT. */ enum qca_wlan_vendor_twt_setup_req_type { QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1, QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2, QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3, + QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING = 4, + QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT = 5, + QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT = 6, + QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT = 7, + QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT = 8, +}; + +/** + * enum qca_wlan_vendor_attr_twt_early_termination_ind: Represents attributes + * for TWT early SP termination operation. Attributes are sent as part of + * %QCA_WLAN_TWT_EARLY_TERMINATION_IND operation. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_ID: Required (u8) + * Flow ID is the unique identifier for the TWT session. + * Flow ID values from 0 to 254 represent a single TWT session. + * Flow ID value of 255 represents all TWT sessions. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_PEER_MAC_ADDR: Optional, 6-byte + * MAC address. + * Represents the MAC address of the peer for which the TWT Flow ID is + * provided. This is a mandatory attribute in AP mode to represent the + * respective client. + */ +enum qca_wlan_vendor_attr_twt_early_termination_ind { + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_ID = 1, + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_PEER_MAC_ADDR = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_EARLY_TERM_FLOW_AFTER_LAST - 1, }; /** @@ -11441,6 +12910,92 @@ enum qca_wlan_vendor_cfr_capture_type { QCA_WLAN_VENDOR_CFR_NDPA_NDP_ALL = 5, }; +/** + * enum qca_wlan_vendor_cfr_ltf_type - CSI/LTF type for CFR events. + * + * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_HT: CSI from HT-LTF (IEEE 802.11n) + * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_VHT: CSI from VHT-LTF (IEEE 802.11ac) + * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_HE: CSI from HE-LTF (IEEE 802.11ax) + * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_EHT: CSI from EHT-LTF (IEEE 802.11be) + * @QCA_WLAN_VENDOR_CFR_LTF_TYPE_LEGACY: CSI from Legacy (IEEE 802.11a/g) + */ +enum qca_wlan_vendor_cfr_ltf_type { + QCA_WLAN_VENDOR_CFR_LTF_TYPE_HT = 0, + QCA_WLAN_VENDOR_CFR_LTF_TYPE_VHT = 1, + QCA_WLAN_VENDOR_CFR_LTF_TYPE_HE = 2, + QCA_WLAN_VENDOR_CFR_LTF_TYPE_EHT = 3, + QCA_WLAN_VENDOR_CFR_LTF_TYPE_LEGACY = 4, +}; + +/** + * enum qca_wlan_vendor_chip_id - WLAN Chip identifier code. + */ +enum qca_wlan_vendor_chip_id { + QCA_WLAN_VENDOR_CHIP_ID_WCN3680B = 0, + QCA_WLAN_VENDOR_CHIP_ID_WCN3910 = 1, + QCA_WLAN_VENDOR_CHIP_ID_WCN3950 = 2, + QCA_WLAN_VENDOR_CHIP_ID_WCN3988 = 3, + QCA_WLAN_VENDOR_CHIP_ID_WCN3991 = 4, + QCA_WLAN_VENDOR_CHIP_ID_WCN3998 = 5, + QCA_WLAN_VENDOR_CHIP_ID_QCA639x = 6, + QCA_WLAN_VENDOR_CHIP_ID_WCN685x = 7, + QCA_WLAN_VENDOR_CHIP_ID_WCN6750 = 8, + QCA_WLAN_VENDOR_CHIP_ID_WCN785x = 9, + QCA_WLAN_VENDOR_CHIP_ID_WCN7750 = 10, + QCA_WLAN_VENDOR_CHIP_ID_WCN7950 = 11, + QCA_WLAN_VENDOR_CHIP_ID_WCN7880 = 12, + QCA_WLAN_VENDOR_CHIP_ID_WCN7881 = 13, + QCA_WLAN_VENDOR_CHIP_ID_WCN8850 = 14, +}; + +/** + * qca_wlan_vendor_cfr_stop_reason - Reason codes for CFR stop indication used + * by attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_UNSPEC: Unspecified or unknown reason. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_COMPLETED: CFR collection completed + * successfully as planned. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_USER_ABORTED: CFR collection stopped + * explicitly upon userspace abort/stop request. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_PEER_UNAVAILABLE: Peer disconnected or + * unavailable. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_CONCURRENCY: Stopped to accommodate a + * higher-priority concurrency operation. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_ROAMING: Stopped due to roaming activity. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_FW_ERROR: Stopped because of a firmware or + * internal error. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_CHANNEL_SWITCHED: Channel changed (CSA). + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_SWITCHED: Stopped due to MLO link + * switch. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_RECONFIG: Stopped due to MLO link + * reconfiguration. + * + * @QCA_WLAN_VENDOR_CFR_STOP_REASON_RECOVERY: Stopped as part of driver + * recovery, restart, or assert handling. + */ +enum qca_wlan_vendor_cfr_stop_reason { + QCA_WLAN_VENDOR_CFR_STOP_REASON_UNSPEC = 0, + QCA_WLAN_VENDOR_CFR_STOP_REASON_COMPLETED = 1, + QCA_WLAN_VENDOR_CFR_STOP_REASON_USER_ABORTED = 2, + QCA_WLAN_VENDOR_CFR_STOP_REASON_PEER_UNAVAILABLE = 3, + QCA_WLAN_VENDOR_CFR_STOP_REASON_CONCURRENCY = 4, + QCA_WLAN_VENDOR_CFR_STOP_REASON_ROAMING = 5, + QCA_WLAN_VENDOR_CFR_STOP_REASON_FW_ERROR = 6, + QCA_WLAN_VENDOR_CFR_STOP_REASON_CHANNEL_SWITCHED = 7, + QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_SWITCHED = 8, + QCA_WLAN_VENDOR_CFR_STOP_REASON_LINK_RECONFIG = 9, + QCA_WLAN_VENDOR_CFR_STOP_REASON_RECOVERY = 10, +}; + /** * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer @@ -11448,7 +13003,8 @@ enum qca_wlan_vendor_cfr_capture_type { * capture. * * @QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR: Optional (6-byte MAC address) - * MAC address of peer. This is for CFR version 1 only. + * MAC address of peer. This is for CFR version 1 and in peer CFR event for + * version 3. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE: Required (flag) * Enable peer CFR capture. This attribute is mandatory to enable peer CFR @@ -11456,24 +13012,26 @@ enum qca_wlan_vendor_cfr_capture_type { * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH: Optional (u8) * BW of measurement, attribute uses the values in enum nl80211_chan_width - * Supported values: 20, 40, 80, 80+80, 160. + * Supported bandwidth: 20, 40, 80, 80+80, 160, 320. * Note that all targets may not support all bandwidths. - * This attribute is mandatory for version 1 if attribute + * This attribute is mandatory for version 1 and version 3 if attribute * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + * This attribute is also applicable for peer CFR event with CFR data format + * version 3 * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY: Optional (u32) * Periodicity of CFR measurement in milliseconds. * Periodicity should be a multiple of Base timer. * Current Base timer value supported is 10 milliseconds (default). * 0 for one shot capture. - * This attribute is mandatory for version 1 if attribute - * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + * This attribute is mandatory for version 1 and optional for version 3 if + * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD: Optional (u8) * Method used to capture Channel Frequency Response. * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method. - * This attribute is mandatory for version 1 if attribute - * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + * This attribute is mandatory for version 1 and optional for version 3 if + * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. * * @QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE: Optional (flag) * Enable periodic CFR capture. @@ -11481,9 +13039,9 @@ enum qca_wlan_vendor_cfr_capture_type { * If this attribute is not present, periodic CFR capture is disabled. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION: Optional (u8) - * Value is 1 or 2 since there are two versions of CFR capture. Two versions - * can't be enabled at same time. This attribute is mandatory if target - * support both versions and use one of them. + * Value is 1, 2, or 3 since there are three versions of CFR capture. + * Only one version can be enabled at a time. This attribute is mandatory + * if the target supports multiple versions and uses one of the versions. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP: Optional (u32) * This attribute is mandatory for version 2 if @@ -11538,7 +13096,7 @@ enum qca_wlan_vendor_cfr_capture_type { * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA: Optional (6-byte MAC address) * Transmitter address which is used to filter frames. This MAC address takes * effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK. This is for CFR - * version 2 only. + * version 2 and version 3. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA: Optional (6-byte MAC address) * Receiver address which is used to filter frames. This MAC address takes @@ -11547,7 +13105,7 @@ enum qca_wlan_vendor_cfr_capture_type { * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK: Optional (6-byte MAC address) * Mask of transmitter address which is used to filter frames. This is for CFR - * version 2 only. + * version 2 and version 3. * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK: Optional (6-byte MAC address) * Mask of receiver address which is used to filter frames. This is for CFR @@ -11555,8 +13113,9 @@ enum qca_wlan_vendor_cfr_capture_type { * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS: Optional (u32) * Indicates frames with a specific NSS will be filtered for CFR capture. - * This is for CFR version 2 only. This is a bitmask. Bits 7:0 request CFR - * capture to be done for frames matching the NSS specified within this bitmask. + * This is for CFR version 2 and version 3. This is a bitmask. Bits 7:0 request + * CFR capture to be done for frames matching the NSS specified within this + * bitmask. * Bits 31:8 are reserved for future use. Bits 7:0 map to NSS: * bit 0 : NSS 1 * bit 1 : NSS 2 @@ -11565,11 +13124,10 @@ enum qca_wlan_vendor_cfr_capture_type { * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW: Optional (u32) * Indicates frames with a specific bandwidth will be filtered for CFR capture. - * This is for CFR version 2 only. This is a bitmask. Bits 4:0 request CFR - * capture to be done for frames matching the bandwidths specified within this - * bitmask. Bits 31:5 are reserved for future use. Bits 4:0 map to bandwidth - * numerated in enum nl80211_band (although not all bands may be supported - * by a given device). + * This is for CFR version 2 only. This is a bitmask where each bit N + * corresponds to BIT(N), with N being a value from enum nl80211_chan_width. + * Setting a bit requests CFR capture for frames matching that bandwidth + * (although not all bandwidths may be supported by a given device). * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER: Optional (u32) * Management frames matching the subtype filter categories will be filtered in @@ -11613,6 +13171,132 @@ enum qca_wlan_vendor_cfr_capture_type { * * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD: Attribute used for padding for 64-bit * alignment. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREQ: Optional (u32) + * Frequency (in MHz) used for CFR capture. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_TYPE: Optional (u8) + * IEEE 802.11 WLAN frame type configuration. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SUBTYPE: Optional (u8) + * IEEE 802.11 WLAN frame subtype configuration. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL: Optional (u32) + * Interval (in milliseconds) at which CSI reports are generated + * and delivered to the user. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_OUI: Optional (u8 array, 3 or + * 5 bytes) Organizationally Unique Identifier (OUI) for the CFR data format. + * The OUI is assigned by IEEE and uniquely identifies the vendor or + * organization. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_VERSION: Optional (u8) + * Version of the CFR data format associated with the specified OUI. + * If not set, version 1 is used by default. + * OUI and version together define the vendor-specific format to interpret CFR + * data. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_TIMESTAMP_US: Optional (u64) + * Timestamp (in microseconds) indicating when the packet was received, + * based on the receiver's internal clock. This value represents the local + * timing reference for the captured frame. + * Applicable for peer CFR event and CFR data format version 3 only. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INFO: Optional (nested) + * Nested attribute containing one or more antenna entries. Each entry is a + * nested attribute that includes: + * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX, + * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI, + * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC, + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX: Optional (u8) + * Index of the receiving antenna corresponding to each entry. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI: Optional (s8) + * RSSI value (in dBm) measured on the antenna specified by + * %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC: Optional (u8) + * AGC (Automatic Gain Control) value in dB for the antenna + * specified by %QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_IS_LAST_REPORT: Optional (flag) + * Indicates that this event is the last entry in the current CSI reporting + * period. This flag is used when %QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL + * is configured to help the receiver determine the end of a CSI report batch. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SEQUENCE_NUMBER: Optional (u16) + * Sequence number of the IEEE 802.11 frame (without the fragment number) that + * triggered the CSI capture. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CHIP_ID: Optional (u16) + * Vendor-specific chip identifier of the reporting device. Values are defined + * in enum qca_wlan_vendor_chip_id. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TSF: Optional (u64) + * TSF (Timing Synchronization Function) timestamp in microseconds. + * This value is derived at the receiver of the frame by aligning with + * the TSF provided by the AP through periodic Beacon or Probe Response frames. + * Applicable for peer CFR events using CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CFO: Optional (s16) + * Carrier Frequency Offset (in 0.01 ppm) indicating frequency drift between the + * transmitter and receiver for the captured frame. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_LTF_TYPE: Optional (u8) + * CSI LTF type for the CFR event. Values are defined by + * enum qca_wlan_vendor_cfr_ltf_type. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_NUM_SPATIAL_STREAMS: Optional (u8) + * Number of spatial streams used to capture the CFR data. + * Applicable for peer CFR event with CFR data format version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON: Optional (u32) + * This attribute signifies that CFR collection for a peer has been stopped + * and provides the corresponding reason code. The reason codes are defined + * in enum qca_wlan_vendor_cfr_stop_reason. + * Applicable for peer CFR events when CFR data format version is 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FIXED_AGC: Optional (flag) + * This attribute indicates that the Wi-Fi firmware should fix RX antenna gain + * during CSI capturing. This is for CFR version 2 and version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_ONLY_LAST_FRAME: Optional (flag) + * Report only the last captured frame per MAC address in each reporting + * interval configured by %QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL, + * i.e., one report per interval per MAC address. When this flag is not + * included, all captured frames in the reporting interval are reported. + * Applicable only for CFR version 3. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_TOTAL_LEN: Optional (u32) + * Total length (in bytes) of the full CFR data being delivered across + * multiple fragmented vendor events. Present in every fragment when + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA is bifurcated. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_OFFSET: Optional (u32) + * Byte offset of the QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA payload + * within the full CFR data buffer. Present in every fragment when + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA is bifurcated. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_IS_LAST_FRAG: Optional (flag) + * When present, indicates that this vendor event carries the final fragment + * of a bifurcated QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA payload. + * */ enum qca_wlan_vendor_peer_cfr_capture_attr { QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0, @@ -11645,6 +13329,30 @@ enum qca_wlan_vendor_peer_cfr_capture_attr { QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID = 27, QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA = 28, QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD = 29, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREQ = 30, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_TYPE = 31, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SUBTYPE = 32, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_INTERVAL = 33, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_OUI = 34, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_FORMAT_VERSION = 35, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_TIMESTAMP_US = 36, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INFO = 37, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_INDEX = 38, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_RSSI = 39, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RX_ANTENNA_AGC = 40, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_IS_LAST_REPORT = 41, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FRAME_SEQUENCE_NUMBER = 42, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CHIP_ID = 43, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TSF = 44, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CFO = 45, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CSI_LTF_TYPE = 46, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_NUM_SPATIAL_STREAMS = 47, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_STOP_REASON = 48, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FIXED_AGC = 49, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_REPORT_ONLY_LAST_FRAME = 50, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_TOTAL_LEN = 51, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_OFFSET = 52, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA_IS_LAST_FRAG = 53, /* Keep last */ QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST, @@ -12411,6 +14119,12 @@ enum qca_wlan_vendor_attr_add_sta_node_params { */ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_IS_ML = 3, + /* + * This is u8 attribute used to identify a specific link affiliated + * to an AP MLD. + */ + QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_LINK_ID = 4, + /* keep last */ QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX = @@ -12421,18 +14135,22 @@ enum qca_wlan_vendor_attr_add_sta_node_params { * enum qca_btc_chain_mode - Specifies BT coex chain mode. * This enum defines the valid set of values of BT coex chain mode. * These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of - * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE. + * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE or + * %QCA_NL80211_VENDOR_SUBCMD_GET_COEX_STATS. * * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4 GHz are shared. * @QCA_BTC_CHAIN_SEPARATED_HYBRID: chains of BT and WLAN 2.4 GHz are * separated, hybrid mode. * @QCA_BTC_CHAIN_SEPARATED_FDD: chains of BT and WLAN 2.4 GHz are * separated, fixed FDD mode. + * @QCA_BTC_CHAIN_DBAM: DBAM (Dedicated BT Antenna Mode) for BT/WLAN + * coexistence. */ enum qca_btc_chain_mode { QCA_BTC_CHAIN_SHARED = 0, QCA_BTC_CHAIN_SEPARATED_HYBRID = 1, QCA_BTC_CHAIN_SEPARATED_FDD = 2, + QCA_BTC_CHAIN_DBAM = 3, }; /* deprecated legacy name */ @@ -12462,6 +14180,99 @@ enum qca_vendor_attr_btc_chain_mode { QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST - 1, }; +/** + * enum qca_btc_policy - Specifies Bluetooth coexistence (BTC) policy. + * This enum defines the valid set of BTC policy values. + * These values are used by attribute %QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY. + * + * @QCA_BTC_POLICY_FREE_FRUN: "Free run" mode for BTC. WLAN operates without + * strict BTC constraints. + * + * @QCA_BTC_POLICY_OCS: "OCS" (Off Channel Scheme) mode for BTC. Specific + * coexistence mechanisms are enforced to manage interference between Bluetooth + * and WLAN. + * + * @QCA_BTC_POLICY_NO_COEX: "No Coex" mode. No active coexistence conflict + * between WLAN and Bluetooth (e.g., when operating in different bands). + * + * @QCA_BTC_POLICY_INVALID: Invalid policy value. Used for error handling or + * uninitialized state. + */ +enum qca_btc_policy { + QCA_BTC_POLICY_FREE_FRUN = 0, + QCA_BTC_POLICY_OCS = 1, + QCA_BTC_POLICY_NO_COEX = 2, + QCA_BTC_POLICY_INVALID = 255 +}; + +/** + * enum qca_coex_policy_type: Specifies coexistence policy for non-BT + * technologies like MWS and UWB. + * + * @QCA_COEX_POLICY_NO_COEX: No active coexistence conflict between WLAN and + * other technologies (e.g., when operating in different bands). + * + * @QCA_COEX_POLICY_COEX: Coexistence mechanisms are enforced to manage + * interference between WLAN and other technologies (e.g., UWB, MWS). + * + * @QCA_COEX_POLICY_INVALID: Invalid policy value. Used for error handling. + */ +enum qca_coex_policy_type { + QCA_COEX_POLICY_NO_COEX = 0, + QCA_COEX_POLICY_COEX = 1, + QCA_COEX_POLICY_INVALID = 255 +}; + +/** + * enum qca_wlan_vendor_attr_coex_stats - Coexistence statistics attributes + * @QCA_WLAN_VENDOR_ATTR_COEX_STATS_ARRAY_INDEX: Nested attribute containing + * the following attributes: + * %QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID + * %QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY + * %QCA_VENDOR_ATTR_BTC_CHAIN_MODE + * %QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY + * %QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY + * %QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD + * %QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT + * %QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT + * @QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID: 8-bit unsigned value to indicate PDEV ID. + * @QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY: 8-bit unsigned value to current BTC + * policy. The policies are defined in enum qca_btc_policy. + * @QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY: 8-bit unsigned value to current MWS + * (Modem Wireless System) policy. MWS refers to modem coexistence where modem + * and WLAN may share frequency bands. The policies are defined in + * enum qca_coex_policy_type. + * @QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY: 8-bit unsigned value to current UWB + * (Ultra-Wideband) policy. UWB is a ranging technology that may conflict with + * WLAN if operating in the same band. The policies are defined in + * enum qca_coex_policy_type. + * @QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD: A 32-bit unsigned value + * specifying the monitoring interval in milliseconds. This defines the time + * window over which statistics are collected. + * @QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT: 8-bit unsigned value to + * indicate the percentage of OCS (Off Channel Scheduling) policy active period + * in %QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD. + * @QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT: 8-bit unsigned value to + * indicate the percentage OCS policy active period given to non-WLAN + * technology. + */ +enum qca_wlan_vendor_attr_coex_stats { + QCA_WLAN_VENDOR_ATTR_COEX_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_COEX_STATS_ARRAY_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_COEX_PDEV_ID = 2, + QCA_WLAN_VENDOR_ATTR_COEX_BTC_POLICY = 3, + QCA_WLAN_VENDOR_ATTR_COEX_MWS_POLICY = 4, + QCA_WLAN_VENDOR_ATTR_COEX_UWB_POLICY = 5, + QCA_WLAN_VENDOR_ATTR_COEX_MONITORING_PERIOD = 6, + QCA_WLAN_VENDOR_ATTR_COEX_OCS_ACTIVE_PERCENT = 7, + QCA_WLAN_VENDOR_ATTR_COEX_OCS_NON_WLAN_PERCENT = 8, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_COEX_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_COEX_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_COEX_STATS_AFTER_LAST - 1, +}; + /** * enum qca_vendor_wlan_sta_flags - Station feature flags * Bits will be set to 1 if the corresponding features are enabled. @@ -12799,6 +14610,63 @@ enum qca_vendor_wlan_sta_guard_interval { * used in STA mode. This represents the number of MSDU packets * (unicast/multicast/broadcast) transmitted/received with each NSS value. See * enum qca_wlan_vendor_attr_nss_pkt. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT: Array of nested attributes, + * used in STA mode. This represents the number of PPDUs + * (unicast/multicast/broadcast) transmitted/received with each MCS value. See + * enum qca_wlan_vendor_attr_mcs_pkt. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT: Array of nested attributes, + * used in STA mode. This represents the number of PPDUs + * (unicast/multicast/broadcast) transmitted/received on each bandwidth value. + * See enum qca_wlan_vendor_attr_bw_pkt. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT: Array of nested attributes + * representing the CCA statistics for the affiliated AP(s) in STA mode. This + * uses attributes defined in enum qca_wlan_vendor_attr_cca_stat. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT: Array of nested + * attributes representing the beacon miss data for the affiliated AP(s) in STA + * mode. This uses attributes defined in + * enum qca_wlan_vendor_attr_beacon_miss_stat. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS: Array of nested attributes and + * applicable only in STA mode. Contains per-link statistics for ML connection. + * Each link is identified by %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID: u8 attribute and applicable + * only in STA mode. MLO link ID for which the statistics are being reported. + * Used only for ML connections. This is mandatory attribute inside + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS and represents the MLO link ID + * of the nested attribute set. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_NSS: u8 attribute and applicable only + * in STA mode. This represents the current operating Tx NSS with the + * affiliated AP. For non-ML connection, this attribute will be reported + * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection, + * this attribute will be reported inside + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_NSS: u8 attribute and applicable only + * in STA mode. This represents the current operating Rx NSS with the + * affiliated AP. For non-ML connection, this attribute will be reported + * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection, + * this attribute will be reported inside + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_CHAINS: u8 attribute and applicable + * only in STA mode. This represents the current operating Tx chains with the + * affiliated AP. For non-ML connection, this attribute will be reported + * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection, + * this attribute will be reported inside + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link. + * + * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_CHAINS: u8 attribute and applicable + * only in STA mode. This represents the current operating Rx chains with the + * affiliated AP. For non-ML connection, this attribute will be reported + * outside %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS. For ML connection, + * this attribute will be reported inside + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS for each MLO link. */ enum qca_wlan_vendor_attr_get_sta_info { QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0, @@ -12857,6 +14725,16 @@ enum qca_wlan_vendor_attr_get_sta_info { QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD = 53, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER = 54, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT = 55, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT = 56, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT = 57, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT = 58, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT = 59, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINKS = 60, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MLO_LINK_ID = 61, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_NSS = 62, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_NSS = 63, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_CHAINS = 64, + QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_CHAINS = 65, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST, @@ -12946,6 +14824,21 @@ enum qca_wlan_vendor_attr_update_sta_info { * able to move to the channel mentioned by the AP in CSA. * * @QCA_DISCONNECT_REASON_USER_TRIGGERED: User triggered disconnection. + * + * @QCA_DISCONNECT_REASON_KEY_FAIL_TO_INSTALL: Disconnection triggered because + * the encryption keys couldn't be installed during the secure handshake + * process. + * + * @QCA_DISCONNECT_REASON_FW_TRIGGERED_LINK_SWITCH: Firmware initiated a switch + * from the active link to a standby link, resulting in disconnection from the + * current link. + * + * @QCA_DISCONNECT_REASON_HOST_TRIGGERED_LINK_DELETE: Host system dynamically + * removed a link, causing the associated connection to drop. + * + * @QCA_DISCONNECT_REASON_HOST_OCI_MISMATCH: Disconnect occurred due to a + * mismatch in Operating Channel Information between the AP and STA, violating + * channel coordination. */ enum qca_disconnect_reason_codes { QCA_DISCONNECT_REASON_UNSPECIFIED = 0, @@ -12965,6 +14858,10 @@ enum qca_disconnect_reason_codes { QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE = 14, QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE = 15, QCA_DISCONNECT_REASON_USER_TRIGGERED = 16, + QCA_DISCONNECT_REASON_KEY_FAIL_TO_INSTALL = 17, + QCA_DISCONNECT_REASON_FW_TRIGGERED_LINK_SWITCH = 18, + QCA_DISCONNECT_REASON_HOST_TRIGGERED_LINK_DELETE = 19, + QCA_DISCONNECT_REASON_HOST_OCI_MISMATCH = 20, }; /** @@ -14357,6 +16254,22 @@ enum qca_wlan_vendor_monitor_ctrl_frame_type { QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME = BIT(1), }; +/* + * enum qca_wlan_vendor_monitor_operating_type: Attributes used by vendor + * attribute %QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE + * + * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC: Local packet capture. + * Capture frames sent and received by the current client interface from the + * BSS. + * + * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC: Operating channel capture. + * Capture all frames on the current operating channel of client interface. + */ +enum qca_wlan_vendor_monitor_operating_type { + QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC = 0, + QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC = 1, +}; + /** * enum qca_wlan_vendor_attr_set_monitor_mode - Used by the * vendor command QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE to set the @@ -14393,6 +16306,12 @@ enum qca_wlan_vendor_monitor_ctrl_frame_type { * Represents the interval in milliseconds only for the connected Beacon frames, * expecting the connected BSS's Beacon frames to be sent on the monitor * interface at this specific interval. + * + * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE: u32 attribute. + * Represents the monitor operating type (u32). These operating types are + * defined in enum qca_wlan_vendor_monitor_operating_type. + * If this attribute is not included, default operating type LPC ("local + * packet capture") used. */ enum qca_wlan_vendor_attr_set_monitor_mode { QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_INVALID = 0, @@ -14403,6 +16322,7 @@ enum qca_wlan_vendor_attr_set_monitor_mode { QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE = 5, QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE = 6, QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL = 7, + QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE = 8, /* keep last */ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST, @@ -14631,6 +16551,105 @@ enum qca_wlan_vendor_attr_ratemask_params { QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_AFTER_LAST - 1, }; +/** + * enum qca_wlan_rx_mcs_map_params_phy_type - RX MCS map config PHY type + * + * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_VHT: VHT PHY type + * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_HE: HE PHY type + * @QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_EHT: EHT PHY type + */ +enum qca_wlan_rx_mcs_map_params_phy_type { + QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_VHT = 0, + QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_HE = 1, + QCA_WLAN_RX_MCS_MAP_PARAMS_PHY_TYPE_EHT = 2, +}; + +/** + * enum qca_wlan_vendor_attr_rx_mcs_map_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_RX_MCS_MAP_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LIST: + * Array of nested attributes containing + * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE and + * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP and optionally + * QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID. + * + * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE: u8, represents + * the PHY type to which RX MCS Map config is to be applied. + * The values for this attribute are referred from enum + * qca_wlan_rx_mcs_map_params_phy_type. + * + * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP: binary, RX MCS map bitmap. + * + * For EHT targets 3 bit correspond to one NSS setting. + * b0-2 => NSS1 + * b3-5 => NSS2 + * b6-8 => NSS3 + * b9-11 => NSS4 + * b12-14 => NSS5 + * b15-17 => NSS6 + * b18-20 => NSS7 + * b21-23 => NSS8 + * + * Below are the possible values + * 000 - Disabled/Not supported + * 001 - MCS 0-7 + * 010 - MCS 0-9 + * 011 - MCS 0-11 + * 100 - MCS 0-13 + * 110 - MCS 0-14 + * 111 - MCS 0-15 + * + * For HE targets, 2 bits correspond to one NSS setting + * b0-1 => NSS1 + * b2-3 => NSS2 + * b4-5 => NSS3 + * b6-7 => NSS4 + * b8-9 => NSS5 + * b10-11 => NSS6 + * b12-13 => NSS7 + * b14-15 => NSS8 + * + * Below are the possible values + * 00 - Disabled/Not supported + * 01 - MCS 0-7 + * 10 - MCS 0-9 + * 11 - MCS 0-11 + * + * for VHT targets, 2 bits correspond to one NSS setting. + * b0-1 => NSS1 + * b2-3 => NSS2 + * b4-5 => NSS3 + * b6-7 => NSS4 + * b8-9 => NSS5 + * b10-11 => NSS6 + * b12-13 => NSS7 + * b14-15 => NSS8 + * + * Below are the possible values + * 00 - Disabled/Not supported + * 01 - MCS 0-7 + * 10 - MCS 0-8 + * 11 - MCS 0-9 + * + * @QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID: u8, used to specify the + * MLO link ID of a link to be configured. Optional attribute. + * No need of this attribute in non-MLO cases. If the attribute is + * not provided, MCS map will be applied for the association link. + */ +enum qca_wlan_vendor_attr_rx_mcs_map_params { + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LIST = 1, + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_PHY_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_BITMAP = 3, + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_LINK_ID = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_RX_MCS_MAP_PARAMS_AFTER_LAST - 1, +}; + /** * enum qca_wlan_audio_data_path - Defines the data path to be used for audio * traffic. @@ -14693,6 +16712,56 @@ enum qca_wlan_vendor_pasn_action { * If present, it indicates the successful PASN handshake with the peer. If * this flag is not present, it indicates that the PASN handshake with the * peer device failed. + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM: Optional u32 attribute. It indicates the + * AKM suite that is preferred in the PASN handshake in the event from the + * driver to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to + * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace + * to the driver, it indicates the actual AKM suite used in the handshake. + * Userspace can select the AKM based on the AP's capabilities, if the + * given AKM suite is not applicable. Possible values are defined in + * IEEE Std 802.11-2020, 9.4.2.24.3 (AKM suites) (e.g., 0x000FAC04) + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER: Optional u32 attribute. It indicates + * the pairwise cipher suite that is preferred in the PASN handshake in + * the event from the driver to userspace when + * %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to + * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. In the status report from userspace + * to the driver, it indicates the actual cipher used in the handshake. + * Userspace can select the cipher suite based on the capabilities of the + * P, if the given cipher suite is not applicable. Possible values are + * defined in IEEE Std 802.11-2020, 9.4.2.24.2 (Cipher suites) + * (e.g., 0x000FAC04). + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD: This is a variable length byte + * array attribute. This attribute is present if the AKM suite specified + * in %QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM requires a password. The + * password is used in PASN handshake request in an event from the driver + * to userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to + * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID: This is a byte array attribute with a + * size of 16 bytes. When this attribute is present this PMKSA caching + * using the PMKSA identified by this PMKID is preferred to be used with + * PASN. This attribute is sent along with PASN handshake request in an + * event from the driver to userspace when + * %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to + * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER: u16 attribute in units for + * TUs (1024 microseconds). This attribute is sent from userspace along + * with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE to the + * driver in the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN + * subcommand when the AP request PASN to be retried later. + * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE: This is a variable length byte array + * attribute. In case an AP refused PASN temporarily, the STA can retry + * PASN handshake by attaching this attribute data to PASN request after + * the time period mentioned in the attribute + * %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER. + * In case the AP refused the PASN handshake temporarily, cookie data is + * received from the AP and it is sent from userspace to the driver along + * with the attribute %QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER in + * the status report using the %QCA_NL80211_VENDOR_SUBCMD_PASN subcommand. + * When the driver wants to retry PASN with the same AP after having + * received this information, this attribute must be sent along with PASN + * handshake request in an event from the driver to + * userspace when %QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to + * %QCA_WLAN_VENDOR_PASN_ACTION_AUTH. */ enum qca_wlan_vendor_attr_pasn_peer { QCA_WLAN_VENDOR_ATTR_PASN_PEER_INVALID = 0, @@ -14700,6 +16769,12 @@ enum qca_wlan_vendor_attr_pasn_peer { QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR = 2, QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED = 3, QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS = 4, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM = 5, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER = 6, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD = 7, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_PMKID = 8, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER = 9, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE = 10, /* keep last */ QCA_WLAN_VENDOR_ATTR_PASN_PEER_AFTER_LAST, @@ -15510,10 +17585,14 @@ enum qca_wlan_vendor_attr_sr_stats { * * @QCA_WLAN_SR_REASON_CODE_CONCURRENCY: The SR feature is disabled/enabled due * to change in concurrent interfaces that are supported by the driver. + * + * @QCA_WLAN_SR_REASON_CODE_BCN_IE_CHANGE: The SR feature is disabled/enabled + * on non-AP STA, due to BSS SR parameter(s) update. */ enum qca_wlan_sr_reason_code { QCA_WLAN_SR_REASON_CODE_ROAMING = 0, QCA_WLAN_SR_REASON_CODE_CONCURRENCY = 1, + QCA_WLAN_SR_REASON_CODE_BCN_IE_CHANGE = 2, }; /** @@ -15693,12 +17772,37 @@ enum qca_wlan_vendor_attr_sr_params { * This attribute is used in response from the driver to a command in which * %QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to * %QCA_WLAN_SR_OPERATION_GET_STATS. + * + * @QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS: Array of nested links each identified + * by %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID. + * This attribute enables user to configure a single or multiple links by + * nesting %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID and corresponding SR + * configuration attributes defined in enum qca_wlan_vendor_attr_sr. + * In %QCA_WLAN_SR_OPERATION_GET_PARAMS or %QCA_WLAN_SR_OPERATION_GET_STATS + * in enum qca_wlan_sr_operation, %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID shall + * be used to pack the per link configuration that are currently in use. + * For STA interface, this attribute is applicable only in connected state + * when the current connection is MLO capable. The valid values of + * %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID are the link IDs of the connected AP + * MLD links. + * For AP interface, this attribute is applicable only after adding MLO + * links to the AP interface with %NL80211_CMD_ADD_LINK and the valid values + * of %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID are the link IDs specified in + * %NL80211_CMD_ADD_LINK while adding the MLO links to the AP interface. + * + * @QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID: 8-bit unsigned value. Used to specify + * the MLO link ID of a link that is being configured. This attribute must be + * included in each record nested under %QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS and + * may be included without nesting to indicate the target link for + * configuration attributes. */ enum qca_wlan_vendor_attr_sr { QCA_WLAN_VENDOR_ATTR_SR_INVALID = 0, QCA_WLAN_VENDOR_ATTR_SR_OPERATION = 1, QCA_WLAN_VENDOR_ATTR_SR_PARAMS = 2, QCA_WLAN_VENDOR_ATTR_SR_STATS = 3, + QCA_WLAN_VENDOR_ATTR_SR_MLO_LINKS = 4, + QCA_WLAN_VENDOR_ATTR_SR_MLO_LINK_ID = 5, /* Keep last */ QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST, @@ -16736,12 +18840,17 @@ enum qca_wlan_vendor_attr_tdls_state { * to configure power save inactivity timeout and speculative wake up * interval through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes. + * @QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED: Enable optimized power-save mode + * based on interface level latency tolerance value which is configured + * through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LATENCY_TOLERANCE and + * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_LEVEL. */ enum qca_wlan_vendor_opm_mode { QCA_WLAN_VENDOR_OPM_MODE_DISABLE = 0, QCA_WLAN_VENDOR_OPM_MODE_ENABLE = 1, QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED = 2, + QCA_WLAN_VENDOR_OPM_MODE_LATENCY_BASED = 3, }; /* @@ -17125,6 +19234,30 @@ enum qca_wlan_vendor_attr_tpc_links { QCA_WLAN_VENDOR_ATTR_TPC_AFTER_LAST - 1, }; +/** + * enum qca_wlan_vendor_attr_emlsr_info: Represent attributes to configure + * the EHT MLO links for EHT EMLSR operation and the EMLSR operation in STA + * mode. These attributes are used inside nested attribute + * %QCA_WLAN_VENDOR_ATTR_CONFIG_EMLSR_LINKS. + * + * @QCA_WLAN_VENDOR_ATTR_EMLSR_OPERATION: Required attribute, u8. + * 0 - Enter, 1 - Exit + * + * @QCA_WLAN_VENDOR_ATTR_EMLSR_LINKS_BITMAP: Required, u16 attribute. This + * indicates the bitmap of the link IDs to specify the links corresponding + * to the bit set to be used for the EHT EMLSR operation. + */ +enum qca_wlan_vendor_attr_emlsr_info { + QCA_WLAN_VENDOR_ATTR_EMLSR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_EMLSR_OPERATION = 1, + QCA_WLAN_VENDOR_ATTR_EMLSR_LINKS_BITMAP = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EMLSR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_EMLSR_MAX = + QCA_WLAN_VENDOR_ATTR_EMLSR_AFTER_LAST - 1, +}; + /** * enum qca_wlan_vendor_attr_fw_page_fault_report - Used by the vendor * command %QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT. @@ -17408,6 +19541,9 @@ enum qca_wlan_vendor_attr_ap_suspend { * @QCA_TRAFFIC_TYPE_SCREEN_SHARE: Traffic type is screen share * @QCA_TRAFFIC_TYPE_UNKNOWN: Traffic type is unknown * @QCA_TRAFFIC_TYPE_INVALID: Invalid traffic type + * @QCA_TRAFFIC_TYPE_BROWSING: Traffic type is browsing website + * @QCA_TRAFFIC_TYPE_APERIODIC_BURSTS: Traffic type is aperiodic bursts + * @QCA_TRAFFIC_TYPE_LIVESTREAM: Traffic type is livestream */ enum qca_traffic_type { QCA_TRAFFIC_TYPE_STREAMING = 0, @@ -17417,6 +19553,9 @@ enum qca_traffic_type { QCA_TRAFFIC_TYPE_SCREEN_SHARE = 4, QCA_TRAFFIC_TYPE_UNKNOWN = 5, QCA_TRAFFIC_TYPE_INVALID = 6, + QCA_TRAFFIC_TYPE_BROWSING = 7, + QCA_TRAFFIC_TYPE_APERIODIC_BURSTS = 8, + QCA_TRAFFIC_TYPE_LIVESTREAM = 9, }; /** @@ -17703,11 +19842,22 @@ enum qca_wlan_vendor_attr_flow_stats { * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE: Mandatory u8 * attribute indicates the traffic type learned for this flow tuple. Uses the * enum qca_traffic_type values. + * + * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_UL_TID: Optional u8 attribute + * indicates the TID value to be used by the driver in uplink direction for this + * flow tuple. + * + * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS: Optional nested + * attribute array containing classify results insights. Each array element + * contains traffic type and probability information using attributes from + * enum qca_wlan_vendor_attr_classify_insights. */ enum qca_wlan_vendor_attr_flow_classify_result { QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INVALID = 0, QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_FLOW_TUPLE = 1, QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_UL_TID = 3, + QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS = 4, /* keep last */ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST, @@ -17715,6 +19865,29 @@ enum qca_wlan_vendor_attr_flow_classify_result { QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST - 1, }; +/** + * enum qca_wlan_vendor_attr_classify_insights - Classify results insights + * used by @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INSIGHTS as nested + * attributes within each array element. + * + * @QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABLE_TRAFFIC_TYPE: Mandatory u8 + * attribute indicates probable traffic type. Uses values from the + * enum qca_traffic_type. + * + * @QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABILITY: Mandatory u8 attribute + * indicates probability percentages for probable traffic type. Range: 0-100. + */ +enum qca_wlan_vendor_attr_classify_insights { + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABLE_TRAFFIC_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_PROBABILITY = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_MAX = + QCA_WLAN_VENDOR_ATTR_CLASSIFY_INSIGHTS_AFTER_LAST - 1 +}; + /** * enum qca_async_stats_sub_module - The statistics type used in async * statistics policy. @@ -17801,6 +19974,67 @@ enum qca_wlan_vendor_attr_nss_pkt { QCA_WLAN_VENDOR_ATTR_NSS_PKT_AFTER_LAST - 1, }; +/** + * enum qca_wlan_vendor_attr_mcs_pkt - Attributes used by + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MCS_PKT_COUNT. + * + * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX: u8 attribute. This + * represents the value of MCS index. + * + * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_TX_PACKET_COUNT: u64 attribute. This + * represents the number of PPDUs transmitted with the MCS index + * specified in %QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX. + * + * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_RX_PACKET_COUNT: u64 attribute. This + * represents the number of PPDUs received with the MCS index + * specified in %QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX. + * + * @QCA_WLAN_VENDOR_ATTR_MCS_PKT_PAD: Attribute used for padding for 64-bit + * alignment. + */ +enum qca_wlan_vendor_attr_mcs_pkt { + QCA_WLAN_VENDOR_ATTR_MCS_PKT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_MCS_PKT_MCS_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_MCS_PKT_TX_PACKET_COUNT = 2, + QCA_WLAN_VENDOR_ATTR_MCS_PKT_RX_PACKET_COUNT = 3, + QCA_WLAN_VENDOR_ATTR_MCS_PKT_PAD = 4, + + QCA_WLAN_VENDOR_ATTR_MCS_PKT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MCS_PKT_MAX = + QCA_WLAN_VENDOR_ATTR_MCS_PKT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_bw_pkt - Attributes used by + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BW_PKT_COUNT. + * + * @QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_CHAN_WIDTH: u8 attribute. This + * represents the value of bandwidth. This attribute uses values defined in + * enum nl80211_chan_width. + * + * @QCA_WLAN_VENDOR_ATTR_BW_PKT_TX_PACKET_COUNT: u64 attribute. This + * represents the number of PPDUs transmitted with the bandwidth + * specified in %QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_VALUE. + * + * @QCA_WLAN_VENDOR_ATTR_BW_PKT_RX_PACKET_COUNT: u64 attribute. This + * represents the number of PPDUs received with the bandwidth + * specified in %QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_VALUE. + * + * @QCA_WLAN_VENDOR_ATTR_BW_PKT_PAD: Attribute used for padding for 64-bit + * alignment. + */ +enum qca_wlan_vendor_attr_bw_pkt { + QCA_WLAN_VENDOR_ATTR_BW_PKT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_BW_PKT_BW_CHAN_WIDTH = 1, + QCA_WLAN_VENDOR_ATTR_BW_PKT_TX_PACKET_COUNT = 2, + QCA_WLAN_VENDOR_ATTR_BW_PKT_RX_PACKET_COUNT = 3, + QCA_WLAN_VENDOR_ATTR_BW_PKT_PAD = 4, + + QCA_WLAN_VENDOR_ATTR_BW_PKT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BW_PKT_MAX = + QCA_WLAN_VENDOR_ATTR_BW_PKT_AFTER_LAST - 1, +}; + /** * enum qca_wlan_intf_offload_type - Definition of available values for * QCA_WLAN_VENDOR_ATTR_CONFIG_INTF_OFFLOAD_TYPE to specify the offload path for @@ -17849,4 +20083,3861 @@ enum qca_wlan_intf_offload_type { QCA_WLAN_INTF_OFFLOAD_TYPE_PPE_DS = 3, }; +/** + * enum qca_wlan_vendor_attr_usd_op_type: Attribute values for + * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE to the vendor subcmd + * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a mandatory u8 attribute which + * represents the USD command type. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH: Indicates USD tear down of all active + * publish and subscribe sessions. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH: Indicates USD solicited publish + * operation that enables to offer a service for other devices based on + * given parameters. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE: Indicates USD active subscribe + * operation that requests for a given service with given parameters from + * other devices that offer the service. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH: Indicates update of an instance + * of the publish function of given publish id. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH: Indicates cancellation of an + * instance of the publish function. + * + * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE: Indicates cancellation of an + * instance of the subscribe function. + */ +enum qca_wlan_vendor_attr_an_usd_op_type { + QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH = 0, + QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH = 1, + QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE = 2, + QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH = 3, + QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH = 4, + QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE = 5, +}; + +/** + * enum qca_wlan_vendor_attr_usd_service_protocol_type: Attribute values for + * %QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE to the vendor subcmd + * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a u8 attribute which represents the + * USD service protocol type for service specific information. + * + * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR: Indicates SSI info is + * of type Bonjour + * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC: Indicates SSI info is + * of type generic + * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER: Indicates SSI info + * is of type CSA/Matter + */ +enum qca_wlan_vendor_attr_usd_service_protocol_type { + QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR = 1, + QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC = 2, + QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER = 3, +}; + +/** + * enum qca_wlan_vendor_attr_usd_chan_config - Attributes used inside nested + * attribute %QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ: Required + * u32 attribute containing the default channel frequency (MHz). + * + * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST: Optional array of channel + * frequencies in MHz (u32) to publish or subscribe. + */ +enum qca_wlan_vendor_attr_usd_chan_config { + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ = 1, + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST = 2, + + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_usd_status + * + * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS: USD request success status. + * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED: USD request failed status. + */ +enum qca_wlan_vendor_attr_usd_status { + QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS, + QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED, +}; + +/* enum qca_wlan_vendor_attr_usd: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_USD. + * + * @QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR: 6-byte source MAC address + * Mandatory attribute used with type + * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and + * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE. + * + * @QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE: Required u8 attribute. + * It indicates the type of the USD command. It uses values defined in enum + * qca_wlan_vendor_attr_usd_op_type. + * + * @QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID: Required u8 attribute. + * It contains the publisher/subscribe id that is specific to the + * publish/subscribe instance. + * + * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID: Required 6-byte attribute. + * It contains the service id that is specific to the service being + * published/subscribed. + * + * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE: u8 attribute that indicates + * the service protocol type of service specific info. It uses values + * defined in enum qca_wlan_vendor_attr_usd_service_protocol_type. It is + * applicable when %QCA_WLAN_VENDOR_ATTR_USD_SSI is present. + * + * @QCA_WLAN_VENDOR_ATTR_USD_SSI: u8 array containing service specific + * information that has to be conveyed in publish/subscribe message. + * Optional attribute used with type + * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH, + * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE, and + * %QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH. + * + * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG: Nested attribute containing USD + * channel configuration parameters. + * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and + * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE. + * See enum qca_wlan_vendor_attr_usd_chan_config for nested attributes. + * + * @QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER: u8 array containing a USD + * element container buffer that has to be conveyed in publish/subscribe + * message. + * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and + * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE. + * + * @QCA_WLAN_VENDOR_ATTR_USD_TTL: u16 attribute. Indicates the timeout + * for each request in seconds. Timeout value 0 represents single time + * operation. + * + * @QCA_WLAN_VENDOR_ATTR_USD_STATUS: u8 attribute. Status received in event + * indicating whether the underlying driver/firmware has started the USD + * operation as indicated by attributes + * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE and + * %QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID. + * enum qca_wlan_vendor_attr_usd_status indicates status values. + * + * @QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID: 6-byte MAC address that has to be + * used in publish/subscribe multicast frames. It is an optional attribute + * used with the types %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and + * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE. + */ +enum qca_wlan_vendor_attr_usd { + QCA_WLAN_VENDOR_ATTR_USD_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID = 3, + QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID = 4, + QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE = 5, + QCA_WLAN_VENDOR_ATTR_USD_SSI = 6, + QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG = 7, + QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER = 8, + QCA_WLAN_VENDOR_ATTR_USD_TTL = 9, + QCA_WLAN_VENDOR_ATTR_USD_STATUS = 10, + QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID = 11, + + QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_USD_MAX = + QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_audio_transport_switch_type - Represents the possible transport + * switch types. + * + * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_NON_WLAN: Request to route audio data + * via non-WLAN transport (e.g., Bluetooth). + * + * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_WLAN: Request to route audio data via + * WLAN transport. + */ +enum qca_wlan_audio_transport_switch_type { + QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_NON_WLAN = 0, + QCA_WLAN_AUDIO_TRANSPORT_SWITCH_TYPE_WLAN = 1, +}; + +/** + * enum qca_wlan_audio_transport_switch_status - Represents the status of audio + * transport switch request. + * + * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_REJECTED: Request to switch transport + * has been rejected. For example, when transport switch is requested from WLAN + * to non-WLAN transport, user space modules and peers would evaluate the switch + * request and may not be ready for switch and hence switch to non-WLAN + * transport gets rejected. + * + * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_COMPLETED: Request to switch + * transport has been completed. This is sent only in the command path. For + * example, when the driver had requested for audio transport switch and + * userspace modules as well as peers are ready for the switch, userspace module + * switches the transport and sends the subcommand with status completed to the + * driver. + */ +enum qca_wlan_audio_transport_switch_status { + QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_REJECTED = 0, + QCA_WLAN_AUDIO_TRANSPORT_SWITCH_STATUS_COMPLETED = 1, +}; + +/** + * enum qca_wlan_audio_transport_switch_reason - Represents the reason of audio + * transport switch request. + * + * @QCA_WLAN_AUDIO_TRANSPORT_SWITCH_REASON_TERMINATING: Requester transport is + * terminating. After this indication, requester module may not be available to + * process further request on its transport. For example, to handle a high + * priority concurrent interface, WLAN transport needs to terminate and hence + * indicates switch to a non-WLAN transport with reason terminating. User space + * modules switch to non-WLAN immediately without waiting for further + * confirmation. + */ +enum qca_wlan_audio_transport_switch_reason { + QCA_WLAN_AUDIO_TRANSPORT_SWITCH_REASON_TERMINATING = 0, +}; + +/** + * enum qca_wlan_vendor_attr_audio_transport_switch - Attributes used by + * %QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH vendor command. + * + * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_TYPE: u8 attribute. Indicates + * the transport switch type from one of the values in enum + * qca_wlan_audio_transport_switch_type. This is mandatory attribute in both + * command and event path. This attribute is included in both requests and + * responses. + * + * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_STATUS: u8 attribute. Indicates + * the transport switch status from one of the values in enum + * qca_wlan_audio_transport_switch_status. This is optional attribute and used + * in both command and event path. This attribute must not be included in + * requests. + * + * @QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_REASON: u8 attribute. Indicates + * the transport switch reason from one of the values in enum + * qca_wlan_audio_transport_switch_reason. This is optional attribute and used + * in both command and event path. + */ +enum qca_wlan_vendor_attr_audio_transport_switch { + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_STATUS = 2, + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_REASON = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_MAX = + QCA_WLAN_VENDOR_ATTR_AUDIO_TRANSPORT_SWITCH_AFTER_LAST - 1, +}; + + +/** + * enum qca_wlan_connect_ext_features - Feature flags for + * %QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES + * + * @QCA_CONNECT_EXT_FEATURE_RSNO: Flag attribute. This indicates supplicant + * support for RSN overriding. The driver shall enable RSN overriding in the + * (re)association attempts only if this flag is indicated. This functionality + * is available only when the driver indicates support for + * @QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA. + * + * @QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE: Flag attribute. This indicates + * supplicant support for external authentication with EPPKE authentication + * algorithm. The driver can offload EPPKE authentication to supplicant via + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH only if this flag is enabled. + * + * @QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X: Flag attribute. This indicates + * supplicant support for external authentication with IEEE 802.1X + * Authentication algorithm (EAP over Authentication frames) as specified + * in IEEE P802.11bi/D4.0, 12.16.5. The driver can offload authentication to + * supplicant via QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH only if this flag is + * enabled. + * + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits. + */ +enum qca_wlan_connect_ext_features { + QCA_CONNECT_EXT_FEATURE_RSNO = 0, + QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE = 1, + QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X = 2, + NUM_QCA_CONNECT_EXT_FEATURES /* keep last */ +}; + +/* enum qca_wlan_vendor_attr_connect_ext: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT. + * + * @QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES: Feature flags contained in a byte + * array. The feature flags are identified by their bit index (see &enum + * qca_wlan_connect_ext_features) with the first byte being the least + * significant one and the last one being the most significant one. + * @QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS: Nested attribute of + * BSSIDs to indicate allowed BSSIDs for association. This configuration stays + * in effect only for the current connection request and for the next connect + * request if there is no connection currently. + */ +enum qca_wlan_vendor_attr_connect_ext { + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES = 1, + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS = 2, + + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_MAX = + QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_p2p_mode - Defines the values used with + * %QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG. + * + * @QCA_P2P_MODE_WFD_R1: Wi-Fi Direct R1 only. + * @QCA_P2P_MODE_WFD_R2: Wi-Fi Direct R2 only. + * @QCA_P2P_MODE_WFD_PCC: P2P Connection Compatibility Mode which supports both + * Wi-Fi Direct R1 and R2. + */ +enum qca_wlan_vendor_p2p_mode { + QCA_P2P_MODE_WFD_R1 = 0, + QCA_P2P_MODE_WFD_R2 = 1, + QCA_P2P_MODE_WFD_PCC = 2, +}; + +/* enum qca_wlan_vendor_attr_set_p2p_mode: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE. + * + * @QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG: u8 attribute. Sets the P2P device + * mode. The values used are defined in enum qca_wlan_vendor_p2p_mode. + * This configuration is valid until the interface is brought up next time after + * this configuration and the driver shall use this configuration only when the + * interface is brought up in NL80211_IFTYPE_P2P_GO mode. + * When this parameter has not been set, the interface is brought up with + * Wi-Fi Direct R1 only configuration by default. + */ +enum qca_wlan_vendor_attr_set_p2p_mode { + QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG = 1, + + QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_MAX = + QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_chan_usage_req_chan_list: Attributes used inside + * nested attributes %QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST. + * + * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_CHAN: u8 attribute. Indicates + * the channel number of the channel list entry. + * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_OP_CLASS: u8 attribute. + * Indicates the operating class of the channel list entry. + */ +enum qca_wlan_vendor_attr_chan_usage_req_chan_list { + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_CHAN = 1, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_OP_CLASS = 2, + + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_MAX = + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_chan_usage_req_mode: Defines the values used + * with %QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE. + * + * @QCA_CHAN_USAGE_MODE_UNAVAILABILITY_INDICATION: Mode set by STA to indicate + * the AP about its unavailability during a peer-to-peer TWT agreement. + * + * @QCA_CHAN_USAGE_MODE_CHANNEL_SWITCH_REQ: Mode set by the STA that is in a + * channel-usage-aidable BSS to request a channel switch. Other Channel Usage + * elements are not required. Optional HT/VHT/HE Capabilities are present. + */ +enum qca_wlan_vendor_attr_chan_usage_req_mode { + QCA_CHAN_USAGE_MODE_UNAVAILABILITY_INDICATION = 3, + QCA_CHAN_USAGE_MODE_CHANNEL_SWITCH_REQ = 4, +}; + +/** + * enum qca_wlan_vendor_attr_chan_usage_req: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ. + * + * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE: Required u8 attribute. Identifies + * the usage of the channel list entry provided in the channel usage request. + * Channel switch request and unavailability channel usage modes are + * configured on a STA/P2P Client. + * See enum qca_wlan_vendor_attr_chan_usage_req_mode for attribute values. + * See IEEE P802.11-REVme/D7.0, 9.4.2.84, Table 9-268 for more information. + * + * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST: Required array of nested + * attributes containing channel usage parameters. + * Required when channel usage mode is Channel-usage-aidable BSS channel + * switch request. + * See enum qca_wlan_vendor_attr_req_chan_list for nested attributes. + * + * @QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_UNAVAILABILITY_CONFIG_PARAMS: Nested + * attribute representing the parameters configured for unavailability + * indication. Required when channel usage mode is unavailability indication. + * + * Below attributes from enum qca_wlan_vendor_attr_twt_setup are used inside + * this nested attribute: + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION, + * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA. + */ +enum qca_wlan_vendor_attr_chan_usage_req { + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MODE = 1, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_CHAN_LIST = 2, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_UNAVAILABILITY_CONFIG_PARAMS = 3, + + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_MAX = + QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_fw_scan_bss_flags - Flags for + * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS + * + * @QCA_WLAN_FW_SCAN_BSS_HT_OPS: This indicates HT Operation element + * (IEEE Std 802.11-2020, 9.4.2.56) is present in the Beacon or Probe Response + * frame of the BSS. + * + * @QCA_WLAN_FW_SCAN_BSS_VHT_OPS: This indicates VHT Operation element + * (IEEE Std 802.11-2020, 9.4.2.158) is present in the Beacon or Probe Response + * frame of the BSS. + * + * @QCA_WLAN_FW_SCAN_BSS_HE_OPS: This indicates HE Operation element + * (IEEE Std 802.11ax-2021, 9.4.2.249) is present in the Beacon or Probe + * Response frame of the BSS. + * + * @QCA_WLAN_FW_SCAN_BSS_EHT_OPS: This indicates EHT Operation element + * (IEEE Std 802.11be-2024, 9.4.2.321) is present in the Beacon or Probe + * Response frame of the BSS. + * + * @QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER: This indicates Fine Timing Measurement + * Responder bit is set to 1 in the Extended Capabilities field of the Extended + * Capabilities element (IEEE Std 802.11-2020, 9.4.2.26) in the Beacon or Probe + * Response frame of the BSS. + * + * @NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS: Number of assigned feature bits. + */ +enum qca_wlan_fw_scan_bss_flags { + QCA_WLAN_FW_SCAN_BSS_HT_OPS = 0, + QCA_WLAN_FW_SCAN_BSS_VHT_OPS = 1, + QCA_WLAN_FW_SCAN_BSS_HE_OPS = 2, + QCA_WLAN_FW_SCAN_BSS_EHT_OPS = 3, + QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER = 4, + + NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS /* keep last */ +}; + +/* enum qca_wlan_vendor_attr_fw_scan_bss: Attributes used inside + * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST nested attribute. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO: Required (u32). Indicates how many + * milliseconds ago from %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP this BSS + * was last scanned (i.e., Beacon or Probe Response frame received). + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID: Required (6-byte MAC address). BSSID + * of the BSS. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID: Required (binary attribute, + * 0..32 octets). SSID of the BSS. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI: Required (s8). RSSI of the last + * received Beacon or Probe Response frame. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY: Required (CPU byte order, u16). + * The Capability Information field from the last received Beacon or Probe + * Response frame. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS: BSS capability flags contained in a + * byte array. The flags are identified by their bit index (see &enum + * qca_wlan_fw_scan_bss_flags) with the first byte being the least significant + * one and the last one being the most significant one. This information will be + * populated from the last received Beacon or Probe Response frame. This is a + * mandatory attribute. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ: Required (u32). Indicates + * primary 20 MHz channel center frequency in MHz of the BSS. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH: Required (u8). Indicates + * channel width of the BSS. This uses values defined in + * enum nl80211_chan_width. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1: Required (u32). Indicates the + * center frequency (MHz) of the first segment. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2: Optional (u32). Indicates the + * center frequency (MHz) of the second segment. Used only for + * %NL80211_CHAN_WIDTH_80P80 value in + * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH. + */ +enum qca_wlan_vendor_attr_fw_scan_bss { + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO = 1, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID = 2, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID = 3, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI = 4, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY = 5, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS = 6, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ = 7, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH = 8, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1 = 9, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2 = 10, + + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MAX = + QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST - 1, +}; + +/* enum qca_wlan_vendor_attr_fw_scan_report: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP: 64-bit unsigned value to + * indicate the timestamp when this report is generated, timestamp in + * microseconds from system boot. A mandatory attribute. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST: Nested attribute of u32 + * attributes. This indicates the list of frequencies that were scanned. This is + * an optional attribute. If this is not specified, all frequencies allowed in + * the current regulatory domain were scanned. + * + * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST: Nested attribute. + * This indicates information of the scanned BSSs by the firmware. This is an + * optional attribute. + * + * The attributes defined in enum qca_wlan_vendor_attr_fw_scan_bss are nested + * in this attribute. + */ +enum qca_wlan_vendor_attr_fw_scan_report { + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP = 1, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST = 2, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST = 3, + + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_MAX = + QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_idle_shutdown_status: Represents idle shutdown status. + * + * @QCA_WLAN_IDLE_SHUTDOWN_STARTED: Indicates idle shutdown is started in the + * host driver. + * @QCA_WLAN_IDLE_SHUTDOWN_COMPLETED: Indicates idle shutdown is completed in + * the host driver. + */ +enum qca_wlan_idle_shutdown_status { + QCA_WLAN_IDLE_SHUTDOWN_STARTED = 0, + QCA_WLAN_IDLE_SHUTDOWN_COMPLETED = 1, +}; + +/* + * enum qca_wlan_vendor_attr_idle_shutdown: Attributes used by vendor event + * %QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN. + * + * @QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS: Required u8 attribute. Indicates + * the status of the idle shutdown from one of the values in enum + * qca_wlan_idle_shutdown_status. + */ +enum qca_wlan_vendor_attr_idle_shutdown { + QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS = 1, + + QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_MAX = + QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_pri_link_migrate: Attributes used by the vendor + * subcommand/event %QCA_NL80211_VENDOR_SUBCMD_PRI_LINK_MIGRATE. + * + * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR: 6 byte MAC address. + * (a) Used in a subcommand to indicate that primary link migration + * will occur only for the ML client with the given MLD MAC address. + * (b) Used in an event to specify the MAC address of the peer for which + * the primary link has been modified. + * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID: Optional u8 + * attribute. Used with subcommand only. + * When specified, all ML clients having their current primary + * link as specified will be considered for migration. + * @QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID: Optional u8 attribute. + * (a) Used in subcommand, to indicate the new primary link to which the + * selected ML clients should be migrated to. If not provided, the driver + * will select a suitable primary link on its own. + * (b) Used in event, to indicate the new link ID which is set as the + * primary link. + */ +enum qca_wlan_vendor_attr_pri_link_migrate { + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MLD_MAC_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_CURRENT_PRI_LINK_ID = 2, + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_NEW_PRI_LINK_ID = 3, + + /* keep this last */ + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_MAX = + QCA_WLAN_VENDOR_ATTR_PRI_LINK_MIGR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_btm_req_resp_type: Represents response types to follow + * upon receiving BTM request from AP. + * + * @QCA_WLAN_BTM_REQ_RESP_DEFAULT: Reset to default behavior. + * @QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME: Send link reconfiguration request + * frames with specified info. + * @QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME: Send TTLM request frame. + * @QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME: Send Reassociation Request frame. + */ +enum qca_wlan_vendor_btm_req_resp_type { + QCA_WLAN_BTM_REQ_RESP_DEFAULT = 0, + QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME = 1, + QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME = 2, + QCA_WLAN_BTM_REQ_RESP_REASSOC_FRAME = 3, +}; + +/** + * enum qca_wlan_vendor_attr_reconfig_frame_info - Attribute used by + * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO. + * + * @QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK: u16 attribute. Bitmask of + * link IDs to be added. + * + * @QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK: u16 attribute bitmask of + * link IDs to be removed. + */ +enum qca_wlan_vendor_attr_reconfig_frame_info { + QCA_WLAN_VENDOR_ATTR_RECONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_RECONFIG_ADD_LINKS_BITMASK = 1, + QCA_WLAN_VENDOR_ATTR_RECONFIG_DELETE_LINKS_BITMASK = 2, + + QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RECONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_RECONFIG_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_btm_req_resp - Attribute used by + * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BTM_REQ_RESP. + * + * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE: u8 attribute. Indicates type of + * response to send. Possible values for this attribute are defined in + * enum qca_wlan_vendor_btm_req_resp_type. This is a mandatory attribute. + * + * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO: Array of nested + * attributes containing information about one or more setup link + * reconfiguration request frames, each set represents one link reconfiguration + * frame information. The driver shall send a separate link reconfiguration + * frame for each nested attribute set. It takes attributes as defined in enum + * qca_wlan_vendor_attr_reconfig_frame_info. This attribute must be present + * when %QCA_WLAN_BTM_REQ_RESP_RECONFIG_FRAME specified in + * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute. + * + * @QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP: TID to Link Mapping to + * be used in TTLM request frame. This nested attribute with + * %NL80211_ATTR_MLO_TTLM_DLINK and %NL80211_ATTR_MLO_TTLM_ULINK is used to + * specify the TID to Link mapping for downlink/uplink traffic. This attribute + * must be present when %QCA_WLAN_BTM_REQ_RESP_TTLM_FRAME specified in + * %QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE attribute. + */ +enum qca_wlan_vendor_attr_btm_req_resp { + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_RECONFIG_FRAME_INFO = 2, + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_TTLM_MAP = 3, + + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_MAX = + QCA_WLAN_VENDOR_ATTR_BTM_REQ_RESP_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg: Attributes used + * by vendor subcmd QCA_NL80211_VENDOR_SUBCMD_PERIODIC_PROBE_RSP_CFG + * + * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR: Connected peer + * MAC address to which Probe Response frames are to be sent. + * Multicast/Broadcast addresses are not supported. + * + * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD: 32-bit unsigned value. + * This attribute specifies the interval (in microseconds) in which directed + * Probe Response frames are sent periodically to the peer as specified in + * attribute QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PEER_MAC_ADDR. When the peer + *is in power save, sending of the frames might be delayed until the device + * comes out of power save. Attribute value can be in the range of minimum value + * of 50000 and maximum value of 1500000. + * + * @QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_COUNT: 8-bit unsigned value. + * Specifies number of directed Probe Responses frames that can be sent as per + * interval defined in QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD. When + * attribute value is 255, directed Probe Response frames are sent continuously + * until this attribute is sent as 0 in the command to disable period + * transmission. When the attribute value is 1, one directed Probe Response + * frame will be sent and the attribute + * QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_PERIOD will not be considered. + */ +enum qca_wlan_vendor_attr_periodic_probe_rsp_cfg { + QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PEER_MAC_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_PERIOD = 2, + QCA_WLAN_VENDOR_ATTR_PROBE_RSP_CFG_COUNT = 3, + + QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_MAX = + QCA_WLAN_VENDOR_ATTR_PROBE_RESP_CFG_AFTER_LAST - 1, +}; + +/** + * enum qca_flow_status_update_type - Attribute values for + * %QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE. + * @QCA_FLOW_STATUS_UPDATE_PAUSE: Flow paused. + * @QCA_FLOW_STATUS_UPDATE_RESUME: Flow resumed. + * @QCA_FLOW_STATUS_UPDATE_DELETE: Flow deleted. + */ +enum qca_flow_status_update_type { + QCA_FLOW_STATUS_UPDATE_PAUSE = 0, + QCA_FLOW_STATUS_UPDATE_RESUME = 1, + QCA_FLOW_STATUS_UPDATE_DELETE = 2, +}; + +/** + * enum qca_wlan_vendor_attr_flow_status - Definition of attributes to + * specify status updates of a classified flow. This enum is used by + * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_STATUS. + * + * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TUPLE: Mandatory nested attribute + * containing attributes defined by enum qca_wlan_vendor_attr_flow_tuple. + * + * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TRAFFIC_TYPE: Mandatory u8 attribute + * indicates the traffic type of this flow tuple. Uses the + * enum qca_traffic_type values. + * + * @QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE: Mandatory u8 attribute + * indicates the flow status update type. + * Uses the enum qca_flow_status_update_type values. + */ +enum qca_wlan_vendor_attr_flow_status { + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TUPLE = 1, + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_TRAFFIC_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_UPDATE_TYPE = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_LAST, + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_MAX = + QCA_WLAN_VENDOR_ATTR_FLOW_STATUS_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_iq_data_inference - Represents the attributes sent + * as part of IQ data inference messages. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CMD_TYPE: u32 attribute represents + * the command type sent from userspace to the driver. + * The values are defined in enum qca_wlan_vendor_iq_inference_cmd_type. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_BW: u32 attribute containing + * one of the values of &enum nl80211_chan_width, describing the channel width. + * See the documentation of the enum for more information. + * + * @CA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CHANNEL_FREQ: u32 attribute represents + * the channel frequency (in MHz). + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_1: u32 attribute + * represents the primary center frequency (in MHz). + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_2: u32 attribute + * represents the secondary center frequency (in MHz); valid only for + * 80+80 MHz channels. + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MCS: u32 attribute represents the MCS + * Index w.r.t. various PHY mode as per + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE. + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TEMPERATURE: s32 attribute represents + * the device temperature in degree Celcius. + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STAGE: u32 attributes represents the + * inference stage. The values are defined in + * enum qca_wlan_vendor_iq_inference_stage. + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_EVM: s32 attribute represents the + * Error Vector Magniture (in dB). Userspace App derives EVM based on the IQ + * samples data and send it to the driver. + * This attribute is used with @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MASK_MARGIN: s32 attribute represents + * the Spectral Mask Margin (in dB). Userspace app derives mask margin based on + * the IQ samples data and send it to the driver. + * This attribute is used with @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE: u32 attribute represents + * the PHY mode for which inference is being sent. + * This is sent from the driver to user space as part of the event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_SAMPLE_SIZE: u32 attribute represents + * the IQ sample size (in bytes). + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * User space application can read the IQ samples from the memory mapped + * IO file /dev/txpb. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STATUS: u32 attribute represents the + * inference status. + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * The values are defined in enum qca_wlan_vendor_iq_inference_status. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_COOKIE: u64 cookie provided by the + * driver for the specific inference request. + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE and the same cookie needs to be + * passed back to the driver as part of the command + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE + * to maintain synchronization between commands and asynchronous events. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PAD: Attribute used for padding for + * 64-bit alignment. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_POWER: s32 attribute represents + * the TX power of an inferencing packet (in dBm). + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + * + * @QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_CHAIN_IDX: u32 attribute + * represents the TX chain index on which inferencing is requested for. + * This is sent from the driver to user space as part of event + * @QCA_NL80211_VENDOR_SUBCMD_IQ_DATA_INFERENCE. + */ +enum qca_wlan_vendor_attr_iq_data_inference { + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CMD_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_BW = 2, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CHANNEL_FREQ = 3, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_1 = 4, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_CENTER_FREQ_2 = 5, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MCS = 6, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TEMPERATURE = 7, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STAGE = 8, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_EVM = 9, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MASK_MARGIN = 10, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PHY_MODE = 11, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_SAMPLE_SIZE = 12, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_STATUS = 13, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_COOKIE = 14, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_PAD = 15, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_POWER = 16, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_TX_CHAIN_IDX = 17, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_MAX = + QCA_WLAN_VENDOR_ATTR_IQ_DATA_INFERENCE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_iq_inference_cmd_type - Represents the command types + * userspace app sends to the driver. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_START: Represents the stage where + * userspace app has started and is ready to receive IQ samples. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_STOP: Represents the stage where + * userspace app has stopped and is no longer ready to receive IQ samples. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT: Represents the inference result + * sent from userspace to the driver. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_FAILURE: Represents the inference failure + * sent from userspace to the driver. + * + */ +enum qca_wlan_vendor_iq_inference_cmd_type { + QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_START = 0, + QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_APP_STOP = 1, + QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_RESULT = 2, + QCA_WLAN_VENDOR_IQ_INFERENCE_CMD_FAILURE = 3, +}; + +/** + * enum qca_wlan_vendor_iq_inference_stage - Represents the inference stage + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FIRST_PASS: Represents the first + * pass in inference stage. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_SECOND_PASS: Represents the second + * pass in inference stage. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_THIRD_PASS: Represents the third + * pass in inference stage. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FOURTH_PASS: Represents the fourth + * pass in inference stage. + */ +enum qca_wlan_vendor_iq_inference_stage { + QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FIRST_PASS = 0, + QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_SECOND_PASS = 1, + QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_THIRD_PASS = 2, + QCA_WLAN_VENDOR_IQ_INFERENCE_STAGE_FOURTH_PASS = 3, +}; + +/** + * enum qca_wlan_vendor_iq_inference_status - Represents the inference + * status sent from driver to user space. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_START_INFERENCE: Represents the stage where + * userspace app to start the inference. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_ABORT: Represents the stage where + * userspace app to stop the inference. + * + * @QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_COMPLETE: Represents the stage where + * inference is complete. + * + */ +enum qca_wlan_vendor_iq_inference_status { + QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_START_INFERENCE = 0, + QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_ABORT = 1, + QCA_WLAN_VENDOR_IQ_INFERENCE_STATUS_COMPLETE = 2, +}; + +/** + * enum qca_wlan_vendor_attr_p2p_set_noa - Represents the attributes sent as a + * part of set NoA message. This enum is used by + * %QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT: u8 mandatory attribute. + * 0 - Disable NoA + * 1 to 254 - Number of absence periods to be scheduled during the NoA. + * 255 - Absence periods shall repeat until the NoA is disabled. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_DURATION: u32 optional attribute. This is + * mandatory attribute when %QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT is set to + * non-zero value. This specifies the duration (in TUs) of each absence period. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INTERVAL: u32 optional attribute. This is + * mandatory attribute when %QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT is set to + * non-zero value. This specifies time (in TUs) between consecutive absence + * periods. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_START: u32 optional attribute. This + * specifies the start offset time (in milliseconds) of the first absence period + * after the beacon advertising the NoA. If this attribute not present, the + * driver will use the default start offset value. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA: Optional u8 attribute + * for use with QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA on a P2P GO interface. + * + * In multi-channel concurrency (MCC) scenarios, when a local STA interface + * sharing the same radio with the P2P GO performs a connection or roaming + * operation on a different channel, the firmware automatically initiates a + * one-shot NoA schedule on the P2P GO to facilitate the STA's channel + * operations. This attribute controls whether the firmware should automatically + * cancel this one-shot NoA schedule when the STA operation completes. + * + * This feature should only be enabled when all connected P2P Clients support + * staying awake during one-shot NoA (see + * QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA). Peer device + * capability should be determined through vendor-specific mechanisms such as + * device model/OUI whitelists or capability handshake via Bluetooth. + * + * Values: + * 1 - Enable: The firmware will cancel the current firmware-initiated one-shot + * NoA schedule when the local STA interface completes its connection or + * roaming operation. The cancellation will be reflected in subsequent + * Beacon frames (typically the next Beacon frame, though timing may vary + * based on beacon scheduling). This allows immediate resumption of P2P data + * transmission. + * 0 - Disable (default): NoA schedules are not automatically cancelled and + * will run for their full configured duration. + * + * Note: This attribute only affects firmware-initiated one-shot NoA schedules + * created for MCC STA operations. It does not affect NoA schedules explicitly + * configured by userspace through other attributes of this command. + * + * @QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA: Optional + * u8 attribute for use with QCA_NL80211_VENDOR_SUBCMD_P2P_SET_NOA on a P2P + * Client interface. + * + * This attribute controls whether the P2P Client should stay awake during + * one-shot NoA periods instead of entering sleep. This is designed to work in + * conjunction with QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA on the + * P2P GO side. + * + * This feature should only be enabled when the peer P2P GO supports early NoA + * cancellation (see QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA). + * Peer device capability should be determined through vendor-specific + * mechanisms such as device model/OUI whitelists or capability handshake via + * Bluetooth. + * + * Values: + * 1 - Enable: The P2P Client will stay awake during one-shot NoA periods + * instead of entering sleep. This allows the client to immediately receive + * frames when the P2P GO cancels a one-shot NoA early, without waiting for + * the originally configured NoA duration to expire. + * 0 - Disable (default): The P2P Client enters sleep during NoA periods as + * specified in the NoA schedule received from the P2P GO. + * + * Note: This attribute only affects behavior during one-shot NoA periods. It + * does not affect behavior during continuous NoA schedules. + */ +enum qca_wlan_vendor_attr_p2p_set_noa { + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_COUNT = 1, + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_DURATION = 2, + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_INTERVAL = 3, + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_START = 4, + QCA_WLAN_VENDOR_ATTR_P2P_SET_GO_CANCEL_ONE_SHOT_NOA = 5, + QCA_WLAN_VENDOR_ATTR_P2P_SET_GC_KEEP_AWAKE_DURING_ONE_SHOT_NOA = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_MAX = + QCA_WLAN_VENDOR_ATTR_P2P_SET_NOA_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wlan_telemetry - Represents the attributes sent + * from userspace application to the driver to get WLAN statistics. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_OBJECT: u8 attribute representing the + * object type for which the statistics are requested. The types of objects are + * defined in enum qca_wlan_vendor_wlan_telemetry_obj_type. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEATURE: Nested attribute representing + * the set of WLAN features for which statistics are requested. + * The nested attributes are of type NLA_FLAG and allow userspace applications + * to request multiple feature-specific statistics simultaneously. When a + * userspace application needs specific feature statistics, the corresponding + * feature attribute will be present. If a feature is not specified by the user, + * all feature attributes will be present by default. The nested flag + * attributes identifying the types of features are defined in + * enum qca_wlan_vendor_wlan_telemetry_feat_type. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_STA_MAC: 6 byte MAC address attribute of + * a station which is required when the statistics are being requested for a + * specific station. This can be either an MLD MAC address or a link MAC + * address. In case the MLD MAC address and the link MAC address are same, + * an additional attribute @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID to + * specify the Link ID is required. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_REQUEST_ID: u64 attribute representing + * the request ID for a non-blocking statistics request. This request ID will + * be returned back in the corresponding reply event from the driver to allow + * userspace to correlate the event reply with its request command. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID: u8 attribute representing the + * link ID for which the statistics are requested. + */ +enum qca_wlan_vendor_attr_wlan_telemetry { + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_OBJECT = 1, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEATURE = 2, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_STA_MAC = 3, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_REQUEST_ID = 4, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_LINK_ID = 5, + + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_MAX = + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_wlan_telemetry_obj_type - Represents the object types for + * which the statistics are requested. + * + * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_STA: Represents the station object. This + * enum value will be present if the userspace application requests for station + * level statistics from the driver. + * + * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_VDEV: Represents the vdev object which + * corresponds to the virtual interface of the radio. This enum + * value will be present if the userspace application requests for vdev level + * statistics from the driver. + * + * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_RADIO: Represents the radio interface + * of the device. This enum value will be present if the userspace application + * requests for radio level statistics from the driver. + * + * @QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_DEVICE: Represents the device object. + * This enum value will be present if the userspace application requests + * for device level statistics from the driver. + */ + +enum qca_wlan_vendor_wlan_telemetry_obj_type { + QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_STA = 1, + QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_VDEV = 2, + QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_RADIO = 3, + QCA_WLAN_VENDOR_WLAN_TELEMETRY_OBJ_DEVICE = 4, +}; + +/** + * qca_wlan_vendor_attr_wlan_telemetry_feat_type - Represents the feature types + * for which the statistics are requested. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_TX: Flag attribute representing the + * TX feature type which will be present if the userspace application requests + * for TX statistics from the driver. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX: Flag attribute representing the + * RX feature type which will be present if the userspace application requests + * for RX statistics from the driver. + * + */ +enum qca_wlan_vendor_attr_wlan_telemetry_feat_type { + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_TX = 1, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX = 2, + + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_MAX = + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wlan_telemetry_event_reply - Represents the set of + * attributes included in the event reply from the driver to the userspace + * application in response to the WLAN statistics request command. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_OBJECT: u8 attribute representing + * the object type of the statistics event reply. The types of objects are + * defined in enum qca_wlan_vendor_wlan_telemetry_obj_type. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_LINK_ID: u8 attribute representing + * the link ID of the statistics event reply. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_REQUEST_ID: u64 attribute + * representing the request ID of the statistics event reply. This ID is taken + * from the statistics request command sent by userspace application and + * included in the statistics event reply to correlate the event reply with its + * request command. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_RX_STATS: Nested attribute + * representing the types of WLAN RX statistics. This attribute will be present + * if the userspace application requests for RX statistics + * (QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_FEAT_RX) in the request command. + * The types of RX statistics are defined in enum + * qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_TX_STATS: Nested attribute + * representing the attributes that define the WLAN TX statistic types. + * This attribute will be present if the userspace application requests + * for TX statistics (QCA_WLAN_VENDOR_ATTR_TELEMETRY_FEAT_TX) in the + * request command. The types of TX statistics are defined in enum + * qca_wlan_vendor_attr_telemetry_tx_stats_types. + */ +enum qca_wlan_vendor_attr_wlan_telemetry_event_reply { + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_OBJECT = 1, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_LINK_ID = 2, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_REQUEST_ID = 3, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_RX_STATS = 4, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_TX_STATS = 5, + + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_MAX = + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_EVENT_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types - Represents the types of + * RX statistics included in the event reply from driver to userspace + * application. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_RXDMA_ERR_EVENT: Nested + * attribute representing the types of RXDMA error statistics included in the + * event reply. The types of RXDMA errors are defined in + * enum qca_wlan_vendor_wlan_telemetry_rxdma_error_types. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_ERR_EVENT: Nested attribute + * representing the types of REO error statistics included in the event reply. + * The types of REO errors are defined in + * enum qca_wlan_vendor_wlan_telemetry_reo_error_types. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_WBM_SW_DROP_RSN: Nested + * attribute representing the attributes that define the WBM software error + * types. The types of WBM software drop attributes are defined in + * enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_SW_DROP_RSN: Nested + * attribute representing the REO ring attributes which are defined in + * enum qca_wlan_vendor_telemetry_reo_ring_attr. Each REO ring attribute is + * further a nested attribute which represents the attributes that define the + * REO software error drops. The types of REO software error drops are defined + * in enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason. Since this error + * drop is applicable across all REO rings this is first nested with REO ring + * attributes and each REO ring attribute is further nested with the REO + * software error drop attributes. + * + * @QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_PER_PKT_STATS: Nested attribute + * representing the REO ring attributes which are defined in + * enum qca_wlan_vendor_telemetry_reo_ring_attr. Each REO ring attribute is + * further a nested attribute which represents the attributes that define the + * RX per packet statistics. The types of RX per packet statistics are defined + * in enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types. Since this + * statistics is applicable across all REO rings this is first nested with REO + * ring attributes and each REO ring attribute is further nested with the RX + * per packet statistics. + */ + +enum qca_wlan_vendor_attr_wlan_telemetry_rx_stats_types { + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_RXDMA_ERR_EVENT = 1, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_ERR_EVENT = 2, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_WBM_SW_DROP_RSN = 3, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_REO_SW_DROP_RSN = 4, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_PER_PKT_STATS = 5, + + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_WLAN_TELEMETRY_RX_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_tx_stats_types - Represents the + * attributes that define the types of TX statistics included in the event + * reply from the driver to a userspace application. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_COMP_ERR: Nested attribute representing + * the TCL ring attributes which are defined in + * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is + * further a nested attribute which represents the attributes that define the + * TX completion errors. The types of TX completion errors are defined in + * enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types. Since this error is + * applicable across all TCL rings this is first nested with TCL ring + * attributes and each TCL ring attribute is further nested with the TX + * completion errors. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_PER_PKT_STATS: Nested attribute + * representing the TCL ring attributes which are defined in + * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is + * further a nested attribute which represents the attributes that define the + * TX per packet statistics. The types of TX per packet statistics are defined + * in enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats. Since this + * statistics is applicable across all TCL rings this is first nested with TCL + * ring attributes and each TCL ring attribute is further nested with the TX + * per packet statistics. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INGRESS_STATS: Nested attribute + * representing the TCL ring attributes which are defined in + * enum qca_wlan_vendor_telemetry_tcl_ring_attr. Each TCL ring attribute is + * further a nested attribute which represents the attributes that define the + * TX ingress statistics. The types of TX ingress statistics are defined in + * enum qca_wlan_vendor_attr_telemetry_ingress_stats. Since this statistics is + * applicable across all TCL rings this is first nested with TCL ring + * attributes and each TCL ring attribute is further nested with the TX ingress + * statistics. + */ +enum qca_wlan_vendor_attr_telemetry_tx_stats_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_COMP_ERR = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_PER_PKT_STATS = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_INGRESS_STATS = 3, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TX_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_telemetry_tcl_ring_attr - Represents the attributes + * that define the types of TCL rings. These attributes are used for the + * statistics that are applicable per TCL ring. For statistics that are + * applicable to each individual TCL ring, the corresponding statistics + * attributes are nested under each TCL ring. This design ensures that every + * relevant statistics attribute is distinctly represented for each TCL ring + * enabling precise and ring-specific statistics collection. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_0: Nested attribute representing + * TCL ring 0. This will be nested with the statistics that are applicable + * per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_1: Nested attribute representing + * TCL ring 1. This will be nested with the statistics that are applicable + * per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_2: Nested attribute representing + * TCL ring 2. This will be nested with the statistics that are applicable + * per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_3: Nested attribute representing + * TCL ring 3. This will be nested with the statistics that are applicable + * per TCL ring. + */ +enum qca_wlan_vendor_telemetry_tcl_ring_attr { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_0 = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_1 = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_2 = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_3 = 4, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TCL_RING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_telemetry_reo_ring_attr - Represents the attributes + * that define the types of REO rings. These attributes are used for the + * statistics that are applicable per REO ring. For statistics that are + * applicable to each individual REO ring, the corresponding statistics + * attributes are nested under each REO ring. This design ensures that every + * relevant statistics attribute is distinctly represented for each REO ring + * enabling precise and ring-specific statistics collection. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_0: Nested attribute representing + * REO ring 0. This will be nested with the statistics that are applicable + * per REO ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_1: Nested attribute representing + * REO ring 1. This will be nested with the statistics that are applicable + * per REO ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_2: Nested attribute representing + * REO ring 2. This will be nested with the statistics that are applicable + * per REO ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_3: Nested attribute representing + * REO ring 3. This will be nested with the statistics that are applicable + * per REO ring + */ +enum qca_wlan_vendor_telemetry_reo_ring_attr { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_0 = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_1 = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_2 = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_3 = 4, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_RING_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_wlan_telemetry_rxdma_error_types - Representing the + * types of RXDMA errors. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_OVERFLOW: u32 attribute + * representing the number of MPDU frames that are not completed due to a + * FIFO overflow. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MPDU_LENGTH: u32 attribute + * representing the number of MPDU frames that are not complete due to receiving + * an incomplete MPDU from the PHY. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FCS: u32 attribute representing + * the FCS check failure on the MPDU frames. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DECRYPT: u32 attribute representing + * the decryption error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_TKIP_MIC: u32 attribute + * representing the TKIP MIC error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNENCRYPTED: u32 attribute + * representing the number of frames which are expected to be encrypted but was + * not encrypted. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LEN: u32 attribute + * representing the failures due to MPDU length errors. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LIMIT: u32 attribute + * representing the number of MSDUs which exceeded the max allowed in MPDUs. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_WIFI_PARSE: u32 attribute + * representing the wifi parsing error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_PARSE: u32 attribute + * representing the A-MSDU parsing error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_SA_TIMEOUT: u32 attribute + * representing the source address search timeout. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DA_TIMEOUT: u32 attribute + * representing the destination address search timeout. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLOW_TIMEOUT: u32 attribute + * representing the flow search timeout. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLUSH_REQUEST: u32 attribute + * representing the RXDMA FIFO flush requests. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_AMSDU_FRAGMENT: u32 attribute + * representing the presence of fragmented MPDUs as well as A-MSDUs, as + * reported by the RX PCU. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_MULTICAST_ECHO: u32 attribute + * representing multicast echo as reported by the RX OLE. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_ADDR_MISMATCH: u32 attribute + * representing the count of address mismatch in A-MSDU frames. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNAUTH_WDS: u32 attribute + * representing the count of unauthenticated WDS frames. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_GCAST_AMSDU_OR_WDS: u32 attribute + * representing count of the groupcast A-MSDU or WDS frames. + */ +enum qca_wlan_vendor_attr_wlan_telemetry_rxdma_error_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_OVERFLOW = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MPDU_LENGTH = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FCS = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DECRYPT = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_TKIP_MIC = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNENCRYPTED = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LEN = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MSDU_LIMIT = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_WIFI_PARSE = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_PARSE = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_SA_TIMEOUT = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_DA_TIMEOUT = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLOW_TIMEOUT = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_FLUSH_REQUEST = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_AMSDU_FRAGMENT = 15, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_MULTICAST_ECHO = 16, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AMSDU_ADDR_MISMATCH = 17, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_UNAUTH_WDS = 18, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_GCAST_AMSDU_OR_WDS = 19, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_RXDMA_ERR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wlan_telemetry_reo_error_types - Represents the + * types of REO errors. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_ADDR_0: u32 attribute + * representing the number of times the REO queue descriptor provided in the + * REO_ENTRANCE ring is 0. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_INVALID: u32 attribute + * representing the number of times the REO queue descriptor valid bit is not + * set. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AMPDU_IN_NON_BA: u32 attribute + * representing the number of times A-MPDU frames are received without BA + * session. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_NON_BA_DUPLICATE: u32 attribute + * representing the duplicate frames in a non BA session. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BA_DUPLICATE: u32 attribute + * representing the duplicate frame in a BA session. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_2K_JUMP: u32 attribute + * representing the number of times a normal frame (Management/Data frame) + * received with 2K jump in SN. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_2K_JUMP: u32 attribute + * representing the number of times a BAR frame received with 2K jump in SSN. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_OOR: u32 attribute + * representing the number of times a normal frame (Management/Data frame) + * received with SN falling within the OOR window. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_OOR: u32 attribute + * representing the number of times a BAR is received with SN falling within + * the OOR window. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_NO_BA_SESSION: u32 + * attribute representing the number of times a BAR received without a BA + * session. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_SN_EQUALS_SSN: u32 + * attribute representing the number of times a BAR received with SSN equal to + * SN. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_CHECK_FAILED: u32 attribute + * representing the number of times PN check failed for a frame. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_2K_ERROR_HANDLING_FLAG_SET: u32 + * attribute representing the number of times a frame is forwarded due to + * Seq_2k_error_detected_flag being set in the REO queue descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_ERROR_HANDLING_FLAG_SET: u32 + * attribute representing the number of times a frame is forwarded due to + * pn_error_detected_flag being set in the REO queue descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_BLOCKED_SET: u32 + * attribute representing the number of times a frame is forwarded as a result + * of the queue descriptor (address) being blocked as software/firmware appears + * to be in the process of updating this descriptor. + */ +enum qca_wlan_vendor_attr_wlan_telemetry_reo_error_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_ADDR_0 = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_INVALID = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AMPDU_IN_NON_BA = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_NON_BA_DUPLICATE = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BA_DUPLICATE = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_2K_JUMP = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_2K_JUMP = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_REGULAR_FRAME_OOR = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_OOR = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_NO_BA_SESSION = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_BAR_FRAME_SN_EQUALS_SSN = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_CHECK_FAILED = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_2K_ERROR_HANDLING_FLAG_SET = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_PN_ERROR_HANDLING_FLAG_SET = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_QUEUE_DESC_BLOCKED_SET = 15, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_ERR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason - Represents the + * attributes that define the types of RX WBM software drop reasons. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_ERR: u32 attribute + * representing the number of software drop from WBM due to descriptor error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CK_ERR: u32 attribute + * representing the number of software drop from WBM due to ck error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PEER_ID: u32 attribute + * representing the number of software drop from WBM due to invalid peer id. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_PARSE_ERR: u32 attribute + * representing the number of software drop from WBM due to descriptor parse + * error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_COOKIE: u32 attribute + * representing the number of software drop from WBM due to invalid cookie. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PUSH_RSN: u32 attribute + * representing the number of software drop from WBM due to invalid push reason. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_HW_ID: u32 attribute + * representing the number of software drop from WBM due to invalid hardware id. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PTNR_DP_NA: u32 attribute + * representing the number of software drop from WBM due to NULL partner + * datapath member. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PROC_NA_PTNR_DP: u32 attribute + * representing the number of software drop from WBM while processing NULL + * partner datapath member. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_PDEV: u32 attribute + * representing the number of software drop from WBM due to NULL pdev. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_AR: u32 attribute + * representing the number of software drop from WBM due to NULL ar. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CAC_RUNNING: u32 attribute + * representing the number of software drop from WBM due to CAC running. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_SCATTER_GATHER: u32 attribute + * representing the number of software drop from WBM due to scatter gather. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_NWIFI_HLEN: u32 attribute + * representing the number of software drop from WBM due to invalid native + * Wi-Fi header length. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_REO_GENERIC: u32 attribute + * representing the number of software drop from WBM due to generic REO error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_RXDMA_GENERIC: u32 attribute + * representing the number of software drop from WBM due to generic RXDMA error. + */ +enum qca_wlan_vendor_attr_telemetry_rx_wbm_sw_drop_reason { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_ERR = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CK_ERR = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PEER_ID = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_DESC_PARSE_ERR = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_COOKIE = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_PUSH_RSN = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_HW_ID = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PTNR_DP_NA = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_PROC_NA_PTNR_DP = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_PDEV = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_NULL_AR = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_CAC_RUNNING = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_SCATTER_GATHER = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_INVAL_NWIFI_HLEN = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_REO_GENERIC = 15, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_RXDMA_GENERIC = 16, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_WBM_DROP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason - Represents the + * attributes that define the types of REO software drops for each REO ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MISC: u32 attribute + * representing the number of software drop from REO due to miscellaneous + * reason. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_CK_ERR: u32 attribute + * representing the number of software drop from REO due to ck error in + * software descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_DESC_ERR: u32 attribute + * representing the number of software drop from REO due to error in software + * descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_REPLENISH: u32 attribute + * representing the number of software drop from REO due to replenish error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PTNR_DP_NA: u32 attribute + * representing the number of software drop from REO due to NULL partner + * datapath member. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PDEV_NA: u32 attribute + * representing the number of software drop from REO due to NULL pdev. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_LAST_MSDU_NA: u32 attribute + * representing the number of software drop from REO due to last MSDU bit not + * found. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_NWIFI_HLEN_INVAL: u32 attribute + * representing the number of software drop from REO due to invalid native + * Wi-Fi header length. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_MSDU_LEN: u32 attribute + * representing the number of software drop from REO due to invalid MSDU length. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MSDU_COAL_FAIL: u32 attribute + * representing the number of software drop from REO due to MSDU coalesce + * failure. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MPDU: u32 attribute + * representing the number of software drop from REO due to MPDU drop. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PPDU: u32 attribute + * representing the number of software drop from REO due to PPDU drop. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_PEER: u32 attribute + * representing the number of software drop from REO due to invalid peer. + */ +enum qca_wlan_vendor_attr_telemetry_reo_sw_drop_reason { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MISC = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_CK_ERR = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_DESC_ERR = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_REPLENISH = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PTNR_DP_NA = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PDEV_NA = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_LAST_MSDU_NA = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_NWIFI_HLEN_INVAL = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_MSDU_LEN = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MSDU_COAL_FAIL = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MPDU = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_PPDU = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_INVAL_PEER = 13, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_REO_DROP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types: Represents the + * attributes that define the various per packet RX statistics populated + * for each REO rings. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_FROM_REO: Nested attribute + * representing the attributes that defines the information of MSDUs received + * from REO ring in per packet path. The types of packet information are + * defined in enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK: Nested attribute + * representing the attributes that define the information of MSDUs sent to + * stack in per packet path. The types of packet information are defined in + * enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK_FAST: Nested attribute + * representing the attributes that define the information of MSDUs sent to + * stack in fast path. The types of packet information are defined in + * enum qca_wlan_vendor_attr_telemetry_pktinfo + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MCAST: u32 attribute representing + * the number of RX multicast MSDUs received in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_UCAST: u32 attribute representing + * the number of RX unicast MSDUs received in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_NON_AMSDU: u32 attribute + * representing the number of RX MSDUs received in per packet path that were + * not part of an A-MSDU. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MSDU_IN_AMSDU: u32 attribute + * representing the number of RX MSDUs received in per packet path that were + * part of an A-MSDU. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MPDU_RETRY: u32 attribute + * representing the number of RX MPDU retries happened in per packet path. + */ +enum qca_wlan_vendor_attr_telemetry_per_pkt_stats_rx_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_FROM_REO = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_TO_STK_FAST = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MCAST = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_UCAST = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_NON_AMSDU = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MSDU_IN_AMSDU = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_RX_MPDU_RETRY = 8, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_STATS_RX_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types - Represents the + * attributes that define the types of TX completion error for each TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_MISC: u32 attribute representing + * the number of MSDUs with TX completion error due to miscellaneous reasons. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_DESC: u32 attribute + * representing the number of MSDUs with TX completion error due to invalid + * descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PDEV: u32 attribute + * representing the number of MSDUs with TX completion error due to invalid + * pdev. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_VIF: u32 attribute + * representing the number of MSDUs with TX completion error due to invalid vif. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PEER: u32 attribute + * representing the number of MSDUs with TX completion error due to invalid + * peer. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_LINK_PEER: u32 attribute + * representing the number of MSDUs with TX completion error due to invalid + * link peer. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_DESC_INUSE: u32 attribute + * representing the number of MSDUs with TX completion error due to + * descriptor in use. + */ +enum qca_wlan_vendor_attr_telemetry_tx_comp_err_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_MISC = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_DESC = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PDEV = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_VIF = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_PEER = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_INVAL_LINK_PEER = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_DESC_INUSE = 7, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TXCOMP_ERR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_pktinfo - Represents the attributes that + * define the packet information. + * + * @QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_PKTS: u32 attribute representing the + * number of MSDUs transmitted/received. + * + * @QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_BYTES: u64 attribute representing + * the number of bytes transmitted/received. + */ +enum qca_wlan_vendor_attr_telemetry_pktinfo { + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_INVALID = 0, + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_PKTS = 1, + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_BYTES = 2, + + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_AFTER_LAST, + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_MAX = + QCA_WLAN_VENDOR_TELEMETRY_ATTR_PKTINFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_ingress_stats - Represents the attributes + * that define the types of TX ingress statistics for each TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_FROM_STK: Nested attribute + * representing the attributes that define the information of ingress MSDUs + * received from stack. The types of packet information are defined in + * enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW: Nested attribute + * representing the attributes that define the information of ingress MSDUs + * enqueued to hardware. The types of packet information are defined in + * enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW_FAST: Nested attribute + * representing the attributes that define the information of ingress MSDUs + * enqueued to hardware in fast path. The types of packet information are + * defined in enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCAP_TYPE: Nested attribute + * representing the attributes that define the count of ingress MSDUs with + * respective encapsulation types. The types of encapsulation are defined + * in enum qca_wlan_vendor_attr_telemetry_ingress_encap_type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCRYPT_TYPE: Nested attribute + * representing the attributes that define the count of ingress MSDUs with + * respective encryption types. The types of encryption are defined in + * enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DESC_TYPE: Nested attribute + * representing the attributes that define the count of ingress MSDUs with + * respective descriptor type. The types of descriptor are defined in + * enum qca_wlan_vendor_attr_telemetry_ingress_desc_type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DROP_TYPE: Nested attribute + * representing the attributes that define the count of ingress MSDUs with + * respective drop types. The types of drops are defined in + * enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MCAST: u32 attribute representing + * the number of ingress multicast MSDUs. + */ +enum qca_wlan_vendor_attr_telemetry_ingress_stats { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_FROM_STK = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_PKT_TO_HW_FAST = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCAP_TYPE = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_ENCRYPT_TYPE = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DESC_TYPE = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_DROP_TYPE = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MCAST = 8, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_ingress_encap_type - Represents the + * attributes that define the types of packet encapsulation. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_RAW: u32 attribute representing the + * number of MSDUs with raw encapsulation type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_NWIFI: u32 attribute representing + * the number of MSDUs with native Wi-Fi encapsulation type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_ETH: u32 attribute representing + * the number of MSDUs with ethernet encapsulation type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_802_3: u32 attribute representing + * the number of MSDUs with 802_3 encapsulation type. + */ +enum qca_wlan_vendor_attr_telemetry_ingress_encap_type { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_RAW = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_NWIFI = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_ETH = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_802_3 = 4, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENCAP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type - Represents the + * attributes that define the types of packet encryption. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_NO_MIC: u32 attribute + * representing the number of MSDUs with encryption type TKIP_NO_MIC. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_MIC: u32 attribute representing + * the number of MSDUs with encryption type TKIP_MIC. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI: u32 attribute representing + * the number of MSDUs with encryption type WAPI. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_128: u32 attribute representing + * the number of MSDUs with encryption type CCMP_128. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_OPEN: u32 attribute representing + * the number of MSDUs with encryption type open. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_256: u32 attribute representing + * the number of MSDUs with encryption type CCMP_256. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_128: u32 attribute representing + * the number of MSDUs with encryption type GCMP-128. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_256: u32 attribute + * representing the number of MSDUs with encryption type GCMP-256. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI_GCM_SM4: u32 attribute + * representing the number of MSDUs with encryption type WAPI_GCM_SM4. + */ +enum qca_wlan_vendor_attr_telemetry_ingress_encrypt_type { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_NO_MIC = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_TKIP_MIC = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_128 = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_OPEN = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_CCMP_256 = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_128 = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_GCMP_256 = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_WAPI_GCM_SM4 = 9, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENC_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_ingress_desc_type - Represents the + * attributes that define the ingress MSDUs descriptor types. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_BUF: u32 attribute representing + * the number of ingress MSDUs with buffer descriptor type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_EXT_DESC: u32 attribute + * representing the number of ingress MSDUs with extended descriptor type. + */ +enum qca_wlan_vendor_attr_telemetry_ingress_desc_type { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_BUF = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_EXT_DESC = 2, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_DESC_TYPE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types - Represents + * the attributes that define the ingress enqueue drop error types. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MISC: u32 attribute representing + * the number of ingress MSDUs dropped due to miscellaneous reason. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_VIF_TYPE_MON: u32 attribute + * representing the number of ingress MSDUs dropped due to monitor vif type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_LINK: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid link id. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid arvif. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MGMT_FRAME: u32 attribute + * representing the number of ingress MSDUs dropped as it is Management frame. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TX_LIMIT: u32 attribute + * representing the number of ingress MSDUs dropped due to maximum TX limit + * reached. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid pdev. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PEER: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid peer. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_CACHE_FLUSH: u32 attribute + * representing the number of ingress MSDUs dropped due to cache flush. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_NON_DATA_FRAME: u32 attribute + * representing the number of ingress MSDUs dropped due to non Data frame. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_SW_DESC_NA: u32 attribute + * representing the number of ingress MSDUs dropped due to null software + * descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_RAW: u32 attribute + * representing the number of ingress MSDUs dropped due to raw encapsulation + * type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_802_3: u32 attribute + * representing the number of ingress MSDUs dropped due to 802_3 encapsulation + * type. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_DMA_ERR: u32 attribute representing + * the number of ingress MSDUs dropped due to DMA error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_EXT_DESC_NA: u32 attribute + * representing the number of ingress MSDUs dropped due to null extended + * descriptor. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_HTT_MDATA_ERR: u32 attribute + * representing the number of ingress MSDUs dropped due to HTT metadata error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_NA: u32 attribute + * representing the number of ingress MSDUs dropped due to null descriptor + * error. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_RETRY: u32 attribute + * representing the number of ingress MSDUs dropped due to TCL descriptor retry. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF_FAST: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid arvif in + * fast path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV_FAST: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid pdev in fast + * path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TXLIMIT_FAST: u32 attribute + * representing the number of ingress MSDUs dropped due to maximum TX limit + * reached in fast path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ENCAP_FAST: u32 attribute + * representing the number of ingress MSDUs dropped due to invalid + * encapsulation in fast path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_BRIDGE_VDEV: u32 attribute + * representing the number of ingress MSDUs dropped due to bridge vdev. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ARSTA_NA: u32 attribute representing + * the number of ingress MSDUs dropped due to arsta being NULL. + */ +enum qca_wlan_vendor_attr_telemetry_ingress_enqueue_error_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MISC = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_VIF_TYPE_MON = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_LINK = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MGMT_FRAME = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TX_LIMIT = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PEER = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_CACHE_FLUSH = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_NON_DATA_FRAME = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_SW_DESC_NA = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_RAW = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ENCAP_802_3 = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_DMA_ERR = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_EXT_DESC_NA = 15, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_HTT_MDATA_ERR = 16, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_NA = 17, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_TCL_DESC_RETRY = 18, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ARVIF_FAST = 19, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_PDEV_FAST = 20, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_MAX_TXLIMIT_FAST = 21, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_INV_ENCAP_FAST = 22, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_BRIDGE_VDEV = 23, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ARSTA_NA = 24, + + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_ERR_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_ENQ_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats - Represents the + * attributes that define the various per packet TX statistics per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_COMP_PKT: Nested attribute + * representing the attributes that define the information of TX MSDUs for + * which TX completion is received. The types of packet information are defined + * in enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_SUCCESS: Nested attribute + * representing the attributes that define the information of TX MSDUs which + * are successfully sent. The types of packet information are defined in + * enum qca_wlan_vendor_attr_telemetry_pktinfo. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_FAILED: u32 attribute representing + * the number of MSDUs for which TX is failed in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_WBM_REL_RSN: Nested attribute + * representing the attributes that define the types of TX WBM release reasons. + * The types of WBM release reasons are defined in + * enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TQM_REL_RSN: Nested attribute + * representing the attributes that define the types of TX TQM release reasons. + * The types of TQM release reasons are defined in + * enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_REL_SRC_NOT_TQM: u32 attribute + * representing the number of MSDUs for which release source is not TQM in TX + * completion status processing. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_RETRY_COUNT: u32 attribute + * representing the number of MSDUs successfully sent after one retry in per + * packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TOTAL_MSDU_RETRY: u32 attribute + * representing the total number of MSDUs retransmitted in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MULTI_RETRY_CNT: u32 attribute + * representing the number of MSDUs successfully sent after more than one retry + * in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_OFDMA: u32 attribute representing + * the number of TX OFDMA MSDUs transmitted in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AMSDU_CNT: u32 attribute + * representing the number of TX MSDUs transmitted in per packet path that + * are part of an A-MSDU. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_NON_AMSDU_CNT: u32 attribute + * representing the number of TX MSDUs transmitted in per packet path with no + * MSDU level aggregation. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVAL_LINK_ID: u32 attribute + * representing the number of TX MSDUs transmitted in per packet path for which + * the TX completion came with invalid link id. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MCAST: u32 attribute representing + * the number of TX multicast MSDUs transmitted in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_UCAST: u32 attribute representing + * the number of TX unicast MSDUs transmitted in per packet path. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_BCAST: u32 attribute representing + * the number of TX broadcast MSDUs transmitted in per packet path. + */ +enum qca_wlan_vendor_attr_telemetry_per_pkt_tx_stats { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_COMP_PKT = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_SUCCESS = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_FAILED = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_WBM_REL_RSN = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TQM_REL_RSN = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_REL_SRC_NOT_TQM = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_RETRY_COUNT = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_TOTAL_MSDU_RETRY = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MULTI_RETRY_CNT = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_OFDMA = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AMSDU_CNT = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_NON_AMSDU_CNT = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_INVAL_LINK_ID = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MCAST = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_UCAST = 15, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_BCAST = 16, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_PPKT_TX_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status - Represents the + * attributes that define the types of TX completion status per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_OK: u32 attribute + * representing the number of MSDUs for which TX completion status is OK. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_DROP: u32 attribute + * representing the number of MSDUs which were dropped while transmitting. The + * TX completion status for these frames will be with reason 'drop'. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_TTL: u32 attribute + * representing the number of MSDUs for which TTL timeout occurred while + * transmitting. The TX completion status for these frames will be with reason + * 'TTL'. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_REINJ: u32 attribute + * representing the number of MSDUs which are reinjected. The TX completion + * status for these frames will be with reason 'REINJECT'. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INSPECT: u32 attribute + * representing the number of MSDUs which are under inspection. The TX + * completion status for these frames will be with reason 'INSPECT'. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_MEC_NOTIFY: u32 attribute + * representing the number of MSDUs with mec notify error. The TX completion + * status for these frames will be with reason 'MEC_NOTIFY'. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_VDEVID_MISMATCH: u32 attribute + * representing the number of MSDUs for which vdev id mismatch occurred. The TX + * completion status for these frames will be with reason 'VDEVID_MISMATCH'. + */ +enum qca_wlan_vendor_attr_telemetry_htt_tx_comp_status { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_OK = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_DROP = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_TTL = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_REINJ = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_INSPECT = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_MEC_NOTIFY = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_VDEVID_MISMATCH = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_HTT_TXCOMP_STATUS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types: Represents the + * attributes that define the types of TQM release reason per TCL ring. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_FRAME_ACKED: u32 attribute + * representing the number of MSDUs for which ACK of BA is received. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_MPDU: u32 attribute + * representing the number of MSDUs for which remove command of type + * “Remove_mpdus” is initiated. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_TX: u32 attribute + * representing the number of MSDUs for which remove command of type + * "Remove_transmitted_mpdus" is initiated. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_NOTX: u32 attribute + * representing the number of MSDUs for which remove command of type + * "Remove_untransmitted_mpdus" is initiated. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_AGED: u32 attribute + * representing the number of MSDUs for which remove command of type + * “Remove_aged_mpdus” or “Remove_aged_msdus” is initiated. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN1: u32 attribute + * representing the number of MSDUs for which remove command is given where + * firmware indicated that remove reason is fw_reason1. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN2: u32 attribute + * representing the number of MSDUs for which remove command is given where + * firmware indicated that remove reason is fw_reason2. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN3: u32 attribute + * representing the number of MSDUs for which remove command is given where + * firmware indicated that remove reason is fw_reason3. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_QUE_DISABLE: u32 attribute + * representing the number of MSDUs for which remove command is given where + * firmware indicated that remove reason is disabled queue. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_TILL_NONMATCH: u32 attribute + * representing the number of MSDUs for which remove command is given from + * firmware to remove all MPDU until the first non-match. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_THRESHOLD: u32 attribute + * representing the number of MSDUs dropped due to drop threshold criteria. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_LINK_DESC_NA: u32 attribute + * representing the number of MSDUs dropped due to link desc not available. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_OR_INVAL_MSDU: u32 attribute + * representing the number of MSDUs dropped due to drop bit set or null flow. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MCAST_DROP: u32 attribute + * representing the number of MSDUs dropped due to multicast drop set for VDEV. + * + * @QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_VDEV_MISMATCH: u32 attribute + * representing the number of MSDUs dropped due to vdev Id mismatch. + */ +enum qca_wlan_vendor_attr_telemetry_tqm_rel_reason_types { + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_FRAME_ACKED = 1, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_MPDU = 2, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_TX = 3, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_NOTX = 4, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_AGED = 5, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN1 = 6, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN2 = 7, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_REM_RSN3 = 8, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_QUE_DISABLE = 9, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_CMD_TILL_NONMATCH = 10, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_THRESHOLD = 11, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_LINK_DESC_NA = 12, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_DROP_OR_INVAL_MSDU = 13, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MCAST_DROP = 14, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_VDEV_MISMATCH = 15, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_MAX = + QCA_WLAN_VENDOR_ATTR_TELEMETRY_TQM_REL_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_link_state_change_reason - Defines the different types + * of reasons for the link state change. Will be used with + * QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON attribute. + * + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_VDEV_UP: The link state change reported + * first time after the STA connection/interface up. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_RSSI: The link state change is due + * to low RSSI. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BMISS: The link state change is due to + * beacon miss. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_QUALITY: The link state change is + * due to low link quality. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_CAPACITY: The link state change is due + * to congestion of the links. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_ULL_MODE: The link state change is due + * to ultra low latency mode. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_T2LM: The link state change is due to + * TID_TO_LINK mapping change. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_FORCED: The link state change is due to + * user forced enable/disable. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BT_STATUS: The link state change is due + * to BT status. + * @QCA_WLAN_VENDOR_STATE_CHANGE_REASON_UNKNOWN: The link state change is due + * to unknown reason. + */ +enum qca_wlan_vendor_link_state_change_reason { + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_VDEV_UP = 0, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_RSSI = 1, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BMISS = 2, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_LOW_QUALITY = 3, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_CAPACITY = 4, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_ULL_MODE = 5, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_T2LM = 6, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_FORCED = 7, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_BT_STATUS = 8, + QCA_WLAN_VENDOR_STATE_CHANGE_REASON_UNKNOWN = 9, +}; + +/** + * enum qca_wlan_vendor_attr_link_state_change - Attributes used by + * QCA_NL80211_VENDOR_SUBCMD_LINK_STATE_CHANGE vendor command. + * + * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_PEER_MLD_MAC: 6 byte MAC address. + * Indicates the associated AP MLD MAC address for the non-AP MLD interface. + * Indicates the peer MLD MAC address for the AP MLD interface + * + * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON: u32 attribute. Indicates the + * reason for the link state change triggering the event. + * See enum qca_wlan_vendor_link_state_change_reason for the possible + * reasons. + * + * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_ACTIVE_LINK_BITMAP: u32 attribute. + * Indicates the link ID bitmap of the current active links among the affliated + * MLO links. + * + * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INACTIVE_LINK_BITMAP: u32 attribute. + * Indicates the link ID bitmap of the current inactive links among the + * affiliated MLO links. + */ +enum qca_wlan_vendor_attr_link_state_change { + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_PEER_MLD_MAC = 1, + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_REASON = 2, + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_ACTIVE_LINK_BITMAP = 3, + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_INACTIVE_LINK_BITMAP = 4, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_MAX = + QCA_WLAN_VENDOR_ATTR_LINK_STATE_CHANGE_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_latency_report: Represents the possible values for + * %QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE. + * + * @QCA_WLAN_DAR_LATENCY_REPORT_HISTOGRAM: Indicates the Latency Statistics + * List field contains, or is requested to contain, histogram(s) of latency + * measurements. + * + * @QCA_WLAN_DAR_LATENCY_REPORT_PERCENTILE: Indicates the Latency Statistics + * List field contains, or is requested to contain, set(s) of percentile values + * of latency. + * + * @QCA_WLAN_DAR_LATENCY_REPORT_NO_PREF: Indicates the request has no preference + * as to the contents of the Latency Statistics List field; this value is + * reserved when the attribute is included in a DAR Response or DAR Report frame + */ +enum qca_wlan_dar_latency_report { + QCA_WLAN_DAR_LATENCY_REPORT_HISTOGRAM = 0, + QCA_WLAN_DAR_LATENCY_REPORT_PERCENTILE = 1, + QCA_WLAN_DAR_LATENCY_REPORT_NO_PREF = 2, +}; + +/* + * enum qca_wlan_dar_report_granularity: Represents the possible values for + * %QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY, + * %QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY, + * %QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY. + * + * @QCA_WLAN_DAR_REPORT_GRANULARITY_TID: Indicates the DAR report field + * contains, or is requested to contain, values with granularity at the + * TID level. + * + * @QCA_WLAN_DAR_REPORT_GRANULARITY_AC: Indicates the DAR report field + * contains, or is requested to contain, values with granularity at the AC + * level. + * + * @QCA_WLAN_DAR_REPORT_GRANULARITY_AGGREGATED_AC: Indicates the DAR field + * contains, or is requested to contain, values with granularity aggregated + * across all ACs. + */ +enum qca_wlan_dar_report_granularity { + QCA_WLAN_DAR_REPORT_GRANULARITY_TID = 0, + QCA_WLAN_DAR_REPORT_GRANULARITY_AC = 1, + QCA_WLAN_DAR_REPORT_GRANULARITY_AGGREGATED_AC = 2, +}; + +/* + * enum qca_wlan_dar_link_granularity: Represents the possible values + * for %QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY, + * %QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY, + * %QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY, + * %QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY, + * %QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY. + * + * @QCA_WLAN_DAR_LINK_GRANULARITY_DEVICE_LEVEL: Indicates the Latency + * Statistics List field contains, or is requested to contain, values at the + * device level. If the DAR responder is an MLD, this means at the MLD level. + * + * @QCA_WLAN_DAR_LINK_GRANULARITY_PER_LINK: Indicates the Latency + * Statistics List field contains, or is requested to contain, values at the + * link level (for an MLD). This value is only used if the DAR responder is an + * MLD. + */ +enum qca_wlan_dar_link_granularity { + QCA_WLAN_DAR_LINK_GRANULARITY_DEVICE_LEVEL = 0, + QCA_WLAN_DAR_LINK_GRANULARITY_PER_LINK = 1, +}; + +/* + * enum qca_wlan_dar_stats_latency_attr - Represents the possible latency + * statistics attributes for configuring + * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG. + * + * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE: Optional u8 attribute, + * indicates the type of report. Values are defined in + * enum qca_wlan_dar_latency_report. + * + * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY: Optional u8 attribute, + * indicates the report granularity. Values are defined in enum + * qca_wlan_dar_report_granularity. + * + * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY: Optional u8 attribute, + * indicates the link granularity. Values are defined in enum + * qca_wlan_dar_link_granularity. + * + * @QCA_WLAN_DAR_STATS_LATENCY_ATTR_THRESHOLDS: An array of threshold values + * used to trigger reporting of latency statistics. Indexed first by link + * identifier. The number of threshold values is determined by: A * B * C, where + * - A is the Hamming weight of the Report Granularity Bitmap if Report + * Granularity is 0 or 1; otherwise A is 1. + * - B is the Hamming weight of the Link Granularity Bitmap if Link Granularity + * is 1; otherwise B is 1. + */ +enum qca_wlan_dar_stats_latency_attr { + QCA_WLAN_DAR_STATS_LATENCY_ATTR_INVALID = 0, + QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_TYPE = 1, + QCA_WLAN_DAR_STATS_LATENCY_ATTR_REPORT_GRANULARITY = 2, + QCA_WLAN_DAR_STATS_LATENCY_ATTR_LINK_GRANULARITY = 3, + QCA_WLAN_DAR_STATS_LATENCY_ATTR_THRESHOLDS = 4, + + /* keep last */ + QCA_WLAN_DAR_STATS_LATENCY_ATTR_AFTER_LAST, + QCA_WLAN_DAR_STATS_LATENCY_ATTR_MAX = + QCA_WLAN_DAR_STATS_LATENCY_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_radio_counter_fields - Represents the possible subfields in + * the radio counter attribute for + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_TRANSMIT_POWER_FIELD: Indicates that transmit + * power field is included in the radio counter attribute. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD: Indicates that + * observed cu fraction field is included in the radio counter attribute. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD: Indicates that MPDU + * count statistics field is included in the radio counter attribute. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD: Indicates that RTS + * statistics field is included in the radio counter attribute. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD: Indicates that FCS failure + * field is included in the radio counter attribute. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_THRESHOLD_FIELDS_PRESENT: Indicates the presence + * of threshold fields in the radio counter attribute. This bit is set to 0 if + * the attribute is in a DAR Report frame. + */ +enum qca_wlan_dar_radio_counter_fields { + QCA_WLAN_DAR_RADIO_COUNTERS_TRANSMIT_POWER_FIELD = BIT(0), + QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD = BIT(1), + QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD = BIT(2), + QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD = BIT(3), + QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD = BIT(4), + QCA_WLAN_DAR_RADIO_COUNTERS_THRESHOLD_FIELDS_PRESENT = BIT(5), +}; + +/* + * enum qca_wlan_dar_observed_cu_fractional_fields_attr - Represents the + * possible parameters inside the attribute + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL + * + * @QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY: Optional u8 + * attribute, indicates the granularity level of the Observed CU Fraction List + * field. Values are defined in enum qca_wlan_dar_link_granularity. + * + * @QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_THRESHOLDS: An array of + * possible 8-bit values, indexed by the link identifier. List of thresholds + * each of which is channel utilization fraction normalized to 255. If the + * granularity is at device level only one value will be included in this list. + */ +enum qca_wlan_dar_observed_cu_fractional_fields_attr { + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_INVALID = 0, + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_LINK_GRANULARITY = 1, + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_THRESHOLDS = 2, + + /* keep last */ + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_AFTER_LAST, + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_MAX = + QCA_WLAN_DAR_OBSERVED_CU_FRACTIONAL_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_mpdu_count_stats_field_attr - Represents the possible + * radio statistics attribute configuration for + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS. + * + * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY: u8 attribute, + * Indicates the granularity level of the MPDU count statistics attribute field. + * values are defined in enum qca_wlan_dar_report_granularity. + * + * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY: u8 attribute, + * Indicates the granularity level of the MPDU count stats field. + * Values are defined in enum qca_wlan_dar_link_granularity. + * + * @QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_THRESHOLDS: An array of unsigned 8-bit + * values. Indexed by the link identifier. List of thresholds each of which is + * a one-octet percentile MPDU Retry Rate, equal to {100 * MPDU Retry Count / + * Total MPDU Count}, rounded to the nearest integer. If the Total MPDU Count + * for a measurement is zero, then the threshold is considered to not have been + * exceeded for that measurement. If the granularity is at device level only one + * value will be included in this list. + */ +enum qca_wlan_dar_mpdu_count_stats_field_attr { + QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_INVALID = 0, + QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_REPORT_GRANULARITY = 1, + QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_LINK_GRANULARITY = 2, + QCA_WLAN_DAR_MPDU_COUNT_STATS_ATTR_THRESHOLDS = 3, + + /* keep last */ + QCA_WLAN_DAR_MPDU_COUNT_STATS_AFTER_LAST, + QCA_WLAN_DAR_MPDU_COUNT_STATS_MAX = + QCA_WLAN_DAR_MPDU_COUNT_STATS_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_fcs_failure_fields_attr - Represents the possible + * parameters inside the attribute + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE. + * + * @QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY: u8 attribute, + * Indicates the granularity level of the FCS failure link field. + * Values are defined in enum qca_wlan_dar_link_granularity. + */ +enum qca_wlan_dar_fcs_failure_fields_attr { + QCA_WLAN_DAR_FCS_FAILURE_ATTR_INVALID = 0, + QCA_WLAN_DAR_FCS_FAILURE_ATTR_LINK_GRANULARITY = 1, + + /* keep last */ + QCA_WLAN_DAR_FCS_FAILURE_ATTR_AFTER_LAST, + QCA_WLAN_DAR_FCS_FAILURE_ATTR_MAX = + QCA_WLAN_DAR_FCS_FAILURE_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_rts_stats_field_attr - Represents the possible + * radio stats attribute configuration for + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS. + * + * @QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY: Optional u8 attribute, + * Indicates the granularity level of the RTS statistics attribute field. + * Values are defined in enum qca_wlan_dar_report_granularity. + * + * @QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY: Optional u8 attribute, + * Indicates the granularity level of the RTS stats field. + * Values are defined in enum qca_wlan_dar_link_granularity. + * + * @QCA_WLAN_DAR_RTS_STATS_ATTR_THRESHOLDS: An array of unsigned 8-bit values. + * Indexed by the link identifier. List of thresholds each of which is a + * one-octet percentile RTS Failure Rate, equal to {100 * RTS Failure Count / + * Total RTS Count}, rounded to the nearest integer. If the Total RTS Count for + * a measurement is zero, then the threshold is considered to not have been + * exceeded for that measurement. If the granularity is at device level only one + * value will be included in this list. + */ +enum qca_wlan_dar_rts_stats_field_attr { + QCA_WLAN_DAR_RTS_STATS_ATTR_INVALID = 0, + QCA_WLAN_DAR_RTS_STATS_ATTR_REPORT_GRANULARITY = 1, + QCA_WLAN_DAR_RTS_STATS_ATTR_LINK_GRANULARITY = 2, + QCA_WLAN_DAR_RTS_STATS_ATTR_THRESHOLDS = 3, + + /* keep last */ + QCA_WLAN_DAR_RTS_STATS_ATTR_AFTER_LAST, + QCA_WLAN_DAR_RTS_STATS_ATTR_MAX = + QCA_WLAN_DAR_RTS_STATS_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_radio_counters_attr - Represents the possible + * radio counters attribute configuration for + * %QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP: Mandatory u16 + * attribute, indicates the Radio Counter fields that are requested to the peer + * in the DAR request frame. + * Values are defined in enum qca_wlan_dar_radio_counter_fields. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL: + * Optional nested attribute to configure the parameters that will be included + * in observed channel utilization fractional field. + * This attribute is included only when + * %QCA_WLAN_DAR_RADIO_COUNTERS_OBSERVED_CU_FRACTION_FIELD bit is set in the + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP. + * The attributes used inside this nested attribute is defined in enum + * qca_wlan_dar_observed_cu_fractional_fields_attr. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS: Optional nested attribute + * to configure the parameters that will be included in MPDU Count statistics + * field. + * This attribute is included only when + * %QCA_WLAN_DAR_RADIO_COUNTERS_MPDU_COUNT_STATISTICS_FIELD bit is set in the + * %QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP. + * The attribute used inside this nested attribute is defined in enum + * qca_wlan_dar_mpdu_count_stats_field_attr. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS: Optional nested attribute to + * configure the parameters that will be included in RTS statistics field. + * This attribute is included only when + * %QCA_WLAN_DAR_RADIO_COUNTERS_RTS_STATISTICS_FIELD bit is set in the radio + * counters presence field bitmap. + * The attribute used inside this nested attribute is defined in enum + * qca_wlan_dar_rts_stats_field_attr. + * + * @QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE: Optional nested attribute to + * configure the parameters that will be included in FCS failure statistics + * field. + * This attribute is included only when + * %QCA_WLAN_DAR_RADIO_COUNTERS_FCS_FAILURE_FIELD bit is set in the radio + * counters presence field bitmap. + * The attribute used inside this nested attribute is defined in enum + * qca_wlan_dar_fcs_failure_fields_attr. + */ +enum qca_wlan_dar_radio_counters_attr { + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_INVALID = 0, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_PRESENCE_BITMAP = 1, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_OBSERVED_CU_FRACTIONAL = 2, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MPDU_COUNT_STATS = 3, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_RTS_STATS = 4, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_FCS_FAILURE = 5, + + /* keep last */ + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_AFTER_LAST, + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_MAX = + QCA_WLAN_DAR_RADIO_COUNTERS_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_tuple_codes_attr - Represents the possible control plane + * events tuple code parameters in a request for + * %QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES. + * + * @QCA_WLAN_DAR_TUPLE_CODES_ATTR_CATEGORY_CODE: Optional u8 attribute. + * Indicates the category of the control plane event as defined in Wi-Fi QoS + * Management Specification Version 3.0 + WorkingR4, Table 32. Control Plane + * Event Codes. + * @QCA_WLAN_DAR_TUPLE_CODES_ATTR_SUBCATEGORY_CODE: Optional u8 attribute + * Indicates the subcategory of the control plane event as defined in Wi-Fi QoS + * Management Specification Version 3.0 + WorkingR4, Table 32. Control Plane + * Event Codes. + */ +enum qca_wlan_dar_tuple_codes_attr { + QCA_WLAN_DAR_TUPLE_CODES_ATTR_INVALID = 0, + QCA_WLAN_DAR_TUPLE_CODES_ATTR_CATEGORY_CODE = 1, + QCA_WLAN_DAR_TUPLE_CODES_ATTR_SUBCATEGORY_CODE = 2, + + /* keep last */ + QCA_WLAN_DAR_TUPLE_CODES_ATTR_AFTER_LAST, + QCA_WLAN_DAR_TUPLE_CODES_ATTR_MAX = + QCA_WLAN_DAR_TUPLE_CODES_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_control_plane_events_attr - Represents the possible control + * plane events attribute configuration for + * %QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG. + * + * @QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT: u8 attribute. + * This attribute specifiess the number of control plane event tuples. + * @QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES: Optional array of nested + * attributes specified by enum qca_wlan_dar_tuple_codes_attr. The size of the + * array specified by %QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT + * attribute. + */ +enum qca_wlan_dar_control_plane_events_attr { + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_INVALID = 0, + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_EVENT_COUNT = 1, + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_TUPLE_CODES = 2, + + /* keep last */ + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_AFTER_LAST, + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_MAX = + QCA_WLAN_DAR_CONTROL_PLANE_EVENTS_ATTR_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_dar_stats_types - Represents the type of the DAR statistics + * attribute for %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP. + * + * @QCA_WLAN_DAR_STATS_TYPE_LATENCY: Indicates that latency statistic attribute + * is requested in DAR request frame and DAR session terminate in the DAR + * response attribute. + * @QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS: Indicates that radio counters + * statistic attribute is requested in DAR request frame and DAR session + * terminate in the DAR response attribute. + * @QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE: Indicates that control plane + * statistic attribute is requested in DAR request frame and DAR session + * terminate in the DAR response attribute. + */ +enum qca_wlan_dar_stats_types { + QCA_WLAN_DAR_STATS_TYPE_LATENCY = BIT(0), + QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS = BIT(1), + QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE = BIT(2), +}; + +/* + * enum qca_wlan_dar_report_method - Specifies the report method for the + * DAR Request attribute %QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD. + * + * @QCA_WLAN_DAR_REPORT_PERIODIC: Specifies that the report should be sent at + * regular intervals based on the defined periodicity, supporting continuous + * monitoring. + * @QCA_WLAN_DAR_REPORT_ON_DEMAND: Specifies that the report should be sent + * immediately or after the measurement duration, upon request by the initiator. + * @QCA_WLAN_DAR_REPORT_TRIGGERED: Specifies that the report should be sent when + * a predefined trigger condition is met, enabling event-driven reporting. + */ +enum qca_wlan_dar_report_method { + QCA_WLAN_DAR_REPORT_PERIODIC = 0, + QCA_WLAN_DAR_REPORT_ON_DEMAND = 1, + QCA_WLAN_DAR_REPORT_TRIGGERED = 2, +}; + +/* + * enum qca_wlan_dar_op_type - Specifies the type of DAR operation for + * %QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE attribute. + * + * @QCA_WLAN_DAR_OP_TYPE_REQUEST_ADD: Triggers the driver to fill the request + * type as add in the DAR request attribute of the QoS management element sent + * in the DAR request frame. + * @QCA_WLAN_DAR_OP_TYPE_REQUEST_REMOVE: Triggers the driver to fill the + * request type as remove in the DAR request attribute of the QoS Management + * element sent in the DAR request frame. + * @QCA_WLAN_DAR_OP_TYPE_RESPONSE_TERMINATE: Triggers the driver to fill the + * status code as terminate DAR session in the DAR response attribute of the + * QoS management element sent in the DAR response frame. + */ +enum qca_wlan_dar_op_type { + QCA_WLAN_DAR_OP_TYPE_REQUEST_ADD = 0, + QCA_WLAN_DAR_OP_TYPE_REQUEST_REMOVE = 1, + QCA_WLAN_DAR_OP_TYPE_RESPONSE_TERMINATE = 2, +}; + +/* + * enum qca_wlan_vendor_attr_dar: Attributes used by vendor command + * %QCA_NL80211_VENDOR_SUBCMD_DAR. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE: Mandatory u8 attribute to specify the type + * of DAR operation in request/response attributes. Values are defined in enum + * qca_wlan_dar_op_type. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP: Mandatory u8 attribute. Bitmap of + * various DAR request attribute types. Refer the enum qca_wlan_dar_stats_types. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD: Optional u8 attribute. This + * attribute specifies the reporting method of the DAR Request attribute. It can + * be either periodic, on-demand, or triggered. Refer enum + * qca_wlan_dar_report_method. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENT_DURATION: Optional u16 attribute. + * This attribute specifies the requested measurement period in units of ms. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENTS: Optional u16 attribute. This + * attribute indicates the non-zero value for the requested number of + * measurements. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG: Optional nested attribute + * to configure latency statistic attribute. This attribute is included only + * when %QCA_WLAN_DAR_STATS_TYPE_LATENCY is set in + * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP. + * The attributes used inside this nested attribute are defined in enum + * qca_wlan_dar_stats_latency_attr. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG: Optional nested attribute to + * configure radio counters attribute. This attribute is included only when + * %QCA_WLAN_DAR_STATS_TYPE_RADIO_COUNTERS is set in + * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP. + * The attributes used inside this nested attribute is defined in enum + * qca_wlan_dar_radio_counters_attr. + * + * @QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG: Optional nested + * attribute to configure. This attribute is included only when + * %QCA_WLAN_DAR_STATS_TYPE_CONTROL_PLANE is set in the + * %QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP. + * The attributes used inside this nested attribute is defined in enum + * qca_wlan_dar_control_plane_events_attr. + */ +enum qca_wlan_vendor_attr_dar { + QCA_WLAN_VENDOR_ATTR_DAR_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DAR_OP_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_DAR_STATS_BITMAP = 2, + QCA_WLAN_VENDOR_ATTR_DAR_REPORT_METHOD = 3, + QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENT_DURATION = 4, + QCA_WLAN_VENDOR_ATTR_DAR_MEASUREMENTS = 5, + QCA_WLAN_VENDOR_ATTR_DAR_STATS_LATENCY_CONFIG = 6, + QCA_WLAN_VENDOR_ATTR_DAR_RADIO_COUNTERS_CONFIG = 7, + QCA_WLAN_VENDOR_ATTR_DAR_CONTROL_PLANE_EVENTS_CONFIG = 8, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DAR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DAR_MAX = + QCA_WLAN_VENDOR_ATTR_DAR_AFTER_LAST - 1, +}; + +/* + * enum qca_vendor_attr_feature_config_capability - Represents the attributes + * sent nested under the attribute + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_NSS_MASK - Optional u8 + * attribute. Represents the bit mask of allowed NSS capabilities for the AP. + * Bit index starts from the least significant bit and Nth bit in the bitmask + * represents the (N + 1) NSS capability. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HT - Flag attribute. The AP + * must advertise HT Capability in its Beacon and Probe Response frames. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_VHT - Flag attribute. The AP + * must advertise VHT Capability in its Beacon and Probe Response frames. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HE - Flag attribute. The AP + * must advertise HE Capability in its Beacon and Probe Response frames. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_EHT - Flag attribute. The AP + * must advertise EHT Capability in its Beacon and Probe Response frames. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_BAND_MASK - Optional u32 + * attribute. Carries bitmask value of bits from enum qca_set_band. This + * represents the allowed operating bands for the AP. + */ +enum qca_vendor_attr_feature_config_capability { + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_NSS_MASK = 1, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HT = 2, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_VHT = 3, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_HE = 4, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_EHT = 5, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_BAND_MASK = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_MAX = + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_CAPABILITY_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_feature_config_data - This enum is used by + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. The attributes in this enum + * can be used to represent a configuration data entry to be matched against the + * contents advertised by the AP in its Beacon and Probe Response frames. All + * the specified attributes in the configuration data entry must be matched to + * perform the action specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI: Optional attribute + * of u8 array. The length of this attribute can be 3 or 5 bytes. Represents + * the vendor OUI to be matched against the OUI field in the Vendor Specific + * elements advertised by the AP in its Beacon and Probe Response frames. + * Userspace can optionally configure + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA and + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK along with + * this attribute to match the Vendor Specific element data after the OUI field. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA: Optional attribute + * of u8 array. Represents Vendor Specific element data to be matched after the + * OUI field in Vendor Specific elements when the OUI field matched the OUI + * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI. If this + * is present, userspace must configure the data mask also using + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK: Optional + * attribute of u8 array. Represents the data mask buffer for the data specified + * in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA. This is mandatory + * when %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA is present. The + * length of this attribute must be same as the length of + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID: Optional attribute. + * Represents the 6-byte MAC address to be matched with AP's BSSID. If this + * is present, userspace must configure the BSSID mask also using + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK: Optional attribute. + * Represents the 6-byte MAC address mask for the BSSID specified with + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID. This is mandatory when + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID is present. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY: Optional nested + * attribute. Represents AP capabilities to be matched. The AP must support all + * the nested capabilities specified in this attribute to perform the action + * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. The attributes + * used with this are defined in enum qca_vendor_attr_feature_config_capability. + */ +enum qca_wlan_vendor_attr_feature_config_data { + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_OUI = 1, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA = 2, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_VENDOR_DATA_MASK = 3, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID = 4, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_BSSID_MASK = 5, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_CAPABILITY = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_MAX = + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_feature_config_action - Represents the values of the + * attribute %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS - Enable dynamic SMPS + * (Spatial Multiplex Power Save) only when contents advertised by AP in its + * Beacon and Probe Response frames matches with at least one of the + * configuration data entry from the array of configuration data entries + * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. Otherwise, the + * driver must disable DSMPS for the AP. When this action specified in the + * subcommand request, existing configuration data of the previous subcommand + * requests with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS will be + * cleared since the driver anyway needs to disable DSMPS for all the APs which + * do not meet enable criteria. If the default driver configuration itself + * enables DSMPS already and the subcommand request issued to add configuration + * data entries with this action the driver will enable the DSMPS only for the + * APs which meet the enable criteria and disable DSMPS for all other APs after + * the configuration. + * + * Interaction with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI: + * If %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI is also + * configured and the connected AP matches the adaptive action’s + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST, the adaptive logic may + * disable DSMPS when the AP RSSI is below the firmware-provided threshold, even + * if this static action would otherwise enable DSMPS for that AP. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS - Disable dynamic SMPS + * only when contents advertised by the AP in its Beacon and Probe Response + * frames matches at least one of the configuration data entry from the array of + * the configuration data entries specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. Otherwise, the driver must + * enable DSMPS for the AP. When this action specified in the subcommand + * request, existing configuration data entries of the previous subcommand + * requests with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS will be + * cleared since the driver anyway needs to enable DSMPS for all the APs which + * do not meet disable criteria. If the default driver configuration itself + * disables DSMPS already and the subcommand request issued to add configuration + * data entries with this action the driver will disable the DSMPS only for + * the APs which meet the disable criteria and enable DSMPS for all other APs + * after the configuration. + * + * Interaction with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI: + * If an AP is present in the + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS list, DSMPS remains + * disabled for that AP regardless of any + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI or RSSI + * changes. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI - Enable + * Dynamic Spatial Multiplexing Power Save (DSMPS) adaptively based on AP RSSI. + * This action enables DSMPS only when the Access Point (AP) signal strength + * (RSSI) exceeds a predefined threshold after a connection is established. The + * driver activates or deactivates DSMPS dynamically during the association, + * based on whether the AP’s RSSI is above or below the threshold. The RSSI + * threshold is determined by the default configuration set by the firmware. + * DSMPS is activated or deactivated only if the information advertised in the + * AP’s Beacon and Probe Response frames matches at least one entry from the + * configuration data list specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. The DSMPS state is + * continuously evaluated during the association. If the AP's RSSI crosses the + * threshold (either above or below), the driver will enable or disable DSMPS + * accordingly, as long as the AP matches the configuration data list. + * + * Interaction with other actions: + * - %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI can co-exist + * with %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS and + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS. + * - When AP matches with the configuration data of the both + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS and + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI, DSMPS can be + * disabled due to signal strength being below the RSSI threshold even if the + * AP matches the enable list. + * - The %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI action + * takes effect only for APs not matching with the configuration data of + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS. For APs in the + * disable list, DSMPS remains disabled regardless of RSSI. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2: + * Enable connections using more than two spatial streams (RX or TX) only if + * the AP’s Beacon and Probe Response frames include information that matches + * at least one entry from the configuration data list specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. + * If no match is found, the driver must restrict the connection to two spatial + * streams, even if the AP supports more than two spatial streams. + * + * Interaction with other actions: + * - If a new configuration with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 + * is specified, any existing configuration with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 will be cleared. + * - If neither %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 nor + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 is configured, the + * driver follows its default NSS negotiation logic. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2: + * Restrict connections to a maximum of two spatial streams (RX or TX) if the + * AP’s Beacon and Probe Response frames include information that matches at + * least one entry from the configuration data list specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. + * If no match is found, the driver may allow more than two spatial streams, + * provided the AP supports them. + * + * Interaction with other actions: + * - If a new configuration with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 + * is specified, any existing configuration with + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 will be cleared. + * - If neither %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 nor + * %QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 is configured, the + * driver follows its default NSS negotiation logic. + */ + +enum qca_wlan_vendor_feature_config_action { + QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ENABLE_DSMPS = 0, + QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISABLE_DSMPS = 1, + QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ADAPTIVE_DSMPS_BY_RSSI = 2, + QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_ALLOW_NSS_GT_2 = 3, + QCA_WLAN_VENDOR_FEATURE_CONFIG_ACTION_DISALLOW_NSS_GT_2 = 4, +}; + +/** + * enum qca_wlan_vendor_feature_config_data_op - Represents values of the + * attribute %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_ADD - This operation appends the + * configuration data entries specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST to the existing configuration + * data entries for the action specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + * The existing configuration data entries may have been set through previous + * subcommand requests for the action specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + * + * @QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_CLEAR - This operation clears the + * existing configuration data entries of the action specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. The existing configuration data + * entries may have been set through previous subcommand requests for the action + * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + */ +enum qca_wlan_vendor_feature_config_data_op { + QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_ADD = 0, + QCA_WLAN_VENDOR_FEATURE_CONFIG_DATA_CLEAR = 1, +}; + +/** + * enum qca_wlan_vendor_attr_feature_config - This enum is used by + * %QCA_NL80211_VENDOR_SUBCMD_FEATURE_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION: Mandatory u32 attribute. + * Possible values for this attribute are defined in the + * enum qca_wlan_vendor_feature_config_action. This represents the action to be + * performed by the driver when the contents advertised by the AP in its Beacon + * and Probe Response frames matches at least one of the configuration data + * entries from the list of the configuration data entries specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST: Array of nested attributes. + * Each nested attribute set in the array represents a configuration data entry + * to be matched against the contents advertised by the AP in its Beacon and + * Probe Response frames. The driver needs to perform the action indicated in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION when the AP matches at least one + * of the configuration data entries specified in the list of configuration data + * entries. The attributes in enum qca_wlan_vendor_attr_feature_config_data can + * be used to represent a configuration data entry to be matched against the + * contents advertised by the AP in its Beacon and Probe Response frames. All + * the specified attributes in a single configuration data entry must be matched + * to perform the action specified in + * %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. + * This is a mandatory attribute when %QCA_WLAN_VENDOR_FEATURE_CONFIG_INFO_ADD + * is indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INFO_OP. This attribute + * shall not be present when %QCA_WLAN_VENDOR_FEATURE_CONFIG_INFO_CLEAR is + * indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INFO_OP. + * + * @QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP: Mandatory u8 attribute. This + * indicates the operation to be performed on the configuration data entries + * specified in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST for the action + * indicated in %QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION. Possible values for + * this attribute is defined in enum qca_wlan_vendor_feature_config_data_op. + */ +enum qca_wlan_vendor_attr_feature_config { + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_ACTION = 1, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_LIST = 2, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_DATA_OP = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_FEATURE_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_ops - Defines attributes to be used + * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_RADIO_INDEX: Mandatory u32 attribute. + * An attribute that represents the index of the radio for which ATF + * configuration is received. Radio index is fetched from + * NL80211_WIPHY_RADIO_ATTR_INDEX, which is part of the + * NL80211_ATTR_WIPHY_RADIOS. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_ENABLED: u8 attribute. + * An attribute used to enable or disable ATF offload. + * 0 - Disable. + * 1 - Enable. + * Note: The ATF offload must be enabled first by setting this to 1. + * Only if this attribute is enabled, the remaining ATF configuration + * attributes will be applied. If ATF offload is disabled (value 0), + * all other configuration attributes will be ignored. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG: Nested attribute. + * This attribute is used to send SSID group configuration information. + * Each SSID group must contain at least one SSID, and each group can have + * its own distinct configuration. + * The configuration for each SSID group is provided as a nested attribute. + * These nested attributes are defined in + * enum qca_wlan_vendor_attr_atf_offload_ssid_group_config. + + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG: Nested attribute. + * This attribute is used to send configuration for each WMM Access + * Category (AC) within an SSID group. The attributes defined in + * enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config are nested in + * this attribute. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG: Nested attribute. + * Indicates the airtime configuration of each peer associated to the radio. + * The attributes defined in enum qca_wlan_vendor_attr_atf_offload_peer_config + * are nested in this attribute. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STATS_ENABLED: u8 attribute + * Indicates whether ATF offload statistics are enabled or disabled. + * 0 - Disable. + * 1 - Enable. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STRICT_SCHEDULING_ENABLED: u8 attribute. + * Indicates the ATF strict scheduling to be enabled or disabled. + * 0 - Disable. + * 1 - Enable. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VO_DEDICATED_TIME_CONFIG: u16 attribute. + * An attribute used to dedicate time slot for Voice (VO) within every + * one second window. The value is between 0 to 1000 milliseconds. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VI_DEDICATED_TIME_CONFIG: u16 attribute. + * An attribute used to dedicate time slot for Video (VI) within every one + * second window. The value is between 0 to 1000 milliseconds. + * + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY: Nested attribute + * representing the ATF scheduling policy of the interface. This attribute is + * applicable for an SSID, allowing the firmware to apply the scheduling policy + * directly at the interface level. The attributes defined in enum + * qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy are nested in this + * attribute. + */ +enum qca_wlan_vendor_attr_atf_offload_ops { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_RADIO_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_ENABLED = 2, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG = 3, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG = 4, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG = 5, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STATS_ENABLED = 6, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_STRICT_SCHEDULING_ENABLED = 7, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VO_DEDICATED_TIME_CONFIG = 8, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_VI_DEDICATED_TIME_CONFIG = 9, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY = 10, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_ssid_group_config - + * Defines attributes to be used with vendor attribute + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INDEX: Mandatory + * u8 attribute. Indicates the unique index of the SSID group. + * These indexes are assigned based on the order in which the SSID + * groups are configured. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_AIRTIME_CONFIGURED: + * Mandatory u16 attribute. Indicates the percentage of airtime configured by + * the user for the SSID group. The value is represented as a fixed-point + * integer with one digit after the decimal point. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_POLICY: Mandatory + * u8 attribute. Indicates the scheduling policy of the SSID group. + * Attribute value range is 0 to 2. + * 0 - Fair scheduling, the SSID group can contribute its + * unused airtime with other SSID groups and can also borrow from them. + * 1 - Strict scheduling, the SSID group can contribute + * its unused airtime with other SSID groups but cannot be borrowed. + * 2 - Fair with upper bound, the SSID group can only contribute + * its unused airtime, but cannot borrow airtime from other SSID groups. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS: + * Mandatory u16 attribute. Indicates the number of peers that are associated + * but do not have airtime configured by user. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIGURED_PEERS: + * Mandatory u16 attribute. Indicates the number of peers that are associated + * and have airtime configured by user. Note: The total number of associated + * peers to the SSID group is the sum of configured and unconfigured peers. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS_AIRTIME: + * Mandatory u16 attribute. Indicates the percentage of total airtime allocated + * for unconfigured peers within the SSID group. The value is represented as + * a fixed-point integer with one digit after the decimal point. + */ +enum qca_wlan_vendor_attr_atf_offload_ssid_group_config { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_AIRTIME_CONFIGURED = 2, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_POLICY = 3, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS = 4, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_CONFIGURED_PEERS = 5, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_UNCONFIGURED_PEERS_AIRTIME = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_LAST, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_MAX = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_GROUP_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config - + * Defines attributes to be used with vendor attribute + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_GROUP_INDEX: Mandatory u8 attribute. + * Indicates the unique index of the SSID group. These indexes are assigned + * based on the order in which the SSID groups are configured. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BE_AIRTIME: Mandatory u16 attribute. + * Indicates the airtime percentage configured for the Best Effort (BE) WMM + * Access Category of SSID group. The value is represented as a fixed-point + * integer with one digit after the decimal point. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BK_AIRTIME: Mandatory u16 attribute. + * Indicates the airtime percentage configured for the Background (BK) WMM + * Access Category of SSID group. The value is represented as a fixed-point + * integer with one digit after the decimal point. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VI_AIRTIME: Mandatory u16 attribute. + * Indicates the airtime percentage configured for the Video (VI) WMM Access + * Category of SSID group. The value is represented as a fixed-point integer + * with one digit after the decimal point. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VO_AIRTIME: Mandatory u16 attribute. + * Indicates the airtime percentage configured for the Voice (VO) WMM Access + * Category of SSID group. The value is represented as a fixed-point integer + * with one digit after the decimal point. + */ +enum qca_wlan_vendor_attr_atf_offload_wmm_ac_config { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_GROUP_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BE_AIRTIME = 2, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_BK_AIRTIME = 3, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VI_AIRTIME = 4, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_WMM_AC_VO_AIRTIME = 5, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_LAST, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_MAX = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_NUM_WMM_AC_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_peer_config - Defines + * attributes to be used with vendor attribute + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_FULL_UPDATE: Mandatory NLA_FLAG + * attribute. When included, the configuration update applies to all currently + * connected peers. If not include, the update applies only to newly connected + * peers. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MORE: Mandatory NLA_FLAG + * attribute. Indicates that the current + * QCA_NL80211_VENDOR_SUBCMD_ATF_OFFLOAD_OPS command does not include + * configuration data for all connected peers. When this flag is included, + * it signals that additional command(s) will follow, each carrying the + * remaining peer configurations using the + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG attribute. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_PAYLOAD: Mandatory nested + * attribute. Indicates each peer's configuration associated with the radio. + * The attributes defined in enum qca_wlan_vendor_attr_atf_offload_peer are + * nested in this attribute. + */ +enum qca_wlan_vendor_attr_atf_offload_peer_config { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_FULL_UPDATE = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MORE = 2, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_PAYLOAD = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_LAST, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIG_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_peer - Defines + * attributes to be used with vendor attribute + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_PAYLOAD. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAC: MAC address mandatory attribute. + * Indicates the MAC address of the peer or link peer in case of MLO. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_AIRTIME: Mandatory u16 attribute. + * Indicates the percentage of airtime configured for the peer. The airtime + * assigned to a peer is relative to the SSID group's total airtime allocation. + * The value is represented as a fixed-point integer with one digit after the + * decimal point. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_GROUP_INDEX: Mandatory u8 attribute. + * Indicates the index of the SSID group to which the peer belongs. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIGURED: Mandatory NLA_FLAG + * attribute. This flag is included if the peer has explicitly configured + * airtime by user. + */ +enum qca_wlan_vendor_attr_atf_offload_peer { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAC = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_AIRTIME = 2, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_GROUP_INDEX = 3, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_CONFIGURED = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAX, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_LAST = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_PEER_MAX - 1, +}; + +/** + * enum qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy - + * Defines attributes to be used with vendor attribute + * QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_POLICY. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LINK_ID: u8 attribute. + * Mandatory attribute when interface is configured in Multi Link Operation + * (MLO). This attribute must not be included in non-MLO scenarios. This is the + * link ID of the interface in the MLO case. Possible values are 0 to 14. + * + * @QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHEDULING: Mandatory u8 attribute. + * An attribute used to define the scheduling policy. + * The accepted values for the attribute are + * 0 - fair, 1 - strict, 2 - fair with upper bound. + */ +enum qca_wlan_vendor_attr_atf_offload_ssid_scheduling_policy { + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LINK_ID = 1, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHEDULING = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LAST, + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_MAX = + QCA_WLAN_VENDOR_ATTR_ATF_OFFLOAD_SSID_SCHED_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_cca_stat - Attributes used by + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CCA_STAT. + * + * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_LINK_ID - u8 attribute. This is mandatory + * attribute in case of MLO connection and represents the link ID of the + * affiliated AP/link. For non-MLO connection, this attribute will not be + * present. + * + * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME - u32 attribute, which represents + * total number of milliseconds the radio is monitored with wideband energy + * detection feature on the whole affiliated AP/link channel width accruing + * over time, corresponding to the respective channel. + * + * @QCA_WLAN_VENDOR_ATTR_CCA_STAT_BUSY_TIME - u32 attribute, representing total + * number of milliseconds the the CCA register is busy accruing over time + * corresponding to the whole affiliated AP/link channel width where radio was + * awake for @QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME. + */ +enum qca_wlan_vendor_attr_cca_stat { + QCA_WLAN_VENDOR_ATTR_CCA_STAT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CCA_STAT_LINK_ID = 1, + QCA_WLAN_VENDOR_ATTR_CCA_STAT_ON_TIME = 2, + QCA_WLAN_VENDOR_ATTR_CCA_STAT_BUSY_TIME = 3, + + QCA_WLAN_VENDOR_ATTR_CCA_STAT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CCA_STAT_MAX = + QCA_WLAN_VENDOR_ATTR_CCA_STAT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_beacon_miss_stat - Attributes used by + * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MISS_STAT. + * + * @QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_LINK_ID - u8 attribute. This is + * mandatory attribute in case of MLO connection and represents the link ID of + * the affiliated AP/link. For non-MLO connection, this attribute will not be + * present. + * + * @QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_DATA - Mandatory byte array attribute. + * This attribute indicates the record of beacon miss data when the STA is + * operating on the affiliated AP/link channel. Each bit in the byte array + * corresponds to a TBTT slot: 0 means the beacon was received, 1 means it was + * missed. The least significant bit of the first byte of the byte array + * indicates the most recent TBTT slot's status. The length of this attribute + * depends on the maximum number of TBTT slot statuses that hardware is capable + * of reporting. + */ +enum qca_wlan_vendor_attr_beacon_miss_stat { + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_LINK_ID = 1, + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_DATA = 2, + + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_MAX = + QCA_WLAN_VENDOR_ATTR_BEACON_MISS_STAT_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_dcs - Attributes used by + * %QCA_NL80211_VENDOR_SUBCMD_DCS_CONFIG. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID: 8-bit unsigned value for link ID. + * Specifies which link to set/get in a multi-link setup. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE: 8-bit unsigned value for DCS command type + * 0: GET – Retrieve the current DCS configuration. + * Userspace must provide QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE. + * QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID is also required in multi-link AP + * scenarios. + * The driver will return the following attributes: + * QCA_WLAN_VENDOR_ATTR_DCS_ENABLE + * QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD + * QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY + * QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD + * QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD + * QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD + * QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW + * QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD + * QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU + * 1: SET – Update the DCS configuration. + * Userspaxce must provide QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE. + * QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID is also required in multi-link AP + * scenarios. + * One or more of the above attributes must be included with new + * values to apply the configuration update. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_ENABLE: 16-bit bitmap to set/get enable/disable DCS + * bit 0: Enable Continuous Wave Interference Management (CW IM) + * bit 1: Enable WLAN Interference Management (WLAN IM) + * + * @QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD: 32-bit unsigned + * value to set/get interference detection threshold. This attribute specifies + * the number of interference events required to trigger a channel switch. + * Higher values decrease sensitivity, making DCS less likely to switch. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY: 32-bit unsigned value to set/get + * the PHY error penalty. This value specifies the amount of channel time + * (in microseconds) counted as wasted for each PHY error when estimating + * interference. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD: 32-bit unsigned value to set/get + * the PHY error count threshold. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD: 32-bit unsigned value to + * set/get radar error count threshold. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD: 32-bit unsigned value to set/get + * TX error count threshold. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW: 32-bit unsigned + * value to set/get the interference detection sampling window. The unit is a + * count of sampling intervals, where each interval corresponds to one second. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD: 8-bit unsigned + * value to set/get the co-channel interference threshold level, interpreted as + * a percentage of channel time affected by same-channel interference. + * + * @QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU: 8-bit unsigned value to set/get the maximum + * channel utilization percentage allowed. If the combined TX and RX channel + * utilization exceeds this configured maximum CU, treats the condition as WLAN + * interference. + */ +enum qca_wlan_vendor_attr_dcs { + QCA_WLAN_VENDOR_ATTR_DCS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DCS_LINK_ID = 1, + QCA_WLAN_VENDOR_ATTR_DCS_CMD_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_DCS_ENABLE = 3, + QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_THRESHOLD = 4, + QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_PENALTY = 5, + QCA_WLAN_VENDOR_ATTR_DCS_PHY_ERR_THRESHOLD = 6, + QCA_WLAN_VENDOR_ATTR_DCS_RADAR_ERR_THRESHOLD = 7, + QCA_WLAN_VENDOR_ATTR_DCS_TX_ERR_THRESHOLD = 8, + QCA_WLAN_VENDOR_ATTR_DCS_INTERFERENCE_DETECTION_WINDOW = 9, + QCA_WLAN_VENDOR_ATTR_DCS_COCHANNEL_INTERFERENCE_THRESHOLD = 10, + QCA_WLAN_VENDOR_ATTR_DCS_MAX_CU = 11, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DCS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DCS_MAX = + QCA_WLAN_VENDOR_ATTR_DCS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_qsh_stats - Attributes used by + * %QCA_NL80211_VENDOR_SUBCMD_QSH_GET_STATS. + * + * @QCA_WLAN_VENDOR_ATTR_QSH_STATS_SCAN_COUNT: 32-bit unsigned value + * representing the Wi-Fi scan count from the sensor. This attribute is + * mandatory. It's a response-only attribute. + */ +enum qca_wlan_vendor_attr_qsh_stats { + QCA_WLAN_VENDOR_ATTR_QSH_STATS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_QSH_STATS_SCAN_COUNT = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_QSH_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_QSH_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_QSH_STATS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_host_txrx_stats_attr - Defines attributes to be used + * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_WLAN_HOST_TXRX_STATS. + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_PKTS: 32-bit unsigned value + * for total TX packets successfully transmitted to the firmware. + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_DROPPED: 32-bit unsigned + * value for total TX packets dropped by the WLAN host driver (e.g., due + * to queue exhaustion). + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_PKTS: 32-bit unsigned value + * for total RX packets successfully delivered to the network stack. + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_DROPPED: 32-bit unsigned + * value for total RX packets dropped by the WLAN host driver. + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_IPA_EXCEPTION_RX_DROPPED: + * 64-bit unsigned value for IPA exception RX drops. Counts packets + * routed from IPA to WLAN driver that were dropped. + * + * @QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_PAD: Attribute used for padding + * for 64-bit alignment. + */ + +enum qca_wlan_host_txrx_stats_attr { + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_PKTS = 1, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_TX_DROPPED = 2, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_PKTS = 3, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_RX_DROPPED = 4, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_IPA_EXCEPTION_RX_DROPPED = 5, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_PAD = 6, + + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_LAST, + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_HOST_TXRX_STATS_PARAM_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_config_esp_param - Parameters for ESP configuration + * + * Attributes used to configure (set command) or report (get command) + * Estimated Service Parameters (ESP). ESP describes predicted service + * characteristics such as airtime availability, PPDU duration, and Block Ack + * window size per access category and it is advertised in the Estimated Service + * Parameters Inbound element (see IEEE Std 802.11-2024, 9.4.2.172). + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_ENABLE: u8. Enable (1) or disable (0) + * the advertisement of the Estimated Service Parameters Inbound element + * in Beacon and Probe Response frames. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BE: u8. Predicted percentage of + * airtime available for AC_BE. This is the exact 8-bit value used in the ESP + * Information field as defined in IEEE Std 802.11-2024, 9.4.2.172. The + * value is linearly scaled: 0 represents 0% airtime, 255 represents 100% + * airtime. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BE: u8. BE Access Category "target" + * duration for PPDUs that carry Data MPDUs, i.e., how long the AP expects a + * typical data transmission PPDU to last for the BE AC. Encoded in unts of 50 + * microseconds (actual_duration_us = value × 50) as defined in IEEE Std + * 802.11-2024, 9.4.2.172. + * Range: 0..255 units (0..12.75 ms). Example: value 16 -> 800 microseconds. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BE: u8. BA Window Size subfield + * for AC_BE as defined in IEEE Std 802.11-2024, Table 9-334 (BA Window Size + * subfield encoding). + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BK: u8. Airtime for AC_BK. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BK: u8. PPDU duration for AC_BK. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BK: u8. BA window for AC_BK. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VI: u8. Airtime for AC_VI. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VI: u8. PPDU duration for AC_VI. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VI: u8. BA window for AC_VI. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VO: u8. Airtime for AC_VO. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VO: u8. PPDU duration for AC_VO. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VO: u8. BA window for AC_VO. + */ +enum qca_wlan_vendor_attr_config_esp_param { + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_ENABLE = 1, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BE = 2, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BE = 3, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BE = 4, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_BK = 5, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_BK = 6, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_BK = 7, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VI = 8, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VI = 9, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VI = 10, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AIRTIME_VO = 11, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_PPDU_DUR_VO = 12, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_BA_WINDOW_VO = 13, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_ESP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_ipa_ring_stats_attr - Defines attributes to be used + * with vendor subcmd QCA_NL80211_VENDOR_SUBCMD_IPA_RING_STATS. + * + * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ID: 32-bit unsigned value + * for the ring identifier (ring_id). Distinguishes between different + * rings (e.g., REO, TCL). + * In the request, this specifies which ring to query. + * In the response, this identifies the ring the statistics belong to. + * Values are defined in enum qca_ipa_ring_stats_id. + * + * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_HP_IDX: 32-bit unsigned value + * for the head pointer index (entry index) of the ring. + * (Response only) + * + * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_TP_IDX: 32-bit unsigned value + * for the tail pointer index (entry index) of the ring. + * (Response only) + * + * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_FREE_ENTRIES: 32-bit unsigned + * value for the number of free entries available in the ring. + * (Response only) + * + * @QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ENTRIES: 32-bit unsigned + * value for the total number of entries in the ring (capacity). + * (Response only) + */ +enum qca_wlan_ipa_ring_stats_attr { + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ID = 1, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_HP_IDX = 2, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_TP_IDX = 3, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_FREE_ENTRIES = 4, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_RING_ENTRIES = 5, + + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_LAST, + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_IPA_RING_STATS_PARAM_LAST - 1, +}; + +/** + * enum qca_ipa_ring_stats_id - IPA statistics ring type + * + * @QCA_WLAN_VENDOR_IPA_RING_STATS_ID_REO: Ring ID for REO (Rx) ring. + * @QCA_WLAN_VENDOR_IPA_RING_STATS_ID_TCL: Ring ID for TCL (Tx) ring. + * + * These values are used to distinguish which IPA path statistics + * are being queried or reported. + */ +enum qca_ipa_ring_stats_id { + QCA_WLAN_VENDOR_IPA_RING_STATS_ID_REO = 0, + QCA_WLAN_VENDOR_IPA_RING_STATS_ID_TCL = 1, + + QCA_WLAN_VENDOR_IPA_RING_STATS_ID_MAX, +}; + +/** + * enum qca_wlan_vendor_external_auth_action - Action to perform with + * external authentication request. Used by + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION. + * + * @QCA_WLAN_VENDOR_EXTERNAL_AUTH_START: Start the authentication. + * @QCA_WLAN_VENDOR_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + * + */ +enum qca_wlan_vendor_external_auth_action { + QCA_WLAN_VENDOR_EXTERNAL_AUTH_START = 0, + QCA_WLAN_VENDOR_EXTERNAL_AUTH_ABORT = 1, +}; + +/** + * enum qca_wlan_vendor_attr_external_auth - This enum is used by + * %QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH. + * + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID: Mandatory binary attribute, + * 1..32 octets. This indicates the SSID of the AP and is used by both the + * authentication request event and authentication response commands. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID: Mandatory 6-byte MAC address + * attribute. It specifies the BSSID of the AP with which the + * authentication has to happen. Used by both the authentication request + * event and authentication response command. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR: Optional 6-byte attribute + * for the authentication request event interface. This specifies the MLD + * MAC address of the AP MLD. The driver indicates this to enable MLO + * during the authentication offload to supplicant. The driver shall look + * at %NL80211_ATTR_MLO_SUPPORT flag capability in NL80211_CMD_CONNECT to + * know whether the supplicant supports enabling MLO during the + * authentication offload. Supplicant should use the address of the + * interface (on which the authentication request event is reported) as + * own MLD MAC address. + * + * User space should enable MLO during the authentication only when it + * receives the AP MLD MAC address in authentication offload request. User + * space shouldn't enable MLO when the authentication offload request + * doesn't indicate the AP MLD MAC address even if the AP is MLO capable. + * The supplicant and driver should use MLD MAC addresses in the RA, TA, + * and BSSID fields of Authentication frames sent or received via nl80211. + * The driver translates the MLD MAC addresses to/from link addresses based + * on the link chosen for the authentication. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION: Mandatory u32 attribute for + * authentication request event. This indicates the operation to be + * performed with external authentication request event. Possible values + * for this attribute are defined in + * enum qca_wlan_vendor_external_auth_action. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO: Mandatory u32 attribute for + * authentication request event. This indicates the authentication + * algorithm to be used. Possible values are defined in + * enum nl80211_auth_type. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM: Mandatory u32 attribute for the + * authentication request event. This specifies AKM suite selector for the + * respective authentication, selected by the driver. Possible values are + * defined in IEEE Std 802.11-2024, 9.4.2.23.3 (AKM suites) (e.g., + * 0x000FAC04). + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER: Optional u32 attribute + * for the authentication request event. This specifies the pairwise cipher + * suite for the respective authentication, selected by the driver. + * Possible values are defined in IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher + * suites) (e.g., 0x000FAC04). + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER: Optional u32 attribute for + * the authentication request event. This specifes the group cipher suite + * of the respective authentication, selected by the driver. Possible + * values are defined in IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher suites) + * (e.g., 0x000FAC04). + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER: Optional u32 + * attribute for the authentication request event. This specifies + * the group management cipher suite of the respective authentication, + * selected by the driver. Possible values are defined in + * IEEE Std 802.11-2024, 9.4.2.23.2 (Cipher suites) (e.g., 0x000FAC04). + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE: Mandatory u16 attribute for + * the authentication response command. The supplicant indicates the + * status of the external authentication operation with this attribute to + * the driver. Use %WLAN_STATUS_SUCCESS for successful authentication, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if the supplicant cannot give the real + * status code for failures. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID: Optional byte array attribute with + * a size of 16 bytes for the authentication response command. The + * supplicant indicates the PMKID generated during the external + * authentication operation with this attribute to the driver, which needs + * to be included in the RSNE of (Re)Association Request frame by the + * driver. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB: Optional u16 attribute + * for the authentication request event interface. This specifies the + * RSN Capabilities field (IEEE Std 802.11-2024, 9.4.2.23.4) that is to + * be included in the RSNE of the Authentication frames. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA: Optional binary attribute + * for the authentication request event interface. This specifies the + * entire element data to be included in the RSNXE of the Authentication + * frames. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK: Optional binary attribute for + * the authentication response command interface. This attribute is + * present only when the NL80211_AUTHTYPE_IEEE8021X authentication + * algorithm is used with (re)association frame encryption. + * + * In SME-in-driver mode, the (re)association exchange is entirely handled + * by the driver. The standard requires that the (Re)Association Request + * frame includes a Message Integrity Code (MIC) calculated using the Key + * Confirmation Key (KCK). This attribute is used to pass the KCK from the + * supplicant to the driver. With the KCK provided, the driver can + * calculate the MIC and append it to the (Re)Association Request frame as + * mandated by IEEE P802.11bi/D4.0, 12.16.6.2/12.16.6.3. + */ +enum qca_wlan_vendor_attr_external_auth { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID = 1, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID = 2, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR = 3, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION = 4, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO = 5, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM = 6, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER = 7, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER = 8, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER = 9, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE = 10, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID = 11, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB = 12, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA = 13, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK = 14, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_MAX = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AFTER_LAST - 1, +}; + #endif /* QCA_VENDOR_H */ diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c index a65da613404a..f82a9e4fcedf 100644 --- a/contrib/wpa/src/common/sae.c +++ b/contrib/wpa/src/common/sae.c @@ -114,6 +114,8 @@ void sae_clear_temp_data(struct sae_data *sae) wpabuf_free(tmp->own_rejected_groups); wpabuf_free(tmp->peer_rejected_groups); os_free(tmp->pw_id); + os_free(tmp->parsed_pw_id); + os_free(tmp->dec_pw_id); bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; } @@ -121,12 +123,16 @@ void sae_clear_temp_data(struct sae_data *sae) void sae_clear_data(struct sae_data *sae) { + unsigned int no_pw_id; + if (sae == NULL) return; sae_clear_temp_data(sae); crypto_bignum_deinit(sae->peer_commit_scalar, 0); crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0); + no_pw_id = sae->no_pw_id; os_memset(sae, 0, sizeof(*sae)); + sae->no_pw_id = no_pw_id; } @@ -361,8 +367,11 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const_time_select_bin(found, stub_password, password, password_len, tmp_password); if (hmac_sha256_vector(addrs, sizeof(addrs), 2, - addr, len, pwd_seed) < 0) + addr, len, pwd_seed) < 0) { + wpa_printf(MSG_INFO, + "SAE: hmac_sha256_vector() failed - cannot derive PWE"); break; + } res = sae_test_pwd_seed_ecc(sae, pwd_seed, prime, qr_bin, qnr_bin, x_cand_bin); @@ -823,7 +832,8 @@ fail: static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier, u8 *pwd_seed) + const u8 *identifier, size_t identifier_len, + u8 *pwd_seed) { const u8 *addr[2]; size_t len[2]; @@ -837,10 +847,10 @@ static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len, wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); if (identifier) { - wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", - identifier); + wpa_hexdump_ascii(MSG_DEBUG, "SAE: password identifier", + identifier, identifier_len); addr[num_elem] = (const u8 *) identifier; - len[num_elem] = os_strlen(identifier); + len[num_elem] = identifier_len; num_elem++; } if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len, @@ -865,7 +875,7 @@ static struct crypto_ec_point * sae_derive_pt_ecc(struct crypto_ec *ec, int group, const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier) + const u8 *identifier, size_t identifier_len) { u8 pwd_seed[64]; u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2]; @@ -884,7 +894,7 @@ sae_derive_pt_ecc(struct crypto_ec *ec, int group, pwd_value_len = prime_len + (prime_len + 1) / 2; if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, - identifier, pwd_seed) < 0) + identifier, identifier_len, pwd_seed) < 0) goto fail; /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len) @@ -965,7 +975,7 @@ static struct crypto_bignum * sae_derive_pt_ffc(const struct dh_group *dh, int group, const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier) + const u8 *identifier, size_t identifier_len) { size_t hash_len, prime_len, pwd_value_len; struct crypto_bignum *prime, *order; @@ -989,7 +999,7 @@ sae_derive_pt_ffc(const struct dh_group *dh, int group, goto fail; if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, - identifier, pwd_seed) < 0) + identifier, identifier_len, pwd_seed) < 0) goto fail; /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */ @@ -1042,7 +1052,7 @@ fail: static struct sae_pt * sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier) + const u8 *identifier, size_t identifier_len) { struct sae_pt *pt; @@ -1055,6 +1065,12 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, if (!pt) return NULL; + if (identifier) { + pt->password_id = wpabuf_alloc_copy(identifier, identifier_len); + if (!pt->password_id) + goto fail; + } + #ifdef CONFIG_SAE_PK os_memcpy(pt->ssid, ssid, ssid_len); pt->ssid_len = ssid_len; @@ -1064,7 +1080,7 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, if (pt->ec) { pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len, password, password_len, - identifier); + identifier, identifier_len); if (!pt->ecc_pt) { wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); goto fail; @@ -1080,7 +1096,8 @@ sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, } pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len, - password, password_len, identifier); + password, password_len, identifier, + identifier_len); if (!pt->ffc_pt) { wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); goto fail; @@ -1093,19 +1110,21 @@ fail: } -struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, +struct sae_pt * sae_derive_pt(const int *groups, + const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier) + const u8 *identifier, size_t identifier_len) { struct sae_pt *pt = NULL, *last = NULL, *tmp; - int default_groups[] = { 19, 0 }; + const int default_groups[] = { 19, 0 }; int i; if (!groups) groups = default_groups; for (i = 0; groups[i] > 0; i++) { tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password, - password_len, identifier); + password_len, identifier, + identifier_len); if (!tmp) continue; @@ -1268,6 +1287,7 @@ void sae_deinit_pt(struct sae_pt *pt) crypto_ec_point_deinit(pt->ecc_pt, 1); crypto_bignum_deinit(pt->ffc_pt, 1); crypto_ec_deinit(pt->ec); + wpabuf_free(pt->password_id); prev = pt; pt = pt->next; os_free(prev); @@ -1661,17 +1681,23 @@ fail: int sae_process_commit(struct sae_data *sae) { u8 k[SAE_MAX_PRIME_LEN]; + int ret = 0; + if (sae->tmp == NULL || (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || sae_derive_keys(sae, k) < 0) - return -1; - return 0; + ret = -1; + + forced_memzero(k, SAE_MAX_PRIME_LEN); + + return ret; } int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token, const char *identifier) + const struct wpabuf *token, const u8 *identifier, + size_t identifier_len) { u8 *pos; @@ -1713,11 +1739,11 @@ int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, if (identifier) { /* Password Identifier element */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); - wpabuf_put_u8(buf, 1 + os_strlen(identifier)); + wpabuf_put_u8(buf, 1 + identifier_len); wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); - wpabuf_put_str(buf, identifier); - wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", - identifier); + wpabuf_put_data(buf, identifier, identifier_len); + wpa_hexdump_ascii(MSG_DEBUG, "SAE: own Password Identifier", + identifier, identifier_len); } if (sae->h2e && sae->tmp->own_rejected_groups) { @@ -1877,14 +1903,14 @@ static void sae_parse_token_container(struct sae_data *sae, const u8 *pos, const u8 *end, const u8 **token, size_t *token_len) { - wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - pos, end - pos); if (!sae_is_token_container_elem(pos, end)) return; - *token = pos + 3; - *token_len = pos[1] - 1; + if (token) + *token = pos + 3; + if (token_len) + *token_len = pos[1] - 1; wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", - *token, *token_len); + pos + 3, pos[1] - 1); } @@ -2045,14 +2071,12 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, } -static int sae_parse_password_identifier(struct sae_data *sae, +static int sae_parse_password_identifier(struct sae_data *sae, bool h2e, const u8 **pos, const u8 *end) { const u8 *epos; u8 len; - wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - *pos, end - *pos); if (!sae_is_password_id_elem(*pos, end)) { if (sae->tmp->pw_id) { wpa_printf(MSG_DEBUG, @@ -2060,8 +2084,9 @@ static int sae_parse_password_identifier(struct sae_data *sae, sae->tmp->pw_id); return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; } - os_free(sae->tmp->pw_id); - sae->tmp->pw_id = NULL; + os_free(sae->tmp->parsed_pw_id); + sae->tmp->parsed_pw_id = NULL; + sae->tmp->parsed_pw_id_len = 0; return WLAN_STATUS_SUCCESS; /* No Password Identifier */ } @@ -2073,8 +2098,20 @@ static int sae_parse_password_identifier(struct sae_data *sae, epos++; /* skip ext ID */ len--; + if (!h2e) { + wpa_printf(MSG_DEBUG, + "SAE: Password Identifier included, but H2E is not used"); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + + if (sae->no_pw_id) { + wpa_printf(MSG_DEBUG, + "SAE: Password Identifier included, but none has been enabled"); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + if (sae->tmp->pw_id && - (len != os_strlen(sae->tmp->pw_id) || + (len != sae->tmp->pw_id_len || os_memcmp(sae->tmp->pw_id, epos, len) != 0)) { wpa_printf(MSG_DEBUG, "SAE: The included Password Identifier does not match the expected one (%s)", @@ -2082,14 +2119,17 @@ static int sae_parse_password_identifier(struct sae_data *sae, return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; } - os_free(sae->tmp->pw_id); - sae->tmp->pw_id = os_malloc(len + 1); - if (!sae->tmp->pw_id) + os_free(sae->tmp->parsed_pw_id); + sae->tmp->parsed_pw_id = os_malloc(len + 1); + if (!sae->tmp->parsed_pw_id) { + sae->tmp->parsed_pw_id_len = 0; return WLAN_STATUS_UNSPECIFIED_FAILURE; - os_memcpy(sae->tmp->pw_id, epos, len); - sae->tmp->pw_id[len] = '\0'; + } + os_memcpy(sae->tmp->parsed_pw_id, epos, len); + sae->tmp->parsed_pw_id_len = len; + sae->tmp->parsed_pw_id[len] = '\0'; wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", - sae->tmp->pw_id, len); + sae->tmp->parsed_pw_id, len); *pos = epos + len; return WLAN_STATUS_SUCCESS; } @@ -2101,8 +2141,6 @@ static int sae_parse_rejected_groups(struct sae_data *sae, const u8 *epos; u8 len; - wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - *pos, end - *pos); if (!sae_is_rejected_groups_elem(*pos, end)) { wpabuf_free(sae->tmp->peer_rejected_groups); sae->tmp->peer_rejected_groups = NULL; @@ -2141,8 +2179,6 @@ static int sae_parse_akm_suite_selector(struct sae_data *sae, const u8 *epos; u8 len; - wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - *pos, end - *pos); if (!sae_is_akm_suite_selector_elem(*pos, end)) return WLAN_STATUS_SUCCESS; @@ -2195,8 +2231,13 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, if (ie_offset) *ie_offset = pos - data; + if (end > pos) + wpa_hexdump(MSG_DEBUG, + "SAE: Possible elements at the end of the frame", + pos, end - pos); + /* Optional Password Identifier element */ - res = sae_parse_password_identifier(sae, &pos, end); + res = sae_parse_password_identifier(sae, h2e, &pos, end); if (res != WLAN_STATUS_SUCCESS) return res; @@ -2215,11 +2256,9 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, sae_parse_token_container(sae, pos, end, token, token_len); /* Conditional AKM Suite Selector element */ - if (h2e) { - res = sae_parse_akm_suite_selector(sae, &pos, end); - if (res != WLAN_STATUS_SUCCESS) - return res; - } + res = sae_parse_akm_suite_selector(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; if (sae->own_akm_suite_selector && sae->own_akm_suite_selector != sae->peer_akm_suite_selector) { @@ -2239,6 +2278,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY; } + if (wpa_key_mgmt_sae_ext_key(sae->akmp) && !h2e) { + wpa_printf(MSG_DEBUG, + "SAE: Tried to use EXT-KEY AKM without H2E"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + /* * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as * the values we sent which would be evidence of a reflection attack. diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h index a353aa8da357..afde296bfe76 100644 --- a/contrib/wpa/src/common/sae.h +++ b/contrib/wpa/src/common/sae.h @@ -58,12 +58,19 @@ struct sae_temporary_data { struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; - char *pw_id; + u8 *pw_id; + size_t pw_id_len; + u8 *parsed_pw_id; + size_t parsed_pw_id_len; + char *dec_pw_id; + size_t dec_pw_id_len; + unsigned int pw_id_counter; int vlan_id; u8 bssid[ETH_ALEN]; struct wpabuf *own_rejected_groups; struct wpabuf *peer_rejected_groups; unsigned int own_addr_higher:1; + unsigned int try_other_password:1; #ifdef CONFIG_SAE_PK u8 kek[SAE_MAX_HASH_LEN]; @@ -84,6 +91,8 @@ struct sae_temporary_data { #endif /* CONFIG_SAE_PK */ struct os_reltime disabled_until; + + const void *used_pw; }; struct sae_pt { @@ -94,6 +103,7 @@ struct sae_pt { const struct dh_group *dh; struct crypto_bignum *ffc_pt; + struct wpabuf *password_id; #ifdef CONFIG_SAE_PK u8 ssid[32]; size_t ssid_len; @@ -120,6 +130,7 @@ struct sae_data { u16 rc; /* protocol instance variable: Rc (received send-confirm) */ unsigned int h2e:1; unsigned int pk:1; + unsigned int no_pw_id:1; struct sae_temporary_data *tmp; }; @@ -135,7 +146,8 @@ int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt, int *rejected_groups, const struct sae_pk *pk); int sae_process_commit(struct sae_data *sae); int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token, const char *identifier); + const struct wpabuf *token, const u8 *identifier, + size_t identifier_len); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups, int h2e, int *ie_offset); @@ -146,9 +158,10 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); const char * sae_state_txt(enum sae_state state); size_t sae_ecc_prime_len_2_hash_len(size_t prime_len); size_t sae_ffc_prime_len_2_hash_len(size_t prime_len); -struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, +struct sae_pt * sae_derive_pt(const int *groups, + const u8 *ssid, size_t ssid_len, const u8 *password, size_t password_len, - const char *identifier); + const u8 *identifier, size_t identifier_len); struct crypto_ec_point * sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, const u8 *addr1, const u8 *addr2); diff --git a/contrib/wpa/src/common/sae_pk.c b/contrib/wpa/src/common/sae_pk.c index df79e5f2c3b0..88e16b3cf97b 100644 --- a/contrib/wpa/src/common/sae_pk.c +++ b/contrib/wpa/src/common/sae_pk.c @@ -384,6 +384,12 @@ int sae_pk_set_password(struct sae_data *sae, const char *password) tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5; wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu", tmp->sec, tmp->lambda, tmp->fingerprint_bits); + if (tmp->fingerprint_bits > 8 * SAE_MAX_HASH_LEN) { + wpa_printf(MSG_INFO, + "SAE-PK: fingerprint_bits exceeds SAE_MAX_HASH_LEN"); + bin_clear_free(pw, pw_len); + return -1; + } /* Construct Fingerprint from PasswordBase by prefixing with Sec zero * octets and skipping the Sec_1b bits */ diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index 5b735984983a..3d3c2ae63b7f 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.11" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.12" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c index 1cfdd9ad7e2f..959da3663266 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -19,6 +19,7 @@ #include "ieee802_11_defs.h" #include "ieee802_11_common.h" #include "defs.h" +#include "common/nan_defs.h" #include "wpa_common.h" @@ -62,7 +63,7 @@ static unsigned int wpa_kck2_len(int akmp) #endif /* CONFIG_IEEE80211R */ -static unsigned int wpa_kek_len(int akmp, size_t pmk_len) +unsigned int wpa_kek_len(int akmp, size_t pmk_len) { switch (akmp) { case WPA_KEY_MGMT_FILS_SHA384: @@ -102,7 +103,21 @@ static unsigned int wpa_kek2_len(int akmp) #endif /* CONFIG_IEEE80211R */ -unsigned int wpa_mic_len(int akmp, size_t pmk_len) +static int rsn_mic_len_hash(size_t pmk_len, enum rsn_hash_alg hash) +{ + switch (hash) { + case RSN_HASH_SHA256: + return 16; + case RSN_HASH_SHA384: + return 24; + case RSN_HASH_SHA512: + return 32; + default: + return pmk_len / 2; + } +} + +unsigned int wpa_mic_len(int akmp, size_t pmk_len, enum rsn_hash_alg hash) { switch (akmp) { case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: @@ -115,18 +130,116 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len) case WPA_KEY_MGMT_FT_FILS_SHA384: return 0; case WPA_KEY_MGMT_DPP: - return pmk_len / 2; + return rsn_mic_len_hash(pmk_len, hash); case WPA_KEY_MGMT_OWE: - return pmk_len / 2; + return rsn_mic_len_hash(pmk_len, hash); case WPA_KEY_MGMT_SAE_EXT_KEY: case WPA_KEY_MGMT_FT_SAE_EXT_KEY: - return pmk_len / 2; + return rsn_mic_len_hash(pmk_len, hash); default: return 16; } } +int rsn_cipher_suite_to_wpa_cipher(u32 cipher) +{ + switch (cipher) { + case RSN_CIPHER_SUITE_NONE: + return WPA_CIPHER_NONE; + case RSN_CIPHER_SUITE_TKIP: + return WPA_CIPHER_TKIP; + case RSN_CIPHER_SUITE_CCMP: + return WPA_CIPHER_CCMP; + case RSN_CIPHER_SUITE_AES_128_CMAC: + return WPA_CIPHER_AES_128_CMAC; + case RSN_CIPHER_SUITE_GCMP: + return WPA_CIPHER_GCMP; + case RSN_CIPHER_SUITE_CCMP_256: + return WPA_CIPHER_CCMP_256; + case RSN_CIPHER_SUITE_GCMP_256: + return WPA_CIPHER_GCMP_256; + case RSN_CIPHER_SUITE_BIP_GMAC_128: + return WPA_CIPHER_BIP_GMAC_128; + case RSN_CIPHER_SUITE_BIP_GMAC_256: + return WPA_CIPHER_BIP_GMAC_256; + case RSN_CIPHER_SUITE_BIP_CMAC_256: + return WPA_CIPHER_BIP_CMAC_256; + case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED: + return WPA_CIPHER_GTK_NOT_USED; + default: + return 0; + } +} + + +int rsn_key_mgmt_to_wpa_akm(u32 akm_suite) +{ + switch (akm_suite) { + case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X: + return WPA_KEY_MGMT_IEEE8021X; + case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X: + return WPA_KEY_MGMT_PSK; +#ifdef CONFIG_IEEE80211R + case RSN_AUTH_KEY_MGMT_FT_802_1X: + return WPA_KEY_MGMT_FT_IEEE8021X; + case RSN_AUTH_KEY_MGMT_FT_PSK: + return WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_SHA384 + case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384: + return WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SHA384 + case RSN_AUTH_KEY_MGMT_802_1X_SHA384: + return WPA_KEY_MGMT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ + case RSN_AUTH_KEY_MGMT_802_1X_SHA256: + return WPA_KEY_MGMT_IEEE8021X_SHA256; + case RSN_AUTH_KEY_MGMT_PSK_SHA256: + return WPA_KEY_MGMT_PSK_SHA256; +#ifdef CONFIG_SAE + case RSN_AUTH_KEY_MGMT_SAE: + return WPA_KEY_MGMT_SAE; + case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY: + return WPA_KEY_MGMT_SAE_EXT_KEY; + case RSN_AUTH_KEY_MGMT_FT_SAE: + return WPA_KEY_MGMT_FT_SAE; + case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY: + return WPA_KEY_MGMT_FT_SAE_EXT_KEY; +#endif /* CONFIG_SAE */ + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: + return WPA_KEY_MGMT_IEEE8021X_SUITE_B; + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192: + return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + case RSN_AUTH_KEY_MGMT_FILS_SHA256: + return WPA_KEY_MGMT_FILS_SHA256; + case RSN_AUTH_KEY_MGMT_FILS_SHA384: + return WPA_KEY_MGMT_FILS_SHA384; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256: + return WPA_KEY_MGMT_FT_FILS_SHA256; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384: + return WPA_KEY_MGMT_FT_FILS_SHA384; +#ifdef CONFIG_OWE + case RSN_AUTH_KEY_MGMT_OWE: + return WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + case RSN_AUTH_KEY_MGMT_DPP: + return WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_PASN + case RSN_AUTH_KEY_MGMT_PASN: + return WPA_KEY_MGMT_PASN; +#endif /* CONFIG_PASN */ + case RSN_AUTH_KEY_MGMT_EPPKE: + return WPA_KEY_MGMT_EPPKE; + default: + return 0; + } +} + + /** * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used * @akmp: WPA_KEY_MGMT_* used in key derivation @@ -134,8 +247,7 @@ unsigned int wpa_mic_len(int akmp, size_t pmk_len) */ int wpa_use_akm_defined(int akmp) { - return akmp == WPA_KEY_MGMT_OSEN || - akmp == WPA_KEY_MGMT_OWE || + return akmp == WPA_KEY_MGMT_OWE || akmp == WPA_KEY_MGMT_DPP || akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 || @@ -152,8 +264,7 @@ int wpa_use_akm_defined(int akmp) */ int wpa_use_cmac(int akmp) { - return akmp == WPA_KEY_MGMT_OSEN || - akmp == WPA_KEY_MGMT_OWE || + return akmp == WPA_KEY_MGMT_OWE || akmp == WPA_KEY_MGMT_DPP || wpa_key_mgmt_ft(akmp) || wpa_key_mgmt_sha256(akmp) || @@ -174,8 +285,7 @@ int wpa_use_cmac(int akmp) */ int wpa_use_aes_key_wrap(int akmp) { - return akmp == WPA_KEY_MGMT_OSEN || - akmp == WPA_KEY_MGMT_OWE || + return akmp == WPA_KEY_MGMT_OWE || akmp == WPA_KEY_MGMT_DPP || akmp == WPA_KEY_MGMT_IEEE8021X_SHA384 || wpa_key_mgmt_ft(akmp) || @@ -185,11 +295,68 @@ int wpa_use_aes_key_wrap(int akmp) } +#if defined(CONFIG_SAE) || defined(CONFIG_OWE) || defined(CONFIG_DPP) +static int rsn_eapol_key_mic_hash(const u8 *key, size_t key_len, int akmp, + enum rsn_hash_alg hash_alg, int ver, + const u8 *buf, size_t len, u8 *mic) +{ + u8 hash[SHA512_MAC_LEN]; + size_t mic_len; + + if (hash_alg == RSN_HASH_NOT_SPECIFIED) { + if (key_len == 128 / 8) + hash_alg = RSN_HASH_SHA256; + else if (key_len == 192 / 8) + hash_alg = RSN_HASH_SHA384; + else if (key_len == 256 / 8) + hash_alg = RSN_HASH_SHA512; + else + return -1; + } + + wpa_printf(MSG_DEBUG, + "RSN: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - %s)", + hash_alg == RSN_HASH_SHA512 ? 512 : + hash_alg == RSN_HASH_SHA384 ? 384 : 256, + wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN)); + wpa_hexdump(MSG_DEBUG, "RSN: KCK", key, key_len); + + if (hash_alg == RSN_HASH_SHA256) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + mic_len = 16; +#ifdef CONFIG_SHA384 + } else if (hash_alg == RSN_HASH_SHA384) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + mic_len = 24; +#endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + } else if (hash_alg == RSN_HASH_SHA512) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; + mic_len = 32; +#endif /* CONFIG_SHA512 */ + } else { + wpa_printf(MSG_INFO, + "RSN: Unsupported KCK length: %u (hash_alg=%d)", + (unsigned int) key_len, hash_alg); + return -1; + } + + os_memcpy(mic, hash, mic_len); + wpa_hexdump(MSG_DEBUG, "RSN: EAPOL-Key MIC", mic, mic_len); + return 0; +} +#endif /* CONFIG_SAE || CONFIG_OWE || CONFIG_DPP */ + + /** * wpa_eapol_key_mic - Calculate EAPOL-Key MIC * @key: EAPOL-Key Key Confirmation Key (KCK) * @key_len: KCK length in octets * @akmp: WPA_KEY_MGMT_* used in key derivation + * @hash_alg: Hash algorithm * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) * @buf: Pointer to the beginning of the EAPOL header (version field) * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) @@ -205,7 +372,8 @@ int wpa_use_aes_key_wrap(int akmp) * happened during final editing of the standard and the correct behavior is * defined in the last draft (IEEE 802.11i/D10). */ -int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, +int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, + enum rsn_hash_alg hash_alg, int ver, const u8 *buf, size_t len, u8 *mic) { u8 hash[SHA512_MAC_LEN]; @@ -241,37 +409,11 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, return omac1_aes_128(key, buf, len, mic); case WPA_KEY_MGMT_SAE_EXT_KEY: case WPA_KEY_MGMT_FT_SAE_EXT_KEY: - wpa_printf(MSG_DEBUG, - "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)", - (unsigned int) key_len * 8 * 2); - if (key_len == 128 / 8) { - if (hmac_sha256(key, key_len, buf, len, hash)) - return -1; -#ifdef CONFIG_SHA384 - } else if (key_len == 192 / 8) { - if (hmac_sha384(key, key_len, buf, len, hash)) - return -1; -#endif /* CONFIG_SHA384 */ -#ifdef CONFIG_SHA512 - } else if (key_len == 256 / 8) { - if (hmac_sha512(key, key_len, buf, len, hash)) - return -1; -#endif /* CONFIG_SHA512 */ - } else { - wpa_printf(MSG_INFO, - "SAE: Unsupported KCK length: %u", - (unsigned int) key_len); + if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg, + ver, buf, len, mic) < 0) return -1; - } - os_memcpy(mic, hash, key_len); break; #endif /* CONFIG_SAE */ -#ifdef CONFIG_HS20 - case WPA_KEY_MGMT_OSEN: - wpa_printf(MSG_DEBUG, - "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)"); - return omac1_aes_128(key, buf, len, mic); -#endif /* CONFIG_HS20 */ #ifdef CONFIG_SUITEB case WPA_KEY_MGMT_IEEE8021X_SUITE_B: wpa_printf(MSG_DEBUG, @@ -292,48 +434,16 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, #endif /* CONFIG_SUITEB192 */ #ifdef CONFIG_OWE case WPA_KEY_MGMT_OWE: - wpa_printf(MSG_DEBUG, - "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)", - (unsigned int) key_len * 8 * 2); - if (key_len == 128 / 8) { - if (hmac_sha256(key, key_len, buf, len, hash)) - return -1; - } else if (key_len == 192 / 8) { - if (hmac_sha384(key, key_len, buf, len, hash)) - return -1; - } else if (key_len == 256 / 8) { - if (hmac_sha512(key, key_len, buf, len, hash)) - return -1; - } else { - wpa_printf(MSG_INFO, - "OWE: Unsupported KCK length: %u", - (unsigned int) key_len); + if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg, + ver, buf, len, mic) < 0) return -1; - } - os_memcpy(mic, hash, key_len); break; #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP case WPA_KEY_MGMT_DPP: - wpa_printf(MSG_DEBUG, - "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)", - (unsigned int) key_len * 8 * 2); - if (key_len == 128 / 8) { - if (hmac_sha256(key, key_len, buf, len, hash)) - return -1; - } else if (key_len == 192 / 8) { - if (hmac_sha384(key, key_len, buf, len, hash)) - return -1; - } else if (key_len == 256 / 8) { - if (hmac_sha512(key, key_len, buf, len, hash)) - return -1; - } else { - wpa_printf(MSG_INFO, - "DPP: Unsupported KCK length: %u", - (unsigned int) key_len); + if (rsn_eapol_key_mic_hash(key, key_len, akmp, hash_alg, + ver, buf, len, mic) < 0) return -1; - } - os_memcpy(mic, hash, key_len); break; #endif /* CONFIG_DPP */ #ifdef CONFIG_SHA384 @@ -378,6 +488,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * @ptk: Buffer for pairwise transient key * @akmp: Negotiated AKM * @cipher: Negotiated pairwise cipher + * @z: Additional context data + * @z_len: Length of additional context data * @kdk_len: The length in octets that should be derived for KDK * Returns: 0 on success, -1 on failure * @@ -388,7 +500,9 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * [ || Z.x ]) * * The optional Z.x component is used only with DPP and that part is not defined - * in IEEE 802.11. + * in IEEE 802.11. The additional context data is also used to carry the DHss + * (Diffie-Hellman shared secret) when IEEE 802.1X authentication in + * Authentication frames is used. */ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, @@ -467,6 +581,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA384; #else /* CONFIG_SHA384 */ return -1; #endif /* CONFIG_SHA384 */ @@ -475,6 +590,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA256; #ifdef CONFIG_OWE } else if (akmp == WPA_KEY_MGMT_OWE && (pmk_len == 32 || owe_ptk_workaround)) { @@ -482,16 +598,24 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + if (owe_ptk_workaround && pmk_len == 64) + ptk->hash_alg = RSN_HASH_SHA512; + else if (owe_ptk_workaround && pmk_len == 48) + ptk->hash_alg = RSN_HASH_SHA384; + else + ptk->hash_alg = RSN_HASH_SHA256; } else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 48) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA384; } else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 64) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA512; } else if (akmp == WPA_KEY_MGMT_OWE) { wpa_printf(MSG_INFO, "OWE: Unknown PMK length %u", (unsigned int) pmk_len); @@ -503,16 +627,19 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA256; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA384; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA512; } else if (akmp == WPA_KEY_MGMT_DPP) { wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u", (unsigned int) pmk_len); @@ -526,6 +653,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA256; #ifdef CONFIG_SHA384 } else if (pmk_len == 48) { wpa_printf(MSG_DEBUG, @@ -533,6 +661,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA384; #endif /* CONFIG_SHA384 */ #ifdef CONFIG_SHA512 } else if (pmk_len == 64) { @@ -541,6 +670,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA512; #endif /* CONFIG_SHA512 */ } else { wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u", @@ -553,6 +683,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; + ptk->hash_alg = RSN_HASH_SHA1; } wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, @@ -582,6 +713,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->kek2_len = 0; ptk->kck2_len = 0; + ptk->ptk_len = ptk_len; os_memset(tmp, 0, sizeof(tmp)); os_memset(data, 0, data_len); return 0; @@ -593,7 +725,7 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, const u8 *snonce, const u8 *anonce, const u8 *dh_ss, size_t dh_ss_len, u8 *pmk, size_t *pmk_len) { - u8 nonces[2 * FILS_NONCE_LEN]; + u8 nonces[2 * NONCE_LEN]; const u8 *addr[2]; size_t len[2]; size_t num_elem; @@ -610,12 +742,12 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, return -1; wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len); - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len); - os_memcpy(nonces, snonce, FILS_NONCE_LEN); - os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN); + os_memcpy(nonces, snonce, NONCE_LEN); + os_memcpy(&nonces[NONCE_LEN], anonce, NONCE_LEN); addr[0] = rmsk; len[0] = rmsk_len; num_elem = 1; @@ -625,10 +757,10 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, num_elem++; } if (wpa_key_mgmt_sha384(akmp)) - res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + res = hmac_sha384_vector(nonces, 2 * NONCE_LEN, num_elem, addr, len, pmk); else - res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + res = hmac_sha256_vector(nonces, 2 * NONCE_LEN, num_elem, addr, len, pmk); if (res == 0) wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len); @@ -691,7 +823,7 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, * KDK = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits + FILS-FT_bits, * KDK_bits) */ - data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len; + data_len = 2 * ETH_ALEN + 2 * NONCE_LEN + dhss_len; data = os_malloc(data_len); if (!data) goto err; @@ -700,10 +832,10 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, pos += ETH_ALEN; os_memcpy(pos, aa, ETH_ALEN); pos += ETH_ALEN; - os_memcpy(pos, snonce, FILS_NONCE_LEN); - pos += FILS_NONCE_LEN; - os_memcpy(pos, anonce, FILS_NONCE_LEN); - pos += FILS_NONCE_LEN; + os_memcpy(pos, snonce, NONCE_LEN); + pos += NONCE_LEN; + os_memcpy(pos, anonce, NONCE_LEN); + pos += NONCE_LEN; if (dhss) os_memcpy(pos, dhss, dhss_len); @@ -757,8 +889,8 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa)); - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN); if (dhss) wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len); wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len); @@ -814,8 +946,8 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid)); wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len); - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len); wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); @@ -825,9 +957,9 @@ int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, * [ || gSTA || gAP ]) */ addr[0] = snonce; - len[0] = FILS_NONCE_LEN; + len[0] = NONCE_LEN; addr[1] = anonce; - len[1] = FILS_NONCE_LEN; + len[1] = NONCE_LEN; addr[2] = sta_addr; len[2] = ETH_ALEN; addr[3] = bssid; @@ -1428,27 +1560,66 @@ void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse) * @akmp: Authentication and key management protocol * @cipher: The cipher suite * - * According to IEEE P802.11az/D2.7, 12.12.7, the hash algorithm to use is the - * hash algorithm defined for the Base AKM (see Table 9-151 (AKM suite + * According to IEEE Std 802.11-2024, 12.13.8 (PTKSA derivation with PASN + * authentication), the hash algorithm to use is the + * hash algorithm defined for the Base AKM (see Table 9-190 (AKM suite * selectors)). When there is no Base AKM, the hash algorithm is selected based * on the pairwise cipher suite provided in the RSNE by the AP in the second * PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers * 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used. */ -bool pasn_use_sha384(int akmp, int cipher) +static bool pasn_use_sha384(int akmp, int cipher) { - return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 || - cipher == WPA_CIPHER_GCMP_256)) || + return ((akmp == WPA_KEY_MGMT_PASN || akmp == WPA_KEY_MGMT_EPPKE) && + (cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256)) || wpa_key_mgmt_sha384(akmp); } /** - * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc. + * pasn_select_hash_alg - Select hash algorithm for PTK derivation + * @akmp: Authentication and key management protocol + * @cipher: The cipher suite + * @pmk_len: PMK length in octets + * + * According to IEEE Std 802.11-2024, Table 9-190 (AKM suite selectors), AKMs + * 00-0F-AC:24 and 00-0F-AC:25 have the length of the PMK, the length + * of the SAE key confirmation key, SAE-KCK, and PTK-KCK, and the length of + * PTK-KEK depending on the hash algorithm specified in 12.4.2 (see 12.7.1.3 + * and 12.7.3), i.e, the hash algorithm depends on the prime length associated + * with the selected group per Table 12-1 (Hash algorithm based on length of + * prime). + */ +static enum rsn_hash_alg pasn_select_hash_alg(int akmp, int cipher, + size_t pmk_len) +{ +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae_ext_key(akmp)) { + if (pmk_len == 64) + return RSN_HASH_SHA512; + if (pmk_len == 48) + return RSN_HASH_SHA384; + } +#endif /* CONFIG_SAE */ + + if (pasn_use_sha384(akmp, cipher)) + return RSN_HASH_SHA384; + + return RSN_HASH_SHA256; +} + + +/** + * pasn_pmk_to_ptk - Calculate PASN/EPPKE PTK from PMK, addresses, etc. * @pmk: Pairwise master key * @pmk_len: Length of PMK - * @spa: Suppplicant address - * @bssid: AP BSSID + * @spa: For EPPKE authentication, non-AP MLD MAC address is used for MLO. For + * PASN authentication or EPPKE authentication for non-MLO, non-AP STA link + * MAC address is used. + * @bssid: For EPPKE authentication, AP MLD MAC address is used for MLO. For + * PASN authentication or EPPKE authentication for non-MLO, AP BSSID is + * used. * @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key * exchange encoded as an octet string * @dhss_len: The length of dhss in octets @@ -1456,19 +1627,26 @@ bool pasn_use_sha384(int akmp, int cipher) * @akmp: Negotiated AKM * @cipher: Negotiated pairwise cipher * @kdk_len: the length in octets that should be derived for HTLK. Can be zero. + * @kek_len: The length in octets that should be derived for KEK. Can be zero. + * @alg: Output variable for indicating the selected hash algorithm + * @is_eppke: EPPKE authentication * Returns: 0 on success, -1 on failure */ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len) + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg, + bool is_eppke) { - u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + WPA_KDK_MAX_LEN]; + const u8 *pos; u8 *data; size_t data_len, ptk_len; int ret = -1; - const char *label = "PASN PTK Derivation"; + const char *label = is_eppke ? "EPPKE PTK Derivation" : + "PASN PTK Derivation"; if (!pmk || !pmk_len) { wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation"); @@ -1481,6 +1659,12 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, } /* + * Use "EPPKE PTK Derivation" instead of “PASN PTK Derivation” for + * EPPKE Authentication per IEEE P802.11bi/D4.0, 12.16.9.3.4 (PTKSA + * derivation and MIC computation with EPPKE authentication). For EPPKE + * MLO, the non-AP MLD MAC address is used instead of the SPA and the + * AP MLD MAC address instead of the BSSID. + * * PASN-PTK = KDF(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss) * * KCK = L(PASN-PTK, 0, 256) @@ -1499,7 +1683,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, ptk->kck_len = WPA_PASN_KCK_LEN; ptk->tk_len = wpa_cipher_key_len(cipher); ptk->kdk_len = kdk_len; - ptk->kek_len = 0; + ptk->kek_len = kek_len; ptk->kek2_len = 0; ptk->kck2_len = 0; @@ -1510,22 +1694,42 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, goto err; } - ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len; + ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len + ptk->kek_len; if (ptk_len > sizeof(tmp)) goto err; - if (pasn_use_sha384(akmp, cipher)) { + *alg = pasn_select_hash_alg(akmp, cipher, pmk_len); + + switch (*alg) { + case RSN_HASH_SHA512: +#ifdef CONFIG_SHA512 + wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA512"); + + if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, + ptk_len) < 0) + goto err; + break; +#endif + case RSN_HASH_SHA384: +#ifdef CONFIG_SHA384 wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384"); if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) goto err; - } else { + break; +#endif + case RSN_HASH_SHA256: wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256"); if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) goto err; + break; + default: + wpa_printf(MSG_DEBUG, "PASN: Unsupported hash algorithm %d", + *alg); + goto err; } wpa_printf(MSG_DEBUG, @@ -1538,17 +1742,27 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN); + pos = &tmp[WPA_PASN_KCK_LEN]; - os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len); + if (kek_len) { + os_memcpy(ptk->kek, pos, kek_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: KEK:", + ptk->kek, ptk->kek_len); + pos += kek_len; + } + + os_memcpy(ptk->tk, pos, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len); + pos += ptk->tk_len; if (kdk_len) { - os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len, - ptk->kdk_len); + os_memcpy(ptk->kdk, pos, ptk->kdk_len); wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:", ptk->kdk, ptk->kdk_len); } + ptk->ptk_len = ptk_len; + ptk->hash_alg = *alg; forced_memzero(tmp, sizeof(tmp)); ret = 0; err: @@ -1559,13 +1773,19 @@ err: /* * pasn_mic_len - Returns the MIC length for PASN authentication + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() */ -u8 pasn_mic_len(int akmp, int cipher) +size_t pasn_mic_len(enum rsn_hash_alg alg) { - if (pasn_use_sha384(akmp, cipher)) + switch (alg) { + case RSN_HASH_SHA512: + return 32; + case RSN_HASH_SHA384: return 24; - - return 16; + case RSN_HASH_SHA256: + default: + return 16; + } } @@ -1625,15 +1845,126 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher) } +#ifdef CONFIG_IEEE8021X_AUTH + +int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, + const u8 *aa, const u8 *snonce, const u8 *anonce, + int akmp, int cipher, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + size_t kdk_len) +{ + return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", + spa, aa, snonce, anonce, ptk, akmp, + cipher, dhss, dhss_len, kdk_len); +} + + +/** + * wpa_auth_8021x_mic - Calculate IEEE 802.1X in Authentication frames MIC + * @akmp: Negotiated key management protocol + * @kck: The key confirmation key from 802.1X exchange + * @kck_len: KCK length in octets + * @addr1: Authenticator address + * @addr2: Supplicant address + * @data: This should hold the RSNE + RSNXE. + * For a MIC included by the AP with EAP-Success and the second + * Authentication frame if a PMKSA was identified via a PMKID included in + * the first Authentication frame, this holds the Beacon frame RSNE+RSNXE. + * For a MIC included by the non-AP STA in the (Re)Association Request + * frame, this holds the RSNE and RSNXE sent by the non-AP STA. + * @data_len: The length of data + * @frame: For a MIC included by the AP in EAP-Success and the second + * Authentication frame if a PMKSA was identified via a PMKID included in + * the first Authentication frame, this holds the body of the + * Authentication frame including the MIC element with the octets in the + * MIC field of the MIC element set to 0, else NULL + * @frame_len: The length of frame + * @mic: Buffer to hold the MIC on success. Must be large enough to handle the + * maximal MIC length. + * Returns: 0 on success, -1 on failure + * + * HMAC-HASH (PTK-KCK, AA || SPA || RSNE || RSNXE || Frame Data) + */ +int wpa_auth_8021x_mic(int akmp, const u8 *kck, size_t kck_len, const u8 *addr1, + const u8 *addr2, const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic) +{ + size_t mic_len; + u8 *buf; + int ret = -1; + size_t buf_len = 2 * ETH_ALEN + data_len + frame_len; + + if (!kck) { + wpa_printf(MSG_INFO, "802.1X: No KCK for MIC calculation"); + return -1; + } + + if (!data || !data_len) { + wpa_printf(MSG_INFO, + "802.1X: Invalid data for MIC calculation"); + return -1; + } + + buf = os_zalloc(buf_len); + if (!buf) + return -1; + + wpa_printf(MSG_DEBUG, "802.1X MIC calculation"); + wpa_printf(MSG_DEBUG, "addr1: " MACSTR, MAC2STR(addr1)); + wpa_printf(MSG_DEBUG, "addr2: " MACSTR, MAC2STR(addr2)); + + os_memcpy(buf, addr1, ETH_ALEN); + os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN); + + wpa_hexdump_key(MSG_DEBUG, "MIC: data", data, data_len); + os_memcpy(buf + 2 * ETH_ALEN, data, data_len); + + wpa_hexdump_key(MSG_DEBUG, "MIC: KCK", kck, kck_len); + + if (frame) { + wpa_hexdump_key(MSG_MSGDUMP, "802.1X: MIC: frame", + frame, frame_len); + os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len); + } + + wpa_hexdump_key(MSG_DEBUG, "MIC: buf", buf, buf_len); + if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "MIC: HMAC-SHA384"); + mic_len = 24; + + if (hmac_sha384(kck, kck_len, buf, buf_len, mic) < 0) + goto out; + } else { + wpa_printf(MSG_DEBUG, "MIC: HMAC-SHA256"); + mic_len = 16; + + if (hmac_sha256(kck, kck_len, buf, buf_len, mic) < 0) + goto out; + } + + wpa_hexdump_key(MSG_DEBUG, "802.1X: Calculated MIC", mic, mic_len); + ret = 0; +out: + bin_clear_free(buf, buf_len); + return ret; +} + +#endif /* CONFIG_IEEE8021X_AUTH */ + + /** * pasn_mic - Calculate PASN MIC + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() * @kck: The key confirmation key for the PASN PTKSA - * @akmp: Negotiated AKM - * @cipher: Negotiated pairwise cipher - * @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the - * BSSID - * @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant - * address + * @kck_len: KCK length in octets + * @addr1: For the 2nd PASN/EPPKE frame supplicant address is used for non-MLO; + * for MLO, 2nd EPPKE authentication to use non-AP MLD MAC address. + * For the 3rd PASN/EPPKE frame BSSID is used for non-MLO; for MLO, 3rd + * EPPKE authentication to use AP MLD MAC address as per + * @addr2: For the 2nd PASN/EPPKE frame BSSID is used for non-MLO; for MLO, 2nd + * EPPKE authentication to use AP MLD MAC address. For the 3rd PASN/EPPKE + * frame supplicant address is used for non-MLO; for MLO, 3rd EPPKE + * Authentication frame to use non-AP MLD MAC address. * @data: For calculating the MIC for the 2nd PASN frame, this should hold the * Beacon frame RSNE + RSNXE. For calculating the MIC for the 3rd PASN * frame, this should hold the hash of the body of the PASN 1st frame. @@ -1645,21 +1976,29 @@ int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher) * maximal MIC length * Returns: 0 on success, -1 on failure */ -int pasn_mic(const u8 *kck, int akmp, int cipher, +int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len, const u8 *addr1, const u8 *addr2, const u8 *data, size_t data_len, const u8 *frame, size_t frame_len, u8 *mic) { u8 *buf; - u8 hash[SHA384_MAC_LEN]; + u8 hash[SHA512_MAC_LEN]; size_t buf_len = 2 * ETH_ALEN + data_len + frame_len; int ret = -1; + size_t mic_len; if (!kck) { wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation"); return -1; } + if (kck_len != WPA_PASN_KCK_LEN) { + wpa_printf(MSG_ERROR, + "PASN: Unexpected KCK length %zu for MIC calculation", + kck_len); + return -1; + } + if (!data || !data_len) { wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation"); return -1; @@ -1683,27 +2022,40 @@ int pasn_mic(const u8 *kck, int akmp, int cipher, wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame", frame, frame_len); os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len); - wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, WPA_PASN_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, kck_len); wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf", buf, buf_len); - if (pasn_use_sha384(akmp, cipher)) { + mic_len = pasn_mic_len(alg); + + switch (alg) { +#ifdef CONFIG_SHA512 + case RSN_HASH_SHA512: + wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA512"); + if (hmac_sha512(kck, kck_len, buf, buf_len, hash)) + goto err; + break; +#endif /* CONFIG_SHA512 */ +#ifdef CONFIG_SHA384 + case RSN_HASH_SHA384: wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384"); - - if (hmac_sha384(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash)) + if (hmac_sha384(kck, kck_len, buf, buf_len, hash)) goto err; - - os_memcpy(mic, hash, 24); - wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 24); - } else { + break; +#endif /* CONFIG_SHA384 */ + case RSN_HASH_SHA256: wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256"); - - if (hmac_sha256(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash)) + if (hmac_sha256(kck, kck_len, buf, buf_len, hash)) goto err; - - os_memcpy(mic, hash, 16); - wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 16); + break; + default: + wpa_printf(MSG_ERROR, + "PASN: Unsupported alg=%d for MIC calculation", alg); + goto err; } + os_memcpy(mic, hash, mic_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: MIC", mic, mic_len); + ret = 0; err: bin_clear_free(buf, buf_len); @@ -1713,23 +2065,33 @@ err: /** * pasn_auth_frame_hash - Computes a hash of an Authentication frame body - * @akmp: Negotiated AKM - * @cipher: Negotiated pairwise cipher + * @alg: Selected hash algorithm from pasn_pmk_to_ptk() * @data: Pointer to the Authentication frame body * @len: Length of the Authentication frame body * @hash: On return would hold the computed hash. Should be big enough to handle - * SHA384. + * SHA512. * Returns: 0 on success, -1 on failure */ -int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, +int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len, u8 *hash) { - if (pasn_use_sha384(akmp, cipher)) { + switch (alg) { +#ifdef CONFIG_SHA512 + case RSN_HASH_SHA512: + wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-512"); + return sha512_vector(1, &data, &len, hash); +#endif /* CONFIG_SHA512 */ +#ifdef CONFIG_SHA384 + case RSN_HASH_SHA384: wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-384"); return sha384_vector(1, &data, &len, hash); - } else { +#endif /* CONFIG_SHA384 */ + case RSN_HASH_SHA256: wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-256"); return sha256_vector(1, &data, &len, hash); + default: + wpa_printf(MSG_ERROR, "PASN: Unsupported alg=%d", alg); + return -1; } } @@ -1738,93 +2100,13 @@ int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, static int rsn_selector_to_bitfield(const u8 *s) { - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) - return WPA_CIPHER_NONE; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) - return WPA_CIPHER_TKIP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) - return WPA_CIPHER_CCMP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) - return WPA_CIPHER_AES_128_CMAC; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) - return WPA_CIPHER_GCMP; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256) - return WPA_CIPHER_CCMP_256; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256) - return WPA_CIPHER_GCMP_256; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128) - return WPA_CIPHER_BIP_GMAC_128; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256) - return WPA_CIPHER_BIP_GMAC_256; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256) - return WPA_CIPHER_BIP_CMAC_256; - if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED) - return WPA_CIPHER_GTK_NOT_USED; - return 0; + return rsn_cipher_suite_to_wpa_cipher(RSN_SELECTOR_GET(s)); } static int rsn_key_mgmt_to_bitfield(const u8 *s) { - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) - return WPA_KEY_MGMT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) - return WPA_KEY_MGMT_PSK; -#ifdef CONFIG_IEEE80211R - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) - return WPA_KEY_MGMT_FT_IEEE8021X; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) - return WPA_KEY_MGMT_FT_PSK; -#ifdef CONFIG_SHA384 - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384) - return WPA_KEY_MGMT_FT_IEEE8021X_SHA384; -#endif /* CONFIG_SHA384 */ -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_SHA384 - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA384) - return WPA_KEY_MGMT_IEEE8021X_SHA384; -#endif /* CONFIG_SHA384 */ - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) - return WPA_KEY_MGMT_IEEE8021X_SHA256; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) - return WPA_KEY_MGMT_PSK_SHA256; -#ifdef CONFIG_SAE - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) - return WPA_KEY_MGMT_SAE; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY) - return WPA_KEY_MGMT_SAE_EXT_KEY; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) - return WPA_KEY_MGMT_FT_SAE; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY) - return WPA_KEY_MGMT_FT_SAE_EXT_KEY; -#endif /* CONFIG_SAE */ - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B) - return WPA_KEY_MGMT_IEEE8021X_SUITE_B; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) - return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256) - return WPA_KEY_MGMT_FILS_SHA256; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384) - return WPA_KEY_MGMT_FILS_SHA384; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256) - return WPA_KEY_MGMT_FT_FILS_SHA256; - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384) - return WPA_KEY_MGMT_FT_FILS_SHA384; -#ifdef CONFIG_OWE - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE) - return WPA_KEY_MGMT_OWE; -#endif /* CONFIG_OWE */ -#ifdef CONFIG_DPP - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP) - return WPA_KEY_MGMT_DPP; -#endif /* CONFIG_DPP */ - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) - return WPA_KEY_MGMT_OSEN; -#ifdef CONFIG_PASN - if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN) - return WPA_KEY_MGMT_PASN; -#endif /* CONFIG_PASN */ - return 0; + return rsn_key_mgmt_to_wpa_akm(RSN_SELECTOR_GET(s)); } @@ -1880,16 +2162,14 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, return -1; } - if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && - rsn_ie[1] == rsn_ie_len - 2 && - WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { - pos = rsn_ie + 6; - left = rsn_ie_len - 6; - - data->group_cipher = WPA_CIPHER_GTK_NOT_USED; - data->has_group = 1; - data->key_mgmt = WPA_KEY_MGMT_OSEN; - data->proto = WPA_PROTO_OSEN; + if (rsn_ie_len >= 2 + 4 + 2 && rsn_ie[1] >= 4 + 2 && + rsn_ie[1] == rsn_ie_len - 2 && + (WPA_GET_BE32(&rsn_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE || + WPA_GET_BE32(&rsn_ie[2]) == + RSNE_OVERRIDE_2_IE_VENDOR_TYPE) && + WPA_GET_LE16(&rsn_ie[2 + 4]) == RSN_VERSION) { + pos = rsn_ie + 2 + 4 + 2; + left = rsn_ie_len - 2 - 4 - 2; } else { const struct rsn_ie_hdr *hdr; @@ -2689,6 +2969,70 @@ int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa, #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_PMKSA_PRIVACY +/** + * rsn_pmkid_privacy - Calculate a new PMKID for PMKSA caching privacy + * @pmkid_anonce: Authenticator nonce (ANonce) + * @pmkid_snonce: Supplicant nonce (SNonce) + * @akmp: Negotiated key management protocol + * @pmk_len: PMK length in bytes + * @pmkid: Buffer for returning PMKID + * Returns: 0 on success, -1 on failure + * + * IEEE P802.11bi/D4.0, 12.16.7.2 (PMKID privacy) + * PMKID = Truncate-128(Hash("PMK Name" || PMKIDANonce || PMKIDSNonce)) + */ +int rsn_pmkid_privacy(const u8 *pmkid_anonce, const u8 *pmkid_snonce, + int akmp, size_t pmk_len, u8 *pmkid) +{ + const char *label = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, NONCE_LEN, NONCE_LEN }; + unsigned char hash[SHA512_MAC_LEN]; + + wpa_hexdump(MSG_DEBUG, "PMKID privacy: PMKIDANonce", + pmkid_anonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "PMKID privacy: PMKIDSNonce", + pmkid_snonce, NONCE_LEN); + + addr[0] = (const u8 *) label; + addr[1] = pmkid_anonce; + addr[2] = pmkid_snonce; + + if (0) { +#if defined(CONFIG_FILS) || defined(CONFIG_SHA384) + } else if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using SHA-384"); + if (sha384_vector(3, addr, len, hash) < 0) + return -1; +#endif /* CONFIG_FILS || CONFIG_SHA384 */ +#ifdef CONFIG_SAE + } else if (wpa_key_mgmt_sae_ext_key(akmp)) { + if (pmk_len == 64) { + if (sha512_vector(3, addr, len, hash) < 0) + return -1; + } else if (pmk_len == 48) { + if (sha384_vector(3, addr, len, hash) < 0) + return -1; + } else { + if (sha256_vector(3, addr, len, hash) < 0) + return -1; + } +#endif /* CONFIG_SAE */ + } else { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using SHA-256"); + if (sha256_vector(3, addr, len, hash) < 0) + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PMKID privacy: new PMKID", hash, PMKID_LEN); + os_memcpy(pmkid, hash, PMKID_LEN); + + return 0; +} +#endif /* CONFIG_PMKSA_PRIVACY */ + + /** * wpa_cipher_txt - Convert cipher suite to a text string * @cipher: Cipher suite (WPA_CIPHER_* enum) @@ -2780,8 +3124,6 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "FT-SAE"; case WPA_KEY_MGMT_FT_SAE_EXT_KEY: return "FT-SAE-EXT-KEY"; - case WPA_KEY_MGMT_OSEN: - return "OSEN"; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: return "WPA2-EAP-SUITE-B"; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: @@ -2802,6 +3144,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "PASN"; case WPA_KEY_MGMT_IEEE8021X_SHA384: return "WPA2-EAP-SHA384"; + case WPA_KEY_MGMT_EPPKE: + return "EPPKE"; default: return "UNKNOWN"; } @@ -2828,8 +3172,6 @@ u32 wpa_akm_to_suite(int akm) return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; if (akm & WPA_KEY_MGMT_CCKM) return RSN_AUTH_KEY_MGMT_CCKM; - if (akm & WPA_KEY_MGMT_OSEN) - return RSN_AUTH_KEY_MGMT_OSEN; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B) return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) @@ -2854,6 +3196,12 @@ u32 wpa_akm_to_suite(int akm) return RSN_AUTH_KEY_MGMT_OWE; if (akm & WPA_KEY_MGMT_DPP) return RSN_AUTH_KEY_MGMT_DPP; +#ifdef CONFIG_PASN + if (akm & WPA_KEY_MGMT_PASN) + return RSN_AUTH_KEY_MGMT_PASN; +#endif /* CONFIG_PASN */ + if (akm & WPA_KEY_MGMT_EPPKE) + return RSN_AUTH_KEY_MGMT_EPPKE; return 0; } @@ -2895,6 +3243,38 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, } +/** + * wpa_compare_rsn_ie_params - Compare RSN IE parameters + * @rsne1: Pointer to first RSNE (with element ID and length) + * @rsne1_len: Length of the first RSNE + * @rsnee2: Pointer to second RSNE (with element ID and length) + * @rsne2_len: Length of the second RSNE + * Returns: 0 if parameters match, -1 otherwise + * + * Compare the security parameters (proto, ciphers, key_mgmt) + * of two RSN IEs. + */ +int wpa_compare_rsne_params(const u8 *rsne1, size_t rsne1_len, + const u8 *rsne2, size_t rsne2_len) +{ + struct wpa_ie_data rsn1, rsn2; + + if (!rsne1 || !rsne2) + return -1; + + if (wpa_parse_wpa_ie_rsn(rsne1, rsne1_len, &rsn1) < 0 || + wpa_parse_wpa_ie_rsn(rsne2, rsne2_len, &rsn2) < 0) + return -1; + + if (rsn1.proto == rsn2.proto && + rsn1.pairwise_cipher == rsn2.pairwise_cipher && + rsn1.key_mgmt == rsn2.key_mgmt) + return 0; + + return -1; +} + + int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace) { u8 *start, *end, *rpos, *rend; @@ -3162,10 +3542,10 @@ int wpa_cipher_put_suites(u8 *start, int ciphers) int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) { - if (ciphers & WPA_CIPHER_CCMP_256) - return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_GCMP_256) return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_CCMP) return WPA_CIPHER_CCMP; if (ciphers & WPA_CIPHER_GCMP) @@ -3180,10 +3560,10 @@ int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) int wpa_pick_group_cipher(int ciphers) { - if (ciphers & WPA_CIPHER_CCMP_256) - return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_GCMP_256) return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; if (ciphers & WPA_CIPHER_CCMP) return WPA_CIPHER_CCMP; if (ciphers & WPA_CIPHER_GCMP) @@ -3440,7 +3820,7 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie) const u8 *p; size_t left; u8 link_id; - char title[50]; + char title[100]; int ret; if (len == 0) @@ -3462,12 +3842,6 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie) return 0; } - if (selector == OSEN_IE_VENDOR_TYPE) { - ie->osen = pos; - ie->osen_len = dlen; - return 0; - } - if (left >= PMKID_LEN && selector == RSN_KEY_DATA_PMKID) { ie->pmkid = p; wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, dlen); @@ -3510,6 +3884,15 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie) return 0; } + if (left > 2 && selector == RSN_KEY_DATA_SAE_PW_IDS) { + ie->sae_pw_ids = p; + ie->sae_pw_ids_len = left; + wpa_hexdump_key(MSG_DEBUG, + "RSN: SAE Password Identifiers in EAPOL-Key", + pos, dlen); + return 0; + } + if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) { ie->ip_addr_req = p; wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", @@ -3621,6 +4004,78 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie) return 0; } + if (left >= 1 && selector == WFA_KEY_DATA_RSN_OVERRIDE_LINK) { + link_id = p[0]; + if (link_id >= MAX_NUM_MLD_LINKS) + return 2; + + ie->rsn_override_link[link_id] = p; + ie->rsn_override_link_len[link_id] = left; + ret = os_snprintf(title, sizeof(title), + "RSN: Link ID %u - RSN Override Link KDE in EAPOL-Key", + link_id); + if (!os_snprintf_error(sizeof(title), ret)) + wpa_hexdump(MSG_DEBUG, title, pos, dlen); + return 0; + } + + if (selector == RSNE_OVERRIDE_IE_VENDOR_TYPE) { + ie->rsne_override = pos; + ie->rsne_override_len = dlen; + wpa_hexdump(MSG_DEBUG, + "RSN: RSNE Override element in EAPOL-Key", + ie->rsne_override, ie->rsne_override_len); + return 0; + } + + if (selector == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) { + ie->rsne_override_2 = pos; + ie->rsne_override_2_len = dlen; + wpa_hexdump(MSG_DEBUG, + "RSN: RSNE Override 2 element in EAPOL-Key", + ie->rsne_override_2, ie->rsne_override_2_len); + return 0; + } + + if (selector == RSNXE_OVERRIDE_IE_VENDOR_TYPE) { + ie->rsnxe_override = pos; + ie->rsnxe_override_len = dlen; + wpa_hexdump(MSG_DEBUG, + "RSN: RSNXE Override element in EAPOL-Key", + ie->rsnxe_override, ie->rsnxe_override_len); + return 0; + } + + if (selector == RSN_SELECTION_IE_VENDOR_TYPE) { + ie->rsn_selection = p; + ie->rsn_selection_len = left; + wpa_hexdump(MSG_DEBUG, + "RSN: RSN Selection element in EAPOL-Key", + ie->rsn_selection, ie->rsn_selection_len); + return 0; + } + +#if defined(CONFIG_NAN) || defined(CONFIG_PASN) + if (left >= sizeof(struct nan_nik_kde) && + selector == NAN_KEY_DATA_NIK) { + ie->nan_nik = p; + ie->nan_nik_len = left; + wpa_hexdump_key(MSG_DEBUG, "RSN: NAN NIK KDE in EAPOL-Key", + ie->nan_nik, ie->nan_nik_len); + return 0; + } + + if (left >= sizeof(struct nan_key_lifetime_kde) && + selector == NAN_KEY_DATA_LIFETIME) { + ie->nan_key_lifetime = p; + ie->nan_key_lifetime_len = left; + wpa_hexdump(MSG_DEBUG, + "RSN: NAN Key Lifetime KDE in EAPOL-Key", + ie->nan_key_lifetime, ie->nan_key_lifetime_len); + return 0; + } +#endif /* CONFIG_NAN || CONFIG_PASN */ + return 2; } @@ -3779,7 +4234,7 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) /* * wpa_pasn_build_auth_header - Add the MAC header and initialize Authentication - * frame for PASN + * frame for PASN/EPPKE * * @buf: Buffer in which the header will be added * @bssid: The BSSID of the AP @@ -3787,15 +4242,17 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) * @dst: Destination address * @trans_seq: Authentication transaction sequence number * @status: Authentication status + * @is_eppke: EPPKE authentication */ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, const u8 *src, const u8 *dst, - u8 trans_seq, u16 status) + u8 trans_seq, u16 status, bool is_eppke) { struct ieee80211_mgmt *auth; + u16 auth_alg = is_eppke ? WLAN_AUTH_EPPKE : WLAN_AUTH_PASN; - wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u", - trans_seq); + wpa_printf(MSG_DEBUG, "%s: Add authentication header trans_seq=%u", + is_eppke ? "EPPKE" : "PASN", trans_seq); auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, u.auth.variable)); @@ -3808,7 +4265,7 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, os_memcpy(auth->bssid, bssid, ETH_ALEN); auth->seq_ctrl = 0; - auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN); + auth->u.auth.auth_alg = host_to_le16(auth_alg); auth->u.auth.auth_transaction = host_to_le16(trans_seq); auth->u.auth.status_code = host_to_le16(status); } @@ -3862,6 +4319,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher) case WPA_KEY_MGMT_PASN: RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN); break; +#ifdef CONFIG_ENC_ASSOC + case WPA_KEY_MGMT_EPPKE: + /* EPPKE without base AKM: EPPKE AKMP is used as the selected + * AKMP per IEEE P802.11bi/D4.0, 12.16.9.1. */ + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE); + break; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE case WPA_KEY_MGMT_SAE: RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); @@ -4037,7 +4501,7 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf, data_len -= len - 1; while (data_len) { - if (wpabuf_tailroom(buf) < 1 + data_len) + if (wpabuf_tailroom(buf) < 2 + data_len) return -1; wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); len = data_len > 255 ? 255 : data_len; @@ -4052,23 +4516,29 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf, /* - * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE + * wpa_pasn_validate_rsne - Validate PASN/EPPKE specific data of RSNE * @data: Parsed representation of an RSNE + * @is_eppke: EPPKE Authentication * Returns -1 for invalid data; otherwise 0 */ -int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke) { - u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR; + u16 capab = WPA_CAPABILITY_MFPC; + + if (!is_eppke) + capab |= WPA_CAPABILITY_MFPR; if (data->proto != WPA_PROTO_RSN) return -1; if ((data->capabilities & capab) != capab) { - wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities"); + wpa_printf(MSG_DEBUG, "%s: Invalid RSNE capabilities", + is_eppke ? "EPPKE" : "PASN"); return -1; } - if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) { + if (!data->has_group || + (!is_eppke && data->group_cipher != WPA_CIPHER_GTK_NOT_USED)) { wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher"); return -1; } @@ -4098,13 +4568,25 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data) #endif /* CONFIG_IEEE80211R */ case WPA_KEY_MGMT_PASN: break; +#ifdef CONFIG_ENC_ASSOC + case WPA_KEY_MGMT_EPPKE: + /* EPPKE AKMP is used when EPPKE is performed without a base + * AKM (no mutual authentication). This is only valid for EPPKE + * authentication per IEEE P802.11bi/D4.0, 12.16.9.1. */ + if (!is_eppke) { + wpa_printf(MSG_INFO, + "PASN: EPPKE AKM not allowed for PASN"); + return -1; + } + break; +#endif /* CONFIG_ENC_ASSOC */ default: - wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x", - data->key_mgmt); + wpa_printf(MSG_ERROR, "%s: invalid key_mgmt: 0x%0x", + is_eppke ? "EPPKE" : "PASN", data->key_mgmt); return -1; } - if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) { + if (!is_eppke && (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED)) { wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher"); return -1; } @@ -4217,23 +4699,54 @@ int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, } -void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab) +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u64 capab) { size_t flen; - flen = (capab & 0xff00) ? 2 : 1; if (!capab) return; /* no supported extended RSN capabilities */ + + /* Determine how many octets are needed to represent capab */ + if (capab & 0xFF00000000000000ULL) + flen = 8; + else if (capab & 0x00FF000000000000ULL) + flen = 7; + else if (capab & 0x0000FF0000000000ULL) + flen = 6; + else if (capab & 0x000000FF00000000ULL) + flen = 5; + else if (capab & 0xFF000000) + flen = 4; + else if (capab & 0x00FF0000) + flen = 3; + else if (capab & 0x0000FF00) + flen = 2; + else + flen = 1; + if (wpabuf_tailroom(buf) < 2 + flen) return; capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ wpabuf_put_u8(buf, WLAN_EID_RSNX); wpabuf_put_u8(buf, flen); - wpabuf_put_u8(buf, capab & 0x00ff); - capab >>= 8; - if (capab) - wpabuf_put_u8(buf, capab); + + /* Write the little endian capability field octet-by-octet */ + wpabuf_put_u8(buf, capab & 0x000000FF); + if (flen > 1) + wpabuf_put_u8(buf, (capab >> 8) & 0x000000FF); + if (flen > 2) + wpabuf_put_u8(buf, (capab >> 16) & 0x000000FF); + if (flen > 3) + wpabuf_put_u8(buf, (capab >> 24) & 0x000000FF); + if (flen > 4) + wpabuf_put_u8(buf, (capab >> 32) & 0x000000FF); + if (flen > 5) + wpabuf_put_u8(buf, (capab >> 40) & 0x000000FF); + if (flen > 6) + wpabuf_put_u8(buf, (capab >> 48) & 0x000000FF); + if (flen > 7) + wpabuf_put_u8(buf, (capab >> 56) & 0x000000FF); } @@ -4252,7 +4765,7 @@ int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len) if (!len || !extra_ies || !buf) return 0; - if (wpabuf_tailroom(buf) < sizeof(len)) + if (wpabuf_tailroom(buf) < len) return -1; wpabuf_put_data(buf, extra_ies, len); @@ -4260,3 +4773,43 @@ int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len) } #endif /* CONFIG_PASN */ + + +void rsn_set_snonce_cookie(u8 *snonce) +{ + u8 *pos; + + pos = snonce + WPA_NONCE_LEN - 6; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + WPA_PUT_BE24(pos, 0x000029); +} + + +bool rsn_is_snonce_cookie(const u8 *snonce) +{ + const u8 *pos; + + pos = snonce + WPA_NONCE_LEN - 6; + return WPA_GET_BE24(pos) == OUI_WFA && + WPA_GET_BE24(pos + 3) == 0x000029; +} + + +void wpa_add_supported_groups(struct wpabuf *buf, const int *groups) +{ + unsigned int count, i; + + if (!buf || !groups) + return; + + count = int_array_len(groups); + if (wpabuf_tailroom(buf) < 2 + 1 + count * 2) + return; + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + count * 2); + wpabuf_put_u8(buf, WLAN_EID_EXT_SUPPORTED_GROUPS); + for (i = 0; i < count; i++) + wpabuf_put_le16(buf, groups[i]); +} diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h index 1e3136843437..51bd3886f38f 100644 --- a/contrib/wpa/src/common/wpa_common.h +++ b/contrib/wpa/src/common/wpa_common.h @@ -23,11 +23,20 @@ #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 #define WPA_PASN_PMK_LEN 32 -#define WPA_PASN_MAX_MIC_LEN 24 +#define WPA_PASN_MAX_MIC_LEN 32 #define WPA_MAX_RSNXE_LEN 4 +#define WPA_1X_MAX_MIC_LEN 32 #define OWE_DH_GROUP 19 +enum rsn_hash_alg { + RSN_HASH_NOT_SPECIFIED, + RSN_HASH_SHA1, + RSN_HASH_SHA256, + RSN_HASH_SHA384, + RSN_HASH_SHA512, +}; + #ifdef CONFIG_NO_TKIP #define WPA_ALLOWED_PAIRWISE_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_NONE | \ @@ -92,9 +101,9 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_AUTH_KEY_MGMT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 23) #define RSN_AUTH_KEY_MGMT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 24) #define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25) +#define RSN_AUTH_KEY_MGMT_EPPKE RSN_SELECTOR(0x00, 0x0f, 0xac, 29) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) -#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) #define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) @@ -139,11 +148,13 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_KEY_DATA_MLO_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 17) #define RSN_KEY_DATA_MLO_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 18) #define RSN_KEY_DATA_MLO_LINK RSN_SELECTOR(0x00, 0x0f, 0xac, 19) +#define RSN_KEY_DATA_SAE_PW_IDS RSN_SELECTOR(0x00, 0x0f, 0xac, 25) #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) #define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20) #define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21) +#define WFA_KEY_DATA_RSN_OVERRIDE_LINK RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x2d) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) @@ -246,7 +257,6 @@ struct wpa_eapol_key { #define FILS_ICK_MAX_LEN 48 #define FILS_FT_MAX_LEN 48 #define WPA_PASN_KCK_LEN 32 -#define WPA_PASN_MIC_MAX_LEN 24 #define WPA_LTF_KEYSEED_MAX_LEN 48 /** @@ -267,8 +277,12 @@ struct wpa_ptk { size_t kck2_len; size_t kek2_len; size_t kdk_len; + size_t ptk_len; size_t ltf_keyseed_len; + enum rsn_hash_alg hash_alg; int installed; /* 1 if key has already been installed to driver */ + bool installed_rx; /* whether TK has been installed as the next TK + * for temporary RX-only use in the driver */ }; struct wpa_gtk { @@ -346,6 +360,13 @@ struct rsn_error_kde { be16 error_type; } STRUCT_PACKED; +#define WPA_GTK_KDE_PREFIX_LEN 2 +struct wpa_gtk_kde { + u8 keyid; + u8 reserved; + u8 gtk[WPA_GTK_MAX_LEN]; +} STRUCT_PACKED; + #define WPA_IGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN) struct wpa_igtk_kde { u8 keyid[2]; @@ -458,7 +479,8 @@ struct rsn_rdie { #endif /* _MSC_VER */ -int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, +int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, + enum rsn_hash_alg hash, int ver, const u8 *buf, size_t len, u8 *mic); int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, @@ -553,6 +575,8 @@ static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, return -1; } #endif /* CONFIG_SUITEB192 */ +int rsn_pmkid_privacy(const u8 *pmkid_anonce, const u8 *pmkid_snonce, + int akmp, size_t pmk_len, u8 *pmkid); const char * wpa_cipher_txt(int cipher); const char * wpa_key_mgmt_txt(int key_mgmt, int proto); @@ -560,6 +584,8 @@ u32 wpa_akm_to_suite(int akm); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len); +int wpa_compare_rsne_params(const u8 *rsne1, size_t rsne1_len, + const u8 *rsne2, size_t rsne2_len); int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace); struct wpa_ft_ies { @@ -611,7 +637,7 @@ struct wpa_ft_ies { struct wpabuf *fte_buf; }; -/* IEEE P802.11az/D2.6 - 9.4.2.303 PASN Parameters element */ +/* IEEE Std 802.11-2024 - 9.4.2.305 PASN Parameters element */ #define WPA_PASN_CTRL_COMEBACK_INFO_PRESENT BIT(0) #define WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT BIT(1) @@ -643,6 +669,14 @@ struct wpa_pasn_params_data { #define WPA_PASN_PUBKEY_COMPRESSED_1 0x03 #define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04 +/* WPA3 specification - RSN Selection element */ +enum rsn_selection_variant { + RSN_SELECTION_RSNE = 0, + RSN_SELECTION_RSNE_OVERRIDE = 1, + RSN_SELECTION_RSNE_OVERRIDE_2 = 2, +}; + + int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, int key_mgmt, bool reassoc_resp); void wpa_ft_parse_ies_free(struct wpa_ft_ies *parse); @@ -661,6 +695,8 @@ struct wpa_eapol_ie_parse { size_t igtk_len; const u8 *bigtk; size_t bigtk_len; + const u8 *sae_pw_ids; + size_t sae_pw_ids_len; const u8 *mdie; size_t mdie_len; const u8 *ftie; @@ -704,6 +740,18 @@ struct wpa_eapol_ie_parse { u16 aid; const u8 *wmm; size_t wmm_len; + const u8 *rsn_selection; + size_t rsn_selection_len; + const u8 *rsne_override; + size_t rsne_override_len; + const u8 *rsne_override_2; + size_t rsne_override_2_len; + const u8 *rsnxe_override; + size_t rsnxe_override_len; + const u8 *nan_nik; + size_t nan_nik_len; + const u8 *nan_key_lifetime; + size_t nan_key_lifetime_len; u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */ const u8 *mlo_gtk[MAX_NUM_MLD_LINKS]; size_t mlo_gtk_len[MAX_NUM_MLD_LINKS]; @@ -716,6 +764,8 @@ struct wpa_eapol_ie_parse { u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */ const u8 *mlo_link[MAX_NUM_MLD_LINKS]; size_t mlo_link_len[MAX_NUM_MLD_LINKS]; + const u8 *rsn_override_link[MAX_NUM_MLD_LINKS]; + size_t rsn_override_link_len[MAX_NUM_MLD_LINKS]; }; int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie); @@ -740,34 +790,39 @@ int wpa_pick_group_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); -unsigned int wpa_mic_len(int akmp, size_t pmk_len); +unsigned int wpa_mic_len(int akmp, size_t pmk_len, enum rsn_hash_alg hash); +unsigned int wpa_kek_len(int akmp, size_t pmk_len); int wpa_use_akm_defined(int akmp); int wpa_use_cmac(int akmp); int wpa_use_aes_key_wrap(int akmp); int fils_domain_name_hash(const char *domain, u8 *hash); -bool pasn_use_sha384(int akmp, int cipher); int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len); + size_t kdk_len, size_t kek_len, enum rsn_hash_alg *alg, + bool is_eppke); -u8 pasn_mic_len(int akmp, int cipher); +size_t pasn_mic_len(enum rsn_hash_alg alg); -int pasn_mic(const u8 *kck, int akmp, int cipher, +int wpa_auth_8021x_mic(int akmp, const u8 *kck, size_t kck_len, const u8 *addr1, + const u8 *addr2, const u8 *data, size_t data_len, + const u8 *frame, size_t frame_len, u8 *mic); + +int pasn_mic(enum rsn_hash_alg alg, const u8 *kck, size_t kck_len, const u8 *addr1, const u8 *addr2, const u8 *data, size_t data_len, const u8 *frame, size_t frame_len, u8 *mic); int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher); -int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len, +int pasn_auth_frame_hash(enum rsn_hash_alg alg, const u8 *data, size_t len, u8 *hash); void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid, const u8 *src, const u8 *dst, - u8 trans_seq, u16 status); + u8 trans_seq, u16 status, bool is_eppke); int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher); @@ -780,11 +835,24 @@ void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group, int wpa_pasn_add_wrapped_data(struct wpabuf *buf, struct wpabuf *wrapped_data_buf); -int wpa_pasn_validate_rsne(const struct wpa_ie_data *data); +int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke); int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap, struct wpa_pasn_params_data *pasn_params); -void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab); +void wpa_pasn_add_rsnxe(struct wpabuf *buf, u64 capab); int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len); +void rsn_set_snonce_cookie(u8 *snonce); +bool rsn_is_snonce_cookie(const u8 *snonce); + +int rsn_cipher_suite_to_wpa_cipher(u32 cipher); +int rsn_key_mgmt_to_wpa_akm(u32 akm_suite); + +int wpa_auth_802_1x_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, + const u8 *aa, const u8 *snonce, const u8 *anonce, + int akmp, int cipher, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + size_t kdk_len); +void wpa_add_supported_groups(struct wpabuf *buf, const int *groups); + #endif /* WPA_COMMON_H */ diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index f6142501e440..ae93e1e4ef88 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -13,6 +13,8 @@ extern "C" { #endif +#define WPA_CTRL_IFACE_LINK_NAME "link" + /* wpa_supplicant control interface - fixed message prefixes */ /** Interactive request for identity/password/pin */ @@ -116,6 +118,10 @@ extern "C" { #define WPA_EVENT_T2LM_UPDATE "CTRL-EVENT-T2LM-UPDATE " /** MLO link reconfiguration event */ #define WPA_EVENT_LINK_RECONFIG "CTRL-EVENT-LINK-RECONFIG " +/** MLO link STA removed through link reconfiguration */ +#define WPA_EVENT_LINK_STA_REMOVED "CTRL-EVENT-LINK-STA-REMOVED " +/** MLO link STA added through link reconfiguration */ +#define WPA_EVENT_LINK_STA_ADDED "CTRL-EVENT-LINK-STA-ADDED " /** IP subnet status change notification * @@ -204,6 +210,7 @@ extern "C" { #define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID " #define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET " #define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS " +#define DPP_EVENT_CONFOBJ_IDPASS "DPP-CONFOBJ-IDPASS " #define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK " #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR " #define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY " @@ -225,6 +232,7 @@ extern "C" { #define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED " #define DPP_EVENT_MUD_URL "DPP-MUD-URL " #define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT " +#define DPP_EVENT_ENROLLEE_CAPABILITY "DPP-ENROLLEE-CAPABILITY " #define DPP_EVENT_CSR "DPP-CSR " #define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX " #define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED " @@ -232,12 +240,37 @@ extern "C" { #define DPP_EVENT_PB_RESULT "DPP-PB-RESULT " #define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER " -/* Wi-Fi Aware (NAN USD) events */ +/* Wi-Fi Aware (NAN) events */ #define NAN_DISCOVERY_RESULT "NAN-DISCOVERY-RESULT " #define NAN_REPLIED "NAN-REPLIED " #define NAN_PUBLISH_TERMINATED "NAN-PUBLISH-TERMINATED " #define NAN_SUBSCRIBE_TERMINATED "NAN-SUBSCRIBE-TERMINATED " #define NAN_RECEIVE "NAN-RECEIVE " +#define NAN_TRANSMIT_STATUS "NAN-TRANSMIT-STATUS " +#define NAN_CLUSTER_JOIN "NAN-CLUSTER-JOIN " +#define NAN_NDP_REQUEST "NAN-NDP-REQUEST " +#define NAN_NDP_COUNTER_REQUEST "NAN-NDP-COUNTER-REQUEST " +#define NAN_NDP_CONNECTED "NAN-NDP-CONNECTED " +#define NAN_NDP_DISCONNECTED "NAN-NDP-DISCONNECTED " +#define NAN_BOOTSTRAP_REQUEST "NAN-BOOTSTRAP-REQUEST " +#define NAN_BOOTSTRAP_SUCCESS "NAN-BOOTSTRAP-SUCCESS " +#define NAN_BOOTSTRAP_FAILURE "NAN-BOOTSTRAP-FAILURE " +#define NAN_NIK_RECEIVED "NAN-NIK-RECEIVED " +#define NAN_PAIRING_REQUEST "NAN-PAIRING-REQUEST " +#define NAN_PEER_SCHEDULE_CHANGED "NAN-PEER-SCHEDULE-CHANGED " +#define NAN_SCHEDULE_UPDATE_DONE "NAN-SCHEDULE-UPDATE-DONE " + +/* NAN Pairing status + * addr= akmp= cipher= + * status= [nd_pmk=] + */ +#define NAN_PAIRING_STATUS "NAN-PAIRING-STATUS " + +/* NAN local schedule channel evacuation. + * parameters: map_id= freq= + */ +#define NAN_CHAN_EVACUATION "NAN-CHAN-EVACUATION " +#define NAN_STOPPED "NAN-STOPPED " /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " @@ -304,6 +337,10 @@ extern "C" { #define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START " #define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE " +#define P2P_EVENT_BOOTSTRAP_REQUEST "P2P-BOOTSTRAP-REQUEST " +#define P2P_EVENT_BOOTSTRAP_SUCCESS "P2P-BOOTSTRAP-SUCCESS " +#define P2P_EVENT_BOOTSTRAP_FAILURE "P2P-BOOTSTRAP-FAILURE " + #define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " @@ -335,7 +372,6 @@ extern "C" { /* parameters: */ #define RX_VENUE_URL "RX-VENUE-URL " -#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " #define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE " @@ -448,6 +484,11 @@ extern "C" { /* Event triggered for received management frame */ #define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED " +/* Event triggerred on AP receiving Wi-Fi Alliance Generational Capabilities + * indication. + * Parameters: */ +#define WFA_GEN_CAPAB_RX "WFA-GEN-CAPAB " + #ifndef BIT #define BIT(x) (1U << (x)) #endif @@ -455,6 +496,37 @@ extern "C" { /* PASN authentication status */ #define PASN_AUTH_STATUS "PASN-AUTH-STATUS " +/* Proximity Ranging PASN negotiation started + * peer_addr= role= protocol= + */ +#define PR_PASN_NEGOTIATION_STARTED "PR-PASN-NEGOTIATION-STARTED " + +/* Result of PASN performed for Proximity Ranging + * role= protocol= opclass= channel= cc= + */ +#define PR_PASN_RESULT "PR-PASN-RESULT " +#define PR_RANGING_TERMINATED "PR-RANGING-TERMINATED " + +/* Proximity Ranging parameters to use in ranging */ +#define PR_RANGING_PARAMS "PR-RANGING-PARAMS " + +/* Proximity Ranging measurement result */ +#define PR_EVENT_PEER_MEASUREMENT "PR-PEER-MEASUREMENT " + +/* Proximity Ranging measurement session complete */ +#define PR_EVENT_RANGING_COMPLETE "PR-RANGING-COMPLETE " + +/* Proximity Ranging capable peer discovered via USD + * peer_addr= pasn_type=0x name= + * edca=<0|1> edca_ista=<0|1> edca_rsta=<0|1> + * ntb=<0|1> ntb_ista=<0|1> ntb_rsta=<0|1> + * secure_ltf=<0|1> 6ghz=<0|1> freq= dik_valid=<0|1> + */ +#define PR_PEER_FOUND "PR-PEER-FOUND " + +#define AFC_EVENT_RECEIVED "AFC-EVENT-RECEIVED " +#define AFC_EVENT_COMPLETE "AFC-EVENT-COMPLETE " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index 0ac8fc19476a..e6bc7bac3c3f 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -217,7 +217,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); enum crypto_cipher_alg { CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, - CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 + CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC4 }; struct crypto_cipher; diff --git a/contrib/wpa/src/crypto/crypto_gnutls.c b/contrib/wpa/src/crypto/crypto_gnutls.c index a7a163f5cb2a..524d1fe07904 100644 --- a/contrib/wpa/src/crypto/crypto_gnutls.c +++ b/contrib/wpa/src/crypto/crypto_gnutls.c @@ -439,14 +439,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); break; - case CRYPTO_CIPHER_ALG_RC2: - if (key_len == 5) - a = GCRY_CIPHER_RFC2268_40; - else - a = GCRY_CIPHER_RFC2268_128; - res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); - gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); - break; default: os_free(ctx); return NULL; diff --git a/contrib/wpa/src/crypto/crypto_libtomcrypt.c b/contrib/wpa/src/crypto/crypto_libtomcrypt.c index fd79c1a40337..b14e28260c28 100644 --- a/contrib/wpa/src/crypto/crypto_libtomcrypt.c +++ b/contrib/wpa/src/crypto/crypto_libtomcrypt.c @@ -315,9 +315,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, case CRYPTO_CIPHER_ALG_DES: idx = find_cipher("des"); break; - case CRYPTO_CIPHER_ALG_RC2: - idx = find_cipher("rc2"); - break; case CRYPTO_CIPHER_ALG_RC4: idx = -1; rc4 = 1; diff --git a/contrib/wpa/src/crypto/crypto_linux.c b/contrib/wpa/src/crypto/crypto_linux.c index 9278e279795f..0282743fd947 100644 --- a/contrib/wpa/src/crypto/crypto_linux.c +++ b/contrib/wpa/src/crypto/crypto_linux.c @@ -363,8 +363,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } if (*len < ctx->mac_len) { - crypto_hash_deinit(ctx); *len = ctx->mac_len; + crypto_hash_deinit(ctx); return -1; } *len = ctx->mac_len; @@ -580,7 +580,6 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, linux_af_alg_skcipher_deinit(skcipher); return -1; } - os_free(skip_buf); msg.msg_control = NULL; msg.msg_controllen = 0; @@ -588,9 +587,11 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, if (ret < 0) { wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", __func__, strerror(errno)); + os_free(skip_buf); linux_af_alg_skcipher_deinit(skcipher); return -1; } + os_free(skip_buf); linux_af_alg_skcipher_deinit(skcipher); if ((size_t) ret < skip + data_len) { diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index 2d8ff60aa92d..a6bfc7a7c5a2 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -186,8 +186,32 @@ static int EC_GROUP_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a, #endif /* OpenSSL version < 1.1.1 */ +static void openssl_disable_fips(void) +{ +#ifndef CONFIG_FIPS +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + static bool done = false; + + if (done) + return; + done = true; + + if (!EVP_default_properties_is_fips_enabled(NULL)) + return; /* FIPS mode is not enabled */ + + if (!EVP_default_properties_enable_fips(NULL, 0)) + wpa_printf(MSG_INFO, + "OpenSSL: Failed to disable FIPS mode"); + else + wpa_printf(MSG_DEBUG, + "OpenSSL: Disabled FIPS mode to enable non-FIPS-compliant algorithms and parameters"); +#endif /* OpenSSL version >= 3.0 */ +#endif /* !CONFIG_FIPS */ +} + #if OPENSSL_VERSION_NUMBER >= 0x30000000L static OSSL_PROVIDER *openssl_legacy_provider = NULL; +static OSSL_PROVIDER *openssl_default_provider = NULL; #endif /* OpenSSL version >= 3.0 */ void openssl_load_legacy_provider(void) @@ -212,6 +236,36 @@ static void openssl_unload_legacy_provider(void) } +static void openssl_load_default_provider_if_fips(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (openssl_default_provider) + return; + + if (!OSSL_PROVIDER_available(NULL, "fips")) + return; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Load default provider to replace fips provider when needed"); + openssl_default_provider = OSSL_PROVIDER_try_load(NULL, "default", 1); + if (!openssl_default_provider) + wpa_printf(MSG_DEBUG, + "OpenSSL: Failed to load default provider"); +#endif /* OpenSSL version >= 3.0 */ +} + + +static void openssl_unload_default_provider(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (openssl_default_provider) { + OSSL_PROVIDER_unload(openssl_default_provider); + openssl_default_provider = NULL; + } +#endif /* OpenSSL version >= 3.0 */ +} + + #if OPENSSL_VERSION_NUMBER < 0x30000000L static BIGNUM * get_group5_prime(void) @@ -319,8 +373,16 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, #ifndef CONFIG_FIPS +static void openssl_need_md5(void) +{ + openssl_disable_fips(); + openssl_load_default_provider_if_fips(); +} + + int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + openssl_disable_fips(); openssl_load_legacy_provider(); return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); } @@ -369,7 +431,7 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, EVP_CIPHER_CTX *ctx; int outl; int res = -1; - unsigned char skip_buf[16]; + unsigned char skip_buf[16] = { 0 }; openssl_load_legacy_provider(); @@ -404,6 +466,7 @@ out: int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + openssl_need_md5(); return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } @@ -898,11 +961,11 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, cipher = EVP_des_cbc(); break; #endif /* OPENSSL_NO_DES */ -#ifndef OPENSSL_NO_RC2 - case CRYPTO_CIPHER_ALG_RC2: - cipher = EVP_rc2_ecb(); +#ifndef OPENSSL_NO_NULL + case CRYPTO_CIPHER_NULL: + cipher = EVP_enc_null(); break; -#endif /* OPENSSL_NO_RC2 */ +#endif /* OPENSSL_NO_NULL */ default: os_free(ctx); return NULL; @@ -1023,16 +1086,23 @@ err: struct wpabuf *pubkey = NULL, *privkey = NULL; BIGNUM *priv_bn = NULL; EVP_PKEY_CTX *gctx; + const char *propquery = NULL; *priv = NULL; wpabuf_free(*publ); *publ = NULL; + if (OSSL_PROVIDER_available(NULL, "fips")) { + openssl_disable_fips(); + openssl_load_default_provider_if_fips(); + propquery = "provider!=fips"; + } + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, "modp_1536", 0); params[1] = OSSL_PARAM_construct_end(); - gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL); + gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", propquery); if (!gctx || EVP_PKEY_keygen_init(gctx) != 1 || EVP_PKEY_CTX_set_params(gctx, params) != 1 || @@ -1371,6 +1441,9 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, } if (EVP_MAC_init(ctx->ctx, key, key_len, params) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_MAC_init(hmac,digest=%s) failed: %s", + a, ERR_error_string(ERR_get_error(), NULL)); EVP_MAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); ctx = NULL; @@ -1527,13 +1600,30 @@ static int openssl_hmac_vector(char *digest, const u8 *key, EVP_MAC_CTX *ctx; size_t i, mlen; int res; + const char *property_query = NULL; if (TEST_FAIL()) return -1; - hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); - if (!hmac) +#ifndef CONFIG_FIPS + if (os_strcmp(digest, "MD5") == 0) { + openssl_need_md5(); + property_query = "provider!=fips"; + } else if (key_len < 14 && OSSL_PROVIDER_available(NULL, "fips")) { + /* Need to use non-FIPS provider in OpenSSL to handle cases + * where HMAC is used with salt that is less than 112 bits + * instead of the HMAC uses with an actual key. */ + openssl_disable_fips(); + openssl_load_default_provider_if_fips(); + property_query = "provider!=fips"; + } +#endif /* CONFIG_FIPS */ + hmac = EVP_MAC_fetch(NULL, "HMAC", property_query); + if (!hmac) { + wpa_printf(MSG_INFO, "OpenSSL: EVP_MAC_fetch(HMAC) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); return -1; + } params[0] = OSSL_PARAM_construct_utf8_string("digest", digest, 0); params[1] = OSSL_PARAM_construct_end(); @@ -1543,8 +1633,13 @@ static int openssl_hmac_vector(char *digest, const u8 *key, if (!ctx) return -1; - if (EVP_MAC_init(ctx, key, key_len, params) != 1) + if (EVP_MAC_init(ctx, key, key_len, params) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_MAC_init(hmac,digest=%s,key_len=%zu) failed: %s", + digest, key_len, + ERR_error_string(ERR_get_error(), NULL)); goto fail; + } for (i = 0; i < num_elem; i++) { if (EVP_MAC_update(ctx, addr[i], len[i]) != 1) @@ -1786,6 +1881,32 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, } +#ifdef CONFIG_SHA256 +int pbkdf2_sha256(const char *passphrase, const u8 *salt, size_t salt_len, + int iterations, u8 *buf, size_t buflen) +{ + if (PKCS5_PBKDF2_HMAC(passphrase, os_strlen(passphrase), salt, + salt_len, iterations, EVP_sha256(), buflen, + buf) != 1) + return -1; + return 0; +} +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 +int pbkdf2_sha384(const char *passphrase, const u8 *salt, size_t salt_len, + int iterations, u8 *buf, size_t buflen) +{ + if (PKCS5_PBKDF2_HMAC(passphrase, os_strlen(passphrase), salt, + salt_len, iterations, EVP_sha384(), buflen, + buf) != 1) + return -1; + return 0; +} +#endif /* CONFIG_SHA384 */ + + int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) @@ -1822,8 +1943,12 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, if (!emac || !cipher || !(ctx = EVP_MAC_CTX_new(emac)) || - EVP_MAC_init(ctx, key, key_len, params) != 1) + EVP_MAC_init(ctx, key, key_len, params) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_MAC_init(cmac,cipher=%s) failed: %s", + cipher, ERR_error_string(ERR_get_error(), NULL)); goto fail; + } for (i = 0; i < num_elem; i++) { if (!EVP_MAC_update(ctx, addr[i], len[i])) @@ -2650,8 +2775,12 @@ struct crypto_ecdh * crypto_ecdh_init(int group) goto fail; ecdh->pkey = EVP_EC_gen(name); - if (!ecdh->pkey) + if (!ecdh->pkey) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_EC_gen(group=%d) failed: %s", + group, ERR_error_string(ERR_get_error(), NULL)); goto fail; + } done: return ecdh; @@ -3416,8 +3545,8 @@ struct crypto_ec_key * crypto_ec_key_gen(int group) EVP_PKEY_CTX_set_params(ctx, params) != 1 || EVP_PKEY_generate(ctx, &pkey) != 1) { wpa_printf(MSG_INFO, - "OpenSSL: failed to generate EC keypair: %s", - ERR_error_string(ERR_get_error(), NULL)); + "OpenSSL: Failed to generate EC keypair (group=%d): %s", + group, ERR_error_string(ERR_get_error(), NULL)); pkey = NULL; } @@ -3680,6 +3809,8 @@ struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key, ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER", "type-specific", NULL); if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL: OSSL_ENCODER failed: %s", + ERR_error_string(ERR_get_error(), NULL)); OSSL_ENCODER_CTX_free(ctx); EVP_PKEY_free(copy); return NULL; @@ -4248,7 +4379,7 @@ fail: } -struct crypto_csr * crypto_csr_init() +struct crypto_csr * crypto_csr_init(void) { return (struct crypto_csr *)X509_REQ_new(); } @@ -4271,8 +4402,10 @@ struct crypto_csr * crypto_csr_verify(const struct wpabuf *req) if (X509_REQ_verify((X509_REQ *)csr, pkey) != 1) goto fail; + EVP_PKEY_free(pkey); return (struct crypto_csr *)csr; fail: + EVP_PKEY_free(pkey); X509_REQ_free(csr); return NULL; } @@ -4298,6 +4431,7 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, { X509_NAME *n; int nid; + int ret = -1; switch (type) { case CSR_NAME_CN: @@ -4319,7 +4453,7 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, return -1; } - n = X509_REQ_get_subject_name((X509_REQ *) csr); + n = X509_NAME_new(); if (!n) return -1; @@ -4327,15 +4461,21 @@ int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8, (unsigned char *) name, os_strlen(name), -1, 0)) - return -1; + goto fail; #else if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8, (const unsigned char *) name, os_strlen(name), -1, 0)) - return -1; + goto fail; #endif - return 0; + if (X509_REQ_set_subject_name((X509_REQ *) csr, n) != 1) + goto fail; + + ret = 0; +fail: + X509_NAME_free(n); + return ret; } @@ -4365,8 +4505,8 @@ const u8 * crypto_csr_get_attribute(struct crypto_csr *csr, size_t *len, int *type) { X509_ATTRIBUTE *attrib; - ASN1_TYPE *attrib_type; - ASN1_STRING *data; + const ASN1_TYPE *attrib_type; + const ASN1_STRING *data; int loc; int nid; @@ -4793,8 +4933,12 @@ static int hpke_labeled_extract(struct hpke_context *ctx, bool kem, if (!hctx) return -1; - if (EVP_MAC_init(hctx, salt, salt_len, params) != 1) + if (EVP_MAC_init(hctx, salt, salt_len, params) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto fail; + } if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 || EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 || @@ -4902,8 +5046,12 @@ hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk, if (!hctx) goto fail; - if (EVP_MAC_init(hctx, prk, mdlen, params) != 1) + if (EVP_MAC_init(hctx, prk, mdlen, params) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: EVP_MAC_init(hmac,digest/HPKE) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); goto fail; + } if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1) goto fail; @@ -5581,4 +5729,5 @@ struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id, void crypto_unload(void) { openssl_unload_legacy_provider(); + openssl_unload_default_provider(); } diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c index 269174321bdf..d2541fde5173 100644 --- a/contrib/wpa/src/crypto/crypto_wolfssl.c +++ b/contrib/wpa/src/crypto/crypto_wolfssl.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -514,8 +516,10 @@ int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, { int ret; + PRIVATE_KEY_UNLOCK(); ret = wc_PBKDF2(buf, (const byte *) passphrase, os_strlen(passphrase), ssid, ssid_len, iterations, buflen, WC_SHA); + PRIVATE_KEY_LOCK(); if (ret != 0) { if (ret == HMAC_MIN_KEYLEN_E) { LOG_WOLF_ERROR_VA("wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.", @@ -818,7 +822,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, } break; #endif /* NO_DES3 */ - case CRYPTO_CIPHER_ALG_RC2: case CRYPTO_CIPHER_ALG_DES: default: os_free(ctx); @@ -3412,7 +3415,7 @@ const u8 * crypto_csr_get_attribute(struct crypto_csr *csr, { if (!csr || !len || !type) { LOG_INVALID_PARAMETERS(); - return NULL;; + return NULL; } switch (attr) { @@ -3555,6 +3558,284 @@ fail: #endif /* CONFIG_DPP */ +struct crypto_rsa_key { + RsaKey key; + WC_RNG *rng; +}; + +static struct crypto_rsa_key * crypto_rsa_key_init(void) +{ + struct crypto_rsa_key *ret; + int err; + + ret = os_zalloc(sizeof(*ret)); + if (!ret) + return NULL; + + err = wc_InitRsaKey(&ret->key, NULL); + if (err != MP_OKAY) { + LOG_WOLF_ERROR_FUNC(wc_InitRsaKey, err); + goto fail; + } + + ret->rng = wc_rng_init(); + if (!ret->rng) { + LOG_WOLF_ERROR_FUNC_NULL(wc_rng_init); + goto fail; + } + + err = wc_RsaSetRNG(&ret->key, ret->rng); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaSetRNG, err); + goto fail; + } + + return ret; +fail: + crypto_rsa_key_free(ret); + return NULL; +} + + +void crypto_rsa_key_free(struct crypto_rsa_key *key) +{ + if (key) { + int err; + + err = wc_FreeRsaKey(&key->key); + if (err != 0) + LOG_WOLF_ERROR_FUNC(wc_FreeRsaKey, err); + wc_rng_free(key->rng); + os_free(key); + } +} + + +static void read_rsa_key_from_x509(unsigned char *key_pem, size_t key_pem_len, + DerBuffer **key_der) +{ + struct DecodedCert cert; + DerBuffer *cert_der = NULL; + word32 der_key_sz = 0; + int err; + + err = wc_PemToDer(key_pem, (long) key_pem_len, CERT_TYPE, &cert_der, + NULL, NULL, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + + wc_InitDecodedCert(&cert, cert_der->buffer, cert_der->length, NULL); + err = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + + err = wc_GetPubKeyDerFromCert(&cert, NULL, &der_key_sz); + if (err != LENGTH_ONLY_E) { + LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err); + goto fail; + } + + if (*key_der) + wc_FreeDer(key_der); + *key_der = NULL; + + err = wc_AllocDer(key_der, der_key_sz, PUBLICKEY_TYPE, NULL); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_AllocDer, err); + goto fail; + } + + err = wc_GetPubKeyDerFromCert(&cert, (*key_der)->buffer, + &(*key_der)->length); + if (err != 0) { + LOG_WOLF_ERROR_FUNC(wc_GetPubKeyDerFromCert, err); + goto fail; + } + +fail: + if (cert_der) { + wc_FreeDecodedCert(&cert); + wc_FreeDer(&cert_der); + } + + /* caller is responsible for free'ing key_der */ +} + + +struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key) +{ + struct crypto_rsa_key *ret = NULL; + unsigned char *key_pem = NULL; + size_t key_pem_len = 0; + DerBuffer *key_der = NULL; + int key_format = 0; + int err; + int success = 0; + word32 idx = 0; + + key_pem = (unsigned char *) os_readfile(file, &key_pem_len); + if (!key_pem) { + LOG_WOLF_ERROR_FUNC_NULL(os_readfile); + goto fail; + } + + err = wc_PemToDer(key_pem, (long) key_pem_len, + private_key ? PRIVATEKEY_TYPE : PUBLICKEY_TYPE, + &key_der, NULL, NULL, &key_format); + if (err != 0) { + if (private_key) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + goto fail; + } + + /* Input file might be public key or x509 cert we want to + *extract the key from */ + wpa_printf(MSG_DEBUG, + "wolfSSL: Trying to extract key from x509 cert"); + read_rsa_key_from_x509(key_pem, key_pem_len, &key_der); + if (!key_der) { + LOG_WOLF_ERROR_FUNC(wc_PemToDer, err); + LOG_WOLF_ERROR_FUNC_NULL(read_rsa_key_from_x509); + goto fail; + } + } + + if (private_key && key_format != RSAk) { + LOG_WOLF_ERROR("Private key is not RSA key"); + goto fail; + } + + /* No longer needed so let's free the memory early */ + os_free(key_pem); + key_pem = NULL; + + ret = crypto_rsa_key_init(); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(crypto_rsa_key_init); + goto fail; + } + + if (private_key) + err = wc_RsaPrivateKeyDecode(key_der->buffer, &idx, &ret->key, + key_der->length); + else + err = wc_RsaPublicKeyDecode(key_der->buffer, &idx, &ret->key, + key_der->length); + + if (err != 0) { + if (private_key) + LOG_WOLF_ERROR_FUNC(wc_RsaPrivateKeyDecode, err); + else + LOG_WOLF_ERROR_FUNC(wc_RsaPublicKeyDecode, err); + goto fail; + } + + success = 1; +fail: + os_free(key_pem); + if (key_der) + wc_FreeDer(&key_der); + if (!success && ret) { + crypto_rsa_key_free(ret); + ret = NULL; + } + + return ret; +} + + +struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key, + const struct wpabuf *in) +{ + int err; + int success = 0; + struct wpabuf *ret = NULL; + + if (!key || !in) { + LOG_INVALID_PARAMETERS(); + return NULL; + } + + ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key)); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "wolfSSL: crypto_rsa_oaep_sha256_encrypt: wpabuf_len(in) %ld wc_RsaEncryptSize(key->key) %d", + wpabuf_len(in), wc_RsaEncryptSize(&key->key)); + + err = wc_RsaPublicEncrypt_ex(wpabuf_head_u8(in), wpabuf_len(in), + wpabuf_mhead_u8(ret), wpabuf_size(ret), + &key->key, key->rng, + WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA256, + WC_MGF1SHA256, NULL, 0); + if (err <= 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err); + goto fail; + } + wpabuf_put(ret, err); + + success = 1; +fail: + if (!success && ret) { + wpabuf_free(ret); + ret = NULL; + } + + return ret; +} + + +struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key, + const struct wpabuf *in) +{ + int err; + int success = 0; + struct wpabuf *ret = NULL; + + if (!key || !in) { + LOG_INVALID_PARAMETERS(); + return NULL; + } + + ret = wpabuf_alloc(wc_RsaEncryptSize(&key->key)); + if (!ret) { + LOG_WOLF_ERROR_FUNC_NULL(wpabuf_alloc); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "wolfSSL: crypto_rsa_oaep_sha256_decrypt: wpabuf_len(in) %ld wc_RsaEncryptSize(key->key) %d", + wpabuf_len(in), wc_RsaEncryptSize(&key->key)); + + err = wc_RsaPrivateDecrypt_ex(wpabuf_head_u8(in), wpabuf_len(in), + wpabuf_mhead_u8(ret), wpabuf_size(ret), + &key->key, WC_RSA_OAEP_PAD, + WC_HASH_TYPE_SHA256, WC_MGF1SHA256, + NULL, 0); + if (err <= 0) { + LOG_WOLF_ERROR_FUNC(wc_RsaPublicEncrypt_ex, err); + goto fail; + } + wpabuf_put(ret, err); + + success = 1; +fail: + if (!success && ret) { + wpabuf_free(ret); + ret = NULL; + } + + return ret; +} + + void crypto_unload(void) { } diff --git a/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c index d665a9983cf8..de7394a32a73 100644 --- a/contrib/wpa/src/crypto/sha256-prf.c +++ b/contrib/wpa/src/crypto/sha256-prf.c @@ -97,7 +97,7 @@ int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, * Mask out unused bits in the last octet if it does not use all the * bits. */ - if (buf_len_bits % 8) { + if (pos > 0 && (buf_len_bits % 8)) { u8 mask = 0xff << (8 - buf_len_bits % 8); buf[pos - 1] &= mask; } diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h index 8054bbe5c514..82702ae3e117 100644 --- a/contrib/wpa/src/crypto/sha256.h +++ b/contrib/wpa/src/crypto/sha256.h @@ -26,5 +26,7 @@ int tls_prf_sha256(const u8 *secret, size_t secret_len, int hmac_sha256_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int pbkdf2_sha256(const char *passphrase, const u8 *salt, size_t salt_len, + int iterations, u8 *buf, size_t buflen); #endif /* SHA256_H */ diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c index 420e78c380cd..fdf33165506b 100644 --- a/contrib/wpa/src/crypto/sha384-prf.c +++ b/contrib/wpa/src/crypto/sha384-prf.c @@ -97,7 +97,7 @@ int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, * Mask out unused bits in the last octet if it does not use all the * bits. */ - if (buf_len_bits % 8) { + if (pos > 0 && (buf_len_bits % 8)) { u8 mask = 0xff << (8 - buf_len_bits % 8); buf[pos - 1] &= mask; } diff --git a/contrib/wpa/src/crypto/sha384.h b/contrib/wpa/src/crypto/sha384.h index d946907c675d..276cdd3770f4 100644 --- a/contrib/wpa/src/crypto/sha384.h +++ b/contrib/wpa/src/crypto/sha384.h @@ -26,5 +26,7 @@ int tls_prf_sha384(const u8 *secret, size_t secret_len, int hmac_sha384_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int pbkdf2_sha384(const char *passphrase, const u8 *salt, size_t salt_len, + int iterations, u8 *buf, size_t buflen); #endif /* SHA384_H */ diff --git a/contrib/wpa/src/crypto/sha512-prf.c b/contrib/wpa/src/crypto/sha512-prf.c index e48cf5f05662..be458141dc54 100644 --- a/contrib/wpa/src/crypto/sha512-prf.c +++ b/contrib/wpa/src/crypto/sha512-prf.c @@ -97,7 +97,7 @@ int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, * Mask out unused bits in the last octet if it does not use all the * bits. */ - if (buf_len_bits % 8) { + if (pos > 0 && (buf_len_bits % 8)) { u8 mask = 0xff << (8 - buf_len_bits % 8); buf[pos - 1] &= mask; } diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index e3f5b5a4227c..8ce939032e4a 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -62,6 +62,8 @@ struct tls_connection { char *suffix_match; char *domain_match; unsigned int flags; + + char *prio_str; }; @@ -213,7 +215,9 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; - ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", + ret = gnutls_priority_set_direct(conn->session, + conn->prio_str ? conn->prio_str : + "NORMAL:-VERS-SSL3.0", &err); if (ret < 0) { wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " @@ -285,6 +289,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) wpabuf_free(conn->pull_buf); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->prio_str); os_free(conn); } @@ -410,15 +415,18 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1 | - TLS_CONN_DISABLE_TLSv1_2)) { + TLS_CONN_DISABLE_TLSv1_2 | + TLS_CONN_DISABLE_TLSv1_3)) { os_snprintf(prio_buf, sizeof(prio_buf), - "NORMAL:-VERS-SSL3.0%s%s%s", + "NORMAL:-VERS-SSL3.0%s%s%s%s", params->flags & TLS_CONN_DISABLE_TLSv1_0 ? ":-VERS-TLS1.0" : "", params->flags & TLS_CONN_DISABLE_TLSv1_1 ? ":-VERS-TLS1.1" : "", params->flags & TLS_CONN_DISABLE_TLSv1_2 ? - ":-VERS-TLS1.2" : ""); + ":-VERS-TLS1.2" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_3 ? + ":-VERS-TLS1.3" : ""); prio = prio_buf; } @@ -459,6 +467,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, err); return -1; } + os_free(conn->prio_str); + conn->prio_str = os_strdup(prio); } if (params->openssl_ecdh_curves) { @@ -1513,7 +1523,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, conn->global->event_cb(conn->global->cb_ctx, TLS_ALERT, &ev); } - /* continue */ + /* fallthrough */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " "-> %s", __func__, gnutls_strerror(ret)); diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index 17283f99817a..b14b1236031f 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -7,6 +7,10 @@ */ #include "includes.h" +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH +#include +#include +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ #ifdef CONFIG_TESTING_OPTIONS #include #endif /* CONFIG_TESTING_OPTIONS */ @@ -33,6 +37,8 @@ #include #include #include +#include +#include #else /* OpenSSL version >= 3.0 */ #ifndef OPENSSL_NO_DSA #include @@ -244,8 +250,8 @@ struct tls_connection { BIO *ssl_in, *ssl_out; #if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) ENGINE *engine; /* functional reference to the engine */ - EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ + EVP_PKEY *private_key; /* the private key if using engine/provider */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; char *check_cert_subject; int read_alerts, write_alerts, failed; @@ -357,6 +363,161 @@ static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) } +#ifndef ANDROID +#ifdef OPENSSL_NO_ENGINE + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static OSSL_PROVIDER *openssl_pkcs11_provider = NULL; +#endif /* OpenSSL version >= 3.0 */ + +static void openssl_load_pkcs11_provider(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (openssl_pkcs11_provider) + return; + + openssl_pkcs11_provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1); + if (!openssl_pkcs11_provider) + wpa_printf(MSG_WARNING, "PKCS11 provider not present"); +#endif /* OpenSSL version >= 3.0 */ +} + + +static void openssl_unload_pkcs11_provider(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (openssl_pkcs11_provider) { + OSSL_PROVIDER_unload(openssl_pkcs11_provider); + openssl_pkcs11_provider = NULL; + } +#endif /* OpenSSL version >= 3.0 */ +} + + +static bool openssl_can_use_provider(const char *engine_id, const char *req) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (!os_strcmp(engine_id, "pkcs11") && openssl_pkcs11_provider) + return true; + + wpa_printf(MSG_ERROR, + "Cannot find OpenSSL provider for '%s' (missing '%s')", + req, engine_id); +#endif /* OpenSSL version >= 3.0 */ + return false; +} + + +static EVP_PKEY * provider_load_key(const char *uri) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + EVP_PKEY *key = NULL; + + if (!uri) { + tls_show_errors(MSG_ERROR, __func__, + "Invalid NULL uri for key"); + goto err_key; + } + + store = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!store) { + wpa_printf(MSG_DEBUG, "Bad uri for private key:%s", uri); + + tls_show_errors(MSG_ERROR, __func__, + "Failed to open key store"); + goto err_key; + } + + if (os_strncmp(uri, "pkcs11:", 7) && + os_strstr(uri, "type=private") == NULL) { + /* This is a workaround for OpenSSL < 3.2.0 where the code fails + * to correctly source public keys unless explicitly requested + * via an expect hint. */ + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PUBKEY) != 1) { + tls_show_errors(MSG_ERROR, __func__, + "Failed to expect Public Key File"); + goto err_store; + } + } + + while (!OSSL_STORE_eof(store)) { + info = OSSL_STORE_load(store); + if (!info) { + if (OSSL_STORE_error(store)) + break; + continue; + } + if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_PKEY) + key = OSSL_STORE_INFO_get1_PKEY(info); + + OSSL_STORE_INFO_free(info); + if (key) + break; + } + +err_store: + OSSL_STORE_close(store); +err_key: + if (!key) + wpa_printf(MSG_ERROR, "OpenSSL: Failed to load key from URI"); + + return key; +#else /* OpenSSL version >= 3.0 */ + return NULL; +#endif /* OpenSSL version >= 3.0 */ +} + + +static X509 * provider_load_cert(const char *cert_id) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + X509 *cert = NULL; + + if (!cert_id) { + tls_show_errors(MSG_ERROR, __func__, "Invalid NULL uri"); + goto err_cert; + } + + store = OSSL_STORE_open(cert_id, NULL, NULL, NULL, NULL); + if (!store) { + tls_show_errors(MSG_ERROR, __func__, "Failed to open store"); + goto err_cert; + } + + while (!OSSL_STORE_eof(store)) { + info = OSSL_STORE_load(store); + if (!info) { + if (OSSL_STORE_error(store)) + break; + continue; + } + if ((OSSL_STORE_INFO_get_type(info)) == OSSL_STORE_INFO_CERT) + cert = OSSL_STORE_INFO_get1_CERT(info); + + OSSL_STORE_INFO_free(info); + if (cert) + break; + } + OSSL_STORE_close(store); + +err_cert: + if (!cert) + tls_show_errors(MSG_ERROR, __func__, + "Failed to load cert from URI"); + return cert; +#else /* OpenSSL version >= 3.0 */ + return NULL; +#endif /* OpenSSL version >= 3.0 */ +} + +#endif /* OPENSSL_NO_ENGINE */ +#endif /* !ANDROID */ + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -871,6 +1032,79 @@ static int tls_engine_load_dynamic_generic(const char *pre[], } +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + +#define TRUSTED_PATH "/usr/lib/" + +/** + * tls_engine_path_trusted - Verify engine .so path is trusted for loading + * @path: Engine/module path supplied via configuration or D-Bus + * @real_path: Buffer for the canonical path (PATH_MAX bytes) + * Returns: 0 if trusted (real_path filled), -1 otherwise + * + * The PKCS#11/OpenSC engine and module shared object paths are loaded with + * dlopen() within this process, which may be running with elevated + * privileges. Verify that the supplied path cannot be used to load an + * attacker-controlled shared library by requiring, in addition to being + * located under the trusted path, that the target is a regular file owned by + * root, not writable by group or others, and that every ancestor directory up + * to the file is itself a directory, owned by root and not writable by group + * or others (otherwise a root-owned file could be swapped by whoever controls + * such a directory). lstat() is used for the file check so that a symlink is + * never followed, even though realpath() has already canonicalised the path. + */ +static int tls_engine_path_trusted(const char *path, char *real_path) +{ + struct stat st; + char dir[PATH_MAX]; + char *slash, *next; + + if (!path) + return -1; + + if (!realpath(path, real_path)) { + wpa_printf(MSG_INFO, + "ENGINE: Refusing to load %s: realpath: %s", + path, strerror(errno)); + return -1; + } + + if (os_strncmp(TRUSTED_PATH, real_path, os_strlen(TRUSTED_PATH)) != 0) { + wpa_printf(MSG_INFO, + "ENGINE: Refusing to load %s: not in trusted path %s", + real_path, TRUSTED_PATH); + return -1; + } + + if (lstat(real_path, &st) != 0 || !S_ISREG(st.st_mode) || + st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH))) { + wpa_printf(MSG_INFO, + "ENGINE: Refusing to load %s: not a root-owned, non-writable regular file", + real_path); + return -1; + } + + os_strlcpy(dir, real_path, sizeof(dir)); + slash = dir; + while ((next = os_strchr(slash + 1, '/')) { + *next = '\0'; + if (stat(dir, &st) != 0 || !S_ISDIR(st.st_mode) || + st.st_uid != 0 || (st.st_mode & (S_IWGRP | S_IWOTH))) { + wpa_printf(MSG_INFO, + "ENGINE: Refusing to load %s: insecure ancestor directory %s", + real_path, dir); + return -1; + } + *next = '/'; + slash = next; + } + + return 0; +} + +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ + + /** * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc * @pkcs11_so_path: pksc11_so_path from the configuration @@ -880,6 +1114,10 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, const char *pkcs11_module_path) { char *engine_id = "pkcs11"; +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + char real_pkcs11_so_path[PATH_MAX]; + char real_pkcs11_module_path[PATH_MAX]; +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ const char *pre_cmd[] = { "SO_PATH", NULL /* pkcs11_so_path */, "ID", NULL /* engine_id */, @@ -896,12 +1134,29 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, if (!pkcs11_so_path) return 0; +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + if (tls_engine_path_trusted(pkcs11_so_path, real_pkcs11_so_path) < 0) + return -1; + pre_cmd[1] = real_pkcs11_so_path; +#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ pre_cmd[1] = pkcs11_so_path; +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ pre_cmd[3] = engine_id; +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + if (pkcs11_module_path) { + if (tls_engine_path_trusted(pkcs11_module_path, + real_pkcs11_module_path) < 0) + return -1; + post_cmd[1] = real_pkcs11_module_path; + } else { + post_cmd[0] = NULL; + } +#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ if (pkcs11_module_path) post_cmd[1] = pkcs11_module_path; else post_cmd[0] = NULL; +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); @@ -917,6 +1172,9 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) { char *engine_id = "opensc"; +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + char real_opensc_so_path[PATH_MAX]; +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ const char *pre_cmd[] = { "SO_PATH", NULL /* opensc_so_path */, "ID", NULL /* engine_id */, @@ -928,7 +1186,13 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) if (!opensc_so_path) return 0; +#ifdef CONFIG_TLS_ENGINE_TRUSTED_PATH + if (tls_engine_path_trusted(opensc_so_path, real_opensc_so_path) < 0) + return -1; + pre_cmd[1] = real_opensc_so_path; +#else /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ pre_cmd[1] = opensc_so_path; +#endif /* CONFIG_TLS_ENGINE_TRUSTED_PATH */ pre_cmd[3] = engine_id; wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", @@ -1020,6 +1284,9 @@ void * tls_init(const struct tls_config *conf) void openssl_load_legacy_provider(void); openssl_load_legacy_provider(); +#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE) + openssl_load_pkcs11_provider(); +#endif /* !ANDROID && OPENSSL_NO_ENGINE */ tls_global = context = tls_context_new(conf); if (context == NULL) @@ -1088,6 +1355,7 @@ void * tls_init(const struct tls_config *conf) else ssl = NULL; if (ssl == NULL) { + tls_show_errors(MSG_INFO, "SSL_CTX_new", "init"); tls_openssl_ref_count--; if (context != tls_global) os_free(context); @@ -1192,7 +1460,13 @@ void tls_deinit(void *ssl_ctx) if (data->tls_session_lifetime > 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions"); +#if OPENSSL_VERSION_NUMBER >= 0x30400000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + SSL_CTX_flush_sessions_ex(ssl, 0); +#else /* OpenSSL version >= 3.4 */ SSL_CTX_flush_sessions(ssl, 0); +#endif /* OpenSSL version >= 3.4 */ wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions - done"); } while ((sess_data = dl_list_first(&context->sessions, @@ -1211,6 +1485,9 @@ void tls_deinit(void *ssl_ctx) tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { +#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE) + openssl_unload_pkcs11_provider(); +#endif /* !ANDROID && OPENSSL_NO_ENGINE */ #if OPENSSL_VERSION_NUMBER < 0x10100000L #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); @@ -1369,6 +1646,12 @@ err: return ret; #else /* OPENSSL_NO_ENGINE */ +#ifndef ANDROID + conn->private_key = provider_load_key(key_id); + if (!conn->private_key) + return -1; +#endif /* !ANDROID */ + return 0; #endif /* OPENSSL_NO_ENGINE */ } @@ -1376,12 +1659,12 @@ err: static void tls_engine_deinit(struct tls_connection *conn) { -#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) - wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) + wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->engine) { #if !defined(OPENSSL_IS_BORINGSSL) ENGINE_finish(conn->engine); @@ -1854,8 +2137,9 @@ static int tls_match_altsubject_component(X509 *cert, int type, gen = sk_GENERAL_NAME_value(ext, i); if (gen->type != type) continue; - if (os_strlen((char *) gen->d.ia5->data) == len && - os_memcmp(value, gen->d.ia5->data, len) == 0) + if ((size_t) ASN1_STRING_length(gen->d.ia5) == len && + os_memcmp(value, ASN1_STRING_get0_data(gen->d.ia5), len) == + 0) found++; } @@ -1901,6 +2185,8 @@ static int tls_match_altsubject(X509 *cert, const char *match) len = os_strlen(pos); if (tls_match_altsubject_component(cert, type, pos, len) > 0) return 1; + if (!end) + break; pos = end + 1; } while (end); @@ -1994,7 +2280,7 @@ static int match_dn_field(const X509 *cert, int nid, const char *field, const struct tls_dn_field_order_cnt *dn_cnt) { int i, ret = 0, len, config_dn_field_index, match_index = 0; - X509_NAME *name; + const X509_NAME *name; len = os_strlen(value); name = X509_get_subject_name((X509 *) cert); @@ -2006,9 +2292,10 @@ static int match_dn_field(const X509 *cert, int nid, const char *field, return 0; /* Fetch value based on NID */ - for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { - X509_NAME_ENTRY *e; - ASN1_STRING *cn; + for (i = -1; (i = X509_NAME_get_index_by_NID((X509_NAME *) name, nid, + i)) > -1;) { + const X509_NAME_ENTRY *e; + const ASN1_STRING *cn; e = X509_NAME_get_entry(name, i); if (!e) @@ -2163,7 +2450,7 @@ static int tls_match_suffix_helper(X509 *cert, const char *match, int i; stack_index_t j; int dns_name = 0; - X509_NAME *name; + const X509_NAME *name; wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", full ? "": "suffix ", match); @@ -2176,10 +2463,10 @@ static int tls_match_suffix_helper(X509 *cert, const char *match, continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", - gen->d.dNSName->data, - gen->d.dNSName->length); - if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, + ASN1_STRING_get0_data(gen->d.dNSName), + ASN1_STRING_length(gen->d.dNSName)); + if (domain_suffix_match(ASN1_STRING_get0_data(gen->d.dNSName), + ASN1_STRING_length(gen->d.dNSName), match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); @@ -2197,10 +2484,11 @@ static int tls_match_suffix_helper(X509 *cert, const char *match, name = X509_get_subject_name(cert); i = -1; for (;;) { - X509_NAME_ENTRY *e; - ASN1_STRING *cn; + const X509_NAME_ENTRY *e; + const ASN1_STRING *cn; - i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + i = X509_NAME_get_index_by_NID((X509_NAME *) name, + NID_commonName, i); if (i == -1) break; e = X509_NAME_get_entry(name, i); @@ -2210,8 +2498,10 @@ static int tls_match_suffix_helper(X509 *cert, const char *match, if (cn == NULL) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", - cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, + ASN1_STRING_get0_data(cn), + ASN1_STRING_length(cn)); + if (domain_suffix_match(ASN1_STRING_get0_data(cn), + ASN1_STRING_length(cn), match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); @@ -2420,7 +2710,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, gen->type != GEN_URI) continue; - pos = os_malloc(10 + gen->d.ia5->length + 1); + pos = os_malloc(10 + ASN1_STRING_length(gen->d.ia5) + 1); if (pos == NULL) break; altsubject[num_altsubject++] = pos; @@ -2440,8 +2730,9 @@ static void openssl_tls_cert_event(struct tls_connection *conn, break; } - os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length); - pos += gen->d.ia5->length; + os_memcpy(pos, ASN1_STRING_get0_data(gen->d.ia5), + ASN1_STRING_length(gen->d.ia5)); + pos += ASN1_STRING_length(gen->d.ia5); *pos = '\0'; } sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); @@ -2532,7 +2823,27 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) suffix_match = conn->suffix_match; domain_match = conn->domain_match; - if (!preverify_ok && !conn->ca_cert_verify) + if (!conn->ca_cert_verify && depth == 0 && + !(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) { + if (X509_cmp_current_time(X509_get_notBefore(err_cert)) > 0) { + wpa_printf(MSG_INFO, + "OpenSSL: Server certificate is not valid at the current time"); + err = X509_V_ERR_CERT_NOT_YET_VALID; + X509_STORE_CTX_set_error(x509_ctx, err); + preverify_ok = 0; + } else if (X509_cmp_current_time(X509_get_notAfter(err_cert)) < + 0) { + wpa_printf(MSG_INFO, + "TLS: Server certificate has expired"); + err = X509_V_ERR_CERT_HAS_EXPIRED; + X509_STORE_CTX_set_error(x509_ctx, err); + preverify_ok = 0; + } + } + + if (!preverify_ok && !conn->ca_cert_verify && + !(err == X509_V_ERR_CERT_HAS_EXPIRED || + err == X509_V_ERR_CERT_NOT_YET_VALID)) preverify_ok = 1; if (!preverify_ok && depth > 0 && conn->server_cert_only) preverify_ok = 1; @@ -2576,7 +2887,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) err_str = "Server certificate mismatch"; err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; preverify_ok = 0; - } else if (!preverify_ok) { + } else if (!preverify_ok && + err != X509_V_ERR_CERT_HAS_EXPIRED && + err != X509_V_ERR_CERT_NOT_YET_VALID) { /* * Certificate matches pinned certificate, allow * regardless of other problems. @@ -2778,6 +3091,31 @@ static int tls_load_ca_der(struct tls_data *data, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ +static int tls_add_ca_cert(SSL_CTX *ssl_ctx, X509 *cert) +{ + unsigned long err; + + if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), cert) == 1) + return 0; + + err = ERR_peek_error(); + + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + ERR_get_error(); + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - ignoring cert already in hash table error", + __func__); + return 0; + } + + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to certificate store"); + + return -1; +} + + static int tls_connection_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, @@ -2840,49 +3178,69 @@ static int tls_connection_ca_cert(struct tls_data *data, } if (ca_cert_blob) { - X509 *cert = d2i_X509(NULL, - (const unsigned char **) &ca_cert_blob, - ca_cert_blob_len); - if (cert == NULL) { - BIO *bio = BIO_new_mem_buf(ca_cert_blob, - ca_cert_blob_len); + unsigned long err; + X509 *cert; + int count; + BIO *bio; - if (bio) { - cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); - } - - if (!cert) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to parse ca_cert_blob"); - return -1; - } - - while (ERR_get_error()) { - /* Ignore errors from DER conversion. */ - } - } - - if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), - cert)) { - unsigned long err = ERR_peek_error(); - tls_show_errors(MSG_WARNING, __func__, - "Failed to add ca_cert_blob to " - "certificate store"); - if (ERR_GET_LIB(err) == ERR_LIB_X509 && - ERR_GET_REASON(err) == - X509_R_CERT_ALREADY_IN_HASH_TABLE) { - wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " - "cert already in hash table error", - __func__); - } else { + cert = d2i_X509(NULL, + (const unsigned char **) &ca_cert_blob, + ca_cert_blob_len); + if (cert) { + if (tls_add_ca_cert(ssl_ctx, cert) < 0) { X509_free(cert); return -1; } + + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - added ca_cert_blob to certificate store", + __func__); + X509_free(cert); + return 0; } - X509_free(cert); - wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " - "to certificate store", __func__); + + count = 0; + bio = BIO_new_mem_buf(ca_cert_blob, ca_cert_blob_len); + if (bio) { + while ((cert = PEM_read_bio_X509(bio, NULL, NULL, + NULL))) { + if (count == 0) { + /* Ignore errors from DER conversion + * if we detect a certificate in PEM + * format. */ + ERR_clear_error(); + } + count++; + if (tls_add_ca_cert(ssl_ctx, cert) < 0) { + X509_free(cert); + BIO_free(bio); + return -1; + } + X509_free(cert); + } + BIO_free(bio); + } + + if (count == 0) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + /* When the loop ends successfully, it's because of EOF */ + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) != ERR_LIB_PEM || + ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob bundle"); + return -1; + } + + ERR_clear_error(); + + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - added %d ca_cert_blob certificates to certificate store", + __func__, count); return 0; } @@ -3799,11 +4157,17 @@ static int tls_engine_get_cert(struct tls_connection *conn, static int tls_connection_engine_client_cert(struct tls_connection *conn, const char *cert_id) { -#ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID X509 *cert; +#ifndef OPENSSL_NO_ENGINE if (tls_engine_get_cert(conn, cert_id, &cert)) return -1; +#else /* OPENSSL_NO_ENGINE */ + cert = provider_load_cert(cert_id); + if (!cert) + return -1; +#endif /* OPENSSL_NO_ENGINE */ if (!SSL_use_certificate(conn->ssl, cert)) { tls_show_errors(MSG_ERROR, __func__, @@ -3812,13 +4176,12 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn, return -1; } X509_free(cert); - wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> " + wpa_printf(MSG_DEBUG, "ENGINE/provider: SSL_use_certificate --> " "OK"); return 0; - -#else /* OPENSSL_NO_ENGINE */ +#else /* ANDROID */ return -1; -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID */ } @@ -3826,13 +4189,19 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert_id) { -#ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID X509 *cert; SSL_CTX *ssl_ctx = data->ssl; X509_STORE *store; +#ifndef OPENSSL_NO_ENGINE if (tls_engine_get_cert(conn, ca_cert_id, &cert)) return -1; +#else /* OPENSSL_NO_ENGINE */ + cert = provider_load_cert(ca_cert_id); + if (!cert) + return -1; +#endif /* OPENSSL_NO_ENGINE */ /* start off the same as tls_connection_ca_cert */ store = X509_STORE_new(); @@ -3846,7 +4215,7 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, if (!X509_STORE_add_cert(store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, - "Failed to add CA certificate from engine " + "Failed to add CA certificate from engine/provider " "to certificate store"); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { @@ -3859,25 +4228,24 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, } } X509_free(cert); - wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " - "to certificate store", __func__); + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - added CA certificate from engine/provider to certificate store", + __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; return 0; - -#else /* OPENSSL_NO_ENGINE */ +#else /* ANDROID */ return -1; -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID */ } static int tls_connection_engine_private_key(struct tls_connection *conn) { -#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, - "ENGINE: cannot use private key for TLS"); + "ENGINE/provider: cannot use private key for TLS"); return -1; } if (!SSL_check_private_key(conn->ssl)) { @@ -3886,11 +4254,6 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) return -1; } return 0; -#else /* OPENSSL_NO_ENGINE */ - wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " - "engine support was not compiled in"); - return -1; -#endif /* OPENSSL_NO_ENGINE */ } @@ -5437,6 +5800,10 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && ca_cert_id) { +#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE) + if (!openssl_can_use_provider(engine_id, ca_cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; +#endif /* !ANDROID && OPENSSL_NO_ENGINE */ if (tls_connection_engine_ca_cert(data, conn, ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_ca_cert(data, conn, params->ca_cert, @@ -5446,6 +5813,10 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && cert_id) { +#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE) + if (!openssl_can_use_provider(engine_id, cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; +#endif /* !ANDROID && OPENSSL_NO_ENGINE */ if (tls_connection_engine_client_cert(conn, cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_client_cert(conn, params->client_cert, @@ -5454,7 +5825,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; if (engine_id && key_id) { - wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); +#if !defined(ANDROID) && defined(OPENSSL_NO_ENGINE) + if (!openssl_can_use_provider(engine_id, key_id)) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; +#endif /* !ANDROID && OPENSSL_NO_ENGINE */ + wpa_printf(MSG_DEBUG, + "TLS: Using private key from engine/provider"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_private_key(data, conn, @@ -5778,7 +6154,9 @@ int tls_global_set_params(void *tls_ctx, * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ -#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) +#if (defined(OPENSSL_IS_BORINGSSL) || \ + (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || \ + LIBRESSL_VERSION_NUMBER >= 0x4020000fL) static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, const SSL_CIPHER **cipher, void *arg) diff --git a/contrib/wpa/src/crypto/tls_wolfssl.c b/contrib/wpa/src/crypto/tls_wolfssl.c index 0b2947daf9b7..87abbde90317 100644 --- a/contrib/wpa/src/crypto/tls_wolfssl.c +++ b/contrib/wpa/src/crypto/tls_wolfssl.c @@ -42,7 +42,9 @@ static int tls_ref_count = 0; -static int tls_ex_idx_session = 0; +#define TLS_SESSION_EX_IDX (0) +#define TLS_SSL_CTX_CTX_EX_IDX (0) +#define TLS_SSL_CON_EX_IDX (0) /* tls input data for wolfSSL Read Callback */ @@ -63,13 +65,15 @@ struct tls_context { int cert_in_cb; char *ocsp_stapling_response; unsigned int tls_session_lifetime; + /* This is alloc'ed and needs to be free'd */ + char *check_cert_subject; }; static struct tls_context *tls_global = NULL; /* wolfssl tls_connection */ struct tls_connection { - struct tls_context *context; + const struct tls_context *context; WOLFSSL *ssl; int read_alerts; int write_alerts; @@ -80,6 +84,7 @@ struct tls_connection { char *alt_subject_match; char *suffix_match; char *domain_match; + char *check_cert_subject; u8 srv_cert_hash[32]; @@ -120,6 +125,22 @@ static struct tls_context * tls_context_new(const struct tls_config *conf) } +static void tls_context_free(struct tls_context *context) +{ + if (context) { + os_free(context->check_cert_subject); + os_free(context); + } +} + + +/* Helper to make sure the context stays const */ +static const struct tls_context * ssl_ctx_get_tls_context(void *ssl_ctx) +{ + return wolfSSL_CTX_get_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX); +} + + static void wolfssl_reset_in_data(struct tls_in_data *in, const struct wpabuf *buf) { @@ -145,6 +166,9 @@ static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) if (!data) return -1; + if (!data->in_data) + return -2; /* WANT_READ */ + if (get > (wpabuf_len(data->in_data) - data->consumed)) get = wpabuf_len(data->in_data) - data->consumed; @@ -184,7 +208,7 @@ static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) { struct wpabuf *buf; - buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + buf = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); if (!buf) return; wpa_printf(MSG_DEBUG, @@ -192,7 +216,7 @@ static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) buf, sess); wpabuf_free(buf); - wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); + wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, NULL); } @@ -223,11 +247,158 @@ static void wolfSSL_logging_cb(const int log_level, #endif /* DEBUG_WOLFSSL */ +#define SUITEB_OLDTLS_192_CIPHERS "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384" +#define SUITEB_TLS13_192_CIPHERS "TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256" +#define SUITEB_TLS_192_CIPHERS SUITEB_TLS13_192_CIPHERS ":" SUITEB_OLDTLS_192_CIPHERS + +#define SUITEB_OLDTLS_128_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:" SUITEB_OLDTLS_192_CIPHERS +#define SUITEB_TLS13_128_CIPHERS "TLS13-AES128-GCM-SHA256:" SUITEB_TLS13_192_CIPHERS +#define SUITEB_TLS_128_CIPHERS SUITEB_TLS13_128_CIPHERS ":" SUITEB_OLDTLS_128_CIPHERS + +#define SUITEB_TLS_192_SIGALGS "ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384" +#define SUITEB_TLS_128_SIGALGS "ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:" SUITEB_TLS_192_SIGALGS + +#define SUITEB_TLS_192_CURVES "P-384:P-521" +#define SUITEB_TLS_128_CURVES "P-256:" SUITEB_TLS_192_CURVES + +#define SUITEB_TLS_128_RSA_KEY_SZ 2048 +#define SUITEB_TLS_192_RSA_KEY_SZ 3072 + +#define SUITEB_TLS_128_ECC_KEY_SZ 256 +#define SUITEB_TLS_192_ECC_KEY_SZ 384 + +static int handle_ciphersuites(WOLFSSL_CTX *ssl_ctx, WOLFSSL *ssl, + const char *openssl_ciphers, unsigned int flags) +{ + const char *ciphers = "DEFAULT:!aNULL"; + const char *sigalgs = NULL; + const char *curves = NULL; + bool tls13 = !(flags & TLS_CONN_DISABLE_TLSv1_3); + unsigned int tls13_only_mask = TLS_CONN_DISABLE_TLSv1_2 | + TLS_CONN_DISABLE_TLSv1_1 | TLS_CONN_DISABLE_TLSv1_0; + bool old_tls_only = ((flags & tls13_only_mask) != tls13_only_mask) && + !tls13; + bool tls13only = ((flags & tls13_only_mask) == tls13_only_mask) && + !(flags & TLS_CONN_DISABLE_TLSv1_3); + short key_sz = 0; + short ecc_key_sz = 0; + + if (openssl_ciphers) { + if (os_strcmp(openssl_ciphers, "SUITEB128") == 0) { + if (tls13only) + ciphers = SUITEB_TLS13_128_CIPHERS; + else if (old_tls_only) + ciphers = SUITEB_OLDTLS_128_CIPHERS; + else + ciphers = SUITEB_TLS_128_CIPHERS; + sigalgs = SUITEB_TLS_128_SIGALGS; + key_sz = SUITEB_TLS_128_RSA_KEY_SZ; + ecc_key_sz = SUITEB_TLS_128_ECC_KEY_SZ; + curves = SUITEB_TLS_128_CURVES; + } else if (os_strcmp(openssl_ciphers, "SUITEB192") == 0) { + if (tls13only) + ciphers = SUITEB_TLS13_192_CIPHERS; + else if (old_tls_only) + ciphers = SUITEB_OLDTLS_192_CIPHERS; + else + ciphers = SUITEB_TLS_192_CIPHERS; + sigalgs = SUITEB_TLS_192_SIGALGS; + key_sz = SUITEB_TLS_192_RSA_KEY_SZ; + ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ; + curves = SUITEB_TLS_192_CURVES; + } else { + ciphers = openssl_ciphers; + } + } else if (flags & TLS_CONN_SUITEB) { + if (tls13only) + ciphers = SUITEB_TLS13_192_CIPHERS; + else if (old_tls_only) + ciphers = SUITEB_OLDTLS_192_CIPHERS; + else + ciphers = SUITEB_TLS_192_CIPHERS; + sigalgs = SUITEB_TLS_192_SIGALGS; + key_sz = SUITEB_TLS_192_RSA_KEY_SZ; + ecc_key_sz = SUITEB_TLS_192_ECC_KEY_SZ; + curves = SUITEB_TLS_192_CURVES; + } + + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites for %s", + ssl_ctx ? "ctx" : "ssl"); + wpa_printf(MSG_DEBUG, "wolfSSL: openssl_ciphers: %s", + openssl_ciphers ? openssl_ciphers : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", + ciphers ? ciphers : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: sigalgs: %s", + sigalgs ? sigalgs : "N/A"); + wpa_printf(MSG_DEBUG, "wolfSSL: key size: %d", key_sz); + + if (ciphers) { + if ((ssl_ctx && + wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) || + (ssl && wolfSSL_set_cipher_list(ssl, ciphers) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set cipher string '%s'", + ciphers); + return -1; + } + } + + if (sigalgs) { + if ((ssl_ctx && + wolfSSL_CTX_set1_sigalgs_list(ssl_ctx, sigalgs) != 1) || + (ssl && wolfSSL_set1_sigalgs_list(ssl, sigalgs) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set sigalgs '%s'", + sigalgs); + return -1; + } + } + + if (key_sz) { + if ((ssl_ctx && + wolfSSL_CTX_SetMinRsaKey_Sz(ssl_ctx, key_sz) != 1) || + (ssl && wolfSSL_SetMinRsaKey_Sz(ssl, key_sz) != 1) || + (ssl_ctx && + wolfSSL_CTX_SetMinDhKey_Sz(ssl_ctx, key_sz) != 1) || + (ssl && wolfSSL_SetMinDhKey_Sz(ssl, key_sz) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set min key size"); + return -1; + } + } + + if (ecc_key_sz) { + if ((ssl_ctx && + wolfSSL_CTX_SetMinEccKey_Sz(ssl_ctx, ecc_key_sz) != 1) || + (ssl && wolfSSL_SetMinEccKey_Sz(ssl, ecc_key_sz) != 1) || + (ssl_ctx && + wolfSSL_CTX_SetTmpEC_DHE_Sz(ssl_ctx, + ecc_key_sz / 8) != 1) || + (ssl && + wolfSSL_SetTmpEC_DHE_Sz(ssl, ecc_key_sz / 8) != 1)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set min ecc key size"); + return -1; + } + } + + if (curves) { + if ((ssl_ctx && + wolfSSL_CTX_set1_curves_list(ssl_ctx, curves) != 1) || + (ssl && wolfSSL_set1_curves_list(ssl, curves) != 1)) { + wpa_printf(MSG_ERROR, "wolfSSL: Failed to set curves"); + return -1; + } + } + + return 0; +} + + void * tls_init(const struct tls_config *conf) { WOLFSSL_CTX *ssl_ctx; struct tls_context *context; - const char *ciphers; #ifdef DEBUG_WOLFSSL wolfSSL_SetLoggingCb(wolfSSL_logging_cb); @@ -255,16 +426,17 @@ void * tls_init(const struct tls_config *conf) if (!ssl_ctx) { tls_ref_count--; if (context != tls_global) - os_free(context); + tls_context_free(context); if (tls_ref_count == 0) { - os_free(tls_global); + tls_context_free(tls_global); tls_global = NULL; } + return NULL; } wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); context->tls_session_lifetime = conf->tls_session_lifetime; - wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context); + wolfSSL_CTX_set_ex_data(ssl_ctx, TLS_SSL_CTX_CTX_EX_IDX, context); if (conf->tls_session_lifetime > 0) { wolfSSL_CTX_set_session_id_context(ssl_ctx, @@ -280,36 +452,33 @@ void * tls_init(const struct tls_config *conf) WOLFSSL_SESS_CACHE_OFF); } - if (conf && conf->openssl_ciphers) - ciphers = conf->openssl_ciphers; - else - ciphers = "ALL"; - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", ciphers); - if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { - wpa_printf(MSG_ERROR, - "wolfSSL: Failed to set cipher string '%s'", - ciphers); + if (handle_ciphersuites(ssl_ctx, NULL, conf->openssl_ciphers, + conf ? conf->tls_flags : 0) != 0) { + wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites"); tls_deinit(ssl_ctx); return NULL; } + return ssl_ctx; } void tls_deinit(void *ssl_ctx) { - struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + struct tls_context *context; + /* Need to cast the const away to be able to free this */ + context = (struct tls_context *) ssl_ctx_get_tls_context(ssl_ctx); if (context != tls_global) - os_free(context); + tls_context_free(context); wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx); tls_ref_count--; if (tls_ref_count == 0) { wolfSSL_Cleanup(); - os_free(tls_global); + tls_context_free(tls_global); tls_global = NULL; } } @@ -351,8 +520,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); - wolfSSL_set_ex_data(conn->ssl, 0, conn); - conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + wolfSSL_set_ex_data(conn->ssl, TLS_SSL_CON_EX_IDX, conn); + conn->context = ssl_ctx_get_tls_context(ssl_ctx); /* Need randoms post-hanshake for EAP-FAST, export key and deriving * session ID in EAP methods. */ @@ -378,6 +547,7 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) os_free(conn->suffix_match); os_free(conn->domain_match); os_free(conn->peer_subject); + os_free(conn->check_cert_subject); /* self */ os_free(conn); @@ -427,7 +597,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *alt_subject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -461,6 +632,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -819,6 +998,8 @@ static enum tls_fail_reason wolfssl_tls_fail_reason(int err) case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_CERT_REJECTED: return TLS_FAIL_BAD_CERTIFICATE; + case RSA_KEY_SIZE_E: + return TLS_FAIL_INSUFFICIENT_KEY_LEN; default: return TLS_FAIL_UNSPECIFIED; } @@ -838,6 +1019,148 @@ static const char * wolfssl_tls_err_string(int err, const char *err_str) } +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(WOLFSSL_X509 *cert, int nid, const char *field, + const char *value) +{ + int ret = 0; + int len = os_strlen(value); + char buf[256]; + /* Fetch value based on NID */ + int buf_len = wolfSSL_X509_NAME_get_text_by_NID( + wolfSSL_X509_get_subject_name((WOLFSSL_X509 *) cert), nid, + buf, sizeof(buf)); + + if (buf_len >= 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Matching fields: '%s' '%s' '%s'", field, + value, buf); + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + ret = buf_len >= len && + os_memcmp(buf, value, len - 1) == 0; + } else { + ret = os_strcmp(buf, value) == 0; + } + } else { + wpa_printf(MSG_INFO, + "wolfSSL: cert does not contain entry for '%s'", + field); + } + + return ret; +} + + +#define DN_FIELD_LEN 20 + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @processed_nids: List of NIDs already processed + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(WOLFSSL_X509 *cert, char *field_str, + int *processed_nids) +{ + int nid, i; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + nid = wolfSSL_OBJ_txt2nid(name); + if (nid == NID_undef) { + wpa_printf(MSG_ERROR, + "wolfSSL: Unknown field '%s' in check_cert_subject", + name); + return 0; + } + + /* Check for duplicates */ + for (i = 0; processed_nids[i] != NID_undef && i < DN_FIELD_LEN; i++) { + if (processed_nids[i] == nid) { + wpa_printf(MSG_ERROR, + "wolfSSL: No support for multiple DN's in check_cert_subject"); + return 0; + } + } + if (i == DN_FIELD_LEN) { + wpa_printf(MSG_ERROR, + "wolfSSL: Only %d DN's are supported in check_cert_subject", + DN_FIELD_LEN); + return 0; + } + processed_nids[i] = nid; + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "wolfSSL: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(WOLFSSL_X509 *cert, const char *match) +{ + const char *token, *last = NULL; + /* Maximum length of each DN field is 255 characters */ + char field[256]; + int processed_nids[DN_FIELD_LEN], i; + + for (i = 0; i < DN_FIELD_LEN; i++) + processed_nids[i] = NID_undef; + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "wolfSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, processed_nids)) { + wpa_printf(MSG_INFO, "wolfSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) { struct wpabuf *buf = NULL; @@ -845,7 +1168,7 @@ static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) int cert_len; data = wolfSSL_X509_get_der(cert, &cert_len); - if (!data) + if (data) buf = wpabuf_alloc_copy(data, cert_len); return buf; @@ -859,7 +1182,7 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn, { union tls_event_data ev; struct wpabuf *cert = NULL; - struct tls_context *context = conn->context; + const struct tls_context *context = conn->context; if (!context->event_cb) return; @@ -877,13 +1200,44 @@ static void wolfssl_tls_fail_event(struct tls_connection *conn, } +static int wolfssl_cert_tod(X509 *cert) +{ + WOLFSSL_STACK *ext; + int i; + char *buf; + int tod = 0; + + ext = wolfSSL_X509_get_ext_d2i(cert, CERT_POLICY_OID, NULL, NULL); + if (!ext) + return 0; + + for (i = 0; i < wolfSSL_sk_num(ext); i++) { + WOLFSSL_ASN1_OBJECT *policy; + + policy = wolfSSL_sk_value(ext, i); + if (!policy) + continue; + + buf = (char*)policy->obj; + wpa_printf(MSG_DEBUG, "wolfSSL: Certificate Policy %s", buf); + if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0) + tod = 1; /* TOD-STRICT */ + else if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.2") == 0 && !tod) + tod = 2; /* TOD-TOFU */ + } + wolfSSL_sk_pop_free(ext, NULL); + + return tod; +} + + static void wolfssl_tls_cert_event(struct tls_connection *conn, WOLFSSL_X509 *err_cert, int depth, const char *subject) { struct wpabuf *cert = NULL; union tls_event_data ev; - struct tls_context *context = conn->context; + const struct tls_context *context = conn->context; char *alt_subject[TLS_MAX_ALT_SUBJECT]; int alt, num_alt_subject = 0; WOLFSSL_GENERAL_NAME *gen; @@ -964,6 +1318,7 @@ static void wolfssl_tls_cert_event(struct tls_connection *conn, for (alt = 0; alt < num_alt_subject; alt++) ev.peer_cert.altsubject[alt] = alt_subject[alt]; ev.peer_cert.num_altsubject = num_alt_subject; + ev.peer_cert.tod = wolfssl_cert_tod(err_cert); context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); @@ -979,8 +1334,9 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) int err, depth; WOLFSSL *ssl; struct tls_connection *conn; - struct tls_context *context; + const struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx); @@ -996,7 +1352,7 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf, sizeof(buf)); - conn = wolfSSL_get_ex_data(ssl, 0); + conn = wolfSSL_get_ex_data(ssl, TLS_SSL_CON_EX_IDX); if (!conn) { wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data"); return 0; @@ -1069,6 +1425,8 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) } #endif /* CONFIG_SHA256 */ + wolfssl_tls_cert_event(conn, err_cert, depth, buf); + if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'", @@ -1082,7 +1440,19 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", __func__, preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); - if (depth == 0 && match && os_strstr(buf, match) == NULL) { + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->context->check_cert_subject; + if (check_cert_subject && depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + wpa_printf(MSG_WARNING, + "TLS: Subject '%s' did not match with '%s'", + buf, check_cert_subject); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } else if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not match with '%s'", buf, match); @@ -1116,8 +1486,6 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); - } else { - wolfssl_tls_cert_event(conn, err_cert, depth, buf); } if (conn->cert_probe && preverify_ok && depth == 0) { @@ -1129,31 +1497,6 @@ static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } -#ifdef HAVE_OCSP_WOLFSSL - if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && - preverify_ok) { - enum ocsp_result res; - - res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, - conn->peer_issuer, - conn->peer_issuer_issuer); - if (res == OCSP_REVOKED) { - preverify_ok = 0; - wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, - "certificate revoked", - TLS_FAIL_REVOKED); - if (err == X509_V_OK) - X509_STORE_CTX_set_error( - x509_ctx, X509_V_ERR_CERT_REVOKED); - } else if (res != OCSP_GOOD && - (conn->flags & TLS_CONN_REQUIRE_OCSP)) { - preverify_ok = 0; - wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, - "bad certificate status response", - TLS_FAIL_UNSPECIFIED); - } - } -#endif /* HAVE_OCSP_WOLFSSL */ if (depth == 0 && preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); @@ -1237,15 +1580,9 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, } if (ca_cert || ca_path) { - WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new(); - - if (!cm) { - wpa_printf(MSG_INFO, - "SSL: failed to create certificate store"); - return -1; - } - wolfSSL_CTX_set_cert_store(ctx, cm); - + wpa_printf(MSG_DEBUG, "SSL: Loading CA's from '%s' and '%s'", + ca_cert ? ca_cert : "N/A", + ca_path ? ca_path : "N/A"); if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) != SSL_SUCCESS) { wpa_printf(MSG_INFO, @@ -1262,6 +1599,7 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, return -1; } } + wpa_printf(MSG_DEBUG, "SSL: Loaded ca_cert or ca_path"); return 0; } @@ -1272,19 +1610,24 @@ static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags) { + long op = 0; + #ifdef HAVE_SESSION_TICKET if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET)) wolfSSL_UseSessionTicket(ssl); #endif /* HAVE_SESSION_TICKET */ + wpa_printf(MSG_DEBUG, "SSL: conn_flags: %d", flags); + if (flags & TLS_CONN_DISABLE_TLSv1_0) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1); + op |= WOLFSSL_OP_NO_TLSv1; if (flags & TLS_CONN_DISABLE_TLSv1_1) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + op |= WOLFSSL_OP_NO_TLSv1_1; if (flags & TLS_CONN_DISABLE_TLSv1_2) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + op |= WOLFSSL_OP_NO_TLSv1_2; if (flags & TLS_CONN_DISABLE_TLSv1_3) - wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_3); + op |= WOLFSSL_OP_NO_TLSv1_3; + wolfSSL_set_options(ssl, op); } @@ -1296,7 +1639,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match) < 0) { + params->domain_match, + params->check_cert_subject) < 0) { wpa_printf(MSG_INFO, "Error setting subject match"); return -1; } @@ -1324,13 +1668,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", - params->openssl_ciphers ? params->openssl_ciphers : "N/A"); - if (params->openssl_ciphers && - wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { - wpa_printf(MSG_INFO, - "wolfSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + if (handle_ciphersuites(NULL, conn->ssl, params->openssl_ciphers, + params->flags) != 0) { + wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites"); + return -1; + } + + if (params->openssl_ecdh_curves && + wolfSSL_set1_curves_list(conn->ssl, params->openssl_ecdh_curves) != + 1) { + wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); return -1; } @@ -1524,10 +1872,25 @@ void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response) int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { + /* Need to cast away const as this is one of the only places + * where we should modify it */ + struct tls_context *context = + (struct tls_context *) ssl_ctx_get_tls_context(tls_ctx); + wpa_printf(MSG_DEBUG, "SSL: global set params"); - if (params->check_cert_subject) - return -1; /* not yet supported */ + os_free(context->check_cert_subject); + context->check_cert_subject = NULL; + if (params->check_cert_subject) { + context->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!context->check_cert_subject) { + wpa_printf(MSG_ERROR, + "SSL: Failed to copy check_cert_subject '%s'", + params->check_cert_subject); + return -1; + } + } if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", @@ -1556,35 +1919,74 @@ int tls_global_set_params(void *tls_ctx, return -1; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", - params->openssl_ciphers ? params->openssl_ciphers : "N/A"); - if (params->openssl_ciphers && - wolfSSL_CTX_set_cipher_list(tls_ctx, - params->openssl_ciphers) != 1) { - wpa_printf(MSG_INFO, - "wolfSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + if (handle_ciphersuites(tls_ctx, NULL, params->openssl_ciphers, + params->flags) != 0) { + wpa_printf(MSG_INFO, "wolfssl: Error setting ciphersuites"); return -1; } - if (params->openssl_ecdh_curves) { - wpa_printf(MSG_INFO, - "wolfSSL: openssl_ecdh_curves not supported"); + if (params->openssl_ecdh_curves && + wolfSSL_CTX_set1_curves_list((WOLFSSL_CTX *) tls_ctx, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_INFO, "wolfSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); return -1; } #ifdef HAVE_SESSION_TICKET /* Session ticket is off by default - can't disable once on. */ - if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) - wolfSSL_CTX_UseSessionTicket(tls_ctx); + if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET) && + wolfSSL_CTX_UseSessionTicket(tls_ctx) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseSessionTicket failed"); + return -1; + } #endif /* HAVE_SESSION_TICKET */ #ifdef HAVE_OCSP if (params->ocsp_stapling_response) { - wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, - params->ocsp_stapling_response); - wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, - ocsp_resp_free_cb, NULL); + if (wolfSSL_CTX_EnableOCSP(tls_ctx, + WOLFSSL_OCSP_URL_OVERRIDE) != + WOLFSSL_SUCCESS || + /* Workaround to force using the override URL without + * enabling OCSP */ + wolfSSL_CTX_DisableOCSP(tls_ctx) != WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseOCSPStapling() failed"); + return -1; + } + + if (wolfSSL_CTX_UseOCSPStapling(tls_ctx, WOLFSSL_CSR_OCSP, + WOLFSSL_CSR_OCSP_USE_NONCE) != + WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_UseOCSPStapling() failed"); + return -1; + } + + if (wolfSSL_CTX_EnableOCSPStapling(tls_ctx) != + WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_EnableOCSPStapling() failed"); + return -1; + } + + if (wolfSSL_CTX_SetOCSP_OverrideURL( + tls_ctx, + params->ocsp_stapling_response) != + WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_SetOCSP_OverrideURL() failed"); + return -1; + } + + if (wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, + ocsp_resp_free_cb, NULL) != + WOLFSSL_SUCCESS) { + wpa_printf(MSG_ERROR, + "wolfSSL: wolfSSL_CTX_SetOCSP_Cb() failed"); + return -1; + } } #endif /* HAVE_OCSP */ @@ -1610,12 +2012,13 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; - struct tls_context *context; + const struct tls_context *context; if (!conn) return -1; wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer); + wpa_printf(MSG_DEBUG, "SSL: flags: %d", flags); if (verify_peer) { conn->ca_cert_verify = 1; @@ -1629,7 +2032,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, wolfSSL_set_accept_state(conn->ssl); - context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, 0); + context = ssl_ctx_get_tls_context(ssl_ctx); if (context && context->tls_session_lifetime == 0) { /* * Set session id context to a unique value to make sure @@ -1645,7 +2048,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, session_ctx_len); } - /* TODO: do we need to fake a session like OpenSSL does here? */ + tls_set_conn_flags(conn->ssl, flags); return 0; } @@ -1692,12 +2095,28 @@ static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, char msg[80]; wpa_printf(MSG_DEBUG, - "SSL: %s - failed %s", + "SSL: %s - failed (%d) %s", server ? "wolfSSL_accept" : - "wolfSSL_connect", + "wolfSSL_connect", err, wolfSSL_ERR_error_string(err, msg)); conn->failed++; } + + /* Generate extra events */ + if (err == OCSP_CERT_REVOKED || + err == BAD_CERTIFICATE_STATUS_ERROR || + err == OCSP_CERT_REVOKED) { + char buf[256]; + WOLFSSL_X509 *err_cert; + + err_cert = wolfSSL_get_peer_certificate(conn->ssl); + wolfSSL_X509_NAME_oneline( + wolfSSL_X509_get_subject_name(err_cert), + buf, sizeof(buf)); + wolfssl_tls_fail_event(conn, err_cert, err, 0, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } } return conn->output.out_data; @@ -1866,11 +2285,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, char buf[128], *pos, *end; u8 *c; int ret; + bool set_sig_algs = false; if (!conn || !conn->ssl || !ciphers) return -1; - buf[0] = '\0'; + buf[0] = buf[1] = '\0'; pos = buf; end = pos + sizeof(buf); @@ -1890,6 +2310,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, break; case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; + set_sig_algs = true; break; case TLS_CIPHER_RSA_DHE_AES256_SHA: suite = "DHE-RSA-AES256-SHA"; @@ -1910,10 +2331,16 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, c++; } - wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1); + /* +1 to skip the ":" */ + if (handle_ciphersuites(NULL, conn->ssl, buf + 1, conn->flags) != 0) { + wpa_printf(MSG_DEBUG, + "wolfssl: Cipher suite configuration failed"); + return -1; + } - if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) { - wpa_printf(MSG_DEBUG, "Cipher suite configuration failed"); + if (set_sig_algs && + wolfSSL_set1_sigalgs_list(conn->ssl, SUITEB_TLS_128_SIGALGS) != 1) { + wpa_printf(MSG_DEBUG, "wolfssl: Sigalg configuration failed"); return -1; } @@ -1924,34 +2351,19 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - WOLFSSL_CIPHER *cipher; const char *name; if (!conn || !conn->ssl) return -1; - cipher = wolfSSL_get_current_cipher(conn->ssl); - if (!cipher) - return -1; - - name = wolfSSL_CIPHER_get_name(cipher); + if (wolfSSL_version(conn->ssl) == TLS1_3_VERSION) + name = wolfSSL_get_cipher(conn->ssl); + else + name = wolfSSL_get_cipher_name(conn->ssl); if (!name) return -1; - if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0) - os_strlcpy(buf, "RC4-SHA", buflen); - else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0) - os_strlcpy(buf, "ADH-AES128-SHA", buflen); - else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0) - os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen); - else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0) - os_strlcpy(buf, "AES256-SHA", buflen); - else - os_strlcpy(buf, name, buflen); + os_strlcpy(buf, name, buflen); return 0; } @@ -2273,13 +2685,13 @@ void tls_connection_set_success_data(struct tls_connection *conn, goto fail; } - old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + old = wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); if (old) { wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p", old); wpabuf_free(old); } - if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + if (wolfSSL_SESSION_set_ex_data(sess, TLS_SESSION_EX_IDX, data) != 1) goto fail; wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data); @@ -2302,7 +2714,7 @@ tls_connection_get_success_data(struct tls_connection *conn) sess = wolfSSL_get_session(conn->ssl); if (!sess) return NULL; - return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + return wolfSSL_SESSION_get_ex_data(sess, TLS_SESSION_EX_IDX); } diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index 3e77474899eb..ea5e28be0b74 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -22,11 +22,16 @@ #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" +#include "common/nan_defs.h" #ifdef CONFIG_MACSEC #include "pae/ieee802_1x_kay.h" #endif /* CONFIG_MACSEC */ #include "utils/list.h" +struct nan_subscribe_params; +struct nan_publish_params; +struct pr_pasn_ranging_params; + #define HOSTAPD_CHAN_DISABLED 0x00000001 #define HOSTAPD_CHAN_NO_IR 0x00000002 #define HOSTAPD_CHAN_RADAR 0x00000008 @@ -47,6 +52,7 @@ #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 +#define HOSTAPD_CHAN_AUTO_BW 0x00040000 /* Allowed bandwidth mask */ enum hostapd_chan_width_attr { @@ -155,6 +161,12 @@ struct hostapd_channel_data { * need to set this) */ long double interference_factor; + + /** + * interference_bss_based - Indicates whether the interference was + * calculated from number of BSSs + */ + bool interference_bss_based; #endif /* CONFIG_ACS */ /** @@ -208,6 +220,7 @@ struct eht_capabilities { #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) +#define HOSTAPD_MODE_FLAG_EHT_INFO_KNOWN BIT(3) enum ieee80211_op_mode { @@ -317,6 +330,27 @@ struct hostapd_hw_modes { }; +/** + * struct hostapd_multi_hw_info: Supported multiple underlying hardware info + */ +struct hostapd_multi_hw_info { + /** + * hw_idx - Hardware index + */ + u8 hw_idx; + + /** + * start_freq - Frequency range start in MHz + */ + int start_freq; + + /** + * end_freq - Frequency range end in MHz + */ + int end_freq; +}; + + #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 @@ -357,6 +391,8 @@ struct hostapd_hw_modes { * of TSF of the BSS specified by %tsf_bssid. * @tsf_bssid: The BSS that %parent_tsf TSF time refers to. * @beacon_newer: Whether the Beacon frame data is known to be newer + * @mlo_tput_accumulated: Whether the scan result throughput is accumulated + * (used during scan result processing) * @ie_len: length of the following IE field in octets * @beacon_ie_len: length of the following Beacon IE field in octets * @@ -391,6 +427,7 @@ struct wpa_scan_res { u64 parent_tsf; u8 tsf_bssid[ETH_ALEN]; bool beacon_newer; + bool mlo_tput_accumulated; size_t ie_len; size_t beacon_ie_len; /* Followed by ie_len + beacon_ie_len octets of IE data */ @@ -866,6 +903,14 @@ struct hostapd_freq_params { */ bool eht_enabled; + /** + * punct_bitmap - Preamble puncturing bitmap + * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the + * channel with the lowest frequency. A bit set to 1 indicates that the + * subchannel is punctured, otherwise active. + */ + u16 punct_bitmap; + /** * link_id: If >=0 indicates the link of the AP MLD to configure */ @@ -1363,6 +1408,42 @@ struct wpa_driver_associate_params { * mld_params - MLD association parameters */ struct wpa_driver_mld_params mld_params; + + /** + * rsn_overriding - wpa_supplicant RSN overriding support + */ + bool rsn_overriding; + + /** + * spp_amsdu - SPP A-MSDU used on this connection + */ + bool spp_amsdu; + + /** + * bssid_filter - Allowed BSSIDs for the current association + * This can be %NULL to indicate no constraint. */ + const u8 *bssid_filter; + + /** + * bssid_filter_count - Number of allowed BSSIDs + */ + unsigned int bssid_filter_count; + + /** + * p2p_mode - P2P R1 only, P2P R2 only, or PCC mode + */ + enum wpa_p2p_mode p2p_mode; + + /** + * eppke_supported - Whether EPPKE authentication is supported + */ + bool eppke_supported; + + /** + * ieee8021x_auth_supported - Whether IEEE 802.1X authentication in + * Authentication frames is supported by wpa_supplicant + */ + bool ieee8021x_auth_supported; }; enum hide_ssid { @@ -1403,6 +1484,72 @@ struct unsol_bcast_probe_resp { size_t unsol_bcast_probe_resp_tmpl_len; }; +struct mbssid_data { + /** + * mbssid_tx_iface - Transmitting interface of the MBSSID set + */ + const char *mbssid_tx_iface; + + /** + * mbssid_tx_iface_linkid - Link ID of the transmitting interface if + * it is part of an MLD. Otherwise, -1. + */ + int mbssid_tx_iface_linkid; + + /** + * mbssid_index - The index of this BSS in the MBSSID set + */ + unsigned int mbssid_index; + + /** + * mbssid_elem - Buffer containing all MBSSID elements + */ + u8 *mbssid_elem; + + /** + * mbssid_elem_len - Total length of all MBSSID elements + */ + size_t mbssid_elem_len; + + /** + * mbssid_elem_count - The number of MBSSID elements + */ + u8 mbssid_elem_count; + + /** + * mbssid_elem_offset - Offsets to elements in mbssid_elem. + * Kernel will use these offsets to generate multiple BSSID beacons. + */ + u8 **mbssid_elem_offset; + + /** + * ema - Enhanced MBSSID advertisements support. + */ + bool ema; + + /** + * rnr_elem - This buffer contains all of reduced neighbor report (RNR) + * elements + */ + u8 *rnr_elem; + + /** + * rnr_elem_len - Length of rnr_elem buffer + */ + size_t rnr_elem_len; + + /** + * rnr_elem_count - Number of RNR elements + */ + u8 rnr_elem_count; + + /** + * rnr_elem_offset - The offsets to the elements in rnr_elem. + * The driver will use these to include RNR elements in EMA beacons. + */ + u8 **rnr_elem_offset; +}; + struct wpa_driver_ap_params { /** * head - Beacon head from IEEE 802.11 header to IEs before TIM IE @@ -1606,11 +1753,6 @@ struct wpa_driver_ap_params { */ int disable_dgaf; - /** - * osen - Whether OSEN security is enabled - */ - int osen; - /** * freq - Channel parameters for dynamic bandwidth changes */ @@ -1746,40 +1888,11 @@ struct wpa_driver_ap_params { size_t fd_frame_tmpl_len; /** - * mbssid_tx_iface - Transmitting interface of the MBSSID set + * mbssid - MBSSID element related params for Beacon frames + * + * This is used to add MBSSID element in beacon data. */ - const char *mbssid_tx_iface; - - /** - * mbssid_index - The index of this BSS in the MBSSID set - */ - unsigned int mbssid_index; - - /** - * mbssid_elem - Buffer containing all MBSSID elements - */ - u8 *mbssid_elem; - - /** - * mbssid_elem_len - Total length of all MBSSID elements - */ - size_t mbssid_elem_len; - - /** - * mbssid_elem_count - The number of MBSSID elements - */ - u8 mbssid_elem_count; - - /** - * mbssid_elem_offset - Offsets to elements in mbssid_elem. - * Kernel will use these offsets to generate multiple BSSID beacons. - */ - u8 **mbssid_elem_offset; - - /** - * ema - Enhanced MBSSID advertisements support. - */ - bool ema; + struct mbssid_data mbssid; /** * punct_bitmap - Preamble puncturing bitmap @@ -1789,27 +1902,6 @@ struct wpa_driver_ap_params { */ u16 punct_bitmap; - /** - * rnr_elem - This buffer contains all of reduced neighbor report (RNR) - * elements - */ - u8 *rnr_elem; - - /** - * rnr_elem_len - Length of rnr_elem buffer - */ - size_t rnr_elem_len; - - /** - * rnr_elem_count - Number of RNR elements - */ - unsigned int rnr_elem_count; - - /** - * rnr_elem_offset - The offsets to the elements in rnr_elem. - * The driver will use these to include RNR elements in EMA beacons. - */ - u8 **rnr_elem_offset; /* Unsolicited broadcast Probe Response data */ struct unsol_bcast_probe_resp ubpr; @@ -1893,6 +1985,12 @@ struct wpa_driver_set_key_params { * ifname - Interface name (for multi-SSID/VLAN support) */ const char *ifname; + /** + * own_addr - Own MAC address identifying the virtual interface for + * the key operation, or %NULL to use the default interface (ifname) + */ + const u8 *own_addr; + /** * alg - Encryption algorithm * @@ -1988,10 +2086,17 @@ struct wpa_driver_set_key_params { * %KEY_FLAG_GROUP_TX_DEFAULT * GTK key valid for TX only, immediately taking over TX. * %KEY_FLAG_PAIRWISE_RX_TX - * Pairwise key immediately becoming the active pairwise key. + * Pairwise key immediately becoming the active pairwise key. If this + * key was previously set as an alternative RX-only key with + * %KEY_FLAG_PAIRWISE_RX | %KEY_FLAG_NEXT, the alternative RX-only key + * is taken into use for both TX and RX without changing the RX counter + * values. * %KEY_FLAG_PAIRWISE_RX * Pairwise key not yet valid for TX. (Only usable when Extended - * Key ID is supported by the driver.) + * Key ID is supported by the driver or when configuring the next TK + * for RX-only with %KEY_FLAG_NEXT in which case the new TK can be used + * as an alternative key for decrypting received frames without + * replacing the possibly already configured old TK.) * %KEY_FLAG_PAIRWISE_RX_TX_MODIFY * Enable TX for a pairwise key installed with * KEY_FLAG_PAIRWISE_RX. @@ -2007,6 +2112,21 @@ struct wpa_driver_set_key_params { * * Set to a valid Link ID (0-14) when applicable, otherwise -1. */ int link_id; + + /** + * ltf_keyseed_len - Length of the LTF keyseed in octets + * + * Set to 0 if no LTF keyseed is provided. + */ + u8 ltf_keyseed_len; + + /** + * ltf_keyseed - LTF keyseed for secure ranging (IEEE 802.11az) + * + * Used to configure the secure LTF key seed for a peer measurement + * session. Set to NULL if not applicable. + */ + const u8 *ltf_keyseed; }; enum wpa_driver_if_type { @@ -2072,10 +2192,105 @@ enum wpa_driver_if_type { */ WPA_IF_NAN, + /* + * WPA_IF_NAN_DATA - NAN Data interface + */ + WPA_IF_NAN_DATA, + + /* + * WPA_IF_PD - Proxymity Detection (PD) Device + */ + WPA_IF_PD, + /* keep last */ WPA_IF_MAX }; +/** + * struct nan_capa - NAN capabilities + * + * @drv_flags: NAN capability flags (WPA_DRIVER_FLAGS_NAN_*) + * @num_radios: Maximum number of NAN radios + * @sched_chans: Maximum number of channels in NAN schedule (per map) + * @slot_duration: NAN schedule bitmap slot duration (16, 32, 64, or 128) in TUs + * @schedule_period: Schedule period (powers of 2 in range: 128-8192) in TUs + * @max_channel_switch_time: Max channel switch time in microseconds + * @num_antennas: Number of antennas (lower nibble TX, upper nibble RX) + * @op_modes: NAN capability operation modes + * @dev_capa: NAN device capabilities + * @ht_capab: HT capabilities information as defined in IEEE Std 802.11-2024, + * 9.4.2.54.2 (HT Capability Information field) + * @ht_ampdu_params: HT A-MPDU parameters as defined in IEEE Std 802.11-2024, + * 9.4.2.54.3 (A-MPDU Parameters field) + * @ht_mcs_set: HT MCS set as defined in IEEE Std 802.11-2024,9.4.2.54.4 + * (Supported MCS Set field) + * @vht_capab: VHT capabilities information as defined in IEEE Std 802.11-2024, + * 9.4.2.156.2 (VHT Capabilities Information field) + * @vht_mcs_set: VHT MCS set as defined in IEEE Std 802.11-2024, 9.4.2.156.3 + * (Supported VHT-MCS and NSS Set field) + * @vht_valid: Whether &vht_capab and &vht_mcs_set are both valid + * @he_capab: HE capabilities. See &struct he_capabilities. + * @he_valid: Whether HE capabilities are valid + * + * For the schedule capabilities, even if the driver/device supports multiple + * options, only a single option should be selected. For example, if both 16 TU + * and 32 TU slot durations are supported, the driver should report the shortest + * supported slot duration (16 TU). For schedule period, the driver should + * report the maximum supported period, as longer periods can always be + * represented by repetitions of the shorter schedule bitmap. In any case, + * 512/16 configuration is recommended for better interoperability. + * + * As for the PHY capabilities, HT must be supported for NAN Data path, and + * without valid HT capabilities NAN Data path would be disabled. + * + * TODO: For now support only a single PHY capabilities configuration. This + * might need to be extended to support multiple configurations in the future. + */ +struct nan_capa { +/* Driver supports dual band NAN operation */ +#define WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND 0x00000001 +/* Driver supports NAN synchronization configuration */ +#define WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG 0x00000002 +/* Driver supports DW notifications and SDF TX/RX over NAN device interface */ +#define WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE 0x00000004 +/** Driver supports NAN Data path */ +#define WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP 0x00000008 +/** Driver supports NAN beacon protection */ +#define WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT 0x00000010 + u32 drv_flags; + u8 num_radios; + u8 sched_chans; + u8 slot_duration; + u16 schedule_period; + u16 max_channel_switch_time; + u8 num_antennas; + u8 op_modes; + u8 dev_capa; + + u16 ht_capab; + u8 ht_mcs_set[16]; + u8 ht_ampdu_params; + + u32 vht_capab; + u8 vht_mcs_set[8]; + bool vht_valid; + + struct he_capabilities he_capab; + bool he_valid; +}; + +/* Bit index values for pd_bandwidths bitmap */ +#define WPA_PR_CHAN_WIDTH_20 0 +#define WPA_PR_CHAN_WIDTH_40 1 +#define WPA_PR_CHAN_WIDTH_80 2 +#define WPA_PR_CHAN_WIDTH_80P80 3 +#define WPA_PR_CHAN_WIDTH_160 4 + +/* Bit index values for pd_preambles bitmap */ +#define WPA_PR_PREAMBLE_HT 0 +#define WPA_PR_PREAMBLE_VHT 1 +#define WPA_PR_PREAMBLE_HE 2 + /** * struct wpa_driver_capa - Driver capability information */ @@ -2103,7 +2318,6 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000 #define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000 -#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000 #define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY 0x01000000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY 0x02000000 /** Bitfield of supported key management suites */ @@ -2330,6 +2544,38 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP 0x0000000000100000ULL /** Driver supports TWT responder in HT and VHT modes */ #define WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER 0x0000000000200000ULL +/** Driver supports RSN override elements */ +#define WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA 0x0000000000400000ULL +/** Driver supports NAN USD offload */ +#define WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD 0x0000000000800000ULL +/** Driver/device supports SPP A-MSDUs */ +#define WPA_DRIVER_FLAGS2_SPP_AMSDU 0x0000000001000000ULL +/** Driver supports P2P V2 */ +#define WPA_DRIVER_FLAGS2_P2P_FEATURE_V2 0x0000000002000000ULL +/** Driver supports P2P PCC mode */ +#define WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE 0x0000000004000000ULL +/** Driver supports arbitrary channel width changes in AP mode */ +#define WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE 0x0000000008000000ULL +/** Driver supports FTM initiator functionality */ +#define WPA_DRIVER_FLAGS2_FTM_INITIATOR 0x0000000010000000ULL +/** Driver supports non-trigger based ranging responder functionality */ +#define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_RESPONDER 0x0000000020000000ULL +/** Driver supports non-trigger based ranging initiator functionality */ +#define WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR 0x0000000040000000ULL +/** Driver supports NAN Device interface and NAN Synchronization */ +#define WPA_DRIVER_FLAGS2_SUPPORT_NAN 0x0000000080000000ULL +/** Driver supports P2P assisted DFS */ +#define WPA_DRIVER_FLAGS2_P2P_ASSISTED_DFS 0x0000000100000000ULL +/** Driver supports (Re)Association Request/Response frame encryption */ +#define WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION 0x0000000200000000ULL +/** Driver supports EPPKE authentication */ +#define WPA_DRIVER_FLAGS2_EPPKE 0x0000000400000000ULL +/** Driver supports IEEE 802.1X authentication in Authentication frames */ +#define WPA_DRIVER_FLAGS2_802_1X_AUTH 0x0000000800000000ULL +/** Driver supports PMKSA caching privacy */ +#define WPA_DRIVER_FLAGS2_PMKSA_PRIVACY 0x0000001000000000ULL +/** Driver supports MAC address filter for remain-on-channel */ +#define WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER 0x0000002000000000ULL u64 flags2; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -2450,6 +2696,58 @@ struct wpa_driver_capa { unsigned int mbssid_max_interfaces; /* Maximum profile periodicity for enhanced MBSSID advertisement */ unsigned int ema_max_periodicity; + + /* Maximum number of bytes of extra IE(s) that can be added to Probe + * Request frames */ + size_t max_probe_req_ie_len; + + /* EDCA based ranging capabilities */ + u8 max_tx_antenna; + u8 max_rx_antenna; + u32 edca_min_ranging_interval; + + /* NTB based ranging capabilities */ + u8 max_tx_ltf_repetations; + u8 max_rx_ltf_repetations; + u8 max_tx_ltf_total; + u8 max_rx_ltf_total; + u8 max_rx_sts_le_80; + u8 max_rx_sts_gt_80; + u8 max_tx_sts_le_80; + u8 max_tx_sts_gt_80; + u32 ntb_min_ranging_interval; + + /* Peer measurement capabilities */ + u32 pmsr_max_peers; + u8 max_ftms_per_burst; + bool concurrent_ista_rsta; + bool support_6ghz; + bool asap_support; + u32 pd_preambles; + u32 pd_bandwidths; + + struct { + bool support_ntb; + bool support_tb; + bool support_edca; + u32 max_peers; + } ista; + + struct { + bool support_ntb; + bool support_tb; + bool support_edca; + u32 max_peers; + } rsta; + + struct { + bool infra_support; + bool pd_support; + } ranging_type; + +#ifdef CONFIG_NAN + struct nan_capa nan_capa; +#endif /* CONFIG_NAN */ }; @@ -2539,6 +2837,9 @@ struct hostapd_sta_add_params { size_t eht_capab_len; u32 flags; /* bitmask of WPA_STA_* flags */ u32 flags_mask; /* unset bits in flags */ +#ifdef CONFIG_ENC_ASSOC + bool epp_sta; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; u16 peer_aid; @@ -2556,6 +2857,16 @@ struct hostapd_sta_add_params { bool mld_link_sta; s8 mld_link_id; const u8 *mld_link_addr; + u16 eml_cap; + +#ifdef CONFIG_NAN + /* + * For a station added to a NAN Data Interface (NDI) indicate the + * address of the NAN Management Interface (NMI) to which this station + * belongs. + */ + const u8 *nmi_addr; +#endif /* CONFIG_NAN */ }; struct mac_address { @@ -2603,6 +2914,7 @@ struct wpa_bss_params { #define WPA_STA_TDLS_PEER BIT(4) #define WPA_STA_AUTHENTICATED BIT(5) #define WPA_STA_ASSOCIATED BIT(6) +#define WPA_STA_SPP_AMSDU BIT(7) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -2670,6 +2982,22 @@ struct wpa_mlo_signal_info { struct wpa_signal_info links[MAX_NUM_MLD_LINKS]; }; +/** + * struct wpa_mlo_reconfig_info - Information about user-requested add and/or + * remove setup links for the current MLO association. + * + * @add_links: Bitmask of links to be added + * @add_link_bssid: Array of BSSIDs for the links to be added + * @add_link_freq: Array of frequencies for the links to be added + * @delete_links: Bitmask of links to be removed + */ +struct wpa_mlo_reconfig_info { + u16 add_links; + u8 add_link_bssid[MAX_NUM_MLD_LINKS][ETH_ALEN]; + int add_link_freq[MAX_NUM_MLD_LINKS]; + u16 delete_links; +}; + /** * struct wpa_channel_info - Information about the current channel * @frequency: Center frequency of the primary 20 MHz channel @@ -2706,6 +3034,7 @@ struct wpa_channel_info { * @proberesp_ies_len: Length of proberesp_ies in octets * @proberesp_ies_len: Length of proberesp_ies in octets * @probe_resp_len: Length of probe response template (@probe_resp) + * @mbssid: MBSSID element(s) to add into Beacon frames */ struct beacon_data { u8 *head, *tail; @@ -2719,6 +3048,8 @@ struct beacon_data { size_t proberesp_ies_len; size_t assocresp_ies_len; size_t probe_resp_len; + + struct mbssid_data mbssid; }; /** @@ -2730,7 +3061,6 @@ struct beacon_data { * @beacon_after: Next beacon/probe resp/asooc resp info * @counter_offset_beacon: Offset to the count field in beacon's tail * @counter_offset_presp: Offset to the count field in probe resp. - * @punct_bitmap - Preamble puncturing bitmap * @link_id: Link ID to determine the link for MLD; -1 for non-MLD * @ubpr: Unsolicited broadcast Probe Response frame data */ @@ -2745,7 +3075,6 @@ struct csa_settings { u16 counter_offset_beacon[2]; u16 counter_offset_presp[2]; - u16 punct_bitmap; int link_id; struct unsol_bcast_probe_resp ubpr; @@ -2904,6 +3233,16 @@ enum wpa_drv_update_connect_params_mask { * from user space to the driver. * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). * @mld_addr: AP's MLD address or %NULL if MLO is not used + * @auth_alg: Authentication algorithm to be used + * @pairwise_cipher: Pairwise cipher to be used, WPA_CIPHER_* + * @group_cipher: Group cipher to be used, WPA_CIPHER_* + * @group_mgmt_cipher: Group management cipher to be used, WPA_CIPHER_* + * @rsn_capab: RSN capabilities + * @rsnxe_data: Extended RSN capabilities data + * @rsnxe_data_len: Length of Extended RSN capabilities data in octets + * @kck: Key Confirmation Key (KCK) to pass to driver for IEEE 802.1X + * Authentication offload + * @kck_len: Length of the KCK */ struct external_auth { enum { @@ -2917,6 +3256,16 @@ struct external_auth { u16 status; const u8 *pmkid; const u8 *mld_addr; + unsigned int auth_alg; + int pairwise_cipher; + int group_cipher; + int group_mgmt_cipher; + u16 rsn_capab; + const u8 *rsnxe_data; + size_t rsnxe_data_len; + + const u8 *kck; + size_t kck_len; }; #define WPAS_MAX_PASN_PEERS 10 @@ -2943,6 +3292,14 @@ enum pasn_status { * @akmp: Authentication key management protocol type supported. * @cipher: Cipher suite. * @group: Finite cyclic group. Default group used is 19 (ECC). + * @temporary_network: Indicates if a temporary network was created to perform + * PASN authentication. + * @password: Password of user requested network. + * @comeback_len: Length of the comeback cookie data. + * @comeback: Comeback cookie data that may be present in case a temporary + * rejection is received from the AP. + * @comeback_after: The time after which the STA can try for PASN handshake in + * case of temporary rejection. * @ltf_keyseed_required: Indicates whether LTF keyseed generation is required * @status: PASN response status, %PASN_STATUS_SUCCESS for successful * authentication, use %PASN_STATUS_FAILURE if PASN authentication @@ -2956,6 +3313,11 @@ struct pasn_peer { int akmp; int cipher; int group; + bool temporary_network; + char *password; + size_t comeback_len; + u8 *comeback; + u16 comeback_after; bool ltf_keyseed_required; enum pasn_status status; }; @@ -3042,6 +3404,135 @@ struct driver_sta_mlo_info { } links[MAX_NUM_MLD_LINKS]; }; +/** + * struct nan_band_config - NAN band specific configuration + * + * For details on NAN band configuration, see the Wi-Fi Aware Specification + * + * @frequency: Frequency in MHz for the band + * @rssi_close: RSSI close threshold used for NAN state transition algorithm + * @rssi_middle: RSSI middle threshold used for NAN state transition algorithm + * @awake_dw_interval: Committed DW information + * @disable_scan: Disable scan for this band + */ +struct nan_band_config { + u16 frequency; + s8 rssi_close; + s8 rssi_middle; + u8 awake_dw_interval; + bool disable_scan; +}; + +/** + * struct nan_cluster_config - NAN cluster configuration + * + * @master_pref: Master preference for the cluster + * @dual_band: Dual band operation enabled + * @enable_dw_notif: Enable discovery window notifications + * @cluster_id: Cluster ID for the NAN cluster + * @scan_period: Period for NAN scan in seconds + * @scan_dwell_time: Dwell time for NAN scan in TUs per channel + * @discovery_beacon_interval: Interval for discovery beacon in TUs + * @low_band_cfg: Configuration for low band NAN operation + * @high_band_cfg: Configuration for high band NAN operation + * @extra_nan_attrs: Extra NAN attributes to be included in NAN beacons + * @extra_nan_attrs_len: Length of the extra NAN attributes + * @vendor_elems: Vendor specific elements to be included in NAN beacons + * @vendor_elems_len: Length of the vendor specific elements + */ +struct nan_cluster_config { + u8 master_pref; + u8 dual_band; + bool enable_dw_notif; + + u8 cluster_id[ETH_ALEN]; + u16 scan_period; + u16 scan_dwell_time; + u8 discovery_beacon_interval; + struct nan_band_config low_band_cfg; + struct nan_band_config high_band_cfg; + const u8 *extra_nan_attrs; + size_t extra_nan_attrs_len; + const u8 *vendor_elems; + size_t vendor_elems_len; +}; + +/** + * Even if the device supports more channels, 4 channels should be enough + * for any practical purpose + */ +#define MAX_NUM_NAN_SCHEDULE_CHANNELS 4 + +#define MAX_NUM_NAN_MAPS 2 + +/** + * struct nan_schedule_config - NAN schedule configuration + * + * @deferred: true if the driver should announce the schedule update to peers + * before applying it + * @num_channels: Number of channels in the schedule + * @channels: Channel specific schedule information + * @avail_attr: NAN Availability attribute as defined in Wi-Fi Aware spec v4.0, + * Table 93 + */ +struct nan_schedule_config { + bool deferred; + u8 num_channels; + + /** + * channels - Channel specific schedule information + * + * @freq: Frequency in MHz + * @center_freq1: Center frequency 1 in MHz + * @center_freq2: Center frequency 2 in MHz + * @bandwidth: Channel bandwidth + * @time_bitmap: Bitmap indicating the committed availability + * on the channel. + * @rx_nss: Number of spatial streams supported for RX on the + * channel + * @chan_entry: Channel Entry as defined in Wi-Fi Aware spec v4.0, + * Table 100 (Channel Entry format for the NAN Availability + * attribute). + * + * Note: Time bitmap slot duration and schedule length equal to the + * reported NAN capabilities (see &nan_slot_duration and + * &nan_schedule_length in &struct wpa_driver_capa). + */ + struct nan_schedule_channel { + int freq; + int center_freq1; + int center_freq2; + int bandwidth; + struct wpabuf *time_bitmap; + u8 rx_nss; + + u8 chan_entry[6]; + } channels[MAX_NUM_NAN_SCHEDULE_CHANNELS]; + + struct wpabuf *avail_attr; +}; + +/** + * struct nan_peer_schedule_config - NAN peer schedule configuration + * + * @n_maps: Number of maps in the schedule + * @maps: Map specific schedule information + */ +struct nan_peer_schedule_config { + u8 n_maps; + + /** + * maps - Map specific schedule information + * + * @map_id: Map ID + * @sched: NAN schedule configuration for the map + */ + struct nan_schedule_map { + u8 map_id; + struct nan_schedule_config sched; + } maps[MAX_NUM_NAN_MAPS]; +}; + /** * struct wpa_driver_ops - Driver interface API definition * @@ -3101,6 +3592,53 @@ struct wpa_driver_ops { * broadcast keys, so key index 0 is available for this kind of * configuration. * + * For pairwise keys, there are potential race conditions between + * enabling a new TK on each end of the connection and sending the first + * protected frame. Drivers have multiple options on which style of key + * configuration to support with the simplest option not providing any + * protection for the race condition while the more complex options do + * provide partial or full protection. + * + * Option 1: Do not support extended key IDs (i.e., use only Key ID 0 + * for pairwise keys) and do not support configuration of the next TK + * as an alternative RX key. This provides no protection, but is simple + * to support. The driver needs to ignore set_key() calls with + * KEY_FLAG_NEXT. + * + * Option 2: Do not support extended key IDs (i.e., use only Key ID 0 + * for pairwise keys), but support configuration of the next TK as an + * alternative RX key for the initial 4-way handshake. This provides + * protection for the initial key setup at the beginning of an + * association. The driver needs to configure the initial TK for RX-only + * when receiving a set_key() call with KEY_FLAG_NEXT. This RX-only key + * is ready for receiving protected Data frames from the peer before the + * local device has enabled the key for TX. Unprotected EAPOL frames + * need to be allowed even when this next TK is configured as RX-only + * key. The same key is then set with KEY_FLAG_PAIRWISE_RX_TX to enable + * its use for both TX and RX. The driver ignores set_key() calls with + * KEY_FLAG_NEXT when a TK has been configured. When fully enabling the + * TK for TX and RX, the RX counters associated with the TK must not be + * cleared. + * + * Option 3: Same as option 2, but the driver supports multiple RX keys + * in parallel during PTK rekeying. The driver processed set_key() calls + * with KEY_FLAG_NEXT also when a TK has been configured. At that point + * in the rekeying sequence the driver uses the previously configured TK + * for TX and decrypts received frames with either the previously + * configured TK or the next TK (RX-only). + * + * Option 4: The driver supports extended Key IDs and they are used for + * an association but does not support KEY_FLAG_NEXT (options 2 and 3). + * The next TK is configured as RX-only with KEY_FLAG_PAIRWISE_RX and + * it is enabled for TX and RX with KEY_FLAG_PAIRWISE_RX_TX_MODIFY. When + * extended key ID is not used for an association, the driver behaves + * like in option 1. + * + * Option 5 and 6: Like option 4 but with support for KEY_FLAG_NEXT as + * described above for options 2 and 3, respectively. Option 4 is used + * for cases where extended key IDs are used for an association. Option + * 2 or 3 is used for cases where extended key IDs are not used. + * * Please note that TKIP keys include separate TX and RX MIC keys and * some drivers may expect them in different order than wpa_supplicant * is using. If the TX/RX keys are swapped, all TKIP encrypted packets @@ -3344,12 +3882,17 @@ struct wpa_driver_ops { * @num_modes: Variable for returning the number of returned modes * flags: Variable for returning hardware feature flags * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*) + * @alpha2: Buffer for returning the country code (at least 3 bytes), + * or %NULL if not needed. Set to empty string if not available. + * @alpha2_len: Length of the @alpha2 buffer * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, - u16 *flags, u8 *dfs); + u16 *flags, u8 *dfs, + char *alpha2, + size_t alpha2_len); /** * send_mlme - Send management frame from MLME @@ -3458,12 +4001,15 @@ struct wpa_driver_ops { * e.g., wpa_supplicant_event() * @ifname: interface name, e.g., wlan0 * @global_priv: private driver global data from global_init() + * @p2p_mode: P2P mode for a GO (not applicable for other interface + * types) * Returns: Pointer to private data, %NULL on failure * * This function can be used instead of init() if the driver wrapper * uses global data. */ - void * (*init2)(void *ctx, const char *ifname, void *global_priv); + void * (*init2)(void *ctx, const char *ifname, void *global_priv, + enum wpa_p2p_mode p2p_mode); /** * get_interfaces - Get information about available interfaces @@ -3704,13 +4250,14 @@ struct wpa_driver_ops { * @own_addr: Source address and BSSID for the Disassociation frame * @addr: MAC address of the station to disassociate * @reason: Reason code for the Disassociation frame + * @link_id: Link ID to use for Disassociation frame, or -1 if not set * Returns: 0 on success, -1 on failure * * This function requests a specific station to be disassociated and * a Disassociation frame to be sent to it. */ int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason); + u16 reason, int link_id); /** * sta_remove - Remove a station entry (AP only) @@ -3903,18 +4450,6 @@ struct wpa_driver_ops { int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, int vlan_id, int link_id); - /** - * commit - Optional commit changes handler (AP only) - * @priv: driver private data - * Returns: 0 on success, -1 on failure - * - * This optional handler function can be registered if the driver - * interface implementation needs to commit changes (e.g., by setting - * network interface up) at the end of initial configuration. If set, - * this handler will be called after initial setup has been completed. - */ - int (*commit)(void *priv); - /** * set_radius_acl_auth - Notification of RADIUS ACL change * @priv: Private driver interface data @@ -3999,7 +4534,10 @@ struct wpa_driver_ops { * @bssid: BSSID (Address 3) * @data: Frame body * @data_len: data length in octets - @ @no_cck: Whether CCK rates must not be used to transmit this frame + * @no_cck: Whether CCK rates must not be used to transmit this frame + * @link_id: Link ID of the specified link; -1 for non-MLO cases and for + * frames that target the MLD instead of a specific link in MLO + * cases * Returns: 0 on success, -1 on failure * * This command can be used to request the driver to transmit an action @@ -4020,7 +4558,8 @@ struct wpa_driver_ops { */ int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len, int no_cck); + const u8 *data, size_t data_len, int no_cck, + int link_id); /** * send_action_cancel_wait - Cancel action frame TX wait @@ -4037,6 +4576,8 @@ struct wpa_driver_ops { * @priv: Private driver interface data * @freq: Frequency (in MHz) of the channel * @duration: Duration in milliseconds + * @filter_addr: an additional MAC address to accept frames to (NULL for + * using only the interface address) * Returns: 0 on success, -1 on failure * * This command is used to request the driver to remain awake on the @@ -4045,6 +4586,10 @@ struct wpa_driver_ops { * Probe Request frames may also be requested to be reported by calling * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. * + * If filter_addr is provided, the driver will configure an additional + * MAC address for frame filtering, i.e., this new address will be used + * in addition to the interface address. + * * The driver may not be at the requested channel when this function * returns, i.e., the return code is only indicating whether the * request was accepted. The caller will need to wait until the @@ -4055,7 +4600,7 @@ struct wpa_driver_ops { * executed. */ int (*remain_on_channel)(void *priv, unsigned int freq, - unsigned int duration); + unsigned int duration, const u8 *filter_addr); /** * cancel_remain_on_channel - Cancel remain-on-channel operation @@ -4319,6 +4864,15 @@ struct wpa_driver_ops { int (*mlo_signal_poll)(void *priv, struct wpa_mlo_signal_info *mlo_signal_info); + /** + * setup_link_reconfig - Used to initiate Link Reconfiguration request + * for the current MLO association. + * @priv: Private driver interface data + * @info: Link reconfiguration request info + */ + int (*setup_link_reconfig)(void *priv, + struct wpa_mlo_reconfig_info *info); + /** * channel_info - Get parameters of the current operating channel * @priv: Private driver interface data @@ -4327,20 +4881,6 @@ struct wpa_driver_ops { */ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info); - /** - * set_authmode - Set authentication algorithm(s) for static WEP - * @priv: Private driver interface data - * @authmode: 1=Open System, 2=Shared Key, 3=both - * Returns: 0 on success, -1 on failure - * - * This function can be used to set authentication algorithms for AP - * mode when static WEP is used. If the driver uses user space MLME/SME - * implementation, there is no need to implement this function. - * - * DEPRECATED - use set_ap() instead - */ - int (*set_authmode)(void *priv, int authmode); - #ifdef ANDROID /** * driver_cmd - Execute driver-specific command @@ -4425,20 +4965,6 @@ struct wpa_driver_ops { int (*sta_auth)(void *priv, struct wpa_driver_sta_auth_params *params); - /** - * add_tspec - Add traffic stream - * @priv: Private driver interface data - * @addr: MAC address of the station to associate - * @tspec_ie: tspec ie buffer - * @tspec_ielen: tspec ie length - * Returns: 0 on success, -1 on failure - * - * This function adds the traffic steam for the station - * and fills the medium_time in tspec_ie. - */ - int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, - size_t tspec_ielen); - /** * add_sta_node - Add a station node in the driver * @priv: Private driver interface data @@ -5194,15 +5720,19 @@ struct wpa_driver_ops { /** * is_drv_shared - Check whether the driver interface is shared * @priv: Private driver interface data from init() - * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces + * @link_id: Link ID to match + * Returns: true if it is being used or else false. * * Checks whether the driver interface is being used by other partner * BSS(s) or not. This is used to decide whether the driver interface * needs to be deinitilized when one interface is getting deinitialized. * - * Returns: true if it is being used or else false. + * NOTE: @link_id will be used only when there is only one BSS + * present and if that single link is active. In that case, the + * link ID is matched with the active link_id to decide whether the + * driver interface is being used by other partner BSS(s). */ - bool (*is_drv_shared)(void *priv, void *bss_ctx); + bool (*is_drv_shared)(void *priv, int link_id); /** * link_sta_remove - Remove a link STA from an MLD STA @@ -5213,11 +5743,232 @@ struct wpa_driver_ops { */ int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr); + /** + * nan_flush - Flush all NAN offload services + * @priv: Private driver interface data + * Returns: 0 on success, negative value on failure + */ + int (*nan_flush)(void *priv); + + /** + * nan_publish - NAN offload for Publish() + * @priv: Private driver interface data + * @src: Source P2P device addr + * @publish_id: Publish instance to add + * @service_name: Service name + * @service_id: Service ID (6 octet value derived from service name) + * @srv_proto_type: Service protocol type + * @ssi: Service specific information or %NULL + * @elems: Information elements for Element Container attribute or %NULL + * @params: Configuration parameters + * Returns: 0 on success, negative value on failure + */ + int (*nan_publish)(void *priv, const u8 *src, int publish_id, + const char *service_name, const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_publish_params *params, + const u8 *network_id); + + /** + * nan_cancel_publish - NAN offload for CancelPublish() + * @priv: Private driver interface data + * @publish_id: Publish instance to cancel + * Returns: 0 on success, negative value on failure + */ + int (*nan_cancel_publish)(void *priv, int publish_id); + + /** + * nan_update_publish - NAN offload for UpdatePublish() + * @priv: Private driver interface data + * @ssi: Service specific information or %NULL + * Returns: 0 on success, negative value on failure + */ + int (*nan_update_publish)(void *priv, int publish_id, + const struct wpabuf *ssi); + + /** + * nan_subscribe - NAN offload for Subscribe() + * @priv: Private driver interface data + * @src: Source P2P device addr + * @subscribe_id: Subscribe instance to add + * @service_name: Service name + * @service_id: Service ID (6 octet value derived from service name) + * @srv_proto_type: Service protocol type + * @ssi: Service specific information or %NULL + * @elems: Information elements for Element Container attribute or %NULL + * @params: Configuration parameters + * Returns: 0 on success, negative value on failure + */ + int (*nan_subscribe)(void *priv, const u8 *src, int subscribe_id, + const char *service_name, const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + const struct wpabuf *elems, + struct nan_subscribe_params *params, + const u8 *network_id); + + /** + * nan_cancel_subscribe - NAN offload for CancelSubscribe() + * @priv: Private driver interface data + * @subscribe_id: Subscribe instance to cancel + * Returns: 0 on success, negative value on failure + */ + int (*nan_cancel_subscribe)(void *priv, int subscribe_id); + + /** + * can_share_drv - Check whether driver interface can be shared + * @ctx: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * @hapd: Pointer for overwriting the hapd context or %NULL + * if can't find a shared drv + * + * Checks whether the driver interface with same phy name is + * already present under the global driver which can be shared + * instead of creating a new driver interface instance. If present, + * @hapd will be overwritten with the hapd pointer which this shared + * drv's first BSS is using. This will help the caller to later call + * if_add(). + * + * Returns: true if it can be shared or else false. + */ + bool (*can_share_drv)(void *ctx, struct wpa_init_params *params, + void **hapd); + #ifdef CONFIG_TESTING_OPTIONS int (*register_frame)(void *priv, u16 type, const u8 *match, size_t match_len, bool multicast); #endif /* CONFIG_TESTING_OPTIONS */ + + /** + * get_multi_hw_info - Get multiple underlying hardware information + * (hardware IDx and supported frequency range) + * @priv: Private driver interface data + * @num_multi_hws: Variable for returning the number of returned + * hardware info data + * Returns: Pointer to allocated multiple hardware data on success + * or %NULL on failure. Caller is responsible for freeing this. + */ + struct hostapd_multi_hw_info * + (*get_multi_hw_info)(void *priv, unsigned int *num_multi_hws); + +#ifdef CONFIG_NAN + /** + * nan_start - Start NAN operation + * @priv: Private driver interface data + * @conf: NAN configuration parameters + * Returns: 0 on success, -1 on failure + * + * This command joins an existing NAN cluster or starts a new one. + */ + int (*nan_start)(void *priv, const struct nan_cluster_config *conf); + + /** + * nan_change_config - Update the NAN cluster configuration + * @priv: Private driver interface data + * @conf: NAN configuration parameters + * Returns: 0 on success, -1 on failure + * + * This command modifies the NAN cluster configuration. + */ + int (*nan_change_config)(void *priv, + const struct nan_cluster_config *conf); + + /** + * nan_stop - Stop NAN operation + * @priv: Private driver interface data + */ + void (*nan_stop)(void *priv); + + /** + * nan_config_schedule - Configure NAN schedule + * @priv: Private driver interface data + * @map_id: NAN schedule map ID + * @conf: NAN schedule configuration parameters + * Returns 0 on success, -1 on failure + * + * This command configures the local NAN schedule. It should be + * executed on NAN device interface after NAN has been started. + * The configured schedule should be valid for RX for all NAN + * activities (management and data). + * + * For devices that support multiple concurrent NAN radios, this + * callback should be called for each radio with the corresponding + * %map_id. + * + * If previous configuration exists, it is replaced with the new + * one. To delete previous schedule, set %conf.num_channels = 0. + */ + int (*nan_config_schedule)(void *priv, u8 map_id, + struct nan_schedule_config *conf); + + /** + * nan_config_peer_schedule - Configure NAN peer schedule + * @priv: Private driver interface data + * @peer: Peer's NAN device address + * @cdw: Peer's committed DW. + * @sequence_id: Peer's schedule sequence ID + * @max_chan_switch_time: Maximum channel switch time in TUs + * @ulw: Peer's unaligned window attributes or %NULL + * @sched: NAN peer schedule configuration parameters + * Returns 0 on success, -1 on failure + * + * This command configures peer's NAN schedule. To remove previous + * schedule for a given %map_id, set %sched.num_channels = 0. + * %ulw attributes are used to provide the initial information about + * peer's unaligned schedule. Further updates to ULW should be tracked + * internally by the device/driver. + */ + int (*nan_config_peer_schedule)(void *priv, const u8 *peer, + u16 cdw, u8 sequence_id, + u16 max_chan_switch_time, + const struct wpabuf *ulw, + struct nan_peer_schedule_config *sched); +#endif /* CONFIG_NAN */ + +#ifdef CONFIG_PR + /** + * pd_start - Create interface for Proximity Detection + * @priv: Private driver interface data + * @addr: Requested MAC address + * @pd_addr: MAC address of the interface + * Returns: 0 on success, -1 on failure + */ + int (*pd_start)(void *priv, const u8 *addr, u8 *pd_addr); + + /** + * pd_stop - Stop interface for Proximity Detection + * @priv: Private driver interface data + */ + void (*pd_stop)(void *priv); + + /** + * start_peer_measurement - Start peer measurement (FTM ranging) + * @priv: Private driver interface data + * @peer_addr: Peer MAC address + * @freq: Operating frequency in MHz + * @channel: Operating channel number + * @bw: Channel bandwidth in MHz + * @params: Ranging parameters (EDCA/NTB specific) + * Returns: 0 on success, -1 on failure + * + * This function triggers peer measurement (FTM ranging) after + * successful PASN authentication for proximity ranging. + */ + int (*start_peer_measurement)(void *priv, const u8 *peer_addr, + int freq, u8 channel, int bw, + struct pr_pasn_ranging_params *params); + + /** + * stop_peer_measurement - Stop peer measurement and destroy ranging socket + * @priv: Private driver interface data + * + * Unregisters the ranging socket from eloop and frees all associated + * resources. Safe to call when no ranging session is active (no-op). + */ + void (*stop_peer_measurement)(void *priv); +#endif /* CONFIG_PR */ }; /** @@ -5845,6 +6596,87 @@ enum wpa_event_type { * EVENT_LINK_RECONFIG - Notification that AP links removed */ EVENT_LINK_RECONFIG, + + /** + * EVENT_MLD_INTERFACE_FREED - Notification of AP MLD interface removal + */ + EVENT_MLD_INTERFACE_FREED, + + /** + * EVENT_SETUP_LINK_RECONFIG - Notification that new AP links added + */ + EVENT_SETUP_LINK_RECONFIG, + + /** + * EVENT_NAN_CLUSTER_JOIN - Notification of a new cluster having been + * joined or started. + * + * This event is used to notify wpa_supplicant that a NAN cluster has + * been joined or started. The event data includes the NAN cluster ID + * and a boolean indicating whether a new cluster was started or an + * existing cluster was joined. + * + * Described in wpa_event_data.nan_cluster_join_info. + */ + EVENT_NAN_CLUSTER_JOIN, + + /** + * EVENT_NAN_NEXT_DW - Notification of NAN next Discovery Window + * + * This event is used to notify wpa_supplicant that the device/driver + * is ready for the next Discovery Window (DW) frames. It may be used + * to trigger transmission of multicast SDFs (active subscribe and + * unsolicited publish). + * The event data includes the DW frequency. + */ + EVENT_NAN_NEXT_DW, + + /** + * EVENT_INCUMBT_SIG_INTF_DETECTED - Notification of incumbent signal + * interference detection. + */ + EVENT_INCUMBT_SIG_INTF_DETECTED, + + /** + * EVENT_NAN_SCHED_UPDATE_DONE - NAN schedule update completed + * + * This event is used to notify wpa_supplicant that a NAN schedule + * update has been completed by the driver/firmware. + */ + EVENT_NAN_SCHED_UPDATE_DONE, + + /** + * EVENT_NAN_ULW_UPDATE - NAN ULW blob update + * + * This event is used to notify wpa_supplicant that the device ULW blob + * has changed and can be attached to outgoing NAN frames. + */ + EVENT_NAN_ULW_UPDATE, + + /** + * EVENT_NAN_CHAN_EVACUATION - Notification of NAN channel evacuation + * + * This event is used to notify wpa_supplicant that the device/driver + * evacuated one of the channels used for NAN operation, e.g., due to + * resource constraints (not enough resources to support NAN and other + * activities). + */ + EVENT_NAN_CHAN_EVACUATION, + + /** + * EVENT_PEER_MEASUREMENT_RESULT - Ranging measurement result + * + * This event is used to indicate a ranging measurement result. + */ + EVENT_PEER_MEASUREMENT_RESULT, + + /** + * EVENT_PEER_MEASUREMENT_COMPLETE - Ranging session fully done + * + * This event is used to notify that the entire peer + * measurement session identified by the cookie has ended. + */ + EVENT_PEER_MEASUREMENT_COMPLETE, }; @@ -6107,6 +6939,13 @@ union wpa_event_data { * -1 if MLO is not used */ int assoc_link_id; + + /** + * assoc_encrypted - Indicate that the (Re)Association + * Request/Response frames are transmitted encrypted over the + * air. + */ + bool assoc_encrypted; } assoc_info; /** @@ -6271,6 +7110,8 @@ union wpa_event_data { u16 status_code; const u8 *ies; size_t ies_len; + const u8 *frame_body; + size_t frame_body_len; } auth; /** @@ -6352,6 +7193,7 @@ union wpa_event_data { const u8 *bssid; const u8 *addr; int wds; + int link_id; } rx_from_unknown; /** @@ -6832,6 +7674,132 @@ union wpa_event_data { u8 valid_links; struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS]; } t2l_map_info; + + /** + * struct reconfig_info - Data for EVENT_SETUP_LINK_RECONFIG + */ + struct reconfig_info { + u16 added_links; + u8 count; + const u8 *status_list; + const u8 *resp_ie; /* Starting from Group Key Data */ + size_t resp_ie_len; + } reconfig_info; + + struct nan_cluster_join_info { + const u8 *bssid; + bool new_cluster; + } nan_cluster_join_info; + + struct nan_next_dw_info { + int freq; + } nan_next_dw_info; + + /** + * Data for EVENT_INCUMBT_SIG_INTF + */ + struct incumbt_sig_intf_event { + int freq; + enum chan_width chan_width; + int cf1; + int cf2; + u32 chan_bw_interference_bitmap; + int link_id; + } incumbt_sig_intf_event; + + /** + * struct nan_sched_update_done_info - Data for EVENT_NAN_SCHED_UPDATE_DONE + * @success: Indicates whether the NAN schedule update was successful + */ + struct nan_sched_update_done_info { + bool success; + } nan_sched_update_done_info; + + /** + * struct nan_ulw_update_info - Data for EVENT_NAN_ULW_UPDATE + * @ulw: Pointer to ULW blob data, containing one or more Unaligned + * Schedule attributes as defined in the Wi-Fi Aware spec v4.0, + * Table 109 (Unaligned Schedule attribute format), including + * attribute header for each attribute. + * @ulw_len: ULW blob length in bytes + */ + struct nan_ulw_update_info { + const u8 *ulw; + size_t ulw_len; + } nan_ulw_update_info; + + /** + * struct nan_chan_evacuation_info - Data for EVENT_NAN_CHAN_EVACUATION + * @freq: Control frequency of the evacuated channel in MHz + */ + struct nan_chan_evacuation_info { + int freq; + } nan_chan_evacuation_info; + + /** + * struct peer_measurement_result - Ranging measurement result for + * EVENT_PEER_MEASUREMENT_RESULT + */ + struct peer_measurement_result { + u8 addr[ETH_ALEN]; + u32 status; + u64 host_time; + u64 ap_tsf; + bool final; + u64 cookie; + + /** + * struct peer_measurement_ftm_result - + * FTM-specific measurement results + */ + struct peer_measurement_ftm_result { + bool fail; + u32 fail_reason; + u32 burst_index; + u32 num_ftmr_attempts; + u32 num_ftmr_successes; + u32 busy_retry_time; + u32 num_bursts_exp; + u32 burst_duration; + u32 ftms_per_burst; + s32 rssi_avg; + s32 rssi_spread; + s64 rtt_avg; + u64 rtt_variance; + u64 rtt_spread; + s64 dist_avg; + u64 dist_variance; + u64 dist_spread; + const u8 *lci; + size_t lci_len; + const u8 *civicloc; + size_t civicloc_len; + /* Additional FTM parameters */ + u32 burst_period; + u32 tx_ltf_repetition_count; + u32 rx_ltf_repetition_count; + u32 max_time_between_measurements; + u32 min_time_between_measurements; + u32 num_tx_spatial_streams; + u32 num_rx_spatial_streams; + u32 nominal_time; + u8 availability_window; + u32 band_width; + u32 preamble; + bool is_delayed_lmr; + /* Flag indicating if FTM data is present */ + bool has_data; + } ftm; + + } peer_measurement_result; + + /** + * struct peer_measurement_complete - Ranging session complete for + * EVENT_PEER_MEASUREMENT_COMPLETE + */ + struct peer_measurement_complete { + u64 cookie; + } peer_measurement_complete; }; /** @@ -6934,6 +7902,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 eht_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, @@ -6955,9 +7925,6 @@ extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ /* driver_nl80211.c */ extern const struct wpa_driver_ops wpa_driver_nl80211_ops; #endif /* CONFIG_DRIVER_NL80211 */ -#ifdef CONFIG_DRIVER_HOSTAP -extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ -#endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_BSD extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ @@ -6983,10 +7950,6 @@ extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops; /* driver_roboswitch.c */ extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; #endif /* CONFIG_DRIVER_ROBOSWITCH */ -#ifdef CONFIG_DRIVER_ATHEROS -/* driver_atheros.c */ -extern const struct wpa_driver_ops wpa_driver_atheros_ops; -#endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_NONE extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ #endif /* CONFIG_DRIVER_NONE */ diff --git a/contrib/wpa/src/drivers/driver_atheros.c b/contrib/wpa/src/drivers/driver_atheros.c deleted file mode 100644 index 71863306afad..000000000000 --- a/contrib/wpa/src/drivers/driver_atheros.c +++ /dev/null @@ -1,2286 +0,0 @@ -/* - * hostapd / Driver interaction with Atheros driver - * Copyright (c) 2004, Sam Leffler - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2005-2007, Jouni Malinen - * Copyright (c) 2009, Atheros Communications - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include - -#include "common.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "l2_packet/l2_packet.h" - -#include "common.h" -#ifndef _BYTE_ORDER -#ifdef WORDS_BIGENDIAN -#define _BYTE_ORDER _BIG_ENDIAN -#else -#define _BYTE_ORDER _LITTLE_ENDIAN -#endif -#endif /* _BYTE_ORDER */ - -/* - * Note, the ATH_WPS_IE setting must match with the driver build.. If the - * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. - */ -#define ATH_WPS_IE - -#include "ieee80211_external.h" - -/* Avoid conflicting definition from the driver header files with - * common/wpa_common.h */ -#undef WPA_OUI_TYPE - - -#ifdef CONFIG_WPS -#include -#endif /* CONFIG_WPS */ - -#ifndef ETH_P_80211_RAW -#define ETH_P_80211_RAW 0x0019 -#endif - -#include "linux_wext.h" - -#include "driver.h" -#include "eloop.h" -#include "priv_netlink.h" -#include "l2_packet/l2_packet.h" -#include "common/ieee802_11_defs.h" -#include "netlink.h" -#include "linux_ioctl.h" - - -struct atheros_driver_data { - struct hostapd_data *hapd; /* back pointer */ - - char iface[IFNAMSIZ + 1]; - int ifindex; - struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ - struct l2_packet_data *sock_recv; /* raw packet recv socket */ - int ioctl_sock; /* socket for ioctl() use */ - struct netlink_data *netlink; - int we_version; - int fils_en; /* FILS enable/disable in driver */ - u8 acct_mac[ETH_ALEN]; - struct hostap_sta_driver_data acct_data; - - struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ - struct wpabuf *wpa_ie; - struct wpabuf *wps_beacon_ie; - struct wpabuf *wps_probe_resp_ie; - u8 own_addr[ETH_ALEN]; -}; - -static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason_code, int link_id); -static int atheros_set_privacy(void *priv, int enabled); - -static const char * athr_get_ioctl_name(int op) -{ - switch (op) { - case IEEE80211_IOCTL_SETPARAM: - return "SETPARAM"; - case IEEE80211_IOCTL_GETPARAM: - return "GETPARAM"; - case IEEE80211_IOCTL_SETKEY: - return "SETKEY"; - case IEEE80211_IOCTL_SETWMMPARAMS: - return "SETWMMPARAMS"; - case IEEE80211_IOCTL_DELKEY: - return "DELKEY"; - case IEEE80211_IOCTL_GETWMMPARAMS: - return "GETWMMPARAMS"; - case IEEE80211_IOCTL_SETMLME: - return "SETMLME"; - case IEEE80211_IOCTL_GETCHANINFO: - return "GETCHANINFO"; - case IEEE80211_IOCTL_SETOPTIE: - return "SETOPTIE"; - case IEEE80211_IOCTL_GETOPTIE: - return "GETOPTIE"; - case IEEE80211_IOCTL_ADDMAC: - return "ADDMAC"; - case IEEE80211_IOCTL_DELMAC: - return "DELMAC"; - case IEEE80211_IOCTL_GETCHANLIST: - return "GETCHANLIST"; - case IEEE80211_IOCTL_SETCHANLIST: - return "SETCHANLIST"; - case IEEE80211_IOCTL_KICKMAC: - return "KICKMAC"; - case IEEE80211_IOCTL_CHANSWITCH: - return "CHANSWITCH"; - case IEEE80211_IOCTL_GETMODE: - return "GETMODE"; - case IEEE80211_IOCTL_SETMODE: - return "SETMODE"; - case IEEE80211_IOCTL_GET_APPIEBUF: - return "GET_APPIEBUF"; - case IEEE80211_IOCTL_SET_APPIEBUF: - return "SET_APPIEBUF"; - case IEEE80211_IOCTL_SET_ACPARAMS: - return "SET_ACPARAMS"; - case IEEE80211_IOCTL_FILTERFRAME: - return "FILTERFRAME"; - case IEEE80211_IOCTL_SET_RTPARAMS: - return "SET_RTPARAMS"; - case IEEE80211_IOCTL_SET_MEDENYENTRY: - return "SET_MEDENYENTRY"; - case IEEE80211_IOCTL_GET_MACADDR: - return "GET_MACADDR"; - case IEEE80211_IOCTL_SET_HBRPARAMS: - return "SET_HBRPARAMS"; - case IEEE80211_IOCTL_SET_RXTIMEOUT: - return "SET_RXTIMEOUT"; - case IEEE80211_IOCTL_STA_STATS: - return "STA_STATS"; - case IEEE80211_IOCTL_GETWPAIE: - return "GETWPAIE"; - default: - return "??"; - } -} - - -static const char * athr_get_param_name(int op) -{ - switch (op) { - case IEEE80211_IOC_MCASTCIPHER: - return "MCASTCIPHER"; - case IEEE80211_PARAM_MCASTKEYLEN: - return "MCASTKEYLEN"; - case IEEE80211_PARAM_UCASTCIPHERS: - return "UCASTCIPHERS"; - case IEEE80211_PARAM_KEYMGTALGS: - return "KEYMGTALGS"; - case IEEE80211_PARAM_RSNCAPS: - return "RSNCAPS"; - case IEEE80211_PARAM_WPA: - return "WPA"; - case IEEE80211_PARAM_AUTHMODE: - return "AUTHMODE"; - case IEEE80211_PARAM_PRIVACY: - return "PRIVACY"; - case IEEE80211_PARAM_COUNTERMEASURES: - return "COUNTERMEASURES"; - default: - return "??"; - } -} - - -#ifdef CONFIG_FILS -static int -get80211param(struct atheros_driver_data *drv, int op, int *data) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.mode = op; - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0) - return -1; - - *data = iwr.u.mode; - return 0; -} -#endif /* CONFIG_FILS */ - - -static int -set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) -{ - struct iwreq iwr; - int do_inline = len < IFNAMSIZ; - - /* Certain ioctls must use the non-inlined method */ - if (op == IEEE80211_IOCTL_SET_APPIEBUF || - op == IEEE80211_IOCTL_FILTERFRAME) - do_inline = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - if (do_inline) { - /* - * Argument data fits inline; put it there. - */ - os_memcpy(iwr.u.name, data, len); - } else { - /* - * Argument data too big for inline transfer; setup a - * parameter block instead; the kernel will transfer - * the data for the driver. - */ - iwr.u.data.pointer = data; - iwr.u.data.length = len; - } - - if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x " - "(%s) len=%d failed: %d (%s)", - __func__, drv->iface, op, - athr_get_ioctl_name(op), - len, errno, strerror(errno)); - return -1; - } - return 0; -} - -static int -set80211param(struct atheros_driver_data *drv, int op, int arg) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.mode = op; - os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg)); - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { - wpa_printf(MSG_INFO, - "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s", - __func__, drv->iface, op, athr_get_param_name(op), - arg, strerror(errno)); - return -1; - } - return 0; -} - -#ifndef CONFIG_NO_STDOUT_DEBUG -static const char * -ether_sprintf(const u8 *addr) -{ - static char buf[sizeof(MACSTR)]; - - if (addr != NULL) - os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); - else - os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0); - return buf; -} -#endif /* CONFIG_NO_STDOUT_DEBUG */ - -/* - * Configure WPA parameters. - */ -static int -atheros_configure_wpa(struct atheros_driver_data *drv, - struct wpa_bss_params *params) -{ - int v; - - switch (params->wpa_group) { - case WPA_CIPHER_CCMP: - v = IEEE80211_CIPHER_AES_CCM; - break; -#ifdef ATH_GCM_SUPPORT - case WPA_CIPHER_CCMP_256: - v = IEEE80211_CIPHER_AES_CCM_256; - break; - case WPA_CIPHER_GCMP: - v = IEEE80211_CIPHER_AES_GCM; - break; - case WPA_CIPHER_GCMP_256: - v = IEEE80211_CIPHER_AES_GCM_256; - break; -#endif /* ATH_GCM_SUPPORT */ - case WPA_CIPHER_TKIP: - v = IEEE80211_CIPHER_TKIP; - break; - case WPA_CIPHER_WEP104: - v = IEEE80211_CIPHER_WEP; - break; - case WPA_CIPHER_WEP40: - v = IEEE80211_CIPHER_WEP; - break; - case WPA_CIPHER_NONE: - v = IEEE80211_CIPHER_NONE; - break; - default: - wpa_printf(MSG_ERROR, "Unknown group key cipher %u", - params->wpa_group); - return -1; - } - wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); - if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { - wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v); - return -1; - } - if (v == IEEE80211_CIPHER_WEP) { - /* key length is done only for specific ciphers */ - v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); - if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { - wpa_printf(MSG_INFO, - "Unable to set group key length to %u", v); - return -1; - } - } - - v = 0; - if (params->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_CCMP_256) - v |= 1<wpa_pairwise & WPA_CIPHER_GCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_GCMP_256) - v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<wpa_key_mgmt); - if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, - params->wpa_key_mgmt)) { - wpa_printf(MSG_INFO, - "Unable to set key management algorithms to 0x%x", - params->wpa_key_mgmt); - return -1; - } - - v = 0; - if (params->rsn_preauth) - v |= BIT(0); - if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { - v |= BIT(7); - if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) - v |= BIT(6); - } - - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); - if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { - wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", - v); - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); - if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { - wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); - return -1; - } - return 0; -} - -static int -atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) -{ - struct atheros_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); - - if (!params->enabled) { - /* XXX restore state */ - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - IEEE80211_AUTH_AUTO) < 0) - return -1; - /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ - return atheros_set_privacy(drv, 0); - } - if (!params->wpa && !params->ieee802_1x) { - wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (params->wpa && atheros_configure_wpa(drv, params) != 0) { - wpa_printf(MSG_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - - return 0; -} - -static int -atheros_set_privacy(void *priv, int enabled) -{ - struct atheros_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); -} - -static int -atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", - __func__, ether_sprintf(addr), authorized); - - if (authorized) - mlme.im_op = IEEE80211_MLME_AUTHORIZE; - else - mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; - mlme.im_reason = 0; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, - __func__, authorized ? "" : "un", MAC2STR(addr)); - } - - return ret; -} - -static int -atheros_sta_set_flags(void *priv, const u8 *addr, - unsigned int total_flags, unsigned int flags_or, - unsigned int flags_and) -{ - /* For now, only support setting Authorized flag */ - if (flags_or & WPA_STA_AUTHORIZED) - return atheros_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WPA_STA_AUTHORIZED)) - return atheros_set_sta_authorized(priv, addr, 0); - return 0; -} - -static int -atheros_del_key(void *priv, const u8 *addr, int key_idx) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_del_key wk; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", - __func__, ether_sprintf(addr), key_idx); - - os_memset(&wk, 0, sizeof(wk)); - if (addr != NULL) { - os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; - } else { - wk.idk_keyix = key_idx; - } - - ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" - " key_idx %d)", __func__, ether_sprintf(addr), - key_idx); - } - - return ret; -} - -static int -atheros_set_key(void *priv, struct wpa_driver_set_key_params *params) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - int ret; - enum wpa_alg alg = params->alg; - const u8 *addr = params->addr; - int key_idx = params->key_idx; - int set_tx = params->set_tx; - const u8 *key = params->key; - size_t key_len = params->key_len; - - if (alg == WPA_ALG_NONE) - return atheros_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - switch (alg) { - case WPA_ALG_WEP: - cipher = IEEE80211_CIPHER_WEP; - break; - case WPA_ALG_TKIP: - cipher = IEEE80211_CIPHER_TKIP; - break; - case WPA_ALG_CCMP: - cipher = IEEE80211_CIPHER_AES_CCM; - break; -#ifdef ATH_GCM_SUPPORT - case WPA_ALG_CCMP_256: - cipher = IEEE80211_CIPHER_AES_CCM_256; - break; - case WPA_ALG_GCMP: - cipher = IEEE80211_CIPHER_AES_GCM; - break; - case WPA_ALG_GCMP_256: - cipher = IEEE80211_CIPHER_AES_GCM_256; - break; -#endif /* ATH_GCM_SUPPORT */ - case WPA_ALG_BIP_CMAC_128: - cipher = IEEE80211_CIPHER_AES_CMAC; - break; -#ifdef ATH_GCM_SUPPORT - case WPA_ALG_BIP_CMAC_256: - cipher = IEEE80211_CIPHER_AES_CMAC_256; - break; - case WPA_ALG_BIP_GMAC_128: - cipher = IEEE80211_CIPHER_AES_GMAC; - break; - case WPA_ALG_BIP_GMAC_256: - cipher = IEEE80211_CIPHER_AES_GMAC_256; - break; -#endif /* ATH_GCM_SUPPORT */ - default: - wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d", - __func__, alg); - return -1; - } - - if (key_len > sizeof(wk.ik_keydata)) { - wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__, - (unsigned long) key_len); - return -3; - } - - os_memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; - wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL || is_broadcast_ether_addr(addr)) { - os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); - wk.ik_keyix = key_idx; - if (set_tx) - wk.ik_flags |= IEEE80211_KEY_DEFAULT; - } else { - os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - wk.ik_keyix = IEEE80211_KEYIX_NONE; - } - wk.ik_keylen = key_len; - os_memcpy(wk.ik_keydata, key, key_len); - - ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" - " key_idx %d alg %d key_len %lu set_tx %d)", - __func__, ether_sprintf(wk.ik_macaddr), key_idx, - alg, (unsigned long) key_len, set_tx); - } - - return ret; -} - - -static int -atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, - int link_id, u8 *seq) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_key wk; - - wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", - __func__, ether_sprintf(addr), idx); - - os_memset(&wk, 0, sizeof(wk)); - if (addr == NULL) - os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); - else - os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - wk.ik_keyix = idx; - - if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { - wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " - "(addr " MACSTR " key_idx %d)", - __func__, MAC2STR(wk.ik_macaddr), idx); - return -1; - } - -#ifdef WORDS_BIGENDIAN - { - /* - * wk.ik_keytsc is in host byte order (big endian), need to - * swap it to match with the byte order used in WPA. - */ - int i; -#ifndef WPA_KEY_RSC_LEN -#define WPA_KEY_RSC_LEN 8 -#endif - u8 tmp[WPA_KEY_RSC_LEN]; - os_memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); - for (i = 0; i < WPA_KEY_RSC_LEN; i++) { - seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; - } - } -#else /* WORDS_BIGENDIAN */ - os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); -#endif /* WORDS_BIGENDIAN */ - return 0; -} - - -static int -atheros_flush(void *priv, int link_id) -{ - u8 allsta[IEEE80211_ADDR_LEN]; - os_memset(allsta, 0xff, IEEE80211_ADDR_LEN); - return atheros_sta_deauth(priv, NULL, allsta, - IEEE80211_REASON_AUTH_LEAVE, -1); -} - - -static int -atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, - const u8 *addr) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_sta_stats stats; - - os_memset(data, 0, sizeof(*data)); - - /* - * Fetch statistics for station from the system. - */ - os_memset(&stats, 0, sizeof(stats)); - os_memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, - &stats, sizeof(stats))) { - wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " - MACSTR ")", __func__, MAC2STR(addr)); - if (ether_addr_equal(addr, drv->acct_mac)) { - os_memcpy(data, &drv->acct_data, sizeof(*data)); - return 0; - } - - wpa_printf(MSG_INFO, - "Failed to get station stats information element"); - return -1; - } - - data->rx_packets = stats.is_stats.ns_rx_data; - data->rx_bytes = stats.is_stats.ns_rx_bytes; - data->tx_packets = stats.is_stats.ns_tx_data; - data->tx_bytes = stats.is_stats.ns_tx_bytes; - return 0; -} - - -static int -atheros_sta_clear_stats(void *priv, const u8 *addr) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); - - mlme.im_op = IEEE80211_MLME_CLEAR_STATS; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, - sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " - MACSTR ")", __func__, MAC2STR(addr)); - } - - return ret; -} - - -static int -atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) -{ - struct atheros_driver_data *drv = priv; - u8 buf[512]; - struct ieee80211req_getset_appiebuf *app_ie; - - wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, - (unsigned long) ie_len); - wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); - - wpabuf_free(drv->wpa_ie); - if (ie) - drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); - else - drv->wpa_ie = NULL; - - app_ie = (struct ieee80211req_getset_appiebuf *) buf; - if (ie) - os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); - app_ie->app_buflen = ie_len; - - app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; - - /* append WPS IE for Beacon */ - if (drv->wps_beacon_ie != NULL) { - os_memcpy(&(app_ie->app_buf[ie_len]), - wpabuf_head(drv->wps_beacon_ie), - wpabuf_len(drv->wps_beacon_ie)); - app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); - } - wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", - app_ie->app_buf, app_ie->app_buflen); - set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - app_ie->app_buflen); - - /* append WPS IE for Probe Response */ - app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; - if (drv->wps_probe_resp_ie != NULL) { - os_memcpy(&(app_ie->app_buf[ie_len]), - wpabuf_head(drv->wps_probe_resp_ie), - wpabuf_len(drv->wps_probe_resp_ie)); - app_ie->app_buflen = ie_len + - wpabuf_len(drv->wps_probe_resp_ie); - } else - app_ie->app_buflen = ie_len; - wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", - app_ie->app_buf, app_ie->app_buflen); - set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - app_ie->app_buflen); - return 0; -} - -static int -atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason_code, int link_id) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", - __func__, ether_sprintf(addr), reason_code); - - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR - " reason %d)", - __func__, MAC2STR(addr), reason_code); - } - - return ret; -} - -static int -atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason_code) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", - __func__, ether_sprintf(addr), reason_code); - - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " - MACSTR " reason %d)", - __func__, MAC2STR(addr), reason_code); - } - - return ret; -} - -static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, - u8 qos_map_set_len) -{ -#ifdef CONFIG_ATHEROS_QOS_MAP - struct atheros_driver_data *drv = ctx; - struct ieee80211req_athdbg req; - struct ieee80211_qos_map *qos_map = &req.data.qos_map; - struct iwreq iwr; - int i, up_start; - - if (qos_map_set_len < 16 || qos_map_set_len > 58 || - qos_map_set_len & 1) { - wpa_printf(MSG_ERROR, "Invalid QoS Map"); - return -1; - } else { - os_memset(&req, 0, sizeof(struct ieee80211req_athdbg)); - req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); - iwr.u.data.pointer = (void *) &req; - iwr.u.data.length = sizeof(struct ieee80211req_athdbg); - } - - qos_map->valid = 1; - qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; - if (qos_map->num_dscp_except) { - for (i = 0; i < qos_map->num_dscp_except; i++) { - qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; - qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; - } - } - - up_start = qos_map_set_len - 16; - for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { - qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; - qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; - } - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { - wpa_printf(MSG_ERROR, - "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s", - __func__, drv->iface, strerror(errno)); - return -1; - } -#endif /* CONFIG_ATHEROS_QOS_MAP */ - - return 0; -} - - -static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 fc, stype; - int ielen; - const u8 *iebuf; - - if (len < IEEE80211_HDRLEN) - return; - - mgmt = (const struct ieee80211_mgmt *) buf; - - fc = le_to_host16(mgmt->frame_control); - - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) - return; - - stype = WLAN_FC_GET_STYPE(fc); - - wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, - (int) len); - - if (stype == WLAN_FC_STYPE_PROBE_REQ) { - if (len < IEEE80211_HDRLEN) - return; - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = buf + IEEE80211_HDRLEN; - event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN; - wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); - return; - } - - if (stype == WLAN_FC_STYPE_ACTION && - (ether_addr_equal(drv->own_addr, mgmt->bssid) || - is_broadcast_ether_addr(mgmt->bssid))) { - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); - return; - } - - if (!ether_addr_equal(drv->own_addr, mgmt->bssid)) { - wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", - __func__); - return; - } - - switch (stype) { - case WLAN_FC_STYPE_ASSOC_REQ: - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)) - break; - ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); - iebuf = mgmt->u.assoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0, - NULL, -1, 0); - break; - case WLAN_FC_STYPE_REASSOC_REQ: - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)) - break; - ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); - iebuf = mgmt->u.reassoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0, - NULL, -1, 1); - break; - case WLAN_FC_STYPE_AUTH: - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) - break; - os_memset(&event, 0, sizeof(event)); - if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) { - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); - break; - } - os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); - os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); - event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); - event.auth.status_code = - le_to_host16(mgmt->u.auth.status_code); - event.auth.auth_transaction = - le_to_host16(mgmt->u.auth.auth_transaction); - event.auth.ies = mgmt->u.auth.variable; - event.auth.ies_len = len - IEEE80211_HDRLEN - - sizeof(mgmt->u.auth); - wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); - break; - default: - break; - } -} - - -static int atheros_receive_pkt(struct atheros_driver_data *drv) -{ - int ret = 0; - struct ieee80211req_set_filter filt; - - wpa_printf(MSG_DEBUG, "%s Enter", __func__); - filt.app_filterype = 0; -#ifdef CONFIG_WPS - filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; -#endif /* CONFIG_WPS */ - filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | - IEEE80211_FILTER_TYPE_AUTH | - IEEE80211_FILTER_TYPE_ACTION); -#ifdef CONFIG_WNM - filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; -#endif /* CONFIG_WNM */ -#ifdef CONFIG_HS20 - filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; -#endif /* CONFIG_HS20 */ - if (filt.app_filterype) { - ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, - sizeof(struct ieee80211req_set_filter)); - if (ret) - return ret; - } - -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) - drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, - atheros_raw_receive, drv, 1); - if (drv->sock_raw == NULL) - return -1; -#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */ - return ret; -} - -static int atheros_reset_appfilter(struct atheros_driver_data *drv) -{ - struct ieee80211req_set_filter filt; - filt.app_filterype = 0; - return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, - sizeof(struct ieee80211req_set_filter)); -} - -#ifdef CONFIG_WPS -static int -atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) -{ - struct atheros_driver_data *drv = priv; - u8 buf[512]; - struct ieee80211req_getset_appiebuf *beac_ie; - - wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, - (unsigned long) len, frametype); - wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); - - beac_ie = (struct ieee80211req_getset_appiebuf *) buf; - beac_ie->app_frmtype = frametype; - beac_ie->app_buflen = len; - if (ie) - os_memcpy(&(beac_ie->app_buf[0]), ie, len); - - /* append the WPA/RSN IE if it is set already */ - if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || - (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && - (drv->wpa_ie != NULL)) { - wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", - drv->wpa_ie); - os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), - wpabuf_len(drv->wpa_ie)); - beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); - } - - wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", - beac_ie->app_buf, beac_ie->app_buflen); - return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - beac_ie->app_buflen); -} - -static int -atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - struct atheros_driver_data *drv = priv; - - wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); - wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", - proberesp); - wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", - assocresp); - wpabuf_free(drv->wps_beacon_ie); - drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; - wpabuf_free(drv->wps_probe_resp_ie); - drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; - - atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, - assocresp ? wpabuf_len(assocresp) : 0, - IEEE80211_APPIE_FRAME_ASSOC_RESP); - if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, - beacon ? wpabuf_len(beacon) : 0, - IEEE80211_APPIE_FRAME_BEACON)) - return -1; - return atheros_set_wps_ie(priv, - proberesp ? wpabuf_head(proberesp) : NULL, - proberesp ? wpabuf_len(proberesp): 0, - IEEE80211_APPIE_FRAME_PROBE_RESP); -} -#else /* CONFIG_WPS */ -#define atheros_set_ap_wps_ie NULL -#endif /* CONFIG_WPS */ - -static int -atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", - __func__, ether_sprintf(params->addr), params->status); - -#ifdef CONFIG_FILS - /* Copy FILS AAD parameters if the driver supports FILS */ - if (params->fils_auth && drv->fils_en) { - wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS", - __func__); - os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce, - IEEE80211_FILS_NONCE_LEN); - os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce, - IEEE80211_FILS_NONCE_LEN); - os_memcpy(mlme.fils_aad.kek, params->fils_kek, - IEEE80211_MAX_WPA_KEK_LEN); - mlme.fils_aad.kek_len = params->fils_kek_len; - mlme.im_op = IEEE80211_MLME_AUTH_FILS; - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", - mlme.fils_aad.ANonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: SNonce", - mlme.fils_aad.SNonce, FILS_NONCE_LEN); - wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", - mlme.fils_aad.kek, mlme.fils_aad.kek_len); - } else { - mlme.im_op = IEEE80211_MLME_AUTH; - } -#else /* CONFIG_FILS */ - mlme.im_op = IEEE80211_MLME_AUTH; -#endif /* CONFIG_FILS */ - - mlme.im_reason = params->status; - mlme.im_seq = params->seq; - os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN); - mlme.im_optie_len = params->len; - if (params->len) { - if (params->len < IEEE80211_MAX_OPT_IE) { - os_memcpy(mlme.im_optie, params->ie, params->len); - } else { - wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " - "opt_ie STA (addr " MACSTR " reason %d, " - "ie_len %d)", - __func__, MAC2STR(params->addr), - params->status, (int) params->len); - return -1; - } - } - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR - " reason %d)", - __func__, MAC2STR(params->addr), params->status); - } - return ret; -} - -static int -atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, - int reassoc, u16 status_code, const u8 *ie, size_t len) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", - __func__, ether_sprintf(addr), status_code, reassoc); - - if (reassoc) - mlme.im_op = IEEE80211_MLME_REASSOC; - else - mlme.im_op = IEEE80211_MLME_ASSOC; - mlme.im_reason = status_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - mlme.im_optie_len = len; - if (len) { - if (len < IEEE80211_MAX_OPT_IE) { - os_memcpy(mlme.im_optie, ie, len); - } else { - wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " - "opt_ie STA (addr " MACSTR " reason %d, " - "ie_len %d)", - __func__, MAC2STR(addr), status_code, - (int) len); - return -1; - } - } - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR - " reason %d)", - __func__, MAC2STR(addr), status_code); - } - return ret; -} - - -static void -atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct ieee80211req_wpaie ie; - int ielen = 0; - u8 *iebuf = NULL; - - /* - * Fetch negotiated WPA/RSN parameters from the system. - */ - os_memset(&ie, 0, sizeof(ie)); - os_memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { - /* - * See ATH_WPS_IE comment in the beginning of the file for a - * possible cause for the failure.. - */ - wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", - __func__, strerror(errno)); - goto no_ie; - } - wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE", - ie.wpa_ie, IEEE80211_MAX_OPT_IE); - wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", - ie.rsn_ie, IEEE80211_MAX_OPT_IE); -#ifdef ATH_WPS_IE - wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", - ie.wps_ie, IEEE80211_MAX_OPT_IE); -#endif /* ATH_WPS_IE */ - iebuf = ie.wpa_ie; - /* atheros seems to return some random data if WPA/RSN IE is not set. - * Assume the IE was not included if the IE type is unknown. */ - if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) - iebuf[1] = 0; - if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { - /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not - * set. This is needed for WPA2. */ - iebuf = ie.rsn_ie; - if (iebuf[0] != WLAN_EID_RSN) - iebuf[1] = 0; - } - - ielen = iebuf[1]; - -#ifdef ATH_WPS_IE - /* if WPS IE is present, preference is given to WPS */ - if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) { - iebuf = ie.wps_ie; - ielen = ie.wps_ie[1]; - } -#endif /* ATH_WPS_IE */ - - if (ielen == 0) - iebuf = NULL; - else - ielen += 2; - -no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0); - - if (ether_addr_equal(addr, drv->acct_mac)) { - /* Cached accounting data is not valid anymore. */ - os_memset(drv->acct_mac, 0, ETH_ALEN); - os_memset(&drv->acct_data, 0, sizeof(drv->acct_data)); - } -} - -static void -atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, - char *custom, char *end) -{ -#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ - wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - - if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - char *pos; - u8 addr[ETH_ALEN]; - pos = os_strstr(custom, "addr="); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "without sender address ignored"); - return; - } - pos += 5; - if (hwaddr_aton(pos, addr) == 0) { - union wpa_event_data data; - os_memset(&data, 0, sizeof(data)); - data.michael_mic_failure.unicast = 1; - data.michael_mic_failure.src = addr; - wpa_supplicant_event(drv->hapd, - EVENT_MICHAEL_MIC_FAILURE, &data); - } else { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "with invalid MAC address"); - } - } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { - char *key, *value; - u32 val; - key = custom; - while ((key = os_strchr(key, '\n')) != NULL) { - key++; - value = os_strchr(key, '='); - if (value == NULL) - continue; - *value++ = '\0'; - val = strtoul(value, NULL, 10); - if (os_strcmp(key, "mac") == 0) - hwaddr_aton(value, drv->acct_mac); - else if (os_strcmp(key, "rx_packets") == 0) - drv->acct_data.rx_packets = val; - else if (os_strcmp(key, "tx_packets") == 0) - drv->acct_data.tx_packets = val; - else if (os_strcmp(key, "rx_bytes") == 0) - drv->acct_data.rx_bytes = val; - else if (os_strcmp(key, "tx_bytes") == 0) - drv->acct_data.tx_bytes = val; - key = value; - } -#ifdef CONFIG_WPS - } else if (os_strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { - /* Some atheros kernels send push button as a wireless event */ - /* PROBLEM! this event is received for ALL BSSs ... - * so all are enabled for WPS... ugh. - */ - wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); - } else if (os_strncmp(custom, "Manage.prob_req ", 16) == 0) { - /* - * Atheros driver uses a hack to pass Probe Request frames as a - * binary data in the custom wireless event. The old way (using - * packet sniffing) didn't work when bridging. - * Format: "Manage.prob_req " | zero padding | frame - */ - int len = atoi(custom + 16); - if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { - wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " - "length %d", len); - return; - } - atheros_raw_receive(drv, NULL, - (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); -#endif /* CONFIG_WPS */ - } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) { - /* Format: "Manage.assoc_req " | zero padding | - * frame */ - int len = atoi(custom + 17); - if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { - wpa_printf(MSG_DEBUG, - "Invalid Manage.assoc_req event length %d", - len); - return; - } - atheros_raw_receive(drv, NULL, - (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { - /* Format: "Manage.auth " | zero padding | frame */ - int len = atoi(custom + 12); - if (len < 0 || - MGMT_FRAM_TAG_SIZE + len > end - custom) { - wpa_printf(MSG_DEBUG, - "Invalid Manage.auth event length %d", len); - return; - } - atheros_raw_receive(drv, NULL, - (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { - /* Format: "Manage.assoc_req " | zero padding | frame - */ - int len = atoi(custom + 14); - if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { - wpa_printf(MSG_DEBUG, - "Invalid Manage.action event length %d", - len); - return; - } - atheros_raw_receive(drv, NULL, - (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } -} - - -static void send_action_cb_event(struct atheros_driver_data *drv, - char *data, size_t data_len) -{ - union wpa_event_data event; - struct ieee80211_send_action_cb *sa; - const struct ieee80211_hdr *hdr; - u16 fc; - - if (data_len < sizeof(*sa) + 24) { - wpa_printf(MSG_DEBUG, - "athr: Too short event message (data_len=%d sizeof(*sa)=%d)", - (int) data_len, (int) sizeof(*sa)); - wpa_hexdump(MSG_DEBUG, "athr: Short event message", - data, data_len); - return; - } - - sa = (struct ieee80211_send_action_cb *) data; - - hdr = (const struct ieee80211_hdr *) (sa + 1); - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = sa->dst_addr; - event.tx_status.data = (const u8 *) hdr; - event.tx_status.data_len = data_len - sizeof(*sa); - event.tx_status.ack = sa->ack; - wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); -} - - -/* -* Handle size of data problem. WEXT only allows data of 256 bytes for custom -* events, and p2p data can be much bigger. So the athr driver sends a small -* event telling me to collect the big data with an ioctl. -* On the first event, send all pending events to supplicant. -*/ -static void fetch_pending_big_events(struct atheros_driver_data *drv) -{ - union wpa_event_data event; - const struct ieee80211_mgmt *mgmt; - u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ - u16 fc, stype; - struct iwreq iwr; - size_t data_len; - u32 freq, frame_type; - - while (1) { - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - - iwr.u.data.pointer = (void *) tbuf; - iwr.u.data.length = sizeof(tbuf); - iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) - < 0) { - if (errno == ENOSPC) { - wpa_printf(MSG_DEBUG, "%s:%d exit", - __func__, __LINE__); - return; - } - wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" - "P2P_FETCH_FRAME] failed: %s", - __func__, strerror(errno)); - return; - } - data_len = iwr.u.data.length; - wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", - (u8 *) tbuf, data_len); - if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { - wpa_printf(MSG_DEBUG, "athr: frame too short"); - continue; - } - os_memcpy(&freq, tbuf, sizeof(freq)); - os_memcpy(&frame_type, &tbuf[sizeof(freq)], - sizeof(frame_type)); - mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; - data_len -= sizeof(freq) + sizeof(frame_type); - - if (frame_type == IEEE80211_EV_RX_MGMT) { - fc = le_to_host16(mgmt->frame_control); - stype = WLAN_FC_GET_STYPE(fc); - - wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " - "freq=%u len=%u", stype, freq, (int) data_len); - - if (stype == WLAN_FC_STYPE_ACTION) { - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = (const u8 *) mgmt; - event.rx_mgmt.frame_len = data_len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, - &event); - continue; - } - } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) { - wpa_printf(MSG_DEBUG, - "%s: ACTION_CB frame_type=%u len=%zu", - __func__, frame_type, data_len); - send_action_cb_event(drv, (void *) mgmt, data_len); - } else { - wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", - __func__, frame_type); - continue; - } - } -} - -static void -atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, - int opcode, char *buf, int len) -{ - switch (opcode) { - case IEEE80211_EV_P2P_SEND_ACTION_CB: - wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB"); - fetch_pending_big_events(drv); - break; - case IEEE80211_EV_RX_MGMT: - wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); - fetch_pending_big_events(drv); - break; - default: - break; - } -} - -static void -atheros_wireless_event_wireless(struct atheros_driver_data *drv, - char *data, unsigned int len) -{ - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf; - - pos = data; - end = data + len; - - while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) - return; - - custom = pos + IW_EV_POINT_LEN; - if (drv->we_version > 18 && - (iwe->cmd == IWEVMICHAELMICFAILURE || - iwe->cmd == IWEVASSOCREQIE || - iwe->cmd == IWEVCUSTOM)) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - os_memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) { - case IWEVEXPIRED: - drv_event_disassoc(drv->hapd, - (u8 *) iwe->u.addr.sa_data); - break; - case IWEVREGISTERED: - atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data); - break; - case IWEVASSOCREQIE: - /* Driver hack.. Use IWEVASSOCREQIE to bypass - * IWEVCUSTOM size limitations. Need to handle this - * just like IWEVCUSTOM. - */ - case IWEVCUSTOM: - if (iwe->u.data.length > end - custom) - return; - buf = os_malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; /* XXX */ - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - - if (iwe->u.data.flags != 0) { - atheros_wireless_event_atheros_custom( - drv, (int) iwe->u.data.flags, - buf, len); - } else { - atheros_wireless_event_wireless_custom( - drv, buf, buf + iwe->u.data.length); - } - os_free(buf); - break; - } - - pos += iwe->len; - } -} - - -static void -atheros_wireless_event_rtm_newlink(void *ctx, - struct ifinfomsg *ifi, u8 *buf, size_t len) -{ - struct atheros_driver_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - if (ifi->ifi_index != drv->ifindex) - return; - - attrlen = len; - attr = (struct rtattr *) buf; - - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_WIRELESS) { - atheros_wireless_event_wireless( - drv, ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } - attr = RTA_NEXT(attr, attrlen); - } -} - - -static int -atheros_get_we_version(struct atheros_driver_data *drv) -{ - struct iw_range *range; - struct iwreq iwr; - int minlen; - size_t buflen; - - drv->we_version = 0; - - /* - * Use larger buffer than struct iw_range in order to allow the - * structure to grow in the future. - */ - buflen = sizeof(struct iw_range) + 500; - range = os_zalloc(buflen); - if (range == NULL) - return -1; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) range; - iwr.u.data.length = buflen; - - minlen = ((char *) &range->enc_capa) - (char *) range + - sizeof(range->enc_capa); - - if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", - strerror(errno)); - os_free(range); - return -1; - } else if (iwr.u.data.length >= minlen && - range->we_version_compiled >= 18) { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " - "WE(source)=%d enc_capa=0x%x", - range->we_version_compiled, - range->we_version_source, - range->enc_capa); - drv->we_version = range->we_version_compiled; - } - - os_free(range); - return 0; -} - - -static int -atheros_wireless_event_init(struct atheros_driver_data *drv) -{ - struct netlink_config *cfg; - - atheros_get_we_version(drv); - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - return -1; - cfg->ctx = drv; - cfg->newlink_cb = atheros_wireless_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - return -1; - } - - return 0; -} - - -static int -atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr, u32 flags, int link_id) -{ - struct atheros_driver_data *drv = priv; - unsigned char buf[3000]; - unsigned char *bp = buf; - struct l2_ethhdr *eth; - size_t len; - int status; - - /* - * Prepend the Ethernet header. If the caller left us - * space at the front we could just insert it but since - * we don't know we copy to a local buffer. Given the frequency - * and size of frames this probably doesn't matter. - */ - len = data_len + sizeof(struct l2_ethhdr); - if (len > sizeof(buf)) { - bp = os_malloc(len); - if (bp == NULL) { - wpa_printf(MSG_INFO, - "EAPOL frame discarded, cannot malloc temp buffer of size %lu!", - (unsigned long) len); - return -1; - } - } - eth = (struct l2_ethhdr *) bp; - os_memcpy(eth->h_dest, addr, ETH_ALEN); - os_memcpy(eth->h_source, own_addr, ETH_ALEN); - eth->h_proto = host_to_be16(ETH_P_EAPOL); - os_memcpy(eth + 1, data, data_len); - - wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); - - status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); - - if (bp != buf) - os_free(bp); - return status; -} - -static void -handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) -{ - struct atheros_driver_data *drv = ctx; - drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - - -static void atheros_read_fils_cap(struct atheros_driver_data *drv) -{ - int fils = 0; - -#ifdef CONFIG_FILS - /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value - * to automatically check this against the driver header files. */ - if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) { - wpa_printf(MSG_DEBUG, - "%s: Failed to get FILS capability from driver", - __func__); - /* Assume driver does not support FILS */ - fils = 0; - } -#endif /* CONFIG_FILS */ - drv->fils_en = fils; - wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en); -} - - -static void * -atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) -{ - struct atheros_driver_data *drv; - struct ifreq ifr; - struct iwreq iwr; - char brname[IFNAMSIZ]; - - drv = os_zalloc(sizeof(struct atheros_driver_data)); - if (drv == NULL) { - wpa_printf(MSG_INFO, - "Could not allocate memory for atheros driver data"); - return NULL; - } - - drv->hapd = hapd; - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - goto bad; - } - os_memcpy(drv->iface, params->ifname, sizeof(drv->iface)); - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); - if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { - wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", - strerror(errno)); - goto bad; - } - drv->ifindex = ifr.ifr_ifindex; - - drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, - handle_read, drv, 1); - if (drv->sock_xmit == NULL) - goto bad; - if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) - goto bad; - os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); - if (params->bridge[0]) { - wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", - params->bridge[0]); - drv->sock_recv = l2_packet_init(params->bridge[0], NULL, - ETH_P_EAPOL, handle_read, drv, - 1); - if (drv->sock_recv == NULL) - goto bad; - } else if (linux_br_get(brname, drv->iface) == 0) { - wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " - "EAPOL receive", brname); - drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, - handle_read, drv, 1); - if (drv->sock_recv == NULL) - goto bad; - } else - drv->sock_recv = drv->sock_xmit; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - - iwr.u.mode = IW_MODE_MASTER; - - if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { - wpa_printf(MSG_ERROR, - "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s", - strerror(errno)); - goto bad; - } - - /* mark down during setup */ - linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - atheros_set_privacy(drv, 0); /* default to no privacy */ - - if (atheros_receive_pkt(drv)) - goto bad; - - if (atheros_wireless_event_init(drv)) - goto bad; - - /* Read FILS capability from the driver */ - atheros_read_fils_cap(drv); - - return drv; -bad: - atheros_reset_appfilter(drv); - if (drv->sock_raw) - l2_packet_deinit(drv->sock_raw); - if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) - l2_packet_deinit(drv->sock_recv); - if (drv->sock_xmit != NULL) - l2_packet_deinit(drv->sock_xmit); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - os_free(drv); - return NULL; -} - - -static void -atheros_deinit(void *priv) -{ - struct atheros_driver_data *drv = priv; - - atheros_reset_appfilter(drv); - - if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) { - atheros_set_opt_ie(priv, NULL, 0); - wpabuf_free(drv->wpa_ie); - wpabuf_free(drv->wps_beacon_ie); - wpabuf_free(drv->wps_probe_resp_ie); - } - netlink_deinit(drv->netlink); - (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) - l2_packet_deinit(drv->sock_recv); - if (drv->sock_xmit != NULL) - l2_packet_deinit(drv->sock_xmit); - if (drv->sock_raw) - l2_packet_deinit(drv->sock_raw); - os_free(drv); -} - -static int -atheros_set_ssid(void *priv, const u8 *buf, int len) -{ - struct atheros_driver_data *drv = priv; - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.essid.flags = 1; /* SSID active */ - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; - - if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", - len, strerror(errno)); - return -1; - } - return 0; -} - -static int -atheros_get_ssid(void *priv, u8 *buf, int len) -{ - struct atheros_driver_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? - IW_ESSID_MAX_SIZE : len; - - if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", - strerror(errno)); - ret = -1; - } else - ret = iwr.u.essid.length; - - return ret; -} - -static int -atheros_set_countermeasures(void *priv, int enabled) -{ - struct atheros_driver_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); -} - -static int -atheros_commit(void *priv) -{ - struct atheros_driver_data *drv = priv; - return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); -} - -static int atheros_set_authmode(void *priv, int auth_algs) -{ - int authmode; - - if ((auth_algs & WPA_AUTH_ALG_OPEN) && - (auth_algs & WPA_AUTH_ALG_SHARED)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_algs & WPA_AUTH_ALG_OPEN) - authmode = IEEE80211_AUTH_OPEN; - else if (auth_algs & WPA_AUTH_ALG_SHARED) - authmode = IEEE80211_AUTH_SHARED; - else - return -1; - - return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); -} - -static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) -{ - /* - * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, - * set_generic_elem, and hapd_set_ssid. - */ - - wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " - "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " - "wpa_version=0x%x privacy=%d interworking=%d", - params->pairwise_ciphers, params->group_cipher, - params->key_mgmt_suites, params->auth_algs, - params->wpa_version, params->privacy, params->interworking); - wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", - params->ssid, params->ssid_len); - if (params->hessid) - wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, - MAC2STR(params->hessid)); - wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", - params->beacon_ies); - wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", - params->proberesp_ies); - wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", - params->assocresp_ies); - -#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN)) - if (params->osen) { - struct wpa_bss_params bss_params; - - os_memset(&bss_params, 0, sizeof(struct wpa_bss_params)); - bss_params.enabled = 1; - bss_params.wpa = 2; - bss_params.wpa_pairwise = WPA_CIPHER_CCMP; - bss_params.wpa_group = WPA_CIPHER_CCMP; - bss_params.ieee802_1x = 1; - - if (atheros_set_privacy(priv, 1) || - set80211param(priv, IEEE80211_PARAM_OSEN, 1)) - return -1; - - return atheros_set_ieee8021x(priv, &bss_params); - } -#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */ - - return 0; -} - - -static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, - int noack, unsigned int freq, - const u16 *csa_offs, size_t csa_offs_len, - int no_encrypt, unsigned int wait, int link_id) -{ - struct atheros_driver_data *drv = priv; - u8 buf[1510]; - const struct ieee80211_mgmt *mgmt; - struct ieee80211req_mgmtbuf *mgmt_frm; - - mgmt = (const struct ieee80211_mgmt *) frm; - wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, - (unsigned long) data_len, MAC2STR(mgmt->da)); - mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; - os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); - mgmt_frm->buflen = data_len; - if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { - wpa_printf(MSG_INFO, "atheros: Too long frame for " - "atheros_send_mgmt (%u)", (unsigned int) data_len); - return -1; - } - os_memcpy(&mgmt_frm->buf[0], frm, data_len); - return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, - sizeof(struct ieee80211req_mgmtbuf) + data_len); -} - - -#ifdef CONFIG_IEEE80211R - -static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, - size_t tspec_ielen) -{ - struct atheros_driver_data *drv = priv; - int retv; - struct ieee80211req_res req; - struct ieee80211req_res_addts *addts = &req.u.addts; - - wpa_printf(MSG_DEBUG, "%s", __func__); - req.type = IEEE80211_RESREQ_ADDTS; - os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); - os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); - retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, - sizeof(struct ieee80211req_res)); - if (retv < 0) { - wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " - "retv = %d", __func__, retv); - return -1; - } - os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); - return addts->status; -} - - -static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211req_res req; - struct ieee80211req_res_addnode *addnode = &req.u.addnode; - - wpa_printf(MSG_DEBUG, "%s", __func__); - req.type = IEEE80211_RESREQ_ADDNODE; - os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); - addnode->auth_alg = auth_alg; - return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, - sizeof(struct ieee80211req_res)); -} - -#endif /* CONFIG_IEEE80211R */ - - -/* Use only to set a big param, get will not work. */ -static int -set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - - iwr.u.data.pointer = (void *) data; - iwr.u.data.length = len; - iwr.u.data.flags = op; - wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", - __func__, op, op, athr_get_param_name(op), len); - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { - wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " - "value=0x%x,0x%x failed: %d (%s)", - __func__, op, athr_get_ioctl_name(op), iwr.u.mode, - iwr.u.mode, iwr.u.data.length, - iwr.u.data.flags, errno, strerror(errno)); - return -1; - } - return 0; -} - - -static int atheros_send_action(void *priv, unsigned int freq, - unsigned int wait, - const u8 *dst, const u8 *src, - const u8 *bssid, - const u8 *data, size_t data_len, int no_cck) -{ - struct atheros_driver_data *drv = priv; - struct ieee80211_p2p_send_action *act; - int res; - - act = os_zalloc(sizeof(*act) + data_len); - if (act == NULL) - return -1; - act->freq = freq; - os_memcpy(act->dst_addr, dst, ETH_ALEN); - os_memcpy(act->src_addr, src, ETH_ALEN); - os_memcpy(act->bssid, bssid, ETH_ALEN); - os_memcpy(act + 1, data, data_len); - wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" - MACSTR ", bssid=" MACSTR, - __func__, act->freq, wait, MAC2STR(act->dst_addr), - MAC2STR(act->src_addr), MAC2STR(act->bssid)); - wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); - wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); - - res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, - act, sizeof(*act) + data_len); - os_free(act); - return res; -} - - -#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) -static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, - u8 *ie, u16 *len, enum wnm_oper oper) -{ -#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ - u8 buf[IEEE80211_APPIE_MAX]; - struct ieee80211req_getset_appiebuf *tfs_ie; - u16 val; - - wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, - drv->iface, oper, MAC2STR(peer)); - - switch (oper) { - case WNM_SLEEP_TFS_REQ_IE_SET: - if (*len > IEEE80211_APPIE_MAX - - sizeof(struct ieee80211req_getset_appiebuf)) { - wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); - return -1; - } - tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; - tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; - tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; - - /* Command header for driver */ - os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); - val = oper; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); - val = *len; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); - - /* copy the ie */ - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); - - if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, - IEEE80211_APPIE_MAX)) { - wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " - "%s", __func__, strerror(errno)); - return -1; - } - break; - case WNM_SLEEP_TFS_RESP_IE_ADD: - tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; - tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; - tfs_ie->app_buflen = IEEE80211_APPIE_MAX - - sizeof(struct ieee80211req_getset_appiebuf); - /* Command header for driver */ - os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); - val = oper; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); - val = 0; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); - - if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, - IEEE80211_APPIE_MAX)) { - wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " - "%s", __func__, strerror(errno)); - return -1; - } - - *len = tfs_ie->app_buflen; - os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); - wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], - *len); - break; - case WNM_SLEEP_TFS_RESP_IE_NONE: - *len = 0; - break; - case WNM_SLEEP_TFS_IE_DEL: - tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; - tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; - tfs_ie->app_buflen = IEEE80211_APPIE_MAX - - sizeof(struct ieee80211req_getset_appiebuf); - /* Command header for driver */ - os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); - val = oper; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); - val = 0; - os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); - - if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, - IEEE80211_APPIE_MAX)) { - wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " - "%s", __func__, strerror(errno)); - return -1; - } - break; - default: - wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); - break; - } - - return 0; -} - - -static int atheros_wnm_sleep(struct atheros_driver_data *drv, - const u8 *peer, enum wnm_oper oper) -{ - u8 *data, *pos; - size_t dlen; - int ret; - u16 val; - - wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, - oper, MAC2STR(peer)); - - dlen = ETH_ALEN + 2 + 2; - data = os_malloc(dlen); - if (data == NULL) - return -1; - - /* Command header for driver */ - pos = data; - os_memcpy(pos, peer, ETH_ALEN); - pos += ETH_ALEN; - - val = oper; - os_memcpy(pos, &val, 2); - pos += 2; - - val = 0; - os_memcpy(pos, &val, 2); - - ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); - - os_free(data); - - return ret; -} - - -static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, - u8 *buf, u16 *buf_len) -{ - struct atheros_driver_data *drv = priv; - - switch (oper) { - case WNM_SLEEP_ENTER_CONFIRM: - case WNM_SLEEP_ENTER_FAIL: - case WNM_SLEEP_EXIT_CONFIRM: - case WNM_SLEEP_EXIT_FAIL: - return atheros_wnm_sleep(drv, peer, oper); - case WNM_SLEEP_TFS_REQ_IE_SET: - case WNM_SLEEP_TFS_RESP_IE_ADD: - case WNM_SLEEP_TFS_RESP_IE_NONE: - case WNM_SLEEP_TFS_IE_DEL: - return athr_wnm_tfs(drv, peer, buf, buf_len, oper); - default: - wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", - oper); - return -1; - } -} -#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ - - -const struct wpa_driver_ops wpa_driver_atheros_ops = { - .name = "atheros", - .hapd_init = atheros_init, - .hapd_deinit = atheros_deinit, - .set_ieee8021x = atheros_set_ieee8021x, - .set_privacy = atheros_set_privacy, - .set_key = atheros_set_key, - .get_seqnum = atheros_get_seqnum, - .flush = atheros_flush, - .set_generic_elem = atheros_set_opt_ie, - .sta_set_flags = atheros_sta_set_flags, - .read_sta_data = atheros_read_sta_driver_data, - .hapd_send_eapol = atheros_send_eapol, - .sta_disassoc = atheros_sta_disassoc, - .sta_deauth = atheros_sta_deauth, - .hapd_set_ssid = atheros_set_ssid, - .hapd_get_ssid = atheros_get_ssid, - .set_countermeasures = atheros_set_countermeasures, - .sta_clear_stats = atheros_sta_clear_stats, - .commit = atheros_commit, - .set_ap_wps_ie = atheros_set_ap_wps_ie, - .set_authmode = atheros_set_authmode, - .set_ap = atheros_set_ap, - .sta_assoc = atheros_sta_assoc, - .sta_auth = atheros_sta_auth, - .send_mlme = atheros_send_mgmt, -#ifdef CONFIG_IEEE80211R - .add_tspec = atheros_add_tspec, - .add_sta_node = atheros_add_sta_node, -#endif /* CONFIG_IEEE80211R */ - .send_action = atheros_send_action, -#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) - .wnm_oper = atheros_wnm_oper, -#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ - .set_qos_map = atheros_set_qos_map, -}; diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index 3c1ff1b88044..6faac2ceea4c 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -354,6 +354,9 @@ bsd_set_key(void *priv, struct wpa_driver_set_key_params *params) const u8 *key = params->key; size_t key_len = params->key_len; + if (params->key_flag & KEY_FLAG_NEXT) + return -1; + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx, set_tx, seq_len, key_len); @@ -1052,7 +1055,7 @@ bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code, static int bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason_code) + u16 reason_code, int link_id) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, addr); @@ -1675,7 +1678,8 @@ get80211opmode(struct bsd_driver_data *drv) } static void * -wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv) +wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv, + enum wpa_p2p_mode p2p_mode) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c index 9bc5a731dd82..812305cae263 100644 --- a/contrib/wpa/src/drivers/driver_common.c +++ b/contrib/wpa/src/drivers/driver_common.c @@ -100,6 +100,16 @@ const char * event_to_string(enum wpa_event_type event) E2S(LINK_CH_SWITCH_STARTED); E2S(TID_LINK_MAP); E2S(LINK_RECONFIG); + E2S(MLD_INTERFACE_FREED); + E2S(SETUP_LINK_RECONFIG); + E2S(NAN_CLUSTER_JOIN); + E2S(NAN_NEXT_DW); + E2S(INCUMBT_SIG_INTF_DETECTED); + E2S(NAN_SCHED_UPDATE_DONE); + E2S(NAN_ULW_UPDATE); + E2S(NAN_CHAN_EVACUATION); + E2S(PEER_MEASUREMENT_RESULT); + E2S(PEER_MEASUREMENT_COMPLETE); } return "UNKNOWN"; @@ -202,6 +212,21 @@ bool he_supported(const struct hostapd_hw_modes *hw_mode, } +bool eht_supported(const struct hostapd_hw_modes *hw_mode, + enum ieee80211_op_mode op_mode) +{ + if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_EHT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports EHT. Assume + * it does to avoid connection issues. + */ + return true; + } + + return hw_mode->eht_capab[op_mode].eht_supported; +} + + static int wpa_check_wowlan_trigger(const char *start, const char *trigger, int capa_trigger, u8 *param_trigger) { @@ -373,6 +398,7 @@ const char * driver_flag2_to_string(u64 flag2) DF2S(MLO); DF2S(SCAN_MIN_PREQ); DF2S(SAE_OFFLOAD_STA); + DF2S(SPP_AMSDU); } return "UNKNOWN"; #undef DF2S diff --git a/contrib/wpa/src/drivers/driver_hostap.c b/contrib/wpa/src/drivers/driver_hostap.c deleted file mode 100644 index 3aa5860bc433..000000000000 --- a/contrib/wpa/src/drivers/driver_hostap.c +++ /dev/null @@ -1,1208 +0,0 @@ -/* - * Driver interaction with Linux Host AP driver - * Copyright (c) 2003-2005, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include - -#include "linux_wext.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" -#include "eloop.h" -#include "driver_hostap.h" - - -#include -#include - -#include "priv_netlink.h" -#include "netlink.h" -#include "linux_ioctl.h" -#include "common/ieee802_11_defs.h" -#include "common/ieee802_11_common.h" - - -/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X - * frames that might be longer than normal default MTU and they are not - * fragmented */ -#define HOSTAPD_MTU 2290 - -static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - -struct hostap_driver_data { - struct hostapd_data *hapd; - - char iface[IFNAMSIZ + 1]; - int sock; /* raw packet socket for driver access */ - int ioctl_sock; /* socket for ioctl() use */ - struct netlink_data *netlink; - - int we_version; - - u8 *generic_ie; - size_t generic_ie_len; - u8 *wps_ie; - size_t wps_ie_len; -}; - - -static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, - int len); -static int hostap_set_iface_flags(void *priv, int dev_up); - -static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, - u16 stype) -{ - struct ieee80211_hdr *hdr; - u16 fc, ethertype; - u8 *pos, *sa; - size_t left; - union wpa_event_data event; - - if (len < sizeof(struct ieee80211_hdr)) - return; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { - printf("Not ToDS data frame (fc=0x%04x)\n", fc); - return; - } - - sa = hdr->addr2; - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); - event.rx_from_unknown.addr = sa; - wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); - - pos = (u8 *) (hdr + 1); - left = len - sizeof(*hdr); - - if (left < sizeof(rfc1042_header)) { - printf("Too short data frame\n"); - return; - } - - if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { - printf("Data frame with no RFC1042 header\n"); - return; - } - pos += sizeof(rfc1042_header); - left -= sizeof(rfc1042_header); - - if (left < 2) { - printf("No ethertype in data frame\n"); - return; - } - - ethertype = WPA_GET_BE16(pos); - pos += 2; - left -= 2; - switch (ethertype) { - case ETH_P_PAE: - drv_event_eapol_rx(drv->hapd, sa, pos, left); - break; - - default: - printf("Unknown ethertype 0x%04x in data frame\n", ethertype); - break; - } -} - - -static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, - size_t len, int ok) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = buf; - event.tx_status.data_len = len; - event.tx_status.ack = ok; - wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); -} - - -static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) -{ - struct ieee80211_hdr *hdr; - u16 fc, type, stype; - size_t data_len = len; - int ver; - union wpa_event_data event; - - /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass - * these to user space */ - if (len < 24) { - wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", - (unsigned long) len); - return; - } - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - type = WLAN_FC_GET_TYPE(fc); - stype = WLAN_FC_GET_STYPE(fc); - - if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { - wpa_hexdump(MSG_MSGDUMP, "Received management frame", - buf, len); - } - - ver = fc & WLAN_FC_PVER; - - /* protocol version 2 is reserved for indicating ACKed frame (TX - * callbacks), and version 1 for indicating failed frame (no ACK, TX - * callbacks) */ - if (ver == 1 || ver == 2) { - handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); - return; - } else if (ver != 0) { - printf("unknown protocol version %d\n", ver); - return; - } - - switch (type) { - case WLAN_FC_TYPE_MGMT: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = data_len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); - break; - case WLAN_FC_TYPE_CTRL: - wpa_printf(MSG_DEBUG, "CTRL"); - break; - case WLAN_FC_TYPE_DATA: - wpa_printf(MSG_DEBUG, "DATA"); - handle_data(drv, buf, data_len, stype); - break; - default: - wpa_printf(MSG_DEBUG, "unknown frame type %d", type); - break; - } -} - - -static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostap_driver_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); - return; - } - - handle_frame(drv, buf, len); -} - - -static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) -{ - struct ifreq ifr; - struct sockaddr_ll addr; - - drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (drv->sock < 0) { - wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", - strerror(errno)); - return -1; - } - - if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { - wpa_printf(MSG_ERROR, "Could not register read socket"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", - drv->iface) >= (int) sizeof(ifr.ifr_name)) { - wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); - return -1; - } - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", - strerror(errno)); - return -1; - } - - if (hostap_set_iface_flags(drv, 1)) { - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", - addr.sll_ifindex); - - if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); - return -1; - } - - return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); -} - - -static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack, - unsigned int freq, - const u16 *csa_offs, size_t csa_offs_len, - int no_encrypt, unsigned int wait, int link_id) -{ - struct hostap_driver_data *drv = priv; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; - int res; - - /* Request TX callback */ - hdr->frame_control |= host_to_le16(BIT(1)); - res = send(drv->sock, msg, len, 0); - hdr->frame_control &= ~host_to_le16(BIT(1)); - - return res; -} - - -static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr, - u32 flags, int link_id) -{ - struct hostap_driver_data *drv = priv; - struct ieee80211_hdr *hdr; - size_t len; - u8 *pos; - int res; - - len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; - hdr = os_zalloc(len); - if (hdr == NULL) { - printf("malloc() failed for hostapd_send_data(len=%lu)\n", - (unsigned long) len); - return -1; - } - - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); - hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); - if (encrypt) - hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); - memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); - memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - - pos = (u8 *) (hdr + 1); - memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); - pos += sizeof(rfc1042_header); - *((u16 *) pos) = htons(ETH_P_PAE); - pos += 2; - memcpy(pos, data, data_len); - - res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0, 0, 0, -1); - if (res < 0) { - wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " - "failed: %d (%s)", - (unsigned long) len, errno, strerror(errno)); - } - os_free(hdr); - - return res; -} - - -static int hostap_sta_set_flags(void *priv, const u8 *addr, - unsigned int total_flags, unsigned int flags_or, - unsigned int flags_and) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - if (flags_or & WPA_STA_AUTHORIZED) - flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ - if (!(flags_and & WPA_STA_AUTHORIZED)) - flags_and = ~BIT(5); - else - flags_and = ~0; - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - param.u.set_flags_sta.flags_or = flags_or; - param.u.set_flags_sta.flags_and = flags_and; - return hostapd_ioctl(drv, ¶m, sizeof(param)); -} - - -static int hostap_set_iface_flags(void *priv, int dev_up) -{ - struct hostap_driver_data *drv = priv; - struct ifreq ifr; - char ifname[IFNAMSIZ]; - - if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) { - wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); - return -1; - } - if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) - return -1; - - if (dev_up) { - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_mtu = HOSTAPD_MTU; - if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { - wpa_printf(MSG_INFO, - "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s", - strerror(errno)); - } - } - - return 0; -} - - -static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, - int len) -{ - struct hostap_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s", - strerror(errno)); - return -1; - } - - return 0; -} - - -static int wpa_driver_hostap_set_key(void *priv, - struct wpa_driver_set_key_params *params) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param *param; - u8 *buf; - size_t blen; - int ret = 0; - enum wpa_alg alg = params->alg; - const u8 *addr = params->addr; - int key_idx = params->key_idx; - int set_tx = params->set_tx; - const u8 *key = params->key; - size_t key_len = params->key_len; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct prism2_hostapd_param *) buf; - param->cmd = PRISM2_SET_ENCRYPTION; - if (addr == NULL) - memset(param->sta_addr, 0xff, ETH_ALEN); - else - memcpy(param->sta_addr, addr, ETH_ALEN); - switch (alg) { - case WPA_ALG_NONE: - os_strlcpy((char *) param->u.crypt.alg, "NONE", - HOSTAP_CRYPT_ALG_NAME_LEN); - break; - case WPA_ALG_WEP: - os_strlcpy((char *) param->u.crypt.alg, "WEP", - HOSTAP_CRYPT_ALG_NAME_LEN); - break; - case WPA_ALG_TKIP: - os_strlcpy((char *) param->u.crypt.alg, "TKIP", - HOSTAP_CRYPT_ALG_NAME_LEN); - break; - case WPA_ALG_CCMP: - os_strlcpy((char *) param->u.crypt.alg, "CCMP", - HOSTAP_CRYPT_ALG_NAME_LEN); - break; - default: - os_free(buf); - return -1; - } - param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; - param->u.crypt.idx = key_idx; - param->u.crypt.key_len = key_len; - memcpy((u8 *) (param + 1), key, key_len); - - if (hostapd_ioctl(drv, param, blen)) { - printf("Failed to set encryption.\n"); - ret = -1; - } - free(buf); - - return ret; -} - - -static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, - int idx, int link_id, u8 *seq) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param *param; - u8 *buf; - size_t blen; - int ret = 0; - - blen = sizeof(*param) + 32; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct prism2_hostapd_param *) buf; - param->cmd = PRISM2_GET_ENCRYPTION; - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); - param->u.crypt.idx = idx; - - if (hostapd_ioctl(drv, param, blen)) { - printf("Failed to get encryption.\n"); - ret = -1; - } else { - os_memcpy(seq, param->u.crypt.seq, 8); - } - os_free(buf); - - return ret; -} - - -static int hostap_ioctl_prism2param(void *priv, int param, int value) -{ - struct hostap_driver_data *drv = priv; - struct iwreq iwr; - int *i; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; - - if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s", - strerror(errno)); - return -1; - } - - return 0; -} - - -static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) -{ - struct hostap_driver_data *drv = priv; - int enabled = params->enabled; - - /* enable kernel driver support for IEEE 802.1X */ - if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { - printf("Could not setup IEEE 802.1X support in kernel driver." - "\n"); - return -1; - } - - if (!enabled) - return 0; - - /* use host driver implementation of encryption to allow - * individual keys and passing plaintext EAPOL frames */ - if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || - hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { - printf("Could not setup host-based encryption in kernel " - "driver.\n"); - return -1; - } - - return 0; -} - - -static int hostap_set_privacy(void *priv, int enabled) -{ - struct hostap_drvier_data *drv = priv; - - return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, - enabled); -} - - -static int hostap_set_ssid(void *priv, const u8 *buf, int len) -{ - struct hostap_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.essid.flags = 1; /* SSID active */ - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len + 1; - - if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", - len, strerror(errno)); - return -1; - } - - return 0; -} - - -static int hostap_flush(void *priv, int link_id) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_FLUSH; - return hostapd_ioctl(drv, ¶m, sizeof(param)); -} - - -static int hostap_read_sta_data(void *priv, - struct hostap_sta_driver_data *data, - const u8 *addr) -{ - struct hostap_driver_data *drv = priv; - char buf[1024], line[128], *pos; - FILE *f; - unsigned long val; - - memset(data, 0, sizeof(*data)); - snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, - drv->iface, MAC2STR(addr)); - - f = fopen(buf, "r"); - if (!f) - return -1; - /* Need to read proc file with in one piece, so use large enough - * buffer. */ - setbuffer(f, buf, sizeof(buf)); - - while (fgets(line, sizeof(line), f)) { - pos = strchr(line, '='); - if (!pos) - continue; - *pos++ = '\0'; - val = strtoul(pos, NULL, 10); - if (strcmp(line, "rx_packets") == 0) - data->rx_packets = val; - else if (strcmp(line, "tx_packets") == 0) - data->tx_packets = val; - else if (strcmp(line, "rx_bytes") == 0) - data->rx_bytes = val; - else if (strcmp(line, "tx_bytes") == 0) - data->tx_bytes = val; - } - - fclose(f); - - return 0; -} - - -static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - int tx_supp_rates = 0; - size_t i; - -#define WLAN_RATE_1M BIT(0) -#define WLAN_RATE_2M BIT(1) -#define WLAN_RATE_5M5 BIT(2) -#define WLAN_RATE_11M BIT(3) - - for (i = 0; i < params->supp_rates_len; i++) { - if ((params->supp_rates[i] & 0x7f) == 2) - tx_supp_rates |= WLAN_RATE_1M; - if ((params->supp_rates[i] & 0x7f) == 4) - tx_supp_rates |= WLAN_RATE_2M; - if ((params->supp_rates[i] & 0x7f) == 11) - tx_supp_rates |= WLAN_RATE_5M5; - if ((params->supp_rates[i] & 0x7f) == 22) - tx_supp_rates |= WLAN_RATE_11M; - } - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_ADD_STA; - memcpy(param.sta_addr, params->addr, ETH_ALEN); - param.u.add_sta.aid = params->aid; - param.u.add_sta.capability = params->capability; - param.u.add_sta.tx_supp_rates = tx_supp_rates; - return hostapd_ioctl(drv, ¶m, sizeof(param)); -} - - -static int hostap_sta_remove(void *priv, const u8 *addr) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_REMOVE_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, sizeof(param))) { - printf("Could not remove station from kernel driver.\n"); - return -1; - } - return 0; -} - - -static int hostap_get_inact_sec(void *priv, const u8 *addr) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, sizeof(param))) { - return -1; - } - - return param.u.get_info_sta.inactive_sec; -} - - -static int hostap_sta_clear_stats(void *priv, const u8 *addr) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, sizeof(param))) { - return -1; - } - - return 0; -} - - -static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) -{ - struct prism2_hostapd_param *param; - int res; - size_t blen, elem_len; - - elem_len = drv->generic_ie_len + drv->wps_ie_len; - blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = elem_len; - if (drv->generic_ie) { - os_memcpy(param->u.generic_elem.data, drv->generic_ie, - drv->generic_ie_len); - } - if (drv->wps_ie) { - os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], - drv->wps_ie, drv->wps_ie_len); - } - wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", - param->u.generic_elem.data, elem_len); - res = hostapd_ioctl(drv, param, blen); - - os_free(param); - - return res; -} - - -static int hostap_set_generic_elem(void *priv, - const u8 *elem, size_t elem_len) -{ - struct hostap_driver_data *drv = priv; - - os_free(drv->generic_ie); - drv->generic_ie = NULL; - drv->generic_ie_len = 0; - if (elem) { - drv->generic_ie = os_memdup(elem, elem_len); - if (drv->generic_ie == NULL) - return -1; - drv->generic_ie_len = elem_len; - } - - return hostapd_ioctl_set_generic_elem(drv); -} - - -static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - struct hostap_driver_data *drv = priv; - - /* - * Host AP driver supports only one set of extra IEs, so we need to - * use the Probe Response IEs also for Beacon frames since they include - * more information. - */ - - os_free(drv->wps_ie); - drv->wps_ie = NULL; - drv->wps_ie_len = 0; - if (proberesp) { - drv->wps_ie = os_memdup(wpabuf_head(proberesp), - wpabuf_len(proberesp)); - if (drv->wps_ie == NULL) - return -1; - drv->wps_ie_len = wpabuf_len(proberesp); - } - - return hostapd_ioctl_set_generic_elem(drv); -} - - -static void -hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, - char *custom) -{ - wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - - if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - char *pos; - u8 addr[ETH_ALEN]; - pos = strstr(custom, "addr="); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "without sender address ignored"); - return; - } - pos += 5; - if (hwaddr_aton(pos, addr) == 0) { - union wpa_event_data data; - os_memset(&data, 0, sizeof(data)); - data.michael_mic_failure.unicast = 1; - data.michael_mic_failure.src = addr; - wpa_supplicant_event(drv->hapd, - EVENT_MICHAEL_MIC_FAILURE, &data); - } else { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "with invalid MAC address"); - } - } -} - - -static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, - char *data, unsigned int len) -{ - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf; - - pos = data; - end = data + len; - - while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) - return; - - custom = pos + IW_EV_POINT_LEN; - if (drv->we_version > 18 && - (iwe->cmd == IWEVMICHAELMICFAILURE || - iwe->cmd == IWEVCUSTOM)) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) { - case IWEVCUSTOM: - if (iwe->u.data.length > end - custom) - return; - buf = malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; - memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - hostapd_wireless_event_wireless_custom(drv, buf); - free(buf); - break; - } - - pos += iwe->len; - } -} - - -static void hostapd_wireless_event_rtm_newlink(void *ctx, - struct ifinfomsg *ifi, - u8 *buf, size_t len) -{ - struct hostap_driver_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - /* TODO: use ifi->ifi_index to filter out wireless events from other - * interfaces */ - - attrlen = len; - attr = (struct rtattr *) buf; - - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_WIRELESS) { - hostapd_wireless_event_wireless( - drv, ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } - attr = RTA_NEXT(attr, attrlen); - } -} - - -static int hostap_get_we_version(struct hostap_driver_data *drv) -{ - struct iw_range *range; - struct iwreq iwr; - int minlen; - size_t buflen; - - drv->we_version = 0; - - /* - * Use larger buffer than struct iw_range in order to allow the - * structure to grow in the future. - */ - buflen = sizeof(struct iw_range) + 500; - range = os_zalloc(buflen); - if (range == NULL) - return -1; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) range; - iwr.u.data.length = buflen; - - minlen = ((char *) &range->enc_capa) - (char *) range + - sizeof(range->enc_capa); - - if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", - strerror(errno)); - os_free(range); - return -1; - } else if (iwr.u.data.length >= minlen && - range->we_version_compiled >= 18) { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " - "WE(source)=%d enc_capa=0x%x", - range->we_version_compiled, - range->we_version_source, - range->enc_capa); - drv->we_version = range->we_version_compiled; - } - - free(range); - return 0; -} - - -static int hostap_wireless_event_init(struct hostap_driver_data *drv) -{ - struct netlink_config *cfg; - - hostap_get_we_version(drv); - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - return -1; - cfg->ctx = drv; - cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - return -1; - } - - return 0; -} - - -static void * hostap_init(struct hostapd_data *hapd, - struct wpa_init_params *params) -{ - struct hostap_driver_data *drv; - - drv = os_zalloc(sizeof(struct hostap_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for hostapd driver data\n"); - return NULL; - } - - drv->hapd = hapd; - drv->ioctl_sock = drv->sock = -1; - memcpy(drv->iface, params->ifname, sizeof(drv->iface)); - - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - os_free(drv); - return NULL; - } - - if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { - wpa_printf(MSG_ERROR, - "Could not enable hostapd mode for interface %s", - drv->iface); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - if (hostap_init_sockets(drv, params->own_addr) || - hostap_wireless_event_init(drv)) { - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - return drv; -} - - -static void hostap_driver_deinit(void *priv) -{ - struct hostap_driver_data *drv = priv; - - netlink_deinit(drv->netlink); - (void) hostap_set_iface_flags(drv, 0); - (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); - (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); - - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - - if (drv->sock >= 0) - close(drv->sock); - - os_free(drv->generic_ie); - os_free(drv->wps_ie); - - free(drv); -} - - -static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason, int link_id) -{ - struct hostap_driver_data *drv = priv; - struct ieee80211_mgmt mgmt; - - if (is_broadcast_ether_addr(addr)) { - /* - * New Prism2.5/3 STA firmware versions seem to have issues - * with this broadcast deauth frame. This gets the firmware in - * odd state where nothing works correctly, so let's skip - * sending this for the hostap driver. - */ - return 0; - } - - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DEAUTH); - memcpy(mgmt.da, addr, ETH_ALEN); - memcpy(mgmt.sa, own_addr, ETH_ALEN); - memcpy(mgmt.bssid, own_addr, ETH_ALEN); - mgmt.u.deauth.reason_code = host_to_le16(reason); - return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0, 0, NULL, 0, 0, 0, -1); -} - - -static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - struct hostap_driver_data *drv = priv; - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.freq.m = freq->channel; - iwr.u.freq.e = 0; - - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", - strerror(errno)); - return -1; - } - - return 0; -} - - -static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason) -{ - struct hostap_driver_data *drv = priv; - struct ieee80211_mgmt mgmt; - - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DISASSOC); - memcpy(mgmt.da, addr, ETH_ALEN); - memcpy(mgmt.sa, own_addr, ETH_ALEN); - memcpy(mgmt.bssid, own_addr, ETH_ALEN); - mgmt.u.disassoc.reason_code = host_to_le16(reason); - return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0, 0, NULL, 0, 0, 0, - -1); -} - - -static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, - u16 *num_modes, - u16 *flags, u8 *dfs) -{ - struct hostapd_hw_modes *mode; - int i, clen, rlen; - const short chan2freq[14] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 - }; - - mode = os_zalloc(sizeof(struct hostapd_hw_modes)); - if (mode == NULL) - return NULL; - - *num_modes = 1; - *flags = 0; - *dfs = 0; - - mode->mode = HOSTAPD_MODE_IEEE80211B; - mode->num_channels = 14; - mode->num_rates = 4; - - clen = mode->num_channels * sizeof(struct hostapd_channel_data); - rlen = mode->num_rates * sizeof(int); - - mode->channels = os_zalloc(clen); - mode->rates = os_zalloc(rlen); - if (mode->channels == NULL || mode->rates == NULL) { - os_free(mode->channels); - os_free(mode->rates); - os_free(mode); - return NULL; - } - - for (i = 0; i < 14; i++) { - mode->channels[i].chan = i + 1; - mode->channels[i].freq = chan2freq[i]; - mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20; - /* TODO: Get allowed channel list from the driver */ - if (i >= 11) - mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; - } - - mode->rates[0] = 10; - mode->rates[1] = 20; - mode->rates[2] = 55; - mode->rates[3] = 110; - - return mode; -} - - -static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, - const u8 *addr, int qos) -{ - struct ieee80211_hdr hdr; - - os_memset(&hdr, 0, sizeof(hdr)); - - /* - * WLAN_FC_STYPE_NULLFUNC would be more appropriate, - * but it is apparently not retried so TX Exc events - * are not received for it. - * This is the reason the driver overrides the default - * handling. - */ - hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_DATA); - - hdr.frame_control |= - host_to_le16(WLAN_FC_FROMDS); - os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); - os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - - hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0, - -1); -} - - -const struct wpa_driver_ops wpa_driver_hostap_ops = { - .name = "hostap", - .desc = "Host AP driver (Intersil Prism2/2.5/3)", - .set_key = wpa_driver_hostap_set_key, - .hapd_init = hostap_init, - .hapd_deinit = hostap_driver_deinit, - .set_ieee8021x = hostap_set_ieee8021x, - .set_privacy = hostap_set_privacy, - .get_seqnum = hostap_get_seqnum, - .flush = hostap_flush, - .set_generic_elem = hostap_set_generic_elem, - .read_sta_data = hostap_read_sta_data, - .hapd_send_eapol = hostap_send_eapol, - .sta_set_flags = hostap_sta_set_flags, - .sta_deauth = hostap_sta_deauth, - .sta_disassoc = hostap_sta_disassoc, - .sta_remove = hostap_sta_remove, - .hapd_set_ssid = hostap_set_ssid, - .send_mlme = hostap_send_mlme, - .sta_add = hostap_sta_add, - .get_inact_sec = hostap_get_inact_sec, - .sta_clear_stats = hostap_sta_clear_stats, - .get_hw_feature_data = hostap_get_hw_feature_data, - .set_ap_wps_ie = hostap_set_ap_wps_ie, - .set_freq = hostap_set_freq, - .poll_client = wpa_driver_hostap_poll_client, -}; diff --git a/contrib/wpa/src/drivers/driver_hostap.h b/contrib/wpa/src/drivers/driver_hostap.h deleted file mode 100644 index ac0b83ad24d1..000000000000 --- a/contrib/wpa/src/drivers/driver_hostap.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Driver interaction with Linux Host AP driver - * Copyright (c) 2002-2006, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef HOSTAP_DRIVER_H -#define HOSTAP_DRIVER_H - -/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ - -/* New wireless extensions API - SET/GET convention (even ioctl numbers are - * root only) - */ -#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) -#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) -#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) -#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) -#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) -#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) -#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) -#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) -#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) -#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) -#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) -#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) -#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) -#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) - -/* following are not in SIOCGIWPRIV list; check permission in the driver code - */ -#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) -#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) - - -/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ -enum { - /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_TXRATECTRL = 2, - PRISM2_PARAM_BEACON_INT = 3, - PRISM2_PARAM_PSEUDO_IBSS = 4, - PRISM2_PARAM_ALC = 5, - /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_DUMP = 7, - PRISM2_PARAM_OTHER_AP_POLICY = 8, - PRISM2_PARAM_AP_MAX_INACTIVITY = 9, - PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, - PRISM2_PARAM_DTIM_PERIOD = 11, - PRISM2_PARAM_AP_NULLFUNC_ACK = 12, - PRISM2_PARAM_MAX_WDS = 13, - PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, - PRISM2_PARAM_AP_AUTH_ALGS = 15, - PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, - PRISM2_PARAM_HOST_ENCRYPT = 17, - PRISM2_PARAM_HOST_DECRYPT = 18, - PRISM2_PARAM_HOST_ROAMING = 21, - PRISM2_PARAM_BCRX_STA_KEY = 22, - PRISM2_PARAM_IEEE_802_1X = 23, - PRISM2_PARAM_ANTSEL_TX = 24, - PRISM2_PARAM_ANTSEL_RX = 25, - PRISM2_PARAM_MONITOR_TYPE = 26, - PRISM2_PARAM_WDS_TYPE = 27, - PRISM2_PARAM_HOSTSCAN = 28, - PRISM2_PARAM_AP_SCAN = 29, - PRISM2_PARAM_ENH_SEC = 30, - PRISM2_PARAM_IO_DEBUG = 31, - PRISM2_PARAM_BASIC_RATES = 32, - PRISM2_PARAM_OPER_RATES = 33, - PRISM2_PARAM_HOSTAPD = 34, - PRISM2_PARAM_HOSTAPD_STA = 35, - PRISM2_PARAM_WPA = 36, - PRISM2_PARAM_PRIVACY_INVOKED = 37, - PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, - PRISM2_PARAM_DROP_UNENCRYPTED = 39, - PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, -}; - -enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, - HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; - - -/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ -enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, - AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, - AP_MAC_CMD_KICKALL = 4 }; - - -/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ -enum { - PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, - /* Note! Old versions of prism2_srec have a fatal error in CRC-16 - * calculation, which will corrupt all non-volatile downloads. - * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to - * prevent use of old versions of prism2_srec for non-volatile - * download. */ - PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, - PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, - /* Persistent versions of volatile download commands (keep firmware - * data in memory and automatically re-download after hw_reset */ - PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, - PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, -}; - -struct prism2_download_param { - u32 dl_cmd; - u32 start_addr; - u32 num_areas; - struct prism2_download_area { - u32 addr; /* wlan card address */ - u32 len; - caddr_t ptr; /* pointer to data in user space */ - } data[0]; -}; - -#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 -#define PRISM2_MAX_DOWNLOAD_LEN 262144 - - -/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ -enum { - PRISM2_HOSTAPD_FLUSH = 1, - PRISM2_HOSTAPD_ADD_STA = 2, - PRISM2_HOSTAPD_REMOVE_STA = 3, - PRISM2_HOSTAPD_GET_INFO_STA = 4, - /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ - PRISM2_SET_ENCRYPTION = 6, - PRISM2_GET_ENCRYPTION = 7, - PRISM2_HOSTAPD_SET_FLAGS_STA = 8, - PRISM2_HOSTAPD_GET_RID = 9, - PRISM2_HOSTAPD_SET_RID = 10, - PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, - PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, - PRISM2_HOSTAPD_MLME = 13, - PRISM2_HOSTAPD_SCAN_REQ = 14, - PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, -}; - -#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 -#define PRISM2_HOSTAPD_RID_HDR_LEN \ -((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) -#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) - -/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() - */ -#define HOSTAP_CRYPT_ALG_NAME_LEN 16 - - -struct prism2_hostapd_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u16 aid; - u16 capability; - u8 tx_supp_rates; - } add_sta; - struct { - u32 inactive_sec; - } get_info_sta; - struct { - u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; - u32 flags; - u32 err; - u8 idx; - u8 seq[8]; /* sequence counter (set: RX, get: TX) */ - u16 key_len; - u8 key[0]; - } crypt; - struct { - u32 flags_and; - u32 flags_or; - } set_flags_sta; - struct { - u16 rid; - u16 len; - u8 data[0]; - } rid; - struct { - u8 len; - u8 data[0]; - } generic_elem; - struct { -#define MLME_STA_DEAUTH 0 -#define MLME_STA_DISASSOC 1 - u16 cmd; - u16 reason_code; - } mlme; - struct { - u8 ssid_len; - u8 ssid[SSID_MAX_LEN]; - } scan_req; - } u; -}; - -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) -#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) - -#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 -#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 -#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 -#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 - -#endif /* HOSTAP_DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_macsec_linux.c b/contrib/wpa/src/drivers/driver_macsec_linux.c index c867154981e9..57bea3b42244 100644 --- a/contrib/wpa/src/drivers/driver_macsec_linux.c +++ b/contrib/wpa/src/drivers/driver_macsec_linux.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "utils/common.h" @@ -32,7 +33,8 @@ #define UNUSED_SCI 0xffffffffffffffff -#if LIBNL_VER_NUM >= LIBNL_VER(3, 6) +#if (LIBNL_VER_NUM >= LIBNL_VER(3, 6) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)) #define LIBNL_HAS_OFFLOAD #endif @@ -1198,6 +1200,7 @@ static int macsec_drv_create_transmit_sc( rtnl_link_put(link); wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d", err); + drv->created_link = false; return err; } @@ -1221,6 +1224,7 @@ static int macsec_drv_create_transmit_sc( drv->link = rtnl_link_macsec_alloc(); if (!drv->link) { wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link"); + drv->created_link = false; return -1; } @@ -1257,6 +1261,11 @@ static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc) return 0; } + if (!drv->link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "no link to delete"); + return -1; + } + err = rtnl_link_delete(drv->sk, drv->link); if (err < 0) wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link"); diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index 5d6db8fe9b2e..646b1f53c5b2 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -1037,6 +1037,9 @@ static int wpa_driver_ndis_set_key_wrapper(void *priv, struct wpa_driver_set_key_params *params) { + if (params->key_flag & KEY_FLAG_NEXT) + return -1; + return wpa_driver_ndis_set_key(params->ifname, priv, params->alg, params->addr, params->key_idx, params->set_tx, diff --git a/contrib/wpa/src/drivers/driver_nl80211.c b/contrib/wpa/src/drivers/driver_nl80211.c index e6fbad96d57f..f4260bec7d8f 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.c +++ b/contrib/wpa/src/drivers/driver_nl80211.c @@ -30,6 +30,9 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_common.h" +#include "common/nan_defs.h" +#include "common/nan_de.h" +#include "common/proximity_ranging.h" #include "crypto/sha256.h" #include "crypto/sha384.h" #include "netlink.h" @@ -161,9 +164,10 @@ static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, struct hostapd_freq_params *freq); static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, - const u8 *set_addr, int first, - const char *driver_params); +wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss, + const u8 *set_addr, + const char *driver_params, + enum wpa_p2p_mode p2p_mode); static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, @@ -179,16 +183,13 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, int ifidx_reason); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, int ifidx_reason); -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, - int ifidx_reason); static int nl80211_set_channel(struct i802_bss *bss, struct hostapd_freq_params *freq, int set_chan); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, - int reset_mode); +static int nl80211_leave_ibss(struct i802_bss *bss, int reset_mode); static int i802_set_iface_flags(struct i802_bss *bss, int up); static int nl80211_set_param(void *priv, const char *param); @@ -198,7 +199,10 @@ static int nl80211_put_mesh_config(struct nl_msg *msg, struct wpa_driver_mesh_bss_params *params); #endif /* CONFIG_MESH */ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason); + u16 reason, int link_id); +#ifdef CONFIG_PR +static void nl80211_pd_stop(void *priv); +#endif /* CONFIG_PR */ /* Converts nl80211_chan_width to a common format */ @@ -246,6 +250,13 @@ static int is_p2p_net_interface(enum nl80211_iftype nlmode) } +static bool nl80211_is_netdev_iftype(enum nl80211_iftype t) +{ + return t != NL80211_IFTYPE_P2P_DEVICE && t != NL80211_IFTYPE_NAN && + t != NL80211_IFTYPE_PD; +} + + struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, int ifindex) { @@ -345,6 +356,7 @@ struct nl80211_ack_err_args { int err; struct nl_msg *orig_msg; struct nl80211_err_info *err_info; + struct wpa_driver_nl80211_data *drv; }; static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, @@ -381,21 +393,34 @@ static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, if (tb[NLMSGERR_ATTR_MSG]) { len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]), nla_len(tb[NLMSGERR_ATTR_MSG])); - wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s", - len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG])); + if (err_args->drv) + wpa_msg(err_args->drv->ctx, MSG_ERROR, "nl80211: kernel reports: %*s", + len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG])); + else + wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s", + len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG])); } - if (!err_args->err_info) - return NL_SKIP; - - /* Check if it was a per-link error report */ - + /* Done if the the problematic attribute was not specified */ if (!tb[NLMSGERR_ATTR_OFFS] || os_memcmp(orig_nlh, &err->msg, sizeof(err->msg)) != 0) return NL_SKIP; offset = nla_get_u32(tb[NLMSGERR_ATTR_OFFS]); + if (offset < orig_nlh->nlmsg_len) { + struct nlattr *orig_attr = + (void *) ((unsigned char *) orig_nlh + offset); + + wpa_printf(MSG_DEBUG, + "nl80211: kernel reports error at offset %u, attribute type %d", + offset, nla_type(orig_attr)); + } + + /* Resolve problematic link if err_info is not NULL */ + if (!err_args->err_info) + return NL_SKIP; + mlo_links = nlmsg_find_attr(orig_nlh, GENL_HDRLEN, NL80211_ATTR_MLO_LINKS); if (!mlo_links) @@ -480,20 +505,146 @@ static int send_event_marker(struct wpa_driver_nl80211_data *drv) out: nlmsg_free(msg); - if (err) + if (err < 0) wpa_printf(MSG_INFO, "nl80211: %s failed: %s", __func__, nl_geterror(err)); return res; } -int send_and_recv(struct nl80211_global *global, - struct nl_sock *nl_handle, struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data, - int (*ack_handler_custom)(struct nl_msg *, void *), - void *ack_data, - struct nl80211_err_info *err_info) +struct nl80211_pending_event { + struct dl_list list; + + struct nl_msg *msg; + int (*handler)(struct nl_msg *, void *); + void *data; +}; + + +static void nl80211_remove_pending_event(struct nl80211_pending_event *event) +{ + dl_list_del(&event->list); + nlmsg_free(event->msg); + os_free(event); +} + + +static void nl80211_deliver_pending_events(void *eloop_ctx, void *data) +{ + struct nl80211_global *global = eloop_ctx; + struct dl_list pending_events; + + wpa_printf(MSG_DEBUG, "nl80211: Delivering pending events"); + + if (dl_list_empty(&global->pending_events)) + return; + + global->delivering_pending_events = true; + + dl_list_init(&pending_events); + + global->pending_events.next->prev = &pending_events; + global->pending_events.prev->next = &pending_events; + pending_events.next = global->pending_events.next; + pending_events.prev = global->pending_events.prev; + dl_list_init(&global->pending_events); + + while (!dl_list_empty(&pending_events)) { + struct nl80211_pending_event *event = + dl_list_first(&pending_events, + struct nl80211_pending_event, list); + + event->handler(event->msg, event->data); + nl80211_remove_pending_event(event); + } + + global->delivering_pending_events = false; +} + + +/* Unfortunately, libnl does not permit us to call an existing handler. If we + * could either fetch the information or if nl_cb_call was exposed, this would + * be much more elegant. Unfortunately it is not, so we need a hook in every + * event handler that might need to be overriden. + */ +int nl80211_reply_hook(struct nl80211_global *global, struct nl_msg *msg, + int (*delayed_handler)(struct nl_msg *, void *), + void *delayed_data) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct nl80211_pending_event *event; + + if (!global->sync_reply_handling) { + /* We cannot rely on eloop handling the timeout before trying + * to read the socket again (it should usually, but it may not + * if multiple timeouts have expired). As such, check here if + * there are already pending events, and if there are, queue + * this one to preserve delivery order. + * + * However, if we are already inside + * nl80211_deliver_pending_events(), do not re-queue events + * triggered by event handlers. Re-queuing during delivery + * causes events to accumulate across command cycles. When the + * accumulated event count is large enough, a re-queued event's + * sequence number eventually matches the reply_seq of a + * concurrent send_and_recv_glb() call, causing the reply + * handler to fire with the wrong message and corrupt the + * caller's stack. + */ + if (!dl_list_empty(&global->pending_events) && + !global->delivering_pending_events) + goto queue_event; + + return NL_OK; + } + + /* This may be a reply if the port ID is nonzero (broadcast). In that + * case the sequence number should also match. We will treat everything + * else as an event and queue it for later processing. + */ + if (hdr->nlmsg_pid && hdr->nlmsg_seq == global->reply_seq) { + if (global->reply_handler) + global->reply_handler(msg, global->reply_data); + + return NL_SKIP; + } + + /* Schedule a timeout to deliver the events from the eventloop. */ + if (dl_list_empty(&global->pending_events)) { + if (eloop_register_timeout(0, 0, nl80211_deliver_pending_events, + global, NULL) < 0) + wpa_printf(MSG_ERROR, "%s: failed to register timeout", + __func__); + } + +queue_event: + TEST_FAIL_TAG("queued"); + + event = os_zalloc(sizeof(*event)); + if (!event) { + wpa_printf(MSG_ERROR, + "%s: failed to allocate delayed event context", + __func__); + return NL_SKIP; + } + + event->handler = delayed_handler; + event->data = delayed_data; + nlmsg_get(msg); + event->msg = msg; + dl_list_add_tail(&global->pending_events, &event->list); + + return NL_SKIP; +} + +int send_and_recv_glb(struct nl80211_global *global, + struct wpa_driver_nl80211_data *drv, /* may be NULL */ + struct nl_sock *nl_handle, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data, + int (*ack_handler_custom)(struct nl_msg *, void *), + void *ack_data, + struct nl80211_err_info *err_info) { struct nl_cb *cb, *s_nl_cb; struct nl80211_ack_err_args err; @@ -512,13 +663,22 @@ int send_and_recv(struct nl80211_global *global, /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ opt = 1; - setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, - NETLINK_EXT_ACK, &opt, sizeof(opt)); + if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_EXT_ACK, &opt, sizeof(opt)) < 0) + wpa_printf(MSG_DEBUG, + "nl80211: setsockopt(NETLINK_EXT_ACK) failed: %s (ignored)", + strerror(errno)); /* try to set NETLINK_CAP_ACK to 1, ignoring errors */ opt = 1; - setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, - NETLINK_CAP_ACK, &opt, sizeof(opt)); + if (setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_CAP_ACK, &opt, sizeof(opt)) < 0) + wpa_printf(MSG_DEBUG, + "nl80211: setsockopt(NETLINK_CAP_ACK) failed: %s (ignored)", + strerror(errno)); + + if (TEST_FAIL_TAG("pre-send-sleep")) + os_sleep(1, 0); err.err = nl_send_auto_complete(nl_handle, msg); if (err.err < 0) { @@ -535,6 +695,7 @@ int send_and_recv(struct nl80211_global *global, err.err = 1; err.orig_msg = msg; err.err_info = err_info; + err.drv = drv; nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err); @@ -545,12 +706,13 @@ int send_and_recv(struct nl80211_global *global, nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_custom, ack_data); } else { - nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err.err); } - if (valid_handler) - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, - valid_handler, valid_data); + global->sync_reply_handling = true; + global->reply_seq = nlmsg_hdr(msg)->nlmsg_seq; + global->reply_handler = valid_handler; + global->reply_data = valid_data; while (err.err > 0) { int res = nl_recvmsgs(nl_handle, cb); @@ -574,6 +736,12 @@ int send_and_recv(struct nl80211_global *global, __func__, res, nl_geterror(res)); } } + + global->sync_reply_handling = false; + global->reply_seq = 0; + global->reply_handler = NULL; + global->reply_data = NULL; + out: nl_cb_put(cb); /* Always clear the message as it can potentially contain keys */ @@ -650,8 +818,8 @@ static int nl_get_multicast_id(struct nl80211_global *global, return -1; } - ret = send_and_recv(global, global->nl, msg, family_handler, &res, - NULL, NULL, NULL); + ret = send_and_recv_glb(global, NULL, global->nl, msg, family_handler, + &res, NULL, NULL, NULL); if (ret == 0) ret = res.id; return ret; @@ -739,6 +907,8 @@ struct wiphy_idx_data { enum nl80211_iftype nlmode; u8 *macaddr; u8 use_4addr; + u64 wdev_id; + bool wdev_id_set; }; @@ -764,6 +934,11 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_4ADDR]) info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]); + if (tb[NL80211_ATTR_WDEV]) { + info->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + info->wdev_id_set = true; + } + return NL_SKIP; } @@ -846,7 +1021,7 @@ static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, return -1; } - ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL, + ret = send_and_recv(drv, w->nl_beacons, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " @@ -947,12 +1122,14 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, "wiphy beacons"); if (w->nl_beacons == NULL) { + nl_cb_put(w->nl_cb); os_free(w); return NULL; } if (nl80211_register_beacons(bss->drv, w)) { nl_destroy_handles(&w->nl_beacons); + nl_cb_put(w->nl_cb); os_free(w); return NULL; } @@ -1103,7 +1280,7 @@ static int nl80211_get_sta_mlo_info(void *priv, if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { struct nl_msg *msg; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE); if (send_and_recv_resp(drv, msg, get_mlo_info, &drv->sta_mlo_info)) return -1; @@ -1114,23 +1291,37 @@ static int nl80211_get_sta_mlo_info(void *priv, } +int get_sta_mlo_interface_info(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE); + if (!msg || + send_and_recv_resp(drv, msg, get_mlo_info, &drv->sta_mlo_info)) + return -1; + + return 0; +} + + static void wpa_driver_nl80211_event_newlink( - struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + struct nl80211_global *global, struct i802_bss *bss, int ifindex, const char *ifname) { union wpa_event_data event; - if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { - if (if_nametoindex(drv->first_bss->ifname) == 0) { + if (bss && os_strcmp(bss->ifname, ifname) == 0) { + if (if_nametoindex(bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", - drv->first_bss->ifname); + bss->ifname); return; } - if (!drv->if_removed) + if (!bss->if_removed) return; wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event", - drv->first_bss->ifname); - drv->if_removed = 0; + bss->ifname); + bss->if_removed = 0; } os_memset(&event, 0, sizeof(event)); @@ -1138,8 +1329,8 @@ static void wpa_driver_nl80211_event_newlink( os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_ADDED; - if (drv) - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (bss) + wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_STATUS, &event); else wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, &event); @@ -1147,20 +1338,20 @@ static void wpa_driver_nl80211_event_newlink( static void wpa_driver_nl80211_event_dellink( - struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + struct nl80211_global *global, struct i802_bss *bss, int ifindex, const char *ifname) { union wpa_event_data event; - if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { - if (drv->if_removed) { + if (bss && os_strcmp(bss->ifname, ifname) == 0) { + if (bss->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", ifname); return; } wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1", ifname); - drv->if_removed = 1; + bss->if_removed = 1; } else { wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed", ifname); @@ -1171,15 +1362,15 @@ static void wpa_driver_nl80211_event_dellink( os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; - if (drv) - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (bss) + wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_STATUS, &event); else wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, &event); } -static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, +static int wpa_driver_nl80211_own_ifname(const char *ifname, u8 *buf, size_t len) { int attrlen, rta_len; @@ -1191,8 +1382,7 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { - if (os_strcmp(((char *) attr) + rta_len, - drv->first_bss->ifname) == 0) + if (os_strcmp(((char *) attr) + rta_len, ifname) == 0) return 1; else break; @@ -1204,17 +1394,24 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, } -static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, +static int wpa_driver_nl80211_own_ifindex(struct i802_bss *bss, int ifindex, u8 *buf, size_t len) { - if (drv->ifindex == ifindex) + if (bss->ifindex == ifindex) return 1; - if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { - nl80211_check_global(drv->global); + if (bss->if_removed && + wpa_driver_nl80211_own_ifname(bss->ifname, buf, len)) { + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->first_bss == bss) + nl80211_check_global(drv->global); + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0) + del_ifidx(drv, bss->ifindex, IFIDX_ANY); + if (wpa_driver_nl80211_finish_drv_init(bss, NULL, NULL, + WPA_P2P_MODE_WFD_R1) < 0) return -1; return 1; } @@ -1223,8 +1420,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, } -static struct wpa_driver_nl80211_data * -nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len, +static struct i802_bss * +nl80211_find_bss(struct nl80211_global *global, int idx, u8 *buf, size_t len, int *init_failed) { struct wpa_driver_nl80211_data *drv; @@ -1234,16 +1431,21 @@ nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len, *init_failed = 0; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { - res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len); - if (res < 0) { - wpa_printf(MSG_DEBUG, - "nl80211: Found matching own interface, but failed to complete reinitialization"); - if (init_failed) - *init_failed = 1; - return drv; + struct i802_bss *bss; + + for (bss = drv->first_bss; bss; bss = bss->next) { + res = wpa_driver_nl80211_own_ifindex(bss, idx, buf, + len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Found matching own interface, but failed to complete reinitialization"); + if (init_failed) + *init_failed = 1; + return bss; + } + if (res > 0) + return bss; } - if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY)) - return drv; } return NULL; } @@ -1282,7 +1484,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, u8 *buf, size_t len) { struct nl80211_global *global = ctx; - struct wpa_driver_nl80211_data *drv; + struct wpa_driver_nl80211_data *drv = NULL; + struct i802_bss *bss = NULL; int attrlen; struct rtattr *attr; u32 brid = 0; @@ -1334,36 +1537,34 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed); - if (!drv) + bss = nl80211_find_bss(global, ifi->ifi_index, buf, len, &init_failed); + if (!bss) goto event_newlink; if (init_failed) return; /* do not update interface state */ - if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + drv = bss->drv; + + if (!bss->if_disabled && !(ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); - drv->ignore_if_down_event = 0; + bss->ignore_if_down_event = 0; /* Re-read MAC address as it may have changed */ nl80211_refresh_mac(drv, ifi->ifi_index, 1); return; } wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)", namebuf, ifname); - if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { - wpa_printf(MSG_DEBUG, - "nl80211: Not the main interface (%s) - do not indicate interface down", - drv->first_bss->ifname); - } else if (drv->ignore_if_down_event) { + if (bss->ignore_if_down_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event generated by mode change"); - drv->ignore_if_down_event = 0; + bss->ignore_if_down_event = 0; } else { - drv->if_disabled = 1; - wpa_supplicant_event(drv->ctx, + bss->if_disabled = 1; + wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_DISABLED, NULL); /* @@ -1371,14 +1572,17 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, * part of the EVENT_INTERFACE_DISABLED handling for * dynamic interfaces */ - drv = nl80211_find_drv(global, ifi->ifi_index, - buf, len, NULL); - if (!drv) + drv = NULL; + bss = nl80211_find_bss(global, ifi->ifi_index, buf, len, + &init_failed); + if (!bss) return; + + drv = bss->drv; } } - if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (bss->if_disabled && (ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) { @@ -1389,24 +1593,20 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, } wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)", namebuf, ifname); - if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { - wpa_printf(MSG_DEBUG, - "nl80211: Not the main interface (%s) - do not indicate interface up", - drv->first_bss->ifname); - } else if (if_nametoindex(drv->first_bss->ifname) == 0) { + if (if_nametoindex(bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", - drv->first_bss->ifname); - } else if (drv->if_removed) { + bss->ifname); + } else if (bss->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is marked " - "removed", drv->first_bss->ifname); + "removed", bss->ifname); } else { /* Re-read MAC address as it may have changed */ nl80211_refresh_mac(drv, ifi->ifi_index, 0); - drv->if_disabled = 0; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + bss->if_disabled = 0; + wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_ENABLED, NULL); } } @@ -1417,22 +1617,20 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, * fails. This will hit us when wpa_supplicant does not need to do * IEEE 802.1X authentication */ - if (drv->operstate == 1 && + if (bss->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) { wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate"); - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, -1, IF_OPER_UP); } event_newlink: if (ifname[0]) - wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index, + wpa_driver_nl80211_event_newlink(global, bss, ifi->ifi_index, ifname); - if (ifi->ifi_family == AF_BRIDGE && brid && drv) { - struct i802_bss *bss; - + if (ifi->ifi_family == AF_BRIDGE && brid && drv && bss) { /* device has been added to bridge */ if (!if_indextoname(brid, namebuf)) { wpa_printf(MSG_DEBUG, @@ -1444,12 +1642,8 @@ event_newlink: brid, namebuf); add_ifidx(drv, brid, ifi->ifi_index); - for (bss = drv->first_bss; bss; bss = bss->next) { - if (os_strcmp(ifname, bss->ifname) == 0) { - os_strlcpy(bss->brname, namebuf, IFNAMSIZ); - break; - } - } + if (os_strcmp(ifname, bss->ifname) == 0) + os_strlcpy(bss->brname, namebuf, IFNAMSIZ); } } @@ -1459,7 +1653,8 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, u8 *buf, size_t len) { struct nl80211_global *global = ctx; - struct wpa_driver_nl80211_data *drv; + struct wpa_driver_nl80211_data *drv = NULL; + struct i802_bss *bss = NULL; int attrlen; struct rtattr *attr; u32 brid = 0; @@ -1506,7 +1701,9 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL); + bss = nl80211_find_bss(global, ifi->ifi_index, buf, len, NULL); + if (bss) + drv = bss->drv; if (ifi->ifi_family == AF_BRIDGE && brid && drv) { /* device has been removed from bridge */ @@ -1525,7 +1722,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, } if (ifi->ifi_family != AF_BRIDGE || !brid) - wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index, + wpa_driver_nl80211_event_dellink(global, bss, ifi->ifi_index, ifname); } @@ -1717,6 +1914,54 @@ try_again: } +const u8 * nl80211_get_assoc_bssid(struct i802_bss *bss) +{ + struct nl_msg *msg; + int ret; + struct nl80211_get_assoc_freq_arg arg; + int count = 0; + struct wpa_driver_nl80211_data *drv = bss->drv; + +try_again: + msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + if (!msg) + return NULL; + os_memset(&arg, 0, sizeof(arg)); + arg.drv = drv; + ret = send_and_recv_resp(drv, msg, nl80211_get_assoc_freq_handler, + &arg); + if (ret == -EAGAIN) { + count++; + if (count >= 10) { + wpa_printf(MSG_INFO, + "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid"); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to receive consistent scan result dump for get_assoc_bssid - try again"); + goto try_again; + } + } + if (ret == 0) { + os_memcpy(drv->bssid, arg.assoc_bssid, ETH_ALEN); + + if (drv->sta_mlo_info.valid_links) { + int i; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + os_memcpy(drv->sta_mlo_info.links[i].bssid, + arg.bssid[i], ETH_ALEN); + } + + return drv->bssid; + } + + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)", + ret, strerror(-ret)); + + return NULL; +} + + static int get_link_noise(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1761,15 +2006,16 @@ static int get_link_noise(struct nl_msg *msg, void *arg) } -int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, +int nl80211_get_link_noise(struct i802_bss *bss, struct wpa_signal_info *sig_change) { struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; sig_change->current_noise = WPA_INVALID_NOISE; sig_change->frequency = drv->assoc_freq; - msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); return send_and_recv_resp(drv, msg, get_link_noise, sig_change); } @@ -1833,7 +2079,7 @@ static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci) struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE); return send_and_recv_resp(drv, msg, get_channel_info, ci); } @@ -1972,6 +2218,26 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) if (global->nl_event == NULL) goto err; + /* Needs to be registered early so that process_global_event() calls + * the sync reply handler hook. + */ + nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_global_event, global); + +#ifdef CONFIG_NAN + ret = nl_get_multicast_id(global, "nl80211", "config"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "nl80211: Could not add multicast membership for config events: %d (%s)", + ret, nl_geterror(ret)); + goto err; + } +#endif /* CONFIG_NAN */ + ret = nl_get_multicast_id(global, "nl80211", "scan"); if (ret >= 0) ret = nl_socket_add_membership(global->nl_event, ret); @@ -2035,11 +2301,6 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) genl_family_put(family); nl_cache_free(cache); - nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, - no_seq_check, NULL); - nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, - process_global_event, global); - nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, global->nl_cb, 0); @@ -2091,10 +2352,10 @@ static void wpa_driver_nl80211_rfkill_blocked(void *ctx) wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); /* - * rtnetlink ifdown handler will report interfaces other than the P2P - * Device interface as disabled. + * rtnetlink ifdown handler will report interfaces other than the + * P2P/NAN Device interfaces as disabled. */ - if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (!nl80211_is_netdev_iftype(drv->nlmode)) wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); } @@ -2113,10 +2374,10 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) nl80211_disable_11b_rates(drv, drv->ifindex, 1); /* - * rtnetlink ifup handler will report interfaces other than the P2P - * Device interface as enabled. + * rtnetlink ifup handler will report interfaces other than the P2P/NAN + * Device interfaces as enabled. */ - if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (!nl80211_is_netdev_iftype(drv->nlmode)) wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); } @@ -2218,11 +2479,21 @@ static int nl80211_init_bss(struct i802_bss *bss) static void nl80211_destroy_bss(struct i802_bss *bss) { + struct nl80211_pending_event *event, *next; + nl_cb_put(bss->nl_cb); bss->nl_cb = NULL; if (bss->nl_connect) nl80211_destroy_eloop_handle(&bss->nl_connect, 1); + + dl_list_for_each_safe(event, next, &bss->drv->global->pending_events, + struct nl80211_pending_event, list) { + if (event->data != bss) + continue; + + nl80211_remove_pending_event(event); + } } @@ -2240,25 +2511,27 @@ wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv) rcfg->ctx = drv; - /* rfkill uses netdev sysfs for initialization. However, P2P Device is - * not associated with a netdev, so use the name of some other interface - * sharing the same wiphy as the P2P Device interface. + /* rfkill uses netdev sysfs for initialization. However, P2P/NAN Device + * is not associated with a netdev, so use the name of some other + * interface sharing the same wiphy as the P2P/NAN Device interface. * - * Note: This is valid, as a P2P Device interface is always dynamically - * created and is created only once another wpa_s interface was added. + * Note: This is valid, as a P2P/NAN Device interface is always + * dynamically created and is created only once another wpa_s interface + * was added. */ - if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) { + if (!nl80211_is_netdev_iftype(drv->nlmode)) { struct nl80211_global *global = drv->global; struct wpa_driver_nl80211_data *tmp1; dl_list_for_each(tmp1, &global->interfaces, struct wpa_driver_nl80211_data, list) { if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx || - !tmp1->rfkill) + !tmp1->rfkill || + !nl80211_is_netdev_iftype(tmp1->nlmode)) continue; wpa_printf(MSG_DEBUG, - "nl80211: Use (%s) to initialize P2P Device rfkill", + "nl80211: Use (%s) to initialize P2P/NAN Device rfkill", tmp1->first_bss->ifname); os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname, sizeof(rcfg->ifname)); @@ -2282,8 +2555,10 @@ wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv) static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr, - const char *driver_params) + const char *driver_params, + enum wpa_p2p_mode p2p_mode) { + static unsigned int next_unique_drv_id = 0; struct wpa_driver_nl80211_data *drv; struct i802_bss *bss; char path[128], buf[200], *pos; @@ -2316,6 +2591,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, drv->ctx = ctx; drv->hostapd = !!hostapd; drv->eapol_sock = -1; + drv->unique_drv_id = next_unique_drv_id++; /* * There is no driver capability flag for this, so assume it is @@ -2337,15 +2613,14 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, bss->ctx = ctx; os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); - drv->monitor_ifidx = -1; - drv->monitor_sock = -1; drv->eapol_tx_sock = -1; drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; if (nl80211_init_bss(bss)) goto failed; - if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) + if (wpa_driver_nl80211_finish_drv_init(bss, set_addr, driver_params, + p2p_mode)) goto failed; if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) { @@ -2366,9 +2641,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, "nl80211: wifi status sockopt failed: %s", strerror(errno)); drv->data_tx_status = 0; - if (!drv->use_monitor) - drv->capa.flags &= - ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; } else { eloop_register_read_sock( drv->eapol_tx_sock, @@ -2388,6 +2661,7 @@ skip_wifi_status: * Use link ID 0 for the single "link" of a non-MLD. */ bss->valid_links = 0; + bss->active_links = 0; bss->flink = &bss->links[0]; os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN); @@ -2408,10 +2682,11 @@ failed: * Returns: Pointer to private data, %NULL on failure */ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, - void *global_priv) + void *global_priv, + enum wpa_p2p_mode p2p_mode) { return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL, - NULL); + NULL, p2p_mode); } @@ -2439,7 +2714,7 @@ static int nl80211_register_frame(struct i802_bss *bss, return -1; } - ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL, + ret = send_and_recv(drv, nl_handle, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register frame command " @@ -2493,6 +2768,92 @@ static int nl80211_register_action_frame(struct i802_bss *bss, } +#define NAN_SDF_ACTION ((const u8 *) "\x04\x09\x50\x6f\x9a\x13") +#define NAN_PROTECTED_SDF_ACTION ((const u8 *) "\x09\x09\x50\x6f\x9a\x13") +#define NAN_NAF_ACTION ((const u8 *) "\x04\x09\x50\x6f\x9a\x18") +#define NAN_PROTECTED_NAF_ACTION ((const u8 *) "\x09\x09\x50\x6f\x9a\x18") + +static int nl80211_mgmt_subscribe_nan(struct i802_bss *bss, + enum nl80211_iftype nlmode) +{ +#ifdef CONFIG_NAN + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!(drv->capa.nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) { + wpa_printf(MSG_DEBUG, + "nl80211: User space DE is not supported, don't subscribe to NAN public action frames"); + return 0; + } + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + + wpa_printf(MSG_DEBUG, + "nl80211: Subscribe to mgmt frames for NAN with handle %p: nlmode=%u", + bss->nl_mgmt, nlmode); + + /* NAN SDF Public Action */ + if (nlmode == NL80211_IFTYPE_NAN && + nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, true) < 0) { + /* fallback to non-multicast */ + if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, + false) < 0) { + wpa_printf(MSG_INFO, + "nl80211: Failed to subscribe to NAN public action frames"); + nl_destroy_handles(&bss->nl_mgmt); + return -1; + } + } + + /* NAF Public Action */ + if (nl80211_register_action_frame2(bss, NAN_NAF_ACTION, 6, true)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to subscribe to NAFs"); + nl_destroy_handles(&bss->nl_mgmt); + return -1; + } + +#ifdef CONFIG_PASN + /* NAN Protected SDF Public Action */ + if (nlmode == NL80211_IFTYPE_NAN && + nl80211_register_action_frame2(bss, NAN_PROTECTED_SDF_ACTION, 6, + true)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to subscribe to NAN protected SDFs"); + nl_destroy_handles(&bss->nl_mgmt); + return -1; + } + + /* Register for PASN Authentication frames */ + if (nlmode == NL80211_IFTYPE_NAN && + nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4), + (u8 *) "\x07\x00", 2, false)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to subscribe to NAN PASN Authentication frames"); + nl_destroy_handles(&bss->nl_mgmt); + return -1; + } +#endif /* CONFIG_PASN */ + + /* NAF in Protected Dual of Public Action frame */ + if (nl80211_register_action_frame2(bss, NAN_PROTECTED_NAF_ACTION, 6, + true)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to subscribe to protected NAFs"); + nl_destroy_handles(&bss->nl_mgmt); + return -1; + } + + nl80211_mgmt_handle_register_eloop(bss); + +#endif /* CONFIG_NAN */ + + return 0; +} + + static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -2514,6 +2875,24 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) (u8 *) "\x03\x00", 2, false); } +#ifdef CONFIG_ENC_ASSOC + if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_EPPKE) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + /* register for EPPKE Authentication frames */ + nl80211_register_frame(bss, bss->nl_mgmt, type, + (const u8 *) "\x09\x00", 2, false); + } +#endif /* CONFIG_ENC_ASSOC */ + +#ifdef CONFIG_IEEE8021X_AUTH + if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_802_1X_AUTH) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + /* register for IEEE 802.1X Authentication frames */ + nl80211_register_frame(bss, bss->nl_mgmt, type, + (const u8 *) "\x08\x00", 2, false); + } +#endif /* CONFIG_IEEE8021X_AUTH */ + #ifdef CONFIG_PASN /* register for PASN Authentication frames */ if (nl80211_register_frame(bss, bss->nl_mgmt, type, @@ -2565,15 +2944,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_NAN_USD -#define NAN_PUB_ACTION ((u8 *) "\x04\x09\x50\x6f\x9a\x13") /* NAN SDF Public Action */ - if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6, true) < 0) { + if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, true) < 0) { /* fallback to non-multicast */ - if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6, + if (nl80211_register_action_frame2(bss, NAN_SDF_ACTION, 6, false) < 0) ret = -1; } -#undef NAN_PUB_ACTION #endif /* CONFIG_NAN_USD */ #ifdef CONFIG_DPP /* DPP Public Action */ @@ -2719,7 +3096,7 @@ static int nl80211_register_spurious_class3(struct i802_bss *bss) int ret; msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME); - ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL, + ret = send_and_recv(bss->drv, bss->nl_mgmt, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " @@ -2766,6 +3143,14 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0) ret = -1; #endif /* CONFIG_FST */ +#ifdef CONFIG_IEEE80211BE + /* Protected EHT - Link Reconfiguration Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x25\x0b", 2) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211BE */ + /* Vendor-specific Protected */ + if (nl80211_register_action_frame(bss, (u8 *) "\x7e", 1) < 0) + ret = -1; /* Vendor-specific */ if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0) ret = -1; @@ -2868,7 +3253,7 @@ static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) } -static void nl80211_del_p2pdev(struct i802_bss *bss) +static void nl80211_del_non_netdev(struct i802_bss *bss) { struct nl_msg *msg; int ret; @@ -2876,9 +3261,13 @@ static void nl80211_del_p2pdev(struct i802_bss *bss) msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE); ret = send_and_recv_cmd(bss->drv, msg); - wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", + wpa_printf(MSG_DEBUG, "nl80211: Delete P2P/NAN Device %s (0x%llx): %s", bss->ifname, (long long unsigned int) bss->wdev_id, strerror(-ret)); +#ifdef CONFIG_NAN + if (bss->drv->nlmode == NL80211_IFTYPE_NAN && bss->drv->global->nl_nan) + nl80211_destroy_eloop_handle(&bss->drv->global->nl_nan, 0); +#endif /* CONFIG_NAN */ } @@ -2899,18 +3288,87 @@ static int nl80211_set_p2pdev(struct i802_bss *bss, int start) } +static int nl80211_set_pr_dev(struct i802_bss *bss, bool start) +{ + struct nl_msg *msg; + int ret; + + msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_PD : + NL80211_CMD_STOP_PD); + ret = send_and_recv_cmd(bss->drv, msg); + + wpa_printf(MSG_DEBUG, "nl80211: %s PD Device %s (0x%llx): %s", + start ? "Start" : "Stop", + bss->ifname, (unsigned long long) bss->wdev_id, + strerror(-ret)); + return ret; +} + + +#ifdef CONFIG_NAN +static void nl80211_nan_stop(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started) + return; + + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_STOP_NAN); + if (!msg) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to alloc NAN stop command"); + return; + } + + if (send_and_recv_resp(drv, msg, NULL, NULL)) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN stop command"); + + drv->nan_started = 0; +} +#endif /* CONFIG_NAN */ + + +static int nl80211_set_nandev(struct i802_bss *bss, int start) +{ +#ifdef CONFIG_NAN + /* + * We don't implicitly start NAN. + * NAN is started through a dedicated API, however we do need to + * stop it. + * For rfkill, we rely on ENABLED/DISABLED events. + */ + if (start) + return 0; + + nl80211_nan_stop(bss); + +#endif /* CONFIG_NAN */ + return 0; +} + + static int i802_set_iface_flags(struct i802_bss *bss, int up) { enum nl80211_iftype nlmode; nlmode = nl80211_get_ifmode(bss); - if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { - return linux_set_iface_flags(bss->drv->global->ioctl_sock, - bss->ifname, up); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + /* P2P Device has start/stop which is equivalent */ + return nl80211_set_p2pdev(bss, up); } - /* P2P Device has start/stop which is equivalent */ - return nl80211_set_p2pdev(bss, up); + if (nlmode == NL80211_IFTYPE_NAN) + return nl80211_set_nandev(bss, up); + + if (nlmode == NL80211_IFTYPE_PD) { + /* PR Device has start/stop which is equivalent */ + return nl80211_set_pr_dev(bss, up); + } + + return linux_set_iface_flags(bss->drv->global->ioctl_sock, + bss->ifname, up); } @@ -2968,33 +3426,113 @@ static void qca_vendor_test(struct wpa_driver_nl80211_data *drv) } -static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, - const u8 *set_addr, int first, - const char *driver_params) +#ifdef CONFIG_DRIVER_NL80211_QCA +static int nl80211_set_p2p_mode(void *priv, enum wpa_p2p_mode mode) { - struct i802_bss *bss = drv->first_bss; + struct i802_bss *bss = priv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + enum qca_wlan_vendor_p2p_mode drv_mode; + + wpa_printf(MSG_DEBUG, "nl80211: Set P2P mode: %d", mode); + + switch (mode) { + case WPA_P2P_MODE_WFD_R1: + drv_mode = QCA_P2P_MODE_WFD_R1; + break; + case WPA_P2P_MODE_WFD_R2: + drv_mode = QCA_P2P_MODE_WFD_R2; + break; + case WPA_P2P_MODE_WFD_PCC: + drv_mode = QCA_P2P_MODE_WFD_PCC; + break; + default: + return -1; + } + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG, drv_mode)) + goto fail; + + nla_nest_end(msg, container); + + ret = send_and_recv_cmd(bss->drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to set P2P Mode: ret=%d (%s)", + ret, strerror(-ret)); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + +static int +wpa_driver_nl80211_finish_drv_init(struct i802_bss *bss, const u8 *set_addr, + const char *driver_params, + enum wpa_p2p_mode p2p_mode) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; int send_rfkill_event = 0; enum nl80211_iftype nlmode; + bool first = drv->first_bss == bss; - drv->ifindex = if_nametoindex(bss->ifname); - bss->ifindex = drv->ifindex; + bss->ifindex = if_nametoindex(bss->ifname); + if (first) + drv->ifindex = bss->ifindex; bss->wdev_id = drv->global->if_add_wdevid; bss->wdev_id_set = drv->global->if_add_wdevid_set; + /* + * wdev_id is set for wdev-only interfaces during creation. For netdev + * interfaces it is not set, so fetch it via GET_INTERFACE to allow + * event routing by exact wdev_id match. + */ + if (!bss->wdev_id_set) { + struct nl_msg *msg; + struct wiphy_idx_data info; + + os_memset(&info, 0, sizeof(info)); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE); + if (msg && + send_and_recv_resp(drv, msg, netdev_info_handler, + &info) == 0 && + info.wdev_id_set) { + bss->wdev_id = info.wdev_id; + bss->wdev_id_set = 1; + wpa_printf(MSG_DEBUG, "nl80211: %s wdev_id=0x%llx", + bss->ifname, + (unsigned long long) bss->wdev_id); + } + } + bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex; bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set; - drv->global->if_add_wdevid_set = 0; + if (first) + drv->global->if_add_wdevid_set = 0; if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) bss->static_ap = 1; - if (first && - nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + if (first && nl80211_is_netdev_iftype(nl80211_get_ifmode(bss)) && linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) - drv->start_iface_up = 1; + bss->start_iface_up = 1; - if (wpa_driver_nl80211_capa(drv)) + if (first && wpa_driver_nl80211_capa(drv)) return -1; if (driver_params && nl80211_set_param(bss, driver_params) < 0) @@ -3020,16 +3558,21 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, else nlmode = NL80211_IFTYPE_STATION; - if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { + if (first && wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode"); return -1; } - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (!nl80211_is_netdev_iftype(nl80211_get_ifmode(bss))) nl80211_get_macaddr(bss); wpa_driver_nl80211_drv_init_rfkill(drv); +#ifdef CONFIG_DRIVER_NL80211_QCA + if (nlmode == NL80211_IFTYPE_P2P_GO) + nl80211_set_p2p_mode(bss, p2p_mode); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + if (!rfkill_is_blocked(drv->rfkill)) { int ret = i802_set_iface_flags(bss, 1); if (ret) { @@ -3042,36 +3585,45 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1); - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (nlmode == NL80211_IFTYPE_P2P_DEVICE || + nlmode == NL80211_IFTYPE_PD) return ret; } else { wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " "interface '%s' due to rfkill", bss->ifname); if (nlmode != NL80211_IFTYPE_P2P_DEVICE) - drv->if_disabled = 1; + bss->if_disabled = 1; send_rfkill_event = 1; } - if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE) - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + if (!drv->hostapd && nl80211_is_netdev_iftype(nlmode)) + netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, 1, IF_OPER_DORMANT); - if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (nl80211_is_netdev_iftype(nlmode)) { if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr)) return -1; - os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); + if (first) + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); } - if (send_rfkill_event) { + if (send_rfkill_event) eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, - drv, drv->ctx); - } + drv, bss->ctx); if (drv->vendor_cmd_test_avail) qca_vendor_test(drv); + if (bss->added_if_into_bridge && + linux_br_add_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) { + wpa_printf(MSG_WARNING, + "nl80211: Failed to re-add interface %s into bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + return 0; } @@ -3083,7 +3635,7 @@ static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss, struct wpa_driver_nl80211_data *drv = bss->drv; struct i802_link *link = nl80211_get_link(bss, link_id); - if (!link->beacon_set) + if (!link || !link->beacon_set) return 0; wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", @@ -3140,6 +3692,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) eloop_unregister_read_sock(drv->eapol_tx_sock); if (drv->eapol_tx_sock >= 0) close(drv->eapol_tx_sock); +#ifdef CONFIG_PR + nl80211_pd_stop(bss); +#endif /* CONFIG_PR */ if (bss->nl_preq) wpa_driver_nl80211_probe_req_report(bss, 0); @@ -3151,9 +3706,6 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) bss->ifname, bss->brname, strerror(errno)); } - if (drv->rtnl_sk) - nl_socket_free(drv->rtnl_sk); - if (bss->added_bridge) { if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname, 0) < 0) @@ -3166,13 +3718,14 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) bss->brname, strerror(errno)); } - nl80211_remove_monitor_interface(drv); - if (is_ap_interface(drv->nlmode)) { wpa_driver_nl80211_del_beacon_all(bss); nl80211_remove_links(bss); } + if (drv->rtnl_sk) + nl_socket_free(drv->rtnl_sk); + if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); close(drv->eapol_sock); @@ -3184,14 +3737,14 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, + netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, 0, IF_OPER_UP); eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx); rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - if (!drv->start_iface_up) + if (!bss->start_iface_up) (void) i802_set_iface_flags(bss, 0); if (drv->addr_changed) { @@ -3207,15 +3760,16 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) } } - if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (nl80211_is_netdev_iftype(drv->nlmode)) { if (drv->start_mode_sta) wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); nl80211_mgmt_unsubscribe(bss, "deinit"); } else { nl80211_mgmt_unsubscribe(bss, "deinit"); - nl80211_del_p2pdev(bss); + nl80211_del_non_netdev(bss); } + nl80211_put_wiphy_data_ap(bss); nl80211_destroy_bss(drv->first_bss); @@ -3351,7 +3905,6 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[], __AKM(FT_SAE, FT_SAE); __AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY); __AKM(CCKM, CCKM); - __AKM(OSEN, OSEN); __AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B); __AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192); __AKM(FILS_SHA256, FILS_SHA256); @@ -3368,17 +3921,41 @@ static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[], } +static int wpa_ver_supported(struct wpa_driver_nl80211_data *drv, + int key_mgmt_suites, int proto) +{ + enum nl80211_wpa_versions ver = 0; + + if (proto & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (proto & WPA_PROTO_RSN) { + /* + * NL80211_ATTR_SAE_PASSWORD is related and was added + * at the same time as NL80211_WPA_VERSION_3. + */ + if (nl80211_attr_supported(drv, NL80211_ATTR_SAE_PASSWORD) && + wpa_key_mgmt_sae(key_mgmt_suites)) + ver |= NL80211_WPA_VERSION_3; + else + ver |= NL80211_WPA_VERSION_2; + } + + return ver; +} + + #ifdef CONFIG_DRIVER_NL80211_QCA -static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, - const u8 *key, size_t key_len) +static int issue_key_mgmt_set_key(struct i802_bss *bss, const u8 *key, + size_t key_len) { struct nl_msg *msg; int ret; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) return 0; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) || @@ -3399,12 +3976,12 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_DRIVER_NL80211_QCA */ -static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, - const u8 *key, size_t key_len, +static int nl80211_set_pmk(struct i802_bss *bss, const u8 *key, size_t key_len, const u8 *addr) { struct nl_msg *msg = NULL; int ret; + struct wpa_driver_nl80211_data *drv = bss->drv; /* * If the authenticator address is not set, assume it is @@ -3418,7 +3995,7 @@ static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR, MAC2STR(addr)); wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_PMK); if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put(msg, NL80211_ATTR_PMK, key_len, key)) { @@ -3446,6 +4023,7 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, struct nl_msg *key_msg; int ret; int skip_set_key = 1; + bool is_pd_bss = false; const char *ifname = params->ifname; enum wpa_alg alg = params->alg; const u8 *addr = params->addr; @@ -3464,6 +4042,20 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, return 0; ifindex = if_nametoindex(ifname); + +#ifdef CONFIG_PR + /* Route key operation to PD wdev if own_addr matches */ + if (drv->pd_bss && params->own_addr && + ether_addr_equal(params->own_addr, drv->pd_bss->addr)) { + bss = drv->pd_bss; + is_pd_bss = true; + wpa_printf(MSG_DEBUG, + "nl80211: set_key: routing to PD wdev " MACSTR, + MAC2STR(bss->addr)); + ifindex = 0; /* PD wdev has no ifindex */ + } +#endif /* CONFIG_PR */ + wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d", __func__, ifindex, ifname, alg, addr, key_idx, set_tx, @@ -3480,19 +4072,27 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", __func__); - ret = issue_key_mgmt_set_key(drv, key, key_len); + ret = issue_key_mgmt_set_key(bss, key, key_len); return ret; } #endif /* CONFIG_DRIVER_NL80211_QCA */ if (key_flag & KEY_FLAG_PMK) { if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) - return nl80211_set_pmk(drv, key, key_len, addr); + return nl80211_set_pmk(bss, key, key_len, addr); /* The driver does not have any offload mechanism for PMK, so * there is no need to configure this key. */ return 0; } + if (key_flag & KEY_FLAG_NEXT) { + /* For now, ignore these since this needs support from the + * driver to handle the special cases of two active RX keys. */ + wpa_printf(MSG_DEBUG, + "nl80211: set_key for the next TK for RX-only - ignored"); + return -EOPNOTSUPP; + } + ret = -ENOBUFS; key_msg = nlmsg_alloc(); if (!key_msg) @@ -3502,7 +4102,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) { wpa_printf(MSG_DEBUG, "nl80211: SET_KEY (pairwise RX/TX modify)"); - msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); + if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_KEY); + else + msg = nl80211_ifindex_msg(drv, ifindex, 0, + NL80211_CMD_SET_KEY); if (!msg) goto fail2; } else if (alg == WPA_ALG_NONE && (key_flag & KEY_FLAG_RX_TX)) { @@ -3512,7 +4116,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, goto fail2; } else if (alg == WPA_ALG_NONE) { wpa_printf(MSG_DEBUG, "nl80211: DEL_KEY"); - msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); + if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_KEY); + else + msg = nl80211_ifindex_msg(drv, ifindex, 0, + NL80211_CMD_DEL_KEY); if (!msg) goto fail2; } else { @@ -3524,7 +4132,12 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, goto fail2; } wpa_printf(MSG_DEBUG, "nl80211: NEW_KEY"); - msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY); + if (!nl80211_is_netdev_iftype(drv->nlmode) || is_pd_bss) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NEW_KEY); + else + msg = nl80211_ifindex_msg(drv, ifindex, 0, + NL80211_CMD_NEW_KEY); + if (!msg) goto fail2; if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) || @@ -3538,6 +4151,16 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len); } + + if (params->ltf_keyseed_len && params->ltf_keyseed) { + if (nla_put(key_msg, NL80211_KEY_LTF_SEED, + params->ltf_keyseed_len, + params->ltf_keyseed)) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_LTF_SEED", + params->ltf_keyseed, + params->ltf_keyseed_len); + } } if (addr && !is_broadcast_ether_addr(addr)) { @@ -3559,6 +4182,11 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, if (nla_put_u32(key_msg, NL80211_KEY_TYPE, NL80211_KEYTYPE_GROUP)) goto fail; + } else if (alg == WPA_ALG_NONE && + (key_flag & KEY_FLAG_GROUP_MASK) == KEY_FLAG_GROUP) { + if (nla_put_u32(key_msg, NL80211_KEY_TYPE, + NL80211_KEYTYPE_GROUP)) + goto fail; } else if (!(key_flag & KEY_FLAG_PAIRWISE)) { wpa_printf(MSG_DEBUG, " key_flag missing PAIRWISE when setting a pairwise key"); @@ -3621,7 +4249,10 @@ static int wpa_driver_nl80211_set_key(struct i802_bss *bss, if (!key_msg) return ret; - msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); + if (!nl80211_is_netdev_iftype(drv->nlmode)) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_KEY); + else + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); if (!msg) goto fail2; if (!key_msg || @@ -3784,7 +4415,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int ret; struct nl_msg *msg; - if (!(msg = nl80211_drv_msg(drv, 0, cmd)) || + if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) || (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || (local_state_change && @@ -3793,7 +4424,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, return -1; } - ret = send_and_recv(drv->global, bss->nl_connect, msg, + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, @@ -3834,7 +4465,7 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, if (drv->nlmode == NL80211_IFTYPE_ADHOC) { nl80211_mark_disconnected(drv); - return nl80211_leave_ibss(drv, 1); + return nl80211_leave_ibss(bss, 1); } if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { return wpa_driver_nl80211_disconnect(drv, reason_code, bss); @@ -3955,8 +4586,39 @@ static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg) return NL80211_AUTHTYPE_FILS_SK; if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS) return NL80211_AUTHTYPE_FILS_SK_PFS; + if (wpa_auth_alg & WPA_AUTH_ALG_EPPKE) + return NL80211_AUTHTYPE_EPPKE; + if (wpa_auth_alg & WPA_AUTH_ALG_802_1X) + return NL80211_AUTHTYPE_IEEE8021X; - return NL80211_AUTHTYPE_MAX; + return NL80211_AUTHTYPE_MAX + 1; +} + + +static int +nl80211_put_bss_membership_selectors(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg) +{ + u8 selectors[ARRAY_SIZE(drv->extra_bss_membership_selectors) + 1]; + size_t selectors_len; + + if (!nl80211_attr_supported(drv, NL80211_ATTR_SUPPORTED_SELECTORS)) + return 0; + + for (selectors_len = 0; + drv->extra_bss_membership_selectors[selectors_len]; + selectors_len++) { + selectors[selectors_len] = + drv->extra_bss_membership_selectors[selectors_len]; + } + +#ifdef CONFIG_SAE + /* Always add the SAE H2E selector as it is handled by wpa_supplicant */ + selectors[selectors_len++] = BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY; +#endif /* CONFIG_SAE */ + + return nla_put(msg, NL80211_ATTR_SUPPORTED_SELECTORS, + selectors_len, selectors); } @@ -3995,7 +4657,7 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", drv->ifindex); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_AUTHENTICATE); if (!msg) goto fail; @@ -4052,7 +4714,7 @@ retry: } type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (type == NL80211_AUTHTYPE_MAX || + if (type > NL80211_AUTHTYPE_MAX || nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; if (params->local_state_change) { @@ -4061,6 +4723,10 @@ retry: goto fail; } + ret = nl80211_put_bss_membership_selectors(drv, msg); + if (ret) + goto fail; + if (params->mld && params->ap_mld_addr) { wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR, params->mld_link_id, MAC2STR(params->ap_mld_addr)); @@ -4139,7 +4805,7 @@ retry: } } else { wpa_printf(MSG_DEBUG, - "nl80211: Authentication request send successfully"); + "nl80211: Authentication request sent successfully"); } fail: @@ -4207,6 +4873,22 @@ struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id) } +u8 nl80211_get_link_id_from_link(struct i802_bss *bss, struct i802_link *link) +{ + u8 link_id; + + if (link == bss->flink) + return 0; + + for_each_link(bss->valid_links, link_id) { + if (&bss->links[link_id] == link) + return link_id; + } + + return 0; +} + + static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq) { struct i802_link *link = nl80211_get_link(bss, link_id); @@ -4249,12 +4931,14 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, { struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; - int encrypt = !no_encrypt; u16 fc; int use_cookie = 1; int res; struct i802_link *link = nl80211_get_link(bss, link_id); + if (data_len < 24) + return -1; + mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR " sa=" MACSTR @@ -4264,6 +4948,17 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, noack, freq, no_cck, offchanok, wait_time, no_encrypt, fc, fc2str(fc), drv->nlmode); +#ifdef CONFIG_PR + /* Route MLME TX via PD wdev if source address matches */ + if (drv->pd_bss && ether_addr_equal(mgmt->sa, drv->pd_bss->addr)) { + bss = drv->pd_bss; + wpa_printf(MSG_DEBUG, + "nl80211: send_mlme - route via PD wdev sa=" MACSTR, + MAC2STR(mgmt->sa)); + link = bss->flink; + } +#endif /* CONFIG_PR */ + if ((is_sta_interface(drv->nlmode) || drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -4298,21 +4993,9 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, goto send_frame_cmd; } - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { - /* - * Only one of the authentication frame types is encrypted. - * In order for static WEP encryption to work properly (i.e., - * to not encrypt the frame), we need to tell mac80211 about - * the frames that must not be encrypted. - */ - u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); - if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3) - encrypt = 0; - } - - if (is_sta_interface(drv->nlmode) && + if ((is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_P2P_DEVICE || + drv->nlmode == NL80211_IFTYPE_PD) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { if (freq == 0 && @@ -4359,29 +5042,17 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, freq = link->freq; } - if (drv->use_monitor && is_ap_interface(drv->nlmode)) { - wpa_printf(MSG_DEBUG, - "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor", - freq, link->freq); - return nl80211_send_monitor(drv, data, data_len, encrypt, - noack); - } - if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) && link_id == NL80211_DRV_LINK_ID_NA) use_cookie = 0; send_frame_cmd: #ifdef CONFIG_TESTING_OPTIONS - if (no_encrypt && !encrypt && !drv->use_monitor) { + if (no_encrypt) { wpa_printf(MSG_DEBUG, "nl80211: Request to send an unencrypted frame - use a monitor interface for this"); - if (nl80211_create_monitor_interface(drv) < 0) - return -1; - res = nl80211_send_monitor(drv, data, data_len, encrypt, - noack); - nl80211_remove_monitor_interface(drv); - return res; + return nl80211_send_monitor(drv, data, data_len, !no_encrypt, + noack); } #endif /* CONFIG_TESTING_OPTIONS */ @@ -4405,7 +5076,7 @@ static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates) if (!basic_rates) return 0; - for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] > 0; i++) rates[rates_len++] = basic_rates[i] / 5; return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); @@ -4484,7 +5155,7 @@ static int wpa_driver_nl80211_set_acl(void *priv, nla_total_size(acl_nla_sz); nlmsg_sz = nlmsg_total_size(nla_sz); if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz), - drv->ifindex, 0, + bss->ifindex, 0, NL80211_CMD_SET_MAC_ACL)) || nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? NL80211_ACL_POLICY_DENY_UNLESS_LISTED : @@ -4538,7 +5209,7 @@ static int nl80211_set_mesh_config(void *priv, struct nl_msg *msg; int ret; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MESH_CONFIG); if (!msg) return -1; @@ -4727,7 +5398,7 @@ static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe) { u8 sae_pwe; - wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe); + wpa_printf(MSG_DEBUG, "nl80211: sae_pwe=%d", pwe); if (pwe == SAE_PWE_HUNT_AND_PECK) sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK; else if (pwe == SAE_PWE_HASH_TO_ELEMENT) @@ -4809,8 +5480,7 @@ static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss, } -static int nl80211_mbssid(struct nl_msg *msg, - struct wpa_driver_ap_params *params) +static int nl80211_mbssid(struct nl_msg *msg, struct mbssid_data *params) { struct nlattr *config, *elems; int ifidx; @@ -4830,6 +5500,10 @@ static int nl80211_mbssid(struct nl_msg *msg, nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX, ifidx)) return -1; + if (params->mbssid_tx_iface_linkid >= 0 && + nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID, + params->mbssid_tx_iface_linkid)) + return -1; } if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA)) @@ -4887,12 +5561,12 @@ static int nl80211_mbssid(struct nl_msg *msg, #ifdef CONFIG_DRIVER_NL80211_QCA -static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv, - const int *freqs, int num_freqs, - int link_id) +static void qca_set_allowed_ap_freqs(struct i802_bss *bss, const int *freqs, + int num_freqs, int link_id) { struct nl_msg *msg; struct nlattr *params, *freqs_list; + struct wpa_driver_nl80211_data *drv = bss->drv; int i, ret; if (!drv->set_wifi_conf_vendor_cmd_avail || !drv->qca_ap_allowed_freqs) @@ -4900,7 +5574,7 @@ static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Set AP allowed frequency list"); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) || @@ -4937,8 +5611,39 @@ err: #endif /* CONFIG_DRIVER_NL80211_QCA */ +static int nl80211_bw_to_nl(int bw, enum nl80211_chan_width *cw, + int center_freq2) +{ + switch (bw) { + case 20: + *cw = NL80211_CHAN_WIDTH_20; + break; + case 40: + *cw = NL80211_CHAN_WIDTH_40; + break; + case 80: + if (center_freq2) + *cw = NL80211_CHAN_WIDTH_80P80; + else + *cw = NL80211_CHAN_WIDTH_80; + break; + case 160: + *cw = NL80211_CHAN_WIDTH_160; + break; + case 320: + *cw = NL80211_CHAN_WIDTH_320; + break; + default: + return -1; + } + + return 0; +} + + static int nl80211_put_freq_params(struct nl_msg *msg, - const struct hostapd_freq_params *freq) + const struct hostapd_freq_params *freq, + struct i802_bss *bss) { enum hostapd_hw_mode hw_mode; int is_24ghz; @@ -4964,26 +5669,11 @@ static int nl80211_put_freq_params(struct nl_msg *msg, enum nl80211_chan_width cw; wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth); - switch (freq->bandwidth) { - case 20: - cw = NL80211_CHAN_WIDTH_20; - break; - case 40: - cw = NL80211_CHAN_WIDTH_40; - break; - case 80: - if (freq->center_freq2) - cw = NL80211_CHAN_WIDTH_80P80; - else - cw = NL80211_CHAN_WIDTH_80; - break; - case 160: - cw = NL80211_CHAN_WIDTH_160; - break; - case 320: - cw = NL80211_CHAN_WIDTH_320; - break; - default: + if (nl80211_bw_to_nl(freq->bandwidth, &cw, + freq->center_freq2) < 0) { + wpa_printf(MSG_DEBUG, + " * Unsupported bandwidth %d MHz", + freq->bandwidth); return -EINVAL; } @@ -4999,6 +5689,20 @@ static int nl80211_put_freq_params(struct nl_msg *msg, nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, freq->center_freq2))) return -ENOBUFS; + + if (freq->eht_enabled && freq->punct_bitmap) { + if (!bss->drv->puncturing) { + wpa_printf(MSG_INFO, + "nl80211: Puncture bitmap is set but the driver does dot support puncturing"); + return -EINVAL; + } + wpa_printf(MSG_DEBUG, " * punct_bitmap=0x%x", + freq->punct_bitmap); + if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, + freq->punct_bitmap)) + return -ENOBUFS; + + } } else if (freq->ht_enabled) { enum nl80211_channel_type ct; @@ -5055,7 +5759,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, int beacon_set; int num_suites; u32 suites[20], suite; - u32 ver; + enum nl80211_wpa_versions ver; #ifdef CONFIG_MESH struct wpa_driver_mesh_bss_params mesh_params; #endif /* CONFIG_MESH */ @@ -5081,8 +5785,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; - else if (!drv->device_ap_sme && !drv->use_monitor && - !nl80211_get_wiphy_data_ap(bss)) + else if (!drv->device_ap_sme && !nl80211_get_wiphy_data_ap(bss)) return -ENOBUFS; wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", @@ -5108,6 +5811,12 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; + if (params->freq) + nl80211_link_set_freq(bss, + params->mld_ap ? params->mld_link_id : + NL80211_DRV_LINK_ID_NA, + params->freq->freq); + if (params->mld_ap) { wpa_printf(MSG_DEBUG, "nl80211: link_id=%u", params->mld_link_id); @@ -5115,10 +5824,6 @@ static int wpa_driver_nl80211_set_ap(void *priv, if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, params->mld_link_id)) goto fail; - - if (params->freq) - nl80211_link_set_freq(bss, params->mld_link_id, - params->freq->freq); } if (params->proberesp && params->proberesp_len) { @@ -5166,12 +5871,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; } - wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); - ver = 0; - if (params->wpa_version & WPA_PROTO_WPA) - ver |= NL80211_WPA_VERSION_1; - if (params->wpa_version & WPA_PROTO_RSN) - ver |= NL80211_WPA_VERSION_2; + ver = wpa_ver_supported(drv, params->key_mgmt_suites, + params->wpa_version); + wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", ver); if (ver && nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) goto fail; @@ -5326,7 +6028,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_nest_end(msg, ftm); } - if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0) + if (params->freq && nl80211_put_freq_params(msg, params->freq, bss) < 0) goto fail; #ifdef CONFIG_IEEE80211AX @@ -5390,7 +6092,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, nl80211_unsol_bcast_probe_resp(bss, msg, ¶ms->ubpr) < 0) goto fail; - if (nl80211_mbssid(msg, params) < 0) + if (nl80211_mbssid(msg, ¶ms->mbssid) < 0) goto fail; #endif /* CONFIG_IEEE80211AX */ @@ -5405,17 +6107,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; #endif /* CONFIG_FILS */ - if (params->punct_bitmap) { - wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x", - params->punct_bitmap); - if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, - params->punct_bitmap)) - goto fail; - } - #ifdef CONFIG_DRIVER_NL80211_QCA if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs) - qca_set_allowed_ap_freqs(drv, params->allowed_freqs, + qca_set_allowed_ap_freqs(bss, params->allowed_freqs, int_array_len(params->allowed_freqs), params->mld_ap ? params->mld_link_id : NL80211_DRV_LINK_ID_NA); @@ -5423,7 +6117,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER)) goto fail; - ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL, + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", @@ -5494,9 +6188,9 @@ static int nl80211_set_channel(struct i802_bss *bss, freq->he_enabled, freq->eht_enabled, freq->bandwidth, freq->center_freq1, freq->center_freq2); - msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL : + msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL : NL80211_CMD_SET_WIPHY); - if (!msg || nl80211_put_freq_params(msg, freq) < 0) { + if (!msg || nl80211_put_freq_params(msg, freq, bss) < 0) { nlmsg_free(msg); return -1; } @@ -5540,6 +6234,8 @@ static u32 sta_flags_nl80211(int flags) f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); if (flags & WPA_STA_ASSOCIATED) f |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (flags & WPA_STA_SPP_AMSDU) + f |= BIT(NL80211_STA_FLAG_SPP_AMSDU); return f; } @@ -5601,7 +6297,7 @@ static int wpa_driver_nl80211_sta_add(void *priv, wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, cmd_string, MAC2STR(params->addr)); - msg = nl80211_bss_msg(bss, 0, cmd); + msg = nl80211_cmd_msg(bss, 0, cmd); if (!msg) goto fail; @@ -5620,8 +6316,11 @@ static int wpa_driver_nl80211_sta_add(void *priv, params->supp_rates, params->supp_rates_len); wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); - if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, - params->supp_rates_len, params->supp_rates) || + if (((params->supp_rates_len > 0 || + (drv->nlmode != NL80211_IFTYPE_NAN && + drv->nlmode != NL80211_IFTYPE_NAN_DATA)) && + nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, + params->supp_rates_len, params->supp_rates)) || nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability)) goto fail; @@ -5680,6 +6379,15 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } + /* Set EML capabilities of ML STA */ + if (params->mld_link_addr && params->eml_cap) { + wpa_printf(MSG_DEBUG, " * eml_cap =%u", + params->eml_cap); + if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY, + params->eml_cap)) + goto fail; + } + if (is_ap_interface(drv->nlmode) && nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS, params->support_p2p_ps ? @@ -5725,6 +6433,31 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } +#ifdef CONFIG_NAN + if (params->set && drv->nlmode == NL80211_IFTYPE_NAN && + (params->flags & WPA_STA_AUTHORIZED)) { + if (params->ht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * ht_capabilities", + params->ht_capabilities, + sizeof(*params->ht_capabilities)); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, + sizeof(*params->ht_capabilities), + params->ht_capabilities)) + goto fail; + } + + if (params->vht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * vht_capabilities", + params->vht_capabilities, + sizeof(*params->vht_capabilities)); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, + sizeof(*params->vht_capabilities), + params->vht_capabilities)) + goto fail; + } + } +#endif /* CONFIG_NAN */ + if (params->vht_opmode_enabled) { wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode); if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF, @@ -5852,6 +6585,35 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; } +#ifdef CONFIG_ENC_ASSOC + if (params->epp_sta) { + wpa_printf(MSG_DEBUG, " * EPP STA"); + if (nla_put_flag(msg, NL80211_ATTR_EPP_PEER)) + goto fail; + } +#endif /* CONFIG_ENC_ASSOC */ + +#ifdef CONFIG_NAN + /* + * Set the address of the NMI station to which the NDI station belongs. + */ + if (drv->nlmode == NL80211_IFTYPE_NAN_DATA) { + if (!params->nmi_addr) { + wpa_printf(MSG_DEBUG, + "nl80211: NMI address not provided for NDI station"); + ret = -EINVAL; + goto fail; + } + + wpa_printf(MSG_DEBUG, " * nmi_addr=" MACSTR, + MAC2STR(params->nmi_addr)); + + if (nla_put(msg, NL80211_ATTR_NAN_NMI_MAC, ETH_ALEN, + params->nmi_addr)) + goto fail; + } +#endif /* CONFIG_NAN */ + ret = send_and_recv_cmd(drv, msg); msg = NULL; if (ret) @@ -5865,13 +6627,15 @@ fail: } -static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) +static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr, + bool is_bridge) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ndmsg nhdr = { .ndm_state = NUD_PERMANENT, - .ndm_ifindex = bss->ifindex, + .ndm_ifindex = is_bridge ? bss->br_ifindex : bss->ifindex, .ndm_family = AF_BRIDGE, + .ndm_type = is_bridge ? NTF_SELF : 0, }; struct nl_msg *msg; int err; @@ -5888,11 +6652,61 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) err = nl_wait_for_ack(drv->rtnl_sk); if (err < 0) { wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for " - MACSTR " ifindex=%d failed: %s", MAC2STR(addr), - bss->ifindex, nl_geterror(err)); + MACSTR " ifindex=%d ifname %s failed: %s", + MAC2STR(addr), + is_bridge ? bss->br_ifindex : bss->ifindex, + is_bridge ? bss->brname : bss->ifname, + nl_geterror(err)); } else { - wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for " - MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry " + MACSTR " from %s", + MAC2STR(addr), + is_bridge ? bss->brname : bss->ifname); + } + +errout: + nlmsg_free(msg); +} + + +static void rtnl_neigh_add_fdb_entry(struct i802_bss *bss, const u8 *addr, + bool is_bridge) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ndmsg nhdr = { + .ndm_state = NUD_PERMANENT, + .ndm_ifindex = is_bridge ? bss->br_ifindex : bss->ifindex, + .ndm_family = AF_BRIDGE, + /* TODO: remove this check if this flag needs to be used, + * for other interfaces type. + */ + .ndm_flags = is_bridge ? NTF_SELF : 0, + }; + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE); + if (!msg) + return; + + if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 || + nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr) || + nl_send_auto_complete(drv->rtnl_sk, msg) < 0) + goto errout; + + err = nl_wait_for_ack(drv->rtnl_sk); + if (err < 0) { + wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry addition for " + MACSTR " ifindex=%d ifname %s failed: %s", + MAC2STR(addr), + is_bridge ? bss->br_ifindex : bss->ifindex, + is_bridge ? bss->brname : bss->ifname, + nl_geterror(err)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: added bridge FDB entry " MACSTR + " to %s", + MAC2STR(addr), + is_bridge ? bss->brname : bss->ifname); } errout: @@ -5907,7 +6721,7 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, struct nl_msg *msg; int ret; - if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) || + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_STATION)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || (deauth == 0 && nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE, @@ -5927,7 +6741,7 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, bss->ifname, MAC2STR(addr), ret, strerror(-ret)); if (drv->rtnl_sk) - rtnl_neigh_delete_fdb_entry(bss, addr); + rtnl_neigh_delete_fdb_entry(bss, addr, false); if (ret == -ENOENT) return 0; @@ -5984,7 +6798,11 @@ const char * nl80211_iftype_str(enum nl80211_iftype mode) case NL80211_IFTYPE_OCB: return "OCB"; case NL80211_IFTYPE_NAN: - return "NAN"; + return "NAN DEVICE"; + case NL80211_IFTYPE_NAN_DATA: + return "NAN_DATA"; + case NL80211_IFTYPE_PD: + return "PD DEVICE"; default: return "unknown"; } @@ -6011,19 +6829,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype)) goto fail; - if (iftype == NL80211_IFTYPE_MONITOR) { - struct nlattr *flags; - - flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); - if (!flags || - nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES)) - goto fail; - - nla_nest_end(msg, flags); - } else if (wds) { - if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds)) - goto fail; - } + if (wds && nla_put_u8(msg, NL80211_ATTR_4ADDR, wds)) + goto fail; /* * Tell cfg80211 that the interface belongs to the socket that created @@ -6032,11 +6839,49 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) goto fail; - if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) && + if ((addr && (iftype == NL80211_IFTYPE_P2P_DEVICE || + iftype == NL80211_IFTYPE_PD || + iftype == NL80211_IFTYPE_NAN)) && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) goto fail; - ret = send_and_recv_resp(drv, msg, handler, arg); + /* NAN interface is created on a dedicated socket */ + if (iftype == NL80211_IFTYPE_NAN) { +#ifdef CONFIG_NAN + if (drv->global->nl_nan) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to create NAN interface %s: socket in use", + ifname); + goto fail; + } + + drv->global->nl_nan = nl_create_handle(drv->global->nl_cb, + "nan"); + if (!drv->global->nl_nan) + goto fail; + + ret = send_and_recv(drv, drv->global->nl_nan, msg, + handler, arg, NULL, NULL, NULL); + if (ret) { + nl_destroy_handles(&drv->global->nl_nan); + goto fail; + } + + /* + * NAN events are received on a socket which is used to create + * the interface. Note that after this call this socket can't be + * used for sending commands anymore. + */ + nl80211_register_eloop_read(&drv->global->nl_nan, + wpa_driver_nl80211_event_receive, + drv->global->nl_cb, 0); +#else /* CONFIG_NAN */ + ret = -EOPNOTSUPP; +#endif /* CONFIG_NAN */ + } else { + ret = send_and_recv_resp(drv, msg, handler, arg); + } + msg = NULL; if (ret) { fail: @@ -6046,7 +6891,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, return ret; } - if (iftype == NL80211_IFTYPE_P2P_DEVICE) + if (!nl80211_is_netdev_iftype(iftype)) return 0; ifidx = if_nametoindex(ifname); @@ -6078,6 +6923,24 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, } +static int nl80211_use_existing_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, + enum nl80211_iftype iftype, + const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", + ifname); + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr) < 0 && + (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 0) < 0 || + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr) < 0 || + linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1) < 0)) + return -1; + + return -ENFILE; +} + + int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, const u8 *addr, int wds, @@ -6086,25 +6949,17 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, { int ret; + if (use_existing && if_nametoindex(ifname)) + return nl80211_use_existing_iface(drv, ifname, iftype, addr); + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, arg); /* if error occurred and interface exists already */ - if (ret == -ENFILE && if_nametoindex(ifname)) { + if (ret < 0 && if_nametoindex(ifname)) { if (use_existing) { - wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", - ifname); - if (addr && iftype != NL80211_IFTYPE_MONITOR && - linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, - addr) < 0 && - (linux_set_iface_flags(drv->global->ioctl_sock, - ifname, 0) < 0 || - linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, - addr) < 0 || - linux_set_iface_flags(drv->global->ioctl_sock, - ifname, 1) < 0)) - return -1; - return -ENFILE; + return nl80211_use_existing_iface(drv, ifname, iftype, + addr); } wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); @@ -6131,8 +6986,8 @@ static int nl80211_setup_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; - wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d", - bss->ifname, drv->device_ap_sme, drv->use_monitor); + wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d", + bss->ifname, drv->device_ap_sme); /* * Disable Probe Request reporting unless we need it in this way for @@ -6142,20 +6997,13 @@ static int nl80211_setup_ap(struct i802_bss *bss) if (!drv->device_ap_sme) wpa_driver_nl80211_probe_req_report(bss, 0); - if (!drv->device_ap_sme && !drv->use_monitor) - if (nl80211_mgmt_subscribe_ap(bss)) - return -1; - - if (drv->device_ap_sme && !drv->use_monitor) - if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) - wpa_printf(MSG_DEBUG, - "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it"); - - if (!drv->device_ap_sme && drv->use_monitor && - nl80211_create_monitor_interface(drv) && - !drv->device_ap_sme) + if (!drv->device_ap_sme && nl80211_mgmt_subscribe_ap(bss)) return -1; + if (drv->device_ap_sme && nl80211_mgmt_subscribe_ap_dev_sme(bss)) + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it"); + if (drv->device_ap_sme && wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " @@ -6171,16 +7019,14 @@ static void nl80211_teardown_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; - wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d", - bss->ifname, drv->device_ap_sme, drv->use_monitor); + wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d", + bss->ifname, drv->device_ap_sme); if (drv->device_ap_sme) { wpa_driver_nl80211_probe_req_report(bss, 0); - if (!drv->use_monitor) - nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); - } else if (drv->use_monitor) - nl80211_remove_monitor_interface(drv); - else + nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); + } else { nl80211_mgmt_unsubscribe(bss, "AP teardown"); + } nl80211_put_wiphy_data_ap(bss); if (bss->flink) @@ -6218,7 +7064,7 @@ static int nl80211_tx_control_port(void *priv, const u8 *dest, os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg)); ext_arg.ext_data = &cookie; - ret = send_and_recv(bss->drv->global, bss->drv->global->nl, msg, + ret = send_and_recv(bss->drv, bss->drv->global->nl, msg, NULL, NULL, ack_handler_cookie, &ext_arg, NULL); if (ret) { wpa_printf(MSG_DEBUG, @@ -6266,8 +7112,6 @@ static int nl80211_send_eapol_data(struct i802_bss *bss, } -static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - static int wpa_driver_nl80211_hapd_send_eapol( void *priv, const u8 *addr, const u8 *data, size_t data_len, int encrypt, const u8 *own_addr, u32 flags, @@ -6275,11 +7119,6 @@ static int wpa_driver_nl80211_hapd_send_eapol( { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct ieee80211_hdr *hdr; - size_t len; - u8 *pos; - int res; - int qos = flags & WPA_STA_WMM; /* For now, disable EAPOL TX over control port in AP mode by default * since it does not provide TX status notifications. */ @@ -6289,55 +7128,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( data, data_len, !encrypt, link_id); - if (drv->device_ap_sme || !drv->use_monitor) - return nl80211_send_eapol_data(bss, addr, data, data_len); - - len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + - data_len; - hdr = os_zalloc(len); - if (hdr == NULL) { - wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)", - (unsigned long) len); - return -1; - } - - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); - hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); - if (encrypt) - hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); - if (qos) { - hdr->frame_control |= - host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); - } - - memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); - memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - pos = (u8 *) (hdr + 1); - - if (qos) { - /* Set highest priority in QoS header */ - pos[0] = 7; - pos[1] = 0; - pos += 2; - } - - memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); - pos += sizeof(rfc1042_header); - WPA_PUT_BE16(pos, ETH_P_PAE); - pos += 2; - memcpy(pos, data, data_len); - - res = nl80211_send_monitor(drv, hdr, len, encrypt, 0); - if (res < 0) { - wpa_printf(MSG_ERROR, - "hapd_send_eapol - packet len: %lu - failed", - (unsigned long) len); - } - os_free(hdr); - - return res; + return nl80211_send_eapol_data(bss, addr, data, data_len); } @@ -6356,8 +7147,8 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and, !!(total_flags & WPA_STA_AUTHORIZED)); - if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_STATION); + if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) goto fail; /* @@ -6431,20 +7222,20 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " "group (GO)"); nlmode = NL80211_IFTYPE_P2P_GO; +#ifdef CONFIG_DRIVER_NL80211_QCA + nl80211_set_p2p_mode(drv->first_bss, params->p2p_mode); +#endif /* CONFIG_DRIVER_NL80211_QCA */ } else nlmode = NL80211_IFTYPE_AP; old_mode = drv->nlmode; - if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) { - nl80211_remove_monitor_interface(drv); + if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) return -1; - } if (params->freq.freq && nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) { if (old_mode != nlmode) wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); - nl80211_remove_monitor_interface(drv); return -1; } @@ -6452,14 +7243,13 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, } -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, - int reset_mode) +static int nl80211_leave_ibss(struct i802_bss *bss, int reset_mode) { struct nl_msg *msg; int ret; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS); - ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL, + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_IBSS); + ret = send_and_recv(bss->drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " @@ -6470,8 +7260,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, } if (reset_mode && - wpa_driver_nl80211_set_mode(drv->first_bss, - NL80211_IFTYPE_STATION)) { + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "station mode"); } @@ -6536,23 +7325,24 @@ static int nl80211_ht_vht_overrides(struct nl_msg *msg, } -static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, +static int wpa_driver_nl80211_ibss(struct i802_bss *bss, struct wpa_driver_associate_params *params) { struct nl_msg *msg; int ret = -1; int count = 0; + struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); - if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, ¶ms->freq)) { + if (wpa_driver_nl80211_set_mode_ibss(bss, ¶ms->freq)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "IBSS mode"); return -1; } retry: - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_IBSS)) || params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) goto fail; @@ -6563,7 +7353,7 @@ retry: os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; - if (nl80211_put_freq_params(msg, ¶ms->freq) < 0 || + if (nl80211_put_freq_params(msg, ¶ms->freq, bss) < 0 || nl80211_put_beacon_int(msg, params->beacon_int)) goto fail; @@ -6609,7 +7399,7 @@ retry: if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER)) goto fail; - ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL, + ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); msg = NULL; if (ret) { @@ -6619,7 +7409,7 @@ retry: if (ret == -EALREADY && count == 1) { wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " "forced leave"); - nl80211_leave_ibss(drv, 0); + nl80211_leave_ibss(bss, 0); nlmsg_free(msg); goto retry; } @@ -6761,14 +7551,18 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } - if (params->freq.freq && !params->mld_params.mld_addr) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - params->freq.freq)) - return -1; + if (params->freq.freq) { + if (!params->mld_params.mld_addr) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + params->freq.freq)) + return -1; + } + drv->assoc_freq = params->freq.freq; - } else + } else { drv->assoc_freq = 0; + } if (params->freq_hint) { wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint); @@ -6815,23 +7609,10 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; if (params->wpa_proto) { - enum nl80211_wpa_versions ver = 0; - - if (params->wpa_proto & WPA_PROTO_WPA) - ver |= NL80211_WPA_VERSION_1; - if (params->wpa_proto & WPA_PROTO_RSN) { - /* - * NL80211_ATTR_SAE_PASSWORD is related and was added - * at the same time as NL80211_WPA_VERSION_3. - */ - if (nl80211_attr_supported(drv, - NL80211_ATTR_SAE_PASSWORD) && - wpa_key_mgmt_sae(params->key_mgmt_suite)) - ver |= NL80211_WPA_VERSION_3; - else - ver |= NL80211_WPA_VERSION_2; - } + enum nl80211_wpa_versions ver; + ver = wpa_ver_supported(drv, params->key_mgmt_suite, + params->wpa_proto); wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) return -1; @@ -6864,7 +7645,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X || params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_CCKM || - params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_SAE || @@ -6880,7 +7660,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_OWE || params->key_mgmt_suite == WPA_KEY_MGMT_DPP || - params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384) { + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384 || + params->key_mgmt_suite == WPA_KEY_MGMT_EPPKE) { u32 *mgmt; unsigned int akm_count = 1, i; @@ -6922,9 +7703,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_PSK_SHA256: mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256; break; - case WPA_KEY_MGMT_OSEN: - mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN; - break; case WPA_KEY_MGMT_SAE: mgmt[0] = RSN_AUTH_KEY_MGMT_SAE; break; @@ -6967,6 +7745,9 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_IEEE8021X_SHA384: mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA384; break; + case WPA_KEY_MGMT_EPPKE: + mgmt[0] = RSN_AUTH_KEY_MGMT_EPPKE; + break; case WPA_KEY_MGMT_PSK: default: mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; @@ -7062,10 +7843,109 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT)) return -1; + if (params->spp_amsdu) { + wpa_printf(MSG_DEBUG, " * SPP A-MSDU"); + if (nla_put_flag(msg, NL80211_ATTR_ASSOC_SPP_AMSDU)) + return -1; + } + return 0; } +#ifdef CONFIG_DRIVER_NL80211_QCA +static void connect_ext_feature_set(u8 *features, + enum qca_wlan_connect_ext_features idx) +{ + u8 *idx_byte = &features[idx / 8]; + + *idx_byte |= BIT(idx % 8); +} +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + +static int nl80211_connect_ext(struct i802_bss *bss, + struct wpa_driver_associate_params *params) +{ +#ifdef CONFIG_DRIVER_NL80211_QCA + struct nl_msg *msg; + struct nlattr *attr; + struct wpa_driver_nl80211_data *drv = bss->drv; + u8 features[(NUM_QCA_CONNECT_EXT_FEATURES + 7) / 8] = {}; + + if (!drv->connect_ext_vendor_cmd_avail) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Connect_ext (ifindex=%d)", + bss->ifindex); + + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + if (params->rsn_overriding) { + wpa_printf(MSG_DEBUG, "- RSN overriding"); + connect_ext_feature_set(features, QCA_CONNECT_EXT_FEATURE_RSNO); + } + +#ifdef CONFIG_ENC_ASSOC + if (params->eppke_supported) { + wpa_printf(MSG_DEBUG, + "- EPPKE external authentication support"); + connect_ext_feature_set(features, + QCA_CONNECT_EXT_FEATURE_EXT_AUTH_EPPKE); + } +#endif /* CONFIG_ENC_ASSOC */ + +#ifdef CONFIG_IEEE8021X_AUTH + if (params->ieee8021x_auth_supported) { + wpa_printf(MSG_DEBUG, + "- IEEE 802.1X external authentication support"); + connect_ext_feature_set(features, + QCA_CONNECT_EXT_FEATURE_EXT_AUTH_8021X); + } +#endif /* CONFIG_IEEE8021X_AUTH */ + + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_FEATURES, + sizeof(features), features)) + goto fail; + + if (params->bssid_filter && params->bssid_filter_count) { + struct nlattr *bssid_list; + unsigned int i; + + bssid_list = nla_nest_start( + msg, QCA_WLAN_VENDOR_ATTR_CONNECT_EXT_ALLOWED_BSSIDS); + if (!bssid_list) + goto fail; + + for (i = 0; i < params->bssid_filter_count; i++) { + wpa_printf(MSG_DEBUG, "- bssid_filter[%d]=" MACSTR, i, + MAC2STR(¶ms->bssid_filter[i * ETH_ALEN])); + if (nla_put(msg, i + 1, ETH_ALEN, + ¶ms->bssid_filter[i * ETH_ALEN])) + goto fail; + } + + nla_nest_end(msg, bssid_list); + } + + nla_nest_end(msg, attr); + + return send_and_recv_cmd(drv, msg); +fail: + nlmsg_free(msg); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + return -1; +} + + static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, @@ -7081,14 +7961,15 @@ static int wpa_driver_nl80211_try_connect( (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) || wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) { wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); - ret = issue_key_mgmt_set_key(drv, params->psk, 32); + ret = issue_key_mgmt_set_key(bss, params->psk, 32); if (ret) return ret; } #endif /* CONFIG_DRIVER_NL80211_QCA */ + nl80211_connect_ext(bss, params); wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONNECT); if (!msg) return -1; @@ -7146,6 +8027,8 @@ static int wpa_driver_nl80211_try_connect( algs++; if (params->auth_alg & WPA_AUTH_ALG_SAE) algs++; + if (params->auth_alg & WPA_AUTH_ALG_EPPKE) + algs++; if (algs > 1) { wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " "selection"); @@ -7154,7 +8037,7 @@ static int wpa_driver_nl80211_try_connect( type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (type == NL80211_AUTHTYPE_MAX || + if (type > NL80211_AUTHTYPE_MAX || nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; @@ -7165,7 +8048,7 @@ skip_auth_type: if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER)) goto fail; - ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL, + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); msg = NULL; if (ret) { @@ -7176,7 +8059,7 @@ skip_auth_type: drv->roam_indication_done = false; #endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_DEBUG, - "nl80211: Connect request send successfully"); + "nl80211: Connect request sent successfully"); } fail: @@ -7224,7 +8107,7 @@ static int wpa_driver_nl80211_associate( { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl80211_err_info err_info; + struct nl80211_err_info err_info = { -1 }; int ret = -1; struct nl_msg *msg; @@ -7234,7 +8117,7 @@ static int wpa_driver_nl80211_associate( return wpa_driver_nl80211_ap(drv, params); if (params->mode == IEEE80211_MODE_IBSS) - return wpa_driver_nl80211_ibss(drv, params); + return wpa_driver_nl80211_ibss(bss, params); if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? @@ -7250,7 +8133,7 @@ static int wpa_driver_nl80211_associate( wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOCIATE); if (!msg) return -1; @@ -7258,6 +8141,10 @@ static int wpa_driver_nl80211_associate( if (ret) goto fail; + ret = nl80211_put_bss_membership_selectors(drv, msg); + if (ret) + goto fail; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) goto fail; @@ -7281,7 +8168,7 @@ static int wpa_driver_nl80211_associate( if (!TEST_FAIL_TAG("assoc")) { if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER)) goto fail; - ret = send_and_recv(drv->global, drv->first_bss->nl_connect, + ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL, NULL, NULL, NULL, &err_info); msg = NULL; } else { @@ -7316,7 +8203,7 @@ static int wpa_driver_nl80211_associate( } } else { wpa_printf(MSG_DEBUG, - "nl80211: Association request send successfully"); + "nl80211: Association request sent successfully"); } fail: @@ -7325,27 +8212,26 @@ fail: } -static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, - int ifindex, enum nl80211_iftype mode) +static int nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype mode) { struct nl_msg *msg; int ret = -ENOBUFS; wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", - ifindex, mode, nl80211_iftype_str(mode)); + bss->ifindex, mode, nl80211_iftype_str(mode)); - msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_INTERFACE); if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode)) goto fail; - ret = send_and_recv_cmd(drv, msg); + ret = send_and_recv_cmd(bss->drv, msg); msg = NULL; if (!ret) return 0; fail: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" - " %d (%s)", ifindex, mode, ret, strerror(-ret)); + " %d (%s)", bss->ifindex, mode, ret, strerror(-ret)); return ret; } @@ -7365,7 +8251,7 @@ static int wpa_driver_nl80211_set_mode_impl( if (TEST_FAIL()) return -1; - mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); + mode_switch_res = nl80211_set_mode(bss, nlmode); if (mode_switch_res && nlmode == nl80211_get_ifmode(bss)) mode_switch_res = 0; @@ -7429,7 +8315,7 @@ static int wpa_driver_nl80211_set_mode_impl( } /* Try to set the mode again while the interface is down */ - mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); + mode_switch_res = nl80211_set_mode(bss, nlmode); if (mode_switch_res == -EBUSY) { wpa_printf(MSG_DEBUG, "nl80211: Delaying mode set while interface going down"); @@ -7444,7 +8330,7 @@ static int wpa_driver_nl80211_set_mode_impl( wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " "interface is down"); drv->nlmode = nlmode; - drv->ignore_if_down_event = 1; + bss->ignore_if_down_event = 1; } /* Bring the interface back up */ @@ -7490,6 +8376,10 @@ done: nl80211_mgmt_subscribe_mesh(bss)) return -1; + if (nlmode == NL80211_IFTYPE_NAN || + nlmode == NL80211_IFTYPE_NAN_DATA) + return nl80211_mgmt_subscribe_nan(bss, nlmode); + if (!bss->in_deinit && !is_ap_interface(nlmode) && !is_mesh_interface(nlmode) && nl80211_mgmt_subscribe_non_ap(bss) < 0) @@ -7563,10 +8453,10 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state) struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)", - bss->ifname, drv->operstate, state, + bss->ifname, bss->operstate, state, state ? "UP" : "DORMANT"); - drv->operstate = state; - return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, + bss->operstate = state; + return netlink_send_oper_ifla(drv->global->netlink, bss->ifindex, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); } @@ -7657,8 +8547,12 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, struct nl_msg *msg; int res; - msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0, - NL80211_CMD_GET_KEY); + if (!nl80211_is_netdev_iftype(drv->nlmode)) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_KEY); + else + msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0, + NL80211_CMD_GET_KEY); + if (!msg || (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || (link_id != NL80211_DRV_LINK_ID_NA && @@ -7693,7 +8587,7 @@ static int i802_set_rts(void *priv, int rts) else val = rts; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) { nlmsg_free(msg); return -ENOBUFS; @@ -7721,7 +8615,7 @@ static int i802_set_frag(void *priv, int frag) else val = frag; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) { nlmsg_free(msg); return -ENOBUFS; @@ -7754,6 +8648,8 @@ static int i802_flush(void *priv, int link_id) * XXX: FIX! this needs to flush all VLANs too */ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION); + if (!msg) + goto fail; if (link_id >= 0 && (bss->valid_links & BIT(link_id)) && nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) goto fail; @@ -8077,8 +8973,7 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) } -int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, - const u8 *bssid, +int nl80211_get_link_signal(struct i802_bss *bss, const u8 *bssid, struct hostap_sta_driver_data *data) { struct nl_msg *msg; @@ -8086,13 +8981,13 @@ int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, data->signal = -WPA_INVALID_NOISE; data->current_tx_rate = 0; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) { nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_resp(drv, msg, get_sta_handler, data); + return send_and_recv_resp(bss->drv, msg, get_sta_handler, data); } @@ -8254,7 +9149,8 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, HOSTAPD_MODE_IEEE80211AD) { /* Deauthentication is not used in DMG/IEEE 802.11ad; * disassociate the STA instead. */ - return i802_sta_disassoc(priv, own_addr, addr, reason); + return i802_sta_disassoc(priv, own_addr, addr, reason, + link_id); } if (is_mesh_interface(drv->nlmode)) @@ -8278,7 +9174,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - u16 reason) + u16 reason, int link_id) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -8300,7 +9196,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, - 0, NULL, 0, 0, -1); + 0, NULL, 0, 0, link_id); } @@ -8338,7 +9234,7 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d (ifidx_reason %d)", ifidx, ifidx_reason); - if (have_ifidx(drv, ifidx, ifidx_reason)) { + if (nl80211_has_ifidx(drv, ifidx, ifidx_reason)) { wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list", ifidx); return; @@ -8398,7 +9294,7 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, +int nl80211_has_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, int ifidx_reason) { int i; @@ -8420,6 +9316,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, struct wpa_driver_nl80211_data *drv = bss->drv; char name[IFNAMSIZ + 1]; union wpa_event_data event; + bool add_br = false; int ret; ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); @@ -8441,10 +9338,9 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, bss->addr, 1, NULL, NULL, 0) < 0) return -1; - if (bridge_ifname && - linux_br_add_if(drv->global->ioctl_sock, - bridge_ifname, name) < 0) - return -1; + + if (bridge_ifname) + add_br = true; os_memset(&event, 0, sizeof(event)); event.wds_sta_interface.sta_addr = addr; @@ -8458,6 +9354,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " "interface %s up", name); } + + if (add_br && + linux_br_add_if(drv->global->ioctl_sock, + bridge_ifname, name) < 0) + return -1; + return i802_set_sta_vlan(priv, addr, name, 0, NL80211_DRV_LINK_ID_NA); } else { @@ -8485,6 +9387,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct i802_bss *bss; struct sockaddr_ll lladdr; unsigned char buf[3000]; int len; @@ -8498,8 +9401,10 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY)) - drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); + if (nl80211_has_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY)) { + for (bss = drv->first_bss; bss; bss = bss->next) + drv_event_eapol_rx(bss->ctx, lladdr.sll_addr, buf, len); + } } @@ -8577,7 +9482,8 @@ static void *i802_init(struct hostapd_data *hapd, bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, params->global_priv, 1, - params->bssid, params->driver_params); + params->bssid, params->driver_params, + WPA_P2P_MODE_WFD_R1); if (bss == NULL) return NULL; @@ -8711,6 +9617,12 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_DEVICE; case WPA_IF_MESH: return NL80211_IFTYPE_MESH_POINT; + case WPA_IF_NAN: + return NL80211_IFTYPE_NAN; + case WPA_IF_NAN_DATA: + return NL80211_IFTYPE_NAN_DATA; + case WPA_IF_PD: + return NL80211_IFTYPE_PD; default: return -1; } @@ -8796,26 +9708,28 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, if (addr) os_memcpy(if_addr, addr, ETH_ALEN); nlmode = wpa_driver_nl80211_if_type(type); - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { - struct wdev_info p2pdev_info; + if (!nl80211_is_netdev_iftype(nlmode)) { + struct wdev_info nonnetdev_info; - os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); + os_memset(&nonnetdev_info, 0, sizeof(nonnetdev_info)); ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, 0, nl80211_wdev_handler, - &p2pdev_info, use_existing); - if (!p2pdev_info.wdev_id_set || ifidx != 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", + &nonnetdev_info, use_existing); + if (!nonnetdev_info.wdev_id_set || ifidx != 0) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to create a P2P/NAN Device interface %s", ifname); return -1; } - drv->global->if_add_wdevid = p2pdev_info.wdev_id; - drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; - if (!is_zero_ether_addr(p2pdev_info.macaddr)) - os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", + drv->global->if_add_wdevid = nonnetdev_info.wdev_id; + drv->global->if_add_wdevid_set = nonnetdev_info.wdev_id_set; + if (!is_zero_ether_addr(nonnetdev_info.macaddr)) + os_memcpy(if_addr, nonnetdev_info.macaddr, ETH_ALEN); + wpa_printf(MSG_DEBUG, + "nl80211: New P2P/NAN Device interface %s (0x%llx) created", ifname, - (long long unsigned int) p2pdev_info.wdev_id); + (long long unsigned int) nonnetdev_info.wdev_id); } else { ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, 0, NULL, NULL, use_existing); @@ -8828,7 +9742,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (!addr) { - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + if (!nl80211_is_netdev_iftype(nlmode)) os_memcpy(if_addr, bss->addr, ETH_ALEN); else if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, if_addr) < 0) { @@ -8878,6 +9792,20 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, return -1; } + new_bss->ifindex = ifidx; + new_bss->drv = drv; + new_bss->next = drv->first_bss->next; + new_bss->flink = &new_bss->links[0]; + new_bss->valid_links = 0; + + new_bss->flink->freq = drv->first_bss->flink->freq; + new_bss->ctx = bss_ctx; + new_bss->added_if = added; + + /* Set interface mode to NL80211_IFTYPE_AP */ + if (nl80211_set_mode(new_bss, nlmode)) + return -1; + if (bridge && i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " @@ -8898,16 +9826,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); os_memcpy(new_bss->addr, if_addr, ETH_ALEN); - new_bss->ifindex = ifidx; - new_bss->drv = drv; - new_bss->next = drv->first_bss->next; - new_bss->flink = &new_bss->links[0]; - new_bss->valid_links = 0; os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN); - new_bss->flink->freq = drv->first_bss->flink->freq; - new_bss->ctx = bss_ctx; - new_bss->added_if = added; drv->first_bss->next = new_bss; if (drv_priv) *drv_priv = new_bss; @@ -8945,6 +9865,9 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d", __func__, type, ifname, ifindex, bss->added_if); + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx); + if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex)) nl80211_remove_iface(drv, ifindex); else if (ifindex > 0 && !bss->added_if) { @@ -8973,6 +9896,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, bss->brname, strerror(errno)); } + bss->in_deinit = 1; if (bss != drv->first_bss) { struct i802_bss *tbss; @@ -9074,14 +9998,22 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, "cookie 0x%llx", no_ack ? " (no ACK)" : "", (long long unsigned int) cookie); - if (save_cookie) - drv->send_frame_cookie = no_ack ? (u64) -1 : cookie; + if (drv->nlmode == NL80211_IFTYPE_NAN_DATA || no_ack) + drv->send_frame_cookie = (u64) -1; + else if (save_cookie) + drv->send_frame_cookie = cookie; - if (!wait) { - /* There is no need to store this cookie since there - * is no wait that could be canceled later. */ + /* + * Store the cookie only in case there is a wait that needs to + * be cancelled later or in case of a NAN Device interface, + * where multiple Action frames might be sent, specifically in + * cases that the NAN Discovery Engine (NAN DE) is not managed + * by the device. + */ + if (!wait && drv->nlmode != NL80211_IFTYPE_NAN && + drv->nlmode != NL80211_IFTYPE_NAN_DATA) goto fail; - } + if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) { wpa_printf(MSG_DEBUG, "nl80211: Drop oldest pending send frame cookie 0x%llx", @@ -9109,7 +10041,7 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, - int no_cck) + int no_cck, int link_id) { struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; @@ -9121,14 +10053,16 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, bss->flink->beacon_set) offchanok = 0; - if (!freq && is_sta_interface(drv->nlmode)) + if (!freq && (is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_NAN || + drv->nlmode == NL80211_IFTYPE_NAN_DATA)) offchanok = 0; wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, freq=%u MHz wait=%d ms no_cck=%d offchanok=%d dst=" - MACSTR " src=" MACSTR " bssid=" MACSTR ")", + MACSTR " src=" MACSTR " bssid=" MACSTR ", link_id=%d)", drv->ifindex, freq, wait_time, no_cck, offchanok, - MAC2STR(dst), MAC2STR(src), MAC2STR(bssid)); + MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), link_id); buf = os_zalloc(24 + data_len); if (buf == NULL) @@ -9157,7 +10091,8 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, int i; modes = nl80211_get_hw_feature_data(bss, &num_modes, - &flags, &dfs_domain); + &flags, &dfs_domain, + NULL, 0); if (dfs_domain != HOSTAPD_DFS_REGION_ETSI && ieee80211_is_dfs(bss->flink->freq, modes, num_modes)) offchanok = 0; @@ -9171,18 +10106,15 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, } #endif /* CONFIG_MESH */ - if (is_ap_interface(drv->nlmode) && - (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || - (int) freq == bss->flink->freq || drv->device_ap_sme || - !drv->use_monitor)) + if (is_ap_interface(drv->nlmode)) ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, 0, freq, no_cck, offchanok, - wait_time, NULL, 0, 0, -1); + wait_time, NULL, 0, 0, + link_id); else ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, 24 + data_len, 1, no_cck, 0, - offchanok, NULL, 0, - NL80211_DRV_LINK_ID_NA); + offchanok, NULL, 0, link_id); os_free(buf); return ret; @@ -9217,6 +10149,10 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) unsigned int i; u64 cookie; + /* As there are no waits for NAN Device, nothing to do here */ + if (drv->nlmode == NL80211_IFTYPE_NAN) + return; + /* Cancel the last pending TX cookie */ if (drv->send_frame_cookie != (u64) -1) nl80211_frame_wait_cancel(bss, drv->send_frame_cookie); @@ -9273,7 +10209,8 @@ static int nl80211_put_any_link_id(struct nl_msg *msg, static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, - unsigned int duration) + unsigned int duration, + const u8 *filter_addr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -9289,12 +10226,30 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, return -1; } + /* Add MAC address filter if provided and supported */ + if (filter_addr) { + if (!(drv->capa.flags2 & WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support ROC address filter"); + nlmsg_free(msg); + return -1; + } + + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, filter_addr)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to add MAC address filter"); + nlmsg_free(msg); + return -1; + } + } + cookie = 0; ret = send_and_recv_resp(drv, msg, cookie_handler, &cookie); if (ret == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " - "0x%llx for freq=%u MHz duration=%u", - (long long unsigned int) cookie, freq, duration); + wpa_printf(MSG_DEBUG, + "nl80211: Remain-on-channel cookie 0x%llx for freq=%u MHz duration=%u%s", + (unsigned long long) cookie, freq, duration, + filter_addr ? " (with MAC filter)" : ""); drv->remain_on_chan_cookie = cookie; drv->pending_remain_on_chan = 1; return 0; @@ -9442,13 +10397,31 @@ fail: } -static int nl80211_remove_link(struct i802_bss *bss, int link_id) +void nl80211_update_active_links(struct i802_bss *bss, int link_id) +{ + wpa_printf(MSG_DEBUG, "nl80211: Update link (ifindex=%d link_id=%u)", + bss->ifindex, link_id); + + if (!(bss->active_links & BIT(link_id))) { + wpa_printf(MSG_DEBUG, + "nl80211: MLD: Update link: Link not found"); + return; + } + + wpa_driver_nl80211_del_beacon(bss, link_id); + + bss->active_links &= ~BIT(link_id); +} + + +int nl80211_remove_link(struct i802_bss *bss, int link_id) { struct wpa_driver_nl80211_data *drv = bss->drv; struct i802_link *link; struct nl_msg *msg; - size_t i; int ret; + u8 link_addr[ETH_ALEN]; + size_t i; wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)", bss->ifindex, link_id); @@ -9461,15 +10434,22 @@ static int nl80211_remove_link(struct i802_bss *bss, int link_id) link = &bss->links[link_id]; - wpa_driver_nl80211_del_beacon(bss, link_id); + os_memcpy(link_addr, link->addr, ETH_ALEN); /* First remove the link locally */ - bss->valid_links &= ~BIT(link_id); os_memset(link->addr, 0, ETH_ALEN); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx); + /* Clear the active links and set the flink */ + nl80211_update_active_links(bss, link_id); + bss->valid_links &= ~BIT(link_id); + + if (bss->scan_link == link) + bss->scan_link = NULL; + /* Choose new deflink if we are removing that link */ if (bss->flink == link) { - for_each_link_default(bss->valid_links, i, 0) { + for_each_link(bss->valid_links, i) { bss->flink = &bss->links[i]; break; } @@ -9500,6 +10480,9 @@ static int nl80211_remove_link(struct i802_bss *bss, int link_id) "nl80211: remove link (%d) failed. ret=%d (%s)", link_id, ret, strerror(-ret)); + if (drv->rtnl_sk) + rtnl_neigh_delete_fdb_entry(bss, link_addr, true); + return ret; } @@ -9648,13 +10631,13 @@ static int get_channel_width(struct nl_msg *msg, void *arg) } -static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, +static int nl80211_get_channel_width(struct i802_bss *bss, struct wpa_signal_info *sig) { struct nl_msg *msg; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); - return send_and_recv_resp(drv, msg, get_channel_width, sig); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_resp(bss->drv, msg, get_channel_width, sig); } @@ -9665,7 +10648,7 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) int res; os_memset(si, 0, sizeof(*si)); - res = nl80211_get_link_signal(drv, drv->bssid, &si->data); + res = nl80211_get_link_signal(bss, drv->bssid, &si->data); if (res) { if (drv->nlmode != NL80211_IFTYPE_ADHOC && drv->nlmode != NL80211_IFTYPE_MESH_POINT) @@ -9673,11 +10656,11 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) si->data.signal = 0; } - res = nl80211_get_channel_width(drv, si); + res = nl80211_get_channel_width(bss, si); if (res != 0) return res; - return nl80211_get_link_noise(drv, si); + return nl80211_get_link_noise(bss, si); } @@ -9729,13 +10712,13 @@ static int get_links_noise(struct nl_msg *msg, void *arg) } -static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv, +static int nl80211_get_links_noise(struct i802_bss *bss, struct wpa_mlo_signal_info *mlo_sig) { struct nl_msg *msg; - msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - return send_and_recv_resp(drv, msg, get_links_noise, mlo_sig); + msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return send_and_recv_resp(bss->drv, msg, get_links_noise, mlo_sig); } @@ -9783,13 +10766,14 @@ static int get_links_channel_width(struct nl_msg *msg, void *arg) } -static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv, +static int nl80211_get_links_channel_width(struct i802_bss *bss, struct wpa_mlo_signal_info *mlo_sig) { struct nl_msg *msg; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); - return send_and_recv_resp(drv, msg, get_links_channel_width, mlo_sig); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_resp(bss->drv, msg, get_links_channel_width, + mlo_sig); } @@ -9809,11 +10793,23 @@ static int nl80211_mlo_signal_poll(void *priv, mlo_si->valid_links = drv->sta_mlo_info.valid_links; for_each_link(mlo_si->valid_links, i) { - res = nl80211_get_link_signal(drv, + /* + * This should be requesting it not for each link, but for + * the MLD address, and then parse out each link data. + * However, that API isn't completed in the kernel yet, + * and some non-upstream drivers erroneously implemented + * a per-link query here. Be compatible with them for now, + * but ignore if this fails since that happens upstream. + * + * Once a correct query is implemented outside the loop, + * this should be skipped if the correct query returned + * the necessary data. + */ + res = nl80211_get_link_signal(bss, drv->sta_mlo_info.links[i].bssid, &mlo_si->links[i].data); - if (res != 0) - return res; + if (res) + mlo_si->links[i].data.signal = -WPA_INVALID_NOISE; mlo_si->links[i].center_frq1 = -1; mlo_si->links[i].center_frq2 = -1; @@ -9822,11 +10818,11 @@ static int nl80211_mlo_signal_poll(void *priv, mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq; } - res = nl80211_get_links_channel_width(drv, mlo_si); + res = nl80211_get_links_channel_width(bss, mlo_si); if (res != 0) return res; - return nl80211_get_links_noise(drv, mlo_si); + return nl80211_get_links_noise(bss, mlo_si); } @@ -9834,6 +10830,7 @@ static int nl80211_set_param(void *priv, const char *param) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + const char *pos; if (param == NULL) return 0; @@ -9848,12 +10845,14 @@ static int nl80211_set_param(void *priv, const char *param) } #endif /* CONFIG_P2P */ - if (os_strstr(param, "use_monitor=1")) - drv->use_monitor = 1; - if (os_strstr(param, "force_connect_cmd=1")) { drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; drv->force_connect_cmd = 1; + /* + * mac80211_hwsim does not implement SPP A-MSDU in + * offload mode. + */ + drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_SPP_AMSDU; } if (os_strstr(param, "force_bss_selection=1")) @@ -9899,6 +10898,36 @@ static int nl80211_set_param(void *priv, const char *param) WPA_DRIVER_FLAGS2_SEC_LTF_AP; } + if (os_strstr(param, "rsn_override_in_driver=1")) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA; + + pos = os_strstr(param, "extra_bss_membership_selectors="); + if (pos) { + int i = 0; + + pos += 31; + + while (*pos) { + char *end; + int sel; + + sel = strtol(pos, &end, 10); + if (pos == end) + return -EINVAL; + + if (sel > 127 || sel < 0) + return -EINVAL; + if (i == + ARRAY_SIZE(drv->extra_bss_membership_selectors)) + return -EINVAL; + drv->extra_bss_membership_selectors[i++] = sel; + + pos = end; + if (*pos == ',') + pos++; + } + } + return 0; } @@ -9921,6 +10950,7 @@ static void * nl80211_global_init(void *ctx) global->ctx = ctx; global->ioctl_sock = -1; dl_list_init(&global->interfaces); + dl_list_init(&global->pending_events); global->if_add_ifindex = -1; cfg = os_zalloc(sizeof(*cfg)); @@ -9965,6 +10995,16 @@ static void nl80211_global_deinit(void *priv) dl_list_len(&global->interfaces)); } + eloop_cancel_timeout(nl80211_deliver_pending_events, global, NULL); + + while (!dl_list_empty(&global->pending_events)) { + struct nl80211_pending_event *event = + dl_list_first(&global->pending_events, + struct nl80211_pending_event, list); + + nl80211_remove_pending_event(event); + } + if (global->netlink) netlink_deinit(global->netlink); @@ -9975,6 +11015,11 @@ static void nl80211_global_deinit(void *priv) nl_cb_put(global->nl_cb); +#ifdef CONFIG_PR + if (global->nl_pr) + nl80211_destroy_eloop_handle(&global->nl_pr, 1); +#endif /* CONFIG_PR */ + if (global->ioctl_sock >= 0) close(global->ioctl_sock); @@ -10249,13 +11294,15 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) int err; union wpa_event_data data; struct survey_results *survey_results; + void *ctx = (bss->scan_link && bss->scan_link->ctx) ? + bss->scan_link->ctx : bss->ctx; os_memset(&data, 0, sizeof(data)); survey_results = &data.survey_results; dl_list_init(&survey_results->survey_list); - msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); if (!msg) return -ENOBUFS; @@ -10271,7 +11318,7 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) if (err) wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); else - wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + wpa_supplicant_event(ctx, EVENT_SURVEY, &data); clean_survey_results(survey_results); return err; @@ -10452,15 +11499,29 @@ static int nl80211_start_radar_detection(void *priv, return -1; } - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) || - nl80211_put_freq_params(msg, freq) < 0) { + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_RADAR_DETECT)) || + nl80211_put_freq_params(msg, freq, bss) < 0) { nlmsg_free(msg); return -1; } + if (nl80211_link_valid(bss->valid_links, freq->link_id)) { + wpa_printf(MSG_DEBUG, + "nl80211: Radar detection (CAC) on link_id=%d", + freq->link_id); + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) { + nlmsg_free(msg); + return -1; + } + } + ret = send_and_recv_cmd(drv, msg); - if (ret == 0) + if (ret == 0) { + nl80211_link_set_freq(bss, freq->link_id, freq->freq); return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " "%d (%s)", ret, strerror(-ret)); return -1; @@ -10491,8 +11552,7 @@ static int nl80211_add_peer_capab(struct nl_msg *msg, static int -nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv, - int link_id) +nl80211_tdls_set_discovery_resp_link(struct i802_bss *bss, int link_id) { #ifdef CONFIG_DRIVER_NL80211_QCA struct nl_msg *msg; @@ -10501,8 +11561,7 @@ nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: TDLS Discovery Response Tx link ID %u", link_id); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_TDLS_DISC_RSP_EXT) || @@ -10517,7 +11576,7 @@ nl80211_tdls_set_discovery_resp_link(struct wpa_driver_nl80211_data *drv, } nla_nest_end(msg, params); - return send_and_recv_cmd(drv, msg); + return send_and_recv_cmd(bss->drv, msg); #else /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_ERROR, "nl80211: Setting TX link for TDLS Discovery Response not supported"); @@ -10542,13 +11601,13 @@ static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, return -EINVAL; if (link_id >= 0 && - nl80211_tdls_set_discovery_resp_link(drv, link_id) < 0) + nl80211_tdls_set_discovery_resp_link(bss, link_id) < 0) return -EOPNOTSUPP; if (link_id < 0 && drv->sta_mlo_info.valid_links) link_id = drv->sta_mlo_info.assoc_link_id; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_MGMT)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) || nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) || nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) || @@ -10603,7 +11662,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) return -EINVAL; } - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_OPER)) || nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) { nlmsg_free(msg); @@ -10638,7 +11697,7 @@ nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class, if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) || - (ret = nl80211_put_freq_params(msg, params))) { + (ret = nl80211_put_freq_params(msg, params, bss))) { nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch"); return ret; @@ -10744,9 +11803,12 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type, { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; - if (type != WPA_IF_AP_BSS || - !nl80211_link_valid(bss->valid_links, link_id)) + if (type == WPA_IF_AP_VLAN) + return wpa_driver_nl80211_if_remove(bss, type, ifname); + + if (type != WPA_IF_AP_BSS) return -1; wpa_printf(MSG_DEBUG, @@ -10755,26 +11817,39 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type, nl80211_remove_link(bss, link_id); - bss->ctx = bss->flink->ctx; - - if (drv->first_bss == bss && bss->valid_links) + if (drv->ctx == bss->ctx) { + eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); + bss->ctx = bss->flink->ctx; drv->ctx = bss->ctx; + } else { + bss->ctx = bss->flink->ctx; + } if (!bss->valid_links) { + void *ctx = bss->ctx; + wpa_printf(MSG_DEBUG, "nl80211: No more links remaining, so remove interface"); - return wpa_driver_nl80211_if_remove(bss, type, ifname); + ret = wpa_driver_nl80211_if_remove(bss, type, ifname); + if (ret) + return ret; + + /* Notify that the MLD interface is removed */ + wpa_supplicant_event(ctx, EVENT_MLD_INTERFACE_FREED, NULL); } return 0; } -static bool nl80211_is_drv_shared(void *priv, void *bss_ctx) +static bool nl80211_is_drv_shared(void *priv, int link_id) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - unsigned int num_bss = 0; + unsigned int num_bss = 0, num_links = 0; + bool self = false; + u8 i; /* If any other BSS exist, someone else is using this since at this * time, we would have removed all BSSs created by this driver and only @@ -10789,13 +11864,23 @@ static bool nl80211_is_drv_shared(void *priv, void *bss_ctx) /* This is the only BSS present */ bss = priv; - /* If only one/no link is there no one is sharing */ - if (bss->valid_links <= 1) + for_each_link(bss->valid_links, i) { + num_links++; + if (i == link_id) + self = true; + } + + /* More than one links means some one is still sharing */ + if (num_links > 1) + return true; + + /* Even if only one link is there, it should match the given + * link ID to assert that no one else is sharing. */ + if (num_links == 1 && self) return false; - /* More than one link means someone is still using. To check if - * only 1 bit is set, power of 2 condition can be checked. */ - if (!(bss->valid_links & (bss->valid_links - 1))) + /* No links are active means no one is sharing */ + if (num_links == 0) return false; return true; @@ -10850,11 +11935,12 @@ static int driver_nl80211_send_action(void *priv, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, - int no_cck) + int no_cck, int link_id) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src, - bssid, data, data_len, no_cck); + bssid, data, data_len, no_cck, + link_id); } @@ -10875,7 +11961,7 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, u16 mdid = WPA_GET_LE16(md); wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_FT_IES)) || nla_put(msg, NL80211_ATTR_IE, ies_len, ies) || nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) { nlmsg_free(msg); @@ -10926,7 +12012,7 @@ static const u8 * wpa_driver_nl80211_get_macaddr(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + if (nl80211_is_netdev_iftype(drv->nlmode)) return NULL; return bss->addr; @@ -11013,16 +12099,13 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "prev_bssid=" MACSTR "\n" "associated=%d\n" "assoc_freq=%u\n" - "monitor_sock=%d\n" - "monitor_ifidx=%d\n" - "monitor_refcount=%d\n" "last_mgmt_freq=%u\n" "eapol_tx_sock=%d\n" - "%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", drv->phyname, MAC2STR(drv->perm_addr), drv->ifindex, - drv->operstate, + bss->operstate, scan_state_str(drv->scan_state), MAC2STR(drv->auth_bssid), MAC2STR(drv->auth_attempt_bssid), @@ -11030,12 +12113,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) MAC2STR(drv->prev_bssid), drv->associated, drv->assoc_freq, - drv->monitor_sock, - drv->monitor_ifidx, - drv->monitor_refcount, drv->last_mgmt_freq, drv->eapol_tx_sock, - drv->ignore_if_down_event ? + bss->ignore_if_down_event ? "ignore_if_down_event=1\n" : "", drv->scan_complete_events ? "scan_complete_events=1\n" : "", @@ -11050,7 +12130,6 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->data_tx_status ? "data_tx_status=1\n" : "", drv->scan_for_auth ? "scan_for_auth=1\n" : "", drv->retry_auth ? "retry_auth=1\n" : "", - drv->use_monitor ? "use_monitor=1\n" : "", drv->ignore_next_local_disconnect ? "ignore_next_local_disconnect\n" : "", drv->ignore_next_local_deauth ? @@ -11124,7 +12203,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.max_sched_scan_plan_interval=%u\n" "capa.max_sched_scan_plan_iterations=%u\n" "capa.mbssid_max_interfaces=%u\n" - "capa.ema_max_periodicity=%u\n", + "capa.ema_max_periodicity=%u\n" + "capa.max_probe_req_ie_len=%zu\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -11149,7 +12229,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.max_sched_scan_plan_interval, drv->capa.max_sched_scan_plan_iterations, drv->capa.mbssid_max_interfaces, - drv->capa.ema_max_periodicity); + drv->capa.ema_max_periodicity, + drv->capa.max_probe_req_ie_len); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -11175,8 +12256,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) return pos - buf; } - -static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) +static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings, + bool skip_mbssid) { if ((settings->head && nla_put(msg, NL80211_ATTR_BEACON_HEAD, @@ -11198,6 +12279,11 @@ static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) settings->probe_resp_len, settings->probe_resp))) return -ENOBUFS; +#ifdef CONFIG_IEEE80211AX + if (!skip_mbssid && nl80211_mbssid(msg, &settings->mbssid) < 0) + return -ENOBUFS; +#endif /* CONFIG_IEEE80211AX */ + return 0; } @@ -11221,7 +12307,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) settings->freq_params.bandwidth, settings->freq_params.center_freq1, settings->freq_params.center_freq2, - settings->punct_bitmap, + settings->freq_params.punct_bitmap, settings->link_id, settings->freq_params.ht_enabled ? " ht" : "", settings->freq_params.vht_enabled ? " vht" : "", @@ -11291,18 +12377,15 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) || nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count) || - (ret = nl80211_put_freq_params(msg, &settings->freq_params)) || + (ret = nl80211_put_freq_params(msg, &settings->freq_params, bss)) || (settings->block_tx && nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) || - (settings->punct_bitmap && - nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP, - settings->punct_bitmap)) || (settings->link_id != NL80211_DRV_LINK_ID_NA && nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id))) goto error; /* beacon_after params */ - ret = set_beacon_data(msg, &settings->beacon_after); + ret = set_beacon_data(msg, &settings->beacon_after, false); if (ret) goto error; @@ -11311,7 +12394,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) if (!beacon_csa) goto fail; - ret = set_beacon_data(msg, &settings->beacon_csa); + ret = set_beacon_data(msg, &settings->beacon_csa, true); if (ret) goto error; @@ -11388,7 +12471,7 @@ static int nl80211_switch_color(void *priv, struct cca_settings *settings) goto error; /* beacon_after params */ - ret = set_beacon_data(msg, &settings->beacon_after); + ret = set_beacon_data(msg, &settings->beacon_after, false); if (ret) goto error; @@ -11399,7 +12482,7 @@ static int nl80211_switch_color(void *priv, struct cca_settings *settings) goto error; } - ret = set_beacon_data(msg, &settings->beacon_cca); + ret = set_beacon_data(msg, &settings->beacon_cca, true); if (ret) goto error; @@ -11606,7 +12689,7 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, /* This test vendor_cmd can be used with nl80211 commands that * need the connect nl_sock, so use the variant that takes in * bss->nl_connect as the handle. */ - ret = send_and_recv(drv->global, bss->nl_connect, msg, + ret = send_and_recv(drv, bss->nl_connect, msg, cmd_reply_handler, buf, NULL, NULL, NULL); if (ret) wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d", @@ -11693,7 +12776,7 @@ static int nl80211_get_wowlan(void *priv) wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status"); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_WOWLAN); ret = send_and_recv_resp(drv, msg, get_wowlan_handler, &wowlan_enabled); if (ret) { @@ -11766,7 +12849,7 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return -1; } - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_ROAMING) || @@ -11797,7 +12880,7 @@ static int nl80211_disable_fils(void *priv, int disable) if (!drv->set_wifi_conf_vendor_cmd_avail) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) || @@ -11832,7 +12915,7 @@ static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid, if (!drv->roam_vendor_cmd_avail) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_ROAM) || @@ -11886,7 +12969,7 @@ static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) wpa_printf(MSG_DEBUG, "nl80211: Add STA node"); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) || @@ -12035,9 +13118,9 @@ static int nl80211_join_mesh(struct i802_bss *bss, int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_MESH); if (!msg || - nl80211_put_freq_params(msg, ¶ms->freq) || + nl80211_put_freq_params(msg, ¶ms->freq, bss) || nl80211_put_basic_rates(msg, params->basic_rates) || nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) || nl80211_put_beacon_int(msg, params->beacon_int) || @@ -12081,7 +13164,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER)) return -1; - ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL, + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); msg = NULL; if (ret) { @@ -12091,7 +13174,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, } ret = 0; drv->assoc_freq = bss->flink->freq = params->freq.freq; - wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); + wpa_printf(MSG_DEBUG, "nl80211: mesh join request sent successfully"); fail: nlmsg_free(msg); @@ -12138,15 +13221,15 @@ static int wpa_driver_nl80211_leave_mesh(void *priv) int ret; wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH); - ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL, + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_MESH); + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)", ret, strerror(-ret)); } else { wpa_printf(MSG_DEBUG, - "nl80211: mesh leave request send successfully"); + "nl80211: mesh leave request sent successfully"); drv->first_bss->flink->freq = 0; } @@ -12168,7 +13251,7 @@ static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth, struct nl_msg *msg; int ret; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_MESH_LINK); if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put(msg, NL80211_ATTR_FRAME, len, eth)) { @@ -12495,7 +13578,7 @@ static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list) } -static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list) +static int add_freq_list(struct nl_msg *msg, int attr, const int *freq_list) { int i, len, ret; u32 *freqs; @@ -12508,26 +13591,26 @@ static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list) return -1; for (i = 0; i < len; i++) freqs[i] = freq_list[i]; - ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, - sizeof(u32) * len, freqs); + ret = nla_put(msg, attr, sizeof(u32) * len, freqs); os_free(freqs); return ret; } -static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv, +static int nl80211_qca_do_acs(struct i802_bss *bss, struct drv_acs_params *params) { struct nl_msg *msg; struct nlattr *data; int ret; int mode; + struct wpa_driver_nl80211_data *drv = bss->drv; mode = hw_mode_to_qca_acs(params->hw_mode); if (mode < 0) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_DO_ACS) || @@ -12544,7 +13627,8 @@ static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv, nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) || add_acs_ch_list(msg, params->freq_list) || - add_acs_freq_list(msg, params->freq_list) || + add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, + params->freq_list) || (params->edmg_enabled && nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED)) || (params->link_id != NL80211_DRV_LINK_ID_NA && @@ -12605,7 +13689,7 @@ static int nl80211_set_band(void *priv, u32 band_mask) "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d", qca_band_mask, qca_band_value); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SETBAND) || @@ -12802,7 +13886,7 @@ static int nl80211_get_pref_freq_list(void *priv, param.num = *num; param.freq_list = freq_list; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, @@ -12856,7 +13940,7 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) "nl80211: Set P2P probable operating freq %u for ifindex %d", freq, bss->ifindex); - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) || @@ -12906,7 +13990,7 @@ static int nl80211_p2p_lo_start(void *priv, unsigned int freq, if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START)) @@ -12958,7 +14042,7 @@ static int nl80211_p2p_lo_stop(void *priv) if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) { @@ -12989,7 +14073,7 @@ static int nl80211_set_tdls_mode(void *priv, int tdls_external_control) else tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS)) @@ -13126,7 +14210,7 @@ nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg) static struct wpa_bss_candidate_info * -nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) +qca_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -13159,7 +14243,7 @@ nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) */ info->num = params->n_candidates; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS)) @@ -13233,7 +14317,7 @@ static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow) if (!drv->set_wifi_conf_vendor_cmd_avail) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) @@ -13281,7 +14365,7 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params) wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: PASN authentication response for %d entries", params->num_peers); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); if (!msg || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, @@ -13297,6 +14381,8 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params) goto fail; for (i = 0; i < params->num_peers; i++) { + int akmp, cipher; + attr1 = nla_nest_start(msg, i); if (!attr1 || nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR, @@ -13310,12 +14396,33 @@ static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params) QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS)) goto fail; + akmp = params->peer[i].akmp; + cipher = params->peer[i].cipher; + if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM, + wpa_akm_to_suite(akmp)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER, + wpa_cipher_to_cipher_suite(cipher))) + goto fail; + + if (params->peer[i].comeback_after && + params->peer[i].comeback_len && + params->peer[i].comeback && + (nla_put_u16(msg, + QCA_WLAN_VENDOR_ATTR_PASN_PEER_COMEBACK_AFTER, + params->peer[i].comeback_after) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE, + params->peer[i].comeback_len, + params->peer[i].comeback))) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: Own address[%u]: " MACSTR - " Peer address[%u]: " MACSTR " Status: %s", + " Peer address[%u]: " MACSTR + " Status: %s AKMP: 0x%x cipher: 0x%x", i, MAC2STR(params->peer[i].own_addr), i, MAC2STR(params->peer[i].peer_addr), - params->peer[i].status ? "Fail" : "Success"); + params->peer[i].status ? "Fail" : "Success", + akmp, cipher); nla_nest_end(msg, attr1); } @@ -13364,7 +14471,7 @@ static int nl80211_set_secure_ranging_ctx(void *priv, "nl80211: Secure ranging context for " MACSTR, MAC2STR(params->peer_addr)); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); if (!msg || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, @@ -13431,6 +14538,318 @@ fail: #endif /* CONFIG_PASN */ +#ifdef CONFIG_NAN_USD + +static int nl80211_nan_flush(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: NAN USD flush"); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH)) + goto fail; + + nla_nest_end(msg, container); + + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD flush"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + + +static const u8 qca_usd_default_network_id[ETH_ALEN] = { + 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 +}; + +static int nl80211_nan_publish(void *priv, const u8 *src, int publish_id, + const char *service_name, const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + const struct wpabuf *elems, + struct nan_publish_params *params, + const u8 *network_id) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container, *attr; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: Start NAN USD publish: default freq=%u, ttl=%u", + params->freq, params->ttl); + wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR, ETH_ALEN, src) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, publish_id) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID, + NAN_SERVICE_ID_LEN, service_id) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE, + srv_proto_type) || + nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_USD_TTL, params->ttl) || + (elems && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER, + wpabuf_len(elems), wpabuf_head(elems))) || + (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI, + wpabuf_len(ssi), wpabuf_head(ssi))) || + (network_id && + !ether_addr_equal(network_id, qca_usd_default_network_id) && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID, ETH_ALEN, + network_id))) + goto fail; + + attr = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG); + if (!attr) + goto fail; + if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ, + params->freq) || + add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST, + params->freq_list)) + goto fail; + nla_nest_end(msg, attr); + + nla_nest_end(msg, container); + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD publish"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_nan_cancel_publish(void *priv, int publish_id) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel publish"); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, + publish_id)) + goto fail; + + nla_nest_end(msg, container); + + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD cancel publish"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_nan_update_publish(void *priv, int publish_id, + const struct wpabuf *ssi) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: NAN USD update publish: id=%d", + publish_id); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, + publish_id) || + (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI, + wpabuf_len(ssi), wpabuf_head(ssi)))) + goto fail; + + nla_nest_end(msg, container); + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD update publish"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_nan_subscribe(void *priv, const u8 *src, int subscribe_id, + const char *service_name, const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + const struct wpabuf *elems, + struct nan_subscribe_params *params, + const u8 *network_id) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container, *attr; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: Start NAN USD subscribe: freq=%u, ttl=%u", + params->freq, params->ttl); + wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR, ETH_ALEN, src) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, + subscribe_id) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID, + NAN_SERVICE_ID_LEN, service_id) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE, + srv_proto_type) || + nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_USD_TTL, params->ttl) || + (elems && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER, + wpabuf_len(elems), wpabuf_head(elems))) || + (ssi && nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_SSI, + wpabuf_len(ssi), wpabuf_head(ssi))) || + (network_id && + !ether_addr_equal(network_id, qca_usd_default_network_id) && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_USD_NETWORK_ID, ETH_ALEN, + network_id))) + goto fail; + + attr = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG); + if (!attr || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ, + params->freq) || + add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST, + params->freq_list)) + goto fail; + nla_nest_end(msg, attr); + + nla_nest_end(msg, container); + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD subscribe"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_nan_cancel_subscribe(void *priv, int subscribe_id) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel subscribe"); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_USD)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE, + QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID, + subscribe_id)) + goto fail; + + nla_nest_end(msg, container); + + ret = send_and_recv_cmd(drv, msg); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed to send NAN USD cancel subscribe"); + return ret; + +fail: + nlmsg_free(msg); + return -1; +} + +#endif /* CONFIG_NAN_USD */ + #endif /* CONFIG_DRIVER_NL80211_QCA */ @@ -13496,7 +14915,7 @@ static int nl80211_do_acs(void *priv, struct drv_acs_params *params) #ifdef CONFIG_DRIVER_NL80211_QCA if (drv->qca_do_acs) - return nl80211_qca_do_acs(drv, params); + return nl80211_qca_do_acs(bss, params); #endif /* CONFIG_DRIVER_NL80211_QCA */ #ifdef CONFIG_DRIVER_NL80211_BRCM @@ -13508,6 +14927,60 @@ static int nl80211_do_acs(void *priv, struct drv_acs_params *params) } +#ifdef CONFIG_MBO +static struct wpa_bss_candidate_info * +nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) +{ +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) + /* This only exists for testing purposes, disable unless requested */ + if (TEST_FAIL_TAG("simulate")) { + struct wpa_bss_candidate_info *info; + int i; + + info = os_zalloc(sizeof(*info)); + if (!info) + return NULL; + + info->candidates = os_calloc(params->n_candidates, + sizeof(*info->candidates)); + if (!info->candidates) { + os_free(info); + return NULL; + } + + info->num = params->n_candidates; + for (i = 0; i < params->n_candidates; i++) { + char bssid_str[ETH_ALEN * 3]; + + os_memcpy(info->candidates[i].bssid, + ¶ms->bssid[i * ETH_ALEN], ETH_ALEN); + + os_snprintf(bssid_str, sizeof(bssid_str), MACSTR, + MAC2STR(info->candidates[i].bssid)); + + if (TEST_FAIL_TAG(bssid_str)) { + info->candidates[i].is_accept = 0; + info->candidates[i].reject_reason = + MBO_TRANSITION_REJECT_REASON_FRAME_LOSS; + } else { + info->candidates[i].is_accept = 1; + info->candidates[i].reject_reason = 0; + } + } + + return info; + } +#endif /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */ + +#ifdef CONFIG_DRIVER_NL80211_QCA + return qca_get_bss_transition_status(priv, params); +#else /* CONFIG_DRIVER_NL80211_QCA */ + return NULL; +#endif /* CONFIG_DRIVER_NL80211_QCA */ +} +#endif /* CONFIG_MBO */ + + static int nl80211_write_to_file(const char *name, unsigned int val) { int fd, len; @@ -13552,8 +15025,9 @@ static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags) char path[128]; int ret; - /* P2P-Device has no netdev that can (or should) be configured here */ - if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE) + /* P2P/NAN-Device has no netdev that can (or should) be configured here + */ + if (!nl80211_is_netdev_iftype(nl80211_get_ifmode(bss))) return 0; wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x", @@ -13700,7 +15174,7 @@ static int nl80211_update_connection_params( if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) return 0; - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS); if (!msg) goto fail; @@ -13717,7 +15191,7 @@ static int nl80211_update_connection_params( if (mask & WPA_DRV_UPDATE_AUTH_TYPE) { type = get_nl_auth_type(params->auth_alg); - if (type == NL80211_AUTHTYPE_MAX || + if (type > NL80211_AUTHTYPE_MAX || nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); @@ -13757,10 +15231,49 @@ static int nl80211_send_external_auth_status(void *priv, (drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return -1; +#ifdef CONFIG_DRIVER_NL80211_QCA + if (drv->qca_vendor_ext_auth) { + struct nlattr *attr; + + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: QCA vendor: External auth status: %u", + params->status); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr || + nla_put_u16(msg, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_STATUS_CODE, + params->status) || + (params->ssid && params->ssid_len && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID, + params->ssid_len, params->ssid)) || + (params->pmkid && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PMKID, + PMKID_LEN, params->pmkid)) || + (params->bssid && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID, + ETH_ALEN, params->bssid)) || + (params->kck && params->kck_len && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PTK_KCK, + params->kck_len, params->kck))) + goto fail; + + nla_nest_end(msg, attr); + goto send_cmd; + } +#endif /* CONFIG_DRIVER_NL80211_QCA */ + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: External auth status: %u", params->status); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_EXTERNAL_AUTH); if (!msg || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || (params->ssid && params->ssid_len && @@ -13770,6 +15283,11 @@ static int nl80211_send_external_auth_status(void *priv, (params->bssid && nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))) goto fail; + +#ifdef CONFIG_DRIVER_NL80211_QCA +send_cmd: + drv->qca_vendor_ext_auth = 0; +#endif /* CONFIG_DRIVER_NL80211_QCA */ ret = send_and_recv_cmd(drv, msg); msg = NULL; if (ret) { @@ -13878,7 +15396,7 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr, return -EINVAL; } - if (bss->valid_links & BIT(link_id)) { + if (bss->active_links & BIT(link_id)) { wpa_printf(MSG_DEBUG, "nl80211: MLD: Link %u already set", link_id); return -EINVAL; @@ -13910,19 +15428,26 @@ static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr, os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN); /* The new link is the first one, make it the default */ - if (!bss->valid_links) + if (!bss->active_links) bss->flink = &bss->links[link_id]; bss->valid_links |= BIT(link_id); + bss->active_links |= BIT(link_id); bss->links[link_id].ctx = bss_ctx; - wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s", - bss->valid_links, bss->ifname); + wpa_printf(MSG_DEBUG, + "nl80211: MLD: valid_links=0x%04x active_links=0x%04x on %s", + bss->valid_links, bss->active_links, bss->ifname); + + if (drv->rtnl_sk) + rtnl_neigh_add_fdb_entry(bss, addr, true); + return 0; } #ifdef CONFIG_IEEE80211BE + static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id, const u8 *addr) { @@ -13949,6 +15474,195 @@ static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id, return ret; } + + +static int wpa_driver_get_wiphy_name_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_driver_nl80211_data *drv = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_WIPHY_NAME]) + return NL_SKIP; + + os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + + return NL_SKIP; +} + + +static int wpa_driver_get_phyname(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + u32 nl_flags = 0; + u32 feat = get_nl80211_protocol_features(drv); + + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + + if (!(msg = nl80211_cmd_msg(drv->first_bss, nl_flags, + NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return -1; + } + + if (send_and_recv_resp(drv, msg, wpa_driver_get_wiphy_name_handler, + drv)) + return -1; + + return 0; +} + + +static bool +wpa_driver_nl80211_name_match(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_nl80211_data **match_drv) +{ + struct wpa_driver_nl80211_data *drv2; + + dl_list_for_each(drv2, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_strcmp(drv2->phyname, drv->phyname) == 0) { + if (match_drv) + *match_drv = drv2; + return true; + } + } + + return false; +} + + +static bool wpa_driver_nl80211_can_share_drv(void *ctx, + struct wpa_init_params *params, + void **hapd) +{ + struct wpa_driver_nl80211_data *drv, *match_drv = NULL; + struct i802_bss *bss; + bool ret = false; + + if (!params->global_priv) + return false; + + /* Create a temporary drv to read the phyname */ + drv = os_zalloc(sizeof(*drv)); + if (!drv) + return false; + drv->global = params->global_priv; + drv->ctx = ctx; + + drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); + if (!drv->first_bss) { + os_free(drv); + return false; + } + + bss = drv->first_bss; + bss->drv = drv; + bss->ctx = ctx; + + os_strlcpy(bss->ifname, params->ifname, sizeof(bss->ifname)); + + if (nl80211_init_bss(bss)) + goto free_all; + + drv->ifindex = if_nametoindex(bss->ifname); + bss->ifindex = drv->ifindex; + + if (wpa_driver_get_phyname(drv) || + !wpa_driver_nl80211_name_match(drv, &match_drv) || + !match_drv) + goto free_all; + + wpa_printf(MSG_DEBUG, "nl80211: Driver for phy %s already exist", + match_drv->phyname); + + *hapd = match_drv->first_bss->ctx; + ret = true; + +free_all: + nl80211_destroy_bss(bss); + os_free(bss); + os_free(drv); + return ret; +} + + +static int +nl80211_send_link_reconfig_request(void *priv, + struct wpa_mlo_reconfig_info *info) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *add_links, *attr; + int ret = -1; + u8 link_id; + + if (!drv->sta_mlo_info.valid_links) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Send ML Link Reconfiguration Request"); + + if (info->add_links) + wpa_printf(MSG_DEBUG, "Add Setup Links Bitmask: 0x%x", + info->add_links); + + if (info->delete_links) + wpa_printf(MSG_DEBUG, "Remove Setup Links Bitmask: 0x%x", + info->delete_links); + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOC_MLO_RECONF); + if (!msg) + goto error; + + add_links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!add_links) + goto error; + + for_each_link(info->add_links, link_id) { + attr = nla_nest_start(msg, 0); + if (!attr) + goto error; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + info->add_link_bssid[link_id]) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + info->add_link_freq[link_id])) + goto error; + + nla_nest_end(msg, attr); + } + + nla_nest_end(msg, add_links); + + if (nla_put_u16(msg, NL80211_ATTR_MLO_RECONF_REM_LINKS, + info->delete_links)) + goto error; + + ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL, NULL, + NULL); + if (ret) { + wpa_printf(MSG_INFO, + "nl80211: Failed to send Link Reconfiguration Request err=%d (%s)", + ret, strerror(-ret)); + return ret; + } + return 0; + +error: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, + "nl80211: Could not build Link Reconfiguration Request"); + return ret; +} + #endif /* CONFIG_IEEE80211BE */ @@ -13991,12 +15705,982 @@ static int testing_nl80211_radio_disable(void *priv, int disabled) return wpa_driver_nl80211_mlme(drv, drv->bssid, NL80211_CMD_DEAUTHENTICATE, WLAN_REASON_PREV_AUTH_NOT_VALID, 1, - drv->first_bss); + bss); } #endif /* CONFIG_TESTING_OPTIONS */ +static struct hostapd_multi_hw_info * +wpa_driver_get_multi_hw_info(void *priv, unsigned int *num_multi_hws) +{ + struct i802_bss *bss = priv; + + return nl80211_get_multi_hw_info(bss, num_multi_hws); +} + + +#ifdef CONFIG_NAN + +static int nl80211_nan_config(struct i802_bss *bss, + struct wpa_driver_nl80211_data *drv, + const struct nan_cluster_config *params, + enum nl80211_commands cmd) +{ + struct nl_msg *msg; + struct nlattr *conf, *bands_conf, *band; + u32 bands = 0; + + if (params->dual_band > 1) + return -EINVAL; + + bands |= BIT(NL80211_BAND_2GHZ); + + if (params->dual_band) { + if (drv->capa.nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND) { + bands |= BIT(NL80211_BAND_5GHZ); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Driver doesn't support NAN dual band operation"); + return -EINVAL; + } + } + + msg = nl80211_cmd_msg(bss, 0, cmd); + if (!msg || nla_put_u8(msg, NL80211_ATTR_NAN_MASTER_PREF, + params->master_pref) || + (bands && nla_put_u32(msg, NL80211_ATTR_BANDS, bands))) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to build start NAN command"); + goto fail; + } + + conf = nla_nest_start(msg, NL80211_ATTR_NAN_CONFIG); + if (!conf) + goto fail; + + if (params->enable_dw_notif) { + if (!(drv->capa.nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) { + wpa_printf(MSG_INFO, + "nl80211: Driver doesn't support NAN DW notifications"); + goto fail; + } + + if (nla_put_flag(msg, NL80211_NAN_CONF_NOTIFY_DW)) + goto fail; + } + + if (!is_zero_ether_addr(params->cluster_id) && + nla_put(msg, NL80211_NAN_CONF_CLUSTER_ID, + ETH_ALEN, params->cluster_id)) + goto fail; + + if (params->scan_period && + nla_put_u16(msg, NL80211_NAN_CONF_SCAN_PERIOD, + params->scan_period)) + goto fail; + + if (params->scan_dwell_time && + nla_put_u16(msg, NL80211_NAN_CONF_SCAN_DWELL_TIME, + params->scan_dwell_time)) + goto fail; + + if (params->discovery_beacon_interval && + nla_put_u8(msg, NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL, + params->discovery_beacon_interval)) + goto fail; + + bands_conf = nla_nest_start(msg, NL80211_NAN_CONF_BAND_CONFIGS); + if (!bands_conf) + goto fail; + + band = nla_nest_start(msg, 1); + if (!band) + goto fail; + + if (nla_put_u8(msg, NL80211_NAN_BAND_CONF_BAND, + NL80211_BAND_2GHZ) || + nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_CLOSE, + params->low_band_cfg.rssi_close) || + nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_MIDDLE, + params->low_band_cfg.rssi_middle) || + nla_put_u8(msg, NL80211_NAN_BAND_CONF_WAKE_DW, + params->low_band_cfg.awake_dw_interval) || + (params->low_band_cfg.disable_scan && + nla_put_flag(msg, NL80211_NAN_BAND_CONF_DISABLE_SCAN))) + goto fail; + + nla_nest_end(msg, band); + + if (params->dual_band) { + band = nla_nest_start(msg, 2); + if (!band) + goto fail; + + if (nla_put_u8(msg, NL80211_NAN_BAND_CONF_BAND, + NL80211_BAND_5GHZ) || + nla_put_u16(msg, NL80211_NAN_BAND_CONF_FREQ, + params->high_band_cfg.frequency) || + nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_CLOSE, + params->high_band_cfg.rssi_close) || + nla_put_s8(msg, NL80211_NAN_BAND_CONF_RSSI_MIDDLE, + params->high_band_cfg.rssi_middle) || + nla_put_u8(msg, NL80211_NAN_BAND_CONF_WAKE_DW, + params->high_band_cfg.awake_dw_interval) || + (params->high_band_cfg.disable_scan && + nla_put_flag(msg, NL80211_NAN_BAND_CONF_DISABLE_SCAN))) + goto fail; + + nla_nest_end(msg, band); + } + + nla_nest_end(msg, bands_conf); + + if (params->extra_nan_attrs && params->extra_nan_attrs_len && + nla_put(msg, NL80211_NAN_CONF_EXTRA_ATTRS, + params->extra_nan_attrs_len, + params->extra_nan_attrs)) + goto fail; + + if (params->vendor_elems && params->vendor_elems_len && + nla_put(msg, NL80211_NAN_CONF_VENDOR_ELEMS, + params->vendor_elems_len, params->vendor_elems)) + goto fail; + + nla_nest_end(msg, conf); + + return send_and_recv_resp(drv, msg, NULL, NULL); +fail: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_nan_start(void *priv, + const struct nan_cluster_config *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + + if (drv->nlmode != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (drv->nan_started) + return -EALREADY; + + wpa_printf(MSG_DEBUG, "nl80211: Start/join NAN cluster"); + ret = nl80211_nan_config(bss, drv, params, NL80211_CMD_START_NAN); + if (!ret) + drv->nan_started = 1; + + return ret; +} + + +static int +wpa_driver_nl80211_nan_change_config(void *priv, + const struct nan_cluster_config *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->nlmode != NL80211_IFTYPE_NAN) + return -EOPNOTSUPP; + + if (!drv->nan_started) + return -EINVAL; + + wpa_printf(MSG_DEBUG, "nl80211: Update NAN configuration"); + return nl80211_nan_config(bss, drv, params, + NL80211_CMD_CHANGE_NAN_CONFIG); +} + + +static void wpa_driver_nl80211_nan_stop(void *priv) +{ + struct i802_bss *bss = priv; + + nl80211_nan_stop(bss); +} + + +static int +wpa_driver_nl80211_nan_set_channel(struct wpa_driver_nl80211_data *drv, + struct nan_schedule_channel *chan, + struct nl_msg *msg) +{ + struct nlattr *chan_attr; + enum nl80211_chan_width cw; + + chan_attr = nla_nest_start(msg, NL80211_ATTR_NAN_CHANNEL); + if (!chan) + return -ENOBUFS; + + if (nl80211_bw_to_nl(chan->bandwidth, &cw, chan->center_freq2)) + return -EINVAL; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->freq) || + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chan->center_freq1) || + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chan->center_freq2) || + nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) || + nla_put(msg, NL80211_ATTR_NAN_CHANNEL_ENTRY, + sizeof(chan->chan_entry), chan->chan_entry)) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Failed to put channel attributes"); + return -EINVAL; + } + + if (chan->rx_nss && + nla_put_u8(msg, NL80211_ATTR_NAN_RX_NSS, chan->rx_nss)) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Failed to put RX NSS attribute"); + return -EINVAL; + } + + nla_nest_end(msg, chan_attr); + return 0; +} + + +static int +wpa_driver_nl80211_nan_set_slots(struct wpa_driver_nl80211_data *drv, + struct wpabuf *chan_time_bitmap, + u8 *slots, u8 chan_idx) +{ + const u8 *time_bitmap; + size_t time_bitmap_len, idx; + + if (!chan_time_bitmap) + return 0; + + time_bitmap = wpabuf_head(chan_time_bitmap); + time_bitmap_len = wpabuf_len(chan_time_bitmap); + if (!time_bitmap || !time_bitmap_len) + return 0; + + /* + * nl80211 always uses a period of 512 TUs with slot duration of + * 16 TUs, so the map length must be equal to 4. + */ + if (time_bitmap_len != 4) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Invalid time bitmap len=%zu", + time_bitmap_len); + return -EINVAL; + } + + for (idx = 0; idx < time_bitmap_len * 8; idx++) { + size_t byte_idx = idx / 8; + size_t bit_idx = idx % 8; + + if (time_bitmap[byte_idx] & BIT(bit_idx)) + slots[idx] = chan_idx; + } + + return 0; +} + + +static int +wpa_driver_nl80211_nan_set_schedule(struct wpa_driver_nl80211_data *drv, + struct nan_schedule_config *sched, + struct nl_msg *msg) +{ + u8 slots[32]; + size_t i; + + os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT, sizeof(slots)); + + for (i = 0; i < sched->num_channels; i++) { + int ret; + + ret = wpa_driver_nl80211_nan_set_channel( + drv, &sched->channels[i], msg); + if (ret) + return ret; + + ret = wpa_driver_nl80211_nan_set_slots( + drv, sched->channels[i].time_bitmap, slots, i); + if (ret) + return ret; + } + + return nla_put(msg, NL80211_ATTR_NAN_TIME_SLOTS, sizeof(slots), slots); +} + + +static int +wpa_driver_nl80211_nan_config_schedule(void *priv, u8 map_id, + struct nan_schedule_config *sched) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started) + return -EOPNOTSUPP; + + if (!sched) + return -EINVAL; + + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_LOCAL_SCHED); + if (!msg) { + wpa_printf(MSG_INFO, + "nl80211: Failed to alloc NAN local schedule command"); + return -ENOMEM; + } + + if (sched->deferred && + nla_put_flag(msg, NL80211_ATTR_NAN_SCHED_DEFERRED)) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Failed to put deferred attribute"); + goto fail; + } + + ret = wpa_driver_nl80211_nan_set_schedule(drv, sched, msg); + if (ret) + goto fail; + + if (sched->avail_attr && + nl80211_attr_supported(drv, NL80211_ATTR_NAN_AVAIL_BLOB) && + nla_put(msg, NL80211_ATTR_NAN_AVAIL_BLOB, + wpabuf_len(sched->avail_attr), + wpabuf_head(sched->avail_attr))) + goto fail; + + ret = send_and_recv_cmd(bss->drv, msg); + if (ret) + wpa_printf(MSG_INFO, + "nl80211: Failed to send NAN local schedule command: %d", + ret); + + return ret; + +fail: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int +wpa_driver_nl80211_nan_config_peer_schedule( + void *priv, const u8 *peer, u16 cdw, u8 sequence_id, + u16 max_chan_switch_time, const struct wpabuf *ulw, + struct nan_peer_schedule_config *sched) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nan_schedule_channel *channels[MAX_NUM_NAN_MAPS * + MAX_NUM_NAN_SCHEDULE_CHANNELS]; + u8 chan_remap[MAX_NUM_NAN_MAPS][MAX_NUM_NAN_SCHEDULE_CHANNELS]; + unsigned int map_idx, chan_idx, n_channels = 0; + struct nl_msg *msg; + struct nlattr *maps; + int ret; + + if (drv->nlmode != NL80211_IFTYPE_NAN || !drv->nan_started) + return -EOPNOTSUPP; + + if (!sched) + return -EINVAL; + + os_memset(channels, 0, sizeof(channels)); + os_memset(chan_remap, 0, sizeof(chan_remap)); + + /* + * Kernel expects channels at the top level, and peer maps reference + * them by index. Build a global channel list from all maps. + */ + for (map_idx = 0; map_idx < sched->n_maps; map_idx++) { + struct nan_schedule_map *map = &sched->maps[map_idx]; + + for (chan_idx = 0; chan_idx < map->sched.num_channels; + chan_idx++) { + unsigned int i; + + /* Check if we already have this channel */ + for (i = 0; i < n_channels; i++) { + if (os_memcmp(&map->sched.channels[chan_idx].chan_entry, + channels[i]->chan_entry, + sizeof(channels[i]->chan_entry)) == 0) + break; + } + + if (i < n_channels) { + chan_remap[map_idx][chan_idx] = i; + continue; + } + + channels[n_channels] = &map->sched.channels[chan_idx]; + chan_remap[map_idx][chan_idx] = n_channels++; + } + } + + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_NAN_SET_PEER_SCHED); + if (!msg) { + wpa_printf(MSG_INFO, + "nl80211: Failed to alloc NAN peer schedule command"); + return -ENOMEM; + } + + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) || + nla_put_u16(msg, NL80211_ATTR_NAN_COMMITTED_DW, cdw) || + nla_put_u16(msg, NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME, + max_chan_switch_time) || + (sched->n_maps && + nla_put_u8(msg, NL80211_ATTR_NAN_SEQ_ID, sequence_id))) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Failed to put peer schedule attributes"); + goto fail; + } + + if (ulw && wpabuf_len(ulw)) { + if (nla_put(msg, NL80211_ATTR_NAN_ULW, + wpabuf_len(ulw), wpabuf_head(ulw))) { + wpa_printf(MSG_INFO, + "nl80211: NAN: Failed to put ULW attribute"); + goto fail; + } + } + + /* Set the channels */ + wpa_printf(MSG_DEBUG, + "nl80211: NAN: Setting %u channels for peer schedule", + n_channels); + + for (chan_idx = 0; chan_idx < n_channels; chan_idx++) { + ret = wpa_driver_nl80211_nan_set_channel(drv, + channels[chan_idx], + msg); + if (ret) + goto fail; + } + + /* Set the maps */ + maps = nla_nest_start(msg, NL80211_ATTR_NAN_PEER_MAPS); + if (!maps) + goto fail; + + for (map_idx = 0; map_idx < sched->n_maps; map_idx++) { + struct nlattr *map = nla_nest_start(msg, map_idx + 1); + u8 slots[32]; + + if (!map) + goto fail; + + if (nla_put_u8(msg, NL80211_NAN_PEER_MAP_ATTR_MAP_ID, + sched->maps[map_idx].map_id)) + goto fail; + + os_memset(slots, NL80211_NAN_SCHED_NOT_AVAIL_SLOT, + sizeof(slots)); + + for (chan_idx = 0; + chan_idx < sched->maps[map_idx].sched.num_channels; + chan_idx++) { + struct nan_schedule_channel *chan = + &sched->maps[map_idx].sched.channels[chan_idx]; + + ret = wpa_driver_nl80211_nan_set_slots( + drv, chan->time_bitmap, slots, + chan_remap[map_idx][chan_idx]); + if (ret) + goto fail; + } + + if (nla_put(msg, NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS, + sizeof(slots), slots)) + goto fail; + + nla_nest_end(msg, map); + } + + nla_nest_end(msg, maps); + + ret = send_and_recv_cmd(bss->drv, msg); + if (ret) + wpa_printf(MSG_INFO, + "nl80211: Failed to send NAN peer schedule command: %d", + ret); + + return ret; + +fail: + nlmsg_free(msg); + return -ENOBUFS; +} + +#endif /* CONFIG_NAN */ + + +#ifdef CONFIG_PR + +/** + * nl80211_pd_start - Create interface for Proximity Detection + */ +static int nl80211_pd_start(void *priv, const u8 *addr, u8 *pd_addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct i802_bss *pd_bss; + struct wdev_info info; + int ret; + + if (drv->pd_bss) { + wpa_printf(MSG_DEBUG, "nl80211: PD wdev already active"); + if (pd_addr) + os_memcpy(pd_addr, drv->pd_bss->addr, ETH_ALEN); + return 0; + } + + pd_bss = os_zalloc(sizeof(*pd_bss)); + if (!pd_bss) + return -1; + + pd_bss->drv = drv; + pd_bss->ctx = bss->ctx; /* parent wpa_s */ + pd_bss->flink = &pd_bss->links[0]; + os_strlcpy(pd_bss->ifname, "pd-wdev", sizeof(pd_bss->ifname)); + + os_memset(&info, 0, sizeof(info)); + ret = nl80211_create_iface(drv, "pd-wdev", NL80211_IFTYPE_PD, addr, + 0, nl80211_wdev_handler, &info, 0); + if (ret || !info.wdev_id_set) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to create PD wdev (ret=%d)", ret); + os_free(pd_bss); + return -1; + } + + pd_bss->wdev_id = info.wdev_id; + pd_bss->wdev_id_set = 1; + if (!is_zero_ether_addr(info.macaddr)) + os_memcpy(pd_bss->addr, info.macaddr, ETH_ALEN); + else if (addr) + os_memcpy(pd_bss->addr, addr, ETH_ALEN); + os_memcpy(pd_bss->flink->addr, pd_bss->addr, ETH_ALEN); + + /* Activate the PD device */ + ret = nl80211_set_pr_dev(pd_bss, 1); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to start PD device (ret=%d)", ret); + goto failed; + } + + if (nl80211_init_bss(pd_bss)) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to init PD BSS"); + goto failed_stop; + } + + if (nl80211_mgmt_subscribe_non_ap(pd_bss)) + wpa_printf(MSG_DEBUG, + "nl80211: Failed to register frame processing for PD interface - ignore for now"); + + drv->pd_bss = pd_bss; + + if (pd_addr) + os_memcpy(pd_addr, pd_bss->addr, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "nl80211: PD wdev created wdev_id=0x%llx addr=" MACSTR, + (unsigned long long) pd_bss->wdev_id, + MAC2STR(pd_bss->addr)); + return 0; + +failed_stop: + nl80211_set_pr_dev(pd_bss, 0); +failed: + nl80211_del_non_netdev(pd_bss); + os_free(pd_bss); + return -1; +} + + +/** + * nl80211_pd_stop - Stop interface for Proximity Detection + */ +static void nl80211_pd_stop(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!drv->pd_bss) + return; + + wpa_printf(MSG_DEBUG, + "nl80211: Stopping PD wdev wdev_id=0x%llx addr=" MACSTR, + (unsigned long long) drv->pd_bss->wdev_id, + MAC2STR(drv->pd_bss->addr)); + + nl80211_set_pr_dev(drv->pd_bss, 0); + nl80211_destroy_bss(drv->pd_bss); + nl80211_del_non_netdev(drv->pd_bss); + os_free(drv->pd_bss); + drv->pd_bss = NULL; +} + + +static u8 get_pr_preamble(u8 ranging_type, u8 format_bw) +{ + /* Determine preamble based on ranging type and format_bw */ + if (ranging_type & PR_EDCA_BASED_RANGING) { + /* EDCA format_bw maps to different preambles */ + switch (format_bw) { + case EDCA_FORMAT_AND_BW_HT40: + return NL80211_PREAMBLE_HT; + case EDCA_FORMAT_AND_BW_VHT20: + case EDCA_FORMAT_AND_BW_VHT40: + case EDCA_FORMAT_AND_BW_VHT80: + case EDCA_FORMAT_AND_BW_VHT80P80: + case EDCA_FORMAT_AND_BW_VHT160_DUAL_LO: + case EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO: + return NL80211_PREAMBLE_VHT; + default: + return NL80211_PREAMBLE_VHT; + } + } else if (ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING)) { + /* NTB format_bw all use HE preamble */ + return NL80211_PREAMBLE_HE; + } + + /* Default fallback */ + return NL80211_PREAMBLE_VHT; +} + + +/** + * nl80211_start_peer_measurement - Send NL80211_CMD_PEER_MEASUREMENT_START + */ +static int nl80211_start_peer_measurement(void *priv, const u8 *peer_addr, + int freq, u8 channel, int bw, + struct pr_pasn_ranging_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *pmsr_attr, *peers_attr, *peer_attr, *chan_attr, + *req_attr, *ftm_attr; + struct nlattr *data_attr; + int ret = -1, center_freq1 = 0, center_freq2 = 0; + struct nl80211_ack_ext_arg ack_arg; + enum nl80211_chan_width width; + u32 preamble; + u64 cookie; + + if (!peer_addr || !params) { + wpa_printf(MSG_INFO, + "nl80211: Invalid parameters for peer measurement"); + return -1; + } + + /* Create dedicated ranging socket if not already created */ + if (drv->global->nl_pr) { + wpa_printf(MSG_DEBUG, + "nl80211: PR ranging socket already in use"); + return -1; + } + + drv->global->nl_pr = nl_create_handle(drv->global->nl_cb, "pr"); + if (!drv->global->nl_pr) { + wpa_printf(MSG_INFO, + "nl80211: Failed to create PR ranging socket"); + return -1; + } + + /* Route via PD wdev if src_addr matches */ + if (drv->pd_bss && !is_zero_ether_addr(params->src_addr) && + ether_addr_equal(params->src_addr, drv->pd_bss->addr)) { + wpa_printf(MSG_DEBUG, + "nl80211: Peer measurement routed via PD wdev addr=" + MACSTR, MAC2STR(drv->pd_bss->addr)); + bss = drv->pd_bss; + } + + wpa_printf(MSG_DEBUG, "nl80211: Start peer measurement for " MACSTR + " freq=%d ch=%u bw=%d", + MAC2STR(peer_addr), freq, channel, bw); + + /* Use center frequencies from params (calculated in + * wpas_pr_trigger_ranging()) */ + center_freq1 = params->center_freq1; + center_freq2 = params->center_freq2; + + /* channel_width is in MHz */ + switch (params->channel_width) { + case 20: + width = NL80211_CHAN_WIDTH_20; + break; + case 40: + width = NL80211_CHAN_WIDTH_40; + break; + case 80: + width = NL80211_CHAN_WIDTH_80; + break; + case 160: + width = NL80211_CHAN_WIDTH_160; + break; + case 320: + width = NL80211_CHAN_WIDTH_320; + break; + default: + wpa_printf(MSG_INFO, + "nl80211: Unsupported channel width %u MHz", + params->channel_width); + return -1; + } + + wpa_printf(MSG_DEBUG, + "nl80211: Using center_freq1=%u, center_freq2=%u, width=%d", + center_freq1, center_freq2, width); + + /* + * PD interfaces are non-netdev (ifindex=0); use nl80211_cmd_msg() + * which emits NL80211_ATTR_WDEV instead of NL80211_ATTR_IFINDEX. + */ + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_PEER_MEASUREMENT_START); + if (!msg) + return -1; + + /* Add timeout if specified */ + if (params->ranging_timeout > 0) { + if (nla_put_u32(msg, NL80211_ATTR_TIMEOUT, + params->ranging_timeout)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: Added timeout %u ms", + params->ranging_timeout); + } + + /* Add peer measurements attribute */ + pmsr_attr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS); + if (!pmsr_attr) + goto fail; + + /* Add peers array */ + peers_attr = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS); + if (!peers_attr) + goto fail; + + /* Add single peer */ + peer_attr = nla_nest_start(msg, 0); + if (!peer_attr) + goto fail; + + /* Peer MAC address */ + if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, peer_addr)) + goto fail; + + /* Add proximity detection request type */ + if (nla_put_u32(msg, NL80211_PMSR_PEER_ATTR_REQ_TYPE, + NL80211_PMSR_FTM_REQ_TYPE_PD)) + goto fail; + + /* Channel information */ + chan_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_CHAN); + if (!chan_attr) + goto fail; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || + nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, width) || + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq1)) + goto fail; + + if (center_freq2 && + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, center_freq2)) + goto fail; + + nla_nest_end(msg, chan_attr); + + + /* Request attributes */ + req_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_REQ); + if (!req_attr) + goto fail; + + /* Add data attribute as required by nl80211 specification */ + data_attr = nla_nest_start(msg, NL80211_PMSR_REQ_ATTR_DATA); + if (!data_attr) + goto fail; + + /* FTM request */ + ftm_attr = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM); + if (!ftm_attr) + goto fail; + + preamble = get_pr_preamble(params->ranging_type, params->format_bw); + if (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, preamble)) + goto fail; + + /* Map ranging parameters to NL80211 attributes based on protocol type + */ + if (params->ranging_type & PR_EDCA_BASED_RANGING) { + wpa_printf(MSG_DEBUG, + "nl80211: Adding EDCA ranging parameters"); + + /* ASAP and BURST_PERIOD are always set for EDCA */ + if (nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_ASAP) || + nla_put_u16(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, + params->burst_period)) + goto fail; + + /* Other EDCA parameters are only set for ISTA */ + if (params->ranging_role == PR_ISTA_SUPPORT && + (nla_put_u8(msg, + NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, + params->num_bursts_exp) || + nla_put_u8(msg, + NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, + params->ftms_per_burst) || + nla_put_u8(msg, + NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, + params->ftmr_retries) || + nla_put_u8(msg, + NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, + params->burst_duration))) + goto fail; + } + + if (params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING)) { + wpa_printf(MSG_DEBUG, "nl80211: Adding NTB ranging parameters"); + + /* Set non-trigger based flag */ + if (nla_put_flag(msg, + NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED)) + goto fail; + + if (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME, + params->nominal_time)) + goto fail; + + /* ISTA-specific NTB parameters */ + if (params->ranging_role == PR_ISTA_SUPPORT && + (nla_put_u32(msg, + NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + params->min_time_between_measurements) || + nla_put_u32(msg, + NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + params->max_time_between_measurements) || + nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION, + params->availability_window) || + nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, + params->ftms_per_burst))) + goto fail; + } + + /* Location requests */ + if (params->request_lci && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI)) + goto fail; + + if (params->request_civicloc && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC)) + goto fail; + + /* RSTA mode if responder role */ + if (params->ranging_role == PR_RSTA_SUPPORT && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_RSTA)) + goto fail; + + /* + * LMR feedback: set for RSTA role in NTB ranging so that + * the RSTA reports its measurement results back to the ISTA. + * Only valid when NON_TRIGGER_BASED is set. + */ + if (params->ranging_role == PR_RSTA_SUPPORT && + (params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING)) && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK)) + goto fail; + + /* + * PD ingress/egress thresholds: only valid when + * NL80211_PMSR_PEER_ATTR_REQ_TYPE is NL80211_PMSR_FTM_REQ_TYPE_PD + * (always the case here). Thresholds are in millimeters. + */ + if (params->ingress_threshold && + nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_INGRESS, + params->ingress_threshold)) + goto fail; + + if (params->egress_threshold && + nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_EGRESS, + params->egress_threshold)) + goto fail; + + /* + * PD suppress results: suppress ranging results for PD requests. + */ + if (params->pr_suppress_results && + nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS)) + goto fail; + + nla_nest_end(msg, ftm_attr); + nla_nest_end(msg, data_attr); + nla_nest_end(msg, req_attr); + nla_nest_end(msg, peer_attr); + nla_nest_end(msg, peers_attr); + nla_nest_end(msg, pmsr_attr); + + cookie = 0; + os_memset(&ack_arg, 0, sizeof(struct nl80211_ack_ext_arg)); + ack_arg.ext_data = &cookie; + ret = send_and_recv(drv, drv->global->nl_pr, msg, NULL, NULL, + ack_handler_cookie, &ack_arg, NULL); + if (ret < 0) { + wpa_printf(MSG_INFO, + "nl80211: Peer measurement start failed: ret=%d (%s)", + ret, strerror(-ret)); + nl_destroy_handles(&drv->global->nl_pr); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Peer measurement started successfully addr=" + MACSTR " cookie=%llu", MAC2STR(peer_addr), + (unsigned long long) cookie); + params->cookie = cookie; + /* + * Register the PR socket with eloop so that + * NL80211_CMD_PEER_MEASUREMENT_RESULT and + * NL80211_CMD_PEER_MEASUREMENT_COMPLETE events are delivered + * to the existing nl80211 event handler. + */ + nl80211_register_eloop_read(&drv->global->nl_pr, + wpa_driver_nl80211_event_receive, + drv->global->nl_cb, 1); + } + + return ret; + +fail: + wpa_printf(MSG_INFO, + "nl80211: Failed to build peer measurement message"); + nlmsg_free(msg); + nl_destroy_handles(&drv->global->nl_pr); + return -1; +} + + +static void nl80211_stop_peer_measurement(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!drv->global->nl_pr) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Stopping PR ranging socket"); + nl80211_destroy_eloop_handle(&drv->global->nl_pr, 1); +} + +#endif /* CONFIG_PR */ + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -14124,7 +16808,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_default_scan_ies = nl80211_set_default_scan_ies, .set_tdls_mode = nl80211_set_tdls_mode, #ifdef CONFIG_MBO - .get_bss_transition_status = nl80211_get_bss_transition_status, .ignore_assoc_disallow = nl80211_ignore_assoc_disallow, #endif /* CONFIG_MBO */ .set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow, @@ -14133,8 +16816,19 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .send_pasn_resp = nl80211_send_pasn_resp, .set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx, #endif /* CONFIG_PASN */ +#ifdef CONFIG_NAN_USD + .nan_flush = nl80211_nan_flush, + .nan_publish = nl80211_nan_publish, + .nan_cancel_publish = nl80211_nan_cancel_publish, + .nan_update_publish = nl80211_nan_update_publish, + .nan_subscribe = nl80211_nan_subscribe, + .nan_cancel_subscribe = nl80211_nan_cancel_subscribe, +#endif /* CONFIG_NAN_USD */ #endif /* CONFIG_DRIVER_NL80211_QCA */ .do_acs = nl80211_do_acs, +#ifdef CONFIG_MBO + .get_bss_transition_status = nl80211_get_bss_transition_status, +#endif /* CONFIG_MBO */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, .get_ext_capab = nl80211_get_ext_capab, .get_mld_capab = nl80211_get_mld_capab, @@ -14150,9 +16844,25 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .link_remove = driver_nl80211_link_remove, .is_drv_shared = nl80211_is_drv_shared, .link_sta_remove = wpa_driver_nl80211_link_sta_remove, + .can_share_drv = wpa_driver_nl80211_can_share_drv, + .setup_link_reconfig = nl80211_send_link_reconfig_request, #endif /* CONFIG_IEEE80211BE */ #ifdef CONFIG_TESTING_OPTIONS .register_frame = testing_nl80211_register_frame, .radio_disable = testing_nl80211_radio_disable, #endif /* CONFIG_TESTING_OPTIONS */ + .get_multi_hw_info = wpa_driver_get_multi_hw_info, +#ifdef CONFIG_NAN + .nan_start = wpa_driver_nl80211_nan_start, + .nan_stop = wpa_driver_nl80211_nan_stop, + .nan_change_config = wpa_driver_nl80211_nan_change_config, + .nan_config_schedule = wpa_driver_nl80211_nan_config_schedule, + .nan_config_peer_schedule = wpa_driver_nl80211_nan_config_peer_schedule, +#endif /* CONFIG_NAN */ +#ifdef CONFIG_PR + .pd_start = nl80211_pd_start, + .pd_stop = nl80211_pd_stop, + .start_peer_measurement = nl80211_start_peer_measurement, + .stop_peer_measurement = nl80211_stop_peer_measurement, +#endif /* CONFIG_PR */ }; diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h index 618746e67722..580de5a3a95e 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.h +++ b/contrib/wpa/src/drivers/driver_nl80211.h @@ -22,6 +22,8 @@ nla_nest_start(msg, NLA_F_NESTED | (attrtype)) #endif +struct nl_msg; + struct nl80211_global { void *ctx; struct dl_list interfaces; @@ -37,6 +39,25 @@ struct nl80211_global { int ioctl_sock; /* socket for ioctl() use */ struct nl_sock *nl_event; + + /* Handling of sync replies */ + bool sync_reply_handling; + u32 reply_seq; + int (*reply_handler)(struct nl_msg *, void *); + void *reply_data; + + /* pending events that happened while waiting for a sync reply */ + struct dl_list pending_events; + /* set while nl80211_deliver_pending_events() is running */ + bool delivering_pending_events; +#ifdef CONFIG_NAN + /* Dedicated socket for NAN interface creation and events */ + struct nl_sock *nl_nan; +#endif /* CONFIG_NAN */ +#ifdef CONFIG_PR + /* Dedicated socket for PR peer measurement commands and events */ + struct nl_sock *nl_pr; +#endif /* CONFIG_PR */ }; struct nl80211_wiphy_data { @@ -65,12 +86,18 @@ struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; + /* The links which are physically present */ u16 valid_links; + /* The links which are in UP state */ + u16 active_links; struct i802_link links[MAX_NUM_MLD_LINKS]; - struct i802_link *flink; + struct i802_link *flink, *scan_link; int ifindex; + int ignore_if_down_event; int br_ifindex; + int if_removed; + int if_disabled; u64 wdev_id; char ifname[IFNAMSIZ + 1]; char brname[IFNAMSIZ]; @@ -86,6 +113,7 @@ struct i802_bss { u8 prev_addr[ETH_ALEN]; int if_dynamic; + int operstate; void *ctx; struct nl_sock *nl_preq, *nl_mgmt, *nl_connect; @@ -94,6 +122,8 @@ struct i802_bss { struct nl80211_wiphy_data *wiphy_data; struct dl_list wiphy_list; u8 rand_addr[ETH_ALEN]; + + unsigned int start_iface_up:1; }; struct drv_nl80211_if_info { @@ -111,9 +141,6 @@ struct wpa_driver_nl80211_data { u8 perm_addr[ETH_ALEN]; void *ctx; int ifindex; - int if_removed; - int if_disabled; - int ignore_if_down_event; struct rfkill_data *rfkill; struct wpa_driver_capa capa; u8 *extended_capa, *extended_capa_mask; @@ -126,12 +153,10 @@ struct wpa_driver_nl80211_data { u16 mld_capa_and_ops; } iface_capa[NL80211_IFTYPE_MAX]; unsigned int num_iface_capa; + unsigned int unique_drv_id; int has_capability; int has_driver_key_mgmt; - - int operstate; - int scan_complete_events; enum scan_states { NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, @@ -151,10 +176,6 @@ struct wpa_driver_nl80211_data { enum nl80211_iftype ap_scan_as_station; unsigned int assoc_freq; - int monitor_sock; - int monitor_ifidx; - int monitor_refcount; - unsigned int disabled_11b_rates:1; unsigned int pending_remain_on_chan:1; unsigned int in_interface_list:1; @@ -163,10 +184,8 @@ struct wpa_driver_nl80211_data { unsigned int data_tx_status:1; unsigned int scan_for_auth:1; unsigned int retry_auth:1; - unsigned int use_monitor:1; unsigned int hostapd:1; unsigned int start_mode_sta:1; - unsigned int start_iface_up:1; unsigned int test_use_roc_tx:1; unsigned int ignore_deauth_event:1; unsigned int vendor_cmd_test_avail:1; @@ -200,6 +219,10 @@ struct wpa_driver_nl80211_data { unsigned int secure_ranging_ctx_vendor_cmd_avail:1; unsigned int puncturing:1; unsigned int qca_ap_allowed_freqs:1; + unsigned int connect_ext_vendor_cmd_avail:1; + unsigned int support_ap_scan:1; + + u8 extra_bss_membership_selectors[8]; u32 ignore_next_local_disconnect; u32 ignore_next_local_deauth; @@ -264,10 +287,16 @@ struct wpa_driver_nl80211_data { size_t pending_t2lm_data_len; u8 *pending_link_reconfig_data; size_t pending_link_reconfig_data_len; + unsigned int qca_vendor_ext_auth:1; #endif /* CONFIG_DRIVER_NL80211_QCA */ -}; -struct nl_msg; +#ifdef CONFIG_NAN + unsigned int nan_started:1; +#endif /* CONFIG_NAN */ +#ifdef CONFIG_PR + struct i802_bss *pd_bss; /* PD wdev; not in the BSS list */ +#endif /* CONFIG_PR */ +}; struct nl80211_err_info { int link_id; @@ -280,19 +309,38 @@ struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, uint8_t cmd); struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd); -int send_and_recv(struct nl80211_global *global, - struct nl_sock *nl_handle, struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data, - int (*ack_handler_custom)(struct nl_msg *, void *), - void *ack_data, - struct nl80211_err_info *err_info); +int nl80211_reply_hook(struct nl80211_global *global, struct nl_msg *msg, + int (*delayed_handler)(struct nl_msg *, void *), + void *delayed_data); + +int send_and_recv_glb(struct nl80211_global *global, + struct wpa_driver_nl80211_data *drv, /* may be NULL */ + struct nl_sock *nl_handle, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data, + int (*ack_handler_custom)(struct nl_msg *, void *), + void *ack_data, + struct nl80211_err_info *err_info); + +static inline int +send_and_recv(struct wpa_driver_nl80211_data *drv, + struct nl_sock *nl_handle, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data, + int (*ack_handler_custom)(struct nl_msg *, void *), + void *ack_data, + struct nl80211_err_info *err_info) +{ + return send_and_recv_glb(drv->global, drv, nl_handle, msg, + valid_handler, valid_data, + ack_handler_custom, ack_data, err_info); +} static inline int send_and_recv_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg) { - return send_and_recv(drv->global, drv->global->nl, msg, + return send_and_recv(drv, drv->global->nl, msg, NULL, NULL, NULL, NULL, NULL); } @@ -302,7 +350,7 @@ send_and_recv_resp(struct wpa_driver_nl80211_data *drv, int (*valid_handler)(struct nl_msg *, void *), void *valid_data) { - return send_and_recv(drv->global, drv->global->nl, msg, + return send_and_recv(drv, drv->global->nl, msg, valid_handler, valid_data, NULL, NULL, NULL); } @@ -313,6 +361,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, void *arg, int use_existing); void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx); unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv); +const u8 * nl80211_get_assoc_bssid(struct i802_bss *bss); int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid); enum chan_width convert2width(int width); void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv); @@ -321,10 +370,9 @@ struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, int is_ap_interface(enum nl80211_iftype nlmode); int is_sta_interface(enum nl80211_iftype nlmode); int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv); -int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, - const u8 *bssid, +int nl80211_get_link_signal(struct i802_bss *bss, const u8 *bssid, struct hostap_sta_driver_data *data); -int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, +int nl80211_get_link_noise(struct i802_bss *bss, struct wpa_signal_info *sig_change); int nl80211_get_wiphy_index(struct i802_bss *bss); int wpa_driver_nl80211_set_mode(struct i802_bss *bss, @@ -334,8 +382,6 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, int local_state_change, struct i802_bss *bss); -int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); -void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, const void *data, size_t len, int encrypt, int noack); @@ -343,7 +389,7 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv); struct hostapd_hw_modes * nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, - u8 *dfs_domain); + u8 *dfs_domain, char *alpha2, size_t alpha2_len); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); @@ -352,6 +398,11 @@ const char * nl80211_iftype_str(enum nl80211_iftype mode); void nl80211_restore_ap_mode(struct i802_bss *bss); struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id); +u8 nl80211_get_link_id_from_link(struct i802_bss *bss, struct i802_link *link); +int nl80211_remove_link(struct i802_bss *bss, int link_id); +void nl80211_update_active_links(struct i802_bss *bss, int link_id); +int nl80211_has_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); static inline bool nl80211_link_valid(u16 links, s8 link_id) { @@ -409,5 +460,10 @@ int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie); int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); +struct hostapd_multi_hw_info * +nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws); +u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv); + +int get_sta_mlo_interface_info(struct i802_bss *bss); #endif /* DRIVER_NL80211_H */ diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c index dc16bd4458da..245f643e2c72 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_capa.c +++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c @@ -14,6 +14,7 @@ #include "utils/common.h" #include "common/ieee802_11_common.h" #include "common/wpa_common.h" +#include "common/proximity_ranging.h" #include "common/qca-vendor.h" #include "common/qca-vendor-attr.h" #include "common/brcm_vendor.h" @@ -36,7 +37,7 @@ static int protocol_feature_handler(struct nl_msg *msg, void *arg) } -static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) +u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) { u32 feat = 0; struct nl_msg *msg; @@ -62,6 +63,7 @@ struct wiphy_info_data { struct wpa_driver_capa *capa; unsigned int num_multichan_concurrent; + unsigned int num_multichan_concurrent_nan; unsigned int error:1; unsigned int device_ap_sme:1; @@ -82,6 +84,11 @@ struct wiphy_info_data { unsigned int update_ft_ies_supported:1; unsigned int has_key_mgmt:1; unsigned int has_key_mgmt_iftype:1; + unsigned int support_ap_scan:1; + + unsigned int nan_supported:1; + unsigned int nan_ndp_supported:1; + unsigned int nan_phy_capabilities_valid:1; }; @@ -132,6 +139,9 @@ static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, case NL80211_IFTYPE_P2P_CLIENT: info->p2p_client_supported = 1; break; + case NL80211_IFTYPE_NAN: + info->capa->flags2 |= WPA_DRIVER_FLAGS2_SUPPORT_NAN; + break; } } } @@ -157,6 +167,8 @@ static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, }; + bool combination_has_nan = false, combination_has_nan_ndp = false; + unsigned int num_channels; err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, nl_combi, iface_combination_policy); @@ -184,20 +196,29 @@ static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, combination_has_p2p = 1; if (ift == NL80211_IFTYPE_STATION) combination_has_mgd = 1; + if (ift == NL80211_IFTYPE_NAN) + combination_has_nan = true; + if (ift == NL80211_IFTYPE_NAN_DATA) + combination_has_nan_ndp = true; } - if (combination_has_p2p && combination_has_mgd) - break; } + num_channels = nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); if (combination_has_p2p && combination_has_mgd) { - unsigned int num_channels = - nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); info->p2p_concurrent = 1; if (info->num_multichan_concurrent < num_channels) info->num_multichan_concurrent = num_channels; } + if (combination_has_nan) { + info->nan_supported = 1; + info->nan_ndp_supported = combination_has_nan_ndp; + + if (info->num_multichan_concurrent_nan < num_channels) + info->num_multichan_concurrent_nan = num_channels; + } + return 0; } @@ -303,9 +324,6 @@ static unsigned int get_akm_suites_info(struct nlattr *tb) case RSN_AUTH_KEY_MGMT_CCKM: key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM; break; - case RSN_AUTH_KEY_MGMT_OSEN: - key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN; - break; case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B; break; @@ -585,6 +603,9 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ROC_ADDR_FILTER)) + capa->flags2 |= WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER; if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI)) capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI; @@ -720,6 +741,24 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP)) capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT)) + capa->flags2 |= WPA_DRIVER_FLAGS2_SPP_AMSDU; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION)) + capa->flags2 |= WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_EPPKE) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION)) { + capa->flags2 |= WPA_DRIVER_FLAGS2_EPPKE; + } + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_IEEE8021X_AUTH)) + capa->flags2 |= WPA_DRIVER_FLAGS2_802_1X_AUTH; } @@ -746,8 +785,10 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; - if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) + if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) { capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; + capa->flags2 |= WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE; + } if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) { wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch"); @@ -786,6 +827,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE) capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; + + if (flags & NL80211_FEATURE_AP_SCAN) + info->support_ap_scan = 1; } @@ -937,6 +981,352 @@ static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr) } +#ifdef CONFIG_NAN + +static void wiphy_info_nan_capa_handler(struct wpa_driver_capa *capa, + struct wiphy_info_data *info, + struct nlattr *attr) +{ + static struct nla_policy + nan_capa_policy[NL80211_NAN_CAPABILITIES_MAX + 1] = { + [NL80211_NAN_CAPA_CONFIGURABLE_SYNC] = { .type = NLA_FLAG }, + [NL80211_NAN_CAPA_USERSPACE_DE] = { .type = NLA_FLAG }, + }; + struct nlattr *tb_nan_capa[NL80211_NAN_CAPABILITIES_MAX + 1]; + static struct nla_policy + nan_capa_phy_policy[NL80211_NAN_PHY_CAP_ATTR_MAX + 1] = { + [NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] = { .type = NLA_U16 }, + [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] = { + .type = NLA_U8, + }, + [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY] = { + .type = NLA_U8, + }, + [NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] = { + .type = NLA_BINARY, .minlen = 16, .maxlen = 16, + }, + [NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA] = { .type = NLA_U32 }, + [NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] = { + .type = NLA_BINARY, .minlen = 8, .maxlen = 8, + }, + [NL80211_NAN_PHY_CAP_ATTR_HE_MAC] = { + .type = NLA_BINARY, .minlen = 6, .maxlen = 6 + }, + [NL80211_NAN_PHY_CAP_ATTR_HE_PHY] = { + .type = NLA_BINARY, .minlen = 11, .maxlen = 11 + }, + [NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] = { + .type = NLA_BINARY, .minlen = 12, .maxlen = 12, + }, + [NL80211_NAN_PHY_CAP_ATTR_HE_PPE] = { + .type = NLA_BINARY, .maxlen = 255, + }, + }; + struct nlattr *tb_phy[NL80211_NAN_PHY_CAP_ATTR_MAX + 1]; + + if (!attr) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Parsing NAN capabilities"); + + if (nla_parse_nested(tb_nan_capa, NL80211_NAN_CAPABILITIES_MAX, attr, + nan_capa_policy)) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse NAN capabilities"); + return; + } + + if (tb_nan_capa[NL80211_NAN_CAPA_CONFIGURABLE_SYNC]) { + wpa_printf(MSG_DEBUG, "nl80211: NAN sync offload supported"); + capa->nan_capa.drv_flags |= + WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG; + } + + if (tb_nan_capa[NL80211_NAN_CAPA_USERSPACE_DE]) { + wpa_printf(MSG_DEBUG, + "nl80211: NAN user space DE is supported"); + capa->nan_capa.drv_flags |= + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE; + } + + if (tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME]) + capa->nan_capa.max_channel_switch_time = + nla_get_u16(tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME]); + + if (tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS]) + capa->nan_capa.num_antennas = + nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS]); + + if (tb_nan_capa[NL80211_NAN_CAPA_OP_MODE]) + capa->nan_capa.op_modes = + nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_OP_MODE]); + + if (tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES]) + capa->nan_capa.dev_capa = + nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES]); + + if (!tb_nan_capa[NL80211_NAN_CAPA_PHY]) + return; + + if (nla_parse_nested(tb_phy, NL80211_NAN_PHY_CAP_ATTR_MAX, + tb_nan_capa[NL80211_NAN_CAPA_PHY], + nan_capa_phy_policy)) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse NAN PHY capabilities"); + return; + } + + /* HT Capabilities */ + if (!tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY]) + return; + + capa->nan_capa.ht_capab = nla_get_u16( + tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA]); + capa->nan_capa.ht_ampdu_params = nla_get_u8( + tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR]) & 0x3; + capa->nan_capa.ht_ampdu_params |= (nla_get_u8( + tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY]) & 0x7) << 2; + os_memcpy(capa->nan_capa.ht_mcs_set, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET])); + + info->nan_phy_capabilities_valid = 1; + + /* VHT Capabilities */ + if (tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] && + tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]) { + capa->nan_capa.vht_capab = nla_get_u32( + tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]); + os_memcpy(capa->nan_capa.vht_mcs_set, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET])); + capa->nan_capa.vht_valid = true; + } + + /* HE Capabilities */ + if (!tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] || + !tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]) + goto out; + + os_memcpy(capa->nan_capa.he_capab.mac_cap, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MAC])); + os_memcpy(capa->nan_capa.he_capab.phy_cap, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PHY])); + + os_memcpy(capa->nan_capa.he_capab.mcs, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET])); + os_memcpy(capa->nan_capa.he_capab.ppet, + nla_data(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]), + nla_len(tb_phy[NL80211_NAN_PHY_CAP_ATTR_HE_PPE])); + + capa->nan_capa.he_valid = true; +out: + wpa_printf(MSG_DEBUG, + "nl80211: NAN PHY capabilities vht_valid=%u, he_valid=%u", + capa->nan_capa.vht_valid, capa->nan_capa.he_valid); +} + +#endif /* CONFIG_NAN */ + + +#ifdef CONFIG_PR + +static u32 nl80211_to_pd_bw_bitmap(u32 nl_bitmap) +{ + u32 bandwidths = 0; + + if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_20)) + bandwidths |= BIT(WPA_PR_CHAN_WIDTH_20); + if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_40)) + bandwidths |= BIT(WPA_PR_CHAN_WIDTH_40); + if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_80)) + bandwidths |= BIT(WPA_PR_CHAN_WIDTH_80); + if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_80P80)) + bandwidths |= BIT(WPA_PR_CHAN_WIDTH_80P80); + if (nl_bitmap & BIT(NL80211_CHAN_WIDTH_160)) + bandwidths |= BIT(WPA_PR_CHAN_WIDTH_160); + return bandwidths; +} + + +static u32 nl80211_to_pd_preamble_bitmap(u32 nl_bitmap) +{ + u32 preambles = 0; + + if (nl_bitmap & BIT(NL80211_PREAMBLE_HT)) + preambles |= BIT(WPA_PR_PREAMBLE_HT); + if (nl_bitmap & BIT(NL80211_PREAMBLE_VHT)) + preambles |= BIT(WPA_PR_PREAMBLE_VHT); + if (nl_bitmap & BIT(NL80211_PREAMBLE_HE)) + preambles |= BIT(WPA_PR_PREAMBLE_HE); + return preambles; +} + + +static void pmsr_type_ftm_handler(struct wpa_driver_nl80211_data *drv, + struct nlattr *capa) +{ + u32 max_rx_sts, max_tx_sts; + struct nlattr *tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1]; + struct nlattr *ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1]; + struct nlattr *type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1]; + struct nlattr *rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1]; + + if (nla_parse_nested(tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, capa, NULL)) + return; + + /* Parse ISTA capabilities */ + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS] && + nla_parse_nested(ista_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS], + NULL) == 0) { + drv->capa.ista.support_ntb = + !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB]; + drv->capa.ista.support_tb = + !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB]; + drv->capa.ista.support_edca = + !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA]; + if (ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]) + drv->capa.ista.max_peers = + nla_get_u32(ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]); + } + + /* Parse ranging type capabilities */ + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS] && + !nla_parse_nested(type_caps, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS], + NULL)) { + drv->capa.ranging_type.infra_support = + !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT]; + drv->capa.ranging_type.pd_support = + !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT]; + } + + drv->capa.concurrent_ista_rsta = + !!tb[NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT]; + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP]) + drv->capa.max_tx_ltf_repetations = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP]); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP]) + drv->capa.max_rx_ltf_repetations = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]) { + max_rx_sts = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]); + drv->capa.max_rx_sts_le_80 = max_rx_sts; + drv->capa.max_rx_sts_gt_80 = max_rx_sts; + } + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]) { + max_tx_sts = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]); + drv->capa.max_tx_sts_le_80 = max_tx_sts; + drv->capa.max_tx_sts_gt_80 = max_tx_sts; + } + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX]) + drv->capa.max_tx_ltf_total = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX]); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX]) + drv->capa.max_rx_ltf_total = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX]); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST]) + drv->capa.max_ftms_per_burst = + nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST]); + + /* Parse RSTA capabilities */ + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT] && + tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS] && + !nla_parse_nested(rsta_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX, + tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS], + NULL)) { + drv->capa.rsta.support_ntb = + !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB]; + drv->capa.rsta.support_tb = + !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB]; + drv->capa.rsta.support_edca = + !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA]; + if (rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]) + drv->capa.rsta.max_peers = + nla_get_u32(rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]); + } + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]) + drv->capa.max_tx_antenna = + nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]) + drv->capa.max_rx_antenna = + nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]); + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]) + drv->capa.edca_min_ranging_interval = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]) + drv->capa.ntb_min_ranging_interval = + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]); + + /* Parse additional ranging capabilities */ + drv->capa.support_6ghz = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_6GHZ_SUPPORT]; + + drv->capa.asap_support = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_ASAP]; + + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]) + drv->capa.pd_preambles = nl80211_to_pd_preamble_bitmap( + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES])); + if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]) + drv->capa.pd_bandwidths = nl80211_to_pd_bw_bitmap( + nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS])); +} + + +static void wiphy_info_pmsr_type_capa(struct wpa_driver_nl80211_data *drv, + struct nlattr *attr) +{ + struct nlattr *pos; + int rem; + + nla_for_each_nested(pos, attr, rem) { + if (nla_type(pos) == NL80211_PMSR_TYPE_FTM) + pmsr_type_ftm_handler(drv, pos); + } +} + + +static void wiphy_info_pmsr_capa(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + struct nlattr *pmsr_capa[NL80211_PMSR_ATTR_MAX + 1]; + static struct nla_policy + pmsr_policy[NL80211_PMSR_ATTR_MAX + 1] = { + [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_U32 }, + [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_NESTED }, + }; + + if (nla_parse_nested(pmsr_capa, NL80211_PMSR_ATTR_MAX, + tb[NL80211_ATTR_PEER_MEASUREMENTS], + pmsr_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse PMSR capabilities"); + return; + } + + if (pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS]) + drv->capa.pmsr_max_peers = + nla_get_u32(pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS]); + if (pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA]) + wiphy_info_pmsr_type_capa(drv, + pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA]); +} + +#endif /* CONFIG_PR */ + + static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -959,6 +1349,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN]) + capa->max_probe_req_ie_len = + nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) capa->max_sched_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); @@ -1059,6 +1453,11 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]); +#ifdef CONFIG_PR + if (tb[NL80211_ATTR_PEER_MEASUREMENTS]) + wiphy_info_pmsr_capa(drv, tb); +#endif /* CONFIG_PR */ + if (tb[NL80211_ATTR_VENDOR_DATA]) { struct nlattr *nl; int rem; @@ -1120,6 +1519,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT: drv->secure_ranging_ctx_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT: + drv->connect_ext_vendor_cmd_avail = 1; + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ } #ifdef CONFIG_DRIVER_NL80211_BRCM @@ -1181,6 +1583,23 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_MLO_SUPPORT]) capa->flags2 |= WPA_DRIVER_FLAGS2_MLO; +#ifdef CONFIG_NAN + if (tb[NL80211_ATTR_BANDS]) { + u32 bands; + + bands = nla_get_u32(tb[NL80211_ATTR_BANDS]); + wpa_printf(MSG_DEBUG, "nl80211: NAN supported bands 0x%x", + bands); + if ((bands & BIT(NL80211_BAND_2GHZ)) && + (bands & BIT(NL80211_BAND_5GHZ))) + capa->nan_capa.drv_flags |= + WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND; + } + + wiphy_info_nan_capa_handler(capa, info, + tb[NL80211_ATTR_NAN_CAPABILITIES]); +#endif /* CONFIG_NAN */ + return NL_SKIP; } @@ -1196,6 +1615,10 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, info->capa = &drv->capa; info->drv = drv; + /* Default to large buffer of extra IE(s) to maintain previous behavior + * if the driver does not support reporting an accurate limit. */ + info->capa->max_probe_req_ie_len = 1500; + feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) flags = NLM_F_DUMP; @@ -1262,6 +1685,49 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, if (!drv->capa.max_num_akms) drv->capa.max_num_akms = NL80211_MAX_NR_AKM_SUITES; +#ifdef CONFIG_NAN + if (info->nan_supported) { + if (info->nan_ndp_supported) { + if (info->nan_phy_capabilities_valid) { + wpa_printf(MSG_DEBUG, "nl80211: NAN supported"); + drv->capa.nan_capa.drv_flags |= + WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: NAN NDP supported but NAN PHY capabilities not valid"); + } + } + + /* TODO: Currently support only a single radio */ + drv->capa.nan_capa.num_radios = 1; + drv->capa.nan_capa.sched_chans = + info->num_multichan_concurrent_nan; + + /* + * nl80211 supports only a single configuration that uses 16 TU + * slots and a period of 512 TUs. + */ + drv->capa.nan_capa.slot_duration = 16; + drv->capa.nan_capa.schedule_period = 512; + + /* + * Support NAN beacon protection only if at least one of the NAN + * management group ciphers is supported and the driver supports + * beacon protection. + */ + if ((info->capa->enc & (WPA_DRIVER_CAPA_ENC_BIP | + WPA_DRIVER_CAPA_ENC_BIP_GMAC_256)) && + (drv->capa.flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION)) + drv->capa.nan_capa.drv_flags |= + WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT; + + if (drv->capa.nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT) + wpa_printf(MSG_DEBUG, + "nl80211: NAN: beacon protection supported"); + } +#endif /* CONFIG_NAN */ + return 0; } @@ -1443,6 +1909,28 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) drv->qca_ap_allowed_freqs = 1; if (check_feature(QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER, &info)) drv->capa.flags2 |= WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA, &info)) { + wpa_printf(MSG_DEBUG, + "The driver supports RSN overriding in STA mode"); + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA; + } + if (check_feature(QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD, &info)) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_V2, &info)) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_V2; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_PCC_MODE, &info)) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_P2P_ASSISTED_DFS, + &info)) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_ASSISTED_DFS; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_PMKSA_CACHING_PRIVACY, + &info)) + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PMKSA_PRIVACY; + os_free(info.flags); } @@ -1470,6 +1958,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B | WPA_DRIVER_CAPA_KEY_MGMT_OWE | WPA_DRIVER_CAPA_KEY_MGMT_DPP; @@ -1485,6 +1974,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 | + WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY | WPA_DRIVER_CAPA_KEY_MGMT_SAE; else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) drv->capa.key_mgmt |= @@ -1553,22 +2043,23 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) if (info.set_qos_map_supported) drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; drv->have_low_prio_scan = info.have_low_prio_scan; + drv->support_ap_scan = info.support_ap_scan; /* - * If poll command and tx status are supported, mac80211 is new enough - * to have everything we need to not need monitor interfaces. - */ - drv->use_monitor = !info.device_ap_sme && - (!info.poll_command_supported || !info.data_tx_status); - - /* - * If we aren't going to use monitor interfaces, but the - * driver doesn't support data TX status, we won't get TX + * If the driver doesn't support data TX status, we won't get TX * status for EAPOL frames. */ - if (!drv->use_monitor && !info.data_tx_status) + if (!info.data_tx_status) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + /* Enable P2P2 and PCC mode capabilities by default for the drivers + * for which SME runs in wpa_supplicant + */ + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_V2; + drv->capa.flags2 |= WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE; + } + #ifdef CONFIG_DRIVER_NL80211_QCA if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) qca_nl80211_check_dfs_capa(drv); @@ -1602,6 +2093,7 @@ struct phy_info_arg { int last_mode, last_chan_idx; int failed; u8 dfs_domain; + char alpha2[3]; /* Country code from NL80211_ATTR_REG_ALPHA2 */ }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, @@ -1713,6 +2205,8 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80; if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ]) chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_320MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_320; if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { enum nl80211_dfs_state state = @@ -1814,6 +2308,8 @@ static int phy_info_freqs(struct phy_info_arg *phy_info, [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_320MHZ] = { .type = NLA_FLAG }, + }; int new_channels = 0; struct hostapd_channel_data *channel; @@ -1972,12 +2468,10 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode, len); } - if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) { - u16 capa; - - capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]); - he_capab->he_6ghz_capa = le_to_host16(capa); - } + if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA] && + nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) >= 2) + he_capab->he_6ghz_capa = WPA_GET_LE16( + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA])); if (!tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] || !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) @@ -2067,7 +2561,8 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) mode->mode = NUM_HOSTAPD_MODES; mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN | - HOSTAPD_MODE_FLAG_HE_INFO_KNOWN; + HOSTAPD_MODE_FLAG_HE_INFO_KNOWN | + HOSTAPD_MODE_FLAG_EHT_INFO_KNOWN; /* * Unsupported VHT MCS stream is defined as value 3, so the VHT @@ -2361,7 +2856,7 @@ static void nl80211_reg_rule_sec(struct nlattr *tb[], static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, - int end, int max_bw) + int end, int max_bw, u32 flags) { int c; @@ -2376,6 +2871,9 @@ static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, if (max_bw >= 160) chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL; + + if (flags & NL80211_RRF_AUTO_BW) + chan->flag |= HOSTAPD_CHAN_AUTO_BW; } } @@ -2383,7 +2881,7 @@ static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, static void nl80211_reg_rule_vht(struct nlattr *tb[], struct phy_info_arg *results) { - u32 start, end, max_bw; + u32 start, end, max_bw, flags = 0; u16 m; if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || @@ -2395,6 +2893,9 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + if (tb[NL80211_ATTR_REG_RULE_FLAGS]) + flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); + if (max_bw < 80) return; @@ -2406,7 +2907,8 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], if (!results->modes[m].vht_capab) continue; - nl80211_set_vht_mode(&results->modes[m], start, end, max_bw); + nl80211_set_vht_mode(&results->modes[m], start, end, + max_bw, flags); } } @@ -2531,6 +3033,11 @@ static int nl80211_get_reg(struct nl_msg *msg, void *arg) (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); } + /* Store the alpha2 country code for the caller */ + os_strlcpy(results->alpha2, + (const char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), + sizeof(results->alpha2)); + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) { u32 start, end, max_eirp = 0, max_bw = 0, flags = 0; @@ -2674,7 +3181,7 @@ static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv, struct hostapd_hw_modes * nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, - u8 *dfs_domain) + u8 *dfs_domain, char *alpha2, size_t alpha2_len) { u32 feat; struct i802_bss *bss = priv; @@ -2687,11 +3194,14 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, .last_mode = -1, .failed = 0, .dfs_domain = 0, + .alpha2 = "", }; *num_modes = 0; *flags = 0; *dfs_domain = 0; + if (alpha2 && alpha2_len) + alpha2[0] = '\0'; feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) @@ -2719,6 +3229,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, } *dfs_domain = result.dfs_domain; + if (alpha2 && alpha2_len) + os_strlcpy(alpha2, result.alpha2, alpha2_len); modes = wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); @@ -2728,3 +3240,132 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, return NULL; } + + +static int phy_multi_hw_info_parse(struct hostapd_multi_hw_info *hw_info, + struct nlattr *radio_attr) +{ + struct nlattr *tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_MAX + 1]; + int start_freq, end_freq; + + switch (nla_type(radio_attr)) { + case NL80211_WIPHY_RADIO_ATTR_INDEX: + hw_info->hw_idx = nla_get_u32(radio_attr); + return NL_OK; + case NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: + if (nla_parse_nested(tb_freq, NL80211_WIPHY_RADIO_FREQ_ATTR_MAX, + radio_attr, NULL) || + !tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START] || + !tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END]) + return NL_STOP; + + start_freq = nla_get_u32( + tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START]); + end_freq = nla_get_u32( + tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END]); + + /* Convert to MHz and store */ + hw_info->start_freq = start_freq / 1000; + hw_info->end_freq = end_freq / 1000; + return NL_OK; + default: + return NL_OK; + } +} + + +struct phy_multi_hw_info_arg { + bool failed; + unsigned int *num_multi_hws; + struct hostapd_multi_hw_info *multi_hws; +}; + + +static int phy_multi_hw_info_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_multi_hw_info_arg *multi_hw_info = arg; + struct hostapd_multi_hw_info *multi_hws, hw_info; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct nlattr *nl_hw, *radio_attr; + int rem_hw, rem_radio_prop, res; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_RADIOS]) + return NL_SKIP; + + *multi_hw_info->num_multi_hws = 0; + + nla_for_each_nested(nl_hw, tb_msg[NL80211_ATTR_WIPHY_RADIOS], rem_hw) { + os_memset(&hw_info, 0, sizeof(hw_info)); + + nla_for_each_nested(radio_attr, nl_hw, rem_radio_prop) { + res = phy_multi_hw_info_parse(&hw_info, radio_attr); + if (res != NL_OK) + goto out; + } + + if (hw_info.start_freq == 0 || hw_info.end_freq == 0) + goto out; + + multi_hws = os_realloc_array(multi_hw_info->multi_hws, + *multi_hw_info->num_multi_hws + 1, + sizeof(*multi_hws)); + if (!multi_hws) + goto out; + + multi_hw_info->multi_hws = multi_hws; + os_memcpy(&multi_hws[*(multi_hw_info->num_multi_hws)], + &hw_info, sizeof(struct hostapd_multi_hw_info)); + *(multi_hw_info->num_multi_hws) += 1; + } + + return NL_OK; +out: + multi_hw_info->failed = true; + return NL_STOP; +} + + +struct hostapd_multi_hw_info * +nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws) +{ + u32 feat; + struct wpa_driver_nl80211_data *drv = bss->drv; + int nl_flags = 0; + struct nl_msg *msg; + struct phy_multi_hw_info_arg result = { + .failed = false, + .num_multi_hws = num_multi_hws, + .multi_hws = NULL, + }; + + *num_multi_hws = 0; + + if (!drv->has_capability || !(drv->capa.flags2 & WPA_DRIVER_FLAGS2_MLO)) + return NULL; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return NULL; + } + + if (send_and_recv_resp(drv, msg, phy_multi_hw_info_handler, + &result) == 0) { + if (result.failed) { + os_free(result.multi_hws); + *num_multi_hws = 0; + return NULL; + } + + return result.multi_hws; + } + + return NULL; +} diff --git a/contrib/wpa/src/drivers/driver_nl80211_event.c b/contrib/wpa/src/drivers/driver_nl80211_event.c index aee815e9705a..0b111bf66bda 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_event.c +++ b/contrib/wpa/src/drivers/driver_nl80211_event.c @@ -186,6 +186,19 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_REMOVE_LINK_STA) C2S(NL80211_CMD_SET_HW_TIMESTAMP) C2S(NL80211_CMD_LINKS_REMOVED) + 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_INCUMBENT_SIGNAL_DETECT) + C2S(NL80211_CMD_NAN_SET_LOCAL_SCHED) + C2S(NL80211_CMD_NAN_SCHED_UPDATE_DONE) + C2S(NL80211_CMD_NAN_SET_PEER_SCHED) + C2S(NL80211_CMD_NAN_ULW_UPDATE) + C2S(NL80211_CMD_NAN_CHANNEL_EVAC) + C2S(NL80211_CMD_START_PD) + C2S(NL80211_CMD_STOP_PD) C2S(__NL80211_CMD_AFTER_LAST) } #undef C2S @@ -223,6 +236,8 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + event.auth.frame_body = (const u8 *) &mgmt->u.auth; + event.auth.frame_body_len = len - 24; event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); event.auth.auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); @@ -285,6 +300,10 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, } status = le_to_host16(mgmt->u.assoc_resp.status_code); + + if (TEST_FAIL_TAG("denied-unspec")) + status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + if (status != WLAN_STATUS_SUCCESS) { os_memset(&event, 0, sizeof(event)); event.assoc_reject.bssid = mgmt->bssid; @@ -582,11 +601,13 @@ struct links_info { }; -static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len, +static void nl80211_get_basic_mle_links_info(struct wpabuf *mlbuf, struct links_info *info) { size_t rem_len; const u8 *pos; + const u8 *mle = wpabuf_head(mlbuf); + size_t mle_len = wpabuf_len(mlbuf); if (mle_len < MULTI_LINK_CONTROL_LEN + 1 || mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN]) @@ -599,7 +620,22 @@ static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len, /* Parse Subelements */ while (rem_len > 2) { - size_t ie_len = 2 + pos[1]; + size_t ie_len, subelem_defrag_len; + int num_frag_subelems; + + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &subelem_defrag_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse MLE subelem"); + break; + } + + ie_len = 2 + subelem_defrag_len; + if ((size_t) num_frag_subelems * 2 > rem_len) + break; + rem_len -= num_frag_subelems * 2; if (rem_len < ie_len) break; @@ -609,7 +645,8 @@ static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len, const u8 *sta_profile; u16 sta_ctrl; - if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN) + if (subelem_defrag_len < + BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN) goto next_subelem; sta_profile = &pos[2]; @@ -665,8 +702,7 @@ static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo, return -1; } os_memset(&req_info, 0, sizeof(req_info)); - nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle), - &req_info); + nl80211_get_basic_mle_links_info(mle, &req_info); wpabuf_free(mle); mle = ieee802_11_defrag(resp_elems.basic_mle, resp_elems.basic_mle_len, @@ -677,8 +713,7 @@ static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo, return -1; } os_memset(&resp_info, 0, sizeof(resp_info)); - nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle), - &resp_info); + nl80211_get_basic_mle_links_info(mle, &resp_info); wpabuf_free(mle); if (req_info.non_assoc_links != resp_info.non_assoc_links) { @@ -901,6 +936,47 @@ out: #endif /* CONFIG_DRIVER_NL80211_QCA */ +static void mlme_event_link_removal(struct wpa_driver_nl80211_data *drv, + struct nlattr *mlo_links) +{ + struct nlattr *link; + u16 removed_links = 0; + int rem_links, i; + + if (!mlo_links) + return; + nla_for_each_nested(link, mlo_links, rem_links) { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + int link_id; + + nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link), + NULL); + + if (!tb[NL80211_ATTR_MLO_LINK_ID]) + continue; + + link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); + if (link_id >= MAX_NUM_MLD_LINKS) + continue; + + removed_links |= BIT(link_id); + } + + drv->sta_mlo_info.valid_links &= ~removed_links; + drv->sta_mlo_info.req_links &= ~removed_links; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + if (!(removed_links & BIT(i))) + continue; + + os_memset(&drv->sta_mlo_info.links[i], 0, + sizeof(drv->sta_mlo_info.links[i])); + } + + wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL); +} + + static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, bool qca_roam_auth, struct nlattr *status, @@ -916,7 +992,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *fils_erp_next_seq_num, struct nlattr *fils_pmk, struct nlattr *fils_pmkid, - struct nlattr *mlo_links) + struct nlattr *mlo_links, + struct nlattr *assoc_encrypted) { union wpa_event_data event; const u8 *ssid = NULL; @@ -1091,6 +1168,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (fils_pmkid) event.assoc_info.fils_pmkid = nla_data(fils_pmkid); + if (assoc_encrypted) + event.assoc_info.assoc_encrypted = true; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); /* Avoid a race condition by stopping to ignore any following @@ -1204,9 +1284,6 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, int chan_offset = 0; int ifidx; - wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event", - finished ? "" : " started"); - if (!freq) return; @@ -1218,6 +1295,9 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, return; } + wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event for %s", + finished ? "" : " started", bss->ifname); + if (type) { enum nl80211_channel_type ch_type = nla_get_u32(type); @@ -1249,6 +1329,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, os_memset(&data, 0, sizeof(data)); data.ch_switch.freq = nla_get_u32(freq); + if (is_6ghz_freq(data.ch_switch.freq)) + ht_enabled = 0; data.ch_switch.ht_enabled = ht_enabled; data.ch_switch.ch_offset = chan_offset; if (punct_bitmap) @@ -1260,10 +1342,13 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, if (cf2) data.ch_switch.cf2 = nla_get_u32(cf2); - if (link) + if (link) { data.ch_switch.link_id = nla_get_u8(link); - else + wpa_printf(MSG_DEBUG, "nl80211: Link ID: %d", + data.ch_switch.link_id); + } else { data.ch_switch.link_id = NL80211_DRV_LINK_ID_NA; + } if (finished) { if (data.ch_switch.link_id != NL80211_DRV_LINK_ID_NA) { @@ -1300,6 +1385,14 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, return; } + if (link && is_ap_interface(drv->nlmode) && + !nl80211_link_valid(bss->valid_links, data.ch_switch.link_id)) { + wpa_printf(MSG_WARNING, + "nl80211: Unknown link ID (%d) for channel switch (%s), ignoring", + data.ch_switch.link_id, bss->ifname); + return; + } + drv->assoc_freq = data.ch_switch.freq; wpa_supplicant_event(bss->ctx, finished ? @@ -1380,6 +1473,23 @@ static void mlme_event_mgmt(struct i802_bss *bss, } +static bool find_send_frame_cookie(struct wpa_driver_nl80211_data *drv, + u64 cookie_val, unsigned int *idx) +{ + unsigned int i; + + for (i = 0; i < drv->num_send_frame_cookies; i++) { + if (cookie_val == drv->send_frame_cookies[i]) { + if (idx) + *idx = i; + return true; + } + } + + return false; +} + + static void mlme_event_mgmt_tx_status(struct i802_bss *bss, struct nlattr *cookie, const u8 *frame, size_t len, struct nlattr *ack) @@ -1415,15 +1525,42 @@ static void mlme_event_mgmt_tx_status(struct i802_bss *bss, if (!is_ap_interface(drv->nlmode) && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + bool known = false; + unsigned int i; + if (!cookie) return; + if (cookie_val == drv->send_frame_cookie) { + known = true; + drv->send_frame_cookie = (u64) -1; + } + + if ((drv->nlmode == NL80211_IFTYPE_NAN || + drv->nlmode == NL80211_IFTYPE_NAN_DATA) && + drv->num_send_frame_cookies > 0 && + find_send_frame_cookie(drv, cookie_val, &i)) { + /* + * Check the cookie is stored for previously sent + * frames. If it is found, remove it and in case of NAN + * mark it as known. + */ + if (i + 1 < drv->num_send_frame_cookies) + os_memmove(&drv->send_frame_cookies[i], + &drv->send_frame_cookies[i + 1], + (drv->num_send_frame_cookies - + (i + 1)) * sizeof(u64)); + + drv->num_send_frame_cookies--; + known = true; + } + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status: cookie=0x%llx%s (ack=%d)", (long long unsigned int) cookie_val, - cookie_val == drv->send_frame_cookie ? - " (match)" : " (unknown)", ack != NULL); - if (cookie_val != drv->send_frame_cookie) + known ? " (match)" : " (unknown)", ack != NULL); + + if (!known) return; } else if (!is_ap_interface(drv->nlmode) && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { @@ -1627,6 +1764,69 @@ static void mlme_event_unprot_beacon(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_link_addition(struct i802_bss *bss, const u8 *frame, + size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 curr_valid_links, added_links; + u8 count; + const u8 *resp_ie; + const u8 *end; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!frame) { + wpa_printf(MSG_DEBUG, + "Link Reconfiguration Response frame is NULL - unspecified reason"); + return; + } + end = frame + len; + + os_memset(&event, 0, sizeof(event)); + + mgmt = (const struct ieee80211_mgmt *) frame; + + if (len < 24 + 1 + sizeof(mgmt->u.action.u.link_reconf_resp)) { + wpa_printf(MSG_DEBUG, + "nl80211: Too short Link Reconfig Response frame"); + return; + } + + count = mgmt->u.action.u.link_reconf_resp.count; + event.reconfig_info.count = count; + resp_ie = mgmt->u.action.u.link_reconf_resp.variable; + if (end - resp_ie < 3 * count) { + wpa_printf(MSG_DEBUG, + "nl80211: Truncated Link Reconfig Response frame"); + return; + } + event.reconfig_info.status_list = resp_ie; + resp_ie += 3 * count; + curr_valid_links = drv->sta_mlo_info.valid_links; + + if (get_sta_mlo_interface_info(bss) < 0) { + wpa_printf(MSG_INFO, "nl80211: Failed to get STA MLO info"); + return; + } + + if (!nl80211_get_assoc_bssid(bss)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to get BSSID info for newly added links"); + return; + } + + added_links = ~curr_valid_links & drv->sta_mlo_info.valid_links; + + event.reconfig_info.resp_ie = resp_ie; + event.reconfig_info.resp_ie_len = end - resp_ie; + event.reconfig_info.added_links = added_links; + + drv->sta_mlo_info.req_links = drv->sta_mlo_info.valid_links; + + wpa_supplicant_event(drv->ctx, EVENT_SETUP_LINK_RECONFIG, &event); +} + + static s8 nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq) { @@ -1655,6 +1855,7 @@ static void mlme_event(struct i802_bss *bss, size_t len; int link_id = -1; struct i802_link *mld_link = NULL; + const struct ieee80211_mgmt *mgmt = NULL; if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); @@ -1695,9 +1896,11 @@ static void mlme_event(struct i802_bss *bss, /* PASN Authentication frame can be received with a different source MAC * address. Allow NL80211_CMD_FRAME event with foreign addresses also. + * NAN USD frames (Public Action/Vendor Specific with NAN SDF OUI) may + * be addressed to the NAN Network ID or unicast to a forced address + * (e.g., solicited publish replies, follow-ups), allow those, too. */ if (cmd == NL80211_CMD_FRAME && len >= 24) { - const struct ieee80211_mgmt *mgmt; u16 fc; mgmt = (const struct ieee80211_mgmt *) data; @@ -1707,10 +1910,23 @@ static void mlme_event(struct i802_bss *bss, } if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH && - auth_type == host_to_le16(WLAN_AUTH_PASN)) { + auth_type == WLAN_AUTH_PASN) { wpa_printf(MSG_DEBUG, "nl80211: %s: Allow PASN frame for foreign address", bss->ifname); +#ifdef CONFIG_NAN_USD + } else if (cmd == NL80211_CMD_FRAME && + stype == WLAN_FC_STYPE_ACTION && mgmt && + len >= offsetof(struct ieee80211_mgmt, + u.action.u.vs_public_action.variable) + 1 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE32(mgmt->u.action.u.vs_public_action.oui) == + NAN_SDF_VENDOR_TYPE) { + wpa_printf(MSG_DEBUG, "nl80211: %s: Allow NAN USD frame", + bss->ifname); +#endif /* CONFIG_NAN_USD */ } else if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && !ether_addr_equal(bss->addr, data + 4) && @@ -1965,9 +2181,10 @@ static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv, } -static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, +static void send_scan_event(struct i802_bss *bss, int aborted, struct nlattr *tb[], int external_scan) { + struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data event; struct nlattr *nl; int rem; @@ -1975,6 +2192,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, #define MAX_REPORT_FREQS 110 int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; + struct i802_link *mld_link; + void *ctx = bss->ctx; if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; @@ -2038,13 +2257,30 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, ETH_ALEN); } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); + /* Need to pass to the correct link ctx during AP MLD operation */ + if (is_ap_interface(drv->nlmode)) { + mld_link = bss->scan_link; + if (!mld_link) { + wpa_printf(MSG_DEBUG, + "nl80211: Scan event on unknown link"); + } else if (mld_link->ctx) { + u8 link_id = nl80211_get_link_id_from_link(bss, + mld_link); + + wpa_printf(MSG_DEBUG, + "nl80211: Scan event for link_id %d", + link_id); + ctx = mld_link->ctx; + } + } + + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, &event); } -static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) +static void nl80211_cqm_event(struct i802_bss *bss, struct nlattr *tb[]) { + struct wpa_driver_nl80211_data *drv = bss->drv; static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, @@ -2079,7 +2315,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR " (num_packets %u)", MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets); - wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + wpa_supplicant_event(bss->ctx, EVENT_STATION_LOW_ACK, &ed); return; } @@ -2128,7 +2364,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, * nl80211_get_link_signal() and nl80211_get_link_noise() set default * values in case querying the driver fails. */ - res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data); + res = nl80211_get_link_signal(bss, drv->bssid, &ed.signal_change.data); if (res == 0) { wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %lu", ed.signal_change.data.signal, @@ -2138,7 +2374,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, "nl80211: Querying the driver for signal info failed"); } - res = nl80211_get_link_noise(drv, &ed.signal_change); + res = nl80211_get_link_noise(bss, &ed.signal_change); if (res == 0) { wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", ed.signal_change.current_noise); @@ -2411,10 +2647,31 @@ static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, } -static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static void nl80211_stop_ap(struct i802_bss *bss, struct nlattr **tb) { - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); + struct i802_link *mld_link = NULL; + void *ctx = bss->ctx; + int link_id = -1; + + if (tb[NL80211_ATTR_MLO_LINK_ID]) { + link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); + if (!nl80211_link_valid(bss->valid_links, link_id)) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignoring STOP_AP event for invalid link ID %d (valid: 0x%04x)", + link_id, bss->valid_links); + return; + } + + mld_link = nl80211_get_link(bss, link_id); + wpa_printf(MSG_DEBUG, + "nl80211: STOP_AP event on link %d", link_id); + ctx = mld_link->ctx; + + /* Bring down the active link */ + nl80211_update_active_links(bss, link_id); + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); } @@ -2455,29 +2712,62 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, } -static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static void nl80211_process_radar_event(struct i802_bss *bss, + union wpa_event_data *data, + enum nl80211_radar_event event_type) { + wpa_printf(MSG_DEBUG, + "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d", + data->dfs_event.freq, data->dfs_event.ht_enabled, + data->dfs_event.chan_offset, data->dfs_event.chan_width, + data->dfs_event.cf1, data->dfs_event.cf2, + data->dfs_event.link_id); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, data); + break; + case NL80211_RADAR_CAC_FINISHED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, data); + break; + case NL80211_RADAR_CAC_ABORTED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, data); + break; + case NL80211_RADAR_NOP_FINISHED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, data); + break; + case NL80211_RADAR_PRE_CAC_EXPIRED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_PRE_CAC_EXPIRED, + data); + break; + case NL80211_RADAR_CAC_STARTED: + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, data); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Unknown radar event %d received", + event_type); + break; + } +} + + +static void nl80211_radar_event(struct i802_bss *bss, struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data data; enum nl80211_radar_event event_type; + struct i802_bss *bss_iter; + int i; + bool hit = false; if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) return; os_memset(&data, 0, sizeof(data)); - data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA; data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); - if (tb[NL80211_ATTR_MLO_LINK_ID]) { - data.dfs_event.link_id = - nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); - } else if (data.dfs_event.freq) { - data.dfs_event.link_id = - nl80211_get_link_id_by_freq(drv->first_bss, - data.dfs_event.freq); - } - /* Check HT params */ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { data.dfs_event.ht_enabled = 1; @@ -2509,44 +2799,68 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); wpa_printf(MSG_DEBUG, - "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d", - data.dfs_event.freq, data.dfs_event.ht_enabled, - data.dfs_event.chan_offset, data.dfs_event.chan_width, - data.dfs_event.cf1, data.dfs_event.cf2, - data.dfs_event.link_id); + "nl80211: Checking suitable BSS for the DFS event"); - switch (event_type) { - case NL80211_RADAR_DETECTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); - break; - case NL80211_RADAR_CAC_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); - break; - case NL80211_RADAR_CAC_ABORTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); - break; - case NL80211_RADAR_NOP_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); - break; - case NL80211_RADAR_PRE_CAC_EXPIRED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED, - &data); - break; - case NL80211_RADAR_CAC_STARTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " - "received", event_type); - break; + /* It is possible to have the event without ifidx and wdev_id, e.g., + * with NL80211_RADAR_NOP_FINISHED and NL80211_RADAR_PRE_CAC_EXPIRED. + * Hence need to check on all BSSs. */ + for (bss_iter = drv->first_bss; bss_iter; bss_iter = bss_iter->next) { + /* Find a link match based on the frequency. If + * NL80211_DRV_LINK_ID_NA is returned, either a match was not + * found or the BSS could be operating as a non-MLO. */ + data.dfs_event.link_id = nl80211_get_link_id_by_freq( + bss_iter, data.dfs_event.freq); + /* If a link match is found, exit the loop after the handler is + * called */ + if (data.dfs_event.link_id != NL80211_DRV_LINK_ID_NA) + return nl80211_process_radar_event(bss_iter, &data, + event_type); + if (data.dfs_event.link_id == NL80211_DRV_LINK_ID_NA) { + /* For non-MLO operation, frequency should still match + */ + if (!bss_iter->valid_links && + bss_iter->links[0].freq == data.dfs_event.freq) + return nl80211_process_radar_event( + bss_iter, &data, event_type); + } + + /* For event like NL80211_RADAR_NOP_FINISHED, frequency + * information will not match exactly the link frequency. Hence, + * if the link frequency is 5 GHz, pass the event to it. + */ + for_each_link_default(bss_iter->valid_links, i, 0) { + if (bss_iter->links[i].freq < 5180 || + bss_iter->links[i].freq > 5900) + continue; + + data.dfs_event.link_id = bss_iter->valid_links ? + i : NL80211_DRV_LINK_ID_NA; + + /* Cannot just return after one match since if split + * hardware is participating in MLO, possibly the event + * is for 5 GHz upper band and this iteration has picked + * a 5 GHz low band link, but 5 GHz freq check will be + * true for both. Hence, iterate on all possible links. + * The handler should take care whether the event is + * actually for it. */ + nl80211_process_radar_event(bss_iter, &data, + event_type); + + hit = true; + } } + + if (hit) + return; + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on unknown freq on %s", + bss->ifname); } static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, int wds) { - struct wpa_driver_nl80211_data *drv = bss->drv; union wpa_event_data event; if (!tb[NL80211_ATTR_MAC]) @@ -2556,14 +2870,16 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, event.rx_from_unknown.bssid = bss->addr; event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); event.rx_from_unknown.wds = wds; + event.rx_from_unknown.link_id = tb[NL80211_ATTR_MLO_LINK_ID] ? + nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]) : -1; - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); + wpa_supplicant_event(bss->ctx, EVENT_RX_FROM_UNKNOWN, &event); } #ifdef CONFIG_DRIVER_NL80211_QCA -static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, +static void qca_nl80211_avoid_freq(struct i802_bss *bss, const u8 *data, size_t len) { u32 i, count; @@ -2604,7 +2920,7 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, } event.freq_range.range = range; - wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); + wpa_supplicant_event(bss->ctx, EVENT_AVOID_FREQUENCIES, &event); os_free(range); } @@ -2640,7 +2956,8 @@ static unsigned int chan_to_freq(struct wpa_driver_nl80211_data *drv, int i; modes = nl80211_get_hw_feature_data(drv->first_bss, &num_modes, - &flags, &dfs_domain); + &flags, &dfs_domain, + NULL, 0); if (!modes) { wpa_printf(MSG_DEBUG, "nl80211: Fetching hardware mode failed"); @@ -2675,12 +2992,13 @@ try_2_4_or_5: } -static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, +static void qca_nl80211_acs_select_ch(struct i802_bss *bss, const u8 *data, size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; union wpa_event_data event; u8 chan; + struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: ACS channel selection vendor event received"); @@ -2766,7 +3084,7 @@ static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, /* Ignore ACS channel list check for backwards compatibility */ - wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); + wpa_supplicant_event(bss->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); } @@ -2804,7 +3122,8 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_ASSOC_ENCRYPTED]); #ifdef ANDROID #ifdef ANDROID_LIB_EVENT @@ -2836,8 +3155,9 @@ qca_nl80211_key_mgmt_auth_handler(struct wpa_driver_nl80211_data *drv, } -static void qca_nl80211_dfs_offload_radar_event( - struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length) +static void +qca_nl80211_dfs_offload_radar_event(struct i802_bss *bss, u32 subcmd, u8 *msg, + int length) { union wpa_event_data data; struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -2864,8 +3184,7 @@ static void qca_nl80211_dfs_offload_radar_event( nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); } else if (data.dfs_event.freq) { data.dfs_event.link_id = - nl80211_get_link_id_by_freq(drv->first_bss, - data.dfs_event.freq); + nl80211_get_link_id_by_freq(bss, data.dfs_event.freq); } wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, link=%d", @@ -2909,19 +3228,19 @@ static void qca_nl80211_dfs_offload_radar_event( switch (subcmd) { case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, &data); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, &data); break; default: wpa_printf(MSG_DEBUG, @@ -2932,13 +3251,14 @@ static void qca_nl80211_dfs_offload_radar_event( } -static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, - u8 *data, size_t len) +static void qca_nl80211_scan_trigger_event(struct i802_bss *bss, u8 *data, + size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; union wpa_event_data event; struct scan_info *info; + struct wpa_driver_nl80211_data *drv = bss->drv; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || @@ -2958,13 +3278,12 @@ static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, info->nl_scan_event = 0; drv->scan_state = SCAN_STARTED; - wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event); + wpa_supplicant_event(bss->ctx, EVENT_SCAN_STARTED, &event); } -static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, - int aborted, struct nlattr *tb[], - int external_scan) +static void send_vendor_scan_event(struct i802_bss *bss, int aborted, + struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; @@ -3021,17 +3340,18 @@ static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); + wpa_supplicant_event(bss->ctx, EVENT_SCAN_RESULTS, &event); } -static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, - u8 *data, size_t len) +static void qca_nl80211_scan_done_event(struct i802_bss *bss, u8 *data, + size_t len) { struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; u64 cookie = 0; enum scan_status status; int external_scan; + struct wpa_driver_nl80211_data *drv = bss->drv; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, (struct nlattr *) data, len, NULL) || @@ -3055,12 +3375,12 @@ static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, drv->scan_state = SCAN_ABORTED; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); + drv->first_bss->ctx); drv->vendor_scan_cookie = 0; drv->last_scan_cmd = 0; } - send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb, + send_vendor_scan_event(bss, (status == VENDOR_SCAN_STATUS_ABORTED), tb, external_scan); } @@ -3092,17 +3412,17 @@ static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv, #ifdef CONFIG_PASN -static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv, - u8 *data, size_t len) +static void qca_nl80211_pasn_auth(struct i802_bss *bss, u8 *data, size_t len) { int ret = -EINVAL; struct nlattr *attr; struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; struct nlattr *cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX + 1]; - unsigned int n_peers = 0, idx = 0; + unsigned int n_peers = 0, idx = 0, i; int rem_conf; enum qca_wlan_vendor_pasn_action action; union wpa_event_data event; + char *pw[WPAS_MAX_PASN_PEERS]; if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PASN_MAX, (struct nlattr *) data, len, NULL) || @@ -3111,6 +3431,7 @@ static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv, return; } + os_memset(&pw, 0, sizeof(pw)); os_memset(&event, 0, sizeof(event)); action = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]); switch (action) { @@ -3152,7 +3473,49 @@ static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv, nla_data(nl_peer), ETH_ALEN); if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED]) event.pasn_auth.peer[idx].ltf_keyseed_required = true; + if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM]) { + u32 akmp = nla_get_u32( + cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_AKM]); + + event.pasn_auth.peer[idx].akmp = + rsn_key_mgmt_to_wpa_akm(akmp); + } + if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER]) { + u32 cipher = nla_get_u32( + cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_CIPHER]); + + event.pasn_auth.peer[idx].cipher = + rsn_cipher_suite_to_wpa_cipher(cipher); + } + if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD]) { + const char *password; + size_t password_len; + + password_len = nla_len( + cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD]); + password = nla_data( + cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_PASSWORD]); + pw[idx] = os_zalloc(password_len + 1); + if (!pw[idx]) + goto fail; + os_memcpy(pw[idx], password, password_len); + event.pasn_auth.peer[idx].password = pw[idx]; + } + if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE]) { + event.pasn_auth.peer[idx].comeback_len = + nla_len(cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE]); + event.pasn_auth.peer[idx].comeback = + nla_data(cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_COOKIE]); + } + + wpa_printf(MSG_DEBUG, "nl80211: PASN auth action: peer addr " + MACSTR " AKMP 0x%x cipher 0x%x", + MAC2STR(event.pasn_auth.peer[idx].peer_addr), + event.pasn_auth.peer[idx].akmp, + event.pasn_auth.peer[idx].cipher); idx++; + if (idx == WPAS_MAX_PASN_PEERS) + break; } event.pasn_auth.num_peers = n_peers; @@ -3160,15 +3523,127 @@ static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv, "nl80211: PASN auth action: %u, num_bssids: %d", event.pasn_auth.action, event.pasn_auth.num_peers); - wpa_supplicant_event(drv->ctx, EVENT_PASN_AUTH, &event); + wpa_supplicant_event(bss->ctx, EVENT_PASN_AUTH, &event); +fail: + for (i = 0; i < idx; i++) + str_clear_free(pw[i]); } #endif /* CONFIG_PASN */ + +/* Converts nl80211_auth_type to WLAN_AUTH_* format */ +static int get_auth_algo(enum nl80211_auth_type auth_alg) +{ + switch (auth_alg) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + return WLAN_AUTH_OPEN; + case NL80211_AUTHTYPE_SHARED_KEY: + return WLAN_AUTH_SHARED_KEY; + case NL80211_AUTHTYPE_FT: + return WLAN_AUTH_FT; + case NL80211_AUTHTYPE_SAE: + return WLAN_AUTH_SAE; + case NL80211_AUTHTYPE_FILS_SK: + return WLAN_AUTH_FILS_SK; + case NL80211_AUTHTYPE_FILS_SK_PFS: + return WLAN_AUTH_FILS_SK_PFS; + case NL80211_AUTHTYPE_FILS_PK: + return WLAN_AUTH_FILS_PK; + case NL80211_AUTHTYPE_EPPKE: + return WLAN_AUTH_EPPKE; + case NL80211_AUTHTYPE_IEEE8021X: + return WLAN_AUTH_802_1X; + default: + return -1; + } +} + + +static void qca_nl80211_external_auth(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + union wpa_event_data event; + struct external_auth *ea; + enum qca_wlan_vendor_external_auth_action act; + char mld_addr[50]; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM] || + !tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION] || + !tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID] || + !tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID] || + !tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO]) + return; + + os_memset(&event, 0, sizeof(event)); + ea = &event.external_auth; + act = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ACTION]); + switch (act) { + case QCA_WLAN_VENDOR_EXTERNAL_AUTH_START: + ea->action = EXT_AUTH_START; + break; + case QCA_WLAN_VENDOR_EXTERNAL_AUTH_ABORT: + ea->action = EXT_AUTH_ABORT; + break; + default: + return; + } + + ea->key_mgmt_suite = + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AKM]); + ea->ssid_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID]); + if (ea->ssid_len > SSID_MAX_LEN) + return; + ea->ssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_SSID]); + ea->bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_BSSID]); + ea->auth_alg = get_auth_algo( + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_ALGO])); + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER]) + ea->pairwise_cipher = rsn_cipher_suite_to_wpa_cipher( + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_PAIRWISE_CIPHER])); + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER]) + ea->group_cipher = rsn_cipher_suite_to_wpa_cipher( + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_CIPHER])); + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER]) + ea->group_mgmt_cipher = rsn_cipher_suite_to_wpa_cipher( + nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_GROUP_MGMT_CIPHER])); + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB]) + ea->rsn_capab = + nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSN_CAPAB]); + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA]) { + ea->rsnxe_data = + nla_data(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA]); + ea->rsnxe_data_len = + nla_len(tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_RSNXE_DATA]); + } + + mld_addr[0] = '\0'; + if (tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR]) { + ea->mld_addr = nla_data( + tb[QCA_WLAN_VENDOR_ATTR_EXTERNAL_AUTH_AP_MLD_ADDR]); + os_snprintf(mld_addr, sizeof(mld_addr), ", MLD ADDR=" MACSTR, + MAC2STR(ea->mld_addr)); + } + + wpa_printf(MSG_DEBUG, + "nl80211: %s: %u, AKM=0x%x, SSID=%s, auth algo=%d, pairwise_cipher=%u, group_cipher=%u, group_mgmt_cipher=%u, rsn_capab=0x%x, BSSID=" + MACSTR "%s", + __func__, ea->action, ea->key_mgmt_suite, + wpa_ssid_txt(ea->ssid, ea->ssid_len), + ea->auth_alg, ea->pairwise_cipher, ea->group_cipher, + ea->group_mgmt_cipher, ea->rsn_capab, + MAC2STR(ea->bssid), mld_addr); + drv->qca_vendor_ext_auth = 1; + wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event); +} + #endif /* CONFIG_DRIVER_NL80211_QCA */ -static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, +static void nl80211_vendor_event_qca(struct i802_bss *bss, u32 subcmd, u8 *data, size_t len) { switch (subcmd) { @@ -3177,40 +3652,43 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, break; #ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: - qca_nl80211_avoid_freq(drv, data, len); + qca_nl80211_avoid_freq(bss, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: - qca_nl80211_key_mgmt_auth_handler(drv, data, len); + qca_nl80211_key_mgmt_auth_handler(bss->drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: - qca_nl80211_acs_select_ch(drv, data, len); + qca_nl80211_acs_select_ch(bss, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: - qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); + qca_nl80211_dfs_offload_radar_event(bss, subcmd, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: - qca_nl80211_scan_trigger_event(drv, data, len); + qca_nl80211_scan_trigger_event(bss, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: - qca_nl80211_scan_done_event(drv, data, len); + qca_nl80211_scan_done_event(bss, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: - qca_nl80211_p2p_lo_stop_event(drv, data, len); + qca_nl80211_p2p_lo_stop_event(bss->drv, data, len); break; #ifdef CONFIG_PASN case QCA_NL80211_VENDOR_SUBCMD_PASN: - qca_nl80211_pasn_auth(drv, data, len); + qca_nl80211_pasn_auth(bss, data, len); break; #endif /* CONFIG_PASN */ case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP: - qca_nl80211_tid_to_link_map_event(drv, data, len); + qca_nl80211_tid_to_link_map_event(bss->drv, data, len); break; case QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG: - qca_nl80211_link_reconfig_event(drv, data, len); + qca_nl80211_link_reconfig_event(bss->drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_AUTH: + qca_nl80211_external_auth(bss->drv, data, len); break; #endif /* CONFIG_DRIVER_NL80211_QCA */ default: @@ -3249,7 +3727,7 @@ static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg0_center_ch = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]); - if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) + if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]) event.acs_selected_channels.vht_seg1_center_ch = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]); if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]) @@ -3303,13 +3781,13 @@ static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_DRIVER_NL80211_BRCM */ -static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static void nl80211_vendor_event(struct i802_bss *bss, struct nlattr **tb) { u32 vendor_id, subcmd, wiphy = 0; int wiphy_idx; u8 *data = NULL; size_t len = 0; + struct wpa_driver_nl80211_data *drv = bss->drv; if (!tb[NL80211_ATTR_VENDOR_ID] || !tb[NL80211_ATTR_VENDOR_SUBCMD]) @@ -3351,7 +3829,7 @@ static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, switch (vendor_id) { case OUI_QCA: - nl80211_vendor_event_qca(drv, subcmd, data, len); + nl80211_vendor_event_qca(bss, subcmd, data, len); break; #ifdef CONFIG_DRIVER_NL80211_BRCM case OUI_BRCM: @@ -3790,6 +4268,283 @@ static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv, } +#ifdef CONFIG_PR + +static void +nl80211_parse_peer_ftm_result(struct peer_measurement_ftm_result *ftm, + struct nlattr *ftm_data) +{ + struct nlattr *ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_MAX + 1]; + + if (nla_parse_nested(ftm_tb, NL80211_PMSR_FTM_RESP_ATTR_MAX, + ftm_data, NULL)) + return; + + ftm->has_data = true; + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON]) { + ftm->fail = true; + ftm->fail_reason = nla_get_u32( + ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON]); + wpa_printf(MSG_DEBUG, + "nl80211: Ranging failed with reason %u", + ftm->fail_reason); + return; + } + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX]) + ftm->burst_index = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS]) + ftm->num_ftmr_attempts = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES]) + ftm->num_ftmr_successes = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME]) + ftm->busy_retry_time = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP]) + ftm->num_bursts_exp = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION]) + ftm->burst_duration = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST]) + ftm->ftms_per_burst = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG]) + ftm->rssi_avg = + nla_get_s32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD]) + ftm->rssi_spread = + nla_get_s32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG]) + ftm->rtt_avg = + nla_get_s64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE]) + ftm->rtt_variance = + nla_get_u64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD]) + ftm->rtt_spread = + nla_get_u64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG]) + ftm->dist_avg = + nla_get_s64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE]) + ftm->dist_variance = + nla_get_u64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD]) + ftm->dist_spread = + nla_get_u64(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_LCI]) { + ftm->lci = os_memdup( + nla_data(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_LCI]), + nla_len(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_LCI])); + ftm->lci_len = nla_len(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_LCI]); + } + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]) { + ftm->civicloc = os_memdup( + nla_data(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]), + nla_len(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC])); + ftm->civicloc_len = + nla_len(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]); + } + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD]) + ftm->burst_period = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]) + ftm->tx_ltf_repetition_count = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]) + ftm->rx_ltf_repetition_count = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]) + ftm->max_time_between_measurements = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) + ftm->min_time_between_measurements = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]) + ftm->num_tx_spatial_streams = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]) + ftm->num_rx_spatial_streams = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]) + ftm->nominal_time = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]) + ftm->availability_window = + nla_get_u8(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]) + ftm->band_width = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]); + + if (ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]) + ftm->preamble = + nla_get_u32(ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]); + + ftm->is_delayed_lmr = ftm_tb[NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR]; +} + + +static void nl80211_peer_measurement_complete_event(struct i802_bss *bss, + struct nlattr **tb) +{ + union wpa_event_data data; + u64 cookie = 0; + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_COOKIE]) { + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + wpa_printf(MSG_DEBUG, + "nl80211: PR: Peer measurement complete cookie: %llu", + (unsigned long long) cookie); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: PR: Peer measurement complete (no cookie)"); + } + + data.peer_measurement_complete.cookie = cookie; + wpa_supplicant_event(bss->ctx, EVENT_PEER_MEASUREMENT_COMPLETE, &data); +} + + +static void nl80211_peer_measurement_result_event(struct i802_bss *bss, + struct nlattr **tb) +{ + struct nlattr *pmsr[NL80211_PMSR_ATTR_MAX + 1]; + struct nlattr *peer; + u64 cookie = 0; + int rem; + struct nla_policy pmsr_policy[NL80211_PMSR_ATTR_MAX + 1] = { + [NL80211_PMSR_ATTR_PEERS] = { .type = NLA_NESTED }, + }; + struct nla_policy peer_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { + [NL80211_PMSR_PEER_ATTR_ADDR] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_NESTED }, + }; + struct nla_policy resp_policy[NL80211_PMSR_RESP_ATTR_MAX + 1] = { + [NL80211_PMSR_RESP_ATTR_DATA] = { .type = NLA_NESTED }, + [NL80211_PMSR_RESP_ATTR_STATUS] = { .type = NLA_U32 }, + [NL80211_PMSR_RESP_ATTR_HOST_TIME] = { .type = NLA_U64 }, + [NL80211_PMSR_RESP_ATTR_AP_TSF] = { .type = NLA_U64 }, + [NL80211_PMSR_RESP_ATTR_FINAL] = { .type = NLA_FLAG }, + }; + + if (tb[NL80211_ATTR_COOKIE]) { + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + wpa_printf(MSG_DEBUG, + "nl80211: PR: Peer measurement cookie: %llu", + (unsigned long long) cookie); + } + + if (!tb[NL80211_ATTR_PEER_MEASUREMENTS] || + nla_parse_nested(pmsr, NL80211_PMSR_ATTR_MAX, + tb[NL80211_ATTR_PEER_MEASUREMENTS], pmsr_policy)) + return; + + if (!pmsr[NL80211_PMSR_ATTR_PEERS]) + return; + + nla_for_each_nested(peer, pmsr[NL80211_PMSR_ATTR_PEERS], rem) { + struct nlattr *peer_tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; + struct nlattr *resp_tb[NL80211_PMSR_RESP_ATTR_MAX + 1]; + union wpa_event_data data; + + os_memset(&data, 0, sizeof(data)); + + if (nla_parse_nested(peer_tb, NL80211_PMSR_PEER_ATTR_MAX, + peer, peer_policy)) + continue; + + if (!peer_tb[NL80211_PMSR_PEER_ATTR_ADDR] || + !peer_tb[NL80211_PMSR_PEER_ATTR_RESP]) + continue; + + data.peer_measurement_result.cookie = cookie; + + os_memcpy(data.peer_measurement_result.addr, + nla_data(peer_tb[NL80211_PMSR_PEER_ATTR_ADDR]), + ETH_ALEN); + + if (nla_parse_nested(resp_tb, NL80211_PMSR_RESP_ATTR_MAX, + peer_tb[NL80211_PMSR_PEER_ATTR_RESP], + resp_policy)) + continue; + + if (resp_tb[NL80211_PMSR_RESP_ATTR_STATUS]) + data.peer_measurement_result.status = + nla_get_u32(resp_tb[NL80211_PMSR_RESP_ATTR_STATUS]); + + if (resp_tb[NL80211_PMSR_RESP_ATTR_HOST_TIME]) + data.peer_measurement_result.host_time = + nla_get_u64(resp_tb[NL80211_PMSR_RESP_ATTR_HOST_TIME]); + + if (resp_tb[NL80211_PMSR_RESP_ATTR_AP_TSF]) + data.peer_measurement_result.ap_tsf = + nla_get_u64(resp_tb[NL80211_PMSR_RESP_ATTR_AP_TSF]); + + if (resp_tb[NL80211_PMSR_RESP_ATTR_FINAL]) + data.peer_measurement_result.final = true; + + if (resp_tb[NL80211_PMSR_RESP_ATTR_DATA]) { + struct nlattr *data_type_tb[NL80211_PMSR_TYPE_MAX + 1]; + + if (nla_parse_nested(data_type_tb, + NL80211_PMSR_TYPE_MAX, + resp_tb[NL80211_PMSR_RESP_ATTR_DATA], + NULL)) + continue; + + if (data_type_tb[NL80211_PMSR_TYPE_FTM]) + nl80211_parse_peer_ftm_result( + &data.peer_measurement_result.ftm, + data_type_tb[NL80211_PMSR_TYPE_FTM]); + } + + wpa_supplicant_event(bss->ctx, EVENT_PEER_MEASUREMENT_RESULT, + &data); + /* Free deep-copied LCI/civic location data */ + os_free((void *) data.peer_measurement_result.ftm.lci); + os_free((void *) data.peer_measurement_result.ftm.civicloc); + } +} + +#endif /* CONFIG_PR */ + + #ifdef CONFIG_IEEE80211AX static void nl80211_obss_color_event(struct i802_bss *bss, @@ -3853,6 +4608,221 @@ static void nl80211_obss_color_event(struct i802_bss *bss, #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_NAN + +static void +nl80211_nan_cluster_joined_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: NAN cluster joined event"); + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&data, 0, sizeof(data)); + + data.nan_cluster_join_info.bssid = nla_data(tb[NL80211_ATTR_MAC]); + data.nan_cluster_join_info.new_cluster = + !!nla_get_flag(tb[NL80211_ATTR_NAN_NEW_CLUSTER]); + + wpa_supplicant_event(drv->ctx, EVENT_NAN_CLUSTER_JOIN, &data); +} + + +static void nl80211_nan_next_dw_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: NAN Next DW event"); + + if (!tb[NL80211_ATTR_WIPHY_FREQ]) + return; + + os_memset(&data, 0, sizeof(data)); + + data.nan_next_dw_info.freq = + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + wpa_supplicant_event(drv->ctx, EVENT_NAN_NEXT_DW, &data); +} + +static void +nl80211_nan_sched_update_done_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: NAN schedule update done event"); + + os_memset(&data, 0, sizeof(data)); + data.nan_sched_update_done_info.success = + !!tb[NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS]; + + wpa_supplicant_event(drv->ctx, EVENT_NAN_SCHED_UPDATE_DONE, &data); +} + + +static void nl80211_nan_ulw_update_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: NAN ULW update event"); + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_NAN_ULW]) { + data.nan_ulw_update_info.ulw = + nla_data(tb[NL80211_ATTR_NAN_ULW]); + data.nan_ulw_update_info.ulw_len = + nla_len(tb[NL80211_ATTR_NAN_ULW]); + } + + wpa_supplicant_event(drv->ctx, EVENT_NAN_ULW_UPDATE, &data); +} + + +static void +nl80211_nan_channel_evacuate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + struct nlattr *attrs[NL80211_ATTR_MAX + 1]; + + wpa_printf(MSG_DEBUG, "nl80211: NAN channel evacuation event"); + + os_memset(&data, 0, sizeof(data)); + + if (!tb[NL80211_ATTR_NAN_CHANNEL] || + nla_parse_nested(attrs, NL80211_ATTR_MAX, + tb[NL80211_ATTR_NAN_CHANNEL], NULL) || + !attrs[NL80211_ATTR_WIPHY_FREQ]) + return; + + data.nan_chan_evacuation_info.freq = + nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ]); + + wpa_printf(MSG_DEBUG, "nl80211: NAN channel evacuate: freq=%d", + data.nan_chan_evacuation_info.freq); + + wpa_supplicant_event(drv->ctx, EVENT_NAN_CHAN_EVACUATION, &data); +} + + +static void +nl80211_nan_del_interface_event(struct i802_bss *bss, struct nlattr **tb) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + + wpa_printf(MSG_DEBUG, "nl80211: NAN interface removed event for %s", + bss->ifname); + + os_strlcpy(event.interface_status.ifname, bss->ifname, + sizeof(event.interface_status.ifname)); + + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + + bss->drv->nan_started = false; + + wpa_supplicant_event(bss->ctx, EVENT_INTERFACE_STATUS, &event); +} + +#endif /* CONFIG_NAN */ + + +static void nl80211_incumbt_sig_intf_event(struct i802_bss *bss, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data data; + struct i802_bss *bss_iter; + + if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_CHANNEL_WIDTH] || + !tb[NL80211_ATTR_CENTER_FREQ1] || + !tb[NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP]) + return; + + os_memset(&data, 0, sizeof(data)); + + data.incumbt_sig_intf_event.freq = + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + + data.incumbt_sig_intf_event.chan_width = + convert2width(nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + + data.incumbt_sig_intf_event.cf1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.incumbt_sig_intf_event.cf2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + data.incumbt_sig_intf_event.chan_bw_interference_bitmap = + nla_get_u32(tb[NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP]); + + data.incumbt_sig_intf_event.link_id = + nl80211_get_link_id_by_freq(bss, + data.incumbt_sig_intf_event.freq); + if (data.incumbt_sig_intf_event.link_id == NL80211_DRV_LINK_ID_NA) { + /* For non-MLO operation, freq should still match */ + if (!bss->valid_links && + (int) bss->links[0].freq == + data.incumbt_sig_intf_event.freq) + goto process_incumbt_intf_event; + } else { + /* Valid link ID was found */ + goto process_incumbt_intf_event; + } + + wpa_printf(MSG_DEBUG, + "nl80211: Checking suitable BSS for the incumbent intereference event"); + + /* This event comes without ifidx and wdev_id. Hence need to check on + * all BSSs. */ + for (bss_iter = drv->first_bss; bss_iter; bss_iter = bss_iter->next) { + data.incumbt_sig_intf_event.link_id = + nl80211_get_link_id_by_freq( + bss_iter, data.incumbt_sig_intf_event.freq); + if (data.incumbt_sig_intf_event.link_id == + NL80211_DRV_LINK_ID_NA) { + /* For non-MLO operation, freq should still match */ + if (!bss_iter->valid_links && + (int) bss_iter->links[0].freq == + data.incumbt_sig_intf_event.freq) { + bss = bss_iter; + goto process_incumbt_intf_event; + } + } else { + /* Valid link ID was found */ + bss = bss_iter; + goto process_incumbt_intf_event; + } + } + + wpa_printf(MSG_DEBUG, + "nl80211: Incumbent signal interference event on unknown freq on %s", + bss->ifname); + + return; + +process_incumbt_intf_event: + wpa_printf(MSG_DEBUG, + "nl80211: Incumbent signal interference event on freq %d MHz, width: %d, cf1: %d MHz, cf2: %d MHz, bitmap: 0x%x, link_id: %d", + data.incumbt_sig_intf_event.freq, + data.incumbt_sig_intf_event.chan_width, + data.incumbt_sig_intf_event.cf1, + data.incumbt_sig_intf_event.cf2, + data.incumbt_sig_intf_event.chan_bw_interference_bitmap, + data.incumbt_sig_intf_event.link_id); + + wpa_supplicant_event(bss->ctx, EVENT_INCUMBT_SIG_INTF_DETECTED, &data); +} + + static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { @@ -3894,7 +4864,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, switch (cmd) { case NL80211_CMD_TRIGGER_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: Scan trigger"); drv->scan_state = SCAN_STARTED; if (drv->scan_for_auth) { /* @@ -3906,40 +4876,40 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); break; } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + wpa_supplicant_event(bss->ctx, EVENT_SCAN_STARTED, NULL); break; case NL80211_CMD_START_SCHED_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: Sched scan started"); drv->scan_state = SCHED_SCAN_STARTED; break; case NL80211_CMD_SCHED_SCAN_STOPPED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); drv->scan_state = SCHED_SCAN_STOPPED; - wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + wpa_supplicant_event(bss->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); break; case NL80211_CMD_NEW_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: New scan results available"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } - send_scan_event(drv, 0, tb, external_scan_event); + send_scan_event(bss, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb, 0); + send_scan_event(bss, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: Scan aborted"); if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_ABORTED; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { @@ -3948,12 +4918,12 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, * order not to make wpa_supplicant stop its scanning. */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = 0; } else { external_scan_event = 1; } - send_scan_event(drv, 1, tb, external_scan_event); + send_scan_event(bss, 1, tb, external_scan_event); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: @@ -3987,7 +4957,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM], tb[NL80211_ATTR_PMK], tb[NL80211_ATTR_PMKID], - tb[NL80211_ATTR_MLO_LINKS]); + tb[NL80211_ATTR_MLO_LINKS], + tb[NL80211_ATTR_ASSOC_ENCRYPTED]); break; case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: mlme_event_ch_switch(drv, @@ -4031,10 +5002,15 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, mlme_event_remain_on_channel(drv, 1, tb); break; case NL80211_CMD_NOTIFY_CQM: - nl80211_cqm_event(drv, tb); + nl80211_cqm_event(bss, tb); break; case NL80211_CMD_REG_CHANGE: case NL80211_CMD_WIPHY_REG_CHANGE: + if (cmd == NL80211_CMD_REG_CHANGE && + (drv->capa.flags & + WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY)) + break; + nl80211_reg_change_event(drv, tb); break; case NL80211_CMD_REG_BEACON_HINT: @@ -4065,13 +5041,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, mlme_event_ft_event(drv, tb); break; case NL80211_CMD_RADAR_DETECT: - nl80211_radar_event(drv, tb); + nl80211_radar_event(bss, tb); break; case NL80211_CMD_STOP_AP: - nl80211_stop_ap(drv, tb); + nl80211_stop_ap(bss, tb); break; case NL80211_CMD_VENDOR: - nl80211_vendor_event(drv, tb); + nl80211_vendor_event(bss, tb); break; case NL80211_CMD_NEW_PEER_CANDIDATE: nl80211_new_peer_candidate(drv, tb); @@ -4115,8 +5091,43 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, break; #endif /* CONFIG_IEEE80211AX */ case NL80211_CMD_LINKS_REMOVED: - wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL); + mlme_event_link_removal(drv, tb[NL80211_ATTR_MLO_LINKS]); break; + case NL80211_CMD_ASSOC_MLO_RECONF: + mlme_event_link_addition(bss, nla_data(frame), nla_len(frame)); + break; +#ifdef CONFIG_NAN + case NL80211_CMD_NAN_CLUSTER_JOINED: + nl80211_nan_cluster_joined_event(drv, tb); + break; + case NL80211_CMD_NAN_NEXT_DW_NOTIFICATION: + nl80211_nan_next_dw_event(drv, tb); + break; + case NL80211_CMD_NAN_SCHED_UPDATE_DONE: + nl80211_nan_sched_update_done_event(drv, tb); + break; + case NL80211_CMD_NAN_ULW_UPDATE: + nl80211_nan_ulw_update_event(drv, tb); + break; + case NL80211_CMD_NAN_CHANNEL_EVAC: + nl80211_nan_channel_evacuate_event(drv, tb); + break; + case NL80211_CMD_DEL_INTERFACE: + if (drv->nlmode == NL80211_IFTYPE_NAN) + nl80211_nan_del_interface_event(bss, tb); + break; +#endif /* CONFIG_NAN */ + case NL80211_CMD_INCUMBENT_SIGNAL_DETECT: + nl80211_incumbt_sig_intf_event(bss, tb); + break; +#ifdef CONFIG_PR + case NL80211_CMD_PEER_MEASUREMENT_RESULT: + nl80211_peer_measurement_result_event(bss, tb); + break; + case NL80211_CMD_PEER_MEASUREMENT_COMPLETE: + nl80211_peer_measurement_complete_event(bss, tb); + break; +#endif /* CONFIG_PR */ default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); @@ -4125,14 +5136,31 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, } +static bool nl80211_bss_in_drv(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss) +{ + struct i802_bss *tbss; + + if (!drv) + return false; + + for (tbss = drv->first_bss; tbss; tbss = tbss->next) { + if (tbss == bss) + return true; + } + + return false; +} + + static bool nl80211_drv_in_list(struct nl80211_global *global, - struct wpa_driver_nl80211_data *drv) + unsigned int unique_drv_id) { struct wpa_driver_nl80211_data *tmp; dl_list_for_each(tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { - if (drv == tmp) + if (tmp->unique_drv_id == unique_drv_id) return true; } @@ -4140,18 +5168,61 @@ static bool nl80211_drv_in_list(struct nl80211_global *global, } +static struct i802_bss * +nl80211_get_event_bss(struct wpa_driver_nl80211_data *drv, int ifidx, + int wiphy_idx_rx, int wiphy_idx_set, + u64 wdev_id, int wdev_id_set) +{ + struct i802_bss *bss; + int wiphy_idx = -1; + + if (ifidx != -1) { + for (bss = drv->first_bss; bss; bss = bss->next) { + if (ifidx == bss->ifindex) + return bss; + } + } + +#ifdef CONFIG_PR + if (wdev_id_set && drv->pd_bss && drv->pd_bss->wdev_id_set && + wdev_id == drv->pd_bss->wdev_id) + return drv->pd_bss; +#endif /* CONFIG_PR */ + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (wiphy_idx_set) + wiphy_idx = nl80211_get_wiphy_index(bss); + if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) || + (bss->br_ifindex > 0 && + nl80211_has_ifidx(drv, bss->br_ifindex, ifidx)) || + (!wdev_id_set && wiphy_idx_set && + wiphy_idx == wiphy_idx_rx) || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) + return bss; + } + + return NULL; +} + + int process_global_event(struct nl_msg *msg, void *arg) { struct nl80211_global *global = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wpa_driver_nl80211_data *drv, *tmp; - int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1; + int ifidx = -1, wiphy_idx_rx = -1; struct i802_bss *bss; u64 wdev_id = 0; int wdev_id_set = 0; int wiphy_idx_set = 0; bool processed = false; + int res; + + res = nl80211_reply_hook(global, msg, process_global_event, arg); + if (res != NL_OK) + return res; /* Event marker, all prior events have been processed */ if (gnlh->cmd == NL80211_CMD_GET_PROTOCOL_FEATURES) { @@ -4192,26 +5263,33 @@ int process_global_event(struct nl_msg *msg, void *arg) dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { - for (bss = drv->first_bss; bss; bss = bss->next) { - if (wiphy_idx_set) - wiphy_idx = nl80211_get_wiphy_index(bss); - if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) || - ifidx == bss->ifindex || - (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) || - (wdev_id_set && bss->wdev_id_set && - wdev_id == bss->wdev_id)) { - processed = true; - do_process_drv_event(bss, gnlh->cmd, tb); - if (!wiphy_idx_set) - return NL_SKIP; - /* The driver instance could have been removed, - * e.g., due to NL80211_CMD_RADAR_DETECT event, - * so need to stop the loop if that has - * happened. */ - if (!nl80211_drv_in_list(global, drv)) - break; - } - } + unsigned int unique_drv_id = drv->unique_drv_id; + + bss = nl80211_get_event_bss(drv, ifidx, wiphy_idx_rx, + wiphy_idx_set, wdev_id, + wdev_id_set); + if (!bss) + continue; + + processed = true; + do_process_drv_event(bss, gnlh->cmd, tb); + /* There are two types of events that may need to be delivered + * to multiple interfaces: + * 1. Events for a wiphy, as it can have multiple interfaces. + * 2. "Global" events, like NL80211_CMD_REG_CHANGE. + * + * Terminate early only if the event is directed to a specific + * interface or wdev. */ + if (ifidx != -1 || wdev_id_set) + return NL_SKIP; + /* The driver instance could have been removed, e.g., due to + * NL80211_CMD_RADAR_DETECT event, so need to stop the loop if + * that has happened. */ + if (!nl80211_drv_in_list(global, unique_drv_id)) + break; + + if (!nl80211_bss_in_drv(drv, bss)) + break; } if (processed) @@ -4231,6 +5309,12 @@ int process_bss_event(struct nl_msg *msg, void *arg) struct i802_bss *bss = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; + int res; + + res = nl80211_reply_hook(bss->drv->global, msg, + process_bss_event, arg); + if (res != NL_OK) + return res; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); diff --git a/contrib/wpa/src/drivers/driver_nl80211_monitor.c b/contrib/wpa/src/drivers/driver_nl80211_monitor.c index 7ff55f149a61..ca9bb1ec3cd5 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_monitor.c +++ b/contrib/wpa/src/drivers/driver_nl80211_monitor.c @@ -23,259 +23,14 @@ #include "driver_nl80211.h" -static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = buf; - event.tx_status.data_len = len; - event.tx_status.ack = ok; - wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); -} - - -static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len) -{ - struct ieee80211_hdr *hdr = (void *)buf; - u16 fc; - union wpa_event_data event; - - if (len < sizeof(*hdr)) - return; - - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); - event.rx_from_unknown.addr = hdr->addr2; - event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == - (WLAN_FC_FROMDS | WLAN_FC_TODS); - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); -} - - -static void handle_frame(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len, int datarate, int ssi_signal) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - - if (!drv->use_monitor) - return; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - switch (WLAN_FC_GET_TYPE(fc)) { - case WLAN_FC_TYPE_MGMT: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - event.rx_mgmt.datarate = datarate; - event.rx_mgmt.ssi_signal = ssi_signal; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); - break; - case WLAN_FC_TYPE_CTRL: - /* can only get here with PS-Poll frames */ - wpa_printf(MSG_DEBUG, "CTRL"); - from_unknown_sta(drv, buf, len); - break; - case WLAN_FC_TYPE_DATA: - from_unknown_sta(drv, buf, len); - break; - } -} - - -static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - struct ieee80211_radiotap_iterator iter; - int ret; - int datarate = 0, ssi_signal = 0; - int injected = 0, failed = 0, rxflags = 0; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", - strerror(errno)); - return; - } - - if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) { - wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); - return; - } - - while (1) { - ret = ieee80211_radiotap_iterator_next(&iter); - if (ret == -ENOENT) - break; - if (ret) { - wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", - ret); - return; - } - switch (iter.this_arg_index) { - case IEEE80211_RADIOTAP_FLAGS: - if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) - len -= 4; - break; - case IEEE80211_RADIOTAP_RX_FLAGS: - rxflags = 1; - break; - case IEEE80211_RADIOTAP_TX_FLAGS: - injected = 1; - failed = le_to_host16((*(le16 *) iter.this_arg)) & - IEEE80211_RADIOTAP_F_TX_FAIL; - break; - case IEEE80211_RADIOTAP_DATA_RETRIES: - break; - case IEEE80211_RADIOTAP_CHANNEL: - /* TODO: convert from freq/flags to channel number */ - break; - case IEEE80211_RADIOTAP_RATE: - datarate = *iter.this_arg * 5; - break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: - ssi_signal = (s8) *iter.this_arg; - break; - } - } - - if (rxflags && injected) - return; - - if (!injected) - handle_frame(drv, buf + iter._max_length, - len - iter._max_length, datarate, ssi_signal); - else - handle_tx_callback(drv->ctx, buf + iter._max_length, - len - iter._max_length, !failed); -} - - -/* - * we post-process the filter code later and rewrite - * this to the offset to the last instruction - */ -#define PASS 0xFF -#define FAIL 0xFE +#ifdef CONFIG_TESTING_OPTIONS static struct sock_filter msock_filter_insns[] = { - /* - * do a little-endian load of the radiotap length field - */ - /* load lower byte into A */ - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), - /* put it into X (== index register) */ - BPF_STMT(BPF_MISC| BPF_TAX, 0), - /* load upper byte into A */ - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), - /* left-shift it by 8 */ - BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), - /* or with X */ - BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), - /* put result into X */ - BPF_STMT(BPF_MISC| BPF_TAX, 0), - - /* - * Allow management frames through, this also gives us those - * management frames that we sent ourselves with status - */ - /* load the lower byte of the IEEE 802.11 frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off frame type and version */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), - /* accept frame if it's both 0, fall through otherwise */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), - - /* - * TODO: add a bit to radiotap RX flags that indicates - * that the sending station is not associated, then - * add a filter here that filters on our DA and that flag - * to allow us to deauth frames to that bad station. - * - * For now allow all To DS data frames through. - */ - /* load the IEEE 802.11 frame control field */ - BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), - /* mask off frame type, version and DS status */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), - /* accept frame if version 0, type 2 and To DS, fall through otherwise - */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), - -#if 0 - /* - * drop non-data frames - */ - /* load the lower byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off QoS bit */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), - /* drop non-data frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), -#endif - /* load the upper byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), - /* mask off toDS/fromDS */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), - /* accept WDS frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), - - /* - * add header length to index - */ - /* load the lower byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off QoS bit */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), - /* right shift it by 6 to give 0 or 2 */ - BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), - /* add data frame header length */ - BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), - /* add index, was start of 802.11 header */ - BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), - /* move to index, now start of LL header */ - BPF_STMT(BPF_MISC | BPF_TAX, 0), - - /* - * Accept empty data frames, we use those for - * polling activity. - */ - BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), - - /* - * Accept EAPOL frames - */ - BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), - BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), - - /* keep these last two statements or change the code below */ - /* return 0 == "DROP" */ + /* return 0 == "DROP", we don't want RX */ BPF_STMT(BPF_RET | BPF_K, 0), - /* return ~0 == "keep all" */ - BPF_STMT(BPF_RET | BPF_K, ~0), }; -static struct sock_fprog msock_filter = { +static const struct sock_fprog msock_filter = { .len = ARRAY_SIZE(msock_filter_insns), .filter = msock_filter_insns, }; @@ -283,32 +38,6 @@ static struct sock_fprog msock_filter = { static int add_monitor_filter(int s) { - int idx; - - /* rewrite all PASS/FAIL jump offsets */ - for (idx = 0; idx < msock_filter.len; idx++) { - struct sock_filter *insn = &msock_filter_insns[idx]; - - if (BPF_CLASS(insn->code) == BPF_JMP) { - if (insn->code == (BPF_JMP|BPF_JA)) { - if (insn->k == PASS) - insn->k = msock_filter.len - idx - 2; - else if (insn->k == FAIL) - insn->k = msock_filter.len - idx - 3; - } - - if (insn->jt == PASS) - insn->jt = msock_filter.len - idx - 2; - else if (insn->jt == FAIL) - insn->jt = msock_filter.len - idx - 3; - - if (insn->jf == PASS) - insn->jf = msock_filter.len - idx - 2; - else if (insn->jf == FAIL) - insn->jf = msock_filter.len - idx - 3; - } - } - if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &msock_filter, sizeof(msock_filter))) { wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", @@ -320,40 +49,29 @@ static int add_monitor_filter(int s) } -void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv) +static void +nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv, + int ifidx, int sock) { - if (drv->monitor_refcount > 0) - drv->monitor_refcount--; - wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", - drv->monitor_refcount); - if (drv->monitor_refcount > 0) - return; + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface"); - if (drv->monitor_ifidx >= 0) { - nl80211_remove_iface(drv, drv->monitor_ifidx); - drv->monitor_ifidx = -1; - } - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); - drv->monitor_sock = -1; - } + if (ifidx >= 0) + nl80211_remove_iface(drv, ifidx); + if (sock >= 0) + close(sock); } -int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) +static int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv, + int *ifidx, int *sock) { char buf[IFNAMSIZ]; struct sockaddr_ll ll; int optval; socklen_t optlen; - if (drv->monitor_ifidx >= 0) { - drv->monitor_refcount++; - wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", - drv->monitor_refcount); - return 0; - } + *ifidx = -1; + *sock = -1; if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { /* @@ -379,23 +97,10 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) buf[IFNAMSIZ - 1] = '\0'; - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, - 0, NULL, NULL, 0); + *ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, + 0, NULL, NULL, 0); - if (drv->monitor_ifidx == -EOPNOTSUPP) { - /* - * This is backward compatibility for a few versions of - * the kernel only that didn't advertise the right - * attributes for the only driver that then supported - * AP mode w/o monitor -- ath6kl. - */ - wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " - "monitor interface type - try to run without it"); - drv->device_ap_sme = 1; - } - - if (drv->monitor_ifidx < 0) + if (*ifidx < 0) return -1; if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) @@ -403,21 +108,21 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) memset(&ll, 0, sizeof(ll)); ll.sll_family = AF_PACKET; - ll.sll_ifindex = drv->monitor_ifidx; - drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (drv->monitor_sock < 0) { + ll.sll_ifindex = *ifidx; + *sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (*sock < 0) { wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", strerror(errno)); goto error; } - if (add_monitor_filter(drv->monitor_sock)) { + if (add_monitor_filter(*sock)) { wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " "interface; do filtering in user space"); /* This works, but will cost in performance. */ } - if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + if (bind(*sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", strerror(errno)); goto error; @@ -425,30 +130,22 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) optlen = sizeof(optval); optval = 20; - if (setsockopt - (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", strerror(errno)); goto error; } - if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, - drv, NULL)) { - wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); - goto error; - } - - drv->monitor_refcount++; return 0; error: - nl80211_remove_monitor_interface(drv); + nl80211_remove_monitor_interface(drv, *ifidx, *sock); return -1; } -int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt, int noack) +static int _nl80211_send_monitor(int monitor_sock, + const void *data, size_t len, + int encrypt, int noack) { __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ @@ -484,20 +181,32 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, if (encrypt) rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; - if (drv->monitor_sock < 0) { - wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " - "for %s", __func__); - return -1; - } - if (noack) txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; WPA_PUT_LE16(&rtap_hdr[12], txflags); - res = sendmsg(drv->monitor_sock, &msg, 0); + res = sendmsg(monitor_sock, &msg, 0); if (res < 0) { wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); return -1; } return 0; } + + +int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) +{ + int res, ifidx, sock; + + res = nl80211_create_monitor_interface(drv, &ifidx, &sock); + if (res < 0) + return res; + + res = _nl80211_send_monitor(sock, data, len, encrypt, noack); + nl80211_remove_monitor_interface(drv, ifidx, sock); + return res; +} + +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/drivers/driver_nl80211_scan.c b/contrib/wpa/src/drivers/driver_nl80211_scan.c index b055e684a9f8..b44f0a1d5b5a 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_scan.c +++ b/contrib/wpa/src/drivers/driver_nl80211_scan.c @@ -76,12 +76,13 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) static int nl80211_get_noise_for_scan_results( - struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info) + struct i802_bss *bss, struct nl80211_noise_info *info) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; os_memset(info, 0, sizeof(*info)); - msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); return send_and_recv_resp(drv, msg, get_noise_for_scan_results, info); } @@ -153,6 +154,7 @@ fail: void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct i802_bss *bss; wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); #ifdef CONFIG_DRIVER_NL80211_QCA @@ -160,14 +162,27 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0) return; #endif /* CONFIG_DRIVER_NL80211_QCA */ - if (!drv->vendor_scan_cookie && - nl80211_abort_scan(drv->first_bss) == 0) + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->scan_link) + break; + } + + if (!bss) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to find scan BSS"); return; + } + + if (!drv->vendor_scan_cookie && + nl80211_abort_scan(bss) == 0) { + bss->scan_link = NULL; + return; + } wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) - nl80211_restore_ap_mode(drv->first_bss); + nl80211_restore_ap_mode(bss); wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); @@ -224,6 +239,11 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, if (params->extra_ies) { wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", params->extra_ies, params->extra_ies_len); + if (params->extra_ies_len > drv->capa.max_probe_req_ie_len) + wpa_printf(MSG_INFO, + "nl80211: Extra IEs for scan do not fit driver limit (%zu > %zu) - this is likely to fail", + params->extra_ies_len, + drv->capa.max_probe_req_ie_len); if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, params->extra_ies)) goto fail; @@ -251,6 +271,12 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, if (!drv->hostapd && is_ap_interface(drv->nlmode)) { wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_AP"); scan_flags |= NL80211_SCAN_FLAG_AP; + } else if (drv->support_ap_scan && is_ap_interface(drv->nlmode) && + !nl80211_get_link(bss, params->link_id)->beacon_set) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_AP for scan for link %d that is not beaconing", + params->link_id); + scan_flags |= NL80211_SCAN_FLAG_AP; } if (params->only_new_results) { @@ -347,7 +373,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, int ret = -1, timeout; struct nl_msg *msg = NULL; - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + wpa_dbg(bss->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; if (TEST_FAIL()) @@ -401,11 +427,20 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); - if (drv->hostapd && is_ap_interface(drv->nlmode)) { + if (ret == -EOPNOTSUPP && + drv->hostapd && is_ap_interface(drv->nlmode)) { /* - * mac80211 does not allow scan requests in AP mode, so - * try to do this in station mode. + * mac80211 may not allow scan requests in AP mode, so + * try to do this in station mode. Do so only if there + * are not active links in the AP MLD. */ + wpa_printf(MSG_DEBUG, + "nl80211: AP scan failed on %s. Links=0x%x", + bss->ifname, bss->valid_links); + + if (bss->valid_links) + goto fail; + drv->ap_scan_as_station = drv->nlmode; if (wpa_driver_nl80211_set_mode( bss, NL80211_IFTYPE_STATION) || @@ -435,11 +470,20 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, } wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " "seconds", ret, timeout); - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; + bss->scan_link = bss->flink; + if (is_ap_interface(drv->nlmode) && + nl80211_link_valid(bss->valid_links, params->link_id)) { + wpa_dbg(bss->ctx, MSG_DEBUG, + "nl80211: Scan requested for link %d", + params->link_id); + bss->scan_link = nl80211_get_link(bss, params->link_id); + } + fail: nlmsg_free(msg); return ret; @@ -682,7 +726,7 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) return android_pno_stop(bss); #endif /* ANDROID */ - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_STOP_SCHED_SCAN); ret = send_and_recv_cmd(drv, msg); if (ret) { wpa_printf(MSG_DEBUG, @@ -972,8 +1016,9 @@ static void nl80211_update_scan_res_noise(struct wpa_scan_res *res, static struct wpa_scan_results * -nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv, const u8 *bssid) +nl80211_get_scan_results(struct i802_bss *bss, const u8 *bssid) { + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct wpa_scan_results *res; int ret; @@ -984,7 +1029,7 @@ try_again: res = os_zalloc(sizeof(*res)); if (res == NULL) return NULL; - if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, + if (!(msg = nl80211_cmd_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN))) { wpa_scan_results_free(res); return NULL; @@ -1011,7 +1056,7 @@ try_again: wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " "BSSes)", (unsigned long) res->num); - if (nl80211_get_noise_for_scan_results(drv, &info) == 0) { + if (nl80211_get_noise_for_scan_results(bss, &info) == 0) { size_t i; for (i = 0; i < res->num; ++i) @@ -1040,7 +1085,7 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv, struct wpa_driver_nl80211_data *drv = bss->drv; struct wpa_scan_results *res; - res = nl80211_get_scan_results(drv, bssid); + res = nl80211_get_scan_results(bss, bssid); if (res) wpa_driver_nl80211_check_bss_status(drv, res); return res; @@ -1148,7 +1193,7 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request"); drv->scan_for_auth = 0; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) ) @@ -1294,9 +1339,9 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", ret, (long long unsigned int) cookie); - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, bss->ctx); eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); + drv, bss->ctx); drv->last_scan_cmd = NL80211_CMD_VENDOR; fail: @@ -1323,7 +1368,7 @@ int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len) if (!drv->set_wifi_conf_vendor_cmd_avail) return -1; - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) diff --git a/contrib/wpa/src/drivers/driver_openbsd.c b/contrib/wpa/src/drivers/driver_openbsd.c index bfc231178a2f..dac312a0cdd5 100644 --- a/contrib/wpa/src/drivers/driver_openbsd.c +++ b/contrib/wpa/src/drivers/driver_openbsd.c @@ -77,6 +77,9 @@ wpa_driver_openbsd_set_key(void *priv, struct wpa_driver_set_key_params *params) const u8 *key = params->key; size_t key_len = params->key_len; + if (params->key_flag & KEY_FLAG_NEXT) + return -1; + if (key_len > IEEE80211_PMK_LEN || (key_flag & KEY_FLAG_PMK_MASK) != KEY_FLAG_PMK) { return -1; diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c index d6735b49c4af..d7c6b01a317e 100644 --- a/contrib/wpa/src/drivers/driver_privsep.c +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -219,6 +219,9 @@ static int wpa_driver_privsep_set_key(void *priv, const u8 *key = params->key; size_t key_len = params->key_len; + if (params->key_flag & KEY_FLAG_NEXT) + return -1; + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", __func__, priv, alg, key_idx, set_tx); diff --git a/contrib/wpa/src/drivers/driver_wext.c b/contrib/wpa/src/drivers/driver_wext.c index 2c656fb6fcfa..d884e75923cd 100644 --- a/contrib/wpa/src/drivers/driver_wext.c +++ b/contrib/wpa/src/drivers/driver_wext.c @@ -239,7 +239,8 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) custom); os_memset(&data, 0, sizeof(data)); - /* Host AP driver */ + /* Originally from the Host AP driver, but used by other drivers as well + */ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { data.michael_mic_failure.unicast = os_strstr(custom, " unicast ") != NULL; @@ -460,13 +461,18 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, - NULL); + if (!drv->ignore_next_disconnect) { + wpa_supplicant_event(drv->ctx, + EVENT_DISASSOC, + NULL); + drv->ignore_next_disconnect = false; + } } else { wpa_driver_wext_event_assoc_ies(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + drv->ignore_next_disconnect = false; } break; case IWEVMICHAELMICFAILURE: @@ -865,107 +871,6 @@ static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) } -static int wext_hostap_ifname(struct wpa_driver_wext_data *drv, - const char *ifname) -{ - char buf[200], *res; - int type, ret; - FILE *f; - - if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0) - return -1; - - ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type", - drv->ifname, ifname); - if (os_snprintf_error(sizeof(buf), ret)) - return -1; - - f = fopen(buf, "r"); - if (!f) - return -1; - res = fgets(buf, sizeof(buf), f); - fclose(f); - - type = res ? atoi(res) : -1; - wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type); - - if (type == ARPHRD_IEEE80211) { - wpa_printf(MSG_DEBUG, - "WEXT: Found hostap driver wifi# interface (%s)", - ifname); - wpa_driver_wext_alternative_ifindex(drv, ifname); - return 0; - } - return -1; -} - - -static int wext_add_hostap(struct wpa_driver_wext_data *drv) -{ - char buf[200]; - int n; - struct dirent **names; - int ret = -1; - - snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname); - n = scandir(buf, &names, NULL, alphasort); - if (n < 0) - return -1; - - while (n--) { - if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0) - ret = 0; - free(names[n]); - } - free(names); - - return ret; -} - - -static void wext_check_hostap(struct wpa_driver_wext_data *drv) -{ - char path[200], buf[200], *pos; - ssize_t res; - - /* - * Host AP driver may use both wlan# and wifi# interface in wireless - * events. Since some of the versions included WE-18 support, let's add - * the alternative ifindex also from driver_wext.c for the time being. - * This may be removed at some point once it is believed that old - * versions of the driver are not in use anymore. However, it looks like - * the wifi# interface is still used in the current kernel tree, so it - * may not really be possible to remove this before the Host AP driver - * gets removed from the kernel. - */ - - /* First, try to see if driver information is available from sysfs */ - snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver", - drv->ifname); - res = readlink(path, buf, sizeof(buf) - 1); - if (res > 0) { - buf[res] = '\0'; - pos = strrchr(buf, '/'); - if (pos) - pos++; - else - pos = buf; - wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos); - if (os_strncmp(pos, "hostap", 6) == 0 && - wext_add_hostap(drv) == 0) - return; - } - - /* Second, use the old design with hardcoded ifname */ - if (os_strncmp(drv->ifname, "wlan", 4) == 0) { - char ifname2[IFNAMSIZ + 1]; - os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); - os_memcpy(ifname2, "wifi", 4); - wpa_driver_wext_alternative_ifindex(drv, ifname2); - } -} - - static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { int send_rfkill_event = 0; @@ -1011,8 +916,6 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) drv->ifindex = if_nametoindex(drv->ifname); - wext_check_hostap(drv); - netlink_send_oper_ifla(drv->netlink, drv->ifindex, 1, IF_OPER_DORMANT); @@ -1833,6 +1736,9 @@ static int wpa_driver_wext_set_key(void *priv, const u8 *key = params->key; size_t key_len = params->key_len; + if (params->key_flag & KEY_FLAG_NEXT) + return -1; + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, @@ -1990,6 +1896,9 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) "SSID to disconnect"); } } + + /* wpa_supplicant generates a disconnect event internally already */ + drv->ignore_next_disconnect = true; } @@ -2223,6 +2132,9 @@ int wpa_driver_wext_associate(void *priv, wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) ret = -1; + /* Ignore spurious disconnect event if we are reassociating */ + drv->ignore_next_disconnect = true; + return ret; } diff --git a/contrib/wpa/src/drivers/driver_wext.h b/contrib/wpa/src/drivers/driver_wext.h index 6214cdf42a68..1c6144770f3f 100644 --- a/contrib/wpa/src/drivers/driver_wext.h +++ b/contrib/wpa/src/drivers/driver_wext.h @@ -44,6 +44,8 @@ struct wpa_driver_wext_data { int cfg80211; /* whether driver is using cfg80211 */ u8 max_level; + + bool ignore_next_disconnect; }; int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c index e95df6ddb20a..f088b6f2330b 100644 --- a/contrib/wpa/src/drivers/drivers.c +++ b/contrib/wpa/src/drivers/drivers.c @@ -19,9 +19,6 @@ const struct wpa_driver_ops *const wpa_drivers[] = #ifdef CONFIG_DRIVER_WEXT &wpa_driver_wext_ops, #endif /* CONFIG_DRIVER_WEXT */ -#ifdef CONFIG_DRIVER_HOSTAP - &wpa_driver_hostap_ops, -#endif /* CONFIG_DRIVER_HOSTAP */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ @@ -43,9 +40,6 @@ const struct wpa_driver_ops *const wpa_drivers[] = #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ -#ifdef CONFIG_DRIVER_ATHEROS - &wpa_driver_atheros_ops, -#endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_NONE &wpa_driver_none_ops, #endif /* CONFIG_DRIVER_NONE */ diff --git a/contrib/wpa/src/drivers/drivers.mak b/contrib/wpa/src/drivers/drivers.mak index a03d4a034511..160b5378936f 100644 --- a/contrib/wpa/src/drivers/drivers.mak +++ b/contrib/wpa/src/drivers/drivers.mak @@ -84,26 +84,6 @@ endif ##### PURE AP DRIVERS -ifdef CONFIG_DRIVER_HOSTAP -DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP -DRV_AP_OBJS += ../src/drivers/driver_hostap.o -CONFIG_WIRELESS_EXTENSION=y -NEED_AP_MLME=y -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - -ifdef CONFIG_DRIVER_ATHEROS -DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS -DRV_AP_OBJS += ../src/drivers/driver_atheros.o -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -ifdef ATH_GCM_SUPPORT -CFLAGS += -DATH_GCM_SUPPORT -endif -endif - ##### PURE CLIENT DRIVERS ifdef CONFIG_DRIVER_WEXT @@ -193,6 +173,8 @@ ifdef CONFIG_LIBNL32 else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny + DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-tiny) + DRV_CFLAGS += -D_GNU_SOURCE else ifndef CONFIG_OSX DRV_LIBS += -lnl diff --git a/contrib/wpa/src/drivers/drivers.mk b/contrib/wpa/src/drivers/drivers.mk index 10eab6a92e17..ad0ecd768989 100644 --- a/contrib/wpa/src/drivers/drivers.mk +++ b/contrib/wpa/src/drivers/drivers.mk @@ -78,23 +78,6 @@ endif ##### PURE AP DRIVERS -ifdef CONFIG_DRIVER_HOSTAP -DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP -DRV_AP_OBJS += src/drivers/driver_hostap.c -CONFIG_WIRELESS_EXTENSION=y -NEED_AP_MLME=y -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - -ifdef CONFIG_DRIVER_ATHEROS -DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS -DRV_AP_OBJS += src/drivers/driver_atheros.c -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ##### PURE CLIENT DRIVERS ifdef CONFIG_DRIVER_WEXT @@ -171,6 +154,7 @@ endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny + DRV_CFLAGS += -I/usr/include/libnl-tiny else DRV_LIBS += -lnl DRV_LIBS += -lnl-genl diff --git a/contrib/wpa/src/drivers/nl80211_copy.h b/contrib/wpa/src/drivers/nl80211_copy.h index dced2c49daec..9998f6c0a665 100644 --- a/contrib/wpa/src/drivers/nl80211_copy.h +++ b/contrib/wpa/src/drivers/nl80211_copy.h @@ -11,7 +11,7 @@ * Copyright 2008 Jouni Malinen * Copyright 2008 Colin McCabe * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -72,7 +72,7 @@ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: * - a setup station entry is added, not yet authorized, without any rate - * or capability information, this just exists to avoid race conditions + * or capability information; this just exists to avoid race conditions * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid * to add rate and capability information to the station and at the same * time mark it authorized. @@ -87,7 +87,7 @@ * DOC: Frame transmission/registration support * * Frame transmission and registration support exists to allow userspace - * management entities such as wpa_supplicant react to management frames + * management entities such as wpa_supplicant to react to management frames * that are not being handled by the kernel. This includes, for example, * certain classes of action frames that cannot be handled in the kernel * for various reasons. @@ -113,7 +113,7 @@ * * Frame transmission allows userspace to send for example the required * responses to action frames. It is subject to some sanity checking, - * but many frames can be transmitted. When a frame was transmitted, its + * but many frames can be transmitted. When a frame is transmitted, its * status is indicated to the sending socket. * * For more technical details, see the corresponding command descriptions @@ -123,7 +123,7 @@ /** * DOC: Virtual interface / concurrency capabilities * - * Some devices are able to operate with virtual MACs, they can have + * Some devices are able to operate with virtual MACs; they can have * more than one virtual interface. The capability handling for this * is a bit complex though, as there may be a number of restrictions * on the types of concurrency that are supported. @@ -135,7 +135,7 @@ * Once concurrency is desired, more attributes must be observed: * To start with, since some interface types are purely managed in * software, like the AP-VLAN type in mac80211 for example, there's - * an additional list of these, they can be added at any time and + * an additional list of these; they can be added at any time and * are only restricted by some semantic restrictions (e.g. AP-VLAN * cannot be added without a corresponding AP interface). This list * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. @@ -164,7 +164,7 @@ * Packet coalesce feature helps to reduce number of received interrupts * to host by buffering these packets in firmware/hardware for some * predefined time. Received interrupt will be generated when one of the - * following events occur. + * following events occurs. * a) Expiration of hardware timer whose expiration time is set to maximum * coalescing delay of matching coalesce rule. * b) Coalescing buffer in hardware reaches its limit. @@ -174,7 +174,7 @@ * rule. * a) Maximum coalescing delay * b) List of packet patterns which needs to be matched - * c) Condition for coalescence. pattern 'match' or 'no match' + * c) Condition for coalescence: pattern 'match' or 'no match' * Multiple such rules can be created. */ @@ -213,7 +213,7 @@ /** * DOC: FILS shared key authentication offload * - * FILS shared key authentication offload can be advertized by drivers by + * FILS shared key authentication offload can be advertised by drivers by * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support * FILS shared key authentication offload should be able to construct the * authentication and association frames for FILS shared key authentication and @@ -239,7 +239,7 @@ * The PMKSA can be maintained in userspace persistently so that it can be used * later after reboots or wifi turn off/on also. * - * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertised by a FILS * capable AP supporting PMK caching. It specifies the scope within which the * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based @@ -290,12 +290,12 @@ * If the configuration needs to be applied for specific peer then the MAC * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the * configuration will be applied for all the connected peers in the vif except - * any peers that have peer specific configuration for the TID by default; if - * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values + * any peers that have peer-specific configuration for the TID by default; if + * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer-specific values * will be overwritten. * - * All this configuration is valid only for STA's current connection - * i.e. the configuration will be reset to default when the STA connects back + * All this configuration is valid only for STA's current connection, + * i.e., the configuration will be reset to default when the STA connects back * after disconnection/roaming, and this configuration will be cleared when * the interface goes down. */ @@ -413,8 +413,8 @@ * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, - * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, - * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, + * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + * %NL80211_ATTR_CIPHER_SUITE_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. @@ -438,23 +438,19 @@ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type * of disconnection indication should be sent to the station * (Deauthentication or Disassociation frame and reason code for that - * frame). + * frame). %NL80211_ATTR_MLO_LINK_ID can be used optionally to remove + * stations connected and using at least that link as one of its links. * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to - * destination %NL80211_ATTR_MAC on the interface identified by - * %NL80211_ATTR_IFINDEX. + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to - * destination %NL80211_ATTR_MAC on the interface identified by - * %NL80211_ATTR_IFINDEX. + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by * %NL80211_ATTR_MAC. - * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the - * interface identified by %NL80211_ATTR_IFINDEX. - * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC - * or, if no MAC address given, all mesh paths, on the interface identified - * by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by * %NL80211_ATTR_IFINDEX. * @@ -475,15 +471,15 @@ * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our * current alpha2 if it found a match. It also provides - * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each - * regulatory rule is a nested set of attributes given by - * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and - * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by - * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and - * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain - * to the specified ISO/IEC 3166-1 alpha2 country code. The core will - * store this as a valid request and then query userspace for it. + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. * * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX @@ -521,7 +517,7 @@ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified, * scheduled scan will run in an infinite loop with the specified interval. - * These attributes are mutually exculsive, + * These attributes are mutually exclusive, * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if * NL80211_ATTR_SCHED_SCAN_PLANS is defined. * If for some reason scheduled scan is aborted by the driver, all scan @@ -552,7 +548,7 @@ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface * is brought down while a scheduled scan was running. * - * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * @NL80211_CMD_GET_SURVEY: get survey results, e.g. channel occupation * or noise level * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) @@ -563,40 +559,41 @@ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier - * advertized by a FILS capable AP identifying the scope of PMKSA in an + * advertised by a FILS capable AP identifying the scope of PMKSA in an * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS - * authentication. + * authentication. Additionally in case of SAE offload and OWE offloads + * PMKSA entry can be deleted using %NL80211_ATTR_SSID. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain - * has been changed and provides details of the request information - * that caused the change such as who initiated the regulatory request - * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx - * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if - * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or - * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain - * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is - * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on - * to (%NL80211_ATTR_REG_ALPHA2). + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon - * has been found while world roaming thus enabling active scan or - * any mode of operation that initiates TX (beacons) on a channel - * where we would not have been able to do either before. As an example - * if you are world roaming (regulatory domain set to world or if your - * driver is using a custom world roaming regulatory domain) and while - * doing a passive scan on the 5 GHz band you find an AP there (if not - * on a DFS channel) you will now be able to actively scan for that AP - * or use AP mode on your card on that same channel. Note that this will - * never be used for channels 1-11 on the 2 GHz band as they are always - * enabled world wide. This beacon hint is only sent if your device had - * either disabled active scanning or beaconing on a channel. We send to - * userspace the wiphy on which we removed a restriction from - * (%NL80211_ATTR_WIPHY) and the channel on which this occurred - * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) - * the beacon hint was processed. + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. * * @NL80211_CMD_AUTHENTICATE: authentication request and notification. * This command is used both as a command (request to authenticate) and @@ -607,7 +604,7 @@ * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify * the SSID (mainly for association, but is included in authentication * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ + - * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the + * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequency of the * channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the * authentication type. %NL80211_ATTR_IE is used to define IEs * (VendorSpecificInfo, but also including RSN IE and FT IEs) to be added @@ -732,7 +729,9 @@ * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the * driver to schedule this time due to other concurrent needs for the - * radio). + * radio). An optional attribute %NL80211_ATTR_MAC can be used to filter + * incoming frames during remain-on-channel, such that frames + * addressed to the specified destination MAC are reported. * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) * that will be included with any events pertaining to this request; * the cookie is also used to cancel the request. @@ -816,7 +815,7 @@ * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ * and the attributes determining channel width) the given interface - * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. + * (identified by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -888,7 +887,7 @@ * inform userspace of the new replay counter. * * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace - * of PMKSA caching dandidates. + * of PMKSA caching candidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * In addition, this can be used as an event to request userspace to take @@ -909,8 +908,9 @@ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to * implement sending deauth to stations that send unexpected class 3 - * frames. Also used as the event sent by the kernel when such a frame - * is received. + * frames. For NAN_DATA interfaces, this is used to report frames from + * unknown peers (A2 not assigned to any active NDP). + * Also used as the event sent by the kernel when such a frame is received. * For the event, the %NL80211_ATTR_MAC attribute carries the TA and * other attributes like the interface index are present. * If used as the command it must have an interface index and you can @@ -924,7 +924,7 @@ * * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is - * acknowleged. This is used to allow timing out inactive clients. Uses + * acknowledged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a * direct reply with an %NL80211_ATTR_COOKIE that is later used to match * up the event with the request. The event includes the same data and @@ -1088,8 +1088,9 @@ * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is * omitted or set to 0, it means don't-care and the device will - * decide what to use. After this command NAN functions can be - * added. + * decide what to use. Additional cluster configuration may be + * optionally provided with %NL80211_ATTR_NAN_CONFIG. + * After this command NAN functions can be added. * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by * its %NL80211_ATTR_WDEV interface. * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined @@ -1118,7 +1119,11 @@ * current configuration is not changed. If it is present but * set to zero, the configuration is changed to don't-care * (i.e. the device can decide what to do). - * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported. + * Additional parameters may be provided with + * %NL80211_ATTR_NAN_CONFIG. User space should provide all previously + * configured nested attributes under %NL80211_ATTR_NAN_CONFIG, even if + * only a subset was changed. + * @NL80211_CMD_NAN_MATCH: Notification sent when a match is reported. * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and * %NL80211_ATTR_COOKIE. * @@ -1135,11 +1140,15 @@ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously * configured PMK for the authenticator address identified by * %NL80211_ATTR_MAC. - * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was - * completed successfully. Drivers that support 4 way handshake offload - * should send this event after indicating 802.1X FT assocation with - * %NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT - * should be indicated instead. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates port is authorized and + * open for regular data traffic. For STA/P2P-client, this event is sent + * with AP MAC address and for AP/P2P-GO, the event carries the STA/P2P- + * client MAC address. + * Drivers that support 4 way handshake offload should send this event for + * STA/P2P-client after successful 4-way HS or after 802.1X FT following + * NL80211_CMD_CONNECT or NL80211_CMD_ROAM. Drivers using AP/P2P-GO 4-way + * handshake offload should send this event on successful completion of + * 4-way handshake with the peer (STA/P2P-client). * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request * and RX notification. This command is used both as a request to transmit * a control port frame and as a notification that a control port frame @@ -1197,10 +1206,12 @@ * user space through the connect result as the user space would have * initiated the connection through the connect request. * - * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's - * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE, - * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its - * address(specified in %NL80211_ATTR_MAC). + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notifies that a station's + * HT opmode or VHT opmode changed using any of %NL80211_ATTR_SMPS_MODE, + * %NL80211_ATTR_CHANNEL_WIDTH, %NL80211_ATTR_NSS attributes with its + * address (specified in %NL80211_ATTR_MAC). + * Note that 80+80 and 160 MHz might not be differentiated, i.e. may + * report %NL80211_CHAN_WIDTH_160 instead of %NL80211_CHAN_WIDTH_80P80. * * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute. @@ -1323,6 +1334,97 @@ * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide * information about the removed STA MLD setup links. * + * @NL80211_CMD_SET_TID_TO_LINK_MAPPING: Set the TID to Link Mapping for a + * non-AP MLD station. The %NL80211_ATTR_MLO_TTLM_DLINK and + * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the + * TID to Link mapping for downlink/uplink traffic. + * + * @NL80211_CMD_ASSOC_MLO_RECONF: For a non-AP MLD station, request to + * add/remove links to/from the association. To indicate link + * reconfiguration request results from the driver, this command is also + * used as an event to notify userspace about the added links information. + * For notifying the removed links information, the existing + * %NL80211_CMD_LINKS_REMOVED command is used. This command is also used to + * notify userspace about newly added links for the current connection in + * case of AP-initiated link recommendation requests, received via + * a BTM (BSS Transition Management) request or a link reconfig notify + * frame, where the driver handles the link recommendation offload. + * + * @NL80211_CMD_EPCS_CFG: EPCS configuration for a station. Used by userland to + * control EPCS configuration. Used to notify userland on the current state + * of EPCS. + * + * @NL80211_CMD_NAN_NEXT_DW_NOTIFICATION: This command is used to notify + * user space about the next NAN Discovery Window (DW). User space may use + * it to prepare frames to be sent in the next DW. + * %NL80211_ATTR_WIPHY_FREQ is used to indicate the frequency of the next + * DW. SDF transmission should be requested with %NL80211_CMD_FRAME and + * the device/driver shall take care of the actual transmission timing. + * This notification is only sent to the NAN interface owning socket + * (see %NL80211_ATTR_SOCKET_OWNER flag). + * @NL80211_CMD_NAN_CLUSTER_JOINED: This command is used to notify + * user space that the NAN new cluster has been joined. The cluster ID is + * indicated by %NL80211_ATTR_MAC. + * + * @NL80211_CMD_INCUMBENT_SIGNAL_DETECT: Once any incumbent signal is detected + * on the operating channel in 6 GHz band, userspace is notified with the + * signal interference bitmap using + * %NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP. The current channel + * definition is also sent. + * + * @NL80211_CMD_NAN_SET_LOCAL_SCHED: Set the local NAN schedule. NAN must be + * operational (%NL80211_CMD_START_NAN was executed). Must contain + * %NL80211_ATTR_NAN_TIME_SLOTS and %NL80211_ATTR_NAN_AVAIL_BLOB, but + * %NL80211_ATTR_NAN_CHANNEL is optional (for example in case of a channel + * removal, that channel won't be provided). + * If %NL80211_ATTR_NAN_SCHED_DEFERRED is set, the command is a request + * from the device to perform an announced schedule update. See + * %NL80211_ATTR_NAN_SCHED_DEFERRED for more details. + * If not set, the schedule should be applied immediately. + * @NL80211_CMD_NAN_SCHED_UPDATE_DONE: Event sent to user space to notify that + * a deferred local NAN schedule update (requested with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED and %NL80211_ATTR_NAN_SCHED_DEFERRED) + * has been completed. The presence of %NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS + * indicates that the update was successful. + * @NL80211_CMD_NAN_SET_PEER_SCHED: Set the peer NAN schedule. NAN + * must be operational (%NL80211_CMD_START_NAN was executed). + * Required attributes: %NL80211_ATTR_MAC (peer NMI address) and + * %NL80211_ATTR_NAN_COMMITTED_DW. + * Optionally, the full schedule can be provided by including all of: + * %NL80211_ATTR_NAN_SEQ_ID, %NL80211_ATTR_NAN_CHANNEL (one or more), and + * %NL80211_ATTR_NAN_PEER_MAPS (see &enum nl80211_nan_peer_map_attrs). + * If any of these three optional attributes is provided, all three must + * be provided. + * Each peer channel must be compatible with at least one local channel + * set by %NL80211_CMD_SET_LOCAL_NAN_SCHED. Different maps must not + * contain compatible channels. + * For single-radio devices (n_radio <= 1), different maps must not + * schedule the same time slot, as the device cannot operate on multiple + * channels simultaneously. + * When updating an existing peer schedule, the full new schedule must be + * provided - partial updates are not supported. The new schedule will + * completely replace the previous one. + * The peer schedule is automatically removed when the NMI station is + * removed. + * @NL80211_CMD_NAN_ULW_UPDATE: Notification from the driver to user space + * with the updated ULW blob of the device. User space can use this blob + * to attach to frames sent to peers. This notification contains + * %NL80211_ATTR_NAN_ULW with the ULW blob. + * @NL80211_CMD_NAN_CHANNEL_EVAC: Notification to indicate that a NAN + * channel has been evacuated due to resource conflicts with other + * interfaces. This can happen when another interface sharing the channel + * resource with NAN needs to move to a different channel (e.g., channel + * switch or link switch on a BSS interface). + * The notification contains %NL80211_ATTR_NAN_CHANNEL attribute + * identifying the evacuated channel. + * User space may reconfigure the local schedule in response to this + * notification. + * @NL80211_CMD_START_PD: Start PD operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been previously + * created with %NL80211_CMD_NEW_INTERFACE. + * @NL80211_CMD_STOP_PD: Stop the PD operation, identified by + * its %NL80211_ATTR_WDEV interface. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1578,6 +1680,29 @@ enum nl80211_commands { NL80211_CMD_LINKS_REMOVED, + NL80211_CMD_SET_TID_TO_LINK_MAPPING, + + NL80211_CMD_ASSOC_MLO_RECONF, + NL80211_CMD_EPCS_CFG, + + NL80211_CMD_NAN_NEXT_DW_NOTIFICATION, + NL80211_CMD_NAN_CLUSTER_JOINED, + + NL80211_CMD_INCUMBENT_SIGNAL_DETECT, + + NL80211_CMD_NAN_SET_LOCAL_SCHED, + + NL80211_CMD_NAN_SCHED_UPDATE_DONE, + + NL80211_CMD_NAN_SET_PEER_SCHED, + + NL80211_CMD_NAN_ULW_UPDATE, + + NL80211_CMD_NAN_CHANNEL_EVAC, + + NL80211_CMD_START_PD, + NL80211_CMD_STOP_PD, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1702,21 +1827,21 @@ enum nl80211_commands { * (see &enum nl80211_plink_action). * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path - * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at * &enum nl80211_mpath_info. * * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of * &enum nl80211_mntr_flags. * * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the - * current regulatory domain should be set to or is already set to. - * For example, 'CR', for Costa Rica. This attribute is used by the kernel - * to query the CRDA to retrieve one regulatory domain. This attribute can - * also be used by userspace to query the kernel for the currently set - * regulatory domain. We chose an alpha2 as that is also used by the - * IEEE-802.11 country information element to identify a country. - * Users can also simply ask the wireless core to set regulatory domain - * to a specific alpha2. + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11 country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory * rules. * @@ -1759,9 +1884,9 @@ enum nl80211_commands { * @NL80211_ATTR_BSS: scan result BSS * * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain - * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently - * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) * * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies * an array of command numbers (i.e. a mapping index to command number) @@ -1780,15 +1905,15 @@ enum nl80211_commands { * a u32 * * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change - * due to considerations from a beacon hint. This attribute reflects - * the state of the channel _before_ the beacon hint processing. This - * attributes consists of a nested attribute containing - * NL80211_FREQUENCY_ATTR_* + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change - * due to considerations from a beacon hint. This attribute reflects - * the state of the channel _after_ the beacon hint processing. This - * attributes consists of a nested attribute containing - * NL80211_FREQUENCY_ATTR_* + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* * * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported * cipher suites @@ -1835,7 +1960,7 @@ enum nl80211_commands { * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER * flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth - * frames are not forwared over the control port. + * frames are not forwarded over the control port. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -1849,12 +1974,6 @@ enum nl80211_commands { * that protected APs should be used. This is also used with NEW_BEACON to * indicate that the BSS is to use protection. * - * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON - * to indicate which unicast key ciphers will be used with the connection - * (an array of u32). - * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to - * indicate which group key cipher will be used with the connection (a - * u32). * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which WPA version(s) the AP we want to associate with is using * (a u32 with flags from &enum nl80211_wpa_versions). @@ -1885,6 +2004,7 @@ enum nl80211_commands { * with %NL80211_KEY_* sub-attributes * * @NL80211_ATTR_PID: Process ID of a network namespace. + * @NL80211_ATTR_NETNS_FD: File descriptor of a network namespace. * * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for * dumps. This number increases whenever the object list being @@ -1922,8 +2042,9 @@ enum nl80211_commands { * The driver must also specify support for this with the extended * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, * NL80211_EXT_FEATURE_BEACON_RATE_HT, - * NL80211_EXT_FEATURE_BEACON_RATE_VHT and - * NL80211_EXT_FEATURE_BEACON_RATE_HE. + * NL80211_EXT_FEATURE_BEACON_RATE_VHT, + * NL80211_EXT_FEATURE_BEACON_RATE_HE and + * NL80211_EXT_FEATURE_BEACON_RATE_EHT. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. @@ -1939,6 +2060,7 @@ enum nl80211_commands { * * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. + * @NL80211_ATTR_ACK_SIGNAL: Station's ack signal strength (s32) * * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. * @@ -1972,10 +2094,10 @@ enum nl80211_commands { * bit. Depending on which antennas are selected in the bitmap, 802.11n * drivers can derive which chainmasks to use (if all antennas belonging to * a particular chain are disabled this chain should be disabled) and if - * a chain has diversity antennas wether diversity should be used or not. + * a chain has diversity antennas whether diversity should be used or not. * HT capabilities (STBC, TX Beamforming, Antenna selection) can be * derived from the available chains after applying the antenna mask. - * Non-802.11n drivers can derive wether to use diversity or not. + * Non-802.11n drivers can derive whether to use diversity or not. * Drivers may reject configurations or RX/TX mask combinations they cannot * support by returning -EINVAL. * @@ -2048,6 +2170,10 @@ enum nl80211_commands { * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes * defined in &enum nl80211_if_combination_attrs. + * If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set), + * this attribute contains the interface combinations of the first radio. + * See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy + * combinations for the sum of all radios. * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that * are managed in software: interfaces of these types aren't subject to @@ -2136,6 +2262,9 @@ enum nl80211_commands { * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable * this feature during association. This is a flag attribute. * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_DISABLE_EHT: Force EHT capable interfaces to disable + * this feature during association. This is a flag attribute. + * Currently only supported in mac80211 drivers. * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the * ATTR_HT_CAPABILITY to which attention should be paid. * Currently, only mac80211 NICs support this feature. @@ -2145,6 +2274,12 @@ enum nl80211_commands { * All values are treated as suggestions and may be ignored * by the driver as required. The actual values may be seen in * the station debugfs ht_caps file. + * @NL80211_ATTR_VHT_CAPABILITY_MASK: Specify which bits of the + * ATTR_VHT_CAPABILITY to which attention should be paid. + * Currently, only mac80211 NICs support this feature. + * All values are treated as suggestions and may be ignored + * by the driver as required. The actual values may be seen in + * the station debugfs vht_caps file. * * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country * abides to when initiating radiation on DFS channels. A country maps @@ -2248,7 +2383,8 @@ enum nl80211_commands { * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). * This is similar to @NL80211_ATTR_STA_AID but with a difference of being * allowed to be used with the first @NL80211_CMD_SET_STATION command to - * update a TDLS peer STA entry. + * update a TDLS peer STA entry. For S1G interfaces, this is limited to + * 1600 for the current mac80211 implementation. * * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. * @@ -2403,7 +2539,7 @@ enum nl80211_commands { * scheduled scan is started. Or the delay before a WoWLAN * net-detect scan is started, counting from the moment the * system is suspended. This value is a u32, in seconds. - + * * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device * is operating in an indoor environment. * @@ -2545,7 +2681,7 @@ enum nl80211_commands { * from successful FILS authentication and is used with * %NL80211_CMD_CONNECT. * - * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertised by a FILS AP * identifying the scope of PMKSAs. This is used with * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. * @@ -2594,7 +2730,8 @@ enum nl80211_commands { * a flow is assigned on each round of the DRR scheduler. * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from * association request when used with NL80211_CMD_NEW_STATION). Can be set - * only if %NL80211_STA_FLAG_WME is set. + * only if %NL80211_STA_FLAG_WME is set (except for NAN, which uses WME + * anyway). * * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing @@ -2823,8 +2960,210 @@ enum nl80211_commands { * index. If the userspace includes more RNR elements than number of * MBSSID elements then these will be added in every EMA beacon. * - * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is - * disabled. + * @NL80211_ATTR_MLO_LINK_DISABLED: Unused. It was used to indicate that a link + * is disabled during association. However, the AP will send the + * information by including a TTLM in the association response. + * + * @NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA: Include BSS usage data, i.e. + * include BSSes that can only be used in restricted scenarios and/or + * cannot be used at all. + * + * @NL80211_ATTR_MLO_TTLM_DLINK: Binary attribute specifying the downlink TID to + * link mapping. The length is 8 * sizeof(u16). For each TID the link + * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element) + * in Draft P802.11be_D4.0. + * @NL80211_ATTR_MLO_TTLM_ULINK: Binary attribute specifying the uplink TID to + * link mapping. The length is 8 * sizeof(u16). For each TID the link + * mapping is as defined in section 9.4.2.314 (TID-To-Link Mapping element) + * in Draft P802.11be_D4.0. + * + * @NL80211_ATTR_ASSOC_SPP_AMSDU: flag attribute used with + * %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs + * are used on this connection + * + * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios + * belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs. + * + * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the + * supported interface combinations for all radios combined. In each + * nested item, it contains attributes defined in + * &enum nl80211_if_combination_attrs. + * + * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32). + * A value of 0 means all radios. + * + * @NL80211_ATTR_SUPPORTED_SELECTORS: supported BSS Membership Selectors, array + * of supported selectors as defined by IEEE Std 802.11-2020 9.4.2.3 but + * without the length restriction (at most %NL80211_MAX_SUPP_SELECTORS). + * This can be used to provide a list of selectors that are implemented + * by the supplicant. If not given, support for SAE_H2E is assumed. + * + * @NL80211_ATTR_MLO_RECONF_REM_LINKS: (u16) A bitmask of the links requested + * to be removed from the MLO association. + * + * @NL80211_ATTR_EPCS: Flag attribute indicating that EPCS is enabled for a + * station interface. + * + * @NL80211_ATTR_EXT_MLD_CAPA_AND_OPS: Extended MLD capabilities and operations. + * For association and link reconfiguration, indicates extra capabilities + * that userspace implements, currently only "BTM MLD Recommendation For + * Multiple APs Support". + * For wiphy information, additional flags that drivers will set, but + * this is informational only for userspace (it's not expected to set + * these.) + * + * @NL80211_ATTR_WIPHY_RADIO_INDEX: (int) Integer attribute denoting the index + * of the radio in interest. Internally a value of -1 is used to + * indicate that the radio id is not given in user-space. This means + * that all the attributes are applicable to all the radios. If there is + * a radio index provided in user-space, the attributes will be + * applicable to that specific radio only. If the radio id is greater + * thank the number of radios, error denoting invalid value is returned. + * + * @NL80211_ATTR_S1G_LONG_BEACON_PERIOD: (u8) Integer attribute that represents + * the number of beacon intervals between each long beacon transmission + * for an S1G BSS with short beaconing enabled. This is a required + * attribute for initialising an S1G short beaconing BSS. When updating + * the short beacon data, this is not required. It has a minimum value of + * 2 (i.e 2 beacon intervals). + * + * @NL80211_ATTR_S1G_SHORT_BEACON: Nested attribute containing the short beacon + * head and tail used to set or update the short beacon templates. When + * bringing up a new interface, %NL80211_ATTR_S1G_LONG_BEACON_PERIOD is + * required alongside this attribute. Refer to + * @enum nl80211_s1g_short_beacon_attrs for the attribute definitions. + * + * @NL80211_ATTR_BSS_PARAM: nested attribute used with %NL80211_CMD_GET_WIPHY + * which indicates which BSS parameters can be modified. The attribute can + * also be used as flag attribute by user-space in %NL80211_CMD_SET_BSS to + * indicate that it wants strict checking on the BSS parameters to be + * modified. + * + * @NL80211_ATTR_NAN_CONFIG: Nested attribute for + * extended NAN cluster configuration. This is used with + * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG. + * See &enum nl80211_nan_conf_attributes for details. + * This attribute is optional. + * @NL80211_ATTR_NAN_NEW_CLUSTER: Flag attribute indicating that a new + * NAN cluster has been created. This is used with + * %NL80211_CMD_NAN_CLUSTER_JOINED + * @NL80211_ATTR_NAN_CAPABILITIES: Nested attribute for NAN capabilities. + * This is used with %NL80211_CMD_GET_WIPHY to indicate the NAN + * capabilities supported by the driver. See &enum nl80211_nan_capabilities + * for details. + * + * @NL80211_ATTR_S1G_PRIMARY_2MHZ: flag attribute indicating that the S1G + * primary channel is 2 MHz wide, and the control channel designates + * the 1 MHz primary subchannel within that 2 MHz primary. + * + * @NL80211_ATTR_EPP_PEER: A flag attribute to indicate if the peer is an EPP + * STA. Used with %NL80211_CMD_NEW_STA and %NL80211_CMD_ADD_LINK_STA + * + * @NL80211_ATTR_UHR_CAPABILITY: UHR Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if HE/EHT are also available. + * @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable + * this feature during association. This is a flag attribute. + * Currently only supported in mac80211 drivers. + * @NL80211_ATTR_NAN_CHANNEL: This is a nested attribute. There can be multiple + * attributes of this type, each one represents a channel definition and + * consists of top-level attributes like %NL80211_ATTR_WIPHY_FREQ. + * When used with %NL80211_CMD_NAN_SET_LOCAL_SCHED, it specifies + * the channel definitions on which the radio needs to operate during + * specific time slots. All of the channel definitions should be mutually + * incompatible. With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. + * When used with %NL80211_CMD_NAN_SET_PEER_SCHED, it configures the + * peer NAN channels. In that case, the channel definitions can be + * compatible to each other, or even identical just with different RX NSS. + * With this command, %NL80211_ATTR_NAN_CHANNEL_ENTRY and + * %NL80211_ATTR_NAN_RX_NSS are mandatory. + * The number of channels should fit the current configuration of channels + * and the possible interface combinations. + * If an existing NAN channel is changed but the chandef isn't, the + * channel entry must also remain unchanged. + * When used with %NL80211_CMD_NAN_CHANNEL_EVAC, this identifies the + * channels that were evacuated. + * @NL80211_ATTR_NAN_CHANNEL_ENTRY: a byte array of 6 bytes. contains the + * Channel Entry as defined in Wi-Fi Aware (TM) 4.0 specification Table + * 100 (Channel Entry format for the NAN Availability attribute). + * @NL80211_ATTR_NAN_RX_NSS: (u8) RX NSS used for a NAN channel. This is + * used with %NL80211_ATTR_NAN_CHANNEL when configuring NAN channels with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED or %NL80211_CMD_NAN_SET_PEER_SCHED. + * @NL80211_ATTR_NAN_TIME_SLOTS: an array of u8 values and 32 cells. each value + * maps a time slot to the chandef on which the radio should operate on in + * that time. %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * The chandef is represented using its index, where the index is the + * sequential number of the %NL80211_ATTR_NAN_CHANNEL attribute within all + * the attributes of this type. + * Each slots spans over 16TUs, hence the entire schedule spans over + * 512TUs. Other slot durations and periods are currently not supported. + * @NL80211_ATTR_NAN_AVAIL_BLOB: (Binary) The NAN Availability attribute blob, + * including the attribute header, as defined in Wi-Fi Aware (TM) 4.0 + * specification Table 93 (NAN Availability attribute format). Required with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED to provide the raw NAN Availability + * attribute. Used by the device to publish Schedule Update NAFs. + * @NL80211_ATTR_NAN_SCHED_DEFERRED: Flag attribute used with + * %NL80211_CMD_NAN_SET_LOCAL_SCHED. When present, the command is a + * request from the device to perform an announced schedule update. This + * means that it needs to send the updated NAN availability to the peers, + * and do the actual switch on the right time (i.e. at the end of the slot + * after the slot in which the updated NAN Availability was sent). Since + * the slots management is done in the device, the update to the peers + * needs to be sent by the device, so it knows the actual switch time. + * If the flag is not set, the schedule should be applied immediately. + * When this flag is set, the total number of NAN channels from both the + * old and new schedules must not exceed the allowed number of local NAN + * channels, because with deferred scheduling the old channels cannot be + * removed before adding the new ones to free up space. + * @NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS: flag attribute used with + * %NL80211_CMD_NAN_SCHED_UPDATE_DONE to indicate that the deferred + * schedule update completed successfully. If this flag is not present, + * the update failed. + * @NL80211_ATTR_NAN_NMI_MAC: The address of the NMI station to which this NDI + * station belongs. Used with %NL80211_CMD_NEW_STATION when adding an NDI + * station. + * @NL80211_ATTR_NAN_ULW: (Binary) The initial ULW(s) as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 109 + * (Unaligned Schedule attribute format). Used to configure the device + * with the initial ULW(s) of a peer, before the device starts tracking it. + * @NL80211_ATTR_NAN_COMMITTED_DW: (u16) The committed DW as published by the + * peer, as defined in the Wi-Fi Aware (TM) 4.0 specification Table 80 + * (Committed DW Information field format). + * @NL80211_ATTR_NAN_SEQ_ID: (u8) The sequence ID of the peer schedule that + * %NL80211_CMD_NAN_SET_PEER_SCHED defines. The device follows the + * sequence ID in the frames to identify newer schedules. Once a schedule + * with a higher sequence ID is received, the device may stop communicating + * with that peer until a new peer schedule with a matching sequence ID is + * received. + * @NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME: (u16) The maximum channel switch + * time, in microseconds. + * @NL80211_ATTR_NAN_PEER_MAPS: Nested array of peer schedule maps. + * Used with %NL80211_CMD_NAN_SET_PEER_SCHED. Contains up to 2 entries, + * each containing nested attributes from &enum nl80211_nan_peer_map_attrs. + * + * @NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP: u32 attribute specifying + * the signal interference bitmap detected on the operating bandwidth for + * %NL80211_CMD_INCUMBENT_SIGNAL_DETECT. Each bit represents a 20 MHz + * segment, lowest bit corresponds to the lowest 20 MHz segment, in the + * operating bandwidth where the interference is detected. Punctured + * sub-channels are included in the bitmap structure; however, since + * interference detection is not performed on these sub-channels, their + * corresponding bits are consistently set to zero. + * + * @NL80211_ATTR_UHR_OPERATION: Full UHR Operation element, as it appears in + * association response etc., since it's abridged in the beacon. Used + * for START_AP etc. + * + * @NL80211_ATTR_ASSOC_ENCRYPTED: Flag attribute, used only with the + * %NL80211_CMD_CONNECT event in SME-in-driver mode. The driver should + * set this flag to indicate that both the (Re)Association Request frame + * and the corresponding (Re)Association Response frame are transmitted + * encrypted over the air. Enhanced Privacy Protection (EPP), as defined + * in IEEE P802.11bi/D4.0, mandates this encryption. + * + * @NL80211_ATTR_NPCA_PRIMARY_FREQ: NPCA primary channel (u32) + * @NL80211_ATTR_NPCA_PUNCT_BITMAP: NPCA puncturing bitmap (u32) * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -3364,6 +3703,66 @@ enum nl80211_attrs { NL80211_ATTR_MLO_LINK_DISABLED, + NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA, + + NL80211_ATTR_MLO_TTLM_DLINK, + NL80211_ATTR_MLO_TTLM_ULINK, + + NL80211_ATTR_ASSOC_SPP_AMSDU, + + NL80211_ATTR_WIPHY_RADIOS, + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS, + + NL80211_ATTR_VIF_RADIO_MASK, + + NL80211_ATTR_SUPPORTED_SELECTORS, + + NL80211_ATTR_MLO_RECONF_REM_LINKS, + NL80211_ATTR_EPCS, + + NL80211_ATTR_EXT_MLD_CAPA_AND_OPS, + + NL80211_ATTR_WIPHY_RADIO_INDEX, + + NL80211_ATTR_S1G_LONG_BEACON_PERIOD, + NL80211_ATTR_S1G_SHORT_BEACON, + NL80211_ATTR_BSS_PARAM, + NL80211_ATTR_NAN_CONFIG, + NL80211_ATTR_NAN_NEW_CLUSTER, + NL80211_ATTR_NAN_CAPABILITIES, + + NL80211_ATTR_S1G_PRIMARY_2MHZ, + + NL80211_ATTR_EPP_PEER, + + NL80211_ATTR_UHR_CAPABILITY, + NL80211_ATTR_DISABLE_UHR, + + NL80211_ATTR_INCUMBENT_SIGNAL_INTERFERENCE_BITMAP, + + NL80211_ATTR_UHR_OPERATION, + + NL80211_ATTR_NAN_CHANNEL, + NL80211_ATTR_NAN_CHANNEL_ENTRY, + NL80211_ATTR_NAN_TIME_SLOTS, + NL80211_ATTR_NAN_RX_NSS, + NL80211_ATTR_NAN_AVAIL_BLOB, + NL80211_ATTR_NAN_SCHED_DEFERRED, + NL80211_ATTR_NAN_SCHED_UPDATE_SUCCESS, + + NL80211_ATTR_NAN_NMI_MAC, + + NL80211_ATTR_NAN_ULW, + NL80211_ATTR_NAN_COMMITTED_DW, + NL80211_ATTR_NAN_SEQ_ID, + NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME, + NL80211_ATTR_NAN_PEER_MAPS, + + NL80211_ATTR_ASSOC_ENCRYPTED, + + NL80211_ATTR_NPCA_PRIMARY_FREQ, + NL80211_ATTR_NPCA_PUNCT_BITMAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3378,6 +3777,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP +#define NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS NL80211_ATTR_EXT_MLD_CAPA_AND_OPS /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -3408,6 +3808,7 @@ enum nl80211_attrs { #define NL80211_WIPHY_NAME_MAXLEN 64 #define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_SELECTORS 128 #define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 128 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 @@ -3456,6 +3857,10 @@ enum nl80211_attrs { * @NL80211_IFTYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) + * @NL80211_IFTYPE_NAN_DATA: NAN data interface type (netdev); NAN data + * interfaces can only be brought up (IFF_UP) when a NAN interface + * already exists and NAN has been started (using %NL80211_CMD_START_NAN). + * @NL80211_IFTYPE_PD: PD device interface type (not a netdev) * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -3477,6 +3882,8 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_DEVICE, NL80211_IFTYPE_OCB, NL80211_IFTYPE_NAN, + NL80211_IFTYPE_NAN_DATA, + NL80211_IFTYPE_PD, /* keep last */ NUM_NL80211_IFTYPES, @@ -3504,6 +3911,7 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a * previously added station into associated state + * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -3516,6 +3924,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, NL80211_STA_FLAG_ASSOCIATED, + NL80211_STA_FLAG_SPP_AMSDU, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -3526,7 +3935,7 @@ enum nl80211_sta_flags { * enum nl80211_sta_p2p_ps_status - station support of P2P PS * * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism - * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism + * @NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism * @NUM_NL80211_P2P_PS_STATUS: number of values */ enum nl80211_sta_p2p_ps_status { @@ -3564,9 +3973,9 @@ enum nl80211_he_gi { /** * enum nl80211_he_ltf - HE long training field - * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec - * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec - * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec + * @NL80211_RATE_INFO_HE_1XLTF: 3.2 usec + * @NL80211_RATE_INFO_HE_2XLTF: 6.4 usec + * @NL80211_RATE_INFO_HE_4XLTF: 12.8 usec */ enum nl80211_he_ltf { NL80211_RATE_INFO_HE_1XLTF, @@ -3606,6 +4015,22 @@ enum nl80211_eht_gi { NL80211_RATE_INFO_EHT_GI_3_2, }; +/** + * enum nl80211_eht_ltf - EHT long training field + * @NL80211_RATE_INFO_EHT_1XLTF: 3.2 usec + * @NL80211_RATE_INFO_EHT_2XLTF: 6.4 usec + * @NL80211_RATE_INFO_EHT_4XLTF: 12.8 usec + * @NL80211_RATE_INFO_EHT_6XLTF: 19.2 usec + * @NL80211_RATE_INFO_EHT_8XLTF: 25.6 usec + */ +enum nl80211_eht_ltf { + NL80211_RATE_INFO_EHT_1XLTF, + NL80211_RATE_INFO_EHT_2XLTF, + NL80211_RATE_INFO_EHT_4XLTF, + NL80211_RATE_INFO_EHT_6XLTF, + NL80211_RATE_INFO_EHT_8XLTF, +}; + /** * enum nl80211_eht_ru_alloc - EHT RU allocation values * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation @@ -3681,7 +4106,7 @@ enum nl80211_eht_ru_alloc { * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier * (u8, see &enum nl80211_he_gi) * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1) - * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then + * @NL80211_RATE_INFO_HE_RU_ALLOC: HE RU allocation, if not present then * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc) * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15) @@ -3697,6 +4122,12 @@ enum nl80211_eht_ru_alloc { * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate + * @NL80211_RATE_INFO_UHR_MCS: UHR MCS index (u8, 0-15, 17, 19, 20, 23) + * Note that the other EHT attributes (such as @NL80211_RATE_INFO_EHT_NSS) + * are used in conjunction with this where applicable + * @NL80211_RATE_INFO_UHR_ELR: UHR ELR flag, which restricts NSS to 1, + * MCS to 0 or 1, and GI to %NL80211_RATE_INFO_EHT_GI_1_6. + * @NL80211_RATE_INFO_UHR_IM: UHR Interference Mitigation flag * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -3730,6 +4161,9 @@ enum nl80211_rate_info { NL80211_RATE_INFO_4_MHZ_WIDTH, NL80211_RATE_INFO_8_MHZ_WIDTH, NL80211_RATE_INFO_16_MHZ_WIDTH, + NL80211_RATE_INFO_UHR_MCS, + NL80211_RATE_INFO_UHR_ELR, + NL80211_RATE_INFO_UHR_IM, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -3784,7 +4218,7 @@ enum nl80211_sta_bss_param { * (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute - * containing info as possible, see &enum nl80211_rate_info + * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs) * (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs) @@ -3813,8 +4247,8 @@ enum nl80211_sta_bss_param { * Contains a nested array of signal strength attributes (u8, dBm) * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. - * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the - * 802.11 header (u32, kbps) + * @NL80211_STA_INFO_EXPECTED_THROUGHPUT: expected throughput considering also + * the 802.11 header (u32, kbps) * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons * (u64) * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64) @@ -4000,7 +4434,7 @@ enum nl80211_mpath_flags { * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in - * &enum nl80211_mpath_flags; + * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination @@ -4052,6 +4486,10 @@ enum nl80211_mpath_info { * capabilities element * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as * defined in EHT capabilities element + * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC: UHR MAC capabilities as in UHR + * capabilities element + * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY: UHR PHY capabilities as in UHR + * capabilities element * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined */ @@ -4069,6 +4507,8 @@ enum nl80211_band_iftype_attr { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, + NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC, + NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY, /* keep last */ __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST, @@ -4132,6 +4572,46 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA +/** + * enum nl80211_nan_phy_cap_attr - NAN PHY capabilities attributes + * @__NL80211_NAN_PHY_CAP_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET: 16-byte attribute containing HT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_HT_CAPA: HT capabilities (u16) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR: HT A-MPDU factor (u8) + * @NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY: HT A-MPDU density (u8) + * @NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET: 8-byte attribute containing VHT MCS set + * @NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA: VHT capabilities (u32) + * @NL80211_NAN_PHY_CAP_ATTR_HE_MAC: HE MAC capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_PHY: HE PHY capabilities + * @NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET: HE supported NSS/MCS combinations + * @NL80211_NAN_PHY_CAP_ATTR_HE_PPE: HE PPE thresholds + * @NL80211_NAN_PHY_CAP_ATTR_MAX: highest NAN PHY cap attribute number + * @__NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST: internal use + */ +enum nl80211_nan_phy_cap_attr { + __NL80211_NAN_PHY_CAP_ATTR_INVALID, + + /* HT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HT_CAPA, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR, + NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY, + + /* VHT capabilities */ + NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA, + + /* HE capabilities */ + NL80211_NAN_PHY_CAP_ATTR_HE_MAC, + NL80211_NAN_PHY_CAP_ATTR_HE_PHY, + NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET, + NL80211_NAN_PHY_CAP_ATTR_HE_PPE, + + /* keep last */ + __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST, + NL80211_NAN_PHY_CAP_ATTR_MAX = __NL80211_NAN_PHY_CAP_ATTR_AFTER_LAST - 1 +}; + /** * enum nl80211_wmm_rule - regulatory wmm rule * @@ -4140,7 +4620,7 @@ enum nl80211_band_attr { * @NL80211_WMMR_CW_MAX: Maximum contention window slot. * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space. * @NL80211_WMMR_TXOP: Maximum allowed tx operation time. - * @nl80211_WMMR_MAX: highest possible wmm rule. + * @NL80211_WMMR_MAX: highest possible wmm rule. * @__NL80211_WMMR_LAST: Internal use. */ enum nl80211_wmm_rule { @@ -4162,15 +4642,16 @@ enum nl80211_wmm_rule { * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation - * are permitted on this channel, this includes sending probe - * requests, or modes of operation that require beaconing. + * are permitted on this channel, this includes sending probe + * requests, or modes of operation that require beaconing. + * @__NL80211_FREQUENCY_ATTR_NO_IBSS: obsolete, same as _NO_IR * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS * (enum nl80211_dfs_state) - * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in milliseconds for how long * this channel is in this DFS state. * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this * channel as the control channel @@ -4226,6 +4707,36 @@ enum nl80211_wmm_rule { * in current regulatory domain. * @NL80211_FREQUENCY_ATTR_PSD: Power spectral density (in dBm) that * is allowed on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_DFS_CONCURRENT: Operation on this channel is + * allowed for peer-to-peer or adhoc communication under the control + * of a DFS master which operates on the same channel (FCC-594280 D01 + * Section B.3). Should be used together with %NL80211_RRF_DFS only. + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP + * not allowed using this channel + * @NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP + * not allowed using this channel + * @NL80211_FREQUENCY_ATTR_CAN_MONITOR: This channel can be used in monitor + * mode despite other (regulatory) restrictions, even if the channel is + * otherwise completely disabled. + * @NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP: This channel can be used for a + * very low power (VLP) AP, despite being NO_IR. + * @NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY: This channel can be active in + * 20 MHz bandwidth, despite being NO_IR. + * @NL80211_FREQUENCY_ATTR_NO_4MHZ: 4 MHz operation is not allowed on this + * channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_8MHZ: 8 MHz operation is not allowed on this + * channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_16MHZ: 16 MHz operation is not allowed on this + * channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY: Channel is not permitted for use + * as a primary channel. Does not prevent the channel from existing + * as a non-primary subchannel. Only applicable to S1G channels. + * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel + * in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_CAC_START_TIME: Channel Availability Check (CAC) + * start time (CLOCK_BOOTTIME, nanoseconds). Only present when CAC is + * currently in progress on this channel. + * @NL80211_FREQUENCY_ATTR_PAD: attribute used for padding for 64-bit alignment * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -4265,6 +4776,19 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_320MHZ, NL80211_FREQUENCY_ATTR_NO_EHT, NL80211_FREQUENCY_ATTR_PSD, + NL80211_FREQUENCY_ATTR_DFS_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT, + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT, + NL80211_FREQUENCY_ATTR_CAN_MONITOR, + NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP, + NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY, + NL80211_FREQUENCY_ATTR_NO_4MHZ, + NL80211_FREQUENCY_ATTR_NO_8MHZ, + NL80211_FREQUENCY_ATTR_NO_16MHZ, + NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY, + NL80211_FREQUENCY_ATTR_NO_UHR, + NL80211_FREQUENCY_ATTR_CAC_START_TIME, + NL80211_FREQUENCY_ATTR_PAD, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -4277,6 +4801,10 @@ enum nl80211_frequency_attr { #define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \ NL80211_FREQUENCY_ATTR_IR_CONCURRENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT +#define NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT \ + NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT /** * enum nl80211_bitrate_attr - bitrate attributes @@ -4299,16 +4827,16 @@ enum nl80211_bitrate_attr { }; /** - * enum nl80211_initiator - Indicates the initiator of a reg domain request + * enum nl80211_reg_initiator - Indicates the initiator of a reg domain request * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world - * regulatory domain. + * regulatory domain. * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the - * regulatory domain. + * regulatory domain. * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the - * wireless core it thinks its knows the regulatory domain we should be in. + * wireless core it thinks its knows the regulatory domain we should be in. * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an - * 802.11 country information element with regulatory information it - * thinks we should consider. cfg80211 only processes the country + * 802.11 country information element with regulatory information it + * thinks we should consider. cfg80211 only processes the country * code from the IE, and relies on the regulatory domain information * structure passed by userspace (CRDA) from our wireless-regdb. * If a channel is enabled but the country code indicates it should @@ -4327,11 +4855,11 @@ enum nl80211_reg_initiator { * to a specific country. When this is set you can count on the * ISO / IEC 3166 alpha2 country code being valid. * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory - * domain. + * domain. * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom - * driver specific world regulatory domain. These do not apply system-wide - * and are only applicable to the individual devices which have requested - * them to be applied. + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product * of an intersection between two regulatory domains -- the previously * set regulatory domain on the system and the last accepted regulatory @@ -4348,21 +4876,21 @@ enum nl80211_reg_type { * enum nl80211_reg_rule_attr - regulatory rule attributes * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional - * considerations for a given frequency range. These are the - * &enum nl80211_reg_rule_flags. + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory - * rule in KHz. This is not a center of frequency but an actual regulatory - * band edge. + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule - * in KHz. This is not a center a frequency but an actual regulatory - * band edge. + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this * frequency range, in KHz. * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain - * for a given frequency range. The value is in mBi (100 * dBi). - * If you don't have one then don't send this. + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for - * a given frequency range. The value is in mBm (100 * dBm). + * a given frequency range. The value is in mBm (100 * dBm). * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. * If not present or 0 default CAC time will be used. * @NL80211_ATTR_POWER_RULE_PSD: power spectral density (in dBm). @@ -4414,14 +4942,7 @@ enum nl80211_reg_rule_attr { * value as specified by &struct nl80211_bss_select_rssi_adjust. * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching * (this cannot be used together with SSID). - * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the - * band specific minimum rssi thresholds for the bands defined in - * enum nl80211_band. The minimum rssi threshold value(s32) specific to a - * band shall be encapsulated in attribute with type value equals to one - * of the NL80211_BAND_* defined in enum nl80211_band. For example, the - * minimum rssi threshold value for 2.4GHZ band shall be encapsulated - * within an attribute of type NL80211_BAND_2GHZ. And one or more of such - * attributes will be nested within this attribute. + * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Obsolete * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -4434,7 +4955,7 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, - NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, + NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, /* obsolete */ /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -4456,8 +4977,9 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed, - * this includes probe requests or modes of operation that require - * beaconing. + * this includes probe requests or modes of operation that require + * beaconing. + * @__NL80211_RRF_NO_IBSS: obsolete, same as NO_IR * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated * base on contiguous rules and wider channels will be allowed to cross * multiple contiguous/overlapping frequency ranges. @@ -4470,27 +4992,44 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed * @NL80211_RRF_NO_EHT: EHT operation not allowed * @NL80211_RRF_PSD: Ruleset has power spectral density value + * @NL80211_RRF_DFS_CONCURRENT: Operation on this channel is allowed for + * peer-to-peer or adhoc communication under the control of a DFS master + * which operates on the same channel (FCC-594280 D01 Section B.3). + * Should be used together with %NL80211_RRF_DFS only. + * @NL80211_RRF_NO_6GHZ_VLP_CLIENT: Client connection to VLP AP not allowed + * @NL80211_RRF_NO_6GHZ_AFC_CLIENT: Client connection to AFC AP not allowed + * @NL80211_RRF_ALLOW_6GHZ_VLP_AP: Very low power (VLP) AP can be permitted + * despite NO_IR configuration. + * @NL80211_RRF_ALLOW_20MHZ_ACTIVITY: Allow activity in 20 MHz bandwidth, + * despite NO_IR configuration. + * @NL80211_RRF_NO_UHR: UHR operation not allowed */ enum nl80211_reg_rule_flags { - NL80211_RRF_NO_OFDM = 1<<0, - NL80211_RRF_NO_CCK = 1<<1, - NL80211_RRF_NO_INDOOR = 1<<2, - NL80211_RRF_NO_OUTDOOR = 1<<3, - NL80211_RRF_DFS = 1<<4, - NL80211_RRF_PTP_ONLY = 1<<5, - NL80211_RRF_PTMP_ONLY = 1<<6, - NL80211_RRF_NO_IR = 1<<7, - __NL80211_RRF_NO_IBSS = 1<<8, - NL80211_RRF_AUTO_BW = 1<<11, - NL80211_RRF_IR_CONCURRENT = 1<<12, - NL80211_RRF_NO_HT40MINUS = 1<<13, - NL80211_RRF_NO_HT40PLUS = 1<<14, - NL80211_RRF_NO_80MHZ = 1<<15, - NL80211_RRF_NO_160MHZ = 1<<16, - NL80211_RRF_NO_HE = 1<<17, - NL80211_RRF_NO_320MHZ = 1<<18, - NL80211_RRF_NO_EHT = 1<<19, - NL80211_RRF_PSD = 1<<20, + NL80211_RRF_NO_OFDM = 1 << 0, + NL80211_RRF_NO_CCK = 1 << 1, + NL80211_RRF_NO_INDOOR = 1 << 2, + NL80211_RRF_NO_OUTDOOR = 1 << 3, + NL80211_RRF_DFS = 1 << 4, + NL80211_RRF_PTP_ONLY = 1 << 5, + NL80211_RRF_PTMP_ONLY = 1 << 6, + NL80211_RRF_NO_IR = 1 << 7, + __NL80211_RRF_NO_IBSS = 1 << 8, + NL80211_RRF_AUTO_BW = 1 << 11, + NL80211_RRF_IR_CONCURRENT = 1 << 12, + NL80211_RRF_NO_HT40MINUS = 1 << 13, + NL80211_RRF_NO_HT40PLUS = 1 << 14, + NL80211_RRF_NO_80MHZ = 1 << 15, + NL80211_RRF_NO_160MHZ = 1 << 16, + NL80211_RRF_NO_HE = 1 << 17, + NL80211_RRF_NO_320MHZ = 1 << 18, + NL80211_RRF_NO_EHT = 1 << 19, + NL80211_RRF_PSD = 1 << 20, + NL80211_RRF_DFS_CONCURRENT = 1 << 21, + NL80211_RRF_NO_6GHZ_VLP_CLIENT = 1 << 22, + NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1 << 23, + NL80211_RRF_ALLOW_6GHZ_VLP_AP = 1 << 24, + NL80211_RRF_ALLOW_20MHZ_ACTIVITY = 1 << 25, + NL80211_RRF_NO_UHR = 1 << 26, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR @@ -4499,6 +5038,8 @@ enum nl80211_reg_rule_flags { #define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ NL80211_RRF_NO_HT40PLUS) #define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT +#define NL80211_RRF_NO_UHB_VLP_CLIENT NL80211_RRF_NO_6GHZ_VLP_CLIENT +#define NL80211_RRF_NO_UHB_AFC_CLIENT NL80211_RRF_NO_6GHZ_AFC_CLIENT /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) @@ -4609,10 +5150,11 @@ enum nl80211_survey_info { * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP * @NL80211_MNTR_FLAG_CONTROL: pass control frames * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering - * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. - * overrides all other flags. + * @NL80211_MNTR_FLAG_COOK_FRAMES: deprecated + * will unconditionally be refused * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address * and ACK incoming unicast packets. + * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets * * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag @@ -4625,6 +5167,7 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_OTHER_BSS, NL80211_MNTR_FLAG_COOK_FRAMES, NL80211_MNTR_FLAG_ACTIVE, + NL80211_MNTR_FLAG_SKIP_TX, /* keep last */ __NL80211_MNTR_FLAG_AFTER_LAST, @@ -4645,8 +5188,8 @@ enum nl80211_mntr_flags { * alternate between Active and Doze states, but may not wake up * for neighbor's beacons. * - * @__NL80211_MESH_POWER_AFTER_LAST - internal use - * @NL80211_MESH_POWER_MAX - highest possible power save level + * @__NL80211_MESH_POWER_AFTER_LAST: internal use + * @NL80211_MESH_POWER_MAX: highest possible power save level */ enum nl80211_mesh_power_mode { @@ -5027,6 +5570,36 @@ enum nl80211_bss_scan_width { NL80211_BSS_CHAN_WIDTH_2, }; +/** + * enum nl80211_bss_use_for - bitmap indicating possible BSS use + * @NL80211_BSS_USE_FOR_NORMAL: Use this BSS for normal "connection", + * including IBSS/MBSS depending on the type. + * @NL80211_BSS_USE_FOR_MLD_LINK: This BSS can be used as a link in an + * MLO connection. Note that for an MLO connection, all links including + * the assoc link must have this flag set, and the assoc link must + * additionally have %NL80211_BSS_USE_FOR_NORMAL set. + */ +enum nl80211_bss_use_for { + NL80211_BSS_USE_FOR_NORMAL = 1 << 0, + NL80211_BSS_USE_FOR_MLD_LINK = 1 << 1, +}; + +/** + * enum nl80211_bss_cannot_use_reasons - reason(s) connection to a + * BSS isn't possible + * @NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY: NSTR nonprimary links aren't + * supported by the device, and this BSS entry represents one. + * @NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH: STA is not supporting + * the AP power type (SP, VLP, AP) that the AP uses. + */ +enum nl80211_bss_cannot_use_reasons { + NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 1 << 0, + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 1 << 1, +}; + +#define NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH \ + NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH + /** * enum nl80211_bss - netlink attributes for a BSS * @@ -5079,6 +5652,14 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8). * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it. + * @NL80211_BSS_USE_FOR: u32 bitmap attribute indicating what the BSS can be + * used for, see &enum nl80211_bss_use_for. + * @NL80211_BSS_CANNOT_USE_REASONS: Indicates the reason that this BSS cannot + * be used for all or some of the possible uses by the device reporting it, + * even though its presence was detected. + * This is a u64 attribute containing a bitmap of values from + * &enum nl80211_cannot_use_reasons, note that the attribute may be missing + * if no reasons are specified. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -5106,6 +5687,8 @@ enum nl80211_bss { NL80211_BSS_FREQUENCY_OFFSET, NL80211_BSS_MLO_LINK_ID, NL80211_BSS_MLD_ADDR, + NL80211_BSS_USE_FOR, + NL80211_BSS_CANNOT_USE_REASONS, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -5141,6 +5724,9 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key + * @NL80211_AUTHTYPE_EPPKE: Enhanced Privacy Protection Key Exchange + * @NL80211_AUTHTYPE_IEEE8021X: IEEE 802.1X authentication utilizing + * Authentication frames * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -5156,6 +5742,8 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FILS_SK, NL80211_AUTHTYPE_FILS_SK_PFS, NL80211_AUTHTYPE_FILS_PK, + NL80211_AUTHTYPE_EPPKE, + NL80211_AUTHTYPE_IEEE8021X, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -5235,6 +5823,18 @@ enum nl80211_key_default_types { * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode. * Defaults to @NL80211_KEY_RX_TX. * @NL80211_KEY_DEFAULT_BEACON: flag indicating default Beacon frame key + * @NL80211_KEY_LTF_SEED: LTF key seed is used by the driver to generate + * secure LTF keys used in case of peer measurement request with FTM + * request type as either %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED + * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED. Secure LTF key seeds + * will help enable PHY security in peer measurement session. + * The LTF key seed is installed along with the TK (Temporal Key) using + * %NL80211_CMD_NEW_KEY. The TK is configured using the + * %NL80211_ATTR_KEY_DATA attribute, while the LTF key seed is configured + * using this attribute. Both keys must be configured before initiation + * of peer measurement to ensure peer measurement session is secure. + * Only valid if %NL80211_EXT_FEATURE_SET_KEY_LTF_SEED is set. This + * attribute is restricted to pairwise keys (%NL80211_KEYTYPE_PAIRWISE). * * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute @@ -5251,6 +5851,7 @@ enum nl80211_key_attributes { NL80211_KEY_DEFAULT_TYPES, NL80211_KEY_MODE, NL80211_KEY_DEFAULT_BEACON, + NL80211_KEY_LTF_SEED, /* keep last */ __NL80211_KEY_AFTER_LAST, @@ -5273,6 +5874,10 @@ enum nl80211_key_attributes { * see &struct nl80211_txrate_he * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us. * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF. + * @NL80211_TXRATE_EHT: EHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_eht + * @NL80211_TXRATE_EHT_GI: configure EHT GI, (u8, see &enum nl80211_eht_gi) + * @NL80211_TXRATE_EHT_LTF: configure EHT LTF, (u8, see &enum nl80211_eht_ltf) * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -5285,6 +5890,9 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_HE, NL80211_TXRATE_HE_GI, NL80211_TXRATE_HE_LTF, + NL80211_TXRATE_EHT, + NL80211_TXRATE_EHT_GI, + NL80211_TXRATE_EHT_LTF, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -5317,6 +5925,15 @@ enum nl80211_txrate_gi { NL80211_TXRATE_FORCE_LGI, }; +#define NL80211_EHT_NSS_MAX 16 +/** + * struct nl80211_txrate_eht - EHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_eht { + __u16 mcs[NL80211_EHT_NSS_MAX]; +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band @@ -5454,7 +6071,7 @@ enum nl80211_tx_rate_setting { * (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE). * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but * per peer instead. - * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates + * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if set indicates * that the new configuration overrides all previous peer * configurations, otherwise previous peer specific configurations * should be left untouched. @@ -5626,7 +6243,7 @@ struct nl80211_pattern_support { * "TCP connection wakeup" for more details. This is a nested attribute * containing the exact information for establishing and keeping alive * the TCP connection. - * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: For wakeup reporting only, the * wakeup packet was received on the TCP connection * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the * TCP connection was lost or failed to be established @@ -5655,6 +6272,8 @@ struct nl80211_pattern_support { * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one * frequency, it means that the match occurred in more than one * channel. + * @NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC: For wakeup reporting only. + * Wake up happened due to unprotected deauth or disassoc frame in MFP. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * @@ -5682,6 +6301,7 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, NL80211_WOWLAN_TRIG_NET_DETECT, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, + NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC, /* keep last */ NUM_NL80211_WOWLAN_TRIG, @@ -5837,7 +6457,7 @@ enum nl80211_attr_coalesce_rule { /** * enum nl80211_coalesce_condition - coalesce rule conditions - * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * @NL80211_COALESCE_CONDITION_MATCH: coalesce Rx packets when patterns * in a rule are matched. * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns * in a rule are not matched. @@ -5936,7 +6556,7 @@ enum nl80211_if_combination_attrs { * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit - * state of non existent mesh peer links + * state of non-existent mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received @@ -5972,7 +6592,7 @@ enum nl80211_plink_state { * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer * @NUM_NL80211_PLINK_ACTIONS: number of possible actions */ -enum plink_actions { +enum nl80211_plink_action { NL80211_PLINK_ACTION_NO_ACTION, NL80211_PLINK_ACTION_OPEN, NL80211_PLINK_ACTION_BLOCK, @@ -6229,7 +6849,7 @@ enum nl80211_feature_flags { * request to use RRM (see %NL80211_ATTR_USE_RRM) with * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set * the ASSOC_REQ_USE_RRM flag in the association request even if - * NL80211_FEATURE_QUIET is not advertized. + * NL80211_FEATURE_QUIET is not advertised. * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air * sniffer which means that it can be configured to hear packets from * certain groups which can be configured by the @@ -6241,13 +6861,15 @@ enum nl80211_feature_flags { * the BSS that the interface that requested the scan is connected to * (if available). * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the - * time the last beacon/probe was received. The time is the TSF of the - * BSS that the interface that requested the scan is connected to - * (if available). + * time the last beacon/probe was received. For a non-MLO connection, the + * time is the TSF of the BSS that the interface that requested the scan is + * connected to (if available). For an MLO connection, the time is the TSF + * of the BSS corresponding with link ID specified in the scan request (if + * specified). * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of * channel dwell time. * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate - * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate. + * configuration (AP/mesh), supporting a legacy (non-HT/VHT) rate. * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate * configuration (AP/mesh) with HT rates. * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate @@ -6297,6 +6919,7 @@ enum nl80211_feature_flags { * receiving control port frames over nl80211 instead of the netdevice. * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports * (average) ACK signal strength reporting. + * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: Backward-compatible ID * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate * TXQs. * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the @@ -6321,8 +6944,7 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching * (set/del PMKSA operations) in AP mode. * - * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports - * filtering of sched scan results using band specific RSSI thresholds. + * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Obsolete * * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power * to a station. @@ -6426,6 +7048,43 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_OWE_OFFLOAD_AP: Driver/Device wants to do OWE DH IE * handling in AP mode. * + * @NL80211_EXT_FEATURE_DFS_CONCURRENT: The device supports peer-to-peer or + * ad hoc operation on DFS channels under the control of a concurrent + * DFS master on the same channel as described in FCC-594280 D01 + * (Section B.3). This, for example, allows P2P GO and P2P clients to + * operate on DFS channels as long as there's a concurrent BSS connection. + * + * @NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT: The driver has support for SPP + * (signaling and payload protected) A-MSDUs and this shall be advertised + * in the RSNXE. + * + * @NL80211_EXT_FEATURE_BEACON_RATE_EHT: Driver supports beacon rate + * configuration (AP/mesh) with EHT rates. + * + * @NL80211_EXT_FEATURE_EPPKE: Driver supports Enhanced Privacy Protection + * Key Exchange (EPPKE) with user space SME (NL80211_CMD_AUTHENTICATE) + * in non-AP STA mode. + * + * @NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION: This specifies that the + * driver supports encryption of (Re)Association Request and Response + * frames in both non‑AP STA and AP mode as specified in + * "IEEE P802.11bi/D3.0, 12.16.6". + * + * @NL80211_EXT_FEATURE_IEEE8021X_AUTH: Driver supports IEEE 802.1X + * authentication utilizing Authentication frames with user space SME + * (NL80211_CMD_AUTHENTICATE) in non-AP STA mode, as specified in + * "IEEE P802.11bi/D4.0, 12.16.5". + * + * @NL80211_EXT_FEATURE_ROC_ADDR_FILTER: Driver supports MAC address + * filtering during remain-on-channel. When %NL80211_ATTR_MAC is + * provided with %NL80211_CMD_REMAIN_ON_CHANNEL, the driver will + * forward frames with a matching MAC address to userspace during + * the off-channel period. + * + * @NL80211_EXT_FEATURE_SET_KEY_LTF_SEED: Driver supports installing the + * LTF key seed via %NL80211_KEY_LTF_SEED. The seed is used to generate + * secure LTF keys for secure LTF measurement sessions. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6467,7 +7126,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, NL80211_EXT_FEATURE_AP_PMKSA_CACHING, - NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, + NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, /* obsolete */ NL80211_EXT_FEATURE_EXT_KEY_ID, NL80211_EXT_FEATURE_STA_TX_PWR, NL80211_EXT_FEATURE_SAE_OFFLOAD, @@ -6499,6 +7158,14 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA, NL80211_EXT_FEATURE_OWE_OFFLOAD, NL80211_EXT_FEATURE_OWE_OFFLOAD_AP, + NL80211_EXT_FEATURE_DFS_CONCURRENT, + NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT, + NL80211_EXT_FEATURE_BEACON_RATE_EHT, + NL80211_EXT_FEATURE_EPPKE, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION, + NL80211_EXT_FEATURE_IEEE8021X_AUTH, + NL80211_EXT_FEATURE_ROC_ADDR_FILTER, + NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -6583,7 +7250,7 @@ enum nl80211_timeout_reason { * request parameters IE in the probe request * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at - * rate of at least 5.5M. In case non OCE AP is discovered in the channel, + * rate of at least 5.5M. In case non-OCE AP is discovered in the channel, * only the first probe req in the channel will be sent in high rate. * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request * tx deferral (dot11FILSProbeDelay shall be set to 15ms) @@ -6619,7 +7286,7 @@ enum nl80211_timeout_reason { * received on the 2.4/5 GHz channels to actively scan only the 6GHz * channels on which APs are expected to be found. Note that when not set, * the scan logic would scan all 6GHz channels, but since transmission of - * probe requests on non PSC channels is limited, it is highly likely that + * probe requests on non-PSC channels is limited, it is highly likely that * these channels would passively be scanned. Also note that when the flag * is set, in addition to the colocated APs, PSC channels would also be * scanned if the user space has asked for it. @@ -6669,6 +7336,8 @@ enum nl80211_acl_policy { * @NL80211_SMPS_STATIC: static SMPS (use a single antenna) * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and * turn on other antennas after CTS/RTS). + * @__NL80211_SMPS_AFTER_LAST: internal + * @NL80211_SMPS_MAX: highest used enumeration */ enum nl80211_smps_mode { NL80211_SMPS_OFF, @@ -6890,6 +7559,8 @@ enum nl80211_bss_select_attr { * @NL80211_NAN_FUNC_PUBLISH: function is publish * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up + * @__NL80211_NAN_FUNC_TYPE_AFTER_LAST: internal use + * @NL80211_NAN_FUNC_MAX_TYPE: internal use */ enum nl80211_nan_function_type { NL80211_NAN_FUNC_PUBLISH, @@ -6951,7 +7622,7 @@ enum nl80211_nan_func_term_reason { * The instance ID for the follow up Service Discovery Frame. This is u8. * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type * is follow up. This is a u8. - * The requestor instance ID for the follow up Service Discovery Frame. + * The requester instance ID for the follow up Service Discovery Frame. * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the * follow up Service Discovery Frame. This is a binary attribute. * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a @@ -7050,7 +7721,108 @@ enum nl80211_nan_match_attributes { }; /** - * nl80211_external_auth_action - Action to perform with external + * enum nl80211_nan_band_conf_attributes - NAN band configuration attributes + * @__NL80211_NAN_BAND_CONF_INVALID: Invalid. + * @NL80211_NAN_BAND_CONF_BAND: Band for which the configuration is + * being set. The value is according to &enum nl80211_band (u8). + * @NL80211_NAN_BAND_CONF_FREQ: Discovery frequency. This attribute shall not + * be present on 2.4 GHZ band. On 5 GHz band its presence is optional. + * The allowed values are 5220 (channel 44) or 5745 (channel 149). + * If not present, channel 149 is used if allowed, otherwise channel 44 + * will be selected. The value is in MHz (u16). + * @NL80211_NAN_BAND_CONF_RSSI_CLOSE: RSSI close threshold used for NAN state + * transition algorithm as described in chapters 3.3.6 and 3.3.7 "NAN + * Device Role and State Transition" of Wi-Fi Aware (TM) Specification + * v4.0. If not specified, default device value is used. The value should + * be greater than -60 dBm (s8). + * @NL80211_NAN_BAND_CONF_RSSI_MIDDLE: RSSI middle threshold used for NAN state + * transition algorithm as described in chapters 3.3.6 and 3.3.7 "NAN + * Device Role and State Transition" of Wi-Fi Aware (TM) Specification + * v4.0. If not present, default device value is used. The value should be + * greater than -75 dBm and less than %NL80211_NAN_BAND_CONF_RSSI_CLOSE + * (s8). + * @NL80211_NAN_BAND_CONF_WAKE_DW: Committed DW information (values 0-5). + * Value 0 means that the device will not wake up during the + * discovery window. Values 1-5 mean that the device will wake up + * during each 2^(n - 1) discovery window, where n is the value of + * this attribute. Setting this attribute to 0 is not allowed on + * 2.4 GHz band (u8). This is an optional parameter (default is 1). + * @NL80211_NAN_BAND_CONF_DISABLE_SCAN: Optional flag attribute to disable + * scanning (for cluster merge) on the band. If set, the device will not + * scan on this band anymore. Disabling scanning on 2.4 GHz band is not + * allowed. + * @NUM_NL80211_NAN_BAND_CONF_ATTR: Internal. + * @NL80211_NAN_BAND_CONF_ATTR_MAX: Highest NAN band configuration attribute. + * + * These attributes are used to configure NAN band-specific parameters. Note, + * that both RSSI attributes should be configured (or both left unset). + */ +enum nl80211_nan_band_conf_attributes { + __NL80211_NAN_BAND_CONF_INVALID, + NL80211_NAN_BAND_CONF_BAND, + NL80211_NAN_BAND_CONF_FREQ, + NL80211_NAN_BAND_CONF_RSSI_CLOSE, + NL80211_NAN_BAND_CONF_RSSI_MIDDLE, + NL80211_NAN_BAND_CONF_WAKE_DW, + NL80211_NAN_BAND_CONF_DISABLE_SCAN, + + /* keep last */ + NUM_NL80211_NAN_BAND_CONF_ATTR, + NL80211_NAN_BAND_CONF_ATTR_MAX = NUM_NL80211_NAN_BAND_CONF_ATTR - 1, +}; + +/** + * enum nl80211_nan_conf_attributes - NAN configuration attributes + * @__NL80211_NAN_CONF_INVALID: Invalid attribute, used for validation. + * @NL80211_NAN_CONF_CLUSTER_ID: ID for the NAN cluster. This is a MAC + * address that can take values from 50-6F-9A-01-00-00 to + * 50-6F-9A-01-FF-FF. This attribute is optional. If not present, + * a random Cluster ID will be chosen. + * This attribute will be ignored in NL80211_CMD_CHANGE_NAN_CONFIG + * since after NAN was started, the cluster ID can no longer change. + * @NL80211_NAN_CONF_EXTRA_ATTRS: Additional NAN attributes to be + * published in the beacons. This is an optional byte array. + * @NL80211_NAN_CONF_VENDOR_ELEMS: Vendor-specific elements that will + * be published in the beacons. This is an optional byte array. + * @NL80211_NAN_CONF_BAND_CONFIGS: This is a nested array attribute, + * containing multiple entries for each supported band. Each band + * configuration consists of &enum nl80211_nan_band_conf_attributes. + * @NL80211_NAN_CONF_SCAN_PERIOD: Scan period in seconds. If not configured, + * device default is used. Zero value will disable scanning. + * This is u16 (optional). + * @NL80211_NAN_CONF_SCAN_DWELL_TIME: Scan dwell time in TUs per channel. + * Only non-zero values are valid. If not configured the device default + * value is used. This is u16 (optional) + * @NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL: Discovery beacon interval + * in TUs. Valid range is 50-200 TUs. If not configured the device default + * value is used. This is u8 (optional) + * @NL80211_NAN_CONF_NOTIFY_DW: If set, the driver will notify userspace about + * the upcoming discovery window with + * %NL80211_CMD_NAN_NEXT_DW_NOTIFICATION. + * This is a flag attribute. + * @NUM_NL80211_NAN_CONF_ATTR: Internal. + * @NL80211_NAN_CONF_ATTR_MAX: Highest NAN configuration attribute. + * + * These attributes are used to configure NAN-specific parameters. + */ +enum nl80211_nan_conf_attributes { + __NL80211_NAN_CONF_INVALID, + NL80211_NAN_CONF_CLUSTER_ID, + NL80211_NAN_CONF_EXTRA_ATTRS, + NL80211_NAN_CONF_VENDOR_ELEMS, + NL80211_NAN_CONF_BAND_CONFIGS, + NL80211_NAN_CONF_SCAN_PERIOD, + NL80211_NAN_CONF_SCAN_DWELL_TIME, + NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL, + NL80211_NAN_CONF_NOTIFY_DW, + + /* keep last */ + NUM_NL80211_NAN_CONF_ATTR, + NL80211_NAN_CONF_ATTR_MAX = NUM_NL80211_NAN_CONF_ATTR - 1, +}; + +/** + * enum nl80211_external_auth_action - Action to perform with external * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. * @NL80211_EXTERNAL_AUTH_START: Start the authentication. * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. @@ -7068,7 +7840,7 @@ enum nl80211_external_auth_action { * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10), * i.e. starting with the measurement token - * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element + * @NL80211_FTM_RESP_ATTR_CIVICLOC: The content of Measurement Report Element * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13), * i.e. starting with the measurement token * @__NL80211_FTM_RESP_ATTR_LAST: Internal @@ -7246,6 +8018,26 @@ enum nl80211_peer_measurement_resp { NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1 }; +/** + * enum nl80211_peer_measurement_ftm_req_type - FTM ranging request type, + * used with %NL80211_PMSR_PEER_ATTR_REQ_TYPE + * + * @NL80211_PMSR_FTM_REQ_TYPE_INFRA: infrastructure ranging, i.e. STA-to-AP + * @NL80211_PMSR_FTM_REQ_TYPE_PD: peer-to-peer ranging as defined in the + * Wi-Fi Alliance specification "Proximity Ranging (PR) Implementation + * Consideration Draft 1.9 Rev 1" + * @NUM_NL80211_PMSR_FTM_REQ_TYPE: internal + * @NL80211_PMSR_FTM_REQ_TYPE_MAX: highest request type value + */ +enum nl80211_peer_measurement_ftm_req_type { + NL80211_PMSR_FTM_REQ_TYPE_INFRA, + NL80211_PMSR_FTM_REQ_TYPE_PD, + + /* keep last */ + NUM_NL80211_PMSR_FTM_REQ_TYPE, + NL80211_PMSR_FTM_REQ_TYPE_MAX = NUM_NL80211_PMSR_FTM_REQ_TYPE - 1 +}; + /** * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid @@ -7259,6 +8051,9 @@ enum nl80211_peer_measurement_resp { * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by * measurement type, with attributes from the * &enum nl80211_peer_measurement_resp inside. + * @NL80211_PMSR_PEER_ATTR_REQ_TYPE: u32 attribute specifying the ranging + * request type, using values from &enum nl80211_peer_measurement_ftm_req_type. + * If absent, defaults to %NL80211_PMSR_FTM_REQ_TYPE_INFRA. * * @NUM_NL80211_PMSR_PEER_ATTRS: internal * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number @@ -7270,6 +8065,7 @@ enum nl80211_peer_measurement_peer_attrs { NL80211_PMSR_PEER_ATTR_CHAN, NL80211_PMSR_PEER_ATTR_REQ, NL80211_PMSR_PEER_ATTR_RESP, + NL80211_PMSR_PEER_ATTR_REQ_TYPE, /* keep last */ NUM_NL80211_PMSR_PEER_ATTRS, @@ -7296,6 +8092,18 @@ enum nl80211_peer_measurement_peer_attrs { * meaningless, just a list of peers to measure with, with the * sub-attributes taken from * &enum nl80211_peer_measurement_peer_attrs. + * @NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE: u32 attribute indicating the + * maximum number of peers supported when the device operates in the + * ISTA (Initiator STA) role. If absent, no role-specific peer limit + * applies. The sum of %NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE and + * %NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE is enforced when the device + * supports concurrent ISTA/RSTA operation. + * @NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE: u32 attribute indicating the + * maximum number of peers supported when the device operates in the + * RSTA (Responder STA) role. If absent, no role-specific peer limit + * applies. The sum of %NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE and + * %NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE is enforced when the device + * supports concurrent ISTA/RSTA operation. * * @NUM_NL80211_PMSR_ATTR: internal * @NL80211_PMSR_ATTR_MAX: highest attribute number @@ -7308,6 +8116,8 @@ enum nl80211_peer_measurement_attrs { NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, NL80211_PMSR_ATTR_TYPE_CAPA, NL80211_PMSR_ATTR_PEERS, + NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE, + NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE, /* keep last */ NUM_NL80211_PMSR_ATTR, @@ -7341,7 +8151,79 @@ enum nl80211_peer_measurement_attrs { * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if * trigger based ranging measurement is supported * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating - * if non trigger based ranging measurement is supported + * if non-trigger-based ranging measurement is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_6GHZ_SUPPORT: flag attribute indicating if + * ranging on the 6 GHz band is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP: u32 attribute indicating + * the maximum number of LTF repetitions the device can transmit in the + * preamble of the ranging NDP (zero means only one LTF, no repetitions) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP: u32 attribute indicating + * the maximum number of LTF repetitions the device can receive in the + * preamble of the ranging NDP (zero means only one LTF, no repetitions) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS: u32 attribute indicating + * the maximum number of space-time streams supported for ranging NDP TX + * (zero-based) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS: u32 attribute indicating + * the maximum number of space-time streams supported for ranging NDP RX + * (zero-based) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX: u32 attribute indicating the + * maximum total number of LTFs the device can transmit. The total number + * of LTFs is (number of LTF repetitions) * (number of space-time streams). + * This limits the allowed combinations of LTF repetitions and STS. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX: u32 attribute indicating the + * maximum total number of LTFs the device can receive. The total number + * of LTFs is (number of LTF repetitions) * (number of space-time streams). + * This limits the allowed combinations of LTF repetitions and STS. + * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT: flag attribute indicating the + * device supports operating as the RSTA in PMSR FTM request + * @NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS: nested attribute containing ISTA + * (initiator) role capabilities. Uses the same sub-attributes as + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS. + * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS: nested attribute containing RSTA + * (responder) role capabilities. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating NTB ranging support. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating TB ranging support. + * @NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA: flag attribute (used inside + * %NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS or + * %NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS) indicating EDCA based ranging + * support. + * @NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS: nested attribute containing ranging + * type capabilities. Uses sub-attributes from + * &enum nl80211_peer_measurement_ftm_type_capa. + * @NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT: flag attribute + * indicating that the device can simultaneously act as initiator and + * responder in a multi-peer measurement request. Only valid if + * @NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT is set. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS: u32 attribute indicating + * the maximum number of transmit antennas supported for EDCA based ranging + * (0 means unknown) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS: u32 attribute indicating + * the maximum number of receive antennas supported for EDCA based ranging + * (0 means unknown) + * @NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA: u32 attribute indicating + * the minimum EDCA ranging interval supported by the device + * in milli seconds. (0 means unknown). Applications can use this value + * to estimate the burst period to be given in the FTM request for the + * EDCA based ranging case. If non-zero, this value will be used to + * validate the burst period in the FTM request. + * @NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB: u32 attribute indicating + * the minimum NTB ranging interval supported by the device + * in milli seconds. (0 means unknown). Applications can use this value + * to estimate the burst period to be given in the FTM request for the + * NTB ranging case. If non-zero, this value will be used to validate + * the nominal time in the FTM request. + * @NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES: u32 bitmap of values from + * &enum nl80211_preamble indicating the supported preambles for PD + * ranging requests. Only valid if %NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT + * is set. + * @NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS: u32 bitmap of values from + * &enum nl80211_chan_width indicating the supported channel bandwidths + * for PD ranging requests. Only valid if + * %NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT is set. * * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number @@ -7359,12 +8241,60 @@ enum nl80211_peer_measurement_ftm_capa { NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED, NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED, + NL80211_PMSR_FTM_CAPA_ATTR_6GHZ_SUPPORT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX, + NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT, + NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB, + NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA, + NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS, + NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS, + NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA, + NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB, + NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES, + NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS, /* keep last */ NUM_NL80211_PMSR_FTM_CAPA_ATTR, NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1 }; +/** + * enum nl80211_peer_measurement_ftm_type_capa - FTM ranging type capability + * sub-attributes, used inside %NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS + * @__NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT: flag attribute indicating + * that the device supports infrastructure ranging (STA-to-AP or + * AP-to-STA) as part of Proximity Detection + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT: flag attribute indicating that + * the device supports peer-to-peer ranging as mentioned in the + * specification "PR Implementation Consideration Draft 1.9 rev 1" where + * PD stands for proximity detection + * + * @NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR: internal + * @NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_type_capa { + __NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INVALID, + + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT, + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT, + + /* keep last */ + NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR, + NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_TYPE_CAPA_ATTR - 1 +}; + /** * enum nl80211_peer_measurement_ftm_req - FTM request attributes * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid @@ -7374,15 +8304,20 @@ enum nl80211_peer_measurement_ftm_capa { * &enum nl80211_preamble), optional for DMG (u32) * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element" - * (u8, 0-15, optional with default 15 i.e. "no preference") + * (u8, 0-15, optional with default 15 i.e. "no preference". No limit for + * non-EDCA ranging) * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units * of 100ms (u16, optional with default 0) * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016 * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with - * default 15 i.e. "no preference") - * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames - * requested per burst + * default 15 i.e. "no preference"). For non-EDCA ranging, this is the + * burst duration in milliseconds (optional with default 0, i.e. let the + * device decide). + * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: (Optional) number of successful + * FTM frames requested per burst * (u8, 0-31, optional with default 0 i.e. "no preference") + * If the attribute is absent ("no preference"), the driver or firmware can + * choose a suitable value. * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries * (u8, default 3) * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag) @@ -7395,7 +8330,7 @@ enum nl80211_peer_measurement_ftm_capa { * if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based * ranging will be used. - * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based + * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non-trigger-based * ranging measurement (flag) * This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are * mutually exclusive. @@ -7408,6 +8343,58 @@ enum nl80211_peer_measurement_ftm_capa { * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the * responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_RSTA: optional. Request to perform the measurement + * as the RSTA (flag). When set, the device is expected to dwell on the + * channel specified in %NL80211_PMSR_PEER_ATTR_CHAN until it receives the + * FTM request from the peer or the timeout specified by + * %NL80211_ATTR_TIMEOUT has expired. + * Only valid if %NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK is set (so the + * RSTA will have the measurement results to report back in the FTM + * response). + * @NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS: minimum time + * between two consecutive range measurements in units of 100 microseconds, + * for non-trigger based ranging (u32). Should be set as short as possible + * to minimize turnaround time, since two-way ranging with delayed LMR + * requires two measurements. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS: maximum time + * between two consecutive range measurements in units of 10 milliseconds, + * for non-trigger based ranging (u32). Acts as a session timeout; if + * exceeded, the ranging session should be terminated. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME: The nominal time field shall be + * set to the nominal duration between adjacent Availability Windows in + * units of milli seconds (u32). Mandatory if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * @NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION: (Optional) The AW duration field + * shall be set to the duration of the AW in units of 1ms (0-255 ms) (u32). + * Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * If the attribute is absent ("no preference"), the driver or firmware + * can choose a suitable value. + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS: (Optional) number of + * Availability Windows (AWs) to schedule for non-trigger-based ranging. + * Each AW may contain multiple FTM exchanges as configured by + * %NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST. Only valid if + * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set. + * If the attribute is absent ("no preference"), the driver or firmware + * can choose a suitable value. + * @NL80211_PMSR_FTM_REQ_ATTR_PAD: ignore, for u64/s64 padding only. + * @NL80211_PMSR_FTM_REQ_ATTR_INGRESS: optional u64 attribute in units of mm. + * When specified, the measurement result of the peer needs to be + * indicated if the device moves into this range. + * @NL80211_PMSR_FTM_REQ_ATTR_EGRESS: optional u64 attribute in units of mm. + * When specified, the measurement result of the peer needs to be + * indicated if the device moves out of this range. + * If neither or only one of @NL80211_PMSR_FTM_REQ_ATTR_INGRESS and + * @NL80211_PMSR_FTM_REQ_ATTR_EGRESS is specified, only the specified + * threshold is used. If both are specified, both thresholds are applied. + * If neither is specified, results are reported without threshold + * filtering. + * @NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS: Flag to suppress ranging + * results for PD requests. When set, ranging measurements are performed + * but results are not reported to userspace, regardless of ranging role + * or type. Only valid when %NL80211_PMSR_PEER_ATTR_REQ_TYPE is set to + * %NL80211_PMSR_FTM_REQ_TYPE_PD. * * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number @@ -7428,6 +8415,16 @@ enum nl80211_peer_measurement_ftm_req { NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED, NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK, NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, + NL80211_PMSR_FTM_REQ_ATTR_RSTA, + NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME, + NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION, + NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS, + NL80211_PMSR_FTM_REQ_ATTR_PAD, + NL80211_PMSR_FTM_REQ_ATTR_INGRESS, + NL80211_PMSR_FTM_REQ_ATTR_EGRESS, + NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS, /* keep last */ NUM_NL80211_PMSR_FTM_REQ_ATTR, @@ -7473,7 +8470,7 @@ enum nl80211_peer_measurement_ftm_failure_reasons { * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames * transmitted (u32, optional) * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames - * that were acknowleged (u32, optional) + * that were acknowledged (u32, optional) * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the * busy peer (u32, seconds) * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent @@ -7512,6 +8509,35 @@ enum nl80211_peer_measurement_ftm_failure_reasons { * 9.4.2.22.1) starting with the Measurement Token, with Measurement * Type 11. * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD: actual burst period used by + * the responder (similar to request, u16) + * @NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT: negotiated value of + * number of tx ltf repetitions in NDP frames (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT: negotiated value of + * number of rx ltf repetitions in NDP frames (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS: negotiated value + * where latest time by which the ISTA needs to complete the next round of + * measurements, in units of 10 ms (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS: negotiated + * minimum time between two consecutive range measurements initiated by an + * ISTA, in units of 100 us (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS: number of Tx space-time + * streams used in NDP frames during the measurement sounding phase + * (u32, optional). + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS: number of Rx space-time + * streams used in the NDP frames during the measurement sounding phase + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME: negotiated nominal time used in + * this session in milliseconds. (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW: negotiated availability + * window time used in this session, in units of milli seconds. + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH: u32 attribute indicating channel + * width used for measurement, see &enum nl80211_chan_width (optional). + * @NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE: u32 attribute indicating the preamble + * type used for the measurement, see &enum nl80211_preamble (optional). + * @NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR: flag, indicates if the + * current result is delayed LMR data. * * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number @@ -7540,6 +8566,18 @@ enum nl80211_peer_measurement_ftm_resp { NL80211_PMSR_FTM_RESP_ATTR_LCI, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, NL80211_PMSR_FTM_RESP_ATTR_PAD, + NL80211_PMSR_FTM_RESP_ATTR_BURST_PERIOD, + NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT, + NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT, + NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS, + NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME, + NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW, + NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH, + NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE, + NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR, /* keep last */ NUM_NL80211_PMSR_FTM_RESP_ATTR, @@ -7711,6 +8749,7 @@ enum nl80211_sae_pwe_mechanism { * * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit * + * @NUM_NL80211_SAR_TYPE: internal */ enum nl80211_sar_type { NL80211_SAR_TYPE_POWER, @@ -7724,6 +8763,8 @@ enum nl80211_sar_type { /** * enum nl80211_sar_attrs - Attributes for SAR spec * + * @__NL80211_SAR_ATTR_INVALID: Invalid + * * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type. * * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power @@ -7755,6 +8796,8 @@ enum nl80211_sar_attrs { /** * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs * + * @__NL80211_SAR_ATTR_SPECS_INVALID: Invalid + * * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual * power limit value in units of 0.25 dBm if type is * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm). @@ -7836,6 +8879,11 @@ enum nl80211_sar_specs_attrs { * Setting this flag is permitted only if the driver advertises EMA support * by setting wiphy->ema_max_profile_periodicity to non-zero. * + * @NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID: Link ID of the transmitted profile. + * This parameter is mandatory when NL80211_ATTR_MBSSID_CONFIG attributes + * are sent for a non-transmitted profile and if the transmitted profile + * is part of an MLD. For all other cases this parameter is unnecessary. + * * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute */ @@ -7847,6 +8895,7 @@ enum nl80211_mbssid_config_attributes { NL80211_MBSSID_CONFIG_ATTR_INDEX, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX, NL80211_MBSSID_CONFIG_ATTR_EMA, + NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID, /* keep last */ __NL80211_MBSSID_CONFIG_ATTR_LAST, @@ -7869,4 +8918,163 @@ enum nl80211_ap_settings_flags { NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1, }; +/** + * enum nl80211_wiphy_radio_attrs - wiphy radio attributes + * + * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid + * + * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32) + * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this + * radio. Attribute may be present multiple times. + * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface + * combination for this radio. Attribute may be present multiple times + * and contains attributes defined in &enum nl80211_if_combination_attrs. + * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas + * connected to this radio. + * @NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD: RTS threshold (u32) of this radio. + * + * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal + * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute + */ +enum nl80211_wiphy_radio_attrs { + __NL80211_WIPHY_RADIO_ATTR_INVALID, + + NL80211_WIPHY_RADIO_ATTR_INDEX, + NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, + NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, + NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD, + + /* keep last */ + __NL80211_WIPHY_RADIO_ATTR_LAST, + NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1, +}; + +/** + * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range + * + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid + * + * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32). + * The unit is kHz. + * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32). + * The unit is kHz. + * + * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal + * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute + */ +enum nl80211_wiphy_radio_freq_range { + __NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID, + + NL80211_WIPHY_RADIO_FREQ_ATTR_START, + NL80211_WIPHY_RADIO_FREQ_ATTR_END, + + __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST, + NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1, +}; + +/** + * enum nl80211_s1g_short_beacon_attrs - S1G short beacon data + * + * @__NL80211_S1G_SHORT_BEACON_ATTR_INVALID: Invalid + * + * @NL80211_S1G_SHORT_BEACON_ATTR_HEAD: Short beacon head (binary). + * @NL80211_S1G_SHORT_BEACON_ATTR_TAIL: Short beacon tail (binary). + * + * @__NL80211_S1G_SHORT_BEACON_ATTR_LAST: Internal + * @NL80211_S1G_SHORT_BEACON_ATTR_MAX: Highest attribute + */ +enum nl80211_s1g_short_beacon_attrs { + __NL80211_S1G_SHORT_BEACON_ATTR_INVALID, + + NL80211_S1G_SHORT_BEACON_ATTR_HEAD, + NL80211_S1G_SHORT_BEACON_ATTR_TAIL, + + /* keep last */ + __NL80211_S1G_SHORT_BEACON_ATTR_LAST, + NL80211_S1G_SHORT_BEACON_ATTR_MAX = + __NL80211_S1G_SHORT_BEACON_ATTR_LAST - 1 +}; + +/** + * enum nl80211_nan_capabilities - NAN (Neighbor Aware Networking) + * capabilities. + * + * @__NL80211_NAN_CAPABILITIES_INVALID: Invalid. + * @NL80211_NAN_CAPA_CONFIGURABLE_SYNC: Flag attribute indicating that + * the device supports configurable synchronization. If set, the device + * should be able to handle %NL80211_ATTR_NAN_CONFIG + * attribute in the %NL80211_CMD_START_NAN (and change) command. + * @NL80211_NAN_CAPA_USERSPACE_DE: Flag attribute indicating that + * NAN Discovery Engine (DE) is not offloaded and the driver assumes + * user space DE implementation. When set, %NL80211_CMD_ADD_NAN_FUNCTION, + * %NL80211_CMD_DEL_NAN_FUNCTION and %NL80211_CMD_NAN_MATCH commands + * should not be used. In addition, the device/driver should support + * sending discovery window (DW) notifications using + * %NL80211_CMD_NAN_NEXT_DW_NOTIFICATION and handling transmission and + * reception of NAN SDF frames on NAN device interface during DW windows. + * (%NL80211_CMD_FRAME is used to transmit SDFs) + * @NL80211_NAN_CAPA_OP_MODE: u8 attribute indicating the supported operation + * modes as defined in Wi-Fi Aware (TM) specification Table 81 (Operation + * Mode field format). + * @NL80211_NAN_CAPA_NUM_ANTENNAS: u8 attribute indicating the number of + * TX and RX antennas supported by the device. Lower nibble indicates + * the number of TX antennas and upper nibble indicates the number of RX + * antennas. Value 0 indicates the information is not available. + * See table 79 of Wi-Fi Aware (TM) specification (Number of + * Antennas field). + * @NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME: u16 attribute indicating the + * maximum time in microseconds that the device requires to switch + * channels. + * @NL80211_NAN_CAPA_CAPABILITIES: u8 attribute containing the + * capabilities of the device as defined in Wi-Fi Aware (TM) + * specification Table 79 (Capabilities field). + * @NL80211_NAN_CAPA_PHY: nested attribute containing band-agnostic + * capabilities for NAN data path. See &enum nl80211_nan_phy_cap_attr. + * @__NL80211_NAN_CAPABILITIES_LAST: Internal + * @NL80211_NAN_CAPABILITIES_MAX: Highest NAN capability attribute. + */ +enum nl80211_nan_capabilities { + __NL80211_NAN_CAPABILITIES_INVALID, + + NL80211_NAN_CAPA_CONFIGURABLE_SYNC, + NL80211_NAN_CAPA_USERSPACE_DE, + NL80211_NAN_CAPA_OP_MODE, + NL80211_NAN_CAPA_NUM_ANTENNAS, + NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, + NL80211_NAN_CAPA_CAPABILITIES, + NL80211_NAN_CAPA_PHY, + /* keep last */ + __NL80211_NAN_CAPABILITIES_LAST, + NL80211_NAN_CAPABILITIES_MAX = __NL80211_NAN_CAPABILITIES_LAST - 1, +}; + +/** + * enum nl80211_nan_peer_map_attrs - NAN peer schedule map attributes + * + * Nested attributes used within %NL80211_ATTR_NAN_PEER_MAPS to define + * individual peer schedule maps. + * + * @__NL80211_NAN_PEER_MAP_ATTR_INVALID: Invalid + * @NL80211_NAN_PEER_MAP_ATTR_MAP_ID: (u8) The map ID for this schedule map. + * @NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS: An array of u8 values with 32 cells. + * Each value maps a time slot to a channel index within the schedule's + * channel list (%NL80211_ATTR_NAN_CHANNEL attributes). + * %NL80211_NAN_SCHED_NOT_AVAIL_SLOT indicates unscheduled. + * @__NL80211_NAN_PEER_MAP_ATTR_LAST: Internal + * @NL80211_NAN_PEER_MAP_ATTR_MAX: Highest peer map attribute + */ +enum nl80211_nan_peer_map_attrs { + __NL80211_NAN_PEER_MAP_ATTR_INVALID, + + NL80211_NAN_PEER_MAP_ATTR_MAP_ID, + NL80211_NAN_PEER_MAP_ATTR_TIME_SLOTS, + + /* keep last */ + __NL80211_NAN_PEER_MAP_ATTR_LAST, + NL80211_NAN_PEER_MAP_ATTR_MAX = __NL80211_NAN_PEER_MAP_ATTR_LAST - 1, +}; + +#define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff + #endif /* __LINUX_NL80211_H */ diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c index ff8ad8d72a9d..fa7ecd01e443 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.c +++ b/contrib/wpa/src/eap_common/eap_pwd_common.c @@ -76,7 +76,7 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } /* since we're expanding to a bit length, mask off the excess */ - if (resultbitlen % 8) { + if (resultbytelen > 0 && (resultbitlen % 8)) { u8 mask = 0xff; mask <<= (8 - (resultbitlen % 8)); result[resultbytelen - 1] &= mask; diff --git a/contrib/wpa/src/eap_common/eap_teap_common.c b/contrib/wpa/src/eap_common/eap_teap_common.c index ffb9a6234243..aeb0e69b6e12 100644 --- a/contrib/wpa/src/eap_common/eap_teap_common.c +++ b/contrib/wpa/src/eap_common/eap_teap_common.c @@ -118,32 +118,7 @@ int eap_teap_derive_eap_emsk(u16 tls_cs, const u8 *simck, u8 *emsk) } -int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk, u8 *cmk) -{ - u8 imsk[32], imck[EAP_TEAP_IMCK_LEN]; - int res; - - /* FIX: The Basic-Password-Auth (i.e., no inner EAP) case is - * not fully defined in RFC 7170, so this CMK derivation may - * need to be changed if a fixed definition is eventually - * published. For now, derive CMK[0] based on S-IMCK[0] and - * IMSK of 32 octets of zeros. */ - os_memset(imsk, 0, 32); - res = eap_teap_tls_prf(tls_cs, s_imck_msk, EAP_TEAP_SIMCK_LEN, - "Inner Methods Compound Keys", - imsk, 32, imck, sizeof(imck)); - if (res < 0) - return -1; - os_memcpy(cmk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: CMK[no-inner-EAP]", - cmk, EAP_TEAP_CMK_LEN); - forced_memzero(imck, sizeof(imck)); - return 0; -} - - -int eap_teap_derive_imck(u16 tls_cs, - const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk, +int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck, const u8 *msk, size_t msk_len, const u8 *emsk, size_t emsk_len, u8 *s_imck_msk, u8 *cmk_msk, @@ -186,7 +161,7 @@ int eap_teap_derive_imck(u16 tls_cs, imsk, 32); res = eap_teap_tls_prf(tls_cs, - prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN, + prev_s_imck, EAP_TEAP_SIMCK_LEN, "Inner Methods Compound Keys", imsk, 32, imck, EAP_TEAP_IMCK_LEN); forced_memzero(imsk, sizeof(imsk)); @@ -216,7 +191,7 @@ int eap_teap_derive_imck(u16 tls_cs, wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Zero IMSK", imsk, 32); } - res = eap_teap_tls_prf(tls_cs, prev_s_imck_msk, EAP_TEAP_SIMCK_LEN, + res = eap_teap_tls_prf(tls_cs, prev_s_imck, EAP_TEAP_SIMCK_LEN, "Inner Methods Compound Keys", imsk, 32, imck, EAP_TEAP_IMCK_LEN); forced_memzero(imsk, sizeof(imsk)); @@ -342,10 +317,6 @@ static int eap_teap_tls_mac(u16 tls_cs, const u8 *cmk, size_t cmk_len, if (res < 0) return res; - /* FIX: RFC 7170 does not describe how to handle truncation of the - * Compound MAC or if the fields are supposed to be of variable length - * based on the negotiated TLS cipher suite (they are defined as having - * fixed size of 20 octets in the TLV description) */ if (mac_len > sizeof(tmp)) mac_len = sizeof(tmp); os_memcpy(mac, tmp, mac_len); @@ -704,41 +675,3 @@ struct wpabuf * eap_teap_tlv_identity_type(enum teap_identity_types id) wpabuf_put_be16(buf, id); return buf; } - - -int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type) -{ - /* RFC 7170, Section 3.8.3: MUST provide mutual authentication, - * provide key generation, and be resistant to dictionary attack. - * Section 3.8 also mentions requirement for using EMSK Compound MAC. */ - return vendor == EAP_VENDOR_IETF && - (type == EAP_TYPE_PWD || type == EAP_TYPE_EKE); -} - - -int eap_teap_allowed_anon_prov_cipher_suite(u16 cs) -{ - /* RFC 7170, Section 3.8.3: anonymous ciphersuites MAY be supported as - * long as the TLS pre-master secret is generated form contribution from - * both peers. Accept the recommended TLS_DH_anon_WITH_AES_128_CBC_SHA - * cipher suite and other ciphersuites that use DH in some form, have - * SHA-1 or stronger MAC function, and use reasonable strong cipher. */ - static const u16 ok_cs[] = { - /* DH-anon */ - 0x0034, 0x003a, 0x006c, 0x006d, 0x00a6, 0x00a7, - /* DHE-RSA */ - 0x0033, 0x0039, 0x0067, 0x006b, 0x009e, 0x009f, - /* ECDH-anon */ - 0xc018, 0xc019, - /* ECDH-RSA */ - 0xc003, 0xc00f, 0xc029, 0xc02a, 0xc031, 0xc032, - /* ECDH-ECDSA */ - 0xc004, 0xc005, 0xc025, 0xc026, 0xc02d, 0xc02e, - /* ECDHE-RSA */ - 0xc013, 0xc014, 0xc027, 0xc028, 0xc02f, 0xc030, - /* ECDHE-ECDSA */ - 0xc009, 0xc00a, 0xc023, 0xc024, 0xc02b, 0xc02c, - }; - - return tls_cipher_suite_match(ok_cs, ARRAY_SIZE(ok_cs), cs); -} diff --git a/contrib/wpa/src/eap_common/eap_teap_common.h b/contrib/wpa/src/eap_common/eap_teap_common.h index 3a25879499a0..cbf131582e1d 100644 --- a/contrib/wpa/src/eap_common/eap_teap_common.h +++ b/contrib/wpa/src/eap_common/eap_teap_common.h @@ -205,10 +205,7 @@ void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type, struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf); int eap_teap_derive_eap_msk(u16 tls_cs, const u8 *simck, u8 *msk); int eap_teap_derive_eap_emsk(u16 tls_cs, const u8 *simck, u8 *emsk); -int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk, - u8 *cmk); -int eap_teap_derive_imck(u16 tls_cs, - const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk, +int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck, const u8 *msk, size_t msk_len, const u8 *emsk, size_t emsk_len, u8 *s_imck_msk, u8 *cmk_msk, @@ -224,7 +221,5 @@ struct wpabuf * eap_teap_tlv_result(int status, int intermediate); struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error); struct wpabuf * eap_teap_tlv_identity_type(enum teap_identity_types id); enum eap_type; -int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type); -int eap_teap_allowed_anon_prov_cipher_suite(u16 cs); #endif /* EAP_TEAP_H */ diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c index 90fb89e243b8..b37175c14d60 100644 --- a/contrib/wpa/src/eap_common/ikev2_common.c +++ b/contrib/wpa/src/eap_common/ikev2_common.c @@ -10,7 +10,9 @@ #include "common.h" #include "crypto/crypto.h" +#ifndef CONFIG_FIPS #include "crypto/md5.h" +#endif /* CONFIG_FIPS */ #include "crypto/sha1.h" #include "crypto/random.h" #include "ikev2_common.h" @@ -18,7 +20,9 @@ static const struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_SHA1_96, 20, 12 }, +#ifndef CONFIG_FIPS { AUTH_HMAC_MD5_96, 16, 12 } +#endif /* CONFIG_FIPS */ }; #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) @@ -26,7 +30,9 @@ static const struct ikev2_integ_alg ikev2_integ_algs[] = { static const struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_SHA1, 20, 20 }, +#ifndef CONFIG_FIPS { PRF_HMAC_MD5, 16, 16 } +#endif /* CONFIG_FIPS */ }; #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) @@ -34,7 +40,8 @@ static const struct ikev2_prf_alg ikev2_prf_algs[] = { static const struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ - { ENCR_3DES, 24, 8 } + { ENCR_3DES, 24, 8 }, + { ENCR_NULL, 0, 0 } }; #define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) @@ -66,6 +73,7 @@ int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, return -1; os_memcpy(hash, tmphash, 12); break; +#ifndef CONFIG_FIPS case AUTH_HMAC_MD5_96: if (key_len != 16) return -1; @@ -73,6 +81,7 @@ int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, return -1; os_memcpy(hash, tmphash, 12); break; +#endif /* CONFIG_FIPS */ default: return -1; } @@ -102,8 +111,10 @@ int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, case PRF_HMAC_SHA1: return hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); +#ifndef CONFIG_FIPS case PRF_HMAC_MD5: return hmac_md5_vector(key, key_len, num_elem, addr, len, hash); +#endif /* CONFIG_FIPS */ default: return -1; } @@ -185,6 +196,9 @@ int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, case ENCR_AES_CBC: encr_alg = CRYPTO_CIPHER_ALG_AES; break; + case ENCR_NULL: + encr_alg = CRYPTO_CIPHER_NULL; + break; default: wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); return -1; @@ -220,6 +234,9 @@ int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, case ENCR_AES_CBC: encr_alg = CRYPTO_CIPHER_ALG_AES; break; + case ENCR_NULL: + encr_alg = CRYPTO_CIPHER_NULL; + break; default: wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); return -1; @@ -577,9 +594,15 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, return -1; } - pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; - if (pad_len == iv_len) + if (iv_len) { + pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; + if (pad_len == iv_len) + pad_len = 0; + } else { + /* Avoid padding when not necessary */ pad_len = 0; + } + wpabuf_put(plain, pad_len); wpabuf_put_u8(plain, pad_len); diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index 935286242fd8..37d3ecefde80 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -313,11 +313,27 @@ SM_STATE(EAP, RECEIVED) eapReqData = eapol_get_eapReqData(sm); /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ eap_sm_parseEapReq(sm, eapReqData); - sm->num_rounds++; - if (!eapReqData || wpabuf_len(eapReqData) < 20) - sm->num_rounds_short++; - else - sm->num_rounds_short = 0; + + /* + * Only increment the round counters if: + * 1. The request is not an EAP-Identity (i.e., it's a specific EAP + * method). + * 2. Or, an EAP method has already been selected (i.e., we are in the + * middle of a negotiation session). + * + * This avoids incrementing counters for periodic Identity Requests used + * as keep-alive mechanisms in some wired IEEE 802.1X networks. Without + * this, repeated short Identity heartbeats would eventually trigger a + * spurious EAP failure after exceeding EAP_MAX_AUTH_ROUNDS_SHORT. + */ + if (sm->selectedMethod != EAP_TYPE_NONE || + sm->reqMethod != EAP_TYPE_IDENTITY) { + sm->num_rounds++; + if (!eapReqData || wpabuf_len(eapReqData) < 20) + sm->num_rounds_short++; + else + sm->num_rounds_short = 0; + } } diff --git a/contrib/wpa/src/eap_peer/eap_config.h b/contrib/wpa/src/eap_peer/eap_config.h index 58d5a135907f..4e06e413c702 100644 --- a/contrib/wpa/src/eap_peer/eap_config.h +++ b/contrib/wpa/src/eap_peer/eap_config.h @@ -712,8 +712,6 @@ struct eap_peer_config { EXT_CERT_CHECK_GOOD, EXT_CERT_CHECK_BAD, } pending_ext_cert_check; - - int teap_anon_dh; }; diff --git a/contrib/wpa/src/eap_peer/eap_leap.c b/contrib/wpa/src/eap_peer/eap_leap.c index 02daddfb4413..47c069a07917 100644 --- a/contrib/wpa/src/eap_peer/eap_leap.c +++ b/contrib/wpa/src/eap_peer/eap_leap.c @@ -386,7 +386,10 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) elen[3] = LEAP_CHALLENGE_LEN; addr[4] = data->peer_response; elen[4] = LEAP_RESPONSE_LEN; - md5_vector(5, addr, elen, key); + if (md5_vector(5, addr, elen, key)) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); *len = LEAP_KEY_LEN; diff --git a/contrib/wpa/src/eap_peer/eap_teap.c b/contrib/wpa/src/eap_peer/eap_teap.c index ced7b164292c..b9c1ece31fcb 100644 --- a/contrib/wpa/src/eap_peer/eap_teap.c +++ b/contrib/wpa/src/eap_peer/eap_teap.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TEAP (RFC 7170) - * Copyright (c) 2004-2019, Jouni Malinen + * Copyright (c) 2004-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,11 +14,6 @@ #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" -#include "eap_teap_pac.h" - -#ifdef EAP_TEAP_DYNAMIC -#include "eap_teap_pac.c" -#endif /* EAP_TEAP_DYNAMIC */ static void eap_teap_deinit(struct eap_sm *sm, void *priv); @@ -43,13 +38,6 @@ struct eap_teap_data { struct eap_method_type *phase2_types; size_t num_phase2_types; int resuming; /* starting a resumed session */ -#define EAP_TEAP_PROV_UNAUTH 1 -#define EAP_TEAP_PROV_AUTH 2 - int provisioning_allowed; /* Allowed PAC provisioning modes */ - int provisioning; /* doing PAC provisioning (not the normal auth) */ - int anon_provisioning; /* doing anonymous (unauthenticated) - * provisioning */ - int session_ticket_used; int test_outer_tlvs; u8 key_data[EAP_TEAP_KEY_LEN]; @@ -58,96 +46,34 @@ struct eap_teap_data { u8 emsk[EAP_EMSK_LEN]; int success; - struct eap_teap_pac *pac; - struct eap_teap_pac *current_pac; - size_t max_pac_list_len; - int use_pac_binary_format; - + u8 simck[EAP_TEAP_SIMCK_LEN]; u8 simck_msk[EAP_TEAP_SIMCK_LEN]; u8 simck_emsk[EAP_TEAP_SIMCK_LEN]; int simck_idx; - int cmk_emsk_available; + bool cmk_emsk_available; struct wpabuf *pending_phase2_req; struct wpabuf *pending_resp; struct wpabuf *server_outer_tlvs; struct wpabuf *peer_outer_tlvs; + + enum teap_compat { + TEAP_DEFAULT, + TEAP_FREERADIUS, + } teap_compat; }; -static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, - const u8 *client_random, - const u8 *server_random, - u8 *master_secret) -{ - struct eap_teap_data *data = ctx; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback"); - - if (!master_secret) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: SessionTicket failed - fall back to full TLS handshake"); - data->session_ticket_used = 0; - if (data->provisioning_allowed) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Try to provision a new PAC-Key"); - data->provisioning = 1; - data->current_pac = NULL; - } - return 0; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len); - - if (!data->current_pac) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: No PAC-Key available for using SessionTicket"); - data->session_ticket_used = 0; - return 0; - } - - /* EAP-TEAP uses PAC-Key as the TLS master_secret */ - os_memcpy(master_secret, data->current_pac->pac_key, - EAP_TEAP_PAC_KEY_LEN); - - data->session_ticket_used = 1; - - return 1; -} - - static void eap_teap_parse_phase1(struct eap_teap_data *data, const char *phase1) { - const char *pos; - - pos = os_strstr(phase1, "teap_provisioning="); - if (pos) { - data->provisioning_allowed = atoi(pos + 18); - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Automatic PAC provisioning mode: %d", - data->provisioning_allowed); - } - - pos = os_strstr(phase1, "teap_max_pac_list_len="); - if (pos) { - data->max_pac_list_len = atoi(pos + 22); - if (data->max_pac_list_len == 0) - data->max_pac_list_len = 1; - wpa_printf(MSG_DEBUG, "EAP-TEAP: Maximum PAC list length: %lu", - (unsigned long) data->max_pac_list_len); - } - - if (os_strstr(phase1, "teap_pac_format=binary")) { - data->use_pac_binary_format = 1; - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Using binary format for PAC list"); - } - #ifdef CONFIG_TESTING_OPTIONS if (os_strstr(phase1, "teap_test_outer_tlvs=1")) data->test_outer_tlvs = 1; #endif /* CONFIG_TESTING_OPTIONS */ + + if (os_strstr(phase1, "teap_compat=freeradius")) + data->teap_compat = TEAP_FREERADIUS; } @@ -163,21 +89,10 @@ static void * eap_teap_init(struct eap_sm *sm) if (!data) return NULL; data->teap_version = EAP_TEAP_VERSION; - data->max_pac_list_len = 10; if (config->phase1) eap_teap_parse_phase1(data, config->phase1); - if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) && - !config->cert.ca_cert && !config->cert.ca_path) { - /* Prevent PAC provisioning without mutual authentication - * (either by validating server certificate or by suitable - * inner EAP method). */ - wpa_printf(MSG_INFO, - "EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path"); - data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH; - } - if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, &data->num_phase2_types, 0) < 0) { @@ -188,44 +103,12 @@ static void * eap_teap_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - config->teap_anon_dh = !!(data->provisioning_allowed & - EAP_TEAP_PROV_UNAUTH); if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) { wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL"); eap_teap_deinit(sm, data); return NULL; } - if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, - eap_teap_session_ticket_cb, - data) < 0) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Failed to set SessionTicket callback"); - eap_teap_deinit(sm, data); - return NULL; - } - - if (!config->pac_file) { - wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured"); - eap_teap_deinit(sm, data); - return NULL; - } - - if (data->use_pac_binary_format && - eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { - wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file"); - eap_teap_deinit(sm, data); - return NULL; - } - - if (!data->use_pac_binary_format && - eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) { - wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file"); - eap_teap_deinit(sm, data); - return NULL; - } - eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len); - return data; } @@ -244,6 +127,7 @@ static void eap_teap_clear(struct eap_teap_data *data) data->server_outer_tlvs = NULL; wpabuf_free(data->peer_outer_tlvs); data->peer_outer_tlvs = NULL; + forced_memzero(data->simck, EAP_TEAP_SIMCK_LEN); forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN); forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN); } @@ -252,7 +136,6 @@ static void eap_teap_clear(struct eap_teap_data *data) static void eap_teap_deinit(struct eap_sm *sm, void *priv) { struct eap_teap_data *data = priv; - struct eap_teap_pac *pac, *prev; if (!data) return; @@ -262,25 +145,20 @@ static void eap_teap_deinit(struct eap_sm *sm, void *priv) os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); - pac = data->pac; - prev = NULL; - while (pac) { - prev = pac; - pac = pac->next; - eap_teap_free_pac(prev); - } - os_free(data); } static int eap_teap_derive_msk(struct eap_teap_data *data) { - /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] - * is used in this derivation */ - if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk, + wpa_printf(MSG_DEBUG, "EAP-TEAP: Derive MSK/EMSK (n=%d)", + data->simck_idx); + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: S-IMCK[n]", data->simck, + EAP_TEAP_SIMCK_LEN); + + if (eap_teap_derive_eap_msk(data->tls_cs, data->simck, data->key_data) < 0 || - eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk, + eap_teap_derive_eap_emsk(data->tls_cs, data->simck, data->emsk) < 0) return -1; data->success = 1; @@ -296,13 +174,14 @@ static int eap_teap_derive_key_auth(struct eap_sm *sm, /* RFC 7170, Section 5.1 */ res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn, TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0, - data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck, EAP_TEAP_SIMCK_LEN); if (res) return res; wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: session_key_seed (S-IMCK[0])", - data->simck_msk, EAP_TEAP_SIMCK_LEN); - os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN); data->simck_idx = 0; return 0; } @@ -339,17 +218,6 @@ static int eap_teap_select_phase2_method(struct eap_teap_data *data, { size_t i; - /* TODO: TNC with anonymous provisioning; need to require both - * completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */ - - if (data->anon_provisioning && - !eap_teap_allowed_anon_prov_phase2_method(vendor, type)) { - wpa_printf(MSG_INFO, - "EAP-TEAP: EAP type %u:%u not allowed during unauthenticated provisioning", - vendor, type); - return -1; - } - #ifdef EAP_TNC if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_TNC) { data->phase2_type.vendor = EAP_VENDOR_IETF; @@ -518,28 +386,6 @@ static struct wpabuf * eap_teap_tlv_nak(int vendor_id, int tlv_type) } -static struct wpabuf * eap_teap_tlv_pac_ack(void) -{ - struct wpabuf *buf; - struct teap_tlv_result *res; - struct teap_tlv_pac_ack *ack; - - buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); - if (!buf) - return NULL; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)"); - ack = wpabuf_put(buf, sizeof(*ack)); - ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY); - ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr)); - ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); - ack->pac_len = host_to_be16(2); - ack->result = host_to_be16(TEAP_STATUS_SUCCESS); - - return buf; -} - - static struct wpabuf * eap_teap_add_identity_type(struct eap_sm *sm, struct wpabuf *msg) { @@ -693,18 +539,21 @@ static int eap_teap_write_crypto_binding( sizeof(struct teap_tlv_hdr)); rbind->version = EAP_TEAP_VERSION; rbind->received_version = data->received_version; - /* FIX: RFC 7170 is not clear on which Flags value to use when - * Crypto-Binding TLV is used with Basic-Password-Auth */ - flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC : - TEAP_CRYPTO_BINDING_MSK_CMAC; subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE; + if (cmk_emsk) + flags = TEAP_CRYPTO_BINDING_EMSK_CMAC; + else if (cmk_msk) + flags = TEAP_CRYPTO_BINDING_MSK_CMAC; + else + return -1; rbind->subtype = (flags << 4) | subtype; os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce)); inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); - if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs, + if (cmk_msk && + eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs, data->peer_outer_tlvs, cmk_msk, rbind->msk_compound_mac) < 0) return -1; @@ -740,9 +589,7 @@ static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data, data->simck_idx + 1); if (!data->phase2_method) - return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs, - data->simck_msk, - cmk_msk); + goto out; /* no MSK derived in Basic-Password-Auth */ if (!data->phase2_method || !data->phase2_priv) { wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available"); @@ -773,17 +620,34 @@ static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data, &emsk_len); } - res = eap_teap_derive_imck(data->tls_cs, - data->simck_msk, data->simck_emsk, - msk, msk_len, emsk, emsk_len, - data->simck_msk, cmk_msk, - data->simck_emsk, cmk_emsk); +out: + if (data->teap_compat == TEAP_FREERADIUS) { + u8 tmp_simck[EAP_TEAP_SIMCK_LEN]; + u8 tmp_cmk[EAP_TEAP_CMK_LEN]; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: FreeRADIUS compatibility: use S-IMCK_MSK[j-1] and S-IMCK_EMSK[j-1] based on MSK/EMSK derivations instead of a single selected S-IMCK[j-1]"); + res = eap_teap_derive_imck(data->tls_cs, data->simck_msk, + msk, msk_len, emsk, emsk_len, + data->simck_msk, cmk_msk, + tmp_simck, tmp_cmk); + if (emsk) + res = eap_teap_derive_imck(data->tls_cs, + data->simck_emsk, + msk, msk_len, emsk, emsk_len, + tmp_simck, tmp_cmk, + data->simck_emsk, cmk_emsk); + } else { + res = eap_teap_derive_imck(data->tls_cs, data->simck, + msk, msk_len, emsk, emsk_len, + data->simck_msk, cmk_msk, + data->simck_emsk, cmk_emsk); + } bin_clear_free(msk, msk_len); bin_clear_free(emsk, emsk_len); if (res == 0) { data->simck_idx++; - if (emsk) - data->cmk_emsk_available = 1; + data->cmk_emsk_available = emsk != NULL; } return res; } @@ -802,7 +666,7 @@ static int eap_teap_session_id(struct eap_teap_data *data) data->session_id[0] = EAP_TYPE_TEAP; res = tls_get_tls_unique(data->ssl.conn, data->session_id + 1, max_id_len - 1); - if (res < 0) { + if (res < 0 || (size_t) res >= max_id_len) { os_free(data->session_id); data->session_id = NULL; wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id"); @@ -825,10 +689,12 @@ static struct wpabuf * eap_teap_process_crypto_binding( u8 *pos; u8 cmk_msk[EAP_TEAP_CMK_LEN]; u8 cmk_emsk[EAP_TEAP_CMK_LEN]; + const u8 *cmk_msk_ptr = NULL; const u8 *cmk_emsk_ptr = NULL; int res; size_t len; u8 flags; + bool server_msk, server_emsk; if (eap_teap_validate_crypto_binding(data, cb) < 0 || eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0) @@ -836,9 +702,12 @@ static struct wpabuf * eap_teap_process_crypto_binding( /* Validate received MSK/EMSK Compound MAC */ flags = cb->subtype >> 4; + server_msk = flags == TEAP_CRYPTO_BINDING_MSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC; + server_emsk = flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC; - if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC || - flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) { + if (server_msk) { u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; if (eap_teap_compound_mac(data->tls_cs, cb, @@ -860,9 +729,7 @@ static struct wpabuf * eap_teap_process_crypto_binding( } } - if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || - flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) && - data->cmk_emsk_available) { + if (server_emsk && data->cmk_emsk_available) { u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; if (eap_teap_compound_mac(data->tls_cs, cb, @@ -882,8 +749,6 @@ static struct wpabuf * eap_teap_process_crypto_binding( "EAP-TEAP: EMSK Compound MAC did not match"); return NULL; } - - cmk_emsk_ptr = cmk_emsk; } if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC && @@ -898,6 +763,20 @@ static struct wpabuf * eap_teap_process_crypto_binding( * crypto binding to allow server to complete authentication. */ + if (server_emsk && data->cmk_emsk_available) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK"); + os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN); + cmk_emsk_ptr = cmk_emsk; + } else if (server_msk) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_MSK"); + os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN); + cmk_msk_ptr = cmk_msk; + } else { + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]", + data->simck, EAP_TEAP_SIMCK_LEN); + len = sizeof(struct teap_tlv_crypto_binding); resp = wpabuf_alloc(len); if (!resp) @@ -920,7 +799,7 @@ static struct wpabuf * eap_teap_process_crypto_binding( pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding)); if (eap_teap_write_crypto_binding( data, (struct teap_tlv_crypto_binding *) pos, - cb, cmk_msk, cmk_emsk_ptr) < 0) { + cb, cmk_msk_ptr, cmk_emsk_ptr) < 0) { wpabuf_free(resp); return NULL; } @@ -929,231 +808,6 @@ static struct wpabuf * eap_teap_process_crypto_binding( } -static void eap_teap_parse_pac_tlv(struct eap_teap_pac *entry, int type, - u8 *pos, size_t len, int *pac_key_found) -{ - switch (type & 0x7fff) { - case PAC_TYPE_PAC_KEY: - wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: PAC-Key", pos, len); - if (len != EAP_TEAP_PAC_KEY_LEN) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Invalid PAC-Key length %lu", - (unsigned long) len); - break; - } - *pac_key_found = 1; - os_memcpy(entry->pac_key, pos, len); - break; - case PAC_TYPE_PAC_OPAQUE: - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pos, len); - entry->pac_opaque = pos; - entry->pac_opaque_len = len; - break; - case PAC_TYPE_PAC_INFO: - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len); - entry->pac_info = pos; - entry->pac_info_len = len; - break; - default: - wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d", - type); - break; - } -} - - -static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry, - u8 *pac, size_t pac_len) -{ - struct pac_attr_hdr *hdr; - u8 *pos; - size_t left, len; - int type, pac_key_found = 0; - - pos = pac; - left = pac_len; - - while (left > sizeof(*hdr)) { - hdr = (struct pac_attr_hdr *) pos; - type = be_to_host16(hdr->type); - len = be_to_host16(hdr->len); - pos += sizeof(*hdr); - left -= sizeof(*hdr); - if (len > left) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)", - type, (unsigned long) len, - (unsigned long) left); - return -1; - } - - eap_teap_parse_pac_tlv(entry, type, pos, len, &pac_key_found); - - pos += len; - left -= len; - } - - if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC TLV does not include all the required fields"); - return -1; - } - - return 0; -} - - -static int eap_teap_parse_pac_info(struct eap_teap_pac *entry, int type, - u8 *pos, size_t len) -{ - u16 pac_type; - u32 lifetime; - struct os_time now; - - switch (type & 0x7fff) { - case PAC_TYPE_CRED_LIFETIME: - if (len != 4) { - wpa_hexdump(MSG_DEBUG, - "EAP-TEAP: PAC-Info - Invalid CRED_LIFETIME length - ignored", - pos, len); - return 0; - } - - /* - * This is not currently saved separately in PAC files since - * the server can automatically initiate PAC update when - * needed. Anyway, the information is available from PAC-Info - * dump if it is needed for something in the future. - */ - lifetime = WPA_GET_BE32(pos); - os_get_time(&now); - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Info - CRED_LIFETIME %d (%d days)", - lifetime, (lifetime - (u32) now.sec) / 86400); - break; - case PAC_TYPE_A_ID: - wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID", - pos, len); - entry->a_id = pos; - entry->a_id_len = len; - break; - case PAC_TYPE_I_ID: - wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - I-ID", - pos, len); - entry->i_id = pos; - entry->i_id_len = len; - break; - case PAC_TYPE_A_ID_INFO: - wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID-Info", - pos, len); - entry->a_id_info = pos; - entry->a_id_info_len = len; - break; - case PAC_TYPE_PAC_TYPE: - /* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */ - if (len != 2) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Invalid PAC-Type length %lu (expected 2)", - (unsigned long) len); - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-TEAP: PAC-Info - PAC-Type", - pos, len); - return -1; - } - pac_type = WPA_GET_BE16(pos); - if (pac_type != PAC_TYPE_TUNNEL_PAC) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Unsupported PAC Type %d", - pac_type); - return -1; - } - - wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d", - pac_type); - entry->pac_type = pac_type; - break; - default: - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Ignored unknown PAC-Info type %d", type); - break; - } - - return 0; -} - - -static int eap_teap_process_pac_info(struct eap_teap_pac *entry) -{ - struct pac_attr_hdr *hdr; - u8 *pos; - size_t left, len; - int type; - - /* RFC 7170, Section 4.2.12.4 */ - - /* PAC-Type defaults to Tunnel PAC (Type 1) */ - entry->pac_type = PAC_TYPE_TUNNEL_PAC; - - pos = entry->pac_info; - left = entry->pac_info_len; - while (left > sizeof(*hdr)) { - hdr = (struct pac_attr_hdr *) pos; - type = be_to_host16(hdr->type); - len = be_to_host16(hdr->len); - pos += sizeof(*hdr); - left -= sizeof(*hdr); - if (len > left) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)", - type, (unsigned long) len, - (unsigned long) left); - return -1; - } - - if (eap_teap_parse_pac_info(entry, type, pos, len) < 0) - return -1; - - pos += len; - left -= len; - } - - if (!entry->a_id || !entry->a_id_info) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Info does not include all the required fields"); - return -1; - } - - return 0; -} - - -static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm, - struct eap_teap_data *data, - struct eap_method_ret *ret, - u8 *pac, size_t pac_len) -{ - struct eap_peer_config *config = eap_get_config(sm); - struct eap_teap_pac entry; - - os_memset(&entry, 0, sizeof(entry)); - if (eap_teap_process_pac_tlv(&entry, pac, pac_len) || - eap_teap_process_pac_info(&entry)) - return NULL; - - eap_teap_add_pac(&data->pac, &data->current_pac, &entry); - eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len); - if (data->use_pac_binary_format) - eap_teap_save_pac_bin(sm, data->pac, config->pac_file); - else - eap_teap_save_pac(sm, data->pac, config->pac_file); - - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully", - data->provisioning ? "peer" : "server"); - return eap_teap_tlv_pac_ack(); -} - - static int eap_teap_parse_decrypted(struct wpabuf *decrypted, struct eap_teap_tlv_parse *tlv, struct wpabuf **resp) @@ -1208,38 +862,6 @@ static int eap_teap_parse_decrypted(struct wpabuf *decrypted, } -static struct wpabuf * eap_teap_pac_request(void) -{ - struct wpabuf *req; - struct teap_tlv_request_action *act; - struct teap_tlv_hdr *pac; - struct teap_attr_pac_type *type; - - req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type)); - if (!req) - return NULL; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)"); - act = wpabuf_put(req, sizeof(*act)); - act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION); - act->length = host_to_be16(2); - act->status = TEAP_STATUS_SUCCESS; - act->action = TEAP_REQUEST_ACTION_PROCESS_TLV; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)"); - pac = wpabuf_put(req, sizeof(*pac)); - pac->tlv_type = host_to_be16(TEAP_TLV_PAC); - pac->length = host_to_be16(sizeof(*type)); - - type = wpabuf_put(req, sizeof(*type)); - type->type = host_to_be16(PAC_TYPE_PAC_TYPE); - type->length = host_to_be16(2); - type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC); - - return req; -} - - static int eap_teap_process_decrypted(struct eap_sm *sm, struct eap_teap_data *data, struct eap_method_ret *ret, @@ -1387,17 +1009,9 @@ static int eap_teap_process_decrypted(struct eap_sm *sm, } } - if (data->result_success_done && data->session_ticket_used && + if (data->result_success_done && + tls_connection_get_own_cert_used(data->ssl.conn) && eap_teap_derive_msk(data) == 0) { - /* Assume the server might accept authentication without going - * through inner authentication. */ - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC used - server may decide to skip inner authentication"); - ret->methodState = METHOD_MAY_CONT; - ret->decision = DECISION_COND_SUCC; - } else if (data->result_success_done && - tls_connection_get_own_cert_used(data->ssl.conn) && - eap_teap_derive_msk(data) == 0) { /* Assume the server might accept authentication without going * through inner authentication. */ wpa_printf(MSG_DEBUG, @@ -1406,39 +1020,6 @@ static int eap_teap_process_decrypted(struct eap_sm *sm, ret->decision = DECISION_COND_SUCC; } - if (tlv.pac) { - if (tlv.result == TEAP_STATUS_SUCCESS) { - tmp = eap_teap_process_pac(sm, data, ret, - tlv.pac, tlv.pac_len); - resp = wpabuf_concat(resp, tmp); - } else { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC TLV without Result TLV acknowledging success"); - failed = 1; - error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED; - } - } - - if (!data->current_pac && data->provisioning && !failed && !tlv.pac && - tlv.crypto_binding && - (!data->anon_provisioning || - (data->phase2_success && data->phase2_method && - data->phase2_method->vendor == 0 && - eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) && - eap_teap_allowed_anon_prov_phase2_method( - data->phase2_method->vendor, - data->phase2_method->method))) && - (tlv.iresult == TEAP_STATUS_SUCCESS || - tlv.result == TEAP_STATUS_SUCCESS)) { - /* - * Need to request Tunnel PAC when using authenticated - * provisioning. - */ - wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC"); - tmp = eap_teap_pac_request(); - resp = wpabuf_concat(resp, tmp); - } - done: if (failed) { tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); @@ -1470,8 +1051,7 @@ done: wpa_printf(MSG_DEBUG, "EAP-TEAP: Authentication completed successfully"); ret->methodState = METHOD_MAY_CONT; - data->on_tx_completion = data->provisioning ? - METHOD_MAY_CONT : METHOD_DONE; + data->on_tx_completion = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; } @@ -1562,76 +1142,11 @@ continue_req: } -static void eap_teap_select_pac(struct eap_teap_data *data, - const u8 *a_id, size_t a_id_len) -{ - if (!a_id) - return; - data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len, - PAC_TYPE_TUNNEL_PAC); - if (data->current_pac) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC found for this A-ID (PAC-Type %d)", - data->current_pac->pac_type); - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info", - data->current_pac->a_id_info, - data->current_pac->a_id_info_len); - } -} - - -static int eap_teap_use_pac_opaque(struct eap_sm *sm, - struct eap_teap_data *data, - struct eap_teap_pac *pac) -{ - u8 *tlv; - size_t tlv_len, olen; - struct teap_tlv_hdr *ehdr; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension"); - olen = pac->pac_opaque_len; - tlv_len = sizeof(*ehdr) + olen; - tlv = os_malloc(tlv_len); - if (tlv) { - ehdr = (struct teap_tlv_hdr *) tlv; - ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE); - ehdr->length = host_to_be16(olen); - os_memcpy(ehdr + 1, pac->pac_opaque, olen); - } - if (!tlv || - tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, - TLS_EXT_PAC_OPAQUE, - tlv, tlv_len) < 0) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Failed to add PAC-Opaque TLS extension"); - os_free(tlv); - return -1; - } - os_free(tlv); - - return 0; -} - - -static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm, - struct eap_teap_data *data) -{ - if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, - TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Failed to remove PAC-Opaque TLS extension"); - return -1; - } - return 0; -} - - static int eap_teap_process_start(struct eap_sm *sm, struct eap_teap_data *data, u8 flags, const u8 *pos, size_t left) { const u8 *a_id = NULL; - size_t a_id_len = 0; /* TODO: Support (mostly theoretical) case of TEAP/Start request being * fragmented */ @@ -1725,7 +1240,6 @@ static int eap_teap_process_start(struct eap_sm *sm, return -1; } a_id = outer_pos; - a_id_len = tlv_len; } else { wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore unknown Outer TLV (Type %u)", @@ -1740,28 +1254,6 @@ static int eap_teap_process_start(struct eap_sm *sm, return -1; } - eap_teap_select_pac(data, a_id, a_id_len); - - if (data->resuming && data->current_pac) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello"); - if (eap_teap_clear_pac_opaque_ext(sm, data) < 0) - return -1; - } else if (data->current_pac) { - /* - * PAC found for the A-ID and we are not resuming an old - * session, so add PAC-Opaque extension to ClientHello. - */ - if (eap_teap_use_pac_opaque(sm, data, data->current_pac) < 0) - return -1; - } else if (data->provisioning_allowed) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: No PAC found - starting provisioning"); - if (eap_teap_clear_pac_opaque_ext(sm, data) < 0) - return -1; - data->provisioning = 1; - } - return 0; } @@ -1939,8 +1431,6 @@ static struct wpabuf * eap_teap_process(struct eap_sm *sm, void *priv, } if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - char cipher[80]; - wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS done, proceed to Phase 2"); data->tls_cs = @@ -1949,19 +1439,6 @@ static struct wpabuf * eap_teap_process(struct eap_sm *sm, void *priv, "EAP-TEAP: TLS cipher suite 0x%04x", data->tls_cs); - if (data->provisioning && - (!(data->provisioning_allowed & - EAP_TEAP_PROV_AUTH) || - tls_get_cipher(sm->ssl_ctx, data->ssl.conn, - cipher, sizeof(cipher)) < 0 || - os_strstr(cipher, "ADH-") || - os_strstr(cipher, "anon"))) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Using anonymous (unauthenticated) provisioning"); - data->anon_provisioning = 1; - } else { - data->anon_provisioning = 0; - } data->resuming = 0; if (eap_teap_derive_key_auth(sm, data) < 0) { wpa_printf(MSG_DEBUG, @@ -2037,8 +1514,6 @@ static void * eap_teap_init_for_reauth(struct eap_sm *sm, void *priv) data->iresult_verified = 0; data->done_on_tx_completion = 0; data->resuming = 1; - data->provisioning = 0; - data->anon_provisioning = 0; data->simck_idx = 0; return priv; } diff --git a/contrib/wpa/src/eap_peer/eap_teap_pac.c b/contrib/wpa/src/eap_peer/eap_teap_pac.c deleted file mode 100644 index 34a2743560f0..000000000000 --- a/contrib/wpa/src/eap_peer/eap_teap_pac.c +++ /dev/null @@ -1,931 +0,0 @@ -/* - * EAP peer method: EAP-TEAP PAC file processing - * Copyright (c) 2004-2019, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eap_config.h" -#include "eap_i.h" -#include "eap_teap_pac.h" - -/* TODO: encrypt PAC-Key in the PAC file */ - - -/* Text data format */ -static const char *pac_file_hdr = - "wpa_supplicant EAP-TEAP PAC file - version 1"; - -/* - * Binary data format - * 4-octet magic value: 6A E4 92 1C - * 2-octet version (big endian) - * - * - * version=0: - * Sequence of PAC entries: - * 2-octet PAC-Type (big endian) - * 32-octet PAC-Key - * 2-octet PAC-Opaque length (big endian) - * PAC-Opaque data (length bytes) - * 2-octet PAC-Info length (big endian) - * PAC-Info data (length bytes) - */ - -#define EAP_TEAP_PAC_BINARY_MAGIC 0x6ae4921c -#define EAP_TEAP_PAC_BINARY_FORMAT_VERSION 0 - - -/** - * eap_teap_free_pac - Free PAC data - * @pac: Pointer to the PAC entry - * - * Note that the PAC entry must not be in a list since this function does not - * remove the list links. - */ -void eap_teap_free_pac(struct eap_teap_pac *pac) -{ - os_free(pac->pac_opaque); - os_free(pac->pac_info); - os_free(pac->a_id); - os_free(pac->i_id); - os_free(pac->a_id_info); - os_free(pac); -} - - -/** - * eap_teap_get_pac - Get a PAC entry based on A-ID - * @pac_root: Pointer to root of the PAC list - * @a_id: A-ID to search for - * @a_id_len: Length of A-ID - * @pac_type: PAC-Type to search for - * Returns: Pointer to the PAC entry, or %NULL if A-ID not found - */ -struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root, - const u8 *a_id, size_t a_id_len, - u16 pac_type) -{ - struct eap_teap_pac *pac = pac_root; - - while (pac) { - if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && - os_memcmp(pac->a_id, a_id, a_id_len) == 0) { - return pac; - } - pac = pac->next; - } - return NULL; -} - - -static void eap_teap_remove_pac(struct eap_teap_pac **pac_root, - struct eap_teap_pac **pac_current, - const u8 *a_id, size_t a_id_len, u16 pac_type) -{ - struct eap_teap_pac *pac, *prev; - - pac = *pac_root; - prev = NULL; - - while (pac) { - if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && - os_memcmp(pac->a_id, a_id, a_id_len) == 0) { - if (!prev) - *pac_root = pac->next; - else - prev->next = pac->next; - if (*pac_current == pac) - *pac_current = NULL; - eap_teap_free_pac(pac); - break; - } - prev = pac; - pac = pac->next; - } -} - - -static int eap_teap_copy_buf(u8 **dst, size_t *dst_len, - const u8 *src, size_t src_len) -{ - if (src) { - *dst = os_memdup(src, src_len); - if (!(*dst)) - return -1; - *dst_len = src_len; - } - return 0; -} - - -/** - * eap_teap_add_pac - Add a copy of a PAC entry to a list - * @pac_root: Pointer to PAC list root pointer - * @pac_current: Pointer to the current PAC pointer - * @entry: New entry to clone and add to the list - * Returns: 0 on success, -1 on failure - * - * This function makes a clone of the given PAC entry and adds this copied - * entry to the list (pac_root). If an old entry for the same A-ID is found, - * it will be removed from the PAC list and in this case, pac_current entry - * is set to %NULL if it was the removed entry. - */ -int eap_teap_add_pac(struct eap_teap_pac **pac_root, - struct eap_teap_pac **pac_current, - struct eap_teap_pac *entry) -{ - struct eap_teap_pac *pac; - - if (!entry || !entry->a_id) - return -1; - - /* Remove a possible old entry for the matching A-ID. */ - eap_teap_remove_pac(pac_root, pac_current, - entry->a_id, entry->a_id_len, entry->pac_type); - - /* Allocate a new entry and add it to the list of PACs. */ - pac = os_zalloc(sizeof(*pac)); - if (!pac) - return -1; - - pac->pac_type = entry->pac_type; - os_memcpy(pac->pac_key, entry->pac_key, EAP_TEAP_PAC_KEY_LEN); - if (eap_teap_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, - entry->pac_opaque, entry->pac_opaque_len) < 0 || - eap_teap_copy_buf(&pac->pac_info, &pac->pac_info_len, - entry->pac_info, entry->pac_info_len) < 0 || - eap_teap_copy_buf(&pac->a_id, &pac->a_id_len, - entry->a_id, entry->a_id_len) < 0 || - eap_teap_copy_buf(&pac->i_id, &pac->i_id_len, - entry->i_id, entry->i_id_len) < 0 || - eap_teap_copy_buf(&pac->a_id_info, &pac->a_id_info_len, - entry->a_id_info, entry->a_id_info_len) < 0) { - eap_teap_free_pac(pac); - return -1; - } - - pac->next = *pac_root; - *pac_root = pac; - - return 0; -} - - -struct eap_teap_read_ctx { - FILE *f; - const char *pos; - const char *end; - int line; - char *buf; - size_t buf_len; -}; - -static int eap_teap_read_line(struct eap_teap_read_ctx *rc, char **value) -{ - char *pos; - - rc->line++; - if (rc->f) { - if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) - return -1; - } else { - const char *l_end; - size_t len; - - if (rc->pos >= rc->end) - return -1; - l_end = rc->pos; - while (l_end < rc->end && *l_end != '\n') - l_end++; - len = l_end - rc->pos; - if (len >= rc->buf_len) - len = rc->buf_len - 1; - os_memcpy(rc->buf, rc->pos, len); - rc->buf[len] = '\0'; - rc->pos = l_end + 1; - } - - rc->buf[rc->buf_len - 1] = '\0'; - pos = rc->buf; - while (*pos != '\0') { - if (*pos == '\n' || *pos == '\r') { - *pos = '\0'; - break; - } - pos++; - } - - pos = os_strchr(rc->buf, '='); - if (pos) - *pos++ = '\0'; - *value = pos; - - return 0; -} - - -static u8 * eap_teap_parse_hex(const char *value, size_t *len) -{ - int hlen; - u8 *buf; - - if (!value) - return NULL; - hlen = os_strlen(value); - if (hlen & 1) - return NULL; - *len = hlen / 2; - buf = os_malloc(*len); - if (!buf) - return NULL; - if (hexstr2bin(value, buf, *len)) { - os_free(buf); - return NULL; - } - return buf; -} - - -static int eap_teap_init_pac_data(struct eap_sm *sm, const char *pac_file, - struct eap_teap_read_ctx *rc) -{ - os_memset(rc, 0, sizeof(*rc)); - - rc->buf_len = 2048; - rc->buf = os_malloc(rc->buf_len); - if (!rc->buf) - return -1; - - if (os_strncmp(pac_file, "blob://", 7) == 0) { - const struct wpa_config_blob *blob; - - blob = eap_get_config_blob(sm, pac_file + 7); - if (!blob) { - wpa_printf(MSG_INFO, - "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned", - pac_file + 7); - os_free(rc->buf); - return -1; - } - rc->pos = (char *) blob->data; - rc->end = (char *) blob->data + blob->len; - } else { - rc->f = fopen(pac_file, "rb"); - if (!rc->f) { - wpa_printf(MSG_INFO, - "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned", - pac_file); - os_free(rc->buf); - return -1; - } - } - - return 0; -} - - -static void eap_teap_deinit_pac_data(struct eap_teap_read_ctx *rc) -{ - os_free(rc->buf); - if (rc->f) - fclose(rc->f); -} - - -static const char * eap_teap_parse_start(struct eap_teap_pac **pac) -{ - if (*pac) - return "START line without END"; - - *pac = os_zalloc(sizeof(struct eap_teap_pac)); - if (!(*pac)) - return "No memory for PAC entry"; - (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; - return NULL; -} - - -static const char * eap_teap_parse_end(struct eap_teap_pac **pac_root, - struct eap_teap_pac **pac) -{ - if (!(*pac)) - return "END line without START"; - if (*pac_root) { - struct eap_teap_pac *end = *pac_root; - - while (end->next) - end = end->next; - end->next = *pac; - } else - *pac_root = *pac; - - *pac = NULL; - return NULL; -} - - -static const char * eap_teap_parse_pac_type(struct eap_teap_pac *pac, - char *pos) -{ - if (!pos) - return "Cannot parse pac type"; - pac->pac_type = atoi(pos); - if (pac->pac_type != PAC_TYPE_TUNNEL_PAC) - return "Unrecognized PAC-Type"; - - return NULL; -} - - -static const char * eap_teap_parse_pac_key(struct eap_teap_pac *pac, char *pos) -{ - u8 *key; - size_t key_len; - - key = eap_teap_parse_hex(pos, &key_len); - if (!key || key_len != EAP_TEAP_PAC_KEY_LEN) { - os_free(key); - return "Invalid PAC-Key"; - } - - os_memcpy(pac->pac_key, key, EAP_TEAP_PAC_KEY_LEN); - os_free(key); - - return NULL; -} - - -static const char * eap_teap_parse_pac_opaque(struct eap_teap_pac *pac, - char *pos) -{ - os_free(pac->pac_opaque); - pac->pac_opaque = eap_teap_parse_hex(pos, &pac->pac_opaque_len); - if (!pac->pac_opaque) - return "Invalid PAC-Opaque"; - return NULL; -} - - -static const char * eap_teap_parse_a_id(struct eap_teap_pac *pac, char *pos) -{ - os_free(pac->a_id); - pac->a_id = eap_teap_parse_hex(pos, &pac->a_id_len); - if (!pac->a_id) - return "Invalid A-ID"; - return NULL; -} - - -static const char * eap_teap_parse_i_id(struct eap_teap_pac *pac, char *pos) -{ - os_free(pac->i_id); - pac->i_id = eap_teap_parse_hex(pos, &pac->i_id_len); - if (!pac->i_id) - return "Invalid I-ID"; - return NULL; -} - - -static const char * eap_teap_parse_a_id_info(struct eap_teap_pac *pac, - char *pos) -{ - os_free(pac->a_id_info); - pac->a_id_info = eap_teap_parse_hex(pos, &pac->a_id_info_len); - if (!pac->a_id_info) - return "Invalid A-ID-Info"; - return NULL; -} - - -/** - * eap_teap_load_pac - Load PAC entries (text format) - * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() - * @pac_root: Pointer to root of the PAC list (to be filled) - * @pac_file: Name of the PAC file/blob to load - * Returns: 0 on success, -1 on failure - */ -int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root, - const char *pac_file) -{ - struct eap_teap_read_ctx rc; - struct eap_teap_pac *pac = NULL; - int count = 0; - char *pos; - const char *err = NULL; - - if (!pac_file) - return -1; - - if (eap_teap_init_pac_data(sm, pac_file, &rc) < 0) - return 0; - - if (eap_teap_read_line(&rc, &pos) < 0) { - /* empty file - assume it is fine to overwrite */ - eap_teap_deinit_pac_data(&rc); - return 0; - } - if (os_strcmp(pac_file_hdr, rc.buf) != 0) - err = "Unrecognized header line"; - - while (!err && eap_teap_read_line(&rc, &pos) == 0) { - if (os_strcmp(rc.buf, "START") == 0) - err = eap_teap_parse_start(&pac); - else if (os_strcmp(rc.buf, "END") == 0) { - err = eap_teap_parse_end(pac_root, &pac); - count++; - } else if (!pac) - err = "Unexpected line outside START/END block"; - else if (os_strcmp(rc.buf, "PAC-Type") == 0) - err = eap_teap_parse_pac_type(pac, pos); - else if (os_strcmp(rc.buf, "PAC-Key") == 0) - err = eap_teap_parse_pac_key(pac, pos); - else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) - err = eap_teap_parse_pac_opaque(pac, pos); - else if (os_strcmp(rc.buf, "A-ID") == 0) - err = eap_teap_parse_a_id(pac, pos); - else if (os_strcmp(rc.buf, "I-ID") == 0) - err = eap_teap_parse_i_id(pac, pos); - else if (os_strcmp(rc.buf, "A-ID-Info") == 0) - err = eap_teap_parse_a_id_info(pac, pos); - } - - if (pac) { - if (!err) - err = "PAC block not terminated with END"; - eap_teap_free_pac(pac); - } - - eap_teap_deinit_pac_data(&rc); - - if (err) { - wpa_printf(MSG_INFO, "EAP-TEAP: %s in '%s:%d'", - err, pac_file, rc.line); - return -1; - } - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %d PAC entries from '%s'", - count, pac_file); - - return 0; -} - - -static void eap_teap_write(char **buf, char **pos, size_t *buf_len, - const char *field, const u8 *data, - size_t len, int txt) -{ - size_t i, need; - int ret; - char *end; - - if (!data || !buf || !(*buf) || !pos || !(*pos) || *pos < *buf) - return; - - need = os_strlen(field) + len * 2 + 30; - if (txt) - need += os_strlen(field) + len + 20; - - if (*pos - *buf + need > *buf_len) { - char *nbuf = os_realloc(*buf, *buf_len + need); - - if (!nbuf) { - os_free(*buf); - *buf = NULL; - return; - } - *pos = nbuf + (*pos - *buf); - *buf = nbuf; - *buf_len += need; - } - end = *buf + *buf_len; - - ret = os_snprintf(*pos, end - *pos, "%s=", field); - if (os_snprintf_error(end - *pos, ret)) - return; - *pos += ret; - *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); - ret = os_snprintf(*pos, end - *pos, "\n"); - if (os_snprintf_error(end - *pos, ret)) - return; - *pos += ret; - - if (txt) { - ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); - if (os_snprintf_error(end - *pos, ret)) - return; - *pos += ret; - for (i = 0; i < len; i++) { - ret = os_snprintf(*pos, end - *pos, "%c", data[i]); - if (os_snprintf_error(end - *pos, ret)) - return; - *pos += ret; - } - ret = os_snprintf(*pos, end - *pos, "\n"); - if (os_snprintf_error(end - *pos, ret)) - return; - *pos += ret; - } -} - - -static int eap_teap_write_pac(struct eap_sm *sm, const char *pac_file, - char *buf, size_t len) -{ - if (os_strncmp(pac_file, "blob://", 7) == 0) { - struct wpa_config_blob *blob; - - blob = os_zalloc(sizeof(*blob)); - if (!blob) - return -1; - blob->data = (u8 *) buf; - blob->len = len; - buf = NULL; - blob->name = os_strdup(pac_file + 7); - if (!blob->name) { - os_free(blob); - return -1; - } - eap_set_config_blob(sm, blob); - } else { - FILE *f; - - f = fopen(pac_file, "wb"); - if (!f) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Failed to open PAC file '%s' for writing", - pac_file); - return -1; - } - if (fwrite(buf, 1, len, f) != len) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Failed to write all PACs into '%s'", - pac_file); - fclose(f); - return -1; - } - os_free(buf); - fclose(f); - } - - return 0; -} - - -static int eap_teap_add_pac_data(struct eap_teap_pac *pac, char **buf, - char **pos, size_t *buf_len) -{ - int ret; - - ret = os_snprintf(*pos, *buf + *buf_len - *pos, - "START\nPAC-Type=%d\n", pac->pac_type); - if (os_snprintf_error(*buf + *buf_len - *pos, ret)) - return -1; - - *pos += ret; - eap_teap_write(buf, pos, buf_len, "PAC-Key", - pac->pac_key, EAP_TEAP_PAC_KEY_LEN, 0); - eap_teap_write(buf, pos, buf_len, "PAC-Opaque", - pac->pac_opaque, pac->pac_opaque_len, 0); - eap_teap_write(buf, pos, buf_len, "PAC-Info", - pac->pac_info, pac->pac_info_len, 0); - eap_teap_write(buf, pos, buf_len, "A-ID", - pac->a_id, pac->a_id_len, 0); - eap_teap_write(buf, pos, buf_len, "I-ID", - pac->i_id, pac->i_id_len, 1); - eap_teap_write(buf, pos, buf_len, "A-ID-Info", - pac->a_id_info, pac->a_id_info_len, 1); - if (!(*buf)) { - wpa_printf(MSG_DEBUG, "EAP-TEAP: No memory for PAC data"); - return -1; - } - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); - if (os_snprintf_error(*buf + *buf_len - *pos, ret)) - return -1; - *pos += ret; - - return 0; -} - - -/** - * eap_teap_save_pac - Save PAC entries (text format) - * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() - * @pac_root: Root of the PAC list - * @pac_file: Name of the PAC file/blob - * Returns: 0 on success, -1 on failure - */ -int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root, - const char *pac_file) -{ - struct eap_teap_pac *pac; - int ret, count = 0; - char *buf, *pos; - size_t buf_len; - - if (!pac_file) - return -1; - - buf_len = 1024; - pos = buf = os_malloc(buf_len); - if (!buf) - return -1; - - ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); - if (os_snprintf_error(buf + buf_len - pos, ret)) { - os_free(buf); - return -1; - } - pos += ret; - - pac = pac_root; - while (pac) { - if (eap_teap_add_pac_data(pac, &buf, &pos, &buf_len)) { - os_free(buf); - return -1; - } - count++; - pac = pac->next; - } - - if (eap_teap_write_pac(sm, pac_file, buf, pos - buf)) { - os_free(buf); - return -1; - } - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %d PAC entries into '%s'", - count, pac_file); - - return 0; -} - - -/** - * eap_teap_pac_list_truncate - Truncate a PAC list to the given length - * @pac_root: Root of the PAC list - * @max_len: Maximum length of the list (>= 1) - * Returns: Number of PAC entries removed - */ -size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root, - size_t max_len) -{ - struct eap_teap_pac *pac, *prev; - size_t count; - - pac = pac_root; - prev = NULL; - count = 0; - - while (pac) { - count++; - if (count > max_len) - break; - prev = pac; - pac = pac->next; - } - - if (count <= max_len || !prev) - return 0; - - count = 0; - prev->next = NULL; - - while (pac) { - prev = pac; - pac = pac->next; - eap_teap_free_pac(prev); - count++; - } - - return count; -} - - -static void eap_teap_pac_get_a_id(struct eap_teap_pac *pac) -{ - u8 *pos, *end; - u16 type, len; - - pos = pac->pac_info; - end = pos + pac->pac_info_len; - - while (end - pos > 4) { - type = WPA_GET_BE16(pos); - pos += 2; - len = WPA_GET_BE16(pos); - pos += 2; - if (len > (unsigned int) (end - pos)) - break; - - if (type == PAC_TYPE_A_ID) { - os_free(pac->a_id); - pac->a_id = os_memdup(pos, len); - if (!pac->a_id) - break; - pac->a_id_len = len; - } - - if (type == PAC_TYPE_A_ID_INFO) { - os_free(pac->a_id_info); - pac->a_id_info = os_memdup(pos, len); - if (!pac->a_id_info) - break; - pac->a_id_info_len = len; - } - - pos += len; - } -} - - -/** - * eap_teap_load_pac_bin - Load PAC entries (binary format) - * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() - * @pac_root: Pointer to root of the PAC list (to be filled) - * @pac_file: Name of the PAC file/blob to load - * Returns: 0 on success, -1 on failure - */ -int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root, - const char *pac_file) -{ - const struct wpa_config_blob *blob = NULL; - u8 *buf, *end, *pos; - size_t len, count = 0; - struct eap_teap_pac *pac, *prev; - - *pac_root = NULL; - - if (!pac_file) - return -1; - - if (os_strncmp(pac_file, "blob://", 7) == 0) { - blob = eap_get_config_blob(sm, pac_file + 7); - if (!blob) { - wpa_printf(MSG_INFO, - "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned", - pac_file + 7); - return 0; - } - buf = blob->data; - len = blob->len; - } else { - buf = (u8 *) os_readfile(pac_file, &len); - if (!buf) { - wpa_printf(MSG_INFO, - "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned", - pac_file); - return 0; - } - } - - if (len == 0) { - if (!blob) - os_free(buf); - return 0; - } - - if (len < 6 || WPA_GET_BE32(buf) != EAP_TEAP_PAC_BINARY_MAGIC || - WPA_GET_BE16(buf + 4) != EAP_TEAP_PAC_BINARY_FORMAT_VERSION) { - wpa_printf(MSG_INFO, "EAP-TEAP: Invalid PAC file '%s' (bin)", - pac_file); - if (!blob) - os_free(buf); - return -1; - } - - pac = prev = NULL; - pos = buf + 6; - end = buf + len; - while (pos < end) { - u16 val; - - if (end - pos < 2 + EAP_TEAP_PAC_KEY_LEN + 2 + 2) { - pac = NULL; - goto parse_fail; - } - - pac = os_zalloc(sizeof(*pac)); - if (!pac) - goto parse_fail; - - pac->pac_type = WPA_GET_BE16(pos); - pos += 2; - os_memcpy(pac->pac_key, pos, EAP_TEAP_PAC_KEY_LEN); - pos += EAP_TEAP_PAC_KEY_LEN; - val = WPA_GET_BE16(pos); - pos += 2; - if (val > end - pos) - goto parse_fail; - pac->pac_opaque_len = val; - pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len); - if (!pac->pac_opaque) - goto parse_fail; - pos += pac->pac_opaque_len; - if (end - pos < 2) - goto parse_fail; - val = WPA_GET_BE16(pos); - pos += 2; - if (val > end - pos) - goto parse_fail; - pac->pac_info_len = val; - pac->pac_info = os_memdup(pos, pac->pac_info_len); - if (!pac->pac_info) - goto parse_fail; - pos += pac->pac_info_len; - eap_teap_pac_get_a_id(pac); - - count++; - if (prev) - prev->next = pac; - else - *pac_root = pac; - prev = pac; - } - - if (!blob) - os_free(buf); - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %lu PAC entries from '%s' (bin)", - (unsigned long) count, pac_file); - - return 0; - -parse_fail: - wpa_printf(MSG_INFO, "EAP-TEAP: Failed to parse PAC file '%s' (bin)", - pac_file); - if (!blob) - os_free(buf); - if (pac) - eap_teap_free_pac(pac); - return -1; -} - - -/** - * eap_teap_save_pac_bin - Save PAC entries (binary format) - * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() - * @pac_root: Root of the PAC list - * @pac_file: Name of the PAC file/blob - * Returns: 0 on success, -1 on failure - */ -int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root, - const char *pac_file) -{ - size_t len, count = 0; - struct eap_teap_pac *pac; - u8 *buf, *pos; - - len = 6; - pac = pac_root; - while (pac) { - if (pac->pac_opaque_len > 65535 || - pac->pac_info_len > 65535) - return -1; - len += 2 + EAP_TEAP_PAC_KEY_LEN + 2 + pac->pac_opaque_len + - 2 + pac->pac_info_len; - pac = pac->next; - } - - buf = os_malloc(len); - if (!buf) - return -1; - - pos = buf; - WPA_PUT_BE32(pos, EAP_TEAP_PAC_BINARY_MAGIC); - pos += 4; - WPA_PUT_BE16(pos, EAP_TEAP_PAC_BINARY_FORMAT_VERSION); - pos += 2; - - pac = pac_root; - while (pac) { - WPA_PUT_BE16(pos, pac->pac_type); - pos += 2; - os_memcpy(pos, pac->pac_key, EAP_TEAP_PAC_KEY_LEN); - pos += EAP_TEAP_PAC_KEY_LEN; - WPA_PUT_BE16(pos, pac->pac_opaque_len); - pos += 2; - os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); - pos += pac->pac_opaque_len; - WPA_PUT_BE16(pos, pac->pac_info_len); - pos += 2; - os_memcpy(pos, pac->pac_info, pac->pac_info_len); - pos += pac->pac_info_len; - - pac = pac->next; - count++; - } - - if (eap_teap_write_pac(sm, pac_file, (char *) buf, len)) { - os_free(buf); - return -1; - } - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %lu PAC entries into '%s' (bin)", - (unsigned long) count, pac_file); - - return 0; -} diff --git a/contrib/wpa/src/eap_peer/eap_teap_pac.h b/contrib/wpa/src/eap_peer/eap_teap_pac.h deleted file mode 100644 index edf4c5763ad2..000000000000 --- a/contrib/wpa/src/eap_peer/eap_teap_pac.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * EAP peer method: EAP-TEAP PAC file processing - * Copyright (c) 2004-2019, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef EAP_TEAP_PAC_H -#define EAP_TEAP_PAC_H - -#include "eap_common/eap_teap_common.h" - -struct eap_teap_pac { - struct eap_teap_pac *next; - - u8 pac_key[EAP_TEAP_PAC_KEY_LEN]; - u8 *pac_opaque; - size_t pac_opaque_len; - u8 *pac_info; - size_t pac_info_len; - u8 *a_id; - size_t a_id_len; - u8 *i_id; - size_t i_id_len; - u8 *a_id_info; - size_t a_id_info_len; - u16 pac_type; -}; - - -void eap_teap_free_pac(struct eap_teap_pac *pac); -struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root, - const u8 *a_id, size_t a_id_len, - u16 pac_type); -int eap_teap_add_pac(struct eap_teap_pac **pac_root, - struct eap_teap_pac **pac_current, - struct eap_teap_pac *entry); -int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root, - const char *pac_file); -int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root, - const char *pac_file); -size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root, - size_t max_len); -int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root, - const char *pac_file); -int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root, - const char *pac_file); - -#endif /* EAP_TEAP_PAC_H */ diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index 4167e992b3f5..7e167f0b2d9e 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -106,33 +106,6 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) #endif /* EAP_UNAUTH_TLS */ -#ifdef CONFIG_HS20 -static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) -{ - struct eap_tls_data *data; - struct eap_peer_config *config = eap_get_config(sm); - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - - data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : - sm->ssl_ctx; - - if (eap_peer_tls_ssl_init(sm, &data->ssl, config, - EAP_WFA_UNAUTH_TLS_TYPE)) { - wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); - eap_tls_deinit(sm, data); - return NULL; - } - - data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE; - - return data; -} -#endif /* CONFIG_HS20 */ - - static void eap_tls_free_key(struct eap_tls_data *data) { if (data->key_data) { @@ -478,31 +451,3 @@ int eap_peer_unauth_tls_register(void) return eap_peer_method_register(eap); } #endif /* EAP_UNAUTH_TLS */ - - -#ifdef CONFIG_HS20 -int eap_peer_wfa_unauth_tls_register(void) -{ - struct eap_method *eap; - - eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, - "WFA-UNAUTH-TLS"); - if (eap == NULL) - return -1; - - eap->init = eap_wfa_unauth_tls_init; - eap->deinit = eap_tls_deinit; - eap->process = eap_tls_process; - eap->isKeyAvailable = eap_tls_isKeyAvailable; - eap->getKey = eap_tls_getKey; - eap->get_status = eap_tls_get_status; - eap->has_reauth_data = eap_tls_has_reauth_data; - eap->deinit_for_reauth = eap_tls_deinit_for_reauth; - eap->init_for_reauth = eap_tls_init_for_reauth; - eap->get_emsk = eap_tls_get_emsk; - - return eap_peer_method_register(eap); -} -#endif /* CONFIG_HS20 */ diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index 966cbd6c7009..9b40b3a8559c 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -23,10 +23,6 @@ static struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len, return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, code, identifier); - if (type == EAP_WFA_UNAUTH_TLS_TYPE) - return eap_msg_alloc(EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, payload_len, - code, identifier); return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, identifier); } @@ -184,8 +180,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, /* RFC 7170 requires TLS v1.2 or newer to be used with TEAP */ params->flags |= TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1; - if (config->teap_anon_dh) - params->flags |= TLS_CONN_TEAP_ANON_DH; } if (data->eap_type == EAP_TYPE_FAST || data->eap_type == EAP_TYPE_TEAP || @@ -197,8 +191,7 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, } #ifndef EAP_TLSV1_3 if (data->eap_type == EAP_TYPE_TLS || - data->eap_type == EAP_UNAUTH_TLS_TYPE || - data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) { + data->eap_type == EAP_UNAUTH_TLS_TYPE) { /* While the current EAP-TLS implementation is more or less * complete for TLS v1.3, there has been only minimal * interoperability testing with other implementations, so @@ -930,10 +923,6 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, &left); - else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE) - pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, reqData, - &left); else pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left); diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h index 334863413896..2551ff551e07 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.h +++ b/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -99,7 +99,6 @@ struct eap_ssl_data { /* stub type used as a flag for UNAUTH-TLS */ #define EAP_UNAUTH_TLS_TYPE 255 -#define EAP_WFA_UNAUTH_TLS_TYPE 254 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h index d965a25c409c..dd02b0cdb61e 100644 --- a/contrib/wpa/src/eap_server/eap.h +++ b/contrib/wpa/src/eap_server/eap.h @@ -35,7 +35,6 @@ struct eap_user { size_t salt_len; int phase2; int force_version; - unsigned int remediation:1; unsigned int macacl:1; int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ @@ -199,7 +198,6 @@ struct eap_config { */ int pac_key_refresh_time; int eap_teap_auth; - int eap_teap_pac_no_inner; int eap_teap_separate_result; enum eap_teap_id { EAP_TEAP_ID_ALLOW_ANY = 0, diff --git a/contrib/wpa/src/eap_server/eap_server_teap.c b/contrib/wpa/src/eap_server/eap_server_teap.c index e32c6e48c523..d62452396c1b 100644 --- a/contrib/wpa/src/eap_server/eap_server_teap.c +++ b/contrib/wpa/src/eap_server/eap_server_teap.c @@ -1,6 +1,6 @@ /* * EAP-TEAP server (RFC 7170) - * Copyright (c) 2004-2019, Jouni Malinen + * Copyright (c) 2004-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,7 +9,6 @@ #include "includes.h" #include "common.h" -#include "crypto/aes_wrap.h" #include "crypto/tls.h" #include "crypto/random.h" #include "eap_common/eap_teap_common.h" @@ -20,17 +19,11 @@ static void eap_teap_reset(struct eap_sm *sm, void *priv); -/* Private PAC-Opaque TLV types */ -#define PAC_OPAQUE_TYPE_PAD 0 -#define PAC_OPAQUE_TYPE_KEY 1 -#define PAC_OPAQUE_TYPE_LIFETIME 2 -#define PAC_OPAQUE_TYPE_IDENTITY 3 - struct eap_teap_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID, - PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC, + PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, FAILURE_SEND_RESULT, SUCCESS_SEND_RESULT, SUCCESS, FAILURE } state; @@ -44,34 +37,29 @@ struct eap_teap_data { u8 crypto_binding_nonce[32]; int final_result; + u8 simck[EAP_TEAP_SIMCK_LEN]; u8 simck_msk[EAP_TEAP_SIMCK_LEN]; u8 cmk_msk[EAP_TEAP_CMK_LEN]; u8 simck_emsk[EAP_TEAP_SIMCK_LEN]; u8 cmk_emsk[EAP_TEAP_CMK_LEN]; int simck_idx; - int cmk_emsk_available; + bool cmk_emsk_available; - u8 pac_opaque_encr[16]; u8 *srv_id; size_t srv_id_len; char *srv_id_info; unsigned int basic_auth_not_done:1; unsigned int inner_eap_not_done:1; - int anon_provisioning; int skipped_inner_auth; - int send_new_pac; /* server triggered re-keying of Tunnel PAC */ struct wpabuf *pending_phase2_resp; struct wpabuf *server_outer_tlvs; struct wpabuf *peer_outer_tlvs; - u8 *identity; /* from PAC-Opaque or client certificate */ + u8 *identity; /* from client certificate */ size_t identity_len; int eap_seq; int tnc_started; - int pac_key_lifetime; - int pac_key_refresh_time; - enum teap_error_codes error_code; enum teap_identity_types cur_id_type; @@ -104,8 +92,6 @@ static const char * eap_teap_state_txt(int state) return "PHASE2_METHOD"; case CRYPTO_BINDING: return "CRYPTO_BINDING"; - case REQUEST_PAC: - return "REQUEST_PAC"; case FAILURE_SEND_RESULT: return "FAILURE_SEND_RESULT"; case SUCCESS_SEND_RESULT: @@ -137,158 +123,6 @@ static enum eap_type eap_teap_req_failure(struct eap_teap_data *data, } -static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, - const u8 *client_random, - const u8 *server_random, - u8 *master_secret) -{ - struct eap_teap_data *data = ctx; - const u8 *pac_opaque; - size_t pac_opaque_len; - u8 *buf, *pos, *end, *pac_key = NULL; - os_time_t lifetime = 0; - struct os_time now; - u8 *identity = NULL; - size_t identity_len = 0; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback"); - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)", - ticket, len); - - if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { - wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket"); - return 0; - } - - pac_opaque_len = WPA_GET_BE16(ticket + 2); - pac_opaque = ticket + 4; - if (pac_opaque_len < 8 || pac_opaque_len % 8 || - pac_opaque_len > len - 4) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)", - (unsigned long) pac_opaque_len, - (unsigned long) len); - return 0; - } - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque", - pac_opaque, pac_opaque_len); - - buf = os_malloc(pac_opaque_len - 8); - if (!buf) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque"); - return 0; - } - - if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), - (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { - wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque"); - os_free(buf); - /* - * This may have been caused by server changing the PAC-Opaque - * encryption key, so just ignore this PAC-Opaque instead of - * failing the authentication completely. Provisioning can now - * be used to provision a new PAC. - */ - return 0; - } - - end = buf + pac_opaque_len - 8; - wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque", - buf, end - buf); - - pos = buf; - while (end - pos > 1) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - if (elen > end - pos) - break; - - switch (id) { - case PAC_OPAQUE_TYPE_PAD: - goto done; - case PAC_OPAQUE_TYPE_KEY: - if (elen != EAP_TEAP_PAC_KEY_LEN) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Invalid PAC-Key length %d", - elen); - os_free(buf); - return -1; - } - pac_key = pos; - wpa_hexdump_key(MSG_DEBUG, - "EAP-TEAP: PAC-Key from decrypted PAC-Opaque", - pac_key, EAP_TEAP_PAC_KEY_LEN); - break; - case PAC_OPAQUE_TYPE_LIFETIME: - if (elen != 4) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Invalid PAC-Key lifetime length %d", - elen); - os_free(buf); - return -1; - } - lifetime = WPA_GET_BE32(pos); - break; - case PAC_OPAQUE_TYPE_IDENTITY: - identity = pos; - identity_len = elen; - break; - } - - pos += elen; - } -done: - - if (!pac_key) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: No PAC-Key included in PAC-Opaque"); - os_free(buf); - return -1; - } - - if (identity) { - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-TEAP: Identity from PAC-Opaque", - identity, identity_len); - os_free(data->identity); - data->identity = os_malloc(identity_len); - if (data->identity) { - os_memcpy(data->identity, identity, identity_len); - data->identity_len = identity_len; - } - } - - if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)", - lifetime, now.sec); - data->send_new_pac = 2; - /* - * Allow PAC to be used to allow a PAC update with some level - * of server authentication (i.e., do not fall back to full TLS - * handshake since we cannot be sure that the peer would be - * able to validate server certificate now). However, reject - * the authentication since the PAC was not valid anymore. Peer - * can connect again with the newly provisioned PAC after this. - */ - } else if (lifetime - now.sec < data->pac_key_refresh_time) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds"); - data->send_new_pac = 1; - } - - /* EAP-TEAP uses PAC-Key as the TLS master_secret */ - os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN); - - os_free(buf); - - return 1; -} - - static int eap_teap_derive_key_auth(struct eap_sm *sm, struct eap_teap_data *data) { @@ -297,13 +131,14 @@ static int eap_teap_derive_key_auth(struct eap_sm *sm, /* RFC 7170, Section 5.1 */ res = tls_connection_export_key(sm->cfg->ssl_ctx, data->ssl.conn, TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0, - data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck, EAP_TEAP_SIMCK_LEN); if (res) return res; wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: session_key_seed (S-IMCK[0])", - data->simck_msk, EAP_TEAP_SIMCK_LEN); - os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN); data->simck_idx = 0; return 0; } @@ -319,9 +154,7 @@ static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data) data->simck_idx + 1); if (sm->cfg->eap_teap_auth == 1) - return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs, - data->simck_msk, - data->cmk_msk); + goto out; /* no MSK derived in Basic-Password-Auth */ if (!data->phase2_method || !data->phase2_priv) { wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available"); @@ -343,8 +176,8 @@ static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data) &emsk_len); } - res = eap_teap_derive_imck(data->tls_cs, - data->simck_msk, data->simck_emsk, +out: + res = eap_teap_derive_imck(data->tls_cs, data->simck, msk, msk_len, emsk, emsk_len, data->simck_msk, data->cmk_msk, data->simck_emsk, data->cmk_emsk); @@ -352,8 +185,7 @@ static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data) bin_clear_free(emsk, emsk_len); if (res == 0) { data->simck_idx++; - if (emsk) - data->cmk_emsk_available = 1; + data->cmk_emsk_available = emsk != NULL; } return 0; } @@ -377,28 +209,6 @@ static void * eap_teap_init(struct eap_sm *sm) return NULL; } - /* TODO: Add anon-DH TLS cipher suites (and if one is negotiated, - * enforce inner EAP with mutual authentication to be used) */ - - if (tls_connection_set_session_ticket_cb(sm->cfg->ssl_ctx, - data->ssl.conn, - eap_teap_session_ticket_cb, - data) < 0) { - wpa_printf(MSG_INFO, - "EAP-TEAP: Failed to set SessionTicket callback"); - eap_teap_reset(sm, data); - return NULL; - } - - if (!sm->cfg->pac_opaque_encr_key) { - wpa_printf(MSG_INFO, - "EAP-TEAP: No PAC-Opaque encryption key configured"); - eap_teap_reset(sm, data); - return NULL; - } - os_memcpy(data->pac_opaque_encr, sm->cfg->pac_opaque_encr_key, - sizeof(data->pac_opaque_encr)); - if (!sm->cfg->eap_fast_a_id) { wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured"); eap_teap_reset(sm, data); @@ -424,16 +234,6 @@ static void * eap_teap_init(struct eap_sm *sm) return NULL; } - /* PAC-Key lifetime in seconds (hard limit) */ - data->pac_key_lifetime = sm->cfg->pac_key_lifetime; - - /* - * PAC-Key refresh time in seconds (soft limit on remaining hard - * limit). The server will generate a new PAC-Key when this number of - * seconds (or fewer) of the lifetime remains. - */ - data->pac_key_refresh_time = sm->cfg->pac_key_refresh_time; - return data; } @@ -457,7 +257,6 @@ static void eap_teap_reset(struct eap_sm *sm, void *priv) forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN); forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN); forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN); - forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr)); bin_clear_free(data, sizeof(*data)); } @@ -504,8 +303,6 @@ static struct wpabuf * eap_teap_build_start(struct eap_sm *sm, static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data) { - char cipher[64]; - wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2"); if (!data->identity && sm->cfg->eap_teap_auth == 2) { @@ -525,18 +322,6 @@ static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data) wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x", data->tls_cs); - if (tls_get_cipher(sm->cfg->ssl_ctx, data->ssl.conn, - cipher, sizeof(cipher)) < 0) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Failed to get cipher information"); - eap_teap_state(data, FAILURE); - return -1; - } - data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; - - if (data->anon_provisioning) - wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning"); - if (eap_teap_derive_key_auth(sm, data) < 0) { eap_teap_state(data, FAILURE); return -1; @@ -626,8 +411,7 @@ static struct wpabuf * eap_teap_build_crypto_binding( if (!buf) return NULL; - if (data->send_new_pac || data->anon_provisioning || - data->basic_auth_not_done || data->inner_eap_not_done || + if (data->basic_auth_not_done || data->inner_eap_not_done || data->phase2_method || sm->cfg->eap_teap_separate_result) data->final_result = 0; else @@ -663,8 +447,6 @@ static struct wpabuf * eap_teap_build_crypto_binding( cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr)); cb->version = EAP_TEAP_VERSION; cb->received_version = data->peer_version; - /* FIX: RFC 7170 is not clear on which Flags value to use when - * Crypto-Binding TLV is used with Basic-Password-Auth */ flags = data->cmk_emsk_available ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC : TEAP_CRYPTO_BINDING_MSK_CMAC; @@ -714,144 +496,6 @@ static struct wpabuf * eap_teap_build_crypto_binding( } -static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm, - struct eap_teap_data *data) -{ - u8 pac_key[EAP_TEAP_PAC_KEY_LEN]; - u8 *pac_buf, *pac_opaque; - struct wpabuf *buf; - u8 *pos; - size_t buf_len, srv_id_info_len, pac_len; - struct teap_tlv_hdr *pac_tlv; - struct pac_attr_hdr *pac_info; - struct teap_tlv_result *result; - struct os_time now; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC"); - - if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 || - os_get_time(&now) < 0) - return NULL; - wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key", - pac_key, EAP_TEAP_PAC_KEY_LEN); - - pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) + - (2 + sm->identity_len) + 8; - pac_buf = os_malloc(pac_len); - if (!pac_buf) - return NULL; - - srv_id_info_len = os_strlen(data->srv_id_info); - - pos = pac_buf; - *pos++ = PAC_OPAQUE_TYPE_KEY; - *pos++ = EAP_TEAP_PAC_KEY_LEN; - os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN); - pos += EAP_TEAP_PAC_KEY_LEN; - - wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds", - data->pac_key_lifetime); - *pos++ = PAC_OPAQUE_TYPE_LIFETIME; - *pos++ = 4; - WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); - pos += 4; - - if (sm->identity) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity", - sm->identity, sm->identity_len); - *pos++ = PAC_OPAQUE_TYPE_IDENTITY; - *pos++ = sm->identity_len; - os_memcpy(pos, sm->identity, sm->identity_len); - pos += sm->identity_len; - } - - pac_len = pos - pac_buf; - while (pac_len % 8) { - *pos++ = PAC_OPAQUE_TYPE_PAD; - pac_len++; - } - - pac_opaque = os_malloc(pac_len + 8); - if (!pac_opaque) { - os_free(pac_buf); - return NULL; - } - if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), - pac_len / 8, pac_buf, pac_opaque) < 0) { - os_free(pac_buf); - os_free(pac_opaque); - return NULL; - } - os_free(pac_buf); - - pac_len += 8; - wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len); - - buf_len = sizeof(*pac_tlv) + - sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN + - sizeof(struct pac_attr_hdr) + pac_len + - data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); - buf = wpabuf_alloc(buf_len); - if (!buf) { - os_free(pac_opaque); - return NULL; - } - - /* Result TLV */ - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)"); - result = wpabuf_put(buf, sizeof(*result)); - WPA_PUT_BE16((u8 *) &result->tlv_type, - TEAP_TLV_MANDATORY | TEAP_TLV_RESULT); - WPA_PUT_BE16((u8 *) &result->length, 2); - WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS); - - /* PAC TLV */ - wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV"); - pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); - pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC); - - /* PAC-Key */ - eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN); - - /* PAC-Opaque */ - eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); - os_free(pac_opaque); - - /* PAC-Info */ - pac_info = wpabuf_put(buf, sizeof(*pac_info)); - pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); - - /* PAC-Lifetime (inside PAC-Info) */ - eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); - wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); - - /* A-ID (inside PAC-Info) */ - eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); - - /* Note: headers may be misaligned after A-ID */ - - if (sm->identity) { - eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, - sm->identity_len); - } - - /* A-ID-Info (inside PAC-Info) */ - eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, - srv_id_info_len); - - /* PAC-Type (inside PAC-Info) */ - eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); - wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); - - /* Update PAC-Info and PAC TLV Length fields */ - pos = wpabuf_put(buf, 0); - pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); - pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); - - return buf; -} - - static int eap_teap_encrypt_phase2(struct eap_sm *sm, struct eap_teap_data *data, struct wpabuf *plain, int piggyback) @@ -976,9 +620,6 @@ static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id) eap_teap_state(data, PHASE2_METHOD); } break; - case REQUEST_PAC: - req = eap_teap_build_pac(sm, data); - break; case FAILURE_SEND_RESULT: req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); if (data->error_code) @@ -1166,18 +807,9 @@ static void eap_teap_process_phase2_response(struct eap_sm *sm, } eap_teap_state(data, PHASE2_METHOD); - if (data->anon_provisioning) { - /* TODO: Allow any inner EAP method that provides - * mutual authentication and EMSK derivation (i.e., - * EAP-pwd or EAP-EKE). */ - next_vendor = EAP_VENDOR_IETF; - next_type = EAP_TYPE_PWD; - sm->user_eap_method_index = 0; - } else { - next_vendor = sm->user->methods[0].vendor; - next_type = sm->user->methods[0].method; - sm->user_eap_method_index = 1; - } + next_vendor = sm->user->methods[0].vendor; + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %u:%u", next_vendor, next_type); break; @@ -1509,25 +1141,23 @@ static int eap_teap_validate_crypto_binding( return -1; } + if (data->cmk_emsk_available && + (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK"); + os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN); + } else if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK"); + os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]", + data->simck, EAP_TEAP_SIMCK_LEN); + return 0; } -static int eap_teap_pac_type(u8 *pac, size_t len, u16 type) -{ - struct teap_attr_pac_type *tlv; - - if (!pac || len != sizeof(*tlv)) - return 0; - - tlv = (struct teap_attr_pac_type *) pac; - - return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE && - be_to_host16(tlv->length) == 2 && - be_to_host16(tlv->pac_type) == type; -} - - static void eap_teap_process_phase2_tlvs(struct eap_sm *sm, struct eap_teap_data *data, struct wpabuf *in_data) @@ -1556,34 +1186,6 @@ static void eap_teap_process_phase2_tlvs(struct eap_sm *sm, return; } - if (data->state == REQUEST_PAC) { - u16 type, len, res; - - if (!tlv.pac || tlv.pac_len < 6) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: No PAC Acknowledgement received"); - eap_teap_state(data, FAILURE); - return; - } - - type = WPA_GET_BE16(tlv.pac); - len = WPA_GET_BE16(tlv.pac + 2); - res = WPA_GET_BE16(tlv.pac + 4); - - if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || - res != TEAP_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC TLV did not contain acknowledgement"); - eap_teap_state(data, FAILURE); - return; - } - - wpa_printf(MSG_DEBUG, - "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded"); - eap_teap_state(data, SUCCESS); - return; - } - if (check_crypto_binding) { if (!tlv.crypto_binding) { wpa_printf(MSG_DEBUG, @@ -1623,42 +1225,10 @@ static void eap_teap_process_phase2_tlvs(struct eap_sm *sm, "EAP-TEAP: Authentication completed successfully"); } - if (data->anon_provisioning && - sm->cfg->eap_fast_prov != ANON_PROV && - sm->cfg->eap_fast_prov != BOTH_PROV) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled"); - eap_teap_state(data, FAILURE); - return; - } - - if (sm->cfg->eap_fast_prov != AUTH_PROV && - sm->cfg->eap_fast_prov != BOTH_PROV && - tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && - eap_teap_pac_type(tlv.pac, tlv.pac_len, - PAC_TYPE_TUNNEL_PAC)) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled"); - eap_teap_state(data, FAILURE); - return; - } - - if (data->anon_provisioning || - (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && - eap_teap_pac_type(tlv.pac, tlv.pac_len, - PAC_TYPE_TUNNEL_PAC))) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Requested a new Tunnel PAC"); - eap_teap_state(data, REQUEST_PAC); - } else if (data->send_new_pac) { - wpa_printf(MSG_DEBUG, - "EAP-TEAP: Server triggered re-keying of Tunnel PAC"); - eap_teap_state(data, REQUEST_PAC); - } else if (data->final_result) { + if (data->final_result) eap_teap_state(data, SUCCESS); - } else if (sm->cfg->eap_teap_separate_result) { + else if (sm->cfg->eap_teap_separate_result) eap_teap_state(data, SUCCESS_SEND_RESULT); - } } if (tlv.basic_auth_resp) { @@ -1810,7 +1380,7 @@ static int eap_teap_process_phase2_start(struct eap_sm *sm, enum eap_type next_type; if (data->identity) { - /* Used PAC and identity is from PAC-Opaque */ + /* Identity is from client certificate */ os_free(sm->identity); sm->identity = data->identity; data->identity = NULL; @@ -1823,17 +1393,16 @@ static int eap_teap_process_phase2_start(struct eap_sm *sm, next_vendor = EAP_VENDOR_IETF; next_type = EAP_TYPE_NONE; eap_teap_state(data, PHASE2_METHOD); - } else if (sm->cfg->eap_teap_pac_no_inner || - sm->cfg->eap_teap_auth == 2) { + } else if (sm->cfg->eap_teap_auth == 2) { wpa_printf(MSG_DEBUG, - "EAP-TEAP: Used PAC or client certificate and identity already known - skip inner auth"); + "EAP-TEAP: Used client certificate and identity already known - skip inner auth"); data->skipped_inner_auth = 1; - /* FIX: Need to derive CMK here. However, how is that - * supposed to be done? RFC 7170 does not tell that for - * the no-inner-auth case. */ - eap_teap_derive_cmk_basic_pw_auth(data->tls_cs, - data->simck_msk, - data->cmk_msk); + if (eap_teap_derive_imck(data->tls_cs, data->simck, + NULL, 0, NULL, 0, + data->simck_msk, data->cmk_msk, + data->simck_emsk, + data->cmk_emsk)) + return -1; eap_teap_state(data, CRYPTO_BINDING); return 1; } else if (sm->cfg->eap_teap_auth == 1) { @@ -1880,7 +1449,6 @@ static void eap_teap_process_msg(struct eap_sm *sm, void *priv, case PHASE2_BASIC_AUTH: case PHASE2_METHOD: case CRYPTO_BINDING: - case REQUEST_PAC: case SUCCESS_SEND_RESULT: eap_teap_process_phase2(sm, data, data->ssl.tls_in); break; @@ -2043,9 +1611,7 @@ static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len) if (!eapKeyData) return NULL; - /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] - * is used in this derivation */ - if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk, + if (eap_teap_derive_eap_msk(data->tls_cs, data->simck, eapKeyData) < 0) { os_free(eapKeyData); return NULL; @@ -2068,9 +1634,7 @@ static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (!eapKeyData) return NULL; - /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] - * is used in this derivation */ - if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk, + if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck, eapKeyData) < 0) { os_free(eapKeyData); return NULL; diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index 443c293ce658..0caa4c3b7b27 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -114,29 +114,6 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) #endif /* EAP_SERVER_UNAUTH_TLS */ -#ifdef CONFIG_HS20 -static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) -{ - struct eap_tls_data *data; - - data = os_zalloc(sizeof(*data)); - if (data == NULL) - return NULL; - data->state = START; - - if (eap_server_tls_ssl_init(sm, &data->ssl, 0, - EAP_WFA_UNAUTH_TLS_TYPE)) { - wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); - eap_tls_reset(sm, data); - return NULL; - } - - data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE; - return data; -} -#endif /* CONFIG_HS20 */ - - static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -237,10 +214,6 @@ static bool eap_tls_check(struct eap_sm *sm, void *priv, pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &len); - else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) - pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, respData, - &len); else pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, respData, &len); @@ -474,30 +447,3 @@ int eap_server_unauth_tls_register(void) return eap_server_method_register(eap); } #endif /* EAP_SERVER_UNAUTH_TLS */ - - -#ifdef CONFIG_HS20 -int eap_server_wfa_unauth_tls_register(void) -{ - struct eap_method *eap; - - eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, - EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, - "WFA-UNAUTH-TLS"); - if (eap == NULL) - return -1; - - eap->init = eap_wfa_unauth_tls_init; - eap->reset = eap_tls_reset; - eap->buildReq = eap_tls_buildReq; - eap->check = eap_tls_check; - eap->process = eap_tls_process; - eap->isDone = eap_tls_isDone; - eap->getKey = eap_tls_getKey; - eap->isSuccess = eap_tls_isSuccess; - eap->get_emsk = eap_tls_get_emsk; - - return eap_server_method_register(eap); -} -#endif /* CONFIG_HS20 */ diff --git a/contrib/wpa/src/eap_server/eap_server_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c index 717af2e89b71..81d1eedd423c 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -25,10 +25,6 @@ struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len, return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, code, identifier); - else if (type == EAP_WFA_UNAUTH_TLS_TYPE) - return eap_msg_alloc(EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, payload_len, - code, identifier); return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, identifier); } @@ -541,10 +537,6 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &left); - else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE) - pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, - EAP_VENDOR_WFA_UNAUTH_TLS, respData, - &left); else pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); diff --git a/contrib/wpa/src/eap_server/eap_server_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c index b21f12da7919..547435b7e7bb 100644 --- a/contrib/wpa/src/eap_server/eap_server_ttls.c +++ b/contrib/wpa/src/eap_server/eap_server_ttls.c @@ -560,6 +560,10 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, const u8 *password, size_t password_len) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build"); + eap_ttls_state(data, FAILURE); +#else /* CONFIG_FIPS */ u8 *chal, hash[CHAP_MD5_LEN]; if (challenge == NULL || password == NULL || @@ -613,6 +617,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); } +#endif /* CONFIG_FIPS */ } diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h index ad28c7962672..2a8faf9f4089 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.h +++ b/contrib/wpa/src/eap_server/eap_tls_common.h @@ -72,7 +72,6 @@ struct eap_ssl_data { /* stub type used as a flag for UNAUTH-TLS */ #define EAP_UNAUTH_TLS_TYPE 255 -#define EAP_WFA_UNAUTH_TLS_TYPE 254 struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len, diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c index e1b82ebe3605..5e702aeffb28 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -237,7 +237,7 @@ SM_STATE(AUTH_PAE, DISCONNECTED) if (!from_initialize && !pre_auth_logoff) { if (sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->flags & EAPOL_SM_PREAUTH, - sm->remediation, logoff)) { + logoff)) { wpa_printf(MSG_DEBUG, "EAPOL: Do not restart since lower layers will disconnect the port after EAPOL-Logoff"); sm->stopped = true; @@ -298,8 +298,7 @@ SM_STATE(AUTH_PAE, HELD) eap_server_get_name(0, sm->eap_type_supp)); } sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, - sm->flags & EAPOL_SM_PREAUTH, sm->remediation, - false); + sm->flags & EAPOL_SM_PREAUTH, false); } @@ -315,6 +314,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) sm->authPortStatus = Authorized; setPortAuthorized(); sm->reAuthCount = 0; + sm->reAuthenticate = false; if (sm->flags & EAPOL_SM_PREAUTH) extra = " (pre-authentication)"; else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE) @@ -327,8 +327,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) if (sm->authSuccess) sm->authenticated++; sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, - sm->flags & EAPOL_SM_PREAUTH, sm->remediation, - false); + sm->flags & EAPOL_SM_PREAUTH, false); } @@ -1029,13 +1028,9 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, struct eap_user *user) { struct eapol_state_machine *sm = ctx; - int ret; - ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, - identity_len, phase2, user); - if (user->remediation) - sm->remediation = 1; - return ret; + return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); } diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h index 7296a3acaafe..83f5c5d0b25c 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h @@ -47,7 +47,7 @@ struct eapol_auth_cb { void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, size_t datalen); bool (*finished)(void *ctx, void *sta_ctx, int success, int preauth, - int remediation, bool logoff); + bool logoff); int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); int (*sta_entry_alive)(void *ctx, const u8 *addr); diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h index a0cef0f8ec06..c970e73dd4e6 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h @@ -168,8 +168,6 @@ struct eapol_state_machine { void *sta; /* station context pointer to use in callbacks */ - int remediation; - u64 acct_multi_session_id; unsigned int authenticated; /* The number of times authentication has diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index abc1416a3a1d..3f520a5d2865 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -146,6 +146,11 @@ struct eapol_sm { bool use_eap_proxy; struct eap_proxy_sm *eap_proxy; #endif /* CONFIG_EAP_PROXY */ + +#ifdef CONFIG_IEEE8021X_AUTH + bool eap_over_auth_frame; + struct wpabuf *eapRespData; +#endif /* CONFIG_IEEE8021X_AUTH */ }; @@ -259,6 +264,17 @@ SM_STATE(SUPP_PAE, CONNECTING) sm->SUPP_PAE_state == SUPP_PAE_HELD; SM_ENTRY(SUPP_PAE, CONNECTING); +#ifdef CONFIG_IEEE8021X_AUTH + if (sm->eap_over_auth_frame) { + sm->eapTriggerStart = false; + sm->eapolEap = false; + + if (sm->startWhen == 0) + sm->startWhen = 1; + return; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + if (sm->eapTriggerStart) send_start = 1; if (sm->ctx->preauth) @@ -884,6 +900,7 @@ static void eapol_sm_getSuppRsp(struct eapol_sm *sm) static void eapol_sm_txSuppRsp(struct eapol_sm *sm) { struct wpabuf *resp; + bool use_eapol_send = true; wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); @@ -906,13 +923,25 @@ static void eapol_sm_txSuppRsp(struct eapol_sm *sm) return; } - /* Send EAP-Packet from the EAP layer to the Authenticator */ - sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, - IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), - wpabuf_len(resp)); +#ifdef CONFIG_IEEE8021X_AUTH + if (sm->eap_over_auth_frame) + use_eapol_send = false; +#endif /* CONFIG_IEEE8021X_AUTH */ - /* eapRespData is not used anymore, so free it here */ - wpabuf_free(resp); + if (use_eapol_send) { + /* Send EAP-Packet from the EAP layer to the Authenticator */ + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(resp), wpabuf_len(resp)); + + /* eapRespData is not used anymore, so free it here */ + wpabuf_free(resp); + } else { +#ifdef CONFIG_IEEE8021X_AUTH + wpabuf_free(sm->eapRespData); + sm->eapRespData = resp; +#endif /* CONFIG_IEEE8021X_AUTH */ + } if (sm->initial_req) sm->dot1xSuppEapolReqIdFramesRx++; @@ -931,6 +960,10 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm) sm->last_rx_key = NULL; wpabuf_free(sm->eapReqData); sm->eapReqData = NULL; +#ifdef CONFIG_IEEE8021X_AUTH + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; +#endif /* CONFIG_IEEE8021X_AUTH */ eap_sm_abort(sm->eap); #ifdef CONFIG_EAP_PROXY eap_proxy_sm_abort(sm->eap_proxy); @@ -2195,6 +2228,9 @@ void eapol_sm_deinit(struct eapol_sm *sm) #endif /* CONFIG_EAP_PROXY */ os_free(sm->last_rx_key); wpabuf_free(sm->eapReqData); +#ifdef CONFIG_IEEE8021X_AUTH + wpabuf_free(sm->eapRespData); +#endif /* CONFIG_IEEE8021X_AUTH */ os_free(sm->ctx); os_free(sm); } @@ -2286,3 +2322,90 @@ int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, return -1; #endif /* CONFIG_ERP */ } + + +#ifdef CONFIG_IEEE8021X_AUTH + +void eapol_sm_set_eap_over_auth_frame(struct eapol_sm *sm, bool active) +{ + if (!sm) + return; + + sm->eap_over_auth_frame = active; + if (!active) { + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + } +} + + +bool eapol_sm_get_eap_over_auth_frame(struct eapol_sm *sm) +{ + if (!sm) + return false; + return sm->eap_over_auth_frame; +} + + +struct wpabuf * eapol_sm_get_eapol_pdu(struct eapol_sm *sm, u8 type) +{ + struct wpabuf *buf = NULL, *out = NULL; + struct ieee802_1x_hdr *hdr; + + if (!sm) + return NULL; + + switch (type) { + case IEEE802_1X_TYPE_EAP_PACKET: + if (!sm->eapRespData) { + wpa_printf(MSG_INFO, + "EAPOL: EAP-Packet requested but no response data"); + return NULL; + } + + buf = sm->eapRespData; + sm->eapRespData = NULL; + break; + case IEEE802_1X_TYPE_EAPOL_START: + buf = wpabuf_alloc(0); + if (!buf) + return NULL; + break; + default: + return NULL; + } + + out = wpabuf_alloc(sizeof(*hdr) + wpabuf_len(buf)); + if (!out) { + wpabuf_free(buf); + return NULL; + } + + hdr = (struct ieee802_1x_hdr *) wpabuf_put(out, sizeof(*hdr)); + hdr->version = EAPOL_VERSION; + hdr->type = type; + hdr->length = host_to_be16(wpabuf_len(buf)); + + wpabuf_put_buf(out, buf); + wpabuf_free(buf); + + return out; +} + + +bool eapol_sm_get_success(struct eapol_sm *sm) +{ + if (!sm) + return false; + return sm->eapSuccess; +} + + +bool eapol_sm_get_failure(struct eapol_sm *sm) +{ + if (!sm) + return false; + return sm->eapFail; +} + +#endif /* CONFIG_IEEE8021X_AUTH */ diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h index 870ba1d02e7a..122bc2090103 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h @@ -373,6 +373,11 @@ int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, const u8 **realm, size_t *realm_len, u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len); +void eapol_sm_set_eap_over_auth_frame(struct eapol_sm *sm, bool active); +bool eapol_sm_get_eap_over_auth_frame(struct eapol_sm *sm); +struct wpabuf * eapol_sm_get_eapol_pdu(struct eapol_sm *sm, uint8_t type); +bool eapol_sm_get_success(struct eapol_sm *sm); +bool eapol_sm_get_failure(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) diff --git a/contrib/wpa/src/fst/fst.c b/contrib/wpa/src/fst/fst.c index 21ef3d8b78f7..210b7fcd32c7 100644 --- a/contrib/wpa/src/fst/fst.c +++ b/contrib/wpa/src/fst/fst.c @@ -166,6 +166,14 @@ void fst_global_del_ctrl(struct fst_ctrl_handle *h) void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, size_t len) { + if (is_multicast_ether_addr(mgmt->da)) { + wpa_printf(MSG_DEBUG, + "FST: Ignore group-addressed FST Action frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(mgmt->da), MAC2STR(mgmt->sa)); + return; + } + if (fst_iface_is_connected(iface, mgmt->sa, false)) fst_session_on_action_rx(iface, mgmt, len); else diff --git a/contrib/wpa/src/fst/fst_session.c b/contrib/wpa/src/fst/fst_session.c index 49886ffaaac2..f8de9a100fd9 100644 --- a/contrib/wpa/src/fst/fst_session.c +++ b/contrib/wpa/src/fst/fst_session.c @@ -18,11 +18,6 @@ #include "fst/fst_ctrl_defs.h" #endif /* CONFIG_FST_TEST */ -#define US_80211_TU 1024 - -#define US_TO_TU(m) ((m) * / US_80211_TU) -#define TU_TO_US(m) ((m) * US_80211_TU) - #define FST_LLT_SWITCH_IMMEDIATELY 0 #define fst_printf_session(s, level, format, ...) \ @@ -182,7 +177,8 @@ static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) static void fst_session_stt_arm(struct fst_session *s) { /* Action frames sometimes get delayed. Use relaxed timeout (2*) */ - eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + eloop_register_timeout(0, + 2 * TU_TO_USEC(FST_DEFAULT_SESSION_TIMEOUT_TU), fst_session_timeout_handler, NULL, s); s->stt_armed = true; } diff --git a/contrib/wpa/src/nan/Makefile b/contrib/wpa/src/nan/Makefile new file mode 100644 index 000000000000..5fd3e72e2de8 --- /dev/null +++ b/contrib/wpa/src/nan/Makefile @@ -0,0 +1,9 @@ +LIB_OBJS= \ + nan.o \ + nan_crypto.o \ + nan_ndl.o \ + nan_ndp.o \ + nan_sec.o \ + nan_util.o + +include ../lib.rules diff --git a/contrib/wpa/src/nan/nan.c b/contrib/wpa/src/nan/nan.c new file mode 100644 index 000000000000..a62edd10257e --- /dev/null +++ b/contrib/wpa/src/nan/nan.c @@ -0,0 +1,3677 @@ +/* + * Wi-Fi Aware - NAN module + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "utils/eloop.h" +#include "utils/bitfield.h" +#include "common/ieee802_11_common.h" +#include "pasn/pasn_common.h" +#include "nan.h" +#include "nan_i.h" + +#define NAN_MAX_PEERS 32 +#define NAN_MAX_NAF_LEN 1024 + +#define NAN_NDP_SETUP_TIMEOUT_LONG 30 +#define NAN_NDP_SETUP_TIMEOUT_SHORT 2 + +static void nan_peer_state_timeout(void *eloop_ctx, void *timeout_ctx); +static void nan_idle_period_timeout(void *eloop_ctx, void *timeout_ctx); +static void nan_ndp_disconnected(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, + bool locally_generated); +static void nan_set_peer_timeout(struct nan_data *nan, struct nan_peer *peer, + unsigned int sec, unsigned int usec); +static int nan_action_send(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype); + + +struct nan_data * nan_init(const struct nan_config *cfg) +{ + struct nan_data *nan; + + if (!cfg->start || !cfg->stop) + return NULL; + + nan = os_zalloc(sizeof(*nan)); + if (!nan) + return NULL; + + if (cfg->pairing_cfg.pairing_verification && + nan_nira_get_tag_nonce(cfg, nan->nira_nonce, nan->nira_tag) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to get NIRA tag and nonce"); + os_free(nan); + return NULL; + } + + nan->cfg = os_memdup(cfg, sizeof(*cfg)); + if (!nan->cfg) { + os_free(nan); + return NULL; + } + +#ifdef CONFIG_PASN + nan->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + nan->responder_pmksa = pasn_responder_pmksa_cache_init(); + if (!nan->initiator_pmksa || !nan->responder_pmksa) { + wpa_printf(MSG_INFO, + "NAN: Failed to initialize PASN PMKSA cache"); + nan_deinit(nan); + return NULL; + } +#endif /* CONFIG_PASN */ + + dl_list_init(&nan->peer_list); + + wpa_printf(MSG_DEBUG, "NAN: Initialized"); + + return nan; +} + + +static void nan_peer_flush_avail(struct nan_peer_info *info) +{ + nan_flush_avail_entries(&info->avail_entries); +} + + +static void nan_peer_flush_dev_capa(struct nan_peer_info *info) +{ + struct nan_dev_capa_entry *cur, *next; + + dl_list_for_each_safe(cur, next, &info->dev_capa, + struct nan_dev_capa_entry, list) { + dl_list_del(&cur->list); + os_free(cur); + } +} + + +static void nan_peer_flush_elem_container(struct nan_peer_info *info) +{ + struct nan_elem_container_entry *cur, *next; + + dl_list_for_each_safe(cur, next, &info->element_container, + struct nan_elem_container_entry, list) { + dl_list_del(&cur->list); + os_free(cur); + } +} + + +static void nan_peer_flush_ulw(struct nan_peer_info *info) +{ + struct nan_ulw_entry *cur, *next; + + dl_list_for_each_safe(cur, next, &info->ulw, + struct nan_ulw_entry, list) { + dl_list_del(&cur->list); + os_free(cur); + } +} + + +static struct wpabuf * +nan_peer_build_ulw_attrs(const struct nan_peer_info *info) +{ + struct nan_ulw_entry *entry; + struct wpabuf *buf; + size_t len = 0; + + if (dl_list_empty(&info->ulw)) + return NULL; + + dl_list_for_each(entry, &info->ulw, struct nan_ulw_entry, list) + len += NAN_ATTR_HDR_LEN + entry->len; + + buf = wpabuf_alloc(len); + if (!buf) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate buffer for ULW attributes"); + return NULL; + } + + dl_list_for_each(entry, &info->ulw, struct nan_ulw_entry, list) { + wpabuf_put_u8(buf, NAN_ATTR_UNALIGNED_SCHEDULE); + wpabuf_put_le16(buf, entry->len); + wpabuf_put_data(buf, entry->data, entry->len); + } + + return buf; +} + + +static void nan_ndp_setup_stop(struct nan_data *nan, struct nan_peer *peer) +{ + eloop_cancel_timeout(nan_peer_state_timeout, nan, peer); + nan_ndp_setup_reset(nan, peer); + + /* Need to also remove the NDL if no active NDPs */ + if (dl_list_empty(&peer->ndps)) + nan_ndl_reset(nan, peer); +} + + +static void nan_peer_flush_sec(struct nan_peer_info *info) +{ + struct nan_peer_sec_info_entry *cur, *next; + + dl_list_for_each_safe(cur, next, &info->sec, + struct nan_peer_sec_info_entry, list) { + dl_list_del(&cur->list); + bin_clear_free(cur, sizeof(*cur)); + } +} + + +static void nan_peer_del_sec_entry(struct nan_peer_info *info, + const u8 *peer_ndi) +{ + struct nan_peer_sec_info_entry *cur, *next; + + dl_list_for_each_safe(cur, next, &info->sec, + struct nan_peer_sec_info_entry, list) { + if (!ether_addr_equal(cur->peer_ndi, peer_ndi)) + continue; + + wpa_printf(MSG_DEBUG, + "NAN: Removing sec entry for peer_ndi=" MACSTR + " local_ndi=" MACSTR, + MAC2STR(peer_ndi), MAC2STR(cur->local_ndi)); + dl_list_del(&cur->list); + bin_clear_free(cur, sizeof(*cur)); + } +} + + +static void nan_remove_group_keys(struct nan_data *nan, struct nan_peer *peer) +{ + if (peer->igtk_id) { + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, WPA_ALG_NONE, + peer->nmi_addr, peer->igtk_id, NULL, + NULL, 0, KEY_FLAG_GROUP)) + wpa_printf(MSG_DEBUG, + "NAN: Failed to clear Rx IGTK for peer " + MACSTR, MAC2STR(peer->nmi_addr)); + peer->igtk_id = 0; + } + + if (peer->bigtk_id) { + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, WPA_ALG_NONE, + peer->nmi_addr, peer->bigtk_id, + NULL, NULL, 0, KEY_FLAG_GROUP)) + wpa_printf(MSG_DEBUG, + "NAN: Failed to clear Rx BIGTK for peer " + MACSTR, MAC2STR(peer->nmi_addr)); + peer->bigtk_id = 0; + } +} + + +static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer) +{ + if (!peer) + return; + + wpa_printf(MSG_DEBUG, "NAN: Removing peer: " MACSTR, + MAC2STR(peer->nmi_addr)); + + if (!dl_list_empty(&peer->ndps)) { + struct nan_ndp *ndp, *tndp; + + /* TODO: tear down active NDPs */ + wpa_printf(MSG_DEBUG, + "NAN: Peer delete while there are active NDPs"); + + dl_list_for_each_safe(ndp, tndp, &peer->ndps, + struct nan_ndp, list) { + dl_list_del(&ndp->list); + os_free(ndp); + } + } + + if (peer->ndp_setup.ndp) { + wpa_printf(MSG_DEBUG, + "NAN: Peer delete while NDP setup is WIP"); + nan_ndp_setup_stop(nan, peer); + } + + wpabuf_free(peer->bootstrap.npba); + peer->bootstrap.npba = NULL; + nan_bootstrap_reset(nan, peer); + dl_list_del(&peer->list); + nan_peer_flush_avail(&peer->info); + nan_peer_flush_ulw(&peer->info); + nan_peer_flush_dev_capa(&peer->info); + nan_peer_flush_elem_container(&peer->info); + nan_remove_group_keys(nan, peer); + + nan_ndl_reset(nan, peer); + nan_peer_flush_sec(&peer->info); + eloop_cancel_timeout(nan_peer_state_timeout, nan, peer); + nan_pairing_deinit_peer(peer); + os_free(peer); +} + + +static void nan_peer_clear_all(struct nan_data *nan) +{ + struct nan_peer *peer, *n_peer; + + dl_list_for_each_safe(peer, n_peer, &nan->peer_list, + struct nan_peer, list) + nan_del_peer(nan, peer); +} + + +void nan_deinit(struct nan_data *nan) +{ + wpa_printf(MSG_DEBUG, "NAN: Deinit"); + + nan_stop(nan); + nan_flush(nan); + +#ifdef CONFIG_PASN + pasn_initiator_pmksa_cache_deinit(nan->initiator_pmksa); + pasn_responder_pmksa_cache_deinit(nan->responder_pmksa); +#endif /* CONFIG_PASN */ + wpabuf_free(nan->sched.elems); + os_free(nan->cfg); + os_free(nan); +} + + +static int nan_gen_igtk(struct nan_data *nan) +{ + u8 tsc[RSN_PN_LEN]; + enum wpa_alg alg; + int cipher; + + if (((nan->cfg->security_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) == NAN_CS_INFO_CAPA_GTK_SUPP_NONE) + return 0; + + if (nan->cfg->security_capab & + NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256) { + alg = WPA_ALG_BIP_GMAC_256; + cipher = WPA_CIPHER_BIP_GMAC_256; + } else { + alg = WPA_ALG_BIP_CMAC_128; + cipher = WPA_CIPHER_AES_128_CMAC; + } + + nan->igtk.igtk_len = wpa_cipher_key_len(cipher); + nan->igtk_id = 4; + if (os_get_random(nan->igtk.igtk, nan->igtk.igtk_len) < 0) + return -1; + os_memset(tsc, 0, sizeof(tsc)); + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, alg, broadcast_ether_addr, + nan->igtk_id, tsc, nan->igtk.igtk, + nan->igtk.igtk_len, + KEY_FLAG_GROUP_TX_DEFAULT) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to install own IGTK"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: New own IGTK", nan->igtk.igtk, + nan->igtk.igtk_len); + return 0; +} + + +static int nan_gen_bigtk(struct nan_data *nan) +{ + u8 tsc[RSN_PN_LEN]; + enum wpa_alg alg; + int cipher; + + if (((nan->cfg->security_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) != NAN_CS_INFO_CAPA_GTK_SUPP_ALL) { + wpa_printf(MSG_DEBUG, "NAN: BIGTK not supported"); + return 0; + } + + if (nan->cfg->security_capab & + NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256) { + alg = WPA_ALG_BIP_GMAC_256; + cipher = WPA_CIPHER_BIP_GMAC_256; + } else { + alg = WPA_ALG_BIP_CMAC_128; + cipher = WPA_CIPHER_AES_128_CMAC; + } + + nan->bigtk.bigtk_len = wpa_cipher_key_len(cipher); + nan->bigtk_id = 6; + if (os_get_random(nan->bigtk.bigtk, nan->bigtk.bigtk_len) < 0) + return -1; + os_memset(tsc, 0, sizeof(tsc)); + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, alg, broadcast_ether_addr, + nan->bigtk_id, tsc, nan->bigtk.bigtk, + nan->bigtk.bigtk_len, + KEY_FLAG_GROUP_TX_DEFAULT) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to install own BIGTK"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: New own BIGTK", nan->bigtk.bigtk, + nan->bigtk.bigtk_len); + return 0; +} + + +int nan_start(struct nan_data *nan, const struct nan_cluster_config *config) +{ + int ret; + + wpa_printf(MSG_DEBUG, "NAN: Starting/joining NAN cluster"); + + if (nan->nan_started) { + wpa_printf(MSG_DEBUG, "NAN: Already started"); + return -1; + } + + ret = nan->cfg->start(nan->cfg->cb_ctx, config); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Failed to start - ret=%d", ret); + return ret; + } + nan->nan_started = 1; + + if (nan_gen_igtk(nan) < 0 || nan_gen_bigtk(nan) < 0) { + nan_stop(nan); + return -1; + } + + return 0; +} + + +int nan_update_config(struct nan_data *nan, + const struct nan_cluster_config *config) +{ + int ret; + + wpa_printf(MSG_DEBUG, "NAN: Update configuration"); + + if (!nan->nan_started) { + wpa_printf(MSG_DEBUG, "NAN: Not started yet"); + return -1; + } + + ret = nan->cfg->update_config(nan->cfg->cb_ctx, config); + if (ret) + wpa_printf(MSG_DEBUG, "NAN: Failed to update config. ret=%d", + ret); + + return ret; +} + + +void nan_set_cdw_overwrite(struct nan_data *nan, int map_id_2g, int map_id_5g) +{ + u16 cdw_info; + + if (!nan) + return; + + cdw_info = nan->cfg->dev_capa.cdw_info; + + if (map_id_2g >= 0) { + cdw_info &= ~NAN_CDW_INFO_2G_OVERRIDE_MASK; + cdw_info |= ((map_id_2g << NAN_CDW_INFO_2G_OVERRIDE_POS) & + NAN_CDW_INFO_2G_OVERRIDE_MASK); + } + + if (map_id_5g >= 0) { + cdw_info &= ~NAN_CDW_INFO_5G_OVERRIDE_MASK; + cdw_info |= ((map_id_5g << NAN_CDW_INFO_5G_OVERRIDE_POS) & + NAN_CDW_INFO_5G_OVERRIDE_MASK); + } + + wpa_printf(MSG_DEBUG, "NAN: Updated cdw_info=0x%04x", cdw_info); + nan->cfg->dev_capa.cdw_info = cdw_info; +} + + +void nan_flush(struct nan_data *nan) +{ + wpa_printf(MSG_DEBUG, "NAN: Reset internal state"); + + nan_peer_clear_all(nan); + wpabuf_free(nan->sched.elems); + os_memset(&nan->sched, 0, sizeof(nan->sched)); +} + + +void nan_stop(struct nan_data *nan) +{ + wpa_printf(MSG_DEBUG, "NAN: Stopping"); + + if (!nan->nan_started) { + wpa_printf(MSG_DEBUG, "NAN: Already stopped"); + return; + } + + eloop_cancel_timeout(nan_idle_period_timeout, nan, NULL); + + if (nan->igtk.igtk_len) { + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, WPA_ALG_NONE, + NULL, nan->igtk_id, NULL, NULL, + 0, KEY_FLAG_GROUP)) + wpa_printf(MSG_DEBUG, "NAN: Failed to clear own IGTK"); + + nan->igtk.igtk_len = 0; + nan->igtk_id = 0; + } + + if (nan->bigtk.bigtk_len) { + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, WPA_ALG_NONE, + NULL, nan->bigtk_id, NULL, NULL, + 0, KEY_FLAG_GROUP)) + wpa_printf(MSG_DEBUG, "NAN: Failed to clear own BIGTK"); + + nan->bigtk.bigtk_len = 0; + nan->bigtk_id = 0; + } + + /* Even though NAN is stopping, flush internal state */ + nan_flush(nan); + nan->nan_started = 0; + nan->cfg->stop(nan->cfg->cb_ctx); +} + + +struct nan_peer * nan_get_peer(struct nan_data *nan, const u8 *addr) +{ + struct nan_peer *peer; + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + if (ether_addr_equal(peer->nmi_addr, addr)) + return peer; + } + + return NULL; +} + + +/* + * nan_parse_tbm - Parse NAN Time Bitmap attribute + * + * @nan: NAN module context from nan_init() + * @tbm: On return would hold the parsed time bitmap + * @buf: Buffer holding the time bitmap + * @buf_len: Length of &buf + * Return 0 on success; otherwise -1 + */ +static int nan_parse_tbm(struct nan_data *nan, struct nan_time_bitmap *tbm, + const u8 *buf, u16 buf_len) +{ + u32 period; + u16 ctrl; + const struct nan_tbm *bm; + u8 duration_bit; + + if (buf_len < sizeof(*bm)) { + wpa_printf(MSG_DEBUG, "NAN: Too short time bitmap length (%u)", + buf_len); + return -1; + } + + bm = (const struct nan_tbm *) buf; + if (!bm->len || bm->len + sizeof(*bm) > buf_len) { + wpa_printf(MSG_DEBUG, "NAN: Invalid tbm length (%hu)", + bm->len); + return -1; + } + + if (bm->len > sizeof(tbm->bitmap)) { + wpa_printf(MSG_DEBUG, + "NAN: tbm len=%hu exceeds supported len=%zu", + bm->len, sizeof(tbm->bitmap)); + return -1; + } + + ctrl = le_to_host16(bm->ctrl); + + duration_bit = BITS(ctrl, NAN_TIME_BM_CTRL_BIT_DURATION_MASK, + NAN_TIME_BM_CTRL_BIT_DURATION_POS); + + if (duration_bit > NAN_TIME_BM_CTRL_BIT_DURATION_128_TU) { + wpa_printf(MSG_DEBUG, "NAN: Invalid time bitmap duration"); + return -1; + } + + tbm->duration = duration_bit; + + tbm->period = BITS(ctrl, NAN_TIME_BM_CTRL_PERIOD_MASK, + NAN_TIME_BM_CTRL_PERIOD_POS); + if (tbm->period) { + period = BIT(6 + tbm->period); + } else { + wpa_printf(MSG_DEBUG, + "NAN: Bitmap with period=0 is not supported"); + return -1; + } + + tbm->offset = BITS(ctrl, NAN_TIME_BM_CTRL_START_OFFSET_MASK, + NAN_TIME_BM_CTRL_START_OFFSET_POS); + + if (bm->len * 8 * BIT(4 + tbm->duration) > period) { + wpa_printf(MSG_DEBUG, + "NAN: tbm is longer than the repeat period"); + return -1; + } + + if (tbm->offset * 16 > period) { + wpa_printf(MSG_DEBUG, + "NAN: tbm offset %u exceeds period %u", + tbm->offset, period); + return -1; + } + + tbm->len = bm->len; + os_memcpy(tbm->bitmap, bm->bitmap, tbm->len); + return 0; +} + + +/* + * nan_parse_band_chan_list - Parse NAN Band/Channel List entry + * + * @nan: NAN module context from nan_init() + * @entry: On return would hold the parsed band/channel list + * @list: Buffer holding the band/channel list + * @len: Length of &list + * Return 0 on success; otherwise -1 + */ +static int nan_parse_band_chan_list(struct nan_data *nan, + struct nan_avail_entry *entry, + const struct nan_band_chan_list *list, + u16 len) +{ + u8 band_chan_size, i; + bool non_cont; + const u8 *pos; + + if (len < sizeof(*list)) { + wpa_printf(MSG_DEBUG, + "NAN: Too short channel/band list=%hu", len); + return -1; + } + + entry->band_chan_type = list->ctrl & NAN_BAND_CHAN_CTRL_TYPE; + entry->n_band_chan = BITS(list->ctrl, + NAN_BAND_CHAN_CTRL_NUM_ENTRIES_MASK, + NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS); + + len -= sizeof(*list); + pos = list->entries; + + if (entry->band_chan_type == NAN_TYPE_BAND) { + if (!len || !entry->n_band_chan || len < entry->n_band_chan) { + wpa_printf(MSG_DEBUG, + "NAN: Truncated band list. len=%u, band_chan=%u", + len, entry->n_band_chan); + return -1; + } + + entry->band_chan = os_zalloc(sizeof(*entry->band_chan) * + entry->n_band_chan); + if (!entry->band_chan) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate band list"); + return -1; + } + + for (i = 0; i < entry->n_band_chan; i++) + entry->band_chan[i].u.band_id = pos[i]; + + return 0; + } + + non_cont = list->ctrl & NAN_BAND_CHAN_CTRL_NON_CONT_BW; + band_chan_size = non_cont ? NAN_CHAN_ENTRY_80P80_LEN : + NAN_CHAN_ENTRY_MIN_LEN; + + if (len < entry->n_band_chan * band_chan_size) { + wpa_printf(MSG_DEBUG, "NAN: Truncated channel list"); + return -1; + } + + entry->band_chan = os_zalloc(sizeof(*entry->band_chan) * + entry->n_band_chan); + if (!entry->band_chan) { + wpa_printf(MSG_DEBUG, "NAN: Failed to allocate channel list"); + return -1; + } + + for (i = 0; i < entry->n_band_chan; i++) { + struct nan_band_chan *curr = &entry->band_chan[i]; + const struct nan_chan_entry *chan = + (const struct nan_chan_entry *) pos; + + curr->u.chan.op_class = chan->op_class; + curr->u.chan.chan_bitmap = chan->chan_bitmap; + curr->u.chan.pri_chan_bitmap = chan->pri_chan_bitmap; + if (non_cont) + curr->u.chan.aux_chan_bitmap = chan->aux_chan_bitmap; + + pos += band_chan_size; + } + + return 0; +} + + +/* + * nan_split_avail_entry - Split an availability entry + * + * @nan: NAN module context from nan_init() + * @entry: Original entry to split + * Returns a newly allocated potential entry on success, otherwise NULL. + * + * The function expects an availability entry which is both + * committed/conditional and potential and has more than one channel entry. It + * splits the original entry such that: + * + * - The original entry is only committed/conditional with one channel entry + * - A new potential entry with the rest of the channels specified in the + * original entry. + */ +static struct nan_avail_entry * +nan_split_avail_entry(struct nan_data *nan, + struct nan_avail_entry *entry) +{ + struct nan_avail_entry *pot; + struct nan_band_chan *tmp; + + wpa_printf(MSG_DEBUG, + "NAN: Split Committed/Conditional and potential entry"); + + pot = os_zalloc(sizeof(*pot)); + if (!pot) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate availability entry"); + return NULL; + } + + pot->map_id = entry->map_id; + pot->type = NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL; + pot->preference = entry->preference; + pot->utilization = entry->utilization; + pot->rx_nss = entry->rx_nss; + + dl_list_init(&pot->list); + + pot->tbm.duration = entry->tbm.duration; + pot->tbm.period = entry->tbm.period; + pot->tbm.offset = entry->tbm.offset; + pot->tbm.len = entry->tbm.len; + + os_memcpy(pot->tbm.bitmap, entry->tbm.bitmap, pot->tbm.len); + + pot->band_chan_type = entry->band_chan_type; + pot->n_band_chan = entry->n_band_chan - 1; + pot->band_chan = os_zalloc(sizeof(*pot->band_chan) * + pot->n_band_chan); + if (!pot->band_chan) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate channel list: potential"); + os_free(pot); + return NULL; + } + + os_memcpy(pot->band_chan, &entry->band_chan[1], + sizeof(*pot->band_chan) * pot->n_band_chan); + + tmp = entry->band_chan; + + /* Clear potential from the original entry */ + entry->type &= ~NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL; + entry->band_chan = os_memdup(tmp, sizeof(*entry->band_chan)); + if (!entry->band_chan) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate channel list: committed"); + os_free(pot->band_chan); + os_free(pot); + return NULL; + } + + entry->n_band_chan = 1; + + os_free(tmp); + return pot; +} + + +/* + * nan_parse_avail_entry - Parse a NAN availability entry + * + * @nan: NAN module context from nan_init() + * @peer_info: Peer info where the parsed entry would be added + * @avail_entry: Pointer to the availability entry + * @entry_len: Length of the availability entry + * @map_id: Map ID of the availability attribute that this entry belongs to + * Returns: 0 on success, -1 on failure, or 0 to skip the entry + */ +static int nan_parse_avail_entry(struct nan_data *nan, + struct nan_peer_info *peer_info, + const struct nan_avail_ent *avail_entry, + u16 entry_len, u8 map_id) +{ + struct nan_avail_entry *entry; + const u8 *pos; + u16 ctrl, len; + u8 type, preference, utilization; + + if (entry_len < MIN_AVAIL_ENTRY_LEN) { + wpa_printf(MSG_DEBUG, + "NAN: Too short availability entry len=%hu", + entry_len); + return -1; + } + + ctrl = le_to_host16(avail_entry->ctrl); + + type = ctrl & NAN_AVAIL_ENTRY_CTRL_TYPE_MASK; + if (!type || + ((type & NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED) && + (type & NAN_AVAIL_ENTRY_CTRL_TYPE_COND))) { + wpa_printf(MSG_DEBUG, "NAN: Invalid entry type=0x%x", type); + return -1; + } + + preference = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_MASK, + NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS); + utilization = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_UTIL_MASK, + NAN_AVAIL_ENTRY_CTRL_UTIL_POS); + + if (utilization > NAN_AVAIL_ENTRY_CTRL_UTIL_MAX && + utilization != NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN) { + wpa_printf(MSG_DEBUG, "NAN: Invalid tbm utilization"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "NAN: Avail entry: map_id=%u, ctrl=0x%04x, entry_len=%u, type=0x%x, pref=0x%x", + map_id, ctrl, entry_len, type, preference); + + entry = os_zalloc(sizeof(*entry)); + if (!entry) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate availability entry"); + return -1; + } + + entry->map_id = map_id; + entry->type = type; + entry->preference = preference; + entry->utilization = utilization; + dl_list_init(&entry->list); + + entry->rx_nss = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_RX_NSS_MASK, + NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS); + if (!entry->rx_nss) { + wpa_printf(MSG_DEBUG, + "NAN: Avail entry with rx_nss=0. Override to 1"); + entry->rx_nss = 1; + } + + len = entry_len - MIN_AVAIL_ENTRY_LEN; + pos = avail_entry->optional; + + if (ctrl & NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT) { + wpa_printf(MSG_DEBUG, + "NAN: Availability entry: Time bitmap is set"); + + if (len < sizeof(struct nan_tbm)) { + wpa_printf(MSG_DEBUG, + "NAN: Time bitmap set: length too short"); + goto out; + } + + if (nan_parse_tbm(nan, &entry->tbm, pos, len)) + goto out; + + pos += entry->tbm.len + sizeof(struct nan_tbm); + len -= entry->tbm.len + sizeof(struct nan_tbm); + } else { + entry->tbm.len = 0; + os_memset(entry->tbm.bitmap, 0, sizeof(entry->tbm.bitmap)); + } + + if (nan_parse_band_chan_list(nan, entry, + (const struct nan_band_chan_list *) pos, + len)) + goto out; + + /* + * An entry with committed/conditional can either have a single channel + * entry, or multiple channel entries. The latter case is allowed only + * if the entry is also potential, in which case the first channel entry + * belongs to the committed entry and the other channels are potential. + */ + if (entry->type & (NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED | + NAN_AVAIL_ENTRY_CTRL_TYPE_COND)) { + if (entry->band_chan_type != NAN_TYPE_CHANNEL) { + wpa_printf(MSG_DEBUG, + "NAN: Committed/cond avail entry with band"); + goto out; + } + + if (entry->n_band_chan < 1) { + wpa_printf(MSG_DEBUG, + "NAN: Committed/cond avail entry: no channels"); + goto out; + } + + if (entry->n_band_chan > 1) { + struct nan_avail_entry *pot_avail; + + if (!(entry->type & + NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL)) { + wpa_printf(MSG_DEBUG, + "NAN: Committed/cond avail entry: %u chans", + entry->n_band_chan); + goto out; + } + + pot_avail = nan_split_avail_entry(nan, entry); + if (!pot_avail) + goto out; + + dl_list_add(&peer_info->avail_entries, + &pot_avail->list); + } else { + /* + * Committed/conditional with single channel entry. + * Clear the potential in case that it is set. + */ + entry->type &= ~NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL; + } + } + + dl_list_add(&peer_info->avail_entries, &entry->list); + return 0; + +out: + nan_del_avail_entry(entry); + return -1; +} + + +/* + * nan_parse_avail_attr - Parse NAN Availability attribute + * + * @nan: NAN module context from nan_init() + * @peer_info: Peer info where the parsed entries would be added + * @avail_attr: Pointer to the availability attribute + * + * Parse availability attribute as defined in Wi-Fi Aware Specification + * v4.0, section 9.5.17.1. + */ +static int nan_parse_avail_attr(struct nan_data *nan, + struct nan_peer_info *peer_info, + const struct nan_avail *avail_attr, + u16 attr_len) +{ + u8 map_id; + const u8 *entries; + u16 ctrl, entries_len; + + wpa_printf(MSG_DEBUG, "NAN: Parse avail attr: len=%u", attr_len); + if (attr_len < sizeof(*avail_attr)) + return -1; + + ctrl = le_to_host16(avail_attr->ctrl); + map_id = ctrl & NAN_AVAIL_CTRL_MAP_ID_MASK; + + entries = avail_attr->optional; + entries_len = attr_len - sizeof(*avail_attr); + if (!entries_len) { + wpa_printf(MSG_DEBUG, + "NAN: Availability attribute without any entries"); + return -1; + } + + while (entries_len > 2) { + u16 entry_len = WPA_GET_LE16(entries); + const struct nan_avail_ent *avail_entry = + (const struct nan_avail_ent *) entries; + + if (entry_len + 2 > entries_len) { + wpa_printf(MSG_DEBUG, + "NAN: Truncated availability entry"); + return -1; + } + + if (nan_parse_avail_entry(nan, peer_info, avail_entry, + entry_len, map_id)) + return -1; + + entries += entry_len + 2; + entries_len -= entry_len + 2; + } + + if (entries_len) { + wpa_printf(MSG_DEBUG, + "NAN: Availability entries list truncated"); + return -1; + } + + return 0; +} + + +static void nan_peer_dump_info(struct nan_data *nan, struct nan_peer_info *info) +{ + struct nan_avail_entry *entry; + + wpa_printf(MSG_DEBUG, + "NAN: info: seen=%lu.%lu, seq_id=%u", + info->last_seen.sec, info->last_seen.usec, + info->seq_id); + + dl_list_for_each(entry, &info->avail_entries, struct nan_avail_entry, + list) { + unsigned int i; + + wpa_printf(MSG_DEBUG, + "NAN: entry: map_id=%u, type=0x%x, pref=%u, util=%u", + entry->map_id, entry->type, entry->preference, + entry->utilization); + wpa_printf(MSG_DEBUG, + "NAN: entry: band_channel_type=%u, n_band_chan=%u", + entry->band_chan_type, entry->n_band_chan); + + for (i = 0; i < entry->n_band_chan; i++) { + struct nan_band_chan *bc = &entry->band_chan[i]; + + if (entry->type == NAN_TYPE_BAND) + wpa_printf(MSG_DEBUG, + "NAN: band: %u", bc->u.band_id); + else + wpa_printf(MSG_DEBUG, + "NAN: channel: oc=%u, cbtm=0x%x, pcbtm=0x%x", + bc->u.chan.op_class, + bc->u.chan.chan_bitmap, + bc->u.chan.pri_chan_bitmap); + } + } +} + + +static void nan_peer_dump(struct nan_data *nan, struct nan_peer *peer) +{ + wpa_printf(MSG_DEBUG, + "NAN: peer: " MACSTR " last_seen=%lu.%lu", + MAC2STR(peer->nmi_addr), peer->last_seen.sec, + peer->last_seen.usec); + + nan_peer_dump_info(nan, &peer->info); +} + + +static void nan_peer_disconnect_all_ndps(struct nan_data *nan, + struct nan_peer *peer) +{ + struct nan_ndp *ndp, *tmp; + u8 *local_ndi = NULL, *peer_ndi = NULL; + struct nan_ndp_id ndp_id; + + if (peer->ndp_setup.ndp) + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + false); + + dl_list_for_each_safe(ndp, tmp, &peer->ndps, struct nan_ndp, list) { + if (ndp->initiator) { + local_ndi = ndp->init_ndi; + peer_ndi = ndp->resp_ndi; + } else { + local_ndi = ndp->resp_ndi; + peer_ndi = ndp->init_ndi; + } + + os_memcpy(&ndp_id.peer_nmi, peer->nmi_addr, ETH_ALEN); + os_memcpy(ndp_id.init_ndi, ndp->init_ndi, ETH_ALEN); + ndp_id.id = ndp->ndp_id; + + peer->ndp_setup.ndp = ndp; + peer->ndp_setup.state = NAN_NDP_STATE_DONE; + peer->ndp_setup.status = NAN_NDP_STATUS_REJECTED; + peer->ndp_setup.reason = NAN_REASON_UNSPECIFIED_REASON; + nan_action_send(nan, peer, NAN_SUBTYPE_DATA_PATH_TERMINATION); + peer->ndp_setup.ndp = NULL; + + dl_list_del(&ndp->list); + nan_ndp_terminated(nan, peer, &ndp_id, local_ndi, peer_ndi, + NAN_REASON_UNSPECIFIED_REASON, + ndp->gtk_id); + os_free(ndp); + } +} + + +static void nan_peer_update_schedule(struct nan_data *nan, + struct nan_peer *peer, + struct nan_schedule *sched) +{ + struct bitfield *common_bf; + int ret = -1; + + common_bf = nan_peer_schedule_intersection(nan, peer, sched); + if (common_bf && nan_ndl_meets_qos(nan, peer, common_bf) && + nan_ndl_validate_peer_avail(nan, peer)) + ret = nan_configure_peer_schedule(nan, peer, sched); + else + wpa_printf(MSG_DEBUG, "NAN: New peer schedule breaks NDL"); + + if (ret) + nan_peer_disconnect_all_ndps(nan, peer); + else if (nan->cfg->schedule_changed) + nan->cfg->schedule_changed(nan->cfg->cb_ctx, peer->nmi_addr); + + bitfield_free(common_bf); +} + + +/* + * Update the old peer info with information from the new peer info. + * Information that is available in the old peer info but is not available + * in the new peer info will not be changed. + * Peer schedule may be updated if the peer availabilty or ULW changed. + */ +static void nan_merge_peer_info(struct nan_data *nan, struct nan_peer *peer, + struct nan_peer_info *old, + struct nan_peer_info *new) +{ + bool schedule_changed = false; + + if (!dl_list_empty(&new->avail_entries)) { + struct nan_avail_entry *avail, *tmp; + + nan_peer_flush_avail(old); + dl_list_init(&old->avail_entries); + + dl_list_for_each_safe(avail, tmp, &new->avail_entries, + struct nan_avail_entry, list) { + dl_list_del(&avail->list); + dl_list_add(&old->avail_entries, &avail->list); + } + old->seq_id = new->seq_id; + schedule_changed = true; + } + + if (!dl_list_empty(&new->ulw)) { + struct nan_ulw_entry *entry, *tmp; + + nan_peer_flush_ulw(old); + dl_list_init(&old->ulw); + + dl_list_for_each_safe(entry, tmp, &new->ulw, + struct nan_ulw_entry, list) { + dl_list_del(&entry->list); + dl_list_add(&old->ulw, &entry->list); + } + + schedule_changed = true; + } + + old->last_seen = new->last_seen; + + if (schedule_changed && peer->ndl && + peer->ndl->state == NAN_NDL_STATE_DONE) + nan_peer_update_schedule(nan, peer, &nan->sched); +} + + +static int nan_avail_info(struct nan_data *nan, struct nan_peer *peer, + struct nan_attrs *attrs, struct nan_peer_info *info) +{ + const struct nan_avail *avail_attr; + const struct nan_attrs_entry *attr; + + attr = dl_list_first(&attrs->avail, struct nan_attrs_entry, list); + if (!attr) + return 0; + + avail_attr = (const struct nan_avail *) attr->ptr; + + /* + * The sequence ID may wrap around, so if the received sequence iD is + * much smaller than the sequence ID of the last update, assume it has + * wrapped around and accept the new schedule. Otherwise, ignore it as + * an old schedule. + */ + if (!dl_list_empty(&peer->info.avail_entries) && + peer->info.seq_id >= avail_attr->seq_id && + peer->info.seq_id - avail_attr->seq_id < 128) { + wpa_printf(MSG_DEBUG, + "NAN: Ignore peer avail update: seq_id=%hhu, seq_id=%hhu", + avail_attr->seq_id, peer->info.seq_id); + return 0; + } + + info->seq_id = avail_attr->seq_id; + + dl_list_for_each(attr, &attrs->avail, struct nan_attrs_entry, list) { + avail_attr = (const struct nan_avail *) attr->ptr; + + if (avail_attr->seq_id != info->seq_id) { + wpa_printf(MSG_DEBUG, + "NAN: Not all avail attributes have the same seq_id"); + goto out; + } + + if (nan_parse_avail_attr(nan, info, avail_attr, attr->len)) + goto out; + } + + return 0; +out: + nan_peer_flush_avail(info); + return -1; +} + + +static struct nan_dev_capa_entry * nan_get_dev_capa_entry(struct nan_peer *peer, + u8 map_id) +{ + struct nan_dev_capa_entry *entry; + + dl_list_for_each(entry, &peer->info.dev_capa, + struct nan_dev_capa_entry, list) { + if (entry->map_id == map_id) + return entry; + } + + return NULL; +} + + +static void nan_parse_peer_device_capa_attr(struct nan_data *nan, + struct nan_peer *peer, + const struct nan_attrs_entry *attr) +{ + const struct nan_device_capa *capa; + struct nan_dev_capa_entry *entry; + + capa = (const struct nan_device_capa *) attr->ptr; + + /* See if we already have an entry for this map ID */ + entry = nan_get_dev_capa_entry(peer, capa->map_id); + if (!entry) { + entry = os_zalloc(sizeof(*entry)); + if (!entry) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate device capability entry"); + return; + } + + dl_list_init(&entry->list); + dl_list_add(&peer->info.dev_capa, &entry->list); + } + + entry->map_id = capa->map_id; + entry->capa.cdw_info = le_to_host16(capa->cdw_info); + entry->capa.supported_bands = capa->supported_bands; + entry->capa.op_mode = capa->op_mode; + entry->capa.n_antennas = capa->ant; + entry->capa.channel_switch_time = + le_to_host16(capa->channel_switch_time); + entry->capa.capa = capa->capa; +} + + +static void nan_parse_peer_device_capa(struct nan_data *nan, + struct nan_peer *peer, + const struct nan_attrs *attrs) +{ + const struct nan_attrs_entry *attr; + + dl_list_for_each(attr, &attrs->dev_capa, struct nan_attrs_entry, list) + nan_parse_peer_device_capa_attr(nan, peer, attr); +} + + +static void nan_parse_peer_elem_container_attr( + struct nan_data *nan, struct nan_peer *peer, + const struct nan_attrs_entry *attr) +{ + struct nan_elem_container_entry *entry, *next; + u8 map_id = *attr->ptr; + + /* Guarantee that there is only a single entry for each map ID */ + dl_list_for_each_safe(entry, next, &peer->info.element_container, + struct nan_elem_container_entry, list) { + if (entry->map_id == map_id) { + dl_list_del(&entry->list); + os_free(entry); + break; + } + } + + entry = os_zalloc(sizeof(*entry) + attr->len - 1); + if (!entry) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to allocate element container entry"); + return; + } + + dl_list_init(&entry->list); + dl_list_add(&peer->info.element_container, &entry->list); + + entry->map_id = map_id; + entry->len = attr->len - 1; + os_memcpy(entry->data, attr->ptr + 1, entry->len); +} + + +static void nan_parse_peer_elem_container(struct nan_data *nan, + struct nan_peer *peer, + const struct nan_attrs *attrs) +{ + const struct nan_attrs_entry *attr; + + dl_list_for_each(attr, &attrs->element_container, + struct nan_attrs_entry, list) + nan_parse_peer_elem_container_attr(nan, peer, attr); +} + + +static int nan_parse_peer_ulw(const struct nan_attrs *attrs, + const struct nan_peer_info *cur_info, + struct nan_peer_info *info) +{ + struct nan_ulw_entry *cur; + struct nan_attrs_entry *attr; + u8 max_seq_id = 0; + bool max_seq_id_valid = false; + + if (dl_list_empty(&attrs->ulw)) + return 0; + + dl_list_for_each(cur, &cur_info->ulw, struct nan_ulw_entry, list) { + const struct nan_unaligned_sched *ulw; + + ulw = (const struct nan_unaligned_sched *) cur->data; + if (!max_seq_id_valid || ulw->seq_id > max_seq_id) { + max_seq_id = ulw->seq_id; + max_seq_id_valid = true; + } + } + + dl_list_for_each(attr, &attrs->ulw, struct nan_attrs_entry, list) { + struct nan_ulw_entry *entry; + const struct nan_unaligned_sched *ulw = + (const struct nan_unaligned_sched *) attr->ptr; + + if (max_seq_id_valid && ulw->seq_id <= max_seq_id) { + wpa_printf(MSG_DEBUG, + "NAN: Skip old ULW entry with seq_id=%u", + ulw->seq_id); + continue; + } + + entry = os_zalloc(sizeof(*entry) + attr->len); + if (!entry) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate ULW entry"); + nan_peer_flush_ulw(info); + return -1; + } + + dl_list_init(&entry->list); + dl_list_add(&info->ulw, &entry->list); + + entry->len = attr->len; + os_memcpy(entry->data, attr->ptr, entry->len); + } + + return 0; +} + + +void nan_parse_peer_dev_capa_ext(struct nan_data *nan, struct nan_peer *peer, + struct nan_attrs *attrs) +{ + if (!attrs->dev_capa_ext || attrs->dev_capa_ext_len <= 1) + return; + + peer->pairing.pairing_cfg.pairing_setup = attrs->dev_capa_ext[1] & + NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP; + peer->pairing.pairing_cfg.npk_caching = attrs->dev_capa_ext[1] & + NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING; +} + + +static void nan_parse_npba(struct nan_data *nan, struct nan_peer *peer, + struct nan_attrs *attrs) +{ + const u8 *npba = attrs->npba; + u8 type; + + if (!attrs->npba || attrs->npba_len < 5) + return; + + /* Skip the dialog token and get the type */ + type = npba[1] & NAN_PBA_TYPE_MASK; + if (type != NAN_PBA_TYPE_ADVERTISE) + return; + + peer->bootstrap.supported_methods = WPA_GET_LE16(npba + 3); + + wpa_printf(MSG_DEBUG, "NAN: Peer supports bootstrap methods: 0x%04x", + peer->bootstrap.supported_methods); +} + + +static void nan_parse_nira(struct nan_data *nan, struct nan_peer *peer, + struct nan_attrs *attrs) +{ + const u8 *pos; + + if (!attrs->nira) + return; + + peer->pairing.pairing_cfg.pairing_verification = true; + + pos = attrs->nira + 1; + os_memcpy(peer->pairing.nonce, pos, NAN_NIRA_NONCE_LEN); + pos += NAN_NIRA_NONCE_LEN; + os_memcpy(peer->pairing.tag, pos, NAN_NIRA_TAG_LEN); + peer->pairing.nonce_tag_valid = true; +} + + +/* + * nan_parse_device_attrs - Parse device attributes and build availability info + * + * @nan: NAN module context from nan_init() + * @peer: NAN peer + * @attrs_data: Buffer holding the device attributes + * @attrs_len: Length of &attrs_data in octets + * Return 0 on success; -1 otherwise. + */ +int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer, + const u8 *attrs_data, size_t attrs_len) +{ + struct nan_peer_info info; + struct nan_attrs attrs; + int ret; + + os_memset(&info, 0, sizeof(info)); + dl_list_init(&info.avail_entries); + dl_list_init(&info.ulw); + os_get_reltime(&info.last_seen); + + if (nan_parse_attrs(nan, attrs_data, attrs_len, &attrs)) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to parse peer " MACSTR " attributes", + MAC2STR(peer->nmi_addr)); + return -1; + } + + if (nan_avail_info(nan, peer, &attrs, &info)) { + ret = -1; + goto out; + } + + if (nan_parse_peer_ulw(&attrs, &peer->info, &info)) { + ret = -1; + goto out; + } + + nan_merge_peer_info(nan, peer, &peer->info, &info); + nan_parse_peer_device_capa(nan, peer, &attrs); + nan_parse_peer_elem_container(nan, peer, &attrs); + nan_parse_peer_dev_capa_ext(nan, peer, &attrs); + nan_parse_npba(nan, peer, &attrs); + nan_parse_nira(nan, peer, &attrs); + + nan_peer_dump(nan, peer); + ret = 0; +out: + nan_peer_flush_avail(&info); + nan_peer_flush_ulw(&info); + nan_attrs_clear(nan, &attrs); + return ret; +} + + +static struct nan_peer * nan_alloc_peer(struct nan_data *nan) +{ + struct nan_peer *peer, *oldest = NULL; + size_t count = 0; + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + count++; + + /* Do not expire peers that we have NDPs with */ + if (!dl_list_empty(&peer->ndps) || peer->ndp_setup.ndp) + continue; + + if (!oldest || + os_reltime_before(&peer->last_seen, &oldest->last_seen)) + oldest = peer; + } + + if (count >= NAN_MAX_PEERS) { + if (!oldest) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot remove any of the peers"); + return NULL; + } + + wpa_printf(MSG_DEBUG, + "NAN: Remove peer=" MACSTR " to make room", + MAC2STR(oldest->nmi_addr)); + + nan_del_peer(nan, oldest); + } + + peer = os_zalloc(sizeof(*peer)); + if (!peer) + return NULL; + + dl_list_init(&peer->info.avail_entries); + dl_list_init(&peer->info.ulw); + dl_list_init(&peer->info.dev_capa); + dl_list_init(&peer->info.element_container); + dl_list_init(&peer->info.sec); + + dl_list_add(&nan->peer_list, &peer->list); + dl_list_init(&peer->ndps); + return peer; +} + + +int nan_add_peer(struct nan_data *nan, const u8 *addr, + const u8 *device_attrs, size_t device_attrs_len) +{ + struct nan_peer *peer; + + /* Allow adding peer devices even if NAN was not started, to support + * discovery during USD, etc. */ + if (!nan) + return -1; + + if (!device_attrs || !device_attrs_len) { + wpa_printf(MSG_DEBUG, + "NAN: Ignore add_peer with no device attributes"); + return -1; + } + + peer = nan_get_peer(nan, addr); + if (!peer) { + peer = nan_alloc_peer(nan); + if (!peer) + return -1; + + os_memcpy(peer->nmi_addr, addr, ETH_ALEN); + } + + nan_parse_device_attrs(nan, peer, device_attrs, device_attrs_len); + + os_get_reltime(&peer->last_seen); + return 0; +} + + +static void nan_action_build_header(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf, + enum nan_subtype subtype) +{ + u8 category = WLAN_ACTION_PUBLIC; + + if (nan_pairing_is_peer_paired(nan, peer->nmi_addr) || + !dl_list_empty(&peer->info.sec)) + category = WLAN_ACTION_PROTECTED_DUAL; + + wpabuf_put_u8(buf, category); + wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, NAN_NAF_OUI_TYPE); + wpabuf_put_u8(buf, subtype); +} + + +static int nan_action_build(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype, struct wpabuf *buf) +{ + int ret; + + wpa_printf(MSG_DEBUG, "NAN: Build NAF"); + + nan_action_build_header(nan, peer, buf, subtype); + + nan_add_dev_capa_attr(nan, buf); + nan_add_dev_capa_ext_attr(nan, buf); + + ret = nan_ndp_add_ndp_attr(nan, peer, buf); + if (ret) + return ret; + + ret = nan_sec_add_attrs(nan, peer, subtype, buf); + if (ret) + return ret; + + ret = nan_ndl_add_avail_attrs(nan, peer, buf); + if (ret) + return ret; + + ret = nan_ndl_add_ndc_attr(nan, peer, buf); + if (ret) + return ret; + + ret = nan_ndl_add_ndl_attr(nan, peer, buf); + if (ret) + return ret; + + ret = nan_ndl_add_qos_attr(nan, peer, buf); + if (ret) + return ret; + + nan_ndl_add_elem_container_attr(nan, peer, buf); + + wpa_printf(MSG_DEBUG, "NAN: Build NAF: Done"); + + return 0; +} + + +static int nan_action_send(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype) +{ + struct wpabuf *buf; + struct nan_peer_sec_info_entry *cur, *next; + const u8 *src, *dst; + int ret; + + buf = wpabuf_alloc(NAN_MAX_NAF_LEN); + if (!buf) + return -1; + + ret = nan_action_build(nan, peer, subtype, buf); + if (ret) + goto out; + + ret = nan_sec_pre_tx(nan, peer, buf); + if (ret) + goto out; + + if (!nan->cfg->send_naf) + goto out; + + /* + * By default, the NAN management interface is used for the NAF + * transmission. However, when pairing was not established with the peer + * and there is a secure NDP with the peer, need to use the NDIs so that + * the NAF would be sent in a secure manner + */ + src = NULL; + dst = peer->nmi_addr; + + if (!(peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED)) { + dl_list_for_each_safe(cur, next, &peer->info.sec, + struct nan_peer_sec_info_entry, list) { + src = cur->local_ndi; + dst = cur->peer_ndi; + break; + } + } + + ret = nan->cfg->send_naf(nan->cfg->cb_ctx, dst, src, + nan->cluster_id, buf); +out: + wpa_printf(MSG_DEBUG, "NAN: send_naf: ret=%d", ret); + wpabuf_free(buf); + return ret; +} + + +static bool nan_ndp_supported(struct nan_data *nan) +{ + if (nan->cfg->ndp_action_notif && nan->cfg->ndp_connected && + nan->cfg->ndp_disconnected && + nan->cfg->send_naf && nan->cfg->get_chans && + nan->cfg->is_valid_publish_id && + nan->cfg->set_peer_schedule) + return true; + + wpa_printf(MSG_DEBUG, "NAN: NDP operations are not supported"); + return false; +} + + +static void +nan_peer_get_committed_avail(const struct nan_data *nan, + const struct nan_peer *peer, + const struct nan_schedule *local_sched, + struct nan_peer_schedule *sched); + + +int nan_configure_peer_schedule(struct nan_data *nan, struct nan_peer *peer, + const struct nan_schedule *local_sched) +{ + int ret; + struct nan_dev_capa_entry *cur; + struct nan_device_capabilities *capa = NULL; + struct nan_peer_schedule sched; + struct bitfield *common_bf; + struct wpabuf *ulw_elems; + + wpa_printf(MSG_DEBUG, "NAN: Configure peer schedule for " MACSTR, + MAC2STR(peer->nmi_addr)); + + if (nan->sched_update_pending) { + wpa_printf(MSG_DEBUG, + "NAN: Skip peer schedule config - local schedule update pending"); + return 0; + } + + os_memset(&sched, 0, sizeof(sched)); + common_bf = nan_peer_schedule_intersection(nan, peer, local_sched); + if (common_bf) + nan_peer_get_committed_avail(nan, peer, local_sched, &sched); + else + wpa_printf(MSG_DEBUG, + "NAN: Cannot configure peer schedule since there is no intersection"); + + bitfield_free(common_bf); + + dl_list_for_each(cur, &peer->info.dev_capa, + struct nan_dev_capa_entry, list) { + /* + * Take the first one, as both CDW and channel switch time are + * identical across all attributes + */ + capa = &cur->capa; + break; + } + + if (!capa) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot configure peer NMI STA - no device capabilities"); + return -1; + } + + ulw_elems = nan_peer_build_ulw_attrs(&peer->info); + + ret = nan->cfg->set_peer_schedule(nan->cfg->cb_ctx, peer->nmi_addr, + !peer->configured, capa->cdw_info, + peer->info.seq_id, + capa->channel_switch_time, &sched, + ulw_elems); + wpabuf_free(ulw_elems); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Failed to set peer schedule"); + return ret; + } + + peer->configured = true; + return 0; +} + + +int nan_clear_peer_schedule(struct nan_data *nan, struct nan_peer *peer) +{ + int ret; + + wpa_printf(MSG_DEBUG, "NAN: Clear peer schedule, peer->configured=%d", + peer->configured); + + if (!peer->configured) + return 0; + + ret = nan->cfg->set_peer_schedule(nan->cfg->cb_ctx, peer->nmi_addr, + false, 0, peer->info.seq_id, 0, NULL, + NULL); + if (ret) + wpa_printf(MSG_DEBUG, "NAN: Failed to clear peer schedule"); + + peer->configured = false; + return 0; +} + + +/** + * nan_process_followup - Process a received NAN Follow-up Action frame + * @nan: NAN module context from nan_init() + * @addr: Source address of the received frame + * @buf: Buffer containing the received frame + * @len: Length of the received frame in octets + * @req_instance_id: Instance ID of the request that triggered this followup + * @handle: Service handle of the service associated with this followup + * Returns: true if the frame was processed successfully, false on failure + */ +bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf, + size_t len, u8 req_instance_id, int handle) +{ + struct nan_attrs attrs; + bool ret = false; + + if (nan_parse_attrs(nan, buf, len, &attrs)) { + wpa_printf(MSG_DEBUG, + "NAN: Follow-up: Failed parsing attributes"); + return false; + } + + if (attrs.npba && attrs.npba_len) + ret = nan_bootstrap_handle_rx(nan, addr, attrs.npba, + attrs.npba_len, buf, len, handle, + req_instance_id); +#ifdef CONFIG_PASN + else if (attrs.shared_key_desc) + ret = nan_pairing_followup_rx(nan, addr, + (const struct nan_shared_key *) + attrs.shared_key_desc, + attrs.shared_key_desc_len); +#endif /* CONFIG_PASN */ + + nan_attrs_clear(nan, &attrs); + return ret; +} + + +static void nan_peer_state_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct nan_data *nan = eloop_ctx; + struct nan_peer *peer = timeout_ctx; + + wpa_printf(MSG_DEBUG, "NAN: Timeout expired: " MACSTR, + MAC2STR(peer->nmi_addr)); + + if (!peer->ndp_setup.ndp) + return; + + /* If we already sent termination just disconnect */ + if (peer->ndp_setup.state == NAN_NDP_STATE_DONE && + peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, + "NAN: Timeout (NDP setup is done), disconnecting"); + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + true); + return; + } + + /* + * Send NDP Termination to notify the peer about the timeout. + * Prepare the state for building a termination frame. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDP: state: %u --> %u (timeout termination)", + peer->ndp_setup.state, NAN_NDP_STATE_DONE); + + peer->ndp_setup.state = NAN_NDP_STATE_DONE; + peer->ndp_setup.status = NAN_NDP_STATUS_REJECTED; + peer->ndp_setup.reason = NAN_REASON_UNSPECIFIED_REASON; + + if (nan_action_send(nan, peer, NAN_SUBTYPE_DATA_PATH_TERMINATION)) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to send termination on timeout"); + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + true); + return; + } + + /* + * Termination frame sent successfully. Set a short timeout to wait + * for TX status. The TX status handler (nan_tx_status) will call + * nan_ndp_disconnected(). + */ + nan_set_peer_timeout(nan, peer, NAN_NDP_SETUP_TIMEOUT_SHORT, 0); +} + + +static void nan_set_peer_timeout(struct nan_data *nan, struct nan_peer *peer, + unsigned int sec, unsigned int usec) +{ + wpa_printf(MSG_DEBUG, "NAN: Set timeout: " MACSTR " %u.%06u sec", + MAC2STR(peer->nmi_addr), sec, usec); + + eloop_cancel_timeout(nan_peer_state_timeout, nan, peer); + eloop_register_timeout(sec, usec, nan_peer_state_timeout, nan, peer); +} + + +static void nan_ndp_action_notif(struct nan_data *nan, struct nan_peer *peer) +{ + struct nan_ndp_action_notif_params notify; + + os_memset(¬ify, 0, sizeof(notify)); + + os_memcpy(notify.ndp_id.peer_nmi, peer->nmi_addr, ETH_ALEN); + os_memcpy(notify.ndp_id.init_ndi, peer->ndp_setup.ndp->init_ndi, + ETH_ALEN); + notify.ndp_id.id = peer->ndp_setup.ndp->ndp_id; + notify.publish_inst_id = peer->ndp_setup.publish_inst_id; + + notify.is_request = peer->ndp_setup.state == NAN_NDP_STATE_REQ_RECV; + notify.ndp_status = peer->ndp_setup.status; + + if (peer->ndl) + notify.ndl_status = peer->ndl->status; + else + notify.ndl_status = NAN_NDL_STATUS_REJECTED; + + notify.ssi = peer->ndp_setup.ssi; + notify.ssi_len = peer->ndp_setup.ssi_len; + + if (peer->ndp_setup.sec.present) { + notify.csid = peer->ndp_setup.sec.i_csid; + notify.pmkid = peer->ndp_setup.sec.i_pmkid; + } else { + notify.csid = NAN_CS_NONE; + notify.pmkid = NULL; + } + + wpa_printf(MSG_DEBUG, + "NAN: NDP action notification peer=" MACSTR + ", ndp_status=%u, ndl_status=%u", + MAC2STR(peer->nmi_addr), notify.ndp_status, + notify.ndl_status); + + if (nan->cfg->ndp_action_notif) + nan->cfg->ndp_action_notif(nan->cfg->cb_ctx, ¬ify); + nan_set_peer_timeout(nan, peer, NAN_NDP_SETUP_TIMEOUT_LONG, 0); +} + + +static bool nan_peer_ndi_in_use(struct nan_peer *peer, const u8 *peer_ndi) +{ + struct nan_ndp *ndp; + + dl_list_for_each(ndp, &peer->ndps, struct nan_ndp, list) { + if (ndp->initiator) { + if (ether_addr_equal(ndp->resp_ndi, peer_ndi)) + return true; + } else { + if (ether_addr_equal(ndp->init_ndi, peer_ndi)) + return true; + } + } + + return false; +} + + +static void nan_terminate_ndps_for_ndi(struct nan_data *nan, + struct nan_peer *peer, + const u8 *peer_ndi) +{ + struct nan_ndp *ndp, *tndp, *curr_ndp; + + curr_ndp = peer->ndp_setup.ndp; + + dl_list_for_each_safe(ndp, tndp, &peer->ndps, struct nan_ndp, list) { + const u8 *ndp_peer_ndi = ndp->initiator ? + ndp->resp_ndi : ndp->init_ndi; + + if (!ether_addr_equal(ndp_peer_ndi, peer_ndi)) + continue; + + dl_list_del(&ndp->list); + + /* Temporarily set the NDP being disconnected */ + peer->ndp_setup.ndp = ndp; + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + true); + } + + /* Restore the current NDP */ + peer->ndp_setup.ndp = curr_ndp; +} + + +static void nan_handle_idle_period(struct nan_data *nan) +{ + struct nan_peer *peer; + int next_timeout = 0; + + eloop_cancel_timeout(nan_idle_period_timeout, nan, NULL); + + if (!nan->cfg->get_peer_inactivity || !nan->cfg->max_ndl_idle_period) + return; + + wpa_printf(MSG_DEBUG, + "NAN: Handle idle period timeout: max_idle_period=%d sec", + nan->cfg->max_ndl_idle_period); + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + int peer_inactive = -1; + struct nan_ndp *pndp; + + if (dl_list_empty(&peer->ndps) || !peer->ndl) + continue; + + wpa_printf(MSG_DEBUG, "NAN: Check idle period for peer=" MACSTR, + MAC2STR(peer->nmi_addr)); + + /* Find the minimal inactive time over all NDPs */ + dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) { + const u8 *local_ndi, *peer_ndi; + int inactive; + + if (pndp->initiator) { + local_ndi = pndp->init_ndi; + peer_ndi = pndp->resp_ndi; + } else { + local_ndi = pndp->resp_ndi; + peer_ndi = pndp->init_ndi; + } + + inactive = + nan->cfg->get_peer_inactivity(nan->cfg->cb_ctx, + local_ndi, + peer_ndi); + wpa_printf(MSG_DEBUG, + "NAN: local=" MACSTR ", peer" MACSTR + " : inactivity=%d sec", + MAC2STR(local_ndi), MAC2STR(peer_ndi), + inactive); + + if (inactive < 0) + continue; + + /* + * peer_inactive would eventually hold the minimal + * inactive time over all couples + */ + if (peer_inactive == -1 || inactive < peer_inactive) + peer_inactive = inactive; + } + + wpa_printf(MSG_DEBUG, "NAN: Peer " MACSTR + " has been inactive for %d seconds", + MAC2STR(peer->nmi_addr), peer_inactive); + + if (peer_inactive >= nan->cfg->max_ndl_idle_period) { + wpa_printf(MSG_DEBUG, "NAN: Peer " MACSTR + " has been inactive for too long, removing NDPs", + MAC2STR(peer->nmi_addr)); + nan_peer_del_all_ndps(nan, peer->nmi_addr); + continue; + } + + if (peer_inactive == -1) + peer_inactive = 0; + + if (!next_timeout || + next_timeout > + nan->cfg->max_ndl_idle_period - peer_inactive) + next_timeout = + nan->cfg->max_ndl_idle_period - peer_inactive; + } + + wpa_printf(MSG_DEBUG, "NAN: Next idle period timeout in %d seconds", + next_timeout); + + if (next_timeout) + eloop_register_timeout(next_timeout, 0, + nan_idle_period_timeout, nan, NULL); +} + + +static void nan_idle_period_timeout(void *eloop_ctx, void *timeout_ctx) +{ + nan_handle_idle_period(eloop_ctx); +} + + +static int nan_ndp_connected(struct nan_data *nan, struct nan_peer *peer) +{ + struct nan_ndp_connection_params params; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + + wpa_printf(MSG_DEBUG, "NAN: NDP connected notification peer=" MACSTR, + MAC2STR(peer->nmi_addr)); + + os_memcpy(params.ndp_id.peer_nmi, peer->nmi_addr, ETH_ALEN); + os_memcpy(params.ndp_id.init_ndi, peer->ndp_setup.ndp->init_ndi, + ETH_ALEN); + params.ndp_id.id = peer->ndp_setup.ndp->ndp_id; + params.ssi = peer->ndp_setup.ssi; + params.ssi_len = peer->ndp_setup.ssi_len; + + if (peer->ndp_setup.peer_interface_id_valid) { + wpa_printf(MSG_DEBUG, + "NAN: NDP connected with peer interface id"); + + params.interface_id = peer->ndp_setup.peer_interface_id; + } + + if (peer->ndp_setup.ndp->initiator) { + params.local_ndi = peer->ndp_setup.ndp->init_ndi; + params.peer_ndi = peer->ndp_setup.ndp->resp_ndi; + } else { + params.local_ndi = peer->ndp_setup.ndp->resp_ndi; + params.peer_ndi = peer->ndp_setup.ndp->init_ndi; + } + + params.install_keys = nan_sec_ndp_store_keys(nan, peer, + params.peer_ndi, + params.local_ndi); + params.first_ndp = dl_list_empty(&peer->ndps); + + if (peer->ndp_setup.sec.local_gtk.csid != NAN_CS_NONE) + params.local_gtk = &peer->ndp_setup.sec.local_gtk; + + peer->ndp_setup.ndp->gtk_id = peer->ndp_setup.sec.peer_gtk.id; + if (peer->ndp_setup.sec.peer_gtk.id) { + params.peer_gtk = &peer->ndp_setup.sec.peer_gtk; + params.peer_gtk_rsc = peer->ndp_setup.sec.peer_gtk_rsc; + } + + params.new_ndi_sta = !nan_peer_ndi_in_use(peer, params.peer_ndi); + if (nan->cfg->ndp_connected) { + ret = nan->cfg->ndp_connected(nan->cfg->cb_ctx, ¶ms); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDP connected notification failed ret=%d", + ret); + if (ret == -2) + nan_terminate_ndps_for_ndi(nan, peer, + params.peer_ndi); + + return ret; + } + } + + /* Move the NDP to the list of tracked NDPs */ + dl_list_add(&peer->ndps, &peer->ndp_setup.ndp->list); + peer->ndp_setup.ndp = NULL; + + nan_ndp_setup_stop(nan, peer); + nan_handle_idle_period(nan); + + return 0; +} + + +static void nan_ndp_disconnected(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, + bool locally_generated) +{ + const u8 *local_ndi, *peer_ndi; + struct nan_ndp_id ndp_id; + bool remove_sta, fail; + + os_memset(&ndp_id, 0, sizeof(ndp_id)); + + wpa_printf(MSG_DEBUG, + "NAN: NDP disconnected notification peer=" MACSTR, + MAC2STR(peer->nmi_addr)); + + os_memcpy(ndp_id.peer_nmi, peer->nmi_addr, ETH_ALEN); + os_memcpy(ndp_id.init_ndi, peer->ndp_setup.ndp->init_ndi, ETH_ALEN); + ndp_id.id = peer->ndp_setup.ndp->ndp_id; + + if (peer->ndp_setup.ndp->initiator) { + local_ndi = peer->ndp_setup.ndp->init_ndi; + peer_ndi = peer->ndp_setup.ndp->resp_ndi; + } else { + local_ndi = peer->ndp_setup.ndp->resp_ndi; + peer_ndi = peer->ndp_setup.ndp->init_ndi; + } + + /* + * Remove the NDI station only if no other NDP is using the same + * peer NDI address. The disconnecting NDP is in ndp_setup.ndp + * (not in peer->ndps), so checking peer->ndps is sufficient. + */ + remove_sta = !nan_peer_ndi_in_use(peer, peer_ndi); + + /* Remove sec entry if no other NDP is using this peer NDI */ + if (remove_sta) + nan_peer_del_sec_entry(&peer->info, peer_ndi); + + /* + * NAN_NDP_STATE_NONE means the NDP was not in progress, thus + * the failure flag should be false. + */ + fail = peer->ndp_setup.state != NAN_NDP_STATE_NONE; + + if (nan->cfg->ndp_disconnected) + nan->cfg->ndp_disconnected(nan->cfg->cb_ctx, &ndp_id, + local_ndi, peer_ndi, reason, + locally_generated, remove_sta, + fail, peer->ndp_setup.ndp->gtk_id); + + nan_ndp_setup_stop(nan, peer); +} + + +/** + * nan_action_rx_ndp - Process a received NAN Data Path Action Frame + * @nan: NAN module context from nan_init() + * @peer: NAN peer + * @msg: Parsed NAN message + * @resp_oui: OUI subtype to use in case a response is needed + * Returns: 0 on success; -1 on failure. + */ +static int nan_action_rx_ndp(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg, enum nan_subtype resp_oui) +{ + int ret; + + ret = nan_ndp_handle_ndp_attr(nan, peer, msg); + if (ret) { + if (ret > 0) + ret = 0; + return ret; + } + + /* + * NDP request: Also process the NDL/NDC/QoS attributes and store the + * data without actually scheduling. Send an indication to the + * encapsulating logic. + */ + if (peer->ndp_setup.state == NAN_NDP_STATE_REQ_RECV) { + wpa_printf(MSG_DEBUG, "NAN: NDP request"); + + ret = nan_ndl_handle_ndl_attr(nan, peer, msg); + if (ret || !peer->ndl) { + nan_ndp_setup_stop(nan, peer); + return -1; + } + + if (peer->ndl->status == NAN_NDL_STATUS_REJECTED) { + nan_ndp_setup_failure(nan, peer, + NAN_REASON_NDL_UNACCEPTABLE, + false); + if (peer->ndl->send_naf_on_error) + nan_action_send(nan, peer, resp_oui); + nan_ndp_setup_stop(nan, peer); + } else { + nan_ndp_action_notif(nan, peer); + } + return 0; + } + + /* + * NDP was rejected by the peer. Clear the ongoing setup and send an + * event. There is no need to send an NAF in this case. + */ + if (peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, "NAN: NAF: NDP rejected"); + + nan_ndp_disconnected(nan, peer, peer->ndp_setup.reason, false); + return 0; + } + + /* + * NDP state machine is either done or continued, need to trigger NDL + * state machine. + */ + ret = nan_ndl_handle_ndl_attr(nan, peer, msg); + if (ret || !peer->ndl) + return ret; + + if (peer->ndl->status == NAN_NDL_STATUS_REJECTED) { + enum nan_reason reason = peer->ndl->reason; + + if (reason == NAN_REASON_RESERVED) + reason = NAN_REASON_UNSPECIFIED_REASON; + + wpa_printf(MSG_DEBUG, + "NAN: NAF: NDL rejected(ret=%d, reason=%u)", + ret, reason); + + /* NDL handling failure on local side */ + if (peer->ndl->send_naf_on_error) { + nan_ndp_setup_failure(nan, peer, reason, 0); + ret = nan_action_send(nan, peer, resp_oui); + } + + nan_ndp_disconnected(nan, peer, reason, false); + return 0; + } + + if (peer->ndl->status == NAN_NDL_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, "NAN: NAF: NDL continues"); + nan_ndp_action_notif(nan, peer); + return 0; + } + + /* Both state machines are done */ + if (peer->ndp_setup.state == NAN_NDP_STATE_DONE && + peer->ndl->state == NAN_NDL_STATE_DONE) { + wpa_printf(MSG_DEBUG, "NAN: NAF: NDP setup done"); + if (nan_configure_peer_schedule(nan, peer, &nan->sched) || + nan_ndp_connected(nan, peer)) + nan_ndp_disconnected(nan, peer, + NAN_REASON_UNSPECIFIED_REASON, + true); + return 0; + } + + wpa_printf(MSG_DEBUG, "NAN: NAF: NDP setup continues"); + ret = nan_action_send(nan, peer, resp_oui); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NAF: Failed to send NAF. Resetting.."); + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + true); + } + + nan_set_peer_timeout(nan, peer, NAN_NDP_SETUP_TIMEOUT_SHORT, 0); + + return 0; +} + + +/* + * nan_action_substitute_src - Substitute the source address in the NAF if + * it matches an NDI of an existing NDP + * + * @nan: NAN module context from nan_init() + * @mgmt: Pointer to the IEEE 802.11 management frame + * @len: Length of the management frame in octets + * + * NAFs can be sent and received on NDIs. In such cases, the source address + * in the 802.11 header would be the NDI address. This function checks if + * the source address matches any known NDI address and if so, substitutes + * it with the NMI address of the corresponding peer. + */ +static void nan_action_substitute_src(struct nan_data *nan, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct nan_peer *peer; + + /* If the peer is known, nothing needs to be changed */ + peer = nan_get_peer(nan, mgmt->sa); + if (peer) + return; + + /* + * Find a peer with which we have an NDI that matches the source address + * in the frame, and if found, substitute the frames source address with + * the peer NMI + */ + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + struct nan_ndp *pndp; + + /* When a peer is paired, NAFs are not allowed on NDIs */ + if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED) + continue; + + dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) { + const u8 *addr; + + if (pndp->initiator) + addr = pndp->resp_ndi; + else + addr = pndp->init_ndi; + + if (os_memcmp(addr, mgmt->sa, ETH_ALEN)) + continue; + + wpa_printf(MSG_DEBUG, + "NAN: NAF from=" MACSTR " Received on NDI=" MACSTR, + MAC2STR(peer->nmi_addr), MAC2STR(mgmt->sa)); + + os_memcpy((void *)mgmt->sa, peer->nmi_addr, ETH_ALEN); + } + } + + wpa_printf(MSG_DEBUG, + "NAN: NAF from unknown peer=" MACSTR, + MAC2STR(mgmt->sa)); +} + + +/* + * nan_action_rx - Process a received NAN Action Frame + * @nan: NAN module context from nan_init() + * @mgmt: Pointer to the IEEE 802.11 Management frame + * @len: Length of the Management frame in octets + * Return 0 on success; -1 on failure. + */ +int nan_action_rx(struct nan_data *nan, const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct nan_msg msg; + struct nan_peer *peer; + enum nan_subtype resp_oui = NAN_SUBTYPE_INVALID; + int ret; + + if (!nan_ndp_supported(nan)) + return -1; + + nan_action_substitute_src(nan, mgmt, len); + + /* Parse the NAF and validate its general structure */ + + ret = nan_parse_naf(nan, mgmt, len, &msg); + if (ret) + return ret; + + ret = nan_add_peer(nan, mgmt->sa, mgmt->u.action.u.naf.variable, + len - IEEE80211_MIN_ACTION_LEN(naf)); + if (ret) + wpa_printf(MSG_DEBUG, "NAN: Failed to parse peer from NAF"); + + peer = nan_get_peer(nan, mgmt->sa); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to get a peer that was just added"); + goto done; + } + + wpa_printf(MSG_DEBUG, "NAN: NAF: oui_subtype=%u", msg.oui_subtype); + + switch (msg.oui_subtype) { + case NAN_SUBTYPE_DATA_PATH_REQUEST: + resp_oui = NAN_SUBTYPE_DATA_PATH_RESPONSE; + break; + case NAN_SUBTYPE_DATA_PATH_RESPONSE: + resp_oui = NAN_SUBTYPE_DATA_PATH_CONFIRM; + break; + case NAN_SUBTYPE_DATA_PATH_CONFIRM: + resp_oui = NAN_SUBTYPE_DATA_PATH_KEY_INSTALL; + break; + case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL: + case NAN_SUBTYPE_DATA_PATH_TERMINATION: + break; + case NAN_SUBTYPE_RANGING_REQUEST: + case NAN_SUBTYPE_RANGING_RESPONSE: + case NAN_SUBTYPE_RANGING_TERMINATION: + case NAN_SUBTYPE_RANGING_REPORT: + case NAN_SUBTYPE_SCHEDULE_REQUEST: + case NAN_SUBTYPE_SCHEDULE_RESPONSE: + case NAN_SUBTYPE_SCHEDULE_CONFIRM: + case NAN_SUBTYPE_SCHEDULE_UPDATE_NOTIF: + ret = 0; + goto done; + default: + ret = -1; + goto done; + } + + ret = nan_action_rx_ndp(nan, peer, &msg, resp_oui); +done: + nan_attrs_clear(nan, &msg.attrs); + return ret; +} + + +/* + * nan_publish_instance_id_valid - Check if instance ID is a valid publish ID + * @nan: NAN module context from nan_init() + * @instance_id: Instance ID to check + * @service_id: On return, holds the service ID if the instance ID is valid + * Returns: true if there is a local publish service ID with the given instance + * ID; false otherwise + */ +bool nan_publish_instance_id_valid(struct nan_data *nan, u8 instance_id, + u8 *service_id) +{ + if (!nan->cfg->is_valid_publish_id) { + wpa_printf(MSG_INFO, + "NAN: is_valid_publish_id callback not defined"); + return false; + } + + return nan->cfg->is_valid_publish_id(nan->cfg->cb_ctx, instance_id, + service_id); +} + + +/* + * nan_set_cluster_id - Set the cluster ID + * @nan: NAN module context from nan_init() + * @cluster_id: The cluster ID (6 bytes) + */ +void nan_set_cluster_id(struct nan_data *nan, const u8 *cluster_id) +{ + os_memcpy(nan->cluster_id, cluster_id, sizeof(nan->cluster_id)); +} + + +/* + * nan_tx_status_get_peer - Get the peer for a transmitted NAF + * + * @nan: NAN module context from nan_init() + * @dst: Destination address of the transmitted frame + * Return: Pointer to the peer or NULL if not found + */ +static struct nan_peer *nan_tx_status_get_peer(struct nan_data *nan, + const u8 *dst) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan, dst); + if (peer) + return peer; + + /* + * It is possible that the NAF was transmitted over an NDI, e.g., + * in case that a secure NDP was established with the peer + */ + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + struct nan_ndp *pndp; + const u8 *paddr; + + /* When a peer is paired, NAFs are not allowed on NDIs */ + if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED) + continue; + + /* + * When an NDP termination is initiated locally, the NDP is + * removed from the list and is set to the 'ndp_setup' object + * so need to also check that one. + */ + if (peer->ndp_setup.ndp) { + pndp = peer->ndp_setup.ndp; + + if (pndp->initiator) + paddr = pndp->resp_ndi; + else + paddr = pndp->init_ndi; + + if (!os_memcmp(dst, paddr, ETH_ALEN)) + return peer; + } + + dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) { + if (pndp->initiator) + paddr = pndp->resp_ndi; + else + paddr = pndp->init_ndi; + + if (os_memcmp(dst, paddr, ETH_ALEN)) + continue; + + return peer; + } + } + + return NULL; +} + + +/* + * nan_tx_status - Notification of the result of a transmitted NAN Action frame + * @nan: NAN module context from nan_init() + * @dst: Destination address of the transmitted frame + * @data: The transmitted frame + * @data_len: Length of the transmitted frame in octets + * @acked: Whether the frame was acknowledged + * Returns: 0 if the frame is a NAF and -1 if not. + */ +int nan_tx_status(struct nan_data *nan, const u8 *dst, const u8 *data, + size_t data_len, bool acked) +{ + struct nan_peer *peer; + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *)data; + u8 subtype; + int ret; + + if (!nan_is_naf(mgmt, data_len) || !dst) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: TX status: peer=" MACSTR ", acked=%u", + MAC2STR(dst), acked); + + peer = nan_tx_status_get_peer(nan, dst); + if (!peer) { + wpa_printf(MSG_DEBUG, "NAN: TX status: peer not found"); + return 0; + } + + subtype = mgmt->u.action.u.naf.subtype; + + ret = nan_ndp_naf_sent(nan, peer, subtype); + ret |= nan_ndl_naf_sent(nan, peer, subtype); + + if (ret || peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED || + !peer->ndl || peer->ndl->status == NAN_NDL_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, + "NAN: TX status: Stopping NDP establishment. ret=%d", + ret); + + if (peer->ndp_setup.ndp) + nan_ndp_disconnected(nan, peer, peer->ndp_setup.reason, + true); + return 0; + } + + /* Both state machines are done */ + if (peer->ndp_setup.state == NAN_NDP_STATE_DONE && + peer->ndl->state == NAN_NDL_STATE_DONE) { + wpa_printf(MSG_DEBUG, "NAN: TX status: NDP setup done"); + + if (nan_configure_peer_schedule(nan, peer, &nan->sched) || + nan_ndp_connected(nan, peer)) + nan_ndp_disconnected(nan, peer, + NAN_REASON_UNSPECIFIED_REASON, + true); + } + + return 0; +} + + +int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params) +{ + struct nan_peer *peer; + enum nan_subtype naf_oui = NAN_SUBTYPE_INVALID; + unsigned int timeout; + int ret; + + if (!nan_ndp_supported(nan)) + return -1; + + peer = nan_get_peer(nan, params->ndp_id.peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, "NAN: NDP peer not found"); + return -1; + } + + /* + * If the peer is paired, select the CSID based on the pairing + * information (and ignore the CSID in the parameters, if any). + * Otherwise, make sure that PASN CSIDs are not used. + */ + if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED) { + params->sec.csid = peer->pairing.pairing_csid; + wpa_printf(MSG_DEBUG, + "NAN: Paired peer, selected CSID=%d from pairing", + params->sec.csid); + } else if (NAN_CS_IS_PASN(params->sec.csid)) { + wpa_printf(MSG_DEBUG, + "NAN: PASN CSID %d requires peer to be paired", + params->sec.csid); + return -1; + } + + switch (params->type) { + case NAN_NDP_ACTION_REQ: + params->ndp_id.id = nan_get_next_ndp_id(nan); + ret = nan_ndp_setup_req(nan, peer, params); + if (ret) + return ret; + + ret = nan_ndl_setup(nan, peer, params, + peer->ndp_setup.dialog_token); + if (ret) { + nan_ndp_setup_stop(nan, peer); + return ret; + } + + naf_oui = NAN_SUBTYPE_DATA_PATH_REQUEST; + timeout = NAN_NDP_SETUP_TIMEOUT_LONG; + ret = nan_configure_peer_schedule(nan, peer, &nan->sched); + if (ret) { + nan_ndp_setup_stop(nan, peer); + return ret; + } + break; + case NAN_NDP_ACTION_RESP: + /* + * NDL establishment as part of the NDP establishment. It is + * possible that this would use an already existing NDL or start + * a new NDL setup. + */ + ret = nan_ndp_setup_resp(nan, peer, params); + if (ret) { + nan_ndp_setup_stop(nan, peer); + return ret; + } + + if (peer->ndp_setup.status != NAN_NDP_STATUS_REJECTED) { + ret = nan_ndl_setup(nan, peer, params, + peer->ndp_setup.dialog_token); + if (!ret) { + ret = nan_configure_peer_schedule(nan, peer, + &nan->sched); + if (ret) + peer->ndl->send_naf_on_error = 1; + } + + if (ret) { + if (peer->ndl && peer->ndl->send_naf_on_error) { + nan_ndp_setup_failure( + nan, peer, + NAN_REASON_NDL_UNACCEPTABLE, 0); + } else { + nan_ndp_setup_stop(nan, peer); + return ret; + } + } + } else if (peer->ndl && dl_list_empty(&peer->ndps)) { + peer->ndl->status = NAN_NDL_STATUS_REJECTED; + } + + naf_oui = NAN_SUBTYPE_DATA_PATH_RESPONSE; + + /* + * In case of counter proposal, allow the peer more time to + * process the counter request. + */ + timeout = (peer->ndl && + peer->ndl->status == NAN_NDL_STATUS_CONTINUED) ? + NAN_NDP_SETUP_TIMEOUT_LONG : + NAN_NDP_SETUP_TIMEOUT_SHORT; + break; + case NAN_NDP_ACTION_CONF: + ret = nan_ndl_setup(nan, peer, params, + peer->ndp_setup.dialog_token); + if (!ret) { + ret = nan_configure_peer_schedule(nan, peer, + &nan->sched); + if (ret) + peer->ndl->send_naf_on_error = 1; + } + + if (ret) { + if (peer->ndl && peer->ndl->send_naf_on_error) { + nan_ndp_setup_failure( + nan, peer, + NAN_REASON_NDL_UNACCEPTABLE, 0); + } else { + nan_ndp_setup_stop(nan, peer); + return ret; + } + } + + naf_oui = NAN_SUBTYPE_DATA_PATH_CONFIRM; + timeout = NAN_NDP_SETUP_TIMEOUT_SHORT; + break; + + case NAN_NDP_ACTION_TERM: + naf_oui = NAN_SUBTYPE_DATA_PATH_TERMINATION; + timeout = NAN_NDP_SETUP_TIMEOUT_SHORT; + ret = nan_ndp_term_req(nan, peer, ¶ms->ndp_id); + if (ret) + return ret; + break; + default: + wpa_printf(MSG_DEBUG, "NAN: Unsupported NDP setup type=%u", + params->type); + return -1; + } + + ret = nan_action_send(nan, peer, naf_oui); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: Failed sending NAF. Resetting: ret=%d", ret); + nan_ndp_disconnected(nan, peer, peer->ndp_setup.reason, true); + return 0; + } + + nan_set_peer_timeout(nan, peer, timeout, 0); + return ret; +} + + +void nan_ndp_terminated(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_id *ndp_id, const u8 *local_ndi, + const u8 *peer_ndi, enum nan_reason reason, u8 gtk_id) +{ + /* + * Remove the NDI station only if no other NDP is using the same + * peer NDI address. The terminated NDP has already been removed + * from peer->ndps before this function is called. + */ + bool remove_sta = !nan_peer_ndi_in_use(peer, peer_ndi); + + /* Remove sec entry if no other NDP is using this peer NDI */ + if (remove_sta) + nan_peer_del_sec_entry(&peer->info, peer_ndi); + + if (nan->cfg->ndp_disconnected) + nan->cfg->ndp_disconnected(nan->cfg->cb_ctx, ndp_id, local_ndi, + peer_ndi, reason, false, remove_sta, + false, gtk_id); + + /* Need to also remove the NDL if it is not needed */ + if (dl_list_empty(&peer->ndps) && !peer->ndp_setup.ndp) + nan_ndl_reset(nan, peer); +} + + +struct nan_device_capabilities * +nan_peer_get_device_capabilities(struct nan_data *nan, const u8 *addr, + u8 map_id) +{ + struct nan_dev_capa_entry *cur; + struct nan_peer *peer; + + peer = nan_get_peer(nan, addr); + if (!peer) + return NULL; + + dl_list_for_each(cur, &peer->info.dev_capa, struct nan_dev_capa_entry, + list) { + if (cur->map_id == map_id) + return &cur->capa; + } + + return NULL; +} + + +int nan_peer_get_tk(struct nan_data *nan, const u8 *addr, + const u8 *peer_ndi, const u8 *local_ndi, + u8 *tk, size_t *tk_len, enum nan_cipher_suite_id *csid) +{ + struct nan_peer *peer; + + if (!nan || !tk || !tk_len || !csid) + return -1; + + peer = nan_get_peer(nan, addr); + if (!peer) + return -1; + + return nan_sec_get_tk(nan, peer, peer_ndi, local_ndi, tk, tk_len, csid); +} + + +const struct nan_pairing_cfg * nan_peer_get_pairing_cfg(struct nan_data *nan, + const u8 *addr, + const u8 **nonce, + const u8 **tag) +{ + struct nan_peer *peer; + + if (!nan || !addr || !nonce || !tag) + return NULL; + + peer = nan_get_peer(nan, addr); + if (!peer) + return NULL; + + /* Return nonce and tag pointers if valid, NULL otherwise */ + if (peer->pairing.nonce_tag_valid) { + *nonce = peer->pairing.nonce; + *tag = peer->pairing.tag; + } else { + *nonce = NULL; + *tag = NULL; + } + + return &peer->pairing.pairing_cfg; +} + + +static bool +nan_peer_channel_in_local_sched(const struct nan_data *nan, + int peer_ctrl_freq, + const struct nan_schedule *local_sched) +{ + unsigned int i; + + /* It's enough to compare the control freqs to ensure compatibility */ + for (i = 0; i < local_sched->n_chans; i++) { + if (peer_ctrl_freq == local_sched->chans[i].chan.freq) + return true; + } + + return false; +} + + +static void +nan_peer_get_committed_avail_add(const struct nan_data *nan, + const struct nan_peer *peer, + const struct nan_avail_entry *avail, + const struct nan_schedule *local_sched, + struct nan_peer_schedule *sched) +{ + struct nan_map *map; + struct nan_map_chan *chan; + struct nan_sched_chan schan; + const struct oper_class_map *op; + u8 chan_id; + bool committed; + int freq, bw, center_freq1, center_freq2, idx; + u8 i; + const struct nan_band_chan *band_chan; + const struct nan_chan_entry *bc_chan; + + if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED && + avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) + return; + + /* + * This should not happen in practice as committed and conditional + * entries should have only a single channel entry. + */ + if (avail->n_band_chan != 1) { + wpa_printf(MSG_DEBUG, + "NAN: Skip availability entry with n_band_chan=%u", + avail->n_band_chan); + return; + } + + band_chan = &avail->band_chan[0]; + bc_chan = &band_chan->u.chan; + + /* Get all the channel parameters */ + op = get_oper_class(NULL, band_chan->u.chan.op_class); + if (!op) { + wpa_printf(MSG_DEBUG, "NAN: Unknown operating class %u", + band_chan->u.chan.op_class); + return; + } + + idx = ffs(le_to_host16(bc_chan->chan_bitmap)) - 1; + if (idx < 0) { + wpa_printf(MSG_DEBUG, + "NAN: No channel found in chan_bitmap 0x%04x for oper_class %u", + le_to_host16(bc_chan->chan_bitmap), + bc_chan->op_class); + return; + } + + chan_id = op_class_idx_to_chan(op, idx); + if (!chan_id) { + wpa_printf(MSG_DEBUG, + "NAN: No channel found for oper_class %u idx %u", + bc_chan->op_class, idx); + return; + } + + freq = ieee80211_chan_to_freq(NULL, bc_chan->op_class, chan_id); + bw = oper_class_bw_to_int(op); + + center_freq2 = 0; + if (op->op_class < 128) { + center_freq1 = ieee80211_get_center_freq(freq, op->bw); + } else if (op->op_class > 130) { + wpa_printf(MSG_DEBUG, "NAN: Missing support for op_class %u", + op->op_class); + return; + } else { + idx = ffs(bc_chan->pri_chan_bitmap) - 1; + if (idx < 0) { + wpa_printf(MSG_DEBUG, + "NAN: No primary channel found in pri_chan_bitmap 0x%04x", + le_to_host16(bc_chan->pri_chan_bitmap)); + return; + } + + center_freq1 = freq; + if (op->bw == BW80 || op->bw == BW80P80) + freq = freq - 30 + idx * 20; + else if (op->bw == BW160) + freq = freq - 70 + idx * 20; + + /* TODO: Missing support for 80 + 80 */ + } + + /* Skip channels that are not in local schedule */ + if (local_sched && + !nan_peer_channel_in_local_sched(nan, freq, local_sched)) + return; + + /* Assume committed for conditional slots if setup is done */ + committed = (avail->type == NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED) || + (avail->type == NAN_AVAIL_ENTRY_CTRL_TYPE_COND && + peer->ndl->state == NAN_NDL_STATE_DONE && + peer->ndl->status == NAN_NDL_STATUS_ACCEPTED); + + /* Find map ID entry if already exists */ + for (i = 0; i < sched->n_maps; i++) + if (sched->maps[i].map_id == avail->map_id) + break; + + map = &sched->maps[i]; + if (i == sched->n_maps) { + if (sched->n_maps == NAN_MAX_MAPS) { + wpa_printf(MSG_DEBUG, + "NAN: Too many map entries in schedule"); + return; + } + sched->n_maps++; + } + + map->map_id = avail->map_id; + + os_memset(&schan, 0, sizeof(schan)); + + /* Find channel entry if already exists */ + for (i = 0; i < map->n_chans; i++) { + if (map->chans[i].committed != committed) + return; + + if (map->chans[i].chan.freq == freq && + map->chans[i].chan.bandwidth == bw && + map->chans[i].chan.center_freq1 == center_freq1 && + map->chans[i].chan.center_freq2 == center_freq2) + break; + } + + chan = &map->chans[i]; + if (i == map->n_chans) { + if (map->n_chans == NAN_MAX_CHAN_ENTRIES) { + wpa_printf(MSG_DEBUG, + "NAN: Too many channel entries in schedule map_id=%u", + map->map_id); + return; + } + map->n_chans++; + } + + chan->committed = committed; + chan->rx_nss = avail->rx_nss; + chan->chan.freq = freq; + chan->chan.bandwidth = bw; + chan->chan.center_freq1 = center_freq1; + chan->chan.center_freq2 = center_freq2; + + os_memcpy(&chan->tbm, &avail->tbm, sizeof(avail->tbm)); +} + + +static void +nan_peer_get_committed_avail(const struct nan_data *nan, + const struct nan_peer *peer, + const struct nan_schedule *local_sched, + struct nan_peer_schedule *sched) +{ + const struct nan_avail_entry *avail; + + dl_list_for_each(avail, &peer->info.avail_entries, + struct nan_avail_entry, list) + nan_peer_get_committed_avail_add(nan, peer, avail, + local_sched, sched); +} + + +static void nan_peer_set_sched(struct nan_data *nan, struct nan_peer *peer, + struct nan_peer_schedule *sched, + const u8 *sched_buf, size_t sched_buf_len, + bool ndc) +{ + struct dl_list sched_entries; + struct nan_avail_entry *cur; + int ret; + + if (!sched->n_maps) + return; + + if (!sched_buf || !sched_buf_len) + return; + + if (sched_buf_len < sizeof(struct nan_sched_entry)) { + wpa_printf(MSG_DEBUG, "NAN: Schedule buffer too short=%zu", + sched_buf_len); + return; + } + + /* Convert the schedule the availability entries */ + ret = nan_sched_entries_to_avail_entries(nan, &sched_entries, + sched_buf, sched_buf_len); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to parse peer schedule entries"); + return; + } + + /* + * For each schedule entry find the corresponding map in the committed + * schedule and store the copy of the time bitmap. + */ + dl_list_for_each(cur, &sched_entries, struct nan_avail_entry, list) { + struct nan_map *map; + unsigned int i; + + for (i = 0; i < sched->n_maps; i++) { + map = &sched->maps[i]; + + if (map->map_id == cur->map_id) + break; + } + + if (i == sched->n_maps) { + wpa_printf(MSG_DEBUG, + "NAN: No map entry found for map_id=%u in peer schedule", + cur->map_id); + continue; + } + + if (ndc) + os_memcpy(&map->ndc, &cur->tbm, sizeof(cur->tbm)); + else + os_memcpy(&map->immutable, &cur->tbm, sizeof(cur->tbm)); + } + + nan_flush_avail_entries(&sched_entries); +} + + +static void nan_peer_get_ndc_sched(struct nan_data *nan, + struct nan_peer *peer, + struct nan_peer_schedule *sched) +{ + if (!peer->ndl) + return; + + nan_peer_set_sched(nan, peer, sched, + peer->ndl->ndc_sched, + peer->ndl->ndc_sched_len, true); +} + + +static void nan_peer_get_immut_sched(struct nan_data *nan, + struct nan_peer *peer, + struct nan_peer_schedule *sched) +{ + if (!peer->ndl) + return; + + nan_peer_set_sched(nan, peer, sched, + peer->ndl->immut_sched, + peer->ndl->immut_sched_len, false); +} + + + +/* + * nan_peer_get_schedule_info - Get peer's schedule information + * @nan: NAN module context from nan_init() + * @addr: NAN MAC address of the peer + * @sched: on return would hold the schedule information. + * Returns: 0 on success; -1 otherwise. + */ +int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr, + struct nan_peer_schedule *sched) +{ + struct nan_peer *peer; + + if (!nan || !sched) + return -1; + + os_memset(sched, 0, sizeof(*sched)); + + peer = nan_get_peer(nan, addr); + if (!peer) + return -1; + + nan_peer_get_committed_avail(nan, peer, NULL, sched); + nan_peer_get_ndc_sched(nan, peer, sched); + nan_peer_get_immut_sched(nan, peer, sched); + + if (peer->ndl) + sched->max_idle_period = peer->ndl->max_idle_period; + + return 0; +} + + +/* + * nan_peer_get_pot_avail - Get peer's potential availability entries + * @nan: NAN module context from nan_init() + * @addr: NAN MAC address of the peer + * @pot_avail: On return, holds the potential availability entries. + * Returns 0 on success, -1 on failure + */ +int nan_peer_get_pot_avail(struct nan_data *nan, const u8 *addr, + struct nan_peer_potential_avail *pot_avail) +{ + struct nan_avail_entry *avail; + struct nan_peer *peer; + u8 i; + + if (!nan || !pot_avail) + return -1; + + os_memset(pot_avail, 0, sizeof(*pot_avail)); + + peer = nan_get_peer(nan, addr); + if (!peer) + return -1; + + dl_list_for_each(avail, &peer->info.avail_entries, + struct nan_avail_entry, list) { + struct pot_entry *pot; + + if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL) + continue; + + if (pot_avail->n_maps == NAN_MAX_MAPS) { + wpa_printf(MSG_DEBUG, + "NAN: Too many potential maps stored"); + break; + } + + pot = &pot_avail->maps[pot_avail->n_maps++]; + pot->rx_nss = avail->rx_nss; + pot->preference = avail->preference; + pot->utilization = avail->utilization; + pot->is_band = avail->band_chan_type == NAN_TYPE_BAND; + + for (i = 0; i < avail->n_band_chan; i++, pot->n_band_chan++) { + const struct nan_band_chan *band_chan; + + if (pot->n_band_chan == NAN_MAX_CHAN_ENTRIES) { + wpa_printf(MSG_DEBUG, + "NAN: Too many band_chan entries stored for potential entry"); + break; + } + + band_chan = &avail->band_chan[i]; + + if (pot->is_band) { + pot->entries[i].band_id = + band_chan->u.band_id; + } else { + const struct nan_chan_entry *bc_chan; + + bc_chan = &band_chan->u.chan; + pot->entries[i].op_class = bc_chan->op_class; + pot->entries[i].chan_bitmap = + le_to_host16(bc_chan->chan_bitmap); + } + } + } + + return 0; +} + + +/** + * nan_convert_sched_to_avail_attrs - Convert NAN schedule to availability attrs + * @nan: NAN module context from nan_init() + * @map_ids_bitmap: Bitmap of map IDs for which NAN availability attributes + * should be added. Not all map IDs are covered by &chans. For map IDs that + * are not covered, NAN availability attributes will be added with + * potential availability entries. + * @sequence_id: Sequence ID for the availability attributes + * @n_chans: Number of channel entries in chans + * @chans: Channel entries + * @buf: Buffer to which the availability attributes will be added + * @include_potential: Whether to include potential availability entries + * Returns: 0 on success; -1 on failure + * + * Convert the given NAN schedule information to availability attributes and add + * them to the given buffer. For each given map ID the get_chans() callback will + * be used to get the channel entries for the potential availability entries. + */ +int nan_convert_sched_to_avail_attrs(struct nan_data *nan, u8 sequence_id, + u32 map_ids_bitmap, + size_t n_chans, + struct nan_chan_schedule *chans, + struct wpabuf *buf, + bool include_potential) +{ + return nan_add_avail_attrs(nan, sequence_id, map_ids_bitmap, + NAN_AVAIL_ENTRY_CTRL_TYPE_COND, + n_chans, chans, buf, include_potential); +} + + +bool nan_peer_pairing_supported(struct nan_data *nan, const u8 *addr) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan, addr); + if (!peer) + return false; + + return peer->pairing.pairing_cfg.pairing_setup; +} + + +bool nan_peer_npk_nik_caching_supported(struct nan_data *nan, const u8 *addr) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan, addr); + if (!peer) + return false; + + return peer->pairing.pairing_cfg.npk_caching; +} + + +/** + * nan_peer_del_all_ndps - Delete all NDPs with a given peer + * @nan: NAN module context from nan_init() + * @addr: NAN MAC address of the peer + * Returns: 0 on success, -1 on failure + * + * This function deletes all NDPs with the given peer and stops any ongoing + * NDP setup. It also resets the NDL state machine and flushes any security + * context with the peer. The function doesn't delete the peer itself and + * doesn't send any NAFs to the peer notifying about the deletions. + */ +int nan_peer_del_all_ndps(struct nan_data *nan, const u8 *addr) +{ + struct nan_peer *peer; + struct nan_ndp *ndp, *tndp; + + if (!nan) + return -1; + + peer = nan_get_peer(nan, addr); + if (!peer) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: Deleting all NDPs with peer " MACSTR, + MAC2STR(addr)); + + if (peer->ndp_setup.ndp) + nan_ndp_setup_stop(nan, peer); + + dl_list_for_each_safe(ndp, tndp, &peer->ndps, struct nan_ndp, list) { + dl_list_del(&ndp->list); + peer->ndp_setup.ndp = ndp; + nan_ndp_disconnected(nan, peer, NAN_REASON_UNSPECIFIED_REASON, + true); + } + + nan_ndl_reset(nan, peer); + nan_peer_flush_sec(&peer->info); + + return 0; +} + + +/** + * nan_get_peer_elems - Get element container data for a peer + * @nan: NAN module context from nan_init() + * @addr: NAN MAC address of the peer + * @elems: On return, pointer to the element container data + * Returns: Length of the element data on success; -1 on failure + * + * Retrieve the element container data associated with a peer. The function + * first looks for an entry with map_id 0. If not found and the peer has an + * active NDL, it returns the elements corresponding to the NDC channel's + * map_id. If no NDC map_id entry is found, it returns the first entry found. + */ +int nan_get_peer_elems(struct nan_data *nan, const u8 *addr, u8 **elems) +{ + struct nan_elem_container_entry *entry; + struct nan_peer *peer; + u8 ndc_map_id = 0; + bool ndc_map_id_found = false; + + peer = nan_get_peer(nan, addr); + if (!peer) + return -1; + + if (peer->ndl) { + const struct nan_sched_entry *peer_ndc = + (const struct nan_sched_entry *) peer->ndl->ndc_sched; + + if (peer_ndc && + peer->ndl->ndc_sched_len >= sizeof(*peer_ndc)) + ndc_map_id = peer_ndc->map_id; + } + + /* Prefer map_id == 0, so it applies for all */ + dl_list_for_each(entry, &peer->info.element_container, + struct nan_elem_container_entry, list) { + if (entry->map_id == 0) { + *elems = entry->data; + return entry->len; + } + + if (ndc_map_id && entry->map_id == ndc_map_id) + ndc_map_id_found = true; + } + + /* + * TODO: Properly support different elements per map_id. For now, take + * the elements that correspond to the NDC* channel if available. + * Currently upper layers don't support configuring different + * elements per map_id. Until that is changed, take the map_id + * corresponding to the NDC channel as it at least must intersect with + * the local schedule. If no such entry exists, return the first entry + * found. + */ + dl_list_for_each(entry, &peer->info.element_container, + struct nan_elem_container_entry, list) { + if (!ndc_map_id_found || entry->map_id == ndc_map_id) { + *elems = entry->data; + return entry->len; + } + } + + return -1; +} + + +/** + * nan_set_bootstrap_configuration - Set NAN bootstrap configuration + * @nan: NAN module context from nan_init() + * @supported_bootstrap_methods: Bitmap of supported bootstrap methods + * @auto_accept_bootstrap_methods: Bitmap of bootstrap methods to auto-accept + * @bootstrap_comeback_timeout: Timeout in TUs for bootstrap comeback + * Returns: 0 on success, -1 on failure. + */ +int nan_set_bootstrap_configuration(struct nan_data *nan, + u16 supported_bootstrap_methods, + u16 auto_accept_bootstrap_methods, + u16 bootstrap_comeback_timeout) +{ + if (!nan) + return -1; + + nan->cfg->supported_bootstrap_methods = supported_bootstrap_methods; + nan->cfg->auto_accept_bootstrap_methods = + auto_accept_bootstrap_methods; + nan->cfg->bootstrap_comeback_timeout = bootstrap_comeback_timeout; + + return 0; +} + + +/** + * nan_is_ndpe_supported - Check if NDPE attribute is supported with peer + * @nan: NAN module context from nan_init() + * @peer: NAN peer + * Returns: true if the peer supports NDPE attribute; false otherwise. + */ +bool nan_is_ndpe_supported(struct nan_data *nan, const struct nan_peer *peer) +{ + struct nan_dev_capa_entry *cur; + + if (!nan || !peer) + return false; + + dl_list_for_each(cur, &peer->info.dev_capa, + struct nan_dev_capa_entry, list) { + /* + * Take the first one, as NDPE support should be identical + * across all attributes. + */ + return !!(cur->capa.capa & NAN_DEV_CAPA_NDPE_ATTR_SUPP); + } + + return false; +} + + +/** + * nan_set_mgmt_group_cipher - Set NAN management group cipher + * @nan: Pointer to NAN data structure + * @cipher: Cipher suite to be set (WPA_CIPHER_AES_128_CMAC or + * WPA_CIPHER_BIP_GMAC_256) + * Returns: 0 on success, -1 on failure + * + * This function sets the management group cipher for NAN communication. + * The cipher can only be changed when NAN is not started. + */ +int nan_set_mgmt_group_cipher(struct nan_data *nan, int cipher) +{ + if (!nan) + return -1; + + if (nan->nan_started) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot set NAN management group cipher while NAN is started"); + return -1; + } + + if (cipher != WPA_CIPHER_AES_128_CMAC && + cipher != WPA_CIPHER_BIP_GMAC_256) { + wpa_printf(MSG_DEBUG, + "NAN: Unsupported NAN management group cipher %d", + cipher); + return -1; + } + + if (cipher == WPA_CIPHER_BIP_GMAC_256) + nan->cfg->security_capab |= + NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256; + else + nan->cfg->security_capab &= + ~NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256; + return 0; +} + + +/** + * nan_set_beacon_prot - Enable or disable NAN beacon protection + * @nan: Pointer to NAN data structure + * @enable: true to enable beacon protection, false to disable + * Returns: 0 on success, -1 on failure + * + * This function enables or disables NAN beacon protection. Beacon protection + * can only be changed when NAN is not started. Additionally, the device must + * support management frame protection for beacon protection to be enabled. + */ +int nan_set_beacon_prot(struct nan_data *nan, bool enable) +{ + u8 gtk_supp; + + if (!nan) + return -1; + + if (nan->nan_started) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot change beacon protection setting while NAN is started"); + return -1; + } + + if (((nan->cfg->security_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) == NAN_CS_INFO_CAPA_GTK_SUPP_NONE) { + if (enable) { + wpa_printf(MSG_DEBUG, + "NAN: Management frame protection is not supported by the device"); + return -1; + } + return 0; + } + + if (enable) + gtk_supp = NAN_CS_INFO_CAPA_GTK_SUPP_ALL; + else + gtk_supp = NAN_CS_INFO_CAPA_GTK_SUPP_NO_BIGTK; + + nan->cfg->security_capab &= ~NAN_CS_INFO_CAPA_GTK_SUPP_MASK; + nan->cfg->security_capab |= gtk_supp << NAN_CS_INFO_CAPA_GTK_SUPP_POS; + return 0; +} + + +/** + * nan_set_max_ndl_idle_period - Set maximum NDL idle period + * @nan: Pointer to NAN data structure + * @max_idle_period: Maximum idle period in seconds + * Returns: 0 on success, -1 on failure + */ +int nan_set_max_ndl_idle_period(struct nan_data *nan, u16 max_idle_period) +{ + if (!nan) + return -1; + + if (!nan->cfg->get_peer_inactivity) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot set max NDL idle period as get_peer_inactivity callback is not set"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "NAN: Setting max NDL idle period to %u (prev=%u) seconds", + max_idle_period, nan->cfg->max_ndl_idle_period); + + nan->cfg->max_ndl_idle_period = max_idle_period; + + if (!nan->nan_started) + return 0; + + /* + * Handle the current timeout. If a positive idle period is set + * configure the timeout logic to run in 1 second (as calling it + * immediately could cause NDP termination in the same context, and + * the caller might be able to handle it). + */ + eloop_cancel_timeout(nan_idle_period_timeout, nan, NULL); + if (max_idle_period) + eloop_register_timeout(1, 0, nan_idle_period_timeout, + nan, NULL); + return 0; +} + + +bool nan_has_active_ndp(struct nan_data *nan) +{ + struct nan_peer *peer; + + if (!nan) + return false; + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + if (!dl_list_empty(&peer->ndps)) + return true; + } + + return false; +} + + +void nan_set_sched_update_pending(struct nan_data *nan, bool pending) +{ + if (!nan) + return; + + wpa_printf(MSG_DEBUG, "NAN: Set sched_update_pending to %d", pending); + nan->sched_update_pending = pending; +} + + +void nan_local_sched_update(struct nan_data *nan, struct nan_schedule *sched) +{ + struct nan_peer *peer; + + if (!nan || !sched) + return; + + wpabuf_free(nan->sched.elems); + os_memcpy(&nan->sched, sched, sizeof(nan->sched)); + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + wpa_printf(MSG_DEBUG, "NAN: Updating schedule for peer " MACSTR, + MAC2STR(peer->nmi_addr)); + + if (peer->ndl && peer->ndl->state == NAN_NDL_STATE_DONE) + nan_peer_update_schedule(nan, peer, sched); + } +} + + +int nan_get_status(struct nan_data *nan, char *buf, size_t buflen) +{ + char *pos, *end; + struct nan_peer *peer; + int ret; + + if (!nan) + return -1; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, + "nan_started=%d\n" + "nmi=" MACSTR "\n" + "cluster_id=" MACSTR "\n", + nan->nan_started, MAC2STR(nan->cfg->nmi_addr), + MAC2STR(nan->cluster_id)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + struct nan_ndp *ndp; + unsigned int ndp_count = 0; + + dl_list_for_each(ndp, &peer->ndps, struct nan_ndp, list) + ndp_count++; + + ret = os_snprintf(pos, end - pos, + "peer=" MACSTR " paired=%d ndp_count=%u\n", + MAC2STR(peer->nmi_addr), + !!(peer->pairing.flags & + NAN_PAIRING_FLAG_PAIRED), + ndp_count); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +int nan_peer_dump_ndps_to_buf(struct nan_data *nan, const u8 *addr, + char *buf, size_t buflen) +{ + struct nan_peer *peer; + struct nan_ndp *ndp; + char *pos, *end; + + if (!nan) + return -1; + + peer = nan_get_peer(nan, addr); + if (!peer) { + wpa_printf(MSG_DEBUG, "NAN: Peer " MACSTR " not found", + MAC2STR(addr)); + return -1; + } + + pos = buf; + end = buf + buflen; + + dl_list_for_each(ndp, &peer->ndps, struct nan_ndp, list) { + int ret; + + ret = os_snprintf(pos, end - pos, + "ndp_id=%u initiator=%d " + "init_ndi=" MACSTR " resp_ndi=" MACSTR + " qos_min_slots=%u qos_max_latency=%u\n", + ndp->ndp_id, ndp->initiator, + MAC2STR(ndp->init_ndi), + MAC2STR(ndp->resp_ndi), + ndp->qos.min_slots, ndp->qos.max_latency); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +/** + * nan_terminate_ndi_ndps - Terminate all NDPs with a given NDI address + * @nan: NAN module context from nan_init() + * @ndi_addr: NDI address for which all NDPs should be terminated + * + * This function terminates all NDPs that have the given NDI address as either + * initiator or responder NDI. + */ +void nan_terminate_ndi_ndps(struct nan_data *nan, const u8 *ndi_addr) +{ + struct nan_peer *peer; + + if (!nan) + return; + + dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) { + /* + * It is possible that an NDP setup in progress is not on the + * NDI that is being removed. However, to simplify things, stop + * the setup, so the other NDPs could be cleanly removed. + */ + nan_ndp_setup_stop(nan, peer); + nan_terminate_ndps_for_ndi(nan, peer, ndi_addr); + } +} diff --git a/contrib/wpa/src/nan/nan.h b/contrib/wpa/src/nan/nan.h new file mode 100644 index 000000000000..c8a0594ef072 --- /dev/null +++ b/contrib/wpa/src/nan/nan.h @@ -0,0 +1,946 @@ +/* + * Wi-Fi Aware - NAN module + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_H +#define NAN_H + +#include "common/nan_defs.h" +#include "common/wpa_common.h" +#include "utils/list.h" + +struct nan_cluster_config; +enum nan_reason; +struct ieee80211_mgmt; + +struct nan_de_pmkid { + struct dl_list list; + u8 pmkid[PMKID_LEN]; +}; + +/* + * struct nan_device_capabilities - NAN device capabilities + * @cdw_info: Committed DW information + * @supported_bands: Supported bands + * @op_mode: Operation mode + * @n_antennas: Number of antennas + * @channel_switch_time: Maximal channel switch time + * @capa: Device capabilities + */ +struct nan_device_capabilities { + u16 cdw_info; + u8 supported_bands; + u8 op_mode; + u8 n_antennas; + u16 channel_switch_time; + u8 capa; +}; + +/** + * struct nan_qos - NAN QoS requirements + * @min_slots: Minimal number of slots + * @max_latency: Maximum allowed NAN slots between every two non-contiguous + * NAN Data Link (NDL) Common Resource Blocks (CRB) + */ +struct nan_qos { + u8 min_slots; + u16 max_latency; +}; + +/** + * enum nan_ndp_action - NDP action + * @NAN_NDP_ACTION_REQ: Request NDP establishment + * @NAN_NDP_ACTION_RESP: Response to NDP establishment request + * @NAN_NDP_ACTION_CONF: Confirm NDP establishment + * @NAN_NDP_ACTION_TERM: Request NDP termination + */ +enum nan_ndp_action { + NAN_NDP_ACTION_REQ, + NAN_NDP_ACTION_RESP, + NAN_NDP_ACTION_CONF, + NAN_NDP_ACTION_TERM, +}; + +/** + * struct nan_ndp_id - Unique identifier of an NDP + * + * @peer_nmi: Peer NAN Management Interface (NMI) + * @init_ndi: Initiator NAN Data Interface (NDI) + * @id: NDP identifier + */ +struct nan_ndp_id { + u8 peer_nmi[ETH_ALEN]; + u8 init_ndi[ETH_ALEN]; + u8 id; +}; + +/* + * The maximal period of a NAN schedule is 8192 TUs. With time slots of 16 TUs, + * need 64 octets to represent a complete schedule bitmap. + */ +#define NAN_MAX_PERIOD_TUS 8192 +#define NAN_MAX_TIME_BITMAP_SLOTS (NAN_MAX_PERIOD_TUS / 16) +#define NAN_TIME_BITMAP_MAX_LEN (NAN_MAX_TIME_BITMAP_SLOTS / 8) + +/** + * struct nan_time_bitmap - NAN time bitmap + * + * @duration: Slot duration represented by each bit in the bitmap. Valid values + * are as defined in nan_defs.h and Wi-Fi Aware spec v4.0, Table 97 (Time + * Bitmap Control field format for the NAN Availability attribute). + * @period: Indicates the repeat interval of the bitmap. + * When set to zero, the bitmap is not repeated. Valid values are + * as defined in nan_defs.h and Wi-Fi Aware spec v4.0, Table 97 (Time + * Bitmap Control field format for the NAN Availability attribute). + * @offset: The time period specified by the %bitmap field starts at + * 16 * offset TUs after DW0. + * @len: Length of the %bitmap field, in bytes. If this is zero, the NAN device + * is available for 512 NAN slots beginning after the immediate previous + * DW0. + * @bitmap: Each bit in the time bitmap corresponds to a time duration indicated + * by the value of the %duration field. When a bit is set to 1, the NAN + * device is available (or conditionally or potentially available) + * for any NAN operations for the time associated with the bit. + */ +struct nan_time_bitmap { + u8 duration; + u16 period; + u16 offset; + u8 len; + u8 bitmap[NAN_TIME_BITMAP_MAX_LEN]; +}; + +/** + * struct nan_sched_chan - NAN scheduled channel + * + * @freq: Primary channel center frequency of the 20 MHz + * @center_freq1: Center frequency of the first segment + * @center_freq2: Center frequency of the second segment, if any + * @bandwidth: The channel bandwidth in MHz + */ +struct nan_sched_chan { + int freq; + int center_freq1; + int center_freq2; + int bandwidth; +}; + +/** + * struct nan_chan_schedule - NAN channel schedule + * + * @chan: The channel associated with the schedule + * @committed: Committed schedule time bitmap + * @conditonal: Conditional schedule time bitmap + * @map_id: The map_id of the availability attribute where this schedule is + * represented + */ +struct nan_chan_schedule { + struct nan_sched_chan chan; + struct nan_time_bitmap committed; + struct nan_time_bitmap conditional; + u8 map_id; +}; + +/** + * struct nan_sched_qos - QoS requirements in units of 16 TUs per 512 TUs + * + * @required_slots: Number of required slots + * @min_slots: Minimum number of CRB slots needed for this NDL. If this amount + * of CRB slots can't be scheduled the NDL should fail. + * @max_gap: Maximum allowed latency (in slots) of the CRB schedule + */ +struct nan_sched_qos { + u8 required_slots; + u8 min_slots; + u8 max_gap; +}; + +#define NAN_SCHEDULE_MAX_CHANNELS 6 + +/** + * struct nan_schedule - NAN schedule + * + * @map_ids_bitmap: Bitmap of map IDs included in this schedule. Not all map IDs + * are covered by &chans. For map IDs that are not covered, when building + * NAFs, NAN availability attributes would be added with potential + * availability entries. + * @n_chans: Number of channels for this schedule. + * @chans: The channels included in the schedule. The channels must be sorted + * such that the map IDs (in struct nan_chan_schedule) are in ascending + * order. + * @ndc: NDC bitmap schedule + * @ndc_map_id: The NDC map ID + * @sequence_id: Schedule sequence ID + * @elems: Additional elements to be set in an element container attribute + */ +struct nan_schedule { + u32 map_ids_bitmap; + u8 n_chans; + struct nan_chan_schedule chans[NAN_SCHEDULE_MAX_CHANNELS]; + struct nan_time_bitmap ndc; + u8 ndc_map_id; + u8 sequence_id; + struct wpabuf *elems; +}; + +/** + * struct nan_gtk - NAN GTK information + + * @gtk: Group Temporal Key (GTK) + * @id: GTK key ID + * @csid: GTK Cipher suite ID. See &enum nan_cipher_suite_id + */ +struct nan_gtk { + struct wpa_gtk gtk; + u8 id; + u8 csid; +}; + +/** + * struct nan_ndp_sec_params - NAN NDP security parameters + * @csid: Cipher suite ID + * @pmk: NAN Pairwise Master Key (PMK) + * @gtk: Group Temporal Key (GTK) information + */ +struct nan_ndp_sec_params { + enum nan_cipher_suite_id csid; + u8 pmk[PMK_LEN]; + struct nan_gtk gtk; +}; + +/** + * struct nan_ndp_params - Holds the NDP parameters for setting up or + * terminating an NDP. + * + * @type: The request type. See &enum nan_ndp_action + * @ndp_id: The NDP identifier + * @qos: The NDP QoS parameters. In case there is no requirement for + * max_latency, max_latency should be set to NAN_QOS_MAX_LATENCY_NO_PREF. + * Should be set only with NAN_NDP_ACTION_REQ and NAN_NDP_ACTION_RESP. + * Ignored for other types. + * @sec: NDP security parameters. Should be set only with NAN_NDP_ACTION_REQ + * and NAN_NDP_ACTION_RESP. Ignored for other types. + * @ssi: Service specific information. Should be set only with + * NAN_NDP_ACTION_REQ and NAN_NDP_ACTION_RESP. Ignored for other types. + * @ssi_len: Service specific information length + * @publish_inst_id: Identifier for the instance of the Publisher function + * associated with the data path setup request. + * @service_id: Service identifier of the service associated with the data path + * setup request. + * @resp_ndi: In case of successful response, the responder's NDI. In case of + * response to a counter proposal, the initiator's NDI (the one used with + * NAN_NDP_ACTION_REQ). + * @status: Response status + * @reason_code: In case of rejected response, the rejection reason. + * @sched_valid: Indicates whether the schedule field is valid + * @sched: The NAN schedule associated with the NDP parameters + * @interface_id: The interface identifier to be used for the NDP. The interface + * identifier is used to derive the IPv6 link-local address as specified in + * Wi-Fi Aware specification v4.0, Table 90 (IPv6 Link Local TLV format). + */ +struct nan_ndp_params { + enum nan_ndp_action type; + + struct nan_ndp_id ndp_id; + struct nan_qos qos; + struct nan_ndp_sec_params sec; + const u8 *ssi; + u16 ssi_len; + + union { + struct nan_ndp_setup_req { + u8 publish_inst_id; + u8 service_id[NAN_SERVICE_ID_LEN]; + } req; + + /* + * Used with both NAN_NDP_ACTION_RESP (as a response to an NDP + * request) and NAN_NDP_ACTION_CONF (as a response to an NDP + * response with a counter). + */ + struct nan_ndp_setup_resp { + u8 resp_ndi[ETH_ALEN]; + u8 status; + u8 reason_code; + } resp; + } u; + + bool sched_valid; + struct nan_schedule sched; + const u8 *interface_id; +}; + +/** + * struct nan_channel_info - Channel information for NAN channel selection + * @op_class: Operating class + * @channel: Control channel index + * @pref: Channel Preference (higher is preferred). Valid values are 0-3. + */ +struct nan_channel_info { + u8 op_class; + u8 channel; + u8 pref; +}; + +/** + * struct nan_channels - Array of channel information entries + * + * @n_chans: Number of channel information entries + * @chans: Array of channel information. Sorted by preference. + */ +struct nan_channels { + unsigned int n_chans; + struct nan_channel_info *chans; +}; + +/** + * struct nan_ndp_connection_params - Parameters for NDP connection + * @ndp_id: NDP identifier + * @peer_ndi: Peer NDI MAC address + * @local_ndi: Local NDI MAC address + * @ssi: Service specific information + * @ssi_len: Service specific information length + * @install_keys: Whether the new keys should be installed + * @first_ndp: Whether this is the first NDP with the peer + * @new_ndi_sta: Whether a new NDI station needs to be added (peer_ndi not + * already used by another NDP with this peer) + * @interface_id: The interface identifier to be used by the peer for the NDP + * @local_gtk: Pointer to local GTK info. NULL if local GTK is + * not to be installed + * @peer_gtk: Pointer to peer GTK info. NULL if peer GTK is + * not to be installed + * @peer_gtk_rsc: Pointer to the peer GTK receive sequence counter + */ +struct nan_ndp_connection_params { + struct nan_ndp_id ndp_id; + const u8 *peer_ndi; + const u8 *local_ndi; + const u8 *ssi; + size_t ssi_len; + bool install_keys; + bool first_ndp; + bool new_ndi_sta; + const u8 *interface_id; + + const struct nan_gtk *local_gtk; + const struct nan_gtk *peer_gtk; + const u8 *peer_gtk_rsc; +}; + +/** + * struct nan_ndp_action_notif_params - Parameters for NDP action notification + * @ndp_id: NDP identifier + * @is_request: Whether the data is associated with an NDP request frame (true) + * or with an NDP response (false). + * @ndp_status: NDP status + * @ndl_status: NDL status + * @publish_inst_id: Identifier for the publish instance function + * @ssi: Service specific information + * @ssi_len: Service specific information length + * @csid: NAN cipher suite identifier + * @pmkid: NAN PMK identifier; can be NULL if security is not negotiated + */ +struct nan_ndp_action_notif_params { + struct nan_ndp_id ndp_id; + bool is_request; + + enum nan_ndp_status ndp_status; + enum nan_ndl_status ndl_status; + + u8 publish_inst_id; + const u8 *ssi; + size_t ssi_len; + enum nan_cipher_suite_id csid; + const u8 *pmkid; +}; + +#define NAN_MAX_MAPS 8 +#define NAN_MAX_CHAN_ENTRIES 16 + +/** + * struct nan_peer_schedule - NAN peer schedule information + * @n_maps: Number of maps + * @maps: Array of maps + * @map_id: Map ID + * @n_chans: Number of channels in the map + * @chans: Array of channels in the map + * @committed: Committed schedule bitmap for the channel + * @rx_nss: Number of spatial streams supported by the peer for RX on this + * channel + * @chan: Channel information + * @tbm: Time bitmap for the channel + * @ndc: NDC time bitmap for the map + * @immutable: Immutable time bitmap for the map + * @max_idle_period: Maximal NDL idle period in seconds that the peer indicated + */ +struct nan_peer_schedule { + u8 n_maps; + struct nan_map { + u8 map_id; + u8 n_chans; + struct nan_map_chan{ + bool committed; + u8 rx_nss; + struct nan_sched_chan chan; + struct nan_time_bitmap tbm; + } chans[NAN_MAX_CHAN_ENTRIES]; + + struct nan_time_bitmap ndc; + struct nan_time_bitmap immutable; + } maps[NAN_MAX_MAPS]; + + u16 max_idle_period; +}; + +/** + * struct nan_peer_potential_avail - NAN peer potential availability + * @n_maps: Number of maps + * @maps: Array of maps + * @is_band: Indicates whether the entries are bands (true) or channels (false) + * @preference: Preference value for the availability entry + * @utilization: Utilization value for the availability entry + * @rx_nss: Number of spatial streams supported by the peer for RX during + * the time indicated by the availability entry + * @n_band_chan: Number of band/channel entries + * @entries: Array of band/channel entries + */ +struct nan_peer_potential_avail { + unsigned int n_maps; + struct pot_entry { + bool is_band; + u8 preference; + u8 utilization; + u8 rx_nss; + + u8 n_band_chan; + union pot_band_chan{ + u8 band_id; + struct { + u8 op_class; + u16 chan_bitmap; + }; + } entries[NAN_MAX_CHAN_ENTRIES]; + } maps[NAN_MAX_MAPS]; +}; + +#define NAN_PAIRING_PASN_128 BIT(0) +#define NAN_PAIRING_PASN_256 BIT(1) + +/** + * struct nan_pairing_cfg - NAN pairing configuration parameters + * @pairing_setup: Whether pairing setup is enabled + * @npk_caching: Whether NPK caching is enabled + * @pairing_verification: Whether pairing verification is enabled + * @cipher_suites: Bitmap of supported cipher suites (NAN_PAIRING_PASN_*) + */ +struct nan_pairing_cfg { + bool pairing_setup; + bool npk_caching; + bool pairing_verification; + u32 cipher_suites; +}; + +struct nan_config { + void *cb_ctx; + u8 nmi_addr[ETH_ALEN]; + + struct nan_device_capabilities dev_capa; + + /* Wi-Fi Aware spec v4.0, Table 141 (Capability Info field) */ + u8 dev_capa_ext_reg_info; /* NAN_DEV_CAPA_EXT_INFO_0_* */ + + struct nan_pairing_cfg pairing_cfg; + u8 nik[NAN_NIK_LEN]; + + /* in seconds */ + u32 nik_lifetime; + + /* + * The local maximal NDL idle period in seconds. This value should be + * set in the NDL attribute included in NAFs to indicate to the peers + * that the NDL (and all corresponding NDPs) may be terminated if there + * is no data traffic with the peer for max_ndl_idle_period seconds. + */ + u16 max_ndl_idle_period; + + /* + * Supported Pairing Bootstrapping Methods (PBM). + * See Wi-Fi Aware spec v4.0, Table 128 (NPBA format). + */ + u16 supported_bootstrap_methods; + + /* Auto-accepted bootstrapping methods. + * See Wi-Fi Aware spec v4.0, Table 128 (NPBA format). */ + u16 auto_accept_bootstrap_methods; + + /* + * Bootstrap comeback timeout in TUs. This value is used to indicate to + * the peer NAN device requesting bootstrapping to be performed, when + * to send the bootstrapping request again. + */ + u16 bootstrap_comeback_timeout; + + /* Security capabilities. See Wi-Fi Aware spec v4.0, Table 122 (Cipher + * Suite Information attribute (CSIA) field format), Capabilities field. + */ + u8 security_capab; + + /** + * start - Start NAN + * @ctx: Callback context from cb_ctx + * @config: NAN cluster configuration + */ + int (*start)(void *ctx, const struct nan_cluster_config *config); + + /** + * stop - Stop NAN + * @ctx: Callback context from cb_ctx + */ + void (*stop)(void *ctx); + + /** + * update_config - Update NAN configuration + * @ctx: Callback context from cb_ctx + * @config: NAN cluster configuration + */ + int (*update_config)(void *ctx, + const struct nan_cluster_config *config); + + /** + * ndp_action_notif - Notify NDP action is required + * @ctx: Callback context from cb_ctx + * @params: NDP action notification parameters + * + * A notification sent when an NDP establishment frame is received, and + * upper layer input is required to continue the flow. + */ + void (*ndp_action_notif)(void *ctx, + struct nan_ndp_action_notif_params *params); + + /** + * ndp_connected - Notify that NDP was successfully connected + * @ctx: Callback context from cb_ctx + * @params: NDP connection parameters + * Returns: 0 on success, negative on failure. Note that new NDPs + * may trigger security upgrade for the peer NDI station. If this fails, + * -2 is returned and the caller should clean up all the existing NDPs + * with this peer NDI. + */ + int (*ndp_connected)(void *ctx, + struct nan_ndp_connection_params *params); + + /** + * ndp_disconnected - Notify that NDP was disconnected + * @ctx: Callback context from cb_ctx + * @ndp_id: NDP identifier + * @local_ndi: Local NDI MAC address + * @peer_ndi: Peer NDI MAC address + * @reason: Disconnection reason + * @locally_generated: true if the disconnection was locally generated, + * false if triggered by the peer + * @remove_sta: true if the NDI station should be removed (no other NDPs + * using the same peer NDI) + * @failure: true if NDP setup failed (before connected), false if + * graceful disconnection after NDP was established + * @gtk_id: GTK key ID used for the NDP; 0 if no GTK should be removed + * + * This callback notifies that an NDP has been disconnected. When + * @failure is true, NDP setup failed before connection was established. + * When @failure is false, it indicates graceful termination after NDP + * was successfully connected. + */ + void (*ndp_disconnected)(void *ctx, struct nan_ndp_id *ndp_id, + const u8 *local_ndi, const u8 *peer_ndi, + enum nan_reason reason, + bool locally_generated, bool remove_sta, + bool failure, u8 gtk_id); + + /** + * get_chans - Get the prioritized allowed channel information to be + * used for building the potential availability entries associated with + * the given map ID. + * + * @ctx: Callback context from cb_ctx + * @map_id: Map ID of the availability attribute for which the channels + * are requested. + * @chans: Pointer to a nan_channels structure that should be filled + * with the prioritized frequencies. On successful return the + * channels should be sorted having the higher priority channels + * first. + * Returns: 0 on success, -1 on failure. + * + * Note: The callback is responsible for allocating chans->chans as + * needed. The caller (the NAN module) is responsible for freeing the + * memory allocated for the chans->chans. + * + * Note: The callback should add all channels that are considered valid + * for use by the NAN module for the given map. + */ + int (*get_chans)(void *ctx, u8 map_id, struct nan_channels *chans); + + /** + * send_naf - Transmit a NAN Action frame + * @ctx: Callback context from cb_ctx + * @dst: Destination MAC address + * @src: Source MAC address. Can be NULL. + * @cluster_id: The cluster ID + * @buf: Frame body (starting from the Category field) + * Returns: 0 on success, -1 on failure + */ + int (*send_naf)(void *ctx, const u8 *dst, const u8 *src, + const u8 *cluster_id, struct wpabuf *buf); + + /** + * is_valid_publish_id - Check if a publish instance ID is valid + * @ctx: Callback context from cb_ctx + * @instance_id: The instance ID to check + * @service_id: On return, holds the service ID if the instance ID is + * valid + * Returns: true if there is a local publish service ID with the given + * instance ID; false otherse + */ + bool (*is_valid_publish_id)(void *ctx, u8 instance_id, u8 *service_id); + + /** + * set_peer_schedule - Configure peer schedule + * @ctx: Callback context from cb_ctx + * @nmi_addr: NAN Management Interface address of the peer + * @new_sta: Indicates whether this is a new STA (true) or an existing + * STA that is being re-configured (false) + * @cdw: Committed DW information (from device capabilities) + * @sequence_id: Schedule sequence ID + * @max_channel_switch_time: Maximum channel switch time + * @sched: Peer schedule information; can be NULL + * @ulw_elems: ULW elements buffer; can be NULL + * Returns: 0 on success, -1 on failure + */ + int (*set_peer_schedule)(void *ctx, const u8 *nmi_addr, bool new_sta, + u16 cdw, u8 sequence_id, + u16 max_channel_switch_time, + const struct nan_peer_schedule *sched, + const struct wpabuf *ulw_elems); + /** + * bootstrap_request - Notify about received bootstrap request + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + * @pbm: Pairing Bootstrapping Methods from the request. As defined in + * Wi-Fi Aware spec v4.0, Table 128 (NPBA format). + * @handle: Service handle + * @requestor_instance_id: Requestor instance ID + */ + void (*bootstrap_request)(void *ctx, const u8 *peer_nmi, u16 pbm, + int handle, u8 requestor_instance_id); + + /** + * bootstrap_completed - Notify about completed bootstrap + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + * @pbm: Pairing Bootstrapping Method used. As defined in Wi-Fi Aware + * spec v4.0, Table 128 (NPBA format). + * @success: Whether bootstrap was successful + * @reason_code: Reason code for failure (0 if success is true) + * @handle: Service handle + * @requestor_instance_id: Requestor instance ID + */ + void (*bootstrap_completed)(void *ctx, const u8 *peer_nmi, u16 pbm, + bool success, u8 reason_code, + int handle, u8 requestor_instance_id); + + /** + * transmit_followup - Transmit Follow-up message to the peer + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + * @attrs: Attributes to include in the Follow-up message + * @handle: Service handle for which the follow-up is sent + * @req_instance_id: Peer's service instance ID + */ + int (*transmit_followup)(void *ctx, const u8 *peer_nmi, + const struct wpabuf *attrs, int handle, + u8 req_instance_id); + + /** + * get_supported_bootstrap_methods - Get supported bootstrap methods + * @ctx: Callback context from cb_ctx + * @handle: Service handle for which PBM should have been defined + * Returns: Supported Pairing Bootstrapping Methods (PBM) bitfield as + * configured for the service or 0 if service is not found. + */ + u16 (*get_supported_bootstrap_methods)(void *ctx, int handle); + + /** + * send_pasn - Transmit a PASN Authentication frame + * @ctx: Callback context from cb_ctx + * @data: Frame to transmit + * @data_len: Length of frame to transmit + * Returns: 0 on success, -1 on failure + */ + int (*send_pasn)(void *ctx, const u8 *data, size_t data_len); + + /** + * pairing_status_cb - Callback for reporting NAN pairing result + * @ctx: Callback context from cb_ctx + * @peer_addr: Peer NAN device address + * @akmp: AKMP used in the pairing + * @cipher: Cipher used in the pairing + * @status: Status of the pairing (WLAN_STATUS_* ) + * @ptk: Derived PTK for the pairing (valid only if status is success) + * @nd_pmk: ND-PMK from the pairing (valid only if status is success) + * Returns: 0 if status is WLAN_STATUS_SUCCESS and the key was + * installed successfully or status is + * WLAN_STATUS_UNSPECIFIED_FAILURE, -1 otherwise + */ + int (*pairing_result_cb)(void *ctx, const u8 *peer_addr, int akmp, + int cipher, u16 status, struct wpa_ptk *ptk, + const u8 *nd_pmk); + + /** + * update_pairing_credentials - Report received NIK and NPK for a peer + * @ctx: Callback context from cb_ctx + * @nik: NAN Identity Key received from peer + * @nik_len: Length of the NIK + * @cipher_ver: Cipher version of the NIK + * @nik_lifetime: Lifetime of the NIK in seconds + * @akmp: AKMP suite used to establish the NPKSA + * @npk: The NPK associated with the received NIK + * @npk_len: Length of the NPK + * Returns: 0 on success, -1 on failure + */ + int (*update_pairing_credentials)(void *ctx, const u8 *nik, + size_t nik_len, int cipher_ver, + int nik_lifetime, int akmp, + const u8 *npk, size_t npk_len); + + /** + * get_npk_akmp - Get the cached NPK and AKMP for a peer + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + * @nonce: Nonce from the peer's NIRA + * @tag: Tag from the peer's NIRA + * @akmp: On success, set to the AKMP suite used to establish the NPKSA + * Returns: The NPK on success, NULL on failure + */ + const struct wpabuf * (*get_npk_akmp)(void *ctx, const u8 *peer_nmi, + const u8 *nonce, const u8 *tag, + int *akmp); + + /** + * pairing_request - Notify about received pairing request + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + * @csid: Cipher suite ID requested by the peer + * @instance_id: Service instance ID for which the pairing is requested + * @rsn_data: Parsed RSNE data from peer's Authentication frame + */ + void (*pairing_request)(void *ctx, const u8 *peer_nmi, u8 csid, + u8 instance_id, + const struct wpa_ie_data *rsn_data); + + /** + * set_group_key - Install a group key + * @ctx: Callback context from cb_ctx + * @alg: Encryption algorithm (WPA_ALG_* ) + * @addr: Address of the peer STA for Rx group keys, ff:ff:ff:ff:ff:ff + * for Tx keys; when clearing keys, %NULL is used to indicate that + * both the broadcast-only and default key of the specified key + * index is to be cleared + * @key_idx: Key index + * @seq: Packet number, the next packet number to be used for in replay + * protection; %NULL if not set + * @key: Key buffer + * @key_len: Length of the key buffer in octets + * @key_flags: bitwise OR of KEY_FLAG_* + * Returns: 0 on success, -1 on failure + */ + int (*set_group_key)(void *ctx, enum wpa_alg alg, const u8 *addr, + int key_idx, const u8 *seq, + const u8 *key, size_t key_len, + enum key_flag key_flags); + + /** + * get_seqnum - Get the current PN for a group key + * @ctx: Callback context from cb_ctx + * @key_idx: Key index + * @seq: Buffer for returning the latest used PN value + * @ndi_addr: For NDI group keys, the NDI MAC address; %NULL for + * NMI group keys + * Returns: 0 on success, -1 on failure + */ + int (*get_seqnum)(void *ctx, int key_idx, u8 *seq, const u8 *ndi_addr); + + /** + * get_peer_inactivity - Get the inactivity time for a peer + * @ctx: Callback context from cb_ctx + * @local_ndi: Local NDI address + * @peer_ndi: Peer NDI address + * Returns: Peer inactivity in seconds, negative value on failure + */ + int (*get_peer_inactivity)(void *ctx, const u8 *local_ndi, + const u8 *peer_ndi); + + /** + * schedule_changed - Notify about peer schedule change + * @ctx: Callback context from cb_ctx + * @peer_nmi: Peer NMI address + */ + void (*schedule_changed)(void *ctx, const u8 *peer_nmi); +}; + +struct nan_data * nan_init(const struct nan_config *cfg); +void nan_deinit(struct nan_data *nan); +int nan_start(struct nan_data *nan, const struct nan_cluster_config *config); +int nan_update_config(struct nan_data *nan, + const struct nan_cluster_config *config); +void nan_set_cdw_overwrite(struct nan_data *nan, int map_id_2g, int map_id_5g); +void nan_stop(struct nan_data *nan); +void nan_flush(struct nan_data *nan); + +int nan_add_peer(struct nan_data *nan, const u8 *addr, + const u8 *device_attrs, size_t device_attrs_len); +bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf, + size_t len, u8 req_instance_id, int handle); +int nan_bootstrap_request(struct nan_data *nan, int handle, + const u8 *peer_addr, u8 req_instance_id, u16 pbm, + bool auth); +int nan_bootstrap_peer_reset(struct nan_data *nan, const u8 *peer_nmi); +int nan_bootstrap_get_supported_methods(struct nan_data *nan, + const u8 *peer_nmi, + u16 *supported_methods); + +bool nan_publish_instance_id_valid(struct nan_data *nan, u8 instance_id, + u8 *service_id); +void nan_set_cluster_id(struct nan_data *nan, const u8 *cluster_id); +int nan_action_rx(struct nan_data *nan, const struct ieee80211_mgmt *mgmt, + size_t len); +int nan_tx_status(struct nan_data *nan, const u8 *dst, const u8 *data, + size_t data_len, bool acked); +int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params); +struct nan_device_capabilities * +nan_peer_get_device_capabilities(struct nan_data *nan, const u8 *addr, + u8 map_id); +int nan_peer_get_tk(struct nan_data *nan, const u8 *addr, + const u8 *peer_ndi, const u8 *local_ndi, + u8 *tk, size_t *tk_len, enum nan_cipher_suite_id *csid); +int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr, + struct nan_peer_schedule *sched); +int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched, + char *buf, size_t buflen); +int nan_peer_get_pot_avail(struct nan_data *nan, const u8 *addr, + struct nan_peer_potential_avail *pot_avail); +int nan_peer_dump_pot_avail_to_buf(struct nan_peer_potential_avail *pot_avail, + char *buf, size_t buflen); +const struct nan_pairing_cfg * nan_peer_get_pairing_cfg(struct nan_data *nan, + const u8 *addr, + const u8 **nonce, + const u8 **tag); +int nan_convert_sched_to_avail_attrs(struct nan_data *nan, u8 sequence_id, + u32 map_ids_bitmap, + size_t n_chans, + struct nan_chan_schedule *chans, + struct wpabuf *buf, + bool include_potential); +void nan_local_sched_update(struct nan_data *nan, struct nan_schedule *sched); +void nan_set_sched_update_pending(struct nan_data *nan, bool pending); +bool nan_peer_pairing_supported(struct nan_data *nan, const u8 *addr); +bool nan_peer_npk_nik_caching_supported(struct nan_data *nan, const u8 *addr); +int nan_get_peer_ndc_freq(struct nan_data *nan, + const struct nan_peer_schedule *peer_sched, + u8 map_idx); +int nan_crypto_derive_nd_pmk(const char *pwd, const u8 *service_id, + enum nan_cipher_suite_id csid, + const u8 *peer_nmi, u8 *nd_pmk); +int nan_crypto_pmkid_list(struct dl_list *pmkid_list, const u8 *raddr, + const u8 *srv_id, const int *cipher_suites_list, + const u8 *pmk); +void nan_crypto_clear_pmkid_list(struct dl_list *pmkid_list); +void nan_add_dev_capa_attr(struct nan_data *nan, struct wpabuf *buf); +int nan_peer_del_all_ndps(struct nan_data *nan, const u8 *addr); +int nan_get_chan_entry(struct nan_data *nan, const struct nan_sched_chan *chan, + struct nan_chan_entry *chan_entry); +int nan_get_peer_elems(struct nan_data *nan, const u8 *addr, u8 **elems); +int nan_set_bootstrap_configuration(struct nan_data *nan, + u16 supported_bootstrap_methods, + u16 auto_accept_bootstrap_methods, + u16 bootstrap_comeback_timeout); +struct wpabuf * nan_crypto_derive_nira_tag(const u8 *nik, size_t nik_len, + const u8 *nmi_addr, + const u8 *nira_nonce); +int nan_ndp_requested_gtk_csid(struct nan_data *nan, + const struct nan_ndp_id *ndp_id); +int nan_set_mgmt_group_cipher(struct nan_data *nan, int cipher); +int nan_set_beacon_prot(struct nan_data *nan, bool enable); +int nan_set_max_ndl_idle_period(struct nan_data *nan, u16 max_idle_period); +bool nan_has_active_ndp(struct nan_data *nan); +int nan_get_status(struct nan_data *nan, char *buf, size_t buflen); +int nan_peer_dump_ndps_to_buf(struct nan_data *nan, const u8 *addr, + char *buf, size_t buflen); +void nan_terminate_ndi_ndps(struct nan_data *nan, const u8 *ndi_addr); + +#ifdef CONFIG_PASN +int nan_pairing_add_attrs(struct nan_data *nan_data, struct wpabuf *buf); +int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr, + u8 auth_mode, int cipher, int handle, + u8 peer_instance_id, bool responder, + const char *password, + const struct nan_schedule *sched); +int nan_pairing_pasn_auth_tx_status(struct nan_data *nan, const u8 *data, + size_t data_len, bool acked); +int nan_pairing_auth_rx(struct nan_data *nan_data, + const struct ieee80211_mgmt *mgmt, size_t len); +int nan_pairing_set_pairing_setup(struct nan_data *nan_data, bool value); +int nan_pairing_set_npk_caching(struct nan_data *nan_data, bool value); +int nan_pairing_set_pairing_verification(struct nan_data *nan_data, bool value); +int nan_pairing_set_cipher_suites(struct nan_data *nan_data, u32 value); +int nan_pairing_set_nik(struct nan_data *nan, const u8 *nik, size_t nik_len); +int nan_pairing_set_nik_lifetime(struct nan_data *nan, u32 lifetime); +bool nan_pairing_is_peer_paired(struct nan_data *nan_data, const u8 *peer_addr); +int nan_pairing_abort(struct nan_data *nan_data, const u8 *peer_addr); +void nan_pairing_unpair_peer(struct nan_data *nan_data, const u8 *peer_addr); +#else /* CONFIG_PASN */ +static inline int nan_pairing_add_attrs(struct nan_data *nan_data, + struct wpabuf *buf) +{ + return 0; +} + +static inline +int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr, + u8 auth_mode, int cipher, int handle, + u8 peer_instance_id, bool responder, + const char *password, + const struct nan_schedule *sched) +{ + return -1; +} + +static inline int nan_pairing_pasn_auth_tx_status(struct nan_data *nan, + const u8 *data, + size_t data_len, bool acked) +{ + return -1; +} + +static inline int nan_pairing_auth_rx(struct nan_data *nan_data, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + return -1; +} + +static inline +bool nan_pairing_is_peer_paired(struct nan_data *nan_data, const u8 *peer_addr) +{ + return false; +} + +static inline +void nan_pairing_unpair_peer(struct nan_data *nan_data, const u8 *peer_addr) +{ +} + +#endif /* CONFIG_PASN */ + +#endif /* NAN_H */ diff --git a/contrib/wpa/src/nan/nan_bootstrap.c b/contrib/wpa/src/nan/nan_bootstrap.c new file mode 100644 index 000000000000..399ea66a9638 --- /dev/null +++ b/contrib/wpa/src/nan/nan_bootstrap.c @@ -0,0 +1,741 @@ +/* + * Wi-Fi Aware - NAN Bootstrap + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "utils/eloop.h" +#include "nan_i.h" + +#define NAN_BOOTSTRAP_RETRY_TIMEOUT_US (1 * 1000 * 1000) /* 1 second */ +#define NAN_BOOTSTRAP_COOKIE_LEN 4 + +static void nan_bootstrap_timeout(void *eloop_data, void *user_ctx); + + +/** + * nan_complement_pbm - Get complement pairing bootstrapping method + * @pbm: Pairing bootstrapping method + * Returns: Complement pairing bootstrapping method + */ +static u16 nan_complement_pbm(u16 pbm) +{ + if (pbm == NAN_PBA_METHOD_OPPORTUNISTIC) + return NAN_PBA_METHOD_OPPORTUNISTIC; + if (pbm == NAN_PBA_METHOD_PIN_DISPLAY) + return NAN_PBA_METHOD_PIN_KEYPAD; + if (pbm == NAN_PBA_METHOD_PASSPHRASE_DISPLAY) + return NAN_PBA_METHOD_PASSPHRASE_KEYPAD; + if (pbm == NAN_PBA_METHOD_QR_DISPLAY) + return NAN_PBA_METHOD_QR_SCAN; + if (pbm == NAN_PBA_METHOD_NFC_TAG) + return NAN_PBA_METHOD_NFC_READER; + if (pbm == NAN_PBA_METHOD_PIN_KEYPAD) + return NAN_PBA_METHOD_PIN_DISPLAY; + if (pbm == NAN_PBA_METHOD_PASSPHRASE_KEYPAD) + return NAN_PBA_METHOD_PASSPHRASE_DISPLAY; + if (pbm == NAN_PBA_METHOD_QR_SCAN) + return NAN_PBA_METHOD_QR_DISPLAY; + if (pbm == NAN_PBA_METHOD_NFC_READER) + return NAN_PBA_METHOD_NFC_TAG; + if (pbm == NAN_PBA_METHOD_SERVICE_MANAGED) + return NAN_PBA_METHOD_SERVICE_MANAGED; + if (pbm == NAN_PBA_METHOD_HANDSHAKE_SKIPPED) + return NAN_PBA_METHOD_HANDSHAKE_SKIPPED; + + return 0; +} + + +/** + * nan_bootstrap_reset - Reset bootstrap state + * @nan: NAN module context from nan_init() + * @peer: Peer to reset bootstrap state + */ +void nan_bootstrap_reset(struct nan_data *nan, struct nan_peer *peer) +{ + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Reset state"); + + eloop_cancel_timeout(nan_bootstrap_timeout, nan, peer); + os_free(peer->bootstrap.cookie); + + /* + * Do not use memset to reset all data to preserve the peer's + * supported bootstrap methods and the NPBA buffer if present. + */ + peer->bootstrap.cookie = NULL; + peer->bootstrap.cookie_len = 0; + peer->bootstrap.initiator = false; + peer->bootstrap.dialog_token = 0; + peer->bootstrap.requested_pbm = 0; + peer->bootstrap.authorized = 0; + peer->bootstrap.status = 0; + peer->bootstrap.comeback_required = false; + peer->bootstrap.in_progress = false; + peer->bootstrap.reason_code = 0; + peer->bootstrap.handle = -1; + peer->bootstrap.req_instance_id = 0; +} + + +/** + * nan_bootstrap_build_npba - Build NAN Pairing Bootstrap attribute + * @nan: NAN module context from nan_init() + * @peer: Peer for which to build the attribute + * Returns: The constructed NPBA or %NULL on failure + */ +static struct wpabuf * nan_bootstrap_build_npba(struct nan_data *nan, + struct nan_peer *peer) +{ + struct wpabuf *buf; + u16 pbm = peer->bootstrap.requested_pbm; + u8 type_and_status; + u8 *len; + + /* + * Allocate max possible size: header (3) + Dialog Token (1) + Type and + * Status (1) + Reason (1) + Comeback (2 + 1 + Comeback Token) + pbm (2) + */ + buf = wpabuf_alloc(11 + peer->bootstrap.cookie_len); + if (!buf) + return NULL; + + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Build NPBA"); + + if (peer->bootstrap.initiator) { + type_and_status = NAN_PBA_TYPE_RESPONSE; + if (peer->bootstrap.status) + pbm = 0; + } else { + type_and_status = NAN_PBA_TYPE_REQUEST; + } + + type_and_status |= peer->bootstrap.status << NAN_PBA_STATUS_POS; + + wpabuf_put_u8(buf, NAN_ATTR_NPBA); + len = wpabuf_put(buf, 2); + + wpabuf_put_u8(buf, peer->bootstrap.dialog_token); + wpabuf_put_u8(buf, type_and_status); + wpabuf_put_u8(buf, peer->bootstrap.reason_code); + + if (peer->bootstrap.status == NAN_PBA_STATUS_COMEBACK) { + if (peer->bootstrap.initiator) + wpabuf_put_le16(buf, peer->bootstrap.comeback_after); + + wpabuf_put_u8(buf, peer->bootstrap.cookie_len); + if (peer->bootstrap.cookie) + wpabuf_put_data(buf, peer->bootstrap.cookie, + peer->bootstrap.cookie_len); + } + + wpabuf_put_le16(buf, pbm); + WPA_PUT_LE16(len, wpabuf_len(buf) - 3); + + return buf; +} + + +/** + * nan_bootstrap_timeout - Bootstrap timeout handler + * @eloop_data: NAN module context from nan_init() + * @user_ctx: Peer for which the timeout occurred + */ +static void nan_bootstrap_timeout(void *eloop_data, void *user_ctx) +{ + struct nan_data *nan = eloop_data; + struct nan_peer *peer = user_ctx; + struct wpabuf *attr; + + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: timeout. status=%u", + peer->bootstrap.status); + + attr = nan_bootstrap_build_npba(nan, peer); + if (!attr) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed to build attribute"); + + nan_bootstrap_reset(nan, peer); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr, + 0, false, + NAN_REASON_UNSPECIFIED_REASON, + -1, 0); + return; + } + + if (nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr, attr, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed to transmit followup"); + + nan_bootstrap_reset(nan, peer); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr, + 0, false, + NAN_REASON_UNSPECIFIED_REASON, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id); + } + + wpabuf_free(attr); + + /* If the peer didn't reply, try again */ + if (peer->bootstrap.status != NAN_PBA_STATUS_COMEBACK) + eloop_register_timeout(0, NAN_BOOTSTRAP_RETRY_TIMEOUT_US, + nan_bootstrap_timeout, nan, peer); +} + + +/** + * nan_bootstrap_handle_rx_request - Process received bootstrap request + * @nan: NAN module context from nan_init() + * @peer: Peer from which the request was received + * @dialog_token: Dialog token from the request + * @pbm: Pairing bootstrapping method bitmap from the request + * @cookie: Comeback cookie from the request (if any) + * @cookie_len: Length of the comeback cookie + * @status: Status field from the request + * @handle: Follow-up handle + * @req_instance_id: Follow-up instance ID + * @npba: NPBA from the request + * @npba_len: Length of the NPBA + */ +static void nan_bootstrap_handle_rx_request(struct nan_data *nan, + struct nan_peer *peer, + u8 dialog_token, u16 pbm, + const u8 *cookie, + u8 cookie_len, u8 status, + int handle, u8 req_instance_id, + const u8 *npba, u16 npba_len) +{ + struct wpabuf *attr = NULL; + u16 supported_methods; + + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: RX request"); + + if (peer->bootstrap.initiator && peer->bootstrap.comeback_required) { + if (status != NAN_PBA_STATUS_COMEBACK) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Missing comeback cookie. Ignore"); + return; + } + + if (cookie_len != peer->bootstrap.cookie_len || + !!cookie ^ !!peer->bootstrap.cookie || + (cookie && os_memcmp(cookie, peer->bootstrap.cookie, + cookie_len) != 0)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Invalid comeback cookie. Ignore"); + return; + } + + if (dialog_token != peer->bootstrap.dialog_token) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Invalid dialog token. Ignore"); + return; + } + + if (handle != peer->bootstrap.handle || + req_instance_id != peer->bootstrap.req_instance_id) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Invalid handle or instance ID"); + return; + } + + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Valid comeback request(pbm=0x%04x) authorized=0x%04x", + pbm, peer->bootstrap.authorized); + + if (peer->bootstrap.authorized == nan_complement_pbm(pbm)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Peer bootstrap authorized. Accept."); + + peer->bootstrap.status = NAN_PBA_STATUS_ACCEPTED; + peer->bootstrap.reason_code = 0; + peer->bootstrap.comeback_required = false; + peer->bootstrap.requested_pbm = nan_complement_pbm(pbm); + } + + goto send_response; + } + + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: New bootstrap request. pbm=0x%04x", pbm); + + nan_bootstrap_reset(nan, peer); + + peer->bootstrap.initiator = true; + peer->bootstrap.dialog_token = dialog_token; + peer->bootstrap.requested_pbm = nan_complement_pbm(pbm); + peer->bootstrap.in_progress = true; + peer->bootstrap.handle = handle; + peer->bootstrap.req_instance_id = req_instance_id; + supported_methods = + nan->cfg->get_supported_bootstrap_methods(nan->cfg->cb_ctx, + handle); + + if (!(supported_methods & peer->bootstrap.requested_pbm)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: No supported bootstrap methods in request"); + + peer->bootstrap.status = NAN_PBA_STATUS_REJECTED; + peer->bootstrap.reason_code = + NAN_REASON_PAIR_BOOTSTRAP_REJECTED; + goto send_response; + } + + if (peer->bootstrap.requested_pbm & + nan->cfg->auto_accept_bootstrap_methods) { + peer->bootstrap.authorized = peer->bootstrap.requested_pbm; + peer->bootstrap.status = NAN_PBA_STATUS_ACCEPTED; + goto send_response; + } + + peer->bootstrap.status = NAN_PBA_STATUS_COMEBACK; + peer->bootstrap.comeback_required = true; + peer->bootstrap.cookie_len = NAN_BOOTSTRAP_COOKIE_LEN; + peer->bootstrap.cookie = os_malloc(peer->bootstrap.cookie_len); + + if (peer->bootstrap.cookie) { + if (os_get_random(peer->bootstrap.cookie, + peer->bootstrap.cookie_len) < 0) { + nan_bootstrap_reset(nan, peer); + return; + } + } else { + peer->bootstrap.cookie_len = 0; + } + + peer->bootstrap.comeback_after = nan->cfg->bootstrap_comeback_timeout; + + if (nan->cfg->bootstrap_request) + nan->cfg->bootstrap_request(nan->cfg->cb_ctx, peer->nmi_addr, + peer->bootstrap.requested_pbm, + handle, req_instance_id); + +send_response: + attr = nan_bootstrap_build_npba(nan, peer); + if (!attr) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed to build bootstrap attribute"); + goto done; + } + + if (nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr, attr, + handle, req_instance_id)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed to transmit bootstrap followup"); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr, + 0, false, + NAN_REASON_UNSPECIFIED_REASON, + handle, req_instance_id); + goto done; + } + + if (peer->bootstrap.status == NAN_PBA_STATUS_COMEBACK) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Wait for comeback bootstrap request"); + + wpabuf_free(attr); + return; + } + + if (peer->bootstrap.status == NAN_PBA_STATUS_ACCEPTED) { + wpabuf_free(peer->bootstrap.npba); + peer->bootstrap.npba = wpabuf_alloc(3 + npba_len); + if (peer->bootstrap.npba) { + wpabuf_put_u8(peer->bootstrap.npba, NAN_ATTR_NPBA); + wpabuf_put_le16(peer->bootstrap.npba, npba_len); + wpabuf_put_data(peer->bootstrap.npba, npba, npba_len); + } else { + wpa_printf(MSG_INFO, + "NAN: Bootstrap: Failed to store NPBA"); + } + } + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, + peer->nmi_addr, + peer->bootstrap.requested_pbm, + peer->bootstrap.status == + NAN_PBA_STATUS_ACCEPTED, + peer->bootstrap.reason_code, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id); +done: + wpabuf_free(attr); + nan_bootstrap_reset(nan, peer); +} + + +/** + * nan_bootstrap_supported - Check if bootstrap operations are supported + * @nan: NAN module context from nan_init() + * Returns: Whether all required bootstrap operations are supported + */ +static bool nan_bootstrap_supported(struct nan_data *nan) +{ + return nan->cfg->bootstrap_completed && + nan->cfg->bootstrap_request && + nan->cfg->transmit_followup && + nan->cfg->get_supported_bootstrap_methods; +} + + +/** + * nan_bootstrap_handle_rx - Process received bootstrap follow-up + * @nan: NAN module context from nan_init() + * @peer_nmi: Peer address from which the follow-up was received + * @npba: Pointer to the NPBA in the follow-up + * @npba_len: Length of the NPBA + * @buf: Complete Follow-up frame + * @len: Length of the complete Follow-up frame + * @handle: Follow up handle + * @req_instance_id: Follow-up instance ID + * Returns: true if the follow up was processed, false on error + */ +bool nan_bootstrap_handle_rx(struct nan_data *nan, const u8 *peer_nmi, + const u8 *npba, u16 npba_len, + const u8 *buf, size_t len, + int handle, u8 req_instance_id) +{ + const u8 *cookie = NULL; + u8 dialog_token, type, status, reason_code, cookie_len = 0; + u16 pbm, comeback_after = 0; + struct nan_peer *peer; + const u8 *orig_npba = npba; + u16 orig_npba_len = npba_len; + + if (!nan_bootstrap_supported(nan)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Not all bootstrap operations are supported. Ignore"); + return false; + } + + if (npba_len < 3) + return false; + dialog_token = *npba++; + type = *npba & NAN_PBA_TYPE_MASK; + status = (*npba++ >> NAN_PBA_STATUS_POS) & NAN_PBA_STATUS_MASK; + reason_code = *npba++; + npba_len -= 3; + + if (type == NAN_PBA_TYPE_ADVERTISE) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: type == Advertise. Ignore"); + return false; + } + + if (type != NAN_PBA_TYPE_REQUEST && + type != NAN_PBA_TYPE_RESPONSE) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Unknown NPBA type %u", type); + return false; + } + + if (status == NAN_PBA_STATUS_COMEBACK) { + if (type == NAN_PBA_TYPE_RESPONSE) { + if (npba_len < 2) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Response too short for comeback after"); + return false; + } + + comeback_after = WPA_GET_LE16(npba); + npba += 2; + npba_len -= 2; + } + + if (npba_len < 1) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Response too short for comeback cookie length"); + return false; + } + + cookie_len = *npba++; + npba_len--; + if (cookie_len > npba_len) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Comeback field is too long"); + return false; + } + + cookie = npba; + npba += cookie_len; + npba_len -= cookie_len; + } + + if (npba_len < 2) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Buffer too short for PBM"); + return false; + } + + pbm = WPA_GET_LE16(npba); + peer = nan_get_peer(nan, peer_nmi); + + /* Handle NAN bootstrap request */ + if (type == NAN_PBA_TYPE_REQUEST) { + if (!peer) { + nan_add_peer(nan, peer_nmi, buf, len); + peer = nan_get_peer(nan, peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed alloc peer from bootstrap request"); + return false; + } + } + + nan_bootstrap_handle_rx_request(nan, peer, dialog_token, + pbm, cookie, cookie_len, + status, handle, + req_instance_id, + orig_npba, orig_npba_len); + return true; + } + + /* Handle NAN bootstrap response */ + if (!peer || !peer->bootstrap.in_progress || + peer->bootstrap.initiator) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Unexpected bootstrap response"); + return false; + } + + if (dialog_token != peer->bootstrap.dialog_token) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Response with invalid dialog token. Ignore"); + return false; + } + + if (handle != peer->bootstrap.handle || + req_instance_id != peer->bootstrap.req_instance_id) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Response with invalid handle or instance ID. Ignore"); + return false; + } + + peer->bootstrap.status = status; + peer->bootstrap.reason_code = reason_code; + peer->bootstrap.comeback_required = status == NAN_PBA_STATUS_COMEBACK; + peer->bootstrap.comeback_after = comeback_after; + + if (cookie && cookie_len) { + os_free(peer->bootstrap.cookie); + peer->bootstrap.cookie = os_memdup(cookie, cookie_len); + if (!peer->bootstrap.cookie) { + nan_bootstrap_reset(nan, peer); + return false; + } + peer->bootstrap.cookie_len = cookie_len; + } + + if (status == NAN_PBA_STATUS_ACCEPTED && + nan_complement_pbm(pbm) == peer->bootstrap.requested_pbm) { + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Accepted. Complete."); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, + peer->nmi_addr, + peer->bootstrap.requested_pbm, + true, 0, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id); + nan_bootstrap_reset(nan, peer); + } else if (status == NAN_PBA_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Rejected. Complete"); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr, + 0, false, reason_code, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id); + nan_bootstrap_reset(nan, peer); + } else if (status == NAN_PBA_STATUS_COMEBACK) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Comeback required. Schedule timeout for %u TU", + peer->bootstrap.comeback_after); + + eloop_cancel_timeout(nan_bootstrap_timeout, nan, peer); + eloop_register_timeout(peer->bootstrap.comeback_after / 1024, + (peer->bootstrap.comeback_after % 1024) * + 1000, + nan_bootstrap_timeout, nan, peer); + } else { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Unknown status=%u. Abort.", + status); + + nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr, + 0, false, + NAN_REASON_UNSPECIFIED_REASON, + peer->bootstrap.handle, + peer->bootstrap.req_instance_id); + nan_bootstrap_reset(nan, peer); + } + + return true; +} + + +/** + * nan_bootstrap_request - Initiate NAN bootstrap request to a peer + * @nan: NAN module context from nan_init() + * @handle: Follow up handle + * @peer_nmi: Peer address to which to send the bootstrap request + * @req_instance_id: Follow up instance ID + * @pbm: Pairing bootstrapping method bitmap to use + * @auth: If true, authorize the bootstrap request for the peer instead of + * sending the request + * Returns: 0 on success, -1 on failure + */ +int nan_bootstrap_request(struct nan_data *nan, int handle, + const u8 *peer_nmi, u8 req_instance_id, u16 pbm, + bool auth) +{ + struct nan_peer *peer; + int ret; + + if (!nan || !nan->nan_started) + return -1; + + if (!nan_bootstrap_supported(nan)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Not all bootstrap operations are supported. Ignore"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Request bootstrap to peer " MACSTR + " pbm=0x%x auth=%d", + MAC2STR(peer_nmi), pbm, auth); + + peer = nan_get_peer(nan, peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Request for unknown peer"); + return -1; + } + + if (!(pbm & nan->cfg->supported_bootstrap_methods)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Unsupported method=0x%x (local support=0x%x)", + pbm, nan->cfg->supported_bootstrap_methods); + return -1; + } + + if (pbm & (pbm - 1)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Multiple bootstrap methods=0x%x in request", + pbm); + return -1; + } + + if (auth) { + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Authorize request"); + + peer->bootstrap.authorized = pbm; + return 0; + } + + if (peer->bootstrap.in_progress) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: already in progress with the peer"); + return -1; + } + + if (!(nan_complement_pbm(pbm) & peer->bootstrap.supported_methods)) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Peer doesn't support method=0x%x", + pbm); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: Prepare bootstrap request"); + + nan_bootstrap_reset(nan, peer); + + peer->bootstrap.dialog_token = nan_get_next_dialog_token(nan); + peer->bootstrap.requested_pbm = pbm; + peer->bootstrap.in_progress = true; + peer->bootstrap.handle = handle; + peer->bootstrap.req_instance_id = req_instance_id; + + peer->bootstrap.npba = nan_bootstrap_build_npba(nan, peer); + if (!peer->bootstrap.npba) { + nan_bootstrap_reset(nan, peer); + return -1; + } + + ret = nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr, + peer->bootstrap.npba, handle, + req_instance_id); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Failed to transmit follow-up"); + + nan_bootstrap_reset(nan, peer); + return -1; + } + + eloop_register_timeout(0, NAN_BOOTSTRAP_RETRY_TIMEOUT_US, + nan_bootstrap_timeout, nan, peer); + return ret; +} + + +/** + * nan_bootstrap_peer_reset - Reset bootstrap state for a peer + * @nan: NAN module context from nan_init() + * @peer_nmi: Peer address for which to reset the bootstrap state + * Returns: 0 on success, -1 on failure + */ +int nan_bootstrap_peer_reset(struct nan_data *nan, const u8 *peer_nmi) +{ + struct nan_peer *peer; + + if (!nan || !nan->nan_started) + return -1; + + peer = nan_get_peer(nan, peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Reset for unknown peer"); + return -1; + } + + nan_bootstrap_reset(nan, peer); + return 0; +} + + +/** + * nan_bootstrap_get_supported_methods - Get supported bootstrap methods for a peer + * + * @nan: NAN module context from nan_init() + * @peer_nmi: Peer address + * @supported_methods: Pointer to store the supported methods bitmap + * Returns: 0 on success, -1 on failure + */ +int nan_bootstrap_get_supported_methods(struct nan_data *nan, + const u8 *peer_nmi, + u16 *supported_methods) +{ + struct nan_peer *peer; + + if (!nan || !nan->nan_started || !supported_methods) + return -1; + + peer = nan_get_peer(nan, peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Bootstrap: Get supported methods for unknown peer"); + return -1; + } + + *supported_methods = peer->bootstrap.supported_methods; + return 0; +} diff --git a/contrib/wpa/src/nan/nan_crypto.c b/contrib/wpa/src/nan/nan_crypto.c new file mode 100644 index 000000000000..9eee2a3d8198 --- /dev/null +++ b/contrib/wpa/src/nan/nan_crypto.c @@ -0,0 +1,773 @@ +/* + * Wi-Fi Aware - NAN Data link cryptography functions + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "common/ieee802_11_common.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/crypto.h" +#include "crypto/aes_wrap.h" +#include "nan_i.h" + +#define NAN_KCK_MAX_LEN 24 +#define NAN_KEK_MAX_LEN 32 +#define NAN_TK_MAX_LEN 32 + +#define NAN_PTK_LABEL "NAN Pairwise key expansion" +#define NAN_PMKID_LABEL "NAN PMK Name" + +/* NAN ciphers use only SHA-256 and SHA-384, and SHA-384 has a bigger digest */ +#define MAX_MAC_LEN SHA384_MAC_LEN + + +static size_t nan_crypto_cipher_kck_len(enum nan_cipher_suite_id cipher) +{ + switch (cipher) { + case NAN_CS_SK_CCM_128: + case NAN_CS_PK_PASN_128: + return 16; + case NAN_CS_SK_GCM_256: + case NAN_CS_PK_PASN_256: + return 24; + default: + return 0; + } +} + + +static size_t nan_crypto_cipher_kek_len(enum nan_cipher_suite_id cipher) +{ + switch (cipher) { + case NAN_CS_SK_CCM_128: + case NAN_CS_PK_PASN_128: + return 16; + case NAN_CS_SK_GCM_256: + case NAN_CS_PK_PASN_256: + return 32; + default: + return 0; + } +} + + +static size_t nan_cipher_key_len(enum nan_cipher_suite_id cipher) +{ + switch (cipher) { + case NAN_CS_SK_CCM_128: + case NAN_CS_PK_PASN_128: + return 16; + case NAN_CS_SK_GCM_256: + case NAN_CS_PK_PASN_256: + return 32; + default: + return 0; + } +} + + +static int nan_crypto_sha256(const u8 *plaintext, size_t psize, u8 *output) +{ + const u8 *addrs[1]; + size_t lens[1]; + + addrs[0] = plaintext; + lens[0] = psize; + + return sha256_vector(1, addrs, lens, output); +} + + +static int nan_crypto_sha384(const u8 *plaintext, size_t psize, u8 *output) +{ + const u8 *addrs[1]; + size_t lens[1]; + + addrs[0] = plaintext; + lens[0] = psize; + + return sha384_vector(1, addrs, lens, output); +} + + +/** + * nan_crypto_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces + * @pmk: Pairwise master key + * @iaddr: Initiator address + * @raddr: Remote address + * @inonce: Initiator nonce + * @rnonce: Remote nonce + * @ptk: Buffer for Pairwise Transient Key + * @cipher: Negotiated pairwise cipher + * returns: 0 on success, negative value of failure + */ +int nan_crypto_pmk_to_ptk(const u8 *pmk, const u8 *iaddr, const u8 *raddr, + const u8 *inonce, const u8 *rnonce, + struct nan_ptk *ptk, + enum nan_cipher_suite_id cipher) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + u8 tmp[NAN_KCK_MAX_LEN + NAN_KEK_MAX_LEN + NAN_TK_MAX_LEN]; + size_t ptk_len; + int ret; + + if (!NAN_CS_IS_VALID_NDP(cipher)) + return -1; + + if (!ptk) + return -1; + + os_memcpy(data, iaddr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, raddr, ETH_ALEN); + os_memcpy(data + 2 * ETH_ALEN, inonce, WPA_NONCE_LEN); + os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, rnonce, + WPA_NONCE_LEN); + + ptk->kck_len = nan_crypto_cipher_kck_len(cipher); + ptk->kek_len = nan_crypto_cipher_kek_len(cipher); + ptk->tk_len = nan_cipher_key_len(cipher); + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; + + if (NAN_CS_IS_128(cipher)) + ret = sha256_prf(pmk, PMK_LEN, NAN_PTK_LABEL, data, + sizeof(data), tmp, ptk_len); + else + ret = sha384_prf(pmk, PMK_LEN, NAN_PTK_LABEL, data, + sizeof(data), tmp, ptk_len); + if (ret) + goto out; + + wpa_hexdump_key(MSG_DEBUG, "NAN: PMK", pmk, PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: iaddr", iaddr, ETH_ALEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: raddr", raddr, ETH_ALEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: inonce", inonce, WPA_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: rnonce", rnonce, WPA_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: PTK", tmp, ptk_len); + + os_memcpy(ptk->kck, tmp, ptk->kck_len); + wpa_hexdump_key(MSG_DEBUG, "NAN: KCK", ptk->kck, ptk->kck_len); + + os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "NAN: KEK", ptk->kek, ptk->kek_len); + + os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "NAN: TK", ptk->tk, ptk->tk_len); + +out: + forced_memzero(data, sizeof(data)); + forced_memzero(tmp, sizeof(tmp)); + return ret; +} + + +/* + * nan_crypto_calc_pmkid - Calculate a NAN PMKID + * @pmk: Pairwise Master Key + * @iaddr: Initiator address + * @raddr: Remote address + * @serv_id: ID of the service providing the PMK + * @cipher: Negotiated pairwise cipher + * @pmkid: Buffer to hold the pmkid + * Returns: 0 on success, negative value of failure + */ +int nan_crypto_calc_pmkid(const u8 *pmk, const u8 *iaddr, const u8 *raddr, + const u8 *serv_id, + enum nan_cipher_suite_id cipher, u8 *pmkid) +{ + u8 data[sizeof(NAN_PMKID_LABEL) - 1 + 2 * ETH_ALEN + + NAN_SERVICE_ID_LEN]; + u8 digest[MAX_MAC_LEN]; + int ret; + + os_memset(data, 0, sizeof(data)); + os_memset(digest, 0, sizeof(digest)); + + if (!NAN_CS_IS_VALID_NDP(cipher)) + return -1; + + if (!serv_id || is_zero_ether_addr(serv_id)) + return -1; + + os_memcpy(data, NAN_PMKID_LABEL, sizeof(NAN_PMKID_LABEL) - 1); + os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1, iaddr, ETH_ALEN); + os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1 + ETH_ALEN, raddr, + ETH_ALEN); + os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1 + 2 * ETH_ALEN, serv_id, + NAN_SERVICE_ID_LEN); + + wpa_hexdump_key(MSG_DEBUG, "NAN: PMKID data", data, sizeof(data)); + + if (NAN_CS_IS_128(cipher)) + ret = hmac_sha256(pmk, PMK_LEN, data, sizeof(data), digest); + else + ret = hmac_sha384(pmk, PMK_LEN, data, sizeof(data), digest); + if (ret) + goto out; + + os_memcpy(pmkid, digest, PMKID_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: PMKID", pmkid, PMKID_LEN); + +out: + forced_memzero(digest, sizeof(digest)); + return ret; +} + + +/** + * nan_crypto_calc_auth_token - Calculate authentication token + * @buf: Buffer on which to calculate the authentication token + * @len: Length of &buf in octets + * @cipher: Negotiated NAN cipher + * @token: Buffer to hold the token (NAN_AUTH_TOKEN_LEN octets) + * Returns: 0 on success, and a negative error value on failure. + */ +int nan_crypto_calc_auth_token(enum nan_cipher_suite_id cipher, + const u8 *buf, size_t len, u8 *token) +{ + u8 hash[MAX_MAC_LEN]; + int ret; + + if (!NAN_CS_IS_VALID_NDP(cipher)) + return -1; + + if (NAN_CS_IS_128(cipher)) + ret = nan_crypto_sha256(buf, len, hash); + else + ret = nan_crypto_sha384(buf, len, hash); + if (ret) + return ret; + + os_memcpy(token, hash, NAN_AUTH_TOKEN_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: AUTH_TOKEN_DATA", buf, len); + wpa_hexdump_key(MSG_DEBUG, "NAN: AUTH TOKEN", token, + NAN_AUTH_TOKEN_LEN); + + forced_memzero(hash, sizeof(hash)); + + return ret; +} + + +/* + * nan_crypto_key_mic - Calculate MIC over the given buffer + * @buf: Buffer on which to calculate the MIC + * @len: Length of &buf + * @kck: Key Confirmation Key + * @kck_len: Length of &kck + * @cipher: Cipher suite identifier. + * @mic: On successful return, would hold the MIC. + * Return: 0 on success, and a negative error value on failure. + */ +int nan_crypto_key_mic(const u8 *buf, size_t len, const u8 *kck, + size_t kck_len, u8 cipher, u8 *mic) +{ + u8 digest[MAX_MAC_LEN]; + u8 mic_len; + int ret; + + os_memset(digest, 0, sizeof(digest)); + + if (!NAN_CS_IS_VALID_NDP(cipher)) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "NAN: MIC data", buf, len); + wpa_hexdump_key(MSG_DEBUG, "NAN: KCK", kck, kck_len); + + if (NAN_CS_IS_128(cipher)) { + mic_len = NAN_KEY_MIC_LEN; + ret = hmac_sha256(kck, kck_len, buf, len, digest); + } else { + mic_len = NAN_KEY_MIC_24_LEN; + ret = hmac_sha384(kck, kck_len, buf, len, digest); + } + if (ret) + return ret; + + os_memcpy(mic, digest, mic_len); + forced_memzero(digest, sizeof(digest)); + + wpa_hexdump_key(MSG_DEBUG, "NAN: MIC", mic, mic_len); + return 0; +} + + +int nan_crypto_derive_nd_pmk(const char *pwd, const u8 *service_id, + enum nan_cipher_suite_id csid, + const u8 *peer_nmi, u8 *nd_pmk) +{ + u8 salt[1 + 1 + NAN_SERVICE_ID_LEN + ETH_ALEN]; + + salt[0] = 0; + salt[1] = (u8) csid; + os_memcpy(salt + 2, service_id, NAN_SERVICE_ID_LEN); + os_memcpy(salt + 2 + NAN_SERVICE_ID_LEN, peer_nmi, ETH_ALEN); + + switch (csid) { + case NAN_CS_SK_CCM_128: + case NAN_CS_PK_PASN_128: + return pbkdf2_sha256(pwd, salt, sizeof(salt), 4096, nd_pmk, 32); + case NAN_CS_SK_GCM_256: + case NAN_CS_PK_PASN_256: + return pbkdf2_sha384(pwd, salt, sizeof(salt), 4096, nd_pmk, 32); + default: + return -1; + } +} + + +/** + * nan_crypto_derive_nira_tag - Derive NIRA tag + * @nik: NAN Identity Key + * @nik_len: Length of &nik in bytes + * @nmi_addr: NAN Management Interface address (6 bytes) + * @nira_nonce: NIRA nonce (8 bytes) + * Returns: wpabuf containing the derived tag (8 bytes) or %NULL on failure + * + * Derives a NIRA tag for cipher version 0 using HMAC-SHA-256: + * Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI Address || Nonce)) + * The caller is responsible for freeing the returned wpabuf using + * wpabuf_free(). + */ +struct wpabuf * nan_crypto_derive_nira_tag(const u8 *nik, size_t nik_len, + const u8 *nmi_addr, + const u8 *nira_nonce) +{ + u8 data[NAN_NIRA_STR_LEN + ETH_ALEN + NAN_NIRA_NONCE_LEN]; + u8 tag[SHA256_MAC_LEN]; + struct wpabuf *tag_buf; + + if (!nik || nik_len != NAN_NIK_LEN) { + wpa_printf(MSG_INFO, + "NAN: Invalid NIK for tag derivation (len=%zu)", + nik ? nik_len : 0); + return NULL; + } + + if (!nmi_addr || !nira_nonce) { + wpa_printf(MSG_INFO, + "NAN: Invalid parameters for tag derivation"); + return NULL; + } + + /* Tag = Truncate-64(HMAC-SHA-256(NIK, “NIR”, NMI || Nonce)) */ + + /* Construct data: "NIR" || NMI Address || Nonce */ + os_memcpy(data, NAN_NIRA_STR, NAN_NIRA_STR_LEN); + os_memcpy(&data[NAN_NIRA_STR_LEN], nmi_addr, ETH_ALEN); + os_memcpy(&data[NAN_NIRA_STR_LEN + ETH_ALEN], nira_nonce, + NAN_NIRA_NONCE_LEN); + + /* Compute HMAC-SHA-256(NIK, data) */ + if (hmac_sha256(nik, NAN_NIK_LEN, data, sizeof(data), tag) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to compute HMAC for tag"); + return NULL; + } + + tag_buf = wpabuf_alloc_copy(tag, NAN_NIRA_TAG_LEN); + if (!tag_buf) + wpa_printf(MSG_INFO, "NAN: Failed to allocate tag buffer"); + else + wpa_hexdump(MSG_DEBUG, "NAN: Derived NIRA tag", + wpabuf_head(tag_buf), wpabuf_len(tag_buf)); + + forced_memzero(tag, sizeof(tag)); + return tag_buf; +} + + +/** + * nan_crypto_derive_from_kdk - Derive a key from KDK using KDF-HASH-NNN + * @kdk: Key Derivation Key + * @kdk_len: Length of KDK in bytes + * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256) + * @label: Label string for the key derivation + * @initiator_nmi: Pairing Initiator NMI address (6 bytes) + * @responder_nmi: Pairing Responder NMI address (6 bytes) + * @key: Buffer for the derived key + * @key_len: number of bytes to derive + * Returns: 0 on success, -1 on failure + * + * Generic function to derive a key from KDK using: + * KEY = KDF-HASH-NNN(KDK, label, Initiator NMI || Responder NMI) + */ +static int nan_crypto_derive_from_kdk(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const char *label, + const u8 *initiator_nmi, + const u8 *responder_nmi, + u8 *key, size_t key_len) +{ + u8 data[ETH_ALEN * 2]; + int ret = 0; + + if (!kdk || !kdk_len || !label || !initiator_nmi || !responder_nmi || + !key || !key_len) { + wpa_printf(MSG_INFO, + "NAN: Invalid parameters for NPK/KEK derivation"); + return -1; + } + + /* Concatenate: Pairing Initiator NMI || Pairing Responder NMI */ + os_memcpy(data, initiator_nmi, ETH_ALEN); + os_memcpy(data + ETH_ALEN, responder_nmi, ETH_ALEN); + + if (cipher == NAN_CS_PK_PASN_128) { + ret = sha256_prf(kdk, kdk_len, label, data, sizeof(data), key, + key_len); + } else if (cipher == NAN_CS_PK_PASN_256) { + ret = sha384_prf(kdk, kdk_len, label, data, sizeof(data), key, + key_len); + } else { + wpa_printf(MSG_INFO, + "NAN: Unsupported cipher suite for key derivation: %d", + cipher); + return -1; + } + + if (ret) { + wpa_printf(MSG_INFO, + "NAN: NPK/KEK derivation failed (ret=%d)", ret); + return ret; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: KDK", kdk, kdk_len); + wpa_printf(MSG_DEBUG, "NAN: Label: %s", label); + wpa_printf(MSG_DEBUG, "NAN: Initiator NMI " MACSTR, + MAC2STR(initiator_nmi)); + wpa_printf(MSG_DEBUG, "NAN: Responder NMI " MACSTR, + MAC2STR(responder_nmi)); + wpa_hexdump_key(MSG_DEBUG, "NAN: Derived key", key, key_len); + + return 0; +} + + +/** + * nan_crypto_derive_npk - Derive NPK from NM-KDK for opportunistic pairing + * @kdk: NM-KDK (NAN Master Key Derivation Key) + * @kdk_len: Length of KDK in bytes + * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256) + * @initiator_nmi: Pairing Initiator NMI address (6 bytes) + * @responder_nmi: Pairing Responder NMI address (6 bytes) + * @buf: Buffer for the derived NPK + * @buf_len: Length of the buffer (must be 32 bytes) + * Returns: 0 on success, -1 on failure + * + * NPK = KDF-HASH-256(NM-KDK, "NAN Opportunistic NPK Derivation", + * Pairing Initiator NMI || Pairing Responder NMI) + * + * Note: It is unclear whether KDF-HASH-256 means that SHA-256 must be used as + * the hash algorithm, or the hash algorithm is determined by the cipher suite. + * Usually, NCS-PK-PASN-128 cipher comes with SHA-256 and NCS-PK-PASN-256 with + * SHA-384 as defined in Wi-Fi Aware Specification v4.0, section 7.1.2. But for + * opportunistic pairing, section 7.6.4.3 specifies KDF-HASH-256 only for NPK + * derivation. Does this mean that SHA-256 must be used? In IEEE 802.11-2024, + * 12.13.8, where KDF-HASH-NNN is defined, NNN is the number of bits to derive, + * not the hash function. Therefore, we follow the latter interpretation and use + * the hash function corresponding to the cipher suite. + */ +int nan_crypto_derive_npk(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, const u8 *responder_nmi, + u8 *buf, size_t buf_len) +{ + const char *label = "NAN Opportunistic NPK Derivation"; + + wpa_printf(MSG_DEBUG, "NAN: Deriving NPK from NM-KDK"); + + if (buf_len < NAN_NPK_LEN) { + wpa_printf(MSG_INFO, "NAN: NPK buffer too small: %zu bytes", + buf_len); + return -1; + } + + return nan_crypto_derive_from_kdk(kdk, kdk_len, cipher, label, + initiator_nmi, responder_nmi, + buf, buf_len); +} + + +/** + * nan_crypto_derive_kek - Derive KEK from NM-KDK + * @kdk: NM-KDK (NAN Master Key Derivation Key) + * @kdk_len: Length of KDK in bytes + * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256) + * @initiator_nmi: Pairing Initiator NMI address (6 bytes) + * @responder_nmi: Pairing Responder NMI address (6 bytes) + * @ptk: Buffer for the derived KEK + * Returns: 0 on success, -1 on failure + * + * NM-KEK = KDF-HASH-MMM(NM-KDK, "NAN Management KEK Derivation", + * Pairing Initiator NMI || Pairing Responder NMI) + */ +int nan_crypto_derive_kek(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, const u8 *responder_nmi, + struct wpa_ptk *ptk) +{ + const char *label = "NAN Management KEK Derivation"; + + wpa_printf(MSG_DEBUG, "NAN: Deriving KEK from NM-KDK"); + + if (cipher != NAN_CS_PK_PASN_128 && + cipher != NAN_CS_PK_PASN_256) { + wpa_printf(MSG_INFO, + "NAN: Unsupported cipher suite for KEK derivation: %d", + cipher); + return -1; + } + + ptk->kek_len = nan_crypto_cipher_kek_len(cipher); + + return nan_crypto_derive_from_kdk(kdk, kdk_len, cipher, label, + initiator_nmi, responder_nmi, + ptk->kek, ptk->kek_len); +} + + +/** + * nan_crypto_derive_nd_pmk_from_kdk - Derive ND-PMK from NM-KDK + * @kdk: NM-KDK (NAN Master Key Derivation Key) + * @kdk_len: Length of KDK in bytes + * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256) + * @initiator_nmi: Pairing Initiator NMI address (6 bytes) + * @responder_nmi: Pairing Responder NMI address (6 bytes) + * @nd_pmk: Buffer for the derived ND-PMK (must be 32 bytes) + * Returns: 0 on success, -1 on failure + * + * ND-PMK = KDF-HASH-256(NM-KDK, "NDP PMK Derivation", + * Pairing Initiator NMI || Pairing Responder NMI) + */ +int nan_crypto_derive_nd_pmk_from_kdk(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, + const u8 *responder_nmi, u8 *nd_pmk) +{ + const char *label = "NDP PMK Derivation"; + + wpa_printf(MSG_DEBUG, "NAN: Deriving ND-PMK from NM-KDK"); + + /* ND-PMK always uses SHA-256, resulting in 32 bytes */ + return nan_crypto_derive_from_kdk(kdk, kdk_len, cipher, label, + initiator_nmi, responder_nmi, nd_pmk, + PMK_LEN); +} + + +/** + * nan_crypto_encrypt_key - Encrypt key data using AES Key Wrap (RFC 3394) + * @key_data: Key data to be encrypted + * @kek: Key Encryption Key (KEK) + * @kek_len: Length of KEK in octets + * Returns: Encrypted key data in a newly allocated wpabuf, or NULL on failure. + * + * This function encrypts the provided key data using AES Key Wrap algorithm + * as defined in RFC 3394. The input data is padded to 8-byte alignment before + * encryption. The padding scheme uses 0xdd as the first padding byte followed + * by zeros. + * + * The caller is responsible for freeing the returned wpabuf. + */ +struct wpabuf * nan_crypto_encrypt_key_data(const struct wpabuf *key_data, + const u8 *kek, size_t kek_len) +{ + size_t key_data_len; + size_t pad; + size_t padded_len; + u8 *padded_key_data; + struct wpabuf *encrypted_key_data; + + if (!key_data || !kek || !kek_len) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Invalid parameters for key data encryption"); + return NULL; + } + + key_data_len = wpabuf_len(key_data); + if (!key_data_len) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Key data is empty for encryption"); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: Plain key data", wpabuf_head(key_data), + key_data_len); + + /* Calculate padding to align to 8 bytes (AES block size) */ + pad = key_data_len % 8; + if (pad) + pad = 8 - pad; + + padded_len = key_data_len + pad; + padded_key_data = os_zalloc(padded_len); + if (!padded_key_data) + return NULL; + + /* Copy key data and apply padding (0xdd followed by zeros) */ + os_memcpy(padded_key_data, wpabuf_head(key_data), key_data_len); + if (pad) + padded_key_data[key_data_len] = 0xdd; + + /* Allocate buffer for encrypted data (input length + 8 bytes for IV) */ + encrypted_key_data = wpabuf_alloc(padded_len + 8); + if (!encrypted_key_data) + goto fail; + + /* Encrypt the padded data using AES Key Wrap */ + if (aes_wrap(kek, kek_len, padded_len / 8, padded_key_data, + wpabuf_put(encrypted_key_data, padded_len + 8))) { + wpa_printf(MSG_INFO, "NAN: Pairing: AES wrap failed"); + wpabuf_free(encrypted_key_data); + encrypted_key_data = NULL; + } else { + wpa_hexdump(MSG_DEBUG, "NAN: Encrypted key data", + wpabuf_head(encrypted_key_data), + wpabuf_len(encrypted_key_data)); + } + +fail: + bin_clear_free(padded_key_data, padded_len); + return encrypted_key_data; +} + + +/** + * nan_crypto_decrypt_key_data - Decrypt NAN key data using AES-UNWRAP + * @kek: Key Encryption Key + * @kek_len: KEK length in bytes + * @encrypted_data: Encrypted key data to decrypt + * @encrypted_len: Length of encrypted data in bytes + * Returns: wpabuf containing decrypted data or %NULL on failure + * + * This function decrypts NAN key data that was encrypted using AES-WRAP. + * The encrypted data must be at least 16 bytes and a multiple of 8 bytes + * (AES-WRAP requirement). The caller is responsible for freeing the returned + * wpabuf using wpabuf_free(). + */ +struct wpabuf * nan_crypto_decrypt_key_data(const u8 *kek, size_t kek_len, + const u8 *encrypted_data, + size_t encrypted_len) +{ + struct wpabuf *decrypted; + size_t plain_len; + u8 *buf; + + if (!encrypted_data || !encrypted_len) { + wpa_printf(MSG_INFO, "NAN: Invalid encrypted key data"); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: Encrypted key data", + encrypted_data, encrypted_len); + + if (!kek || !kek_len) { + wpa_printf(MSG_INFO, + "NAN: No KEK available for key data decryption"); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: KEK for decryption", kek, kek_len); + + /* AES-WRAP adds 8 bytes overhead */ + if (encrypted_len < 16 || encrypted_len % 8 != 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid encrypted key data length %zu", + encrypted_len); + return NULL; + } + + plain_len = encrypted_len - 8; + decrypted = wpabuf_alloc(plain_len); + if (!decrypted) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate decryption buffer"); + return NULL; + } + + buf = wpabuf_put(decrypted, plain_len); + if (aes_unwrap(kek, kek_len, plain_len / 8, encrypted_data, buf)) { + wpa_printf(MSG_INFO, + "NAN: AES unwrap failed - could not decrypt key data"); + wpabuf_free(decrypted); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: Decrypted key data", + wpabuf_head(decrypted), wpabuf_len(decrypted)); + + return decrypted; +} + + +/** + * nan_crypto_clear_pmkid_list - Clear and free all entries in a PMKID list + * @pmkid_list: List of PMKIDs to clear + * + * This function removes and frees all PMKID entries from the provided list. + */ +void nan_crypto_clear_pmkid_list(struct dl_list *pmkid_list) +{ + struct nan_de_pmkid *p, *n; + + dl_list_for_each_safe(p, n, pmkid_list, struct nan_de_pmkid, list) { + dl_list_del(&p->list); + os_free(p); + } +} + + +/** + * nan_crypto_pmkid_list - Generate PMKIDs for multiple cipher suites + * @pmkid_list: List to which the generated PMKIDs are appended + * @raddr: Responder MAC address + * @srv_id: Service ID (6 bytes) + * @cipher_suites: Array of cipher suite identifiers (int_array) + * @pmk: PMK for which the PMKIDs are generated + * Returns: 0 on success, -1 on failure + * + * This function generates a PMKID for each cipher suite in the provided array + * and adds them to the pmkid_list. + */ +int nan_crypto_pmkid_list(struct dl_list *pmkid_list, const u8 *raddr, + const u8 *srv_id, const int *cipher_suites, + const u8 *pmk) +{ + size_t cs_num = int_array_len(cipher_suites); + size_t i; + + if (!cs_num || !pmk) + return 0; + + for (i = 0; i < cs_num; i++) { + struct nan_de_pmkid *p; + int ret; + static const u8 iaddr[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + enum nan_cipher_suite_id csid = + (enum nan_cipher_suite_id) cipher_suites[i]; + + p = os_zalloc(sizeof(*p)); + if (!p) + return -1; + + ret = nan_crypto_calc_pmkid(pmk, iaddr, raddr, srv_id, csid, + p->pmkid); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to derive PMKID for cipher suite %d", + cipher_suites[i]); + nan_crypto_clear_pmkid_list(pmkid_list); + return ret; + } + + dl_list_add(pmkid_list, &p->list); + } + + return 0; +} diff --git a/contrib/wpa/src/nan/nan_i.h b/contrib/wpa/src/nan/nan_i.h new file mode 100644 index 000000000000..5a9f2b5e9487 --- /dev/null +++ b/contrib/wpa/src/nan/nan_i.h @@ -0,0 +1,867 @@ +/* + * Wi-Fi Aware - Internal definitions for NAN module + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_I_H +#define NAN_I_H + +#include "list.h" +#include "common/ieee802_11_defs.h" +#include "common/nan_defs.h" +#include "common/wpa_common.h" +#include "nan.h" + +struct bitfield; +struct nan_config; + +#define NAN_INVALID_MAP_ID 0xff + +#define NAN_KCK_MAX_LEN 24 +#define NAN_KEK_MAX_LEN 32 +#define NAN_TK_MAX_LEN 32 +#define NAN_NPK_LEN 32 + +#define NAN_ELEMENT_MAX_SIZE 1024 + +/** + * struct nan_ptk - NAN Pairwise Transient Key + * @kck: Key Confirmation Key + * @kek: Key Encryption Key + * @tk: Transient Key + * @kck_len: Length of &kck in octets + * @kek_len: Length of &kek in octets + * @tk_len: Length of &tk in octets + */ +struct nan_ptk { + u8 kck[NAN_KCK_MAX_LEN]; + u8 kek[NAN_KEK_MAX_LEN]; + u8 tk[NAN_TK_MAX_LEN]; + + size_t kck_len; + size_t kek_len; + size_t tk_len; +}; + +/** + * struct nan_ndp_sec - NAN ndp security state + * @present: Whether NDP setup exchange includes security + * @valid: Whether the security configuration is valid + * @replaycnt_ok: Whether replay count ok + * @replaycnt: Current replay count + * @i_nonce: Initiator nonce + * @i_capab: Initiator capabilities + * @i_csid: Initiator cipher suite ID + * @i_instance_id: Initiator publish instance ID + * @i_pmkid: Initiator PMKID + * @r_nonce: Responder nonce + * @r_capab: Responder capabilities + * @r_csid: Responder cipher suite ID + * @r_instance_id: Responder instance ID + * @r_pmkid: Responder PMKID + * @auth_token: Authentication token + * @pmk: PMK used for the secure NDP establishment + * @ptk: Derived PTK + * @local_gtk: Group Temporal Key information of the local NDI + * @peer_gtk: Group Temporal Key information of the peer NDI + * @peer_gtk_rsc: Receive sequence counter of the peer NDI GTK + */ +struct nan_ndp_sec { + bool present; + bool valid; + + bool replaycnt_ok; + u8 replaycnt[8]; + + /* Initiator data */ + u8 i_nonce[WPA_NONCE_LEN]; + u8 i_capab; + u8 i_csid; + u8 i_instance_id; + u8 i_pmkid[PMKID_LEN]; + + /* Responder data */ + u8 r_nonce[WPA_NONCE_LEN]; + u8 r_capab; + u8 r_csid; + u8 r_instance_id; + u8 r_pmkid[PMKID_LEN]; + + u8 auth_token[NAN_AUTH_TOKEN_LEN]; + u8 pmk[PMK_LEN]; + + struct nan_ptk ptk; + + struct nan_gtk local_gtk; + struct nan_gtk peer_gtk; + u8 peer_gtk_rsc[WPA_KEY_RSC_LEN]; +}; + +/* + * enum nan_ndp_state - State of NDP establishment + * @NAN_NDP_STATE_NONE: No NDP establishment in progress + * @NAN_NDP_STATE_START: Starting NDP establishment + * @NAN_NDP_STATE_REQ_SENT: NDP request was sent + * @NAN_NDP_STATE_REQ_RECV: NDP response was received and processed + * @NAN_NDP_STATE_RES_SENT: NDP response was sent and NDP is not accepted yet + * @NAN_NDP_STATE_RES_RECV: NDP response was received and NDP was not accepted + * yet (security is negotiated or confirmation is required) + * @NAN_NDP_STATE_CON_SENT: NDP confirm was sent and NDP was not done yet, as + * security is negotiated + * @NAN_NDP_STATE_CON_RECV: NDP confirm received and NDP was not done yet, as + * security is negotiated + * @NAN_NDP_STATE_DONE: NDP establishment is done (either success or reject). + * In this state the NAN module handles actions such as notification to the + * encapsulating logic, etc. Once processing is done the NDP should either + * be cleared (rejected) or moved to the list of NDPs associated with the + * peer. + */ +enum nan_ndp_state { + NAN_NDP_STATE_NONE, + NAN_NDP_STATE_START, + NAN_NDP_STATE_REQ_SENT, + NAN_NDP_STATE_REQ_RECV, + NAN_NDP_STATE_RES_SENT, + NAN_NDP_STATE_RES_RECV, + NAN_NDP_STATE_CON_SENT, + NAN_NDP_STATE_CON_RECV, + NAN_NDP_STATE_DONE, +}; + +/* + * struct nan_ndp - NDP information + * + * Used to maintain the NDP as an object in a peer's list of NDPs. + * + * @list: Used for linking in the NDPs list + * @peer: Pointer to the peer data structure + * @initiator: True iff the local device is the initiator + * @ndp_id: NDP ID + * @init_ndi: Initiator NDI + * @resp_ndi: Responder NDI. Might not always be set (as this depends on the + * state of NDP establishment and the status). + * @qos: QoS requirements for this NDP + * @gtk_id: GTK key ID used for this NDP; 0 if GTK is not used + */ +struct nan_ndp { + /* for nan_peer ndps list */ + struct dl_list list; + struct nan_peer *peer; + bool initiator; + u8 ndp_id; + u8 init_ndi[ETH_ALEN]; + u8 resp_ndi[ETH_ALEN]; + + struct nan_qos qos; + u8 gtk_id; +}; + +/* + * struct nan_ndp_setup - Holds the state of the NDP setup + * @ndp: NDP information + * @state: Current state + * @status: Current status + * @dialog_token: Setup dialog token + * @publisher_inst_id: Publish function instance ID + * @conf_req: True iff the NDP exchange requires confirm message + * @reason: Reject reason. Only valid when status is rejected. + * @ssi: Service specific information + * @ssi_len: Service specific information length + * @service_id: Service ID of the service used for NDP setup + * @sec: NDP security data + * @local_interface_id_valid: Indicates whether the &local_interface_id + * field is valid. + * @local_interface_id: The local interface identifier to be used for the NDP + * @peer_interface_id_valid: Indicates whether the &peer_interface_id + * field is valid. + * @peer_interface_id: The peer interface identifier to be used for the NDP + */ +struct nan_ndp_setup { + struct nan_ndp *ndp; + enum nan_ndp_state state; + enum nan_ndp_status status; + u8 dialog_token; + u8 publish_inst_id; + bool conf_req; + enum nan_reason reason; + u8 *ssi; + u16 ssi_len; + + u8 service_id[NAN_SERVICE_ID_LEN]; + struct nan_ndp_sec sec; + + bool local_interface_id_valid; + u8 local_interface_id[NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN]; + bool peer_interface_id_valid; + u8 peer_interface_id[NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN]; +}; + +/** + * struct nan_band_chan - NAN channel/band entry + * + * @band_id: Band ID as specified by enum nan_band_entry + * @chan: Channel entry as specified by &struct nan_chan_entry + */ +struct nan_band_chan { + union { + u8 band_id; + struct nan_chan_entry chan; + } u; +}; + +/** + * enum nan_band_chan_type - NAN band or channel + * + * @NAN_TYPE_BAND: The entry is a band entry + * @NAN_TYPE_CHANNEL: The entry is a channel entry + */ +enum nan_band_chan_type { + NAN_TYPE_BAND, + NAN_TYPE_CHANNEL, +}; + +/* Default availability entry parameter values */ +#define NAN_AVAIL_ENTRY_DEF_UTIL NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN +#define NAN_AVAIL_ENTRY_DEF_NSS 2 +#define NAN_AVAIL_ENTRY_DEF_PREF 3 + +/** + * struct nan_avail_entry - NAN availability entry + * + * @list: Used for linking in the availability entries list + * @map_id: Map ID of the availability attribute that this entry belongs to + * @type: Availability type. One of NAN_AVAIL_ENTRY_CTRL_TYPE_*. + * @preference: Preference of being available in the NAN slots specified by + * the associated time bitmap. The preference is higher when the value is + * set larger. Valid values are 0 - 3. + * @utilization: Indicating proportion within the NAN slots specified by the + * associated time bitmap that are already utilized for other purposes, + * quantized to 20%. Valid values are 0 - 5. + * @rx_nss: Maximum number of special streams the NAN device can receive during + * the NAN slots specified by the associated time bitmap + * @tbm: Time bitmap specifying the NAN slots in which the device will be + * available for NAN operations + * @band_chan_type: Type of entries in &band_chan array, as specified by + * enum nan_band_chan_type + * @n_band_chan: Number of entries in &band_chan array + * @band_chan: Array of bands/channels on which the NAN device will be + * available + */ +struct nan_avail_entry { + struct dl_list list; + u8 map_id; + u8 type; + u8 preference; + u8 utilization; + u8 rx_nss; + struct nan_time_bitmap tbm; + enum nan_band_chan_type band_chan_type; + u8 n_band_chan; + struct nan_band_chan *band_chan; +}; + +/** + * struct nan_dev_capa_entry - NAN Device Capability entry + * + * @list: Used for linking in the device capabilities list + * (in struct nan_peer_info::dev_capa) + * @map_id: Map ID of the device capabilities + * @capa: Device capabilities as specified by &struct nan_device_capabilities + */ +struct nan_dev_capa_entry { + struct dl_list list; + u8 map_id; + struct nan_device_capabilities capa; +}; + +/** + * struct nan_elem_container_entry - NAN element container entry + * + * @list: Used for linking in the element container entries list + * (in struct nan_peer_info::element_container) + * @map_id: Map ID of the element container + * @len: Length of data + * @data: Pointer to the data + */ +struct nan_elem_container_entry { + struct dl_list list; + u8 map_id; + u16 len; + u8 data[]; +}; + +/** + * struct nan_ulw_entry - NAN Unaligned Schedule attribute entry + * @list: Used for linking in the ULW entries list in struct nan_peer_info::ulw + * @len: Length of the ULW attribute payload + * @data: Pointer to the ULW attribute payload + */ +struct nan_ulw_entry { + struct dl_list list; + u16 len; + u8 data[]; +}; + +/** + * struct nan_peer_sec_info_entry - NAN peer security information entry + * + * Maintains the latest security information for an NDI pair. + * + * @list: Used for linking in the peer security info list + * (struct nan_peer_info::sec) + * @peer_ndi: Peer NDI address + * @local_ndi: Local NDI address + * @csid: Cipher Suite ID used for the secure NAN communication + * @pmk: PMK shared with the peer + * @pmkid: PMKID shared with the peer + * @ptk: PTK shared with the peer + * @pairing_akmp: AKMP used for the pairing (see See WPA_KEY_MGMT_*) or + * zero if PASN pairing was not used for NDP establishment + */ +struct nan_peer_sec_info_entry { + struct dl_list list; + + u8 peer_ndi[ETH_ALEN]; + u8 local_ndi[ETH_ALEN]; + + enum nan_cipher_suite_id csid; + u8 pmk[PMK_LEN]; + u8 pmkid[PMKID_LEN]; + struct nan_ptk ptk; + int pairing_akmp; +}; + +/** + * struct nan_peer_info - NAN peer information + * + * @last_seen: Timestamp of the last update of the peer info + * @seq_id: Sequence id of the last availability update + * @avail_entries: List of availability entries of the peer + * @ulw: List of Unaligned Schedule attribute payloads of the peer + * (struct nan_ulw_entry::list entries) + * @dev_capa: List of device capabilities of the peer + * (struct nan_dev_capa_entry::list entries) + * @element_container: List of element container entries of the peer + * (struct nan_elem_container_entry::list entries) + * @sec: List of security information entries of the peer + * (struct nan_peer_sec_info_entry::list entries) + */ +struct nan_peer_info { + struct os_reltime last_seen; + u8 seq_id; + struct dl_list avail_entries; + struct dl_list ulw; + struct dl_list dev_capa; + struct dl_list element_container; + struct dl_list sec; +}; + +/** + * enum nan_ndl_state - State of NDL establishment + * + * @NAN_NDL_STATE_NONE: No NDL with the peer + * @NAN_NDL_STATE_START: NDL setup initiated by local device + * @NAN_NDL_STATE_REQ_SENT: Sent NDL request + * @NAN_NDL_STATE_REQ_RECV: Got NDL request + * @NAN_NDL_STATE_RES_SENT: Sent NDL response + * @NAN_NDL_STATE_RES_RECV: Got NDL response + * @NAN_NDL_STATE_CON_SENT: Sent NDL confirm + * @NAN_NDL_STATE_CON_RECV: Got NDL confirm + * @NAN_NDL_STATE_DONE: NDL establishment is done (either success or reject). + */ +enum nan_ndl_state { + NAN_NDL_STATE_NONE, + NAN_NDL_STATE_START, + NAN_NDL_STATE_REQ_SENT, + NAN_NDL_STATE_REQ_RECV, + NAN_NDL_STATE_RES_SENT, + NAN_NDL_STATE_RES_RECV, + NAN_NDL_STATE_CON_SENT, + NAN_NDL_STATE_CON_RECV, + NAN_NDL_STATE_DONE, +}; + +/** + * enum nan_ndl_setup_reason - NAN NDL setup reason + * @NAN_NDL_SETUP_REASON_NONE: none + * @NAN_NDL_SETUP_REASON_NDP: NDL setup request for NDP operation + */ +enum nan_ndl_setup_reason { + NAN_NDL_SETUP_REASON_NONE, + NAN_NDL_SETUP_REASON_NDP, +}; + +/** + * struct nan_ndl - NAN NDL data + * + * @state: Current state + * @status: Current status + * @send_naf_on_error: When set, indicates that in case that the NDL processing + * returned an error, a NAF still needs to be sent to the peer, i.e., the + * error cannot be silently ignored. + * @reason: In case of status == NAN_NDL_STATUS_REJECTED, indicates the reason. + * @dialog_token: The dialog token for the current NDL negotiation. + * @max_idle_period: Indicate a period of time in units of 1024 TU during which + * the peer device can refrain from transmitting over the NDL without + * being terminated. + * @setup_reason: The reason for the NDL setup + * @ndc_id: NDC identifier + * @peer_qos: Peer QoS requirements + * @local_qos: Local QoS requirements (for the current NDP establishment) + * @ndc_sched: The NDC schedule entries. See &struct nan_sched_entry + * @ndc_sched_len: The length in octets of ndc_sched. + * @immut_sched: The immutable schedule entries. See &enum nan_sched_entry + * @immut_sched_len: The length in octets of immut_sched. + */ +struct nan_ndl { + enum nan_ndl_state state; + enum nan_ndl_status status; + u8 send_naf_on_error; + enum nan_reason reason; + + u8 dialog_token; + u16 max_idle_period; + enum nan_ndl_setup_reason setup_reason; + + u8 ndc_id[ETH_ALEN]; + + struct nan_qos peer_qos, local_qos; + + u8 *ndc_sched; + u16 ndc_sched_len; + + u8 *immut_sched; + u16 immut_sched_len; +}; + +/** + * struct nan_bootstrap - NAN bootstrap information + * @supported_methods: Bitmap of supported bootstrap methods. See + * &enum nan_pairing_bootstrapping_method. + * @initiator: Whether this device is the initiator + * @requested_pbm: Bitmap of requested bootstrap methods. See + * &enum nan_pairing_bootstrapping_method. + * @dialog_token: Dialog token of the bootstrap exchange + * @status: Status of the bootstrap exchange. See &enum nan_pba_status. + * @reason_code: Reason code for the bootstrap exchange. See &enum nan_reason. + * @comeback_required: Whether the peer requested a comeback + * @comeback_after: Time after which the comeback is requested + * @cookie: Pointer to the cookie received from the peer + * @cookie_len: Length of the cookie + * @authorized: Authorized bootstrap method. See &enum + * nan_pairing_bootstrapping_method. + * @in_progress: Whether a bootstrap exchange is in progress + * @handle: Follow-up context handle for the ongoing bootstrap request + * @req_instance_id: Instance ID of the bootstrap request + * @npba: The NPBA from the last successful bootstrap + */ +struct nan_bootstrap { + u16 supported_methods; + bool initiator; + u16 requested_pbm; + u8 dialog_token; + u8 status; + u8 reason_code; + + bool comeback_required; + u16 comeback_after; + u8 *cookie; + u8 cookie_len; + + u16 authorized; + bool in_progress; + + int handle; + u8 req_instance_id; + + struct wpabuf *npba; +}; + +/** + * enum nan_pasn_auth_mode - NAN pairing authentication modes + * @NAN_PASN_AUTH_MODE_PASN: Unauthenticated PASN + * @NAN_PASN_AUTH_MODE_SAE: PASN authentication with SAE tunneling + * @NAN_PASN_AUTH_MODE_PMK: PASN authentication with PMK caching + */ +enum nan_pasn_auth_mode { + NAN_PASN_AUTH_MODE_PASN = 0, + NAN_PASN_AUTH_MODE_SAE = 1, + NAN_PASN_AUTH_MODE_PMK = 2, +}; + +/** + * enum nan_pairing_role - NAN pairing role types + * @NAN_PAIRING_ROLE_IDLE: No active pairing role + * @NAN_PAIRING_ROLE_INITIATOR: Device acting as pairing initiator + * @NAN_PAIRING_ROLE_RESPONDER: Device acting as pairing responder + */ +enum nan_pairing_role { + NAN_PAIRING_ROLE_IDLE, + NAN_PAIRING_ROLE_INITIATOR, + NAN_PAIRING_ROLE_RESPONDER, +}; + + +/* Current pairing uses pairing verification */ +#define NAN_PAIRING_FLAG_NPK_VERIFICATION BIT(0) +/* Peer is paired */ +#define NAN_PAIRING_FLAG_PAIRED BIT(1) + +/** + * struct nan_pairing_peer_data - NAN pairing peer information + * @pairing_cfg: NAN pairing configuration parameters + * @self_pairing_role: Role of this device in the pairing process + * @pasn: Pointer to PASN data + * @handle: Handle of the local service instance + * @peer_instance_id: Instance ID of the peer service + * @nonce_tag_valid: Indicates if the nonce and tag fields are valid + * @nonce: Nonce from peer's NIRA + * @tag: Tag from peer's NIRA + * @flags: Bitmap of pairing flags. See NAN_PAIRING_FLAG_* + * @pending_auth1: Pending PASN Authentication frame 1 to be processed + * @pairing_csid: Cipher suite ID used for the pairing + * @pairing_akmp: AKMP used for the pairing. See WPA_KEY_MGMT_*. + */ +struct nan_pairing_peer_data { + struct nan_pairing_cfg pairing_cfg; + enum nan_pairing_role self_pairing_role; + struct pasn_data *pasn; + int handle; + int peer_instance_id; + bool nonce_tag_valid; + u8 nonce[NAN_NIRA_NONCE_LEN]; + u8 tag[NAN_NIRA_TAG_LEN]; + u32 flags; + struct wpabuf *pending_auth1; + enum nan_cipher_suite_id pairing_csid; + int pairing_akmp; +}; + +/** + * struct nan_peer - Represents a known NAN peer + * @list: List node for linking peers + * @nmi_addr: NMI of the peer + * @configured: Indicates if the peer has been configured to the device + * @last_seen: Timestamp of the last time this peer was seen + * @info: Information about the peer + * @ndps: List of NDPs associated with this peer + * @ndp_setup: Used to hold an NDP object while NDP establishment is in + * progress + * @ndl: NDL data associated with this peer + * @bootstrap: Bootstrap information of the peer + * @pairing: Pairing data associated with this peer + * @igtk_id: IGTK key ID used with this peer. Zero if IGTK is not used. + * @bigtk_id: BIGTK key ID used with this peer. Zero if BIGTK is not used. + */ +struct nan_peer { + struct dl_list list; + u8 nmi_addr[ETH_ALEN]; + bool configured; + struct os_reltime last_seen; + struct nan_peer_info info; + + struct dl_list ndps; + + struct nan_ndp_setup ndp_setup; + + struct nan_ndl *ndl; + + struct nan_bootstrap bootstrap; + + struct nan_pairing_peer_data pairing; + + u8 igtk_id; + u8 bigtk_id; +}; + +/** + * struct nan_data - Internal data structure for NAN + * @cfg: Pointer to the NAN configuration structure + * @nan_started: Flag indicating if NAN has been started + * @sched_update_pending: Local schedule update is pending driver confirmation + * @peer_list: List of known peers + * @ndp_id_counter: NDP identifier counter. Incremented for each NDP request, + * and is used to set ndp_id in &struct nan_ndp. + * @next_dialog_token: Dialog token for NDP and NDL negotiations. Incremented + * for each NDP and NDL request. + * @sched: The local schedule + * @cluster_id: Current cluster ID + * @nira_nonce: Nonce for NAN Identity Resolution attribute (NIRA) + * @nira_tag: Tag for NAN Identity Resolution attribute (NIRA) + * @initiator_pmksa: PMKSA cache for PASN-PMK authentication as an initiator + * @responder_pmksa: PMKSA cache for PASN-PMK authentication as a responder + * @igtk: IGTK for NAN secure NDP + * @igtk_id: Key ID of the IGTK + * @bigtk: BIGTK for NAN secure NDP + * @bigtk_id: Key ID of the BIGTK + */ +struct nan_data { + struct nan_config *cfg; + u8 nan_started:1; + u8 sched_update_pending:1; + struct dl_list peer_list; + + u8 ndp_id_counter; + u8 next_dialog_token; + struct nan_schedule sched; + + u8 cluster_id[ETH_ALEN]; + + u8 nira_nonce[NAN_NIRA_NONCE_LEN]; + u8 nira_tag[NAN_NIRA_TAG_LEN]; + + struct rsn_pmksa_cache *initiator_pmksa; + struct rsn_pmksa_cache *responder_pmksa; + + struct wpa_igtk igtk; + u8 igtk_id; + + struct wpa_bigtk bigtk; + u8 bigtk_id; +}; + +struct nan_attrs_entry { + struct dl_list list; + const u8 *ptr; + u16 len; +}; + +struct nan_attrs { + struct dl_list serv_desc_ext; + struct dl_list avail; + struct dl_list ndc; + struct dl_list ulw; + struct dl_list dev_capa; + struct dl_list element_container; + + const u8 *ndp; + const u8 *ndl; + const u8 *ndl_qos; + const u8 *cipher_suite_info; + const u8 *sec_ctxt_info; + const u8 *shared_key_desc; + const u8 *dev_capa_ext; + const u8 *npba; + const u8 *nira; + const u8 *ndpe; + + u16 ndp_len; + u16 ndl_len; + u16 ndl_qos_len; + u16 cipher_suite_info_len; + u16 sec_ctxt_info_len; + u16 shared_key_desc_len; + u16 dev_capa_ext_len; + u16 npba_len; + u16 nira_len; + u16 ndpe_len; +}; + +struct nan_msg { + u8 oui_type; + u8 oui_subtype; + struct nan_attrs attrs; + + /* The full frame is required for the NDP security flows, that compute + * the NDP authentication token over the entire frame body. */ + const struct ieee80211_mgmt *mgmt; + size_t len; +}; + + +/** + * nan_get_next_dialog_token - Allocate the next nonzero dialog token + * + * Wi-Fi Aware Specification v4.0, Tables 82, 86, 105: Dialog Token must be + * set to a nonzero value. + */ +static inline u8 nan_get_next_dialog_token(struct nan_data *nan) +{ + if (++nan->next_dialog_token == 0) + nan->next_dialog_token++; + return nan->next_dialog_token; +} + + +/** + * nan_get_next_ndp_id - Allocate next nonzero NDP identifier + * + * Wi-Fi Aware Specification v4.0, Table 82: NDP ID range is 1-255, + * value zero is reserved. + */ +static inline u8 nan_get_next_ndp_id(struct nan_data *nan) +{ + if (++nan->ndp_id_counter == 0) + nan->ndp_id_counter++; + return nan->ndp_id_counter; +} + + +struct nan_peer * nan_get_peer(struct nan_data *nan, const u8 *addr); +bool nan_is_naf(const struct ieee80211_mgmt *mgmt, size_t len); +int nan_parse_attrs(struct nan_data *nan, const u8 *data, size_t len, + struct nan_attrs *attrs); +int nan_parse_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt, + size_t len, struct nan_msg *msg); +void nan_attrs_clear(struct nan_data *nan, struct nan_attrs *attrs); + +int nan_ndp_setup_req(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_params *params); +int nan_ndp_setup_resp(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_params *params); +int nan_ndp_handle_ndp_attr(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg); +int nan_ndp_add_ndp_attr(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf); +void nan_ndp_setup_reset(struct nan_data *nan, struct nan_peer *peer); +void nan_ndp_setup_failure(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, bool reset_state); +int nan_ndp_naf_sent(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype); +int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer, + const u8 *attrs_data, size_t attrs_len); +int nan_ndp_term_req(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_id *ndp_id); +int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer, + const struct nan_ndp_params *params, u8 dialog_token); +void nan_ndl_setup_failure(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, bool reset_state); +void nan_ndl_reset(struct nan_data *nan, struct nan_peer *peer); +int nan_ndl_handle_ndl_attr(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg); +int nan_ndl_add_ndl_attr(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf); +int nan_ndl_add_ndc_attr(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf); +int nan_ndl_add_qos_attr(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf); +int nan_chan_to_chan_idx_map(struct nan_data *nan, + u8 op_class, u8 channel, u16 *chan_idx_map); +int nan_ndl_naf_sent(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype); +int nan_ndl_add_avail_attrs(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf); +void nan_ndl_add_elem_container_attr(const struct nan_data *nan, + const struct nan_peer *peer, + struct wpabuf *buf); +struct bitfield * nan_peer_schedule_intersection( + struct nan_data *nan, const struct nan_peer *peer, + const struct nan_schedule *sched); +bool nan_ndl_meets_qos(struct nan_data *nan, const struct nan_peer *peer, + const struct bitfield *common_bf); +bool nan_ndl_validate_peer_avail(struct nan_data *nan, struct nan_peer *peer); +int nan_convert_chan_sched_to_bf(struct nan_data *nan, + const struct nan_chan_schedule *chan, + struct bitfield **avail_bf, u8 *map_id, + u8 *op_class, u16 *cbm, u16 *pcbm); +int nan_get_chan_bm(struct nan_data *nan, const struct nan_sched_chan *chan, + u8 *op_class, u16 *chan_bm, u16 *pri_chan_bm); +int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id, + u32 map_ids_bitmap, u8 type_for_conditional, + size_t n_chans, struct nan_chan_schedule *chans, + struct wpabuf *buf, bool include_potential); +void nan_del_avail_entry(struct nan_avail_entry *entry); +void nan_flush_avail_entries(struct dl_list *avail_entries); +int nan_sched_entries_to_avail_entries(struct nan_data *nan, + struct dl_list *avail_entries, + const u8 *sched_entries, + u16 sched_entries_len); +struct bitfield * nan_tbm_to_bf(struct nan_data *nan, + const struct nan_time_bitmap *tbm); +struct bitfield * nan_sched_to_bf(struct nan_data *nan, struct dl_list *sched, + u8 *map_id, enum nan_reason *reason); +bool nan_sched_covered_by_avail_entry(struct nan_data *nan, + struct nan_avail_entry *avail, + struct bitfield *sched_bf, u8 map_id); +bool nan_sched_covered_by_avail_entries(struct nan_data *nan, + struct dl_list *avail_entries, + const u8 *sched, size_t sched_len); +bool nan_sched_bf_covered_by_avail_entries_and_chan( + struct nan_data *nan, const struct dl_list *avail_entries, + struct bitfield *sched_bf, u8 map_id, u8 op_class, u16 cbm); +struct bitfield * nan_avail_entries_to_bf(struct nan_data *nan, + const struct dl_list *avail_entries, + u8 op_class, u16 cbm, u16 pri_cbm); +void nan_ndp_terminated(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_id *ndp_id, const u8 *local_ndi, + const u8 *peer_ndi, enum nan_reason reason, u8 gtk_id); +int nan_crypto_pmk_to_ptk(const u8 *pmk, const u8 *iaddr, const u8 *raddr, + const u8 *inonce, const u8 *rnonce, + struct nan_ptk *ptk, + enum nan_cipher_suite_id cipher); +int nan_crypto_calc_pmkid(const u8 *pmk, const u8 *iaddr, const u8 *raddr, + const u8 *serv_id, + enum nan_cipher_suite_id cipher, u8 *pmkid); +int nan_crypto_calc_auth_token(enum nan_cipher_suite_id cipher, + const u8 *buf, size_t len, u8 *token); +int nan_crypto_key_mic(const u8 *buf, size_t len, const u8 *kck, + size_t kck_len, u8 cipher, u8 *mic); +int nan_crypto_derive_npk(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, const u8 *responder_nmi, + u8 *buf, size_t buf_len); +int nan_crypto_derive_kek(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, const u8 *responder_nmi, + struct wpa_ptk *ptk); +int nan_crypto_derive_nd_pmk_from_kdk(const u8 *kdk, size_t kdk_len, + enum nan_cipher_suite_id cipher, + const u8 *initiator_nmi, + const u8 *responder_nmi, u8 *nd_pmk); +struct wpabuf * nan_crypto_encrypt_key_data(const struct wpabuf *key_data, + const u8 *kek, size_t kek_len); +struct wpabuf * nan_crypto_decrypt_key_data(const u8 *kek, size_t kek_len, + const u8 *encrypted_data, + size_t encrypted_len); +void nan_sec_reset(struct nan_data *nan, struct nan_ndp_sec *ndp_sec); +int nan_sec_rx(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg); +int nan_add_csia(struct wpabuf *buf, u8 capab, size_t cs_list_len, + const struct nan_cipher_suite *cs_list); +int nan_sec_add_attrs(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype, struct wpabuf *buf); +int nan_sec_init_resp(struct nan_data *nan, struct nan_peer *peer); +int nan_sec_pre_tx(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf); +bool nan_sec_ndp_store_keys(struct nan_data *nan, struct nan_peer *peer, + const u8 *peer_ndi, const u8 *local_ndi); +int nan_sec_get_tk(struct nan_data *nan, struct nan_peer *peer, + const u8 *peer_ndi, const u8 *local_ndi, + u8 *tk, size_t *tk_len, enum nan_cipher_suite_id *csid); +void nan_add_dev_capa_ext_attr(struct nan_data *nan, struct wpabuf *buf); + +void nan_bootstrap_reset(struct nan_data *nan, struct nan_peer *peer); +bool nan_bootstrap_handle_rx(struct nan_data *nan, const u8 *peer_nmi, + const u8 *npba, u16 npba_len, + const u8 *buf, size_t len, + int handle, u8 req_instance_id); +int nan_add_nira(struct wpabuf *buf, const u8 *tag, const u8 *nonce); +void nan_parse_peer_dev_capa_ext(struct nan_data *nan, struct nan_peer *peer, + struct nan_attrs *attrs); +int nan_configure_peer_schedule(struct nan_data *nan, struct nan_peer *peer, + const struct nan_schedule *local_sched); +bool nan_is_ndpe_supported(struct nan_data *nan, const struct nan_peer *peer); +void nan_add_kde_hdr(struct wpabuf *buf, u32 kde, size_t data_len); +int nan_clear_peer_schedule(struct nan_data *nan, struct nan_peer *peer); +#ifdef CONFIG_PASN +int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nonce, u8 *tag); +void nan_pairing_deinit_peer(struct nan_peer *peer); +bool nan_pairing_followup_rx(struct nan_data *nan_data, const u8 *peer_addr, + const struct nan_shared_key *shared_key_descr, + size_t attr_len); +#else /* CONFIG_PASN */ +static inline +int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nonce, u8 *tag) +{ + return -1; +} + +static inline void nan_pairing_deinit_peer(struct nan_peer *peer) +{ +} +#endif /* CONFIG_PASN */ + +#endif /* NAN_I_H */ diff --git a/contrib/wpa/src/nan/nan_module_test_cases.c b/contrib/wpa/src/nan/nan_module_test_cases.c new file mode 100644 index 000000000000..943fb829d089 --- /dev/null +++ b/contrib/wpa/src/nan/nan_module_test_cases.c @@ -0,0 +1,817 @@ +/* + * nan_test - NAN NDP state machine test cases + * 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 "utils/list.h" +#include "nan_i.h" +#include "nan_module_tests.h" + +static u8 default_bitmap[] = { 0xfe, 0xff, 0xfe, 0xff }; + +static void nan_test_schedule_cb(struct nan_schedule *sched, bool ndc, + int freq, int center_freq1, int center_freq2, + int bandwidth) +{ + os_memset(sched, 0, sizeof(*sched)); + + /* Only map ID 0 is used */ + sched->map_ids_bitmap = 0x1; + + sched->n_chans = 1; + sched->chans[0].chan.freq = freq; + sched->chans[0].chan.center_freq1 = center_freq1; + sched->chans[0].chan.center_freq2 = center_freq2; + sched->chans[0].chan.bandwidth = bandwidth; + + sched->chans[0].committed.duration = 0; + sched->chans[0].committed.period = 3; + sched->chans[0].committed.offset = 0; + sched->chans[0].committed.len = sizeof(default_bitmap); + os_memcpy(sched->chans[0].committed.bitmap, default_bitmap, + sizeof(default_bitmap)); + + sched->chans[0].map_id = 0; + + if (!ndc) + return; + + os_memset(&sched->ndc, 0, sizeof(sched->ndc)); + sched->ndc.duration = 0; + sched->ndc.period = 3; + sched->ndc.offset = 0; + sched->ndc.len = 1; + sched->ndc.bitmap[0] = 0xfe; +} + + +static int nan_test_schedule_cb_all_ndc(struct nan_schedule *sched) +{ + static u8 seq_id = 1; + + nan_test_schedule_cb(sched, true, 5745, 5745, true, 20); + sched->sequence_id = ++seq_id; + return 0; +} + + +static int nan_test_schedule_cb_all_no_ndc(struct nan_schedule *sched) +{ + static u8 seq_id = 1; + + nan_test_schedule_cb(sched, false, 5745, 5745, 0, 20); + sched->sequence_id = ++seq_id; + return 0; +} + + +static int nan_test_schedule_cb_all_no_ndc_period_4(struct nan_schedule *sched) +{ + static u8 seq_id = 1; + + nan_test_schedule_cb(sched, false, 5745, 5745, 0, 20); + + /* + * Modify the map to have a period or 1024 TUs (both halfs are + * identical). + */ + sched->chans[0].committed.period = 4; + sched->chans[0].committed.len += sizeof(default_bitmap); + os_memcpy(sched->chans[0].committed.bitmap + sizeof(default_bitmap), + default_bitmap, + sizeof(default_bitmap)); + + sched->sequence_id = ++seq_id; + return 0; +} + + +static int nan_test_schedule_cb_2ghz_no_ndc(struct nan_schedule *sched) +{ + static u8 seq_id = 1; + + nan_test_schedule_cb(sched, false, 2437, 2437, 0, 20); + sched->sequence_id = ++seq_id; + return 0; +} + + +static int nan_test_get_chans_default(struct nan_channels *chans) +{ + chans->n_chans = 2; + chans->chans = os_zalloc(sizeof(struct nan_channel_info) * + chans->n_chans); + if (!chans->chans) + return -1; + + chans->chans[0].channel = 6; + chans->chans[0].op_class = 81; + chans->chans[0].pref = 255; + + chans->chans[1].channel = 149; + chans->chans[1].op_class = 126; + chans->chans[1].pref = 254; + + return 0; +} + + +static int nan_test_get_chans_default_reverse(struct nan_channels *chans) +{ + chans->n_chans = 2; + chans->chans = os_zalloc(sizeof(struct nan_channel_info) * + chans->n_chans); + if (!chans->chans) + return -1; + + chans->chans[0].channel = 149; + chans->chans[0].op_class = 126; + chans->chans[0].pref = 255; + + chans->chans[1].channel = 6; + chans->chans[1].op_class = 81; + chans->chans[1].pref = 254; + + return 0; +} + + +static int nan_test_get_chans_default_24g(struct nan_channels *chans) +{ + chans->n_chans = 1; + chans->chans = os_zalloc(sizeof(struct nan_channel_info) * + chans->n_chans); + if (!chans->chans) + return -1; + + chans->chans[0].channel = 6; + chans->chans[0].op_class = 81; + chans->chans[0].pref = 255; + + return 0; +} + + +static int nan_test_get_chans_default_52g(struct nan_channels *chans) +{ + chans->n_chans = 1; + chans->chans = os_zalloc(sizeof(struct nan_channel_info) * + chans->n_chans); + if (!chans->chans) + return -1; + + chans->chans[0].channel = 149; + chans->chans[0].op_class = 126; + chans->chans[0].pref = 255; + + return 0; +} + + +static struct nan_test_case three_way_ndp_two_way_ndl_chan_149 = { + .name = "Three way NDP and two way NDL channel 149", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + } +}; + + +static struct nan_test_case three_way_ndp_two_way_ndl_diff_period = { + .name = "Three way NDP and two way NDL channel 149. Subscriber period=4", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc_period_4, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + } +}; + +static struct nan_test_case three_way_ndp_two_way_ndl_chan_6 = { + .name = "Three way NDP and two way NDL channel 6", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default_24g, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + } +}; + +static struct nan_test_case three_way_ndp_two_way_ndl_chan_mis = { + .name = "Three way NDP and two way NDL channel mismatch", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default_52g, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_2ghz_no_ndc, + .get_chans_cb = nan_test_get_chans_default_24g, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + }, + }, + } +}; + +static struct nan_test_case three_way_ndp_three_way_ndl = { + .name = "Three way NDP and three way NDL", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_2ghz_no_ndc, + .schedule_conf_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default_reverse, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .term_once_connected = 1, + }, + }, + } +}; + +static struct nan_test_case three_way_ndp_two_way_ndl_reject = { + .name = "Three way NDP and two way NDL rejected", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + .reason = NAN_REASON_NDP_REJECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + }, + }, + } +}; + + +static struct nan_test_case four_way_ndp_two_way_ndl_chan_149_ccm_128 = { + .name = "Four way NDP and two way NDL channel 149 with CCMP 128", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_CCM_128, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_CCM_128, + .term_once_connected = 1, + }, + }, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + } +}; + +static struct nan_test_case four_way_ndp_two_way_ndl_chan_149_gcm_256 = { + .name = "Four way NDP and three way NDL with GCMP 256", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_2ghz_no_ndc, + .schedule_conf_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default_reverse, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + .term_once_connected = 1, + }, + }, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + } +}; + + +static struct nan_test_case pmk_mismatch = { + .name = "PMK mismatch test case", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + .csid = NAN_CS_SK_GCM_256, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 1, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + .csid = NAN_CS_SK_GCM_256, + }, + }, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x0, + }, + } +}; + + +static struct nan_test_case two_ndps_no_security = { + .name = "Two NDPs: no security. Both accepted", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 2, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 2, + .ndp_confs = { + { + .accept_request = 1, + .term_once_connected = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + } +}; + + +static struct nan_test_case three_ndps_no_security_middle_one_rejected = { + .name = "Three NDPs: no security. Middle one rejected", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 0, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_DISCONNECTED, + }, + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + }, + } +}; + + +static struct nan_test_case three_ndps_increasing_security = { + .name = "Three NDPs: increasing security levels", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_CCM_128, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_CCM_128, + }, + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + }, + }, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + } +}; + + +static struct nan_test_case three_ndps_deacreasing_security = { + .name = "Three NDPs: decreasing security levels", + .pub_conf = { + .schedule_cb = nan_test_schedule_cb_all_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_GCM_256, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .expected_csid = NAN_CS_SK_GCM_256, + }, + }, + .pot_avail = { + 0x12, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, + 0xba, 0x02, 0x20, 0x02, 0x04 + }, + .pot_avail_len = 13, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + }, + .sub_conf = { + .schedule_cb = nan_test_schedule_cb_all_no_ndc, + .get_chans_cb = nan_test_get_chans_default, + .n_ndps = 3, + .ndp_confs = { + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_GCM_256, + .expected_csid = NAN_CS_SK_GCM_256, + }, + { + .accept_request = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .csid = NAN_CS_SK_CCM_128, + .expected_csid = NAN_CS_SK_GCM_256, + }, + { + .accept_request = 1, + .term_once_connected = 1, + .expected_result = + NAN_TEST_NDP_NOTIFY_CONNECTED, + .expected_csid = NAN_CS_SK_GCM_256, + }, + }, + .pmk = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + }, + } +}; + + +static const struct nan_test_case *g_nan_test_cases[] = { + &three_way_ndp_two_way_ndl_chan_149, + &three_way_ndp_two_way_ndl_diff_period, + &three_way_ndp_two_way_ndl_chan_6, + &three_way_ndp_two_way_ndl_chan_mis, + &three_way_ndp_two_way_ndl_reject, + &three_way_ndp_three_way_ndl, + &four_way_ndp_two_way_ndl_chan_149_ccm_128, + &four_way_ndp_two_way_ndl_chan_149_gcm_256, + &pmk_mismatch, + &two_ndps_no_security, + &three_ndps_no_security_middle_one_rejected, + &three_ndps_increasing_security, + &three_ndps_deacreasing_security, + NULL +}; + + +const struct nan_test_case * nan_test_case_get_next(void) +{ + static u32 nan_test_case_idx = 0; + const struct nan_test_case *curr = g_nan_test_cases[nan_test_case_idx]; + + if (curr) + nan_test_case_idx++; + + return curr; +} diff --git a/contrib/wpa/src/nan/nan_module_tests.c b/contrib/wpa/src/nan/nan_module_tests.c new file mode 100644 index 000000000000..61d126d46ee2 --- /dev/null +++ b/contrib/wpa/src/nan/nan_module_tests.c @@ -0,0 +1,1530 @@ +/* + * NAN NDP/NDL state machine testing + * 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/nan_defs.h" +#include "drivers/driver.h" +#include "nan_i.h" +#include "nan_module_tests.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/crypto.h" + +#define NAN_TEST_MAX_NAF_LEN 1024 +#define NAN_TEST_MAX_PEERS 20 +#define NAN_TEST_MAX_ACTIONS 30 +#define NAN_TEST_MIN_SLOTS 12 +#define NAN_TEST_MAX_LATENCY 3 +#define NAN_TEST_PUBLISH_INST_ID 12 + +static const u8 g_pub_nmi[] = { 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }; +static const u8 g_pub_ndi[] = { 0x00, 0xAA, 0xAA, 0x00, 0x00, 0x00 }; +static const u8 g_sub_nmi[] = { 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB }; +static const u8 g_sub_ndi[] = { 0x00, 0xBB, 0xBB, 0xBB, 0x00, 0x00 }; + +/** + * struct nan_test_action - NAN test action context + * @list: Used for global actions list + * @dev: NAN device for which the action is intended + * @cb: Callback to be called for the action + * @ctx: Parameter for the callback function + * + * The test utility uses actions to handle asynchronous events between the + * devices and internally to the local device. When an event is triggered + * outside of the NAN module context, the event is wrapped by an action item and + * is processed asynchronously. Examples: + * + * NAF transmission: when a NAF is transmitted by a device, it is translated to + * 2 asynchronous actions: one action to indicate Tx status to the transmitting + * device and one to indicate an Rx frame to the peer device. + * + * NDP event: when an NDP event is fired by the NAN module, it is translated to + * an asynchronous action to the local device. + */ +struct nan_test_action { + struct dl_list list; + struct nan_device *dev; + int (*cb)(struct nan_device *dev, void *ctx); + void *ctx; +}; + +/** + * nan_test_tx_status_action - NAN test Tx status action context + * @data: Copy of the original NAF + * @acked: True iff the NAF was acked + * @dst: Destination of the NAF + */ +struct nan_test_tx_status_action { + const struct wpabuf *data; + bool acked; + u8 dst[ETH_ALEN]; +}; + +/** + * nan_test_ndp_notify - NAN test NDP notification handling + * @type: Notification type + * @ndp_id: NDP identifier + * @ssi: Service specific information + * @ssi_len: Length of service specific information + */ +struct nan_test_ndp_notify { + enum nan_test_ndp_notify_type type; + struct nan_ndp_id ndp_id; + const u8 *ssi; + size_t ssi_len; +}; + +/** + * nan_test_global - Global context for the NAN testing + * @devs: List of devices. See &struct nan_device. + * @actions: tracks the NAN actions + * @elems: Default HT/VHT/HE capabilities elements + */ +struct nan_test_global { + struct dl_list devs; + struct dl_list actions; + struct wpabuf *elems; +}; + +#define DEV_NOT_INIT_ERR(_dev) \ +do { \ + if (!(_dev) || !(_dev)->nan) { \ + wpa_printf(MSG_ERROR, \ + "NAN: %s: device not initialized", \ + __func__); \ + return -1; \ + } \ +} while (0) + +#define DEV_NOT_INIT_ERR_VOID(_dev) \ +do { \ + if (!(_dev) || !(_dev)->nan) { \ + wpa_printf(MSG_ERROR, \ + "NAN: %s: device not initialized", \ + __func__); \ + return; \ + } \ +} while (0) + + +/** + * nan_test_global_init - Initialize NAN test global data structures + * @global: NAN test global data structure + */ +static void nan_test_global_init(struct nan_test_global *global) +{ + u8 elems[] = { + /* HT capabilities */ + 0x2d, 0x1a, 0x7e, 0x10, 0x1b, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + /* VHT capabilities */ + 0xbf, 0x0c, 0xfa, 0x04, 0x80, 0x03, 0xaa, 0xaa, + 0x00, 0x00, 0xaa, 0xaa, 0x00, 0x00, + + /* HE capabilities */ + 0xff, 0x1e, 0x23, 0x01, 0x78, 0xc8, 0x1a, 0x40, + 0x00, 0x1c, 0xbf, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfa, 0xff, 0xfa, 0xff, + 0xfa, 0xff, 0xfa, 0xff, 0xfa, 0xff, 0xfa, 0xff, + }; + + wpa_printf(MSG_INFO, "%s: Enter", __func__); + os_memset(global, 0, sizeof(struct nan_test_global)); + dl_list_init(&global->devs); + dl_list_init(&global->actions); + + global->elems = wpabuf_alloc_copy(elems, sizeof(elems)); + + wpa_printf(MSG_INFO, "%s: Done\n", __func__); +} + + +/** + * nan_test_dev_deinit - De-initialize NAN device + * @dev: NAN device + */ +static void nan_test_dev_deinit(struct nan_device *dev) +{ + DEV_NOT_INIT_ERR_VOID(dev); + + os_free(dev->pot_avail); + nan_stop(dev->nan); + nan_deinit(dev->nan); + os_memset(dev, 0, sizeof(struct nan_device)); +} + + +/** + * nan_test_global_deinit - De-initialize NAN test global data structures + * @global: NAN test global data structure + */ +static void nan_test_global_deinit(struct nan_test_global *global) +{ + struct nan_device *dev, *next; + struct nan_test_action *action, *action_next; + + wpa_printf(MSG_INFO, "%s: Enter", __func__); + + dl_list_for_each_safe(dev, next, &global->devs, struct nan_device, + list) { + dl_list_del(&dev->list); + nan_test_dev_deinit(dev); + os_free(dev); + } + + dl_list_for_each_safe(action, action_next, &global->actions, + struct nan_test_action, list) { + dl_list_del(&dev->list); + os_free(action->ctx); + os_free(action); + } + + wpabuf_free(global->elems); +} + + +/** + * nan_test_add_action - Add a NAN test action to the list of actions + * @global: NAN test global data structure + * @dev: NAN device for which the action is intended + * @cb: Callback to be called for the action + * @ctx: Parameter for the callback function + * Returns: A pointer to the newly added action, or NULL on failure + */ +static struct nan_test_action * +nan_test_add_action(struct nan_test_global *global, + struct nan_device *dev, + int (*cb)(struct nan_device *dev, void *ctx), + void *ctx) +{ + struct nan_test_action *action; + + action = os_malloc(sizeof(struct nan_test_action)); + if (!action) { + wpa_printf(MSG_ERROR, "NAN Test: Failed to allocate action"); + return NULL; + } + + action->dev = dev; + action->cb = cb; + action->ctx = ctx; + + dl_list_add_tail(&global->actions, &action->list); + return action; +} + + +/** + * nan_test_run_actions - Iterate over all NAN test actions and execute them + * @global: NAN test global data structure + * Returns: 0 in success, -1 on error + * + * Runs as longs as there are actions to run. Exists where there are no more + * action to perform or on error. + */ +static int nan_test_run_actions(struct nan_test_global *global) +{ + u32 n_actions = 0; + + wpa_printf(MSG_INFO, "%s: Running actions", __func__); + + while (!dl_list_empty(&global->actions)) { + struct nan_test_action *action = + dl_list_first(&global->actions, struct nan_test_action, + list); + int ret; + + dl_list_del(&action->list); + + if (++n_actions > NAN_TEST_MAX_ACTIONS) { + wpa_printf(MSG_ERROR, + "NAN Test action: Too many actions executed"); + return -1; + } + + if (!action->cb || !action->dev) { + wpa_printf(MSG_ERROR, + "NAN Test action: Invalid action"); + return -1; + } + + wpa_printf(MSG_INFO, "%s: ===> NAN Test action <===", + action->dev->name); + ret = action->cb(action->dev, action->ctx); + + /* + * The action context should be freed by the callback function + * that understands the content of the context and can properly + * handle it. + */ + os_free(action); + + if (ret) { + wpa_printf(MSG_ERROR, "NAN Test action: FAILED"); + return ret; + } + } + + wpa_printf(MSG_INFO, "%s: Running actions done", __func__); + return 0; +} + + +static int nan_test_start_cb(void *ctx, const struct nan_cluster_config *config) +{ + struct nan_device *dev = ctx; + + DEV_NOT_INIT_ERR(dev); + + return 0; +} + + +/* + * nan_test_stop_cb - Stop the NAN device. + * @ctx: Pointer to the &struct nan_device + */ +static void nan_test_stop_cb(void *ctx) +{ + struct nan_device *dev = ctx; + + DEV_NOT_INIT_ERR_VOID(dev); + + wpa_printf(MSG_INFO, "%s: %s: Done", __func__, dev->name); +} + + +static int nan_test_nan_ndp_action(struct nan_device *dev, void *ctx) +{ + struct nan_ndp_params *params = ctx; + int ret; + + wpa_printf(MSG_INFO, "%s: %s: type=%u, peer_nmi=" MACSTR, + dev->name, __func__, params->type, + MAC2STR(params->ndp_id.peer_nmi)); + + ret = nan_handle_ndp_setup(dev->nan, params); + + os_free(params); + + return ret; +} + + +/* + * nan_ndp_notify_action - Default NAN NDP notification handling + * @dev: NAN device + * @ctx: Pointer to &struct nan_test_ndp_notify + */ +static int nan_ndp_notify_action(struct nan_device *dev, void *ctx) +{ + struct nan_test_ndp_notify *notify = ctx; + struct nan_test_action *action; + int ret; + + wpa_printf(MSG_INFO, "%s: %s: type=%u, peer_nmi=" MACSTR, + dev->name, __func__, notify->type, + MAC2STR(notify->ndp_id.peer_nmi)); + + ret = -1; + if (notify->type == NAN_TEST_NDP_NOTIFY_REQUEST) { + struct nan_ndp_params *params; + struct nan_device *curd; + bool found = false; + + dl_list_for_each(curd, &dev->global->devs, struct nan_device, + list) { + if (ether_addr_equal(curd->nmi, + notify->ndp_id.peer_nmi)) { + found = true; + break; + } + } + + if (!found) { + wpa_printf(MSG_ERROR, "Peer device not found"); + ret = -1; + goto out; + } + + params = os_zalloc(sizeof(struct nan_ndp_params)); + if (!params) { + ret = -1; + goto out; + } + + params->type = NAN_NDP_ACTION_RESP; + os_memcpy(params->ndp_id.peer_nmi, notify->ndp_id.peer_nmi, + ETH_ALEN); + os_memcpy(params->ndp_id.init_ndi, notify->ndp_id.init_ndi, + ETH_ALEN); + params->ndp_id.id = notify->ndp_id.id; + params->qos.min_slots = NAN_TEST_MIN_SLOTS; + params->qos.max_latency = NAN_TEST_MAX_LATENCY; + + if (dev->conf->ndp_confs[dev->n_ndps].accept_request) { + wpa_printf(MSG_INFO, "%s: Accepting request", + dev->name); + + os_memcpy(params->u.resp.resp_ndi, dev->ndi, ETH_ALEN); + params->u.resp.status = NAN_NDP_STATUS_ACCEPTED; + dev->conf->schedule_cb(¶ms->sched); + params->sched.elems = dev->global->elems; + params->sched_valid = 1; + + params->sec.csid = + dev->conf->ndp_confs[dev->n_ndps].csid; + os_memcpy(params->sec.pmk, dev->conf->pmk, PMK_LEN); + } else { + wpa_printf(MSG_INFO, "%s: Rejecting request", + dev->name); + + params->u.resp.status = NAN_NDP_STATUS_REJECTED; + params->u.resp.reason_code = + dev->conf->ndp_confs[dev->n_ndps].reason; + } + + action = nan_test_add_action(dev->global, dev, + nan_test_nan_ndp_action, + params); + if (action) + ret = 0; + } else if (notify->type == NAN_TEST_NDP_NOTIFY_RESPONSE) { + struct nan_ndp_params *params; + + params = os_zalloc(sizeof(struct nan_ndp_params)); + if (!params) { + ret = -1; + goto out; + } + + params->type = NAN_NDP_ACTION_CONF; + os_memcpy(params->ndp_id.peer_nmi, notify->ndp_id.peer_nmi, + ETH_ALEN); + os_memcpy(params->ndp_id.init_ndi, notify->ndp_id.init_ndi, + ETH_ALEN); + params->ndp_id.id = notify->ndp_id.id; + + params->qos.min_slots = NAN_TEST_MIN_SLOTS; + params->qos.max_latency = NAN_TEST_MAX_LATENCY; + + if (dev->conf->ndp_confs[dev->n_ndps].accept_request) { + wpa_printf(MSG_INFO, "%s: Accepting response", + dev->name); + + os_memcpy(params->u.resp.resp_ndi, dev->ndi, ETH_ALEN); + params->u.resp.status = NAN_NDP_STATUS_ACCEPTED; + + if (!dev->conf->schedule_conf_cb) { + wpa_printf(MSG_ERROR, + "%s: No schedule conf cb defined", + dev->name); + os_free(params); + ret = -1; + goto out; + } + + dev->conf->schedule_conf_cb(¶ms->sched); + params->sched.elems = dev->global->elems; + params->sched_valid = 1; + + params->sec.csid = + dev->conf->ndp_confs[dev->n_ndps].csid; + os_memcpy(params->sec.pmk, dev->conf->pmk, PMK_LEN); + } else { + wpa_printf(MSG_INFO, "%s: Rejecting response", + dev->name); + + params->u.resp.status = NAN_NDP_STATUS_REJECTED; + params->u.resp.reason_code = + dev->conf->ndp_confs[dev->n_ndps].reason; + } + + action = nan_test_add_action(dev->global, dev, + nan_test_nan_ndp_action, + params); + if (action) + ret = 0; + } else if (notify->type == + dev->conf->ndp_confs[dev->n_ndps].expected_result) { + ret = 0; + if (notify->type == NAN_TEST_NDP_NOTIFY_CONNECTED) { + if (dev->conf->ndp_confs[dev->n_ndps]. + term_once_connected) { + struct nan_ndp_params *params; + + wpa_printf(MSG_INFO, + "%s: Connected successfully. Now term", + dev->name); + + params = os_zalloc(sizeof(*params)); + if (!params) { + ret = -1; + goto out; + } + + params->type = NAN_NDP_ACTION_TERM; + os_memcpy(params->ndp_id.peer_nmi, + notify->ndp_id.peer_nmi, + ETH_ALEN); + os_memcpy(params->ndp_id.init_ndi, + notify->ndp_id.init_ndi, + ETH_ALEN); + params->ndp_id.id = notify->ndp_id.id; + + action = nan_test_add_action( + dev->global, dev, + nan_test_nan_ndp_action, params); + if (action) + ret = 0; + } + } + } else if (notify->type == NAN_TEST_NDP_NOTIFY_DISCONNECTED && + dev->connected_notify_received) { + wpa_printf(MSG_INFO, + "%s: Disconnected after connected as expected. Test case done", + dev->name); + ret = 0; + } else { + wpa_printf(MSG_ERROR, + "%s: Unexpected notify: type=%u expected=%u", + dev->name, notify->type, + dev->conf->ndp_confs[dev->n_ndps].expected_result); + ret = -1; + } + +out: + os_free((void *) notify->ssi); + os_free(notify); + return ret; +} + + +static void nan_test_ndp_action(struct nan_device *dev, enum + nan_test_ndp_notify_type type, + struct nan_ndp_id *ndp_id, + u8 publish_inst_id, + const u8 *ssi, size_t ssi_len, + enum nan_cipher_suite_id csid, + const u8 *pmkid) +{ + struct nan_test_ndp_notify *notify; + + DEV_NOT_INIT_ERR_VOID(dev); + + wpa_printf(MSG_INFO, "%s: %s: Enter: type=%u", + dev->name, __func__, type); + + notify = os_zalloc(sizeof(struct nan_test_ndp_notify)); + if (!notify) { + wpa_printf(MSG_ERROR, + "Failed allocation: nan_test_ndp_notify"); + return; + } + + notify->type = type; + os_memcpy(¬ify->ndp_id, ndp_id, sizeof(notify->ndp_id)); + + if (ssi && ssi_len) { + notify->ssi = os_memdup(ssi, ssi_len); + if (!notify->ssi) { + wpa_printf(MSG_ERROR, "Failed allocation: ssi"); + os_free(notify); + return; + } + notify->ssi_len = ssi_len; + } + + if (nan_test_add_action(dev->global, dev, nan_ndp_notify_action, + notify)) + return; + + os_free((void *) notify->ssi); + os_free(notify); +} + + +/** + * nan_test_ndp_action_notfi_cb - Callback for NDP request + * @ctx: Pointer to &struct nan_device + * @params: NDP action notification parameters + * + * The handling of the event is done asynchronously through the NAN test actions + * processing. + */ +static void +nan_test_ndp_action_notfi_cb(void *ctx, + struct nan_ndp_action_notif_params *params) +{ + struct nan_device *dev = ctx; + enum nan_test_ndp_notify_type type; + + DEV_NOT_INIT_ERR_VOID(dev); + + if (params->is_request) + type = NAN_TEST_NDP_NOTIFY_REQUEST; + else + type = NAN_TEST_NDP_NOTIFY_RESPONSE; + + nan_test_ndp_action(dev, type, ¶ms->ndp_id, + params->publish_inst_id, params->ssi, + params->ssi_len, params->csid, params->pmkid); +} + + +/** + * nan_test_ndp_connected_cb - Callback for NDP connected + * @ctx: Pointer to &struct nan_device + * @params: NDP action notification parameters + * Returns: 0 on success, -1 on failure + * + * The handling of the event is done asynchronously through the NAN test actions + * processing. + */ +static int nan_test_ndp_connected_cb(void *ctx, + struct nan_ndp_connection_params *params) +{ + struct nan_device *dev = ctx; + struct nan_peer_schedule sched; + struct nan_peer_potential_avail pot; + + DEV_NOT_INIT_ERR(dev); + + wpa_printf(MSG_INFO, + "%s: %s: Enter. local_ndi=" MACSTR " peer_ndi=" MACSTR, + dev->name, __func__, + MAC2STR(params->local_ndi), MAC2STR(params->peer_ndi)); + + nan_peer_get_schedule_info(dev->nan, params->ndp_id.peer_nmi, &sched); + nan_peer_get_pot_avail(dev->nan, params->ndp_id.peer_nmi, &pot); + + if (nan_peer_get_tk(dev->nan, params->ndp_id.peer_nmi, + params->peer_ndi, params->local_ndi, + dev->tk, &dev->tk_len, &dev->csid) == 0) { + wpa_hexdump(MSG_DEBUG, "NAN Test: TK", dev->tk, dev->tk_len); + + if (dev->csid != + dev->conf->ndp_confs[dev->n_ndps].expected_csid) { + wpa_printf(MSG_ERROR, + "%s: Unexpected CSID: got %u expected %u", + dev->name, dev->csid, + dev->conf->ndp_confs[dev->n_ndps]. + expected_csid); + return -1; + } + } + + nan_test_ndp_action(dev, NAN_TEST_NDP_NOTIFY_CONNECTED, ¶ms->ndp_id, + 0, params->ssi, params->ssi_len, NAN_CS_NONE, NULL); + + dev->connected_notify_received = true; + + return 0; +} + + +/** + * nan_test_ndp_disconnected_cb - Callback for NDP disconnected + * @ctx: Pointer to &struct nan_device + * @ndp_id: NDP identifier + * @local_ndi: Local NDI address + * @peer_ndi: Peer NDI address + * @reason: Reason for disconnection + * @locally_generated: true if locally generated, false if triggered by peer + * @remove_sta: true if the NDI station should be removed + * @failure: true if NDP setup failed, false if graceful disconnection + * @gtk_id: GTK key ID used for the NDP; 0 if no GTK was used + * + * The handling of the event is done asynchronously through the NAN test actions + * processing. + */ +static void nan_test_ndp_disconnected_cb(void *ctx, struct nan_ndp_id *ndp_id, + const u8 *local_ndi, + const u8 *peer_ndi, + enum nan_reason reason, + bool locally_generated, + bool remove_sta, + bool failure, + u8 gtk_id) +{ + struct nan_device *dev = ctx; + + DEV_NOT_INIT_ERR_VOID(dev); + + wpa_printf(MSG_INFO, "%s: %s: Enter", dev->name, __func__); + + nan_test_ndp_action(dev, NAN_TEST_NDP_NOTIFY_DISCONNECTED, + ndp_id, 0, NULL, 0, NAN_CS_NONE, NULL); + + dev->disconnected_notify_received = true; +} + + +/** + * nan_test_send_naf_cb_action - NAN test action to send a NAN to a device + * @dev: NAN device + * @ctx: Pointer to a buffer holding the NAF to be sent + */ +static int nan_test_send_naf_cb_action(struct nan_device *dev, void *ctx) +{ + struct wpabuf *data = ctx; + int ret; + + DEV_NOT_INIT_ERR(dev); + + wpa_printf(MSG_INFO, "%s: %s: Enter", dev->name, __func__); + wpa_hexdump(MSG_DEBUG, "NAN Test: NAF:", wpabuf_head(data), + wpabuf_len(data)); + + ret = nan_action_rx(dev->nan, wpabuf_head(data), wpabuf_len(data)); + wpabuf_free(data); + return ret; +} + + +/** + * nan_test_tx_status_action - NAN test action to send Tx status to a device + * @dev: NAN device + * @ctx: Pointer to &struct nan_test_tx_status_action + */ +static int nan_test_tx_status_action(struct nan_device *dev, void *ctx) +{ + struct nan_test_tx_status_action *tx_status = ctx; + int ret; + + DEV_NOT_INIT_ERR(dev); + + wpa_printf(MSG_INFO, "%s: %s: enter", dev->name, __func__); + + ret = nan_tx_status(dev->nan, tx_status->dst, + wpabuf_head(tx_status->data), + wpabuf_len(tx_status->data), tx_status->acked); + + wpabuf_free((struct wpabuf *)tx_status->data); + os_free(tx_status); + return ret; +} + + +/** + * nan_test_send_naf_cb - NAN send NAF callback function + * @ctx: Pointer to &struct nan_device + * @dst: Destination NAN Management Interface address + * @src: Source NAN Management Interface address + * @cluster_id: NAN Cluster ID + * + * The callback builds the management frame and creates the following NAN test + * actions: + * - NAN test tx status action: to be sent to the transmitting device + * - NAN test send NAF action: to be sent to the destination device. + */ +static int nan_test_send_naf_cb(void *ctx, const u8 *dst, const u8 *src, + const u8 *cluster_id, struct wpabuf *buf) +{ + struct nan_device *dev = ctx; + struct nan_device *curd; + struct ieee80211_hdr *hdr; + struct wpabuf *data = NULL; + struct nan_test_tx_status_action *tx_status = NULL; + struct nan_test_action *dev_action, *cur_action; + bool found = false; + + DEV_NOT_INIT_ERR(dev); + + if (!dst) + return -1; + + wpa_printf(MSG_INFO, "%s: %s: Enter " MACSTR, __func__, dev->name, + MAC2STR(dst)); + + dl_list_for_each(curd, &dev->global->devs, struct nan_device, list) { + if (ether_addr_equal(curd->nmi, dst) || + ether_addr_equal(curd->ndi, dst)) { + found = true; + break; + } + } + + if (!found) { + wpa_printf(MSG_ERROR, "%s: Destination device not found", + __func__); + return -1; + } + + /* Prepare action to send the frame to the peer */ + data = wpabuf_alloc(sizeof(struct ieee80211_hdr) + wpabuf_len(buf)); + if (!data) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate NAF", __func__); + return -1; + } + + hdr = wpabuf_put(data, sizeof(struct ieee80211_hdr)); + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + + os_memcpy(hdr->addr1, dst, ETH_ALEN); + if (!src) + os_memcpy(hdr->addr2, dev->nmi, ETH_ALEN); + else + os_memcpy(hdr->addr2, src, ETH_ALEN); + if (cluster_id) + os_memcpy(hdr->addr3, cluster_id, ETH_ALEN); + + wpabuf_put_data(data, wpabuf_head(buf), wpabuf_len(buf)); + + /* Prepare action to send Tx status */ + tx_status = os_malloc(sizeof(struct nan_test_tx_status_action)); + if (!tx_status) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate Tx status", + __func__); + goto fail; + } + + tx_status->data = wpabuf_dup(data); + if (!tx_status->data) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate Tx status data", + __func__); + goto fail; + } + + os_memcpy(tx_status->dst, dst, ETH_ALEN); + tx_status->acked = 1; + + /* First send the TX status */ + dev_action = nan_test_add_action(dev->global, dev, + nan_test_tx_status_action, tx_status); + if (!dev_action) + goto fail; + + /* And then deliver the frame */ + cur_action = nan_test_add_action(curd->global, curd, + nan_test_send_naf_cb_action, data); + if (!cur_action) + goto fail; + + return 0; + +fail: + wpabuf_free(data); + if (tx_status) + wpabuf_free((struct wpabuf *) tx_status->data); + + os_free(tx_status); + + return -1; +} + + +/** + * nan_test_get_chans_cb - Get NAN supported channels callback + * @ctx: Pointer to &struct nan_device + * @map_id: Channel map identifier + * @chans: Pointer to &struct nan_channels to be filled with supported channels + */ +static int nan_test_get_chans_cb(void *ctx, u8 map_id, + struct nan_channels *chans) +{ + struct nan_device *dev = ctx; + + DEV_NOT_INIT_ERR(dev); + + wpa_printf(MSG_INFO, "%s: %s: Enter", dev->name, __func__); + + return dev->conf->get_chans_cb(chans); +} + + +/** + * nan_test_is_valid_publish_id_cb - Check if the publish instance ID is valid + * @ctx: Pointer to &struct nan_device + * @instance_id: Publish instance ID + * @service_id: Buffer to be filled with the service ID + * Returns true if the instance ID is valid, false otherwise + */ +static bool nan_test_is_valid_publish_id_cb(void *ctx, u8 instance_id, + u8 *service_id) +{ + if (instance_id != NAN_TEST_PUBLISH_INST_ID) + return false; + + os_memset(service_id, 0xaa, NAN_SERVICE_ID_LEN); + return true; +} + + +/* + * nan_test_set_peer_schedule_cb - Set peer schedule callback + * + * @ctx: Pointer to &struct nan_device + * @sched: Pointer to &struct nan_peer_schedule to be filled + */ +static int +nan_test_set_peer_schedule_cb(void *ctx, const u8 *nmi_addr, bool new_sta, + u16 cdw, u8 sequence_id, + u16 max_channel_switch_time, + const struct nan_peer_schedule *sched, + const struct wpabuf *ulw_elems) +{ + struct nan_device *dev = (struct nan_device *)ctx; + + DEV_NOT_INIT_ERR(dev); + + wpa_printf(MSG_INFO, "%s: %s: Enter", dev->name, __func__); + wpa_printf(MSG_INFO, "%s: nmi_addr=" MACSTR + " new_sta=%d cdw=%u sequence_id=%u max_channel_switch_time=%u", + dev->name, MAC2STR(nmi_addr), new_sta, cdw, sequence_id, + max_channel_switch_time); + + return 0; +} + + +/** + * nan_test_dev_init - Initialize a test device instance + * @dev: the instance of the device to initialize + */ +static int nan_test_dev_init(struct nan_device *dev) +{ + struct nan_config nan; + + os_memset(&nan, 0, sizeof(nan)); + os_memcpy(nan.nmi_addr, dev->nmi, ETH_ALEN); + nan.cb_ctx = dev; + + nan.start = nan_test_start_cb; + nan.stop = nan_test_stop_cb; + nan.ndp_action_notif = nan_test_ndp_action_notfi_cb; + nan.ndp_connected = nan_test_ndp_connected_cb; + nan.ndp_disconnected = nan_test_ndp_disconnected_cb; + nan.send_naf = nan_test_send_naf_cb; + nan.get_chans = nan_test_get_chans_cb; + nan.is_valid_publish_id = nan_test_is_valid_publish_id_cb; + nan.set_peer_schedule = nan_test_set_peer_schedule_cb; + + /* Awake on every DW on 2 GHz and 5 GHz */ + nan.dev_capa.cdw_info = 0x9; + nan.dev_capa.supported_bands = NAN_DEV_CAPA_SBAND_2G | + NAN_DEV_CAPA_SBAND_5G | + NAN_DEV_CAPA_SBAND_6G; + + nan.dev_capa.op_mode = NAN_DEV_CAPA_OP_MODE_PHY_MODE; + nan.dev_capa.n_antennas = 0x22; + nan.dev_capa.channel_switch_time = 10; + nan.dev_capa.capa = 0; + + nan.dev_capa_ext_reg_info = 0; + nan.pairing_cfg.npk_caching = true; + nan.pairing_cfg.pairing_setup = true; + + dev->nan = nan_init(&nan); + if (!dev->nan) { + wpa_printf(MSG_DEBUG, "NAN: Failed to init"); + return -1; + } + + return 0; +} + + +/** + * nan_test_start_dev - Start a NAN test device + * @global: NAN test global data structure + * @name: Name of the device + * @nmi: NAN Management interface address + * @ndi: NAN Data interface address + * @cconf: NAN cluster configuration + * @dconf: Test device configuration + */ +static struct nan_device * +nan_test_start_dev(struct nan_test_global *global, + const char *name, const u8 *nmi, + const u8 *ndi, + struct nan_cluster_config *conf, + const struct nan_test_dev_conf *dconf) +{ + struct nan_device *dev; + int ret; + size_t nlen; + + dev = os_zalloc(sizeof(struct nan_device)); + if (!dev) + return NULL; + + nlen = os_strlen(name); + if (nlen >= sizeof(dev->name)) + nlen = sizeof(dev->name) - 1; + os_memcpy(dev->name, name, nlen); + os_memcpy(dev->nmi, nmi, sizeof(dev->nmi)); + os_memcpy(dev->ndi, ndi, sizeof(dev->ndi)); + dl_list_init(&dev->list); + dev->global = global; + + if (dconf->pot_avail_len) { + dev->pot_avail = os_memdup(dconf->pot_avail, + dconf->pot_avail_len); + if (!dev->pot_avail) + goto fail; + dev->pot_avail_len = dconf->pot_avail_len; + } + + ret = nan_test_dev_init(dev); + if (ret) + goto fail; + + dev->conf = dconf; + ret = nan_start(dev->nan, conf); + if (ret) + goto fail; + + dl_list_add(&global->devs, &dev->list); + return dev; + +fail: + nan_test_dev_deinit(dev); + os_free(dev); + return NULL; +} + + +/** + * nan_test_setup_devices - Setup the test devices + * @global: NAN test global data structure + * @pub_conf: Publisher test configuration + * @sub_conf: Subscriber test configuration + */ +static struct nan_device * +nan_test_setup_devices(struct nan_test_global *global, + const struct nan_test_dev_conf *pub_conf, + const struct nan_test_dev_conf *sub_conf) +{ + struct nan_cluster_config cconf = { + .master_pref = 2, + .dual_band = 1, + }; + /* + * Device attributes containing: + * 1. Device capability attribute (ID=0x0F, len=10): + * - map_id=0 + * - cdw_info=0x0009 (awake on every DW on 2G and 5G) + * - supported_bands=0x07 (2G, 5G, 6G) + * - op_mode=0x01 + * - n_antennas=0x22 + * - channel_switch_time=0x000a (10) + * - capa=0x00 + * 2. Availability attribute (ID=0x12, len=12) + */ + const u8 attrs[] = { + /* Device capability attribute */ + 0x0f, 0x09, 0x00, 0x00, 0x09, 0x00, 0x07, 0x01, + 0x22, 0x0a, 0x00, 0x00, + /* Availability attribute */ + 0x12, 0x0c, 0x00, 0x01, 0x20, 0x00, 0x07, 0x00, + 0x1a, 0x00, 0x11, 0x51, 0xff, 0x07, 0x00, + }; + + struct nan_device *pub, *sub; + + wpa_printf(MSG_INFO, "%s: Enter\n", __func__); + + pub = nan_test_start_dev(global, "publisher", g_pub_nmi, g_pub_ndi, + &cconf, pub_conf); + if (!pub) + goto fail; + + sub = nan_test_start_dev(global, "subscriber", g_sub_nmi, g_sub_ndi, + &cconf, sub_conf); + if (!sub) + goto fail; + + nan_add_peer(pub->nan, g_sub_nmi, attrs, sizeof(attrs)); + nan_add_peer(sub->nan, g_pub_nmi, attrs, sizeof(attrs)); + + wpa_printf(MSG_INFO, "\n%s: Done\n", __func__); + return sub; + +fail: + wpa_printf(MSG_INFO, "\n%s: Fail\n", __func__); + return NULL; +} + + +static int nan_test_ndp_request(struct nan_device *sub, const u8 *pub_nmi) +{ + struct nan_ndp_params *params; + struct nan_test_action *action; + + DEV_NOT_INIT_ERR(sub); + + params = os_zalloc(sizeof(struct nan_ndp_params)); + if (!params) { + wpa_printf(MSG_ERROR, "Failed allocation: nan_ndp_params"); + return -1; + } + + params->type = NAN_NDP_ACTION_REQ; + os_memcpy(params->ndp_id.peer_nmi, pub_nmi, ETH_ALEN); + os_memcpy(params->ndp_id.init_ndi, sub->ndi, ETH_ALEN); + params->ndp_id.id = ++sub->counter; + params->qos.min_slots = NAN_TEST_MIN_SLOTS; + params->qos.max_latency = NAN_TEST_MAX_LATENCY; + params->sec.csid = sub->conf->ndp_confs[sub->n_ndps].csid; + os_memcpy(params->sec.pmk, sub->conf->pmk, PMK_LEN); + + /* Use the device specific schedule callback */ + sub->conf->schedule_cb(¶ms->sched); + params->sched_valid = 1; + params->sched.elems = sub->global->elems; + + params->u.req.publish_inst_id = NAN_TEST_PUBLISH_INST_ID; + os_memset(params->u.req.service_id, 0xaa, NAN_SERVICE_ID_LEN); + + action = nan_test_add_action(sub->global, sub, nan_test_nan_ndp_action, + params); + if (action) + return 0; + + wpa_printf(MSG_ERROR, "Failed adding NDP request action"); + os_free(params); + + return -1; +} + + +/** + * nan_test_ndp_setup - test NDP setup + * @global: NAN test global data structure + * @pub_conf: Publisher test configuration + * @sub_conf: Subscriber test configuration + * + * Create the test devices, perform the basic publish/subscribe/match to allow + * NDP establishment and trigger an NDP request flow based on the actions + * mechanism. + */ +static struct nan_device * +nan_test_ndp_setup(struct nan_test_global *global, + const struct nan_test_dev_conf *pub_conf, + const struct nan_test_dev_conf *sub_conf) +{ + if (pub_conf->n_ndps != sub_conf->n_ndps || !pub_conf->n_ndps || + pub_conf->n_ndps >= NAN_MAX_NUM_NDPS) { + wpa_printf(MSG_DEBUG, + "NAN Test: Publisher and Subscriber n_ndps mismatch or invalid"); + return NULL; + } + + return nan_test_setup_devices(global, pub_conf, sub_conf); +} + + +static int nan_test_verify_expected_result(struct nan_device *dev) +{ + DEV_NOT_INIT_ERR(dev); + + if (dev->conf->ndp_confs[dev->n_ndps].expected_result == + NAN_TEST_NDP_NOTIFY_CONNECTED && + !dev->connected_notify_received) { + wpa_printf(MSG_ERROR, + "%s: Expected connected notify not received", + dev->name); + return -1; + } + + if (dev->conf->ndp_confs[dev->n_ndps].expected_result == + NAN_TEST_NDP_NOTIFY_DISCONNECTED && + !dev->disconnected_notify_received) { + wpa_printf(MSG_ERROR, + "%s: Expected disconnected notify not received", + dev->name); + return -1; + } + + return 0; +} + + +static int nan_test_iteration_done(struct nan_test_global *global) +{ + struct nan_device *dev; + u8 tk[NAN_TK_MAX_LEN]; + size_t tk_len = 0; + enum nan_cipher_suite_id csid; + int ret; + + os_memset(tk, 0, sizeof(tk)); + + dl_list_for_each(dev, &global->devs, struct nan_device, list) { + ret = nan_test_verify_expected_result(dev); + + if (ret) + return ret; + + if (tk_len == 0 && dev->tk_len > 0) { + os_memcpy(tk, dev->tk, dev->tk_len); + tk_len = dev->tk_len; + csid = dev->csid; + } else if (dev->tk_len > 0) { + if (tk_len != dev->tk_len || + csid != dev->csid || + os_memcmp(tk, dev->tk, tk_len) != 0) { + wpa_printf(MSG_ERROR, + "%s: TK mismatch with other device", + dev->name); + return -1; + } + + wpa_printf(MSG_INFO, "%s: TK matches with other device", + dev->name); + } + + dev->connected_notify_received = false; + dev->disconnected_notify_received = false; + + os_memset(dev->tk, 0, sizeof(dev->tk)); + dev->tk_len = 0; + + dev->n_ndps++; + } + + return 0; +} + + +/** + * nan_test_run - Run the NAN tests + * Iterates over all tests cases and for each test case initializes the global + * context, creates the NAN devices and perform the test case. + */ +static int nan_test_run(void) +{ + struct nan_test_global global; + const struct nan_test_case *curr_tc; + bool all_failed = false; + + while ((curr_tc = nan_test_case_get_next())) { + struct nan_device *sub; + bool failed = false; + size_t i; + + wpa_printf(MSG_INFO, + "\n======> NAN TEST CASE: %s <======\n", + curr_tc->name); + + nan_test_global_init(&global); + sub = nan_test_ndp_setup(&global, + &curr_tc->pub_conf, + &curr_tc->sub_conf); + if (!sub) { + wpa_printf(MSG_ERROR, + "NAN Test: Failed to setup devices"); + nan_test_global_deinit(&global); + failed = true; + continue; + } + + for (i = 0; i < sub->conf->n_ndps; i++) { + int ret = nan_test_ndp_request(sub, g_pub_nmi); + + if (!ret) + ret = nan_test_run_actions(&global); + + if (!ret) + ret = nan_test_iteration_done(&global); + + wpa_printf(MSG_INFO, + "\n======> NAN TEST CASE: %s: iter=%zu: result=%s <======\n", + curr_tc->name, i, + ret ? "FAILED" : "SUCCESS"); + + if (ret) { + failed = true; + break; + } + } + + wpa_printf(MSG_INFO, + "\n======> NAN TEST CASE: %s: Done. Result=%s <======\n", + curr_tc->name, failed ? "FAILED" : "SUCCESS"); + + all_failed |= failed; + + nan_test_global_deinit(&global); + } + + return all_failed ? -1 : 0; +} + + +static const u8 nan_key[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, +}; + +static u8 nan_addrs[][ETH_ALEN] = { + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + }, + { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + }, +}; + +static u8 nan_nonce[][WPA_NONCE_LEN] = { + { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + }, + { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }, +}; + +static struct nan_ptk nan_ptk_128 = { + .kck = { + 0x67, 0x40, 0x47, 0x6f, 0x9c, 0xb4, 0xcd, 0xee, + 0xfb, 0xc7, 0xad, 0x25, 0x78, 0xd5, 0x5e, 0xb2, + }, + .kek = { + 0xe5, 0xc7, 0x55, 0xbb, 0x4e, 0x4f, 0xd7, 0xbb, + 0xdb, 0x19, 0xa5, 0xb3, 0x6c, 0xcc, 0x77, 0xe3, + }, + .tk = { + 0xe2, 0x7f, 0xfc, 0x7b, 0xa2, 0xd6, 0xb2, 0x44, + 0x63, 0x4a, 0x0c, 0xf2, 0x09, 0xf8, 0x06, 0x9a, + }, + .kck_len = 16, + .kek_len = 16, + .tk_len = 16, +}; + +static struct nan_ptk nan_ptk_256 = { + .kck = { + 0x72, 0x70, 0xd5, 0xdc, 0x05, 0x44, 0x8a, 0xb4, + 0x1a, 0x73, 0xe5, 0x51, 0x56, 0xa0, 0x78, 0x3a, + 0x33, 0x74, 0x62, 0x4e, 0x81, 0xe0, 0x39, 0xf8, + }, + .kek = { + 0x55, 0x3e, 0x2c, 0x8a, 0x0b, 0xab, 0xfc, 0xe1, + 0xf9, 0x45, 0xfb, 0xa7, 0xa6, 0xc9, 0x0c, 0x77, + 0x76, 0x64, 0x6b, 0xaa, 0xc0, 0x59, 0x7c, 0xd6, + 0xa4, 0x99, 0x31, 0xf2, 0x6e, 0xbf, 0x0e, 0x49, + }, + .tk = { + 0x44, 0x61, 0x12, 0xba, 0x4c, 0x25, 0x8b, 0xe5, + 0x29, 0x33, 0x8f, 0x77, 0x45, 0x55, 0x13, 0x87, + 0x69, 0x61, 0xf4, 0x33, 0xad, 0x87, 0x47, 0xbb, + 0xa1, 0xd6, 0x5b, 0x4d, 0xaf, 0x0a, 0x37, 0x5a + }, + .kck_len = 24, + .kek_len = 32, + .tk_len = 32, +}; + +static const u8 nan_service_id[NAN_SERVICE_ID_LEN] = { + 0x48, 0xda, 0x63, 0xdc, 0xde, 0x19, +}; + +static u8 nan_pmkid_128[PMKID_LEN] = { + 0xcf, 0x4f, 0x64, 0x44, 0xd8, 0xe2, 0xc4, 0xef, + 0xa4, 0xf3, 0xa6, 0x51, 0xf3, 0x0f, 0x63, 0x4f, +}; + +static u8 nan_pmkid_256[PMKID_LEN] = { + 0x08, 0x11, 0x23, 0xc8, 0x57, 0x52, 0x6a, 0x09, + 0x6d, 0x46, 0x48, 0x2b, 0xa8, 0x02, 0xfc, 0x28, +}; + +static u8 nan_crypto_sha256_digest[SHA256_MAC_LEN] = { + 0xde, 0x7a, 0xa9, 0x34, 0x0d, 0x1d, 0xbb, 0x73, + 0x4d, 0x4d, 0x5a, 0xcb, 0x6b, 0xe4, 0xed, 0xc9, + 0x0a, 0x16, 0x37, 0xaa, 0x1e, 0x5a, 0x34, 0x2e, + 0x58, 0xdd, 0x0b, 0x30, 0x4b, 0xa4, 0x8c, 0x32, +}; + +static u8 nan_crypto_sha384_digest[SHA384_MAC_LEN] = { + 0xb1, 0xf9, 0x75, 0xbd, 0x80, 0x84, 0x21, 0x35, + 0xc2, 0x38, 0xf7, 0x0d, 0x03, 0x32, 0x38, 0xe7, + 0xbb, 0x55, 0x39, 0x21, 0xf5, 0xb3, 0xbd, 0x5c, + 0x93, 0x78, 0x52, 0x61, 0x6f, 0x83, 0x58, 0x2d, + 0xdc, 0x0d, 0xe2, 0x25, 0x5e, 0x72, 0x6c, 0x95, + 0xd0, 0xcb, 0x09, 0xef, 0x2a, 0x8f, 0x08, 0x17, +}; + +static const char sha_data[] = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + +static u8 sha256_digest[] = { + 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1, +}; + +static u8 sha384_digest[] = { + 0x33, 0x91, 0xfd, 0xdd, 0xfc, 0x8d, 0xc7, 0x39, + 0x37, 0x07, 0xa6, 0x5b, 0x1b, 0x47, 0x09, 0x39, + 0x7c, 0xf8, 0xb1, 0xd1, 0x62, 0xaf, 0x05, 0xab, + 0xfe, 0x8f, 0x45, 0x0d, 0xe5, 0xf3, 0x6b, 0xc6, + 0xb0, 0x45, 0x5a, 0x85, 0x20, 0xbc, 0x4e, 0x6f, + 0x5f, 0xe9, 0x5b, 0x1f, 0xe3, 0xc8, 0x45, 0x2b, +}; + +#define NAN_CRYPTO_FAIL(_ret) \ + if (_ret) { \ + wpa_printf(MSG_ERROR, \ + "NAN Crypto test fail: %s:%d", \ + __func__, __LINE__); \ + return -1; \ + } + + +static int nan_test_crypto_key_mic(void) +{ + u8 digest[SHA384_MAC_LEN]; + u8 *data = nan_nonce[0]; + size_t data_len = sizeof(nan_nonce[0]); + + NAN_CRYPTO_FAIL(nan_crypto_key_mic(data, data_len, nan_key, + sizeof(nan_key), NAN_CS_SK_CCM_128, + digest)); + + NAN_CRYPTO_FAIL(os_memcmp(digest, nan_crypto_sha256_digest, + NAN_KEY_MIC_LEN)); + + os_memset(digest, 0, sizeof(digest)); + + NAN_CRYPTO_FAIL(nan_crypto_key_mic(data, data_len, nan_key, + sizeof(nan_key), NAN_CS_SK_GCM_256, + digest)); + NAN_CRYPTO_FAIL(os_memcmp(digest, nan_crypto_sha384_digest, + NAN_KEY_MIC_24_LEN)); + + return 0; +} + + +static int nan_test_crypto_pmkid(void) +{ + u8 pmkid[PMKID_LEN]; + + NAN_CRYPTO_FAIL(nan_crypto_calc_pmkid(nan_key, nan_addrs[0], + nan_addrs[1], nan_service_id, + NAN_CS_SK_CCM_128, pmkid)); + + NAN_CRYPTO_FAIL(os_memcmp(pmkid, nan_pmkid_128, sizeof(pmkid))); + + NAN_CRYPTO_FAIL(nan_crypto_calc_pmkid(nan_key, nan_addrs[0], + nan_addrs[1], nan_service_id, + NAN_CS_SK_GCM_256, pmkid)); + + NAN_CRYPTO_FAIL(os_memcmp(pmkid, nan_pmkid_256, sizeof(pmkid))); + + return 0; +} + + +static int nan_test_ptk(void) +{ + struct nan_ptk tptk; + + os_memset(&tptk, 0, sizeof(tptk)); + + NAN_CRYPTO_FAIL(nan_crypto_pmk_to_ptk(nan_key, + nan_addrs[0], nan_addrs[1], + nan_nonce[0], nan_nonce[1], + &tptk, NAN_CS_SK_CCM_128)); + + NAN_CRYPTO_FAIL(os_memcmp(&tptk, &nan_ptk_128, sizeof(tptk))); + + os_memset(&tptk, 0, sizeof(tptk)); + + NAN_CRYPTO_FAIL(nan_crypto_pmk_to_ptk(nan_key, + nan_addrs[0], nan_addrs[1], + nan_nonce[0], nan_nonce[1], + &tptk, NAN_CS_SK_GCM_256)); + + NAN_CRYPTO_FAIL(os_memcmp(&tptk, &nan_ptk_256, sizeof(tptk))); + + return 0; +} + + +static int nan_test_crypto_auth_token(void) +{ + u8 digest[SHA384_MAC_LEN]; + const u8 *data = (const u8 *) sha_data; + size_t len = os_strlen(sha_data); + + NAN_CRYPTO_FAIL(nan_crypto_calc_auth_token(NAN_CS_SK_CCM_128, data, + len, digest)); + NAN_CRYPTO_FAIL(os_memcmp(digest, sha256_digest, NAN_AUTH_TOKEN_LEN)); + + os_memset(digest, 0, sizeof(digest)); + + NAN_CRYPTO_FAIL(nan_crypto_calc_auth_token(NAN_CS_SK_GCM_256, data, len, + digest)); + NAN_CRYPTO_FAIL(os_memcmp(digest, sha384_digest, NAN_AUTH_TOKEN_LEN)); + + return 0; +} + + +static int nan_test_derive_nd_pmk(void) +{ + u8 pmk[PMK_LEN]; + /* Wi-Fi Aware spec v4.0, Appendix M.1 - Test Vector 1 */ + const char *pwd = "NAN"; + const u8 service_id[NAN_SERVICE_ID_LEN] = { + 0x2b, 0x9c, 0x45, 0x0f, 0x66, 0x71, + }; + const u8 nmi[ETH_ALEN] = { + 0x02, 0x90, 0x4c, 0x12, 0xd0, 0x01, + }; + const u8 expected_pmk_ccm128[PMK_LEN] = { + 0xee, 0x35, 0x85, 0x06, 0x30, 0x56, 0xd1, 0x64, + 0xd1, 0x54, 0x54, 0xad, 0x39, 0x01, 0x0d, 0x4e, + 0x26, 0x40, 0xb0, 0xd8, 0x2f, 0xb2, 0x4a, 0x2d, + 0x68, 0x99, 0x86, 0x2d, 0x27, 0x3c, 0x68, 0xbf, + }; + + NAN_CRYPTO_FAIL(nan_crypto_derive_nd_pmk(pwd, service_id, + NAN_CS_SK_CCM_128, nmi, pmk)); + NAN_CRYPTO_FAIL(os_memcmp(pmk, expected_pmk_ccm128, PMK_LEN)); + + return 0; +} + +int nan_test_crypto(void) +{ + NAN_CRYPTO_FAIL(nan_test_crypto_key_mic()); + NAN_CRYPTO_FAIL(nan_test_crypto_pmkid()); + NAN_CRYPTO_FAIL(nan_test_ptk()); + NAN_CRYPTO_FAIL(nan_test_crypto_auth_token()); + NAN_CRYPTO_FAIL(nan_test_derive_nd_pmk()); + + return 0; +} + + +int nan_module_tests(void) +{ + if (nan_test_crypto()) + return -1; + + return nan_test_run(); +} diff --git a/contrib/wpa/src/nan/nan_module_tests.h b/contrib/wpa/src/nan/nan_module_tests.h new file mode 100644 index 000000000000..dd6fa4b22f1e --- /dev/null +++ b/contrib/wpa/src/nan/nan_module_tests.h @@ -0,0 +1,137 @@ +/* + * NAN NDP/NDL state machine testing definitions + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_MODULE_TESTS_H +#define NAN_MODULE_TESTS_H + +#define NAN_TEST_NAME_MAX 32 + +/** + * enum nan_test_ndp_notify_type - NDP notification + * NAN_TEST_NDP_NOTIFY_INVALID: Invalid + * NAN_TEST_NDP_NOTIFY_REQUEST: NDP request + * NAN_TEST_NDP_NOTIFY_RESPONSE: NDP response + * NAN_TEST_NDP_NOTIFY_CONNECTED: NDP connected + * NAN_TEST_NDP_NOTIFY_DISCONNECTED: NDP disconnected + */ +enum nan_test_ndp_notify_type { + NAN_TEST_NDP_NOTIFY_INVALID, + NAN_TEST_NDP_NOTIFY_REQUEST, + NAN_TEST_NDP_NOTIFY_RESPONSE, + NAN_TEST_NDP_NOTIFY_CONNECTED, + NAN_TEST_NDP_NOTIFY_DISCONNECTED, +}; + +#define NAN_TEST_MAX_POT_AVAIL 256 +#define NAN_MAX_NUM_NDPS 8 + +/** + * nan_test_dev_conf - NAN test device configuration + * @schedule_cb: Callback to configure different schedule handling policies + * for a device for initial request and response. + * @schedule_conf_cb: Callback to configure different schedule handling + * policies for a device for confirmation. + * @get_chans_cb: Callback to configure different channels for potential + * availability. + * @pot_avail: Device potential availability + * @pot_avail_len: Length of the device potential availability + * @n_ndps: Number of NDP configurations (to support scenarios that involve + * establishing multiple NDPs in a single test case). + * @ndp_confs: Array of NDP configurations. + * @accept_request: For publisher device, indicates whether to accept an NDP + * request or reject it. For subscriber device, indicates whether to accept + * an NDP response or reject it. + * @term_once_connected: Terminate once connected. + * @expected_result: Expected NDP establishment result + * @reason: For publisher device, indicates the reject reason + * @csid: Cipher suite ID + * @expected_csid: Expected Cipher suite ID in case of a successful connection + * @pmk: Pairwise Master Key + */ +struct nan_test_dev_conf { + int (*schedule_cb)(struct nan_schedule *sched); + int (*schedule_conf_cb)(struct nan_schedule *sched); + int (*get_chans_cb)(struct nan_channels *chans); + + u8 pot_avail[NAN_TEST_MAX_POT_AVAIL]; + size_t pot_avail_len; + + size_t n_ndps; + struct ndp_conf { + bool accept_request; + bool term_once_connected; + enum nan_test_ndp_notify_type expected_result; + u8 reason; + enum nan_cipher_suite_id csid; + enum nan_cipher_suite_id expected_csid; + } ndp_confs[NAN_MAX_NUM_NDPS]; + + u8 pmk[PMK_LEN]; +}; + +/** + * nan_device - Represents a NAN test device + * @list: Used for global devices list + * @global: Pointer to NAN test global data structure + * @name: Test device name + * @nmi: Test device NMI + * @ndi: Test device NDI + * @counter: Device counter for NDP various purposes + * @pot_avail: Device potential availability + * @pot_avail_len: Length of the device potential availability + * @nan: Pointer to NAN data structure + * @conf: NAN test device configuration + * @n_ndps: Number of NDPs established so far minus one + * @connected_notify_received: Indicates whether a connected notification + * was received + * @disconnected_notify_received: Indicates whether a disconnected notification + * was received + * @tk: NAN TK + * @tk_len: Length of the NAN TK + * @csid: Cipher suite ID + */ +struct nan_device { + struct dl_list list; + struct nan_test_global *global; + + u8 name[NAN_TEST_NAME_MAX]; + u8 nmi[ETH_ALEN]; + u8 ndi[ETH_ALEN]; + u32 counter; + + u8 *pot_avail; + size_t pot_avail_len; + + struct nan_data *nan; + const struct nan_test_dev_conf *conf; + + size_t n_ndps; + + bool connected_notify_received; + bool disconnected_notify_received; + + u8 tk[NAN_TK_MAX_LEN]; + size_t tk_len; + enum nan_cipher_suite_id csid; +}; + +/** + * nan_test_case - Single NAN test configuration + * @name: Test name + * @pub_conf: Publisher test configuration + * @sub_conf: Subscriber test configuration + */ +struct nan_test_case { + const char *name; + struct nan_test_dev_conf pub_conf; + struct nan_test_dev_conf sub_conf; +}; + +const struct nan_test_case * nan_test_case_get_next(void); + +#endif /* NAN_MODULE_TESTS_H */ diff --git a/contrib/wpa/src/nan/nan_ndl.c b/contrib/wpa/src/nan/nan_ndl.c new file mode 100644 index 000000000000..ce0e212cbefb --- /dev/null +++ b/contrib/wpa/src/nan/nan_ndl.c @@ -0,0 +1,1724 @@ +/* + * Wi-Fi Aware - NAN Data link + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "utils/bitfield.h" +#include "common/ieee802_11_common.h" +#include "nan_i.h" + + +struct ndl_attr_params { + u8 dialog_token; + u8 type; + u8 status; + u8 reason; + enum nan_ndl_setup_reason setup_reason; + + u16 max_idle_period; + u8 min_slots; + u16 max_latency; + + u8 ndc_id[ETH_ALEN]; + const u8 *ndc_sched; + u16 ndc_sched_len; + + const u8 *immut_sched; u16 immut_sched_len; +}; + + +static const char * nan_ndl_state_str(enum nan_ndl_state state) +{ +#define C2S(x) case x: return #x; + switch (state) { + C2S(NAN_NDL_STATE_NONE) + C2S(NAN_NDL_STATE_START) + C2S(NAN_NDL_STATE_REQ_SENT) + C2S(NAN_NDL_STATE_REQ_RECV) + C2S(NAN_NDL_STATE_RES_SENT) + C2S(NAN_NDL_STATE_RES_RECV) + C2S(NAN_NDL_STATE_CON_SENT) + C2S(NAN_NDL_STATE_CON_RECV) + C2S(NAN_NDL_STATE_DONE) + default: + return "Invalid NAN NDL state"; + } +} + + +static void nan_ndl_set_state(struct nan_data *nan, struct nan_ndl *ndl, + enum nan_ndl_state state) +{ + wpa_printf(MSG_DEBUG, "NAN: NDL: State %s (%u) --> %s (%u)", + nan_ndl_state_str(ndl->state), ndl->state, + nan_ndl_state_str(state), state); + + ndl->state = state; +} + + +static void nan_ndl_time_bitmap_print(struct nan_data *nan, + struct nan_time_bitmap *tbm, + const char *type) +{ + if (!tbm->len) + return; + + wpa_printf(MSG_DEBUG, + "channel sched: %s: dur=%u, per=%u, off=%u, len=%u", + type, tbm->duration, tbm->period, tbm->offset, tbm->len); + + wpa_printf(MSG_DEBUG, "bitmap[0:3]: 0x%x:0x%x:0x%x:0x%x", + tbm->bitmap[0], tbm->bitmap[1], + tbm->bitmap[2], tbm->bitmap[3]); +} + + +static void nan_ndl_chan_sched_print(struct nan_data *nan, size_t idx, + struct nan_chan_schedule *cs) +{ + wpa_printf(MSG_DEBUG, "NAN: sched: index=%zu, map_id=%u", + idx, cs->map_id); + + wpa_printf(MSG_DEBUG, + "channel sched: chan: freq=%u, c1=%d, c2=%d, bandwidth=%d", + cs->chan.freq, cs->chan.center_freq1, cs->chan.center_freq2, + cs->chan.bandwidth); + + nan_ndl_time_bitmap_print(nan, &cs->committed, "committed"); + nan_ndl_time_bitmap_print(nan, &cs->conditional, "conditional"); +} + + +static void nan_ndl_sched_print(struct nan_data *nan, + struct nan_schedule *sched) +{ + size_t i; + + wpa_printf(MSG_DEBUG, + "NAN: sched: map_ids=0x%x, n_chans=%u, seq_id=%u", + sched->map_ids_bitmap, + sched->n_chans, sched->sequence_id); + + for (i = 0; i < sched->n_chans; i++) + nan_ndl_chan_sched_print(nan, i, &sched->chans[i]); +} + + +static void nan_ndl_clear(struct nan_data *nan, struct nan_peer *peer) +{ + struct nan_ndl *ndl = peer->ndl; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Clear info for peer=" MACSTR " state=%s (%u)", + MAC2STR(peer->nmi_addr), + nan_ndl_state_str(peer->ndl->state), peer->ndl->state); + + nan_clear_peer_schedule(nan, peer); + + os_free(ndl->ndc_sched); + ndl->ndc_sched = NULL; + ndl->ndc_sched_len = 0; + + os_free(ndl->immut_sched); + ndl->immut_sched = NULL; + ndl->immut_sched_len = 0; + + ndl->dialog_token = 0; + ndl->max_idle_period = 0; + + os_memset(ndl->ndc_id, 0, sizeof(ndl->ndc_id)); + + ndl->peer_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + ndl->local_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + + ndl->setup_reason = NAN_NDL_SETUP_REASON_NONE; +} + + +/* + * nan_ndl_reset - Reset the NDL state + * + * @nan: NAN module context from nan_init() + * @peer: The peer that requires NDL setup reset + */ +void nan_ndl_reset(struct nan_data *nan, struct nan_peer *peer) +{ + wpa_printf(MSG_DEBUG, "NAN: NDL: Reset state for peer=" MACSTR, + MAC2STR(peer->nmi_addr)); + + if (!peer->ndl) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Reset: no NDL"); + return; + } + + nan_ndl_clear(nan, peer); + + os_free(peer->ndl); + peer->ndl = NULL; +} + + +static struct nan_ndl * nan_ndl_alloc(struct nan_data *nan) +{ + struct nan_ndl *ndl; + + wpa_printf(MSG_DEBUG, "NAN: NDL: Allocating a new NDL"); + + ndl = os_zalloc(sizeof(struct nan_ndl)); + if (!ndl) { + wpa_printf(MSG_INFO, "NAN: NDL: Failed to allocate NDL"); + return NULL; + } + + ndl->status = NAN_NDL_STATUS_ACCEPTED; + ndl->reason = NAN_REASON_RESERVED; + + ndl->local_qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF; + ndl->local_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + + ndl->peer_qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF; + ndl->peer_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_NONE); + + return ndl; +} + + +bool nan_ndl_validate_peer_avail(struct nan_data *nan, struct nan_peer *peer) +{ + struct nan_ndl *ndl = peer->ndl; + bool ret; + + /* First, validate if immutable is covered by the availability map */ + ret = nan_sched_covered_by_avail_entries(nan, &peer->info.avail_entries, + ndl->immut_sched, + ndl->immut_sched_len); + if (!ret) { + wpa_printf(MSG_DEBUG, + "NAN: Peer avail: Immutable is not covered by avail"); + peer->ndl->reason = NAN_REASON_IMMUTABLE_UNACCEPTABLE; + return ret; + } + + /* Now validate NDC schedule is covered by the availability map */ + ret = nan_sched_covered_by_avail_entries(nan, &peer->info.avail_entries, + ndl->ndc_sched, + ndl->ndc_sched_len); + if (!ret) { + wpa_printf(MSG_DEBUG, + "NAN: Peer avail: NDC is not covered by avail"); + peer->ndl->reason = NAN_REASON_NDL_UNACCEPTABLE; + return ret; + } + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Peer NDC and immutable are covered by avail"); + + return ret; +} + + +/** + * enum nan_ndl_ver - Verdict of comparing local availability to given schedule + * @NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL: The schedule is a subset of the local one + * @NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL: The schedule is a superset of the local + * one + * @NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL: The schedule is identical to the local + * @NAN_NDL_VER_SCHED_NONE: None of the above. This means that there was no + * match or an error occurred. + */ +enum nan_ndl_ver { + NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL, + NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL, + NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL, + NAN_NDL_VER_SCHED_NONE, +}; + + +/* + * nan_ndl_match_sched_vs_common - Compare the given schedule to the common + * availability of the local device and the peer device, comparing channel + * configuration and bitmap. + * + * @nan: NAN module context from nan_init() + * @avail_entries: Peer availability entries + * @sched: A byte array with 0 or more &struct nan_sched_entry entries + * @sched_len: Length of the &sched array + * @common_bf: Bitfield representing the intersection of local and peer + * availability + * @op_class: Local operating class + * @cbm: Local channel bitmap + * + * Returns one of enum nan_ndl_ver. In case of on an error + * NAN_NDL_VER_SCHED_NONE is returned. + * + * The function first verifies that the given schedule is covered by the peer + * availability. Then, the function verifies that the schedule is covered by + * the common availability between local and peer. + * + * The function can (and should be used) to check the schedule constraints of + * an NDC schedule and/or immutable schedule. + * + * Note: In case that &sched is NULL or &sched_len is 0, returns + * NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL, meaning no constraints. + */ +static enum nan_ndl_ver +nan_ndl_match_sched_vs_common(struct nan_data *nan, + struct dl_list *avail_entries, + const u8 *sched, size_t sched_len, + const struct bitfield *common_bf, + u8 op_class, u16 cbm) +{ + struct dl_list sched_entries; + struct bitfield *sched_bf; + u8 map_id; + enum nan_ndl_ver verdict; + int ret; + enum nan_reason reason; + + /* No constraints */ + if (!sched || !sched_len) + return NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL; + + /* Convert the schedule entries to availability entries */ + ret = nan_sched_entries_to_avail_entries(nan, &sched_entries, + sched, sched_len); + if (ret) + return NAN_NDL_VER_SCHED_NONE; + + /* Convert the schedule availability entries to map ID and bitfield */ + sched_bf = nan_sched_to_bf(nan, &sched_entries, &map_id, &reason); + nan_flush_avail_entries(&sched_entries); + + /* + * Now that the schedule is represented as a bitfield and the map ID is + * obtained, compare these against the peer availability entries and + * channel configuration. A successful match means that the schedule is + * covered by the peer availability entries for the given channel. + */ + ret = nan_sched_bf_covered_by_avail_entries_and_chan( + nan, avail_entries, sched_bf, map_id, op_class, cbm); + if (ret) { + int ret2; + + /* + * After validating the schedule against the peer availability, + * match the peer availability with the local one. + */ + ret = bitfield_is_subset(common_bf, sched_bf); + ret2 = bitfield_is_subset(sched_bf, common_bf); + + if (ret < 0 || ret2 < 0) + verdict = NAN_NDL_VER_SCHED_NONE; + else if (ret == 1 && ret2 == 1) + verdict = NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL; + else if (ret == 1) + verdict = NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL; + else + verdict = NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL; + } else { + verdict = NAN_NDL_VER_SCHED_NONE; + } + + bitfield_free(sched_bf); + return verdict; +} + + +bool nan_ndl_meets_qos(struct nan_data *nan, const struct nan_peer *peer, + const struct bitfield *common_bf) +{ + size_t size, max_latency, i; + u16 crbs; + + /* No QoS requirements */ + if (peer->ndl->peer_qos.min_slots == NAN_QOS_MIN_SLOTS_NO_PREF && + peer->ndl->peer_qos.max_latency == NAN_QOS_MAX_LATENCY_NO_PREF) { + wpa_printf(MSG_DEBUG, "NAN: No QoS requirements from peer"); + return true; + } + + size = bitfield_size(common_bf); + + /* + * The common map covers an entire 8192 period with 16 TU slots. For + * minimal time slots need to only consider the first 32 slots + */ + for (i = 0, crbs = 0, max_latency = 0; i < size; i++) { + if (bitfield_is_set(common_bf, i)) { + if (i < 32) + crbs++; + + max_latency = 0; + } else if (peer->ndl->peer_qos.max_latency != + NAN_QOS_MAX_LATENCY_NO_PREF) { + max_latency++; + if (max_latency > peer->ndl->peer_qos.max_latency) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to meet max latency"); + return false; + } + } + } + + if (peer->ndl->peer_qos.min_slots != NAN_QOS_MIN_SLOTS_NO_PREF && + peer->ndl->peer_qos.min_slots >= crbs) { + wpa_printf(MSG_DEBUG, "NAN: Failed to meet min slots"); + return false; + } + + return true; +} + + +static enum nan_ndl_status nan_ndl_determine_status(struct nan_data *nan, + struct nan_peer *peer, + bool can_counter, + enum nan_reason *reason) +{ + struct nan_schedule *sched = &nan->sched; + struct bitfield *common_bf = NULL, *ndc_bf = NULL, *track_ndc_bf = NULL; + enum nan_ndl_ver verdict; + size_t i; + int ret; + + *reason = NAN_REASON_RESERVED; + + ndc_bf = nan_tbm_to_bf(nan, &sched->ndc); + if (!ndc_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to build NDC bitfield from schedule"); + + *reason = NAN_REASON_UNSPECIFIED_REASON; + return NAN_NDL_STATUS_REJECTED; + } + + track_ndc_bf = bitfield_alloc(bitfield_size(ndc_bf)); + if (!track_ndc_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to allocate bitfield for tracking NDC"); + + bitfield_free(ndc_bf); + *reason = NAN_REASON_UNSPECIFIED_REASON; + return NAN_NDL_STATUS_REJECTED; + } + + /* + * Iterate over all the channels included in the local schedule. For + * each channel, convert the committed and conditional slots to a + * bitfield object and extract the operating class and channel bitmap. + * + * Using the operating class and channel bitmap find the peer + * availability on that channel and intersect it with the local one: + * + * - Accumulate the intersection of the NDC bitmap with the bitmap of + * all channels with the same map ID as the NDC, so that whether the + * NDC is covered by the local availability can be verified later. + * - Accumulate the intersection of local and peer availability for + * all channels, so that the QoS requirements can be verified later. + */ + wpa_printf(MSG_DEBUG, "NAN: NDL: n_chans=%u, ndc_map_id=%u", + sched->n_chans, sched->ndc_map_id); + + for (i = 0; i < sched->n_chans; i++) { + struct bitfield *own_chan_bf = NULL, *peer_chan_bf = NULL; + u16 cbm, pri_cbm; + u8 map_id, op_class; + + /* Convert the schedule for the current channel to bitfield */ + ret = nan_convert_chan_sched_to_bf(nan, &sched->chans[i], + &own_chan_bf, &map_id, + &op_class, &cbm, &pri_cbm); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to convert chan sched to bitfield"); + + *reason = NAN_REASON_UNSPECIFIED_REASON; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + + if (sched->ndc_map_id == map_id) { + struct bitfield *tmp = bitfield_dup(ndc_bf); + + if (tmp) { + bitfield_intersect_in_place(tmp, own_chan_bf); + bitfield_union_in_place(track_ndc_bf, tmp); + bitfield_free(tmp); + } + } + + /* Get the peer availability for the current channel */ + peer_chan_bf = nan_avail_entries_to_bf( + nan, &peer->info.avail_entries, + op_class, cbm, pri_cbm); + if (!peer_chan_bf) { + bitfield_free(own_chan_bf); + continue; + } + + ret = bitfield_intersect_in_place(own_chan_bf, peer_chan_bf); + bitfield_free(peer_chan_bf); + peer_chan_bf = NULL; + + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to intersect own and peer chan bitfields"); + + bitfield_free(own_chan_bf); + + *reason = NAN_REASON_INVALID_AVAILABILITY; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + + /* + * Accumulate schedule common for the local and peer device, so + * it can later be used to verify QoS requirements, etc. + */ + if (common_bf) { + ret = bitfield_union_in_place(common_bf, own_chan_bf); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to unify own chan bitfields"); + + bitfield_free(own_chan_bf); + + *reason = NAN_REASON_UNSPECIFIED_REASON; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + } else { + common_bf = bitfield_dup(own_chan_bf); + if (!common_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to dup own chan bitfield"); + + bitfield_free(own_chan_bf); + + *reason = NAN_REASON_UNSPECIFIED_REASON; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + } + + bitfield_free(own_chan_bf); + own_chan_bf = NULL; + } + + if (!common_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: No common availability between local and peer"); + + *reason = NAN_REASON_INVALID_AVAILABILITY; + ret = NAN_NDL_STATUS_CONTINUED; + goto out; + } + + /* + * Verify that the schedule NDC bitmap is covered by the local + * availability for the map used for the NDC. + */ + if (!bitfield_is_subset(ndc_bf, track_ndc_bf)) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: NDC bitmap is not covered by local availability on NDC channel"); + + *reason = NAN_REASON_UNSPECIFIED_REASON; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + + bitfield_free(ndc_bf); + ndc_bf = track_ndc_bf; + track_ndc_bf = NULL; + + /* + * In case the peer included an immutable schedule, the immutable + * schedule must be covered by the common schedule. If not, reject. + */ + verdict = nan_ndl_match_sched_vs_common(nan, + &peer->info.avail_entries, + peer->ndl->immut_sched, + peer->ndl->immut_sched_len, + common_bf, 0, 0); + if (verdict != NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL && + verdict != NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL) { + wpa_printf(MSG_DEBUG, + "NAN: Schedule does not cover immutable. Reject"); + + *reason = NAN_REASON_IMMUTABLE_UNACCEPTABLE; + ret = NAN_NDL_STATUS_REJECTED; + goto out; + } + + /* + * In case the peer included an NDC, check if it is identical to + * the locally generated one. + * + * Note: The list of peer availability entries needs to be iterated + * again, as the NDC map ID must also be matched. + */ + verdict = nan_ndl_match_sched_vs_common(nan, &peer->info.avail_entries, + peer->ndl->ndc_sched, + peer->ndl->ndc_sched_len, + ndc_bf, 0, 0); + if (verdict != NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL) { + wpa_printf(MSG_DEBUG, + "NAN: Response NDC does not match req NDC"); + + *reason = NAN_REASON_INVALID_AVAILABILITY; + ret = NAN_NDL_STATUS_CONTINUED; + goto out; + } + + if (nan_ndl_meets_qos(nan, peer, common_bf)) { + wpa_printf(MSG_DEBUG, "NAN: NDL QoS requirements met. Accept"); + ret = NAN_NDL_STATUS_ACCEPTED; + } else { + wpa_printf(MSG_DEBUG, "NAN: NDL QoS requirements not met"); + *reason = NAN_REASON_QOS_UNACCEPTABLE; + ret = NAN_NDL_STATUS_CONTINUED; + } + +out: + bitfield_free(common_bf); + bitfield_free(ndc_bf); + bitfield_free(track_ndc_bf); + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Response status=%s (%u)", + ret == NAN_NDL_STATUS_ACCEPTED ? "ACCEPTED" : + ret == NAN_NDL_STATUS_CONTINUED ? "CONTINUED" : + "REJECTED", + ret); + + if (ret == NAN_NDL_STATUS_CONTINUED && !can_counter) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot counter, change verdict to REJECTED"); + ret = NAN_NDL_STATUS_REJECTED; + } + + return ret; +} + + +/* + * nan_ndl_setup - Handle NDL setup either as an initiator or a responder + * + * @nan: NAN module context from nan_init() + * @peer: The peer for which the NDL is being setup + * @params: NDP setup request parameters + * @dialog_token: Dialog token to be used for the NDL setup messages. Should be + * used for a new NDL only. + * Returns: 0 on success, negative on failure. + + * It is possible that an NDL with the peer already exists in which case it + * would be reused. Otherwise, new NDL establishment will be started. + */ +int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer, + const struct nan_ndp_params *params, + u8 dialog_token) +{ + struct nan_ndl *ndl; + enum nan_reason reason; + + if (!peer->ndl) { + if (params->type != NAN_NDP_ACTION_REQ) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Invalid action; expecting request"); + return -1; + } + + peer->ndl = nan_ndl_alloc(nan); + if (!peer->ndl) + return -1; + + ndl = peer->ndl; + } else { + ndl = peer->ndl; + + ndl->send_naf_on_error = 0; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: peer=" MACSTR ". state=%s (%u)", + MAC2STR(peer->nmi_addr), + nan_ndl_state_str(ndl->state), + ndl->state); + + if (ndl->state == NAN_NDL_STATE_DONE) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Already established"); + return 0; + } + + if (!((params->type == NAN_NDP_ACTION_RESP && + ndl->state == NAN_NDL_STATE_REQ_RECV) || + (params->type == NAN_NDP_ACTION_CONF && + ndl->state == NAN_NDL_STATE_RES_RECV))) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Invalid action type=%u, state=%u", + params->type, ndl->state); + return -1; + } + + if (params->u.resp.status == NAN_NDP_STATUS_REJECTED) { + reason = params->u.resp.reason_code; + goto out_fail; + } + } + + if (!params->sched_valid) { + wpa_printf(MSG_DEBUG, "NAN: NDL: no valid schedule"); + reason = NAN_REASON_INVALID_PARAMETERS; + goto out_fail; + } + + wpabuf_free(nan->sched.elems); + os_memcpy(&nan->sched, ¶ms->sched, sizeof(nan->sched)); + nan_ndl_sched_print(nan, &nan->sched); + + /* Copy elems buffer */ + if (params->sched.elems) { + nan->sched.elems = + wpabuf_alloc_copy(wpabuf_head(params->sched.elems), + wpabuf_len(params->sched.elems)); + if (!nan->sched.elems) { + reason = NAN_REASON_UNSPECIFIED_REASON; + goto out_fail; + } + } + + nan_ndl_sched_print(nan, &nan->sched); + + if (is_zero_ether_addr(ndl->ndc_id)) { + if (os_get_random(ndl->ndc_id, ETH_ALEN) < 0) { + reason = NAN_REASON_UNSPECIFIED_REASON; + goto out_fail; + } + wpa_printf(MSG_DEBUG, + "NAN: NDL: generated NDC ID " MACSTR, + MAC2STR(ndl->ndc_id)); + } + + ndl->local_qos.min_slots = params->qos.min_slots; + ndl->local_qos.max_latency = params->qos.max_latency; + + if (ndl->state == NAN_NDL_STATE_NONE) { + ndl->dialog_token = dialog_token; + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_START); + ndl->status = NAN_NDL_STATUS_CONTINUED; + } else { + ndl->status = nan_ndl_determine_status(nan, peer, + ndl->state == + NAN_NDL_STATE_REQ_RECV, + &reason); + if (ndl->status == NAN_NDL_STATUS_REJECTED) + goto out_fail; + } + + ndl->setup_reason = NAN_NDL_SETUP_REASON_NDP; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: success: state=%s (%u). Dialog token=%u", + nan_ndl_state_str(ndl->state), ndl->state, + ndl->dialog_token); + + return 0; + +out_fail: + wpa_printf(MSG_DEBUG, "NAN: NDL: Failed. reason=%u", reason); + if (ndl->state == NAN_NDL_STATE_REQ_RECV || + ndl->state == NAN_NDL_STATE_RES_RECV) { + ndl->status = NAN_NDL_STATUS_REJECTED; + ndl->reason = reason; + ndl->send_naf_on_error = 1; + + /* + * Do not modify the state. Full cleanup will be done on Tx + * status handling. + */ + } else { + nan_ndl_clear(nan, peer); + } + + return -1; +} + + +/** + * nan_ndl_setup_failure - Indicate failure during NDL setup + * @nan: NAN module context from nan_init() + * @peer: NAN peer + * @reason: Failure reason + * @reset_state: Reset the NDL state if true. + */ +void nan_ndl_setup_failure(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, bool reset_state) +{ + struct nan_ndl *ndl = peer->ndl; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Setup failure: peer " MACSTR + ". state=%s (%u). reason=%u", + MAC2STR(peer->nmi_addr), nan_ndl_state_str(ndl->state), + ndl->state, reason); + + if (reset_state) { + nan_ndl_reset(nan, peer); + } else { + ndl->status = NAN_NDL_STATUS_REJECTED; + ndl->reason = reason; + } +} + + +static int nan_ndl_parse_ndc_attr(struct nan_data *nan, + const struct ieee80211_ndc *ndc_attr, + u16 ndc_attr_len, u8 *ndc_id, + const u8 **ndc_schedule, + u16 *ndc_schedule_len) +{ + u16 ext_ndc_len; + + /* Consider only the selected NDC */ + if (!(ndc_attr->ctrl & NAN_NDC_CTRL_SELECTED)) + return -1; + + if (ndc_attr_len < sizeof(*ndc_attr)) + return -1; + ext_ndc_len = ndc_attr_len - sizeof(*ndc_attr); + + if (ext_ndc_len <= sizeof(struct nan_sched_entry)) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Request with invalid len=%u", + ndc_attr_len); + return -1; + } + + os_memcpy(ndc_id, ndc_attr->ndc_id, sizeof(ndc_attr->ndc_id)); + *ndc_schedule_len = ext_ndc_len; + *ndc_schedule = (const u8 *) (ndc_attr + 1); + + wpa_printf(MSG_DEBUG, "NAN: NDL: ndc_id=" MACSTR " schedule_len=%u", + MAC2STR(ndc_id), *ndc_schedule_len); + return 0; +} + + +static int nan_ndl_parse_qos_attr(struct nan_data *nan, + const struct ieee80211_nan_qos *qos_attr, + u16 qos_attr_len, + u8 *min_slots, u16 *max_latency) +{ + *min_slots = qos_attr->min_slots; + *max_latency = le_to_host16(qos_attr->max_latency); + + wpa_printf(MSG_DEBUG, "NAN: QoS attr: min_slots=%u, max_latency=%u", + *min_slots, *max_latency); + return 0; +} + + +static int nan_ndl_attr_handle_req(struct nan_data *nan, struct nan_peer *peer, + const struct ndl_attr_params *params) +{ + struct nan_ndl *ndl; + int ret; + + wpa_printf(MSG_DEBUG, "NAN: NDL: Handle request"); + + if (peer->ndl) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Request while another establishment is ongoing"); + return -1; + } + + if (params->status != NAN_NDL_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Request with invalid status=%u", + params->status); + return -1; + } + + peer->ndl = nan_ndl_alloc(nan); + if (!peer->ndl) + return -1; + + ndl = peer->ndl; + + ndl->status = NAN_NDL_STATUS_CONTINUED; + + ndl->dialog_token = params->dialog_token; + ndl->max_idle_period = params->max_idle_period; + ndl->setup_reason = params->setup_reason; + + if (!is_zero_ether_addr(params->ndc_id)) { + os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)); + + if (params->ndc_sched && params->ndc_sched_len) { + os_free(ndl->ndc_sched); + ndl->ndc_sched_len = 0; + + ndl->ndc_sched = os_memdup(params->ndc_sched, + params->ndc_sched_len); + if (!ndl->ndc_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Failed to copy NDC schedule"); + goto fail; + } + + ndl->ndc_sched_len = params->ndc_sched_len; + } + } + + ndl->peer_qos.min_slots = params->min_slots; + ndl->peer_qos.max_latency = params->max_latency; + + if (params->immut_sched && params->immut_sched_len) { + ndl->immut_sched = os_memdup(params->immut_sched, + params->immut_sched_len); + if (!ndl->immut_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Failed to copy immutable schedule"); + goto fail; + } + ndl->immut_sched_len = params->immut_sched_len; + } + + ret = nan_ndl_validate_peer_avail(nan, peer); + if (!ret) { + ndl->status = NAN_NDL_STATUS_REJECTED; + ndl->send_naf_on_error = 1; + } + + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_REQ_RECV); + + wpa_printf(MSG_DEBUG, "NAN: NDL: Handle request done"); + return 0; + +fail: + nan_ndl_reset(nan, peer); + return -1; +} + + +static int nan_ndl_attr_handle_resp(struct nan_data *nan, struct nan_peer *peer, + const struct ndl_attr_params *params) +{ + struct nan_ndl *ndl = peer->ndl; + int ret; + + wpa_printf(MSG_DEBUG, "NAN: NDL: Handle response"); + + if (!ndl) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Unexpected response"); + return -1; + } + + if (ndl->state != NAN_NDL_STATE_REQ_SENT) { + if (ndl->state != NAN_NDL_STATE_START) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Response while state=%s (%u)", + nan_ndl_state_str(ndl->state), ndl->state); + return -1; + } + + /* + * Due to races with the driver, it is possible that the + * response is received before an ACK is indicated. Allow the + * processing of the attribute, and if all parameters are OK, + * fast forward the state machine below. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDL: Response received before Tx status"); + } + + ndl->send_naf_on_error = 0; + ndl->status = params->status; + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_RES_RECV); + + if (ndl->dialog_token != params->dialog_token) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Resp: Invalid dialog token (%u != %u)", + ndl->dialog_token, params->dialog_token); + return -1; + } + + if (params->status != NAN_NDL_STATUS_CONTINUED && + params->status != NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Resp: Rejected. status=%u, reason=%u", + params->status, params->reason); + + ndl->reason = params->reason; + ndl->status = NAN_NDL_STATUS_REJECTED; + return 0; + } + + if (is_zero_ether_addr(params->ndc_id)) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Response without NDC"); + return -1; + } + + if (os_memcmp(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)) != 0) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Resp: ndc_id changed"); + os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)); + } + + ndl->max_idle_period = params->max_idle_period; + ndl->peer_qos.min_slots = params->min_slots; + ndl->peer_qos.max_latency = params->max_latency; + + /* TODO: validate that in case an ACCEPTED NDL and an NDC, the NDC + * schedule is covered by the local device committed availability. + */ + if (params->ndc_sched && params->ndc_sched_len) { + os_free(ndl->ndc_sched); + ndl->ndc_sched_len = 0; + + ndl->ndc_sched = os_memdup(params->ndc_sched, + params->ndc_sched_len); + if (!ndl->ndc_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Resp: Failed to allocate NDC schedule"); + ret = -1; + goto fail; + } + + ndl->ndc_sched_len = params->ndc_sched_len; + } + + if (params->immut_sched && params->immut_sched_len) { + if (params->status == NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Resp: Immutable not allowed with status == accept"); + return -1; + } + + os_free(ndl->immut_sched); + ndl->immut_sched_len = 0; + ndl->immut_sched = os_memdup(params->immut_sched, + params->immut_sched_len); + if (!ndl->immut_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Resp: fail allocate immutable schedule"); + ret = -1; + goto fail; + } + ndl->immut_sched_len = params->immut_sched_len; + } + + ret = nan_ndl_validate_peer_avail(nan, peer); + if (!ret) + goto fail; + + wpa_printf(MSG_DEBUG, "NAN: NDL: Resp: status=%u", params->status); + + ndl->status = params->status; + if (params->status == NAN_NDL_STATUS_ACCEPTED) + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_DONE); + + return 0; + +fail: + nan_ndl_clear(nan, peer); + ndl->status = NAN_NDL_STATUS_REJECTED; + ndl->reason = NAN_REASON_RESOURCE_LIMITATION; + ndl->send_naf_on_error = 1; + return ret; +} + + +static int nan_ndl_attr_handle_conf(struct nan_data *nan, struct nan_peer *peer, + const struct ndl_attr_params *params) +{ + struct nan_ndl *ndl = peer->ndl; + int ret; + + if (!ndl) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Confirm without an NDL"); + return -1; + } + + if (ndl->state != NAN_NDL_STATE_RES_SENT) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Confirm while not expecting one"); + + if (ndl->state != NAN_NDL_STATE_REQ_RECV || + ndl->status != NAN_NDL_STATUS_CONTINUED) + return -1; + + /* + * Due to races with the driver, it is possible that the + * response is received before an ACK is indicated. Allow the + * processing of the attribute, and if all parameters are OK, + * fast forward the state machine below. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDL: Confirm received before Tx status"); + } + + ndl->send_naf_on_error = 0; + ndl->status = params->status; + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_CON_RECV); + + if (params->status != NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Confirm was not accepted. status=%u, reason=%u", + params->status, params->reason); + ndl->status = NAN_NDL_STATUS_REJECTED; + ndl->reason = params->reason; + return 0; + } + + if (ndl->dialog_token != params->dialog_token) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Confirm with invalid dialog token (%u != %u)", + ndl->dialog_token, params->dialog_token); + return -1; + } + + if (!is_zero_ether_addr(params->ndc_id) && + os_memcmp(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)) != 0) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Confirm: ndc_id changed"); + os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)); + } + + ndl->max_idle_period = params->max_idle_period; + ndl->peer_qos.min_slots = params->min_slots; + ndl->peer_qos.max_latency = params->max_latency; + + /* TODO: validate that the NDC schedule is covered by the local device + * committed availability. + */ + if (params->ndc_sched && params->ndc_sched_len) { + os_free(ndl->ndc_sched); + ndl->ndc_sched_len = 0; + + ndl->ndc_sched = os_memdup(params->ndc_sched, + params->ndc_sched_len); + if (!ndl->ndc_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Failed to allocate NDC schedule"); + ret = -1; + goto fail; + } + ndl->ndc_sched_len = params->ndc_sched_len; + } + + /* TODO: validate that the immutable schedule is covered by the local + * device committed availability. + */ + if (params->immut_sched && params->immut_sched_len) { + os_free(ndl->immut_sched); + ndl->immut_sched_len = 0; + + ndl->immut_sched = os_memdup(params->immut_sched, + params->immut_sched_len); + if (!ndl->immut_sched) { + wpa_printf(MSG_INFO, + "NAN: NDL: Failed to allocate immutable schedule"); + ret = -1; + goto fail; + } + ndl->immut_sched_len = params->immut_sched_len; + } + + ret = nan_ndl_validate_peer_avail(nan, peer); + if (!ret) + goto fail; + + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_DONE); + return 0; + +fail: + ndl->reason = NAN_REASON_RESOURCE_LIMITATION; + ndl->send_naf_on_error = 1; + return ret; +} + + +/* + * nan_ndl_handle_ndl_attr - Handle NDL attribute and update local state + * + * @nan: NAN module context from nan_init() + * @peer: The peer from which the original message was received + * @msg: Parsed NAN Action frame + * Returns: 0 on success, negative on failure to parse the attributes etc. + * + * As part of the NDL attribute handling, the function also parses NDC and QoS + * attributes. + */ +int nan_ndl_handle_ndl_attr(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg) +{ + const struct ieee80211_ndl *ndl_attr; + const u8 *ndl_attr_ext; + struct ndl_attr_params params; + u16 ndl_attr_len, ndl_attr_ext_len; + u16 control; + u8 ctrl_setup_reason; + u8 ndc_ok; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + + if (!msg || !peer) + return -1; + + /* + * It is possible that we receive a confirm NAF before the TX status + * of the previous NAF was processed. If NDL was accepted, the confirm + * would not include the NDL attribute, thus fast forward to state + * "done" here. + */ + if (msg->oui_subtype == NAN_SUBTYPE_DATA_PATH_CONFIRM && + peer->ndl && + peer->ndl->state == NAN_NDL_STATE_REQ_RECV && + peer->ndl->status == NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL is accepted - fast forward to state done"); + nan_ndl_set_state(nan, peer->ndl, NAN_NDL_STATE_DONE); + } + + if (peer->ndl && peer->ndl->state == NAN_NDL_STATE_DONE) { + wpa_printf(MSG_DEBUG, "NAN: NDL: NDL already done"); + return 0; + } + + if (!msg->attrs.ndl) + return 0; + + ndl_attr = (const struct ieee80211_ndl *) msg->attrs.ndl; + ndl_attr_len = msg->attrs.ndl_len; + if (ndl_attr_len < sizeof(struct ieee80211_ndl)) + return -1; + + ndl_attr_ext = (const u8 *) (ndl_attr + 1); + ndl_attr_ext_len = ndl_attr_len - sizeof(struct ieee80211_ndl); + + params.type = BITS(ndl_attr->type_and_status, NAN_NDL_TYPE_MASK, + NAN_NDL_TYPE_POS); + + params.status = BITS(ndl_attr->type_and_status, NAN_NDL_STATUS_MASK, + NAN_NDL_STATUS_POS); + params.reason = ndl_attr->reason_code; + control = le_to_host16(ndl_attr->ctrl); + + if (peer->ndl) + wpa_printf(MSG_DEBUG, + "NAN: NDL: curr: state=%s (%d), status=%d", + nan_ndl_state_str(peer->ndl->state), + peer->ndl->state, peer->ndl->status); + else + wpa_printf(MSG_DEBUG, "NAN: NDL: NDL does not exist"); + + params.dialog_token = ndl_attr->dialog_token; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: dialog=%u, type=0x%x, status=0x%x, ctrl=0x%x", + params.dialog_token, params.type, params.status, control); + + if (control & NAN_NDL_CTRL_PEER_ID_PRESENT) { + if (ndl_attr_ext_len < 1) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Request with invalid len=%u, control=0x%x", + ndl_attr_len, control); + return -1; + } + + /* + * Peer ID is no longer used. It is considered as reserved in + * Wi-Fi Aware Specification v4.0, Table 105 (NDL attribute + * format). Just skip it. + */ + ndl_attr_ext++; + ndl_attr_ext_len--; + } + + if (control & NAN_NDL_CTRL_MAX_IDLE_PERIOD_PRESENT) { + if (ndl_attr_ext_len < 2) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Request with invalid len=%u, control=0x%x", + ndl_attr_len, control); + return -1; + } + + params.max_idle_period = WPA_GET_LE16(ndl_attr_ext); + ndl_attr_ext += 2; + ndl_attr_ext_len -= 2; + + wpa_printf(MSG_DEBUG, "NAN: NDL: max_idle_period=%u", + params.max_idle_period); + } + + ndc_ok = 1; + if (control & NAN_NDL_CTRL_NDC_ATTR_PRESENT) { + struct nan_attrs_entry *n; + + ret = -1; + dl_list_for_each(n, &msg->attrs.ndc, struct nan_attrs_entry, + list) { + ret = nan_ndl_parse_ndc_attr( + nan, (const struct ieee80211_ndc *) n->ptr, + n->len, params.ndc_id, + ¶ms.ndc_sched, ¶ms.ndc_sched_len); + if (!ret) + break; + } + + if (ret) + ndc_ok = 0; + } else if (params.type == NAN_NDL_TYPE_RESPONSE) { + ndc_ok = 0; + } + + if (!ndc_ok && params.status != NAN_NDL_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Missing valid selected NDC"); + return -1; + } + + params.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF; + params.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + if (control & NAN_NDL_CTRL_NDL_QOS_ATTR_PRESENT) { + if (!msg->attrs.ndl_qos) { + wpa_printf(MSG_DEBUG, + "NAN: NDL QoS attribute not present but control flag is set"); + return -1; + } + ret = nan_ndl_parse_qos_attr( + nan, + (const struct ieee80211_nan_qos *) msg->attrs.ndl_qos, + msg->attrs.ndl_qos_len, ¶ms.min_slots, + ¶ms.max_latency); + if (ret) + return ret; + } + + if (control & NAN_NDL_CTRL_IMMUT_SCHED_PRESENT) { + if (ndl_attr_ext_len <= sizeof(struct nan_sched_entry)) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Request with invalid len=%u, control=0x%x", + ndl_attr_len, control); + return -1; + } + + params.immut_sched = ndl_attr_ext; + params.immut_sched_len = ndl_attr_ext_len; + } + + ctrl_setup_reason = BITS(control, NAN_NDL_CTRL_NDL_SETUP_REASON_MASK, + NAN_NDL_CTRL_NDL_SETUP_REASON_POS); + if (ctrl_setup_reason == NAN_NDL_CTRL_NDL_SETUP_REASON_NDP) { + params.setup_reason = NAN_NDL_SETUP_REASON_NDP; + } else { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Unknown setup reason. Assume NDP"); + params.setup_reason = NAN_NDL_SETUP_REASON_NDP; + } + + wpa_printf(MSG_DEBUG, + "NAN: NDL: max_idle_period=%u, immutable len=%u", + params.max_idle_period, params.immut_sched_len); + + switch (params.type) { + case NAN_NDL_TYPE_REQUEST: + return nan_ndl_attr_handle_req(nan, peer, ¶ms); + case NAN_NDL_TYPE_RESPONSE: + return nan_ndl_attr_handle_resp(nan, peer, ¶ms); + case NAN_NDL_TYPE_CONFIRM: + return nan_ndl_attr_handle_conf(nan, peer, ¶ms); + default: + return -1; + } +} + + +/** + * nan_ndl_add_avail_attrs - Add availability attributes + * @nan: NAN module context from nan_init() + * @peer: NAN peer for NDL establishment + * @buf: Frame buffer to which the attribute would be added + * Returns: 0 on success, negative on failure + * + * An availability attribute is added for each map (identified by map ID) in the + * NDL schedule. Each attribute holds an availability entry for committed slots + * and an availability entry for conditional slots. + */ +int nan_ndl_add_avail_attrs(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_schedule *sched; + u8 type_for_conditional = NAN_AVAIL_ENTRY_CTRL_TYPE_COND; + + if (!peer || !peer->ndl) + return -1; + + sched = &nan->sched; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Add Avail attribute. state=%s, status=%u", + nan_ndl_state_str(peer->ndl->state), peer->ndl->status); + + if (sched->n_chans < 1) { + if (peer->ndl->status == NAN_NDL_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Rejected. Not adding availability attributes"); + return 0; + } + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Cannot build availability without channels"); + return -1; + } + + /* In case the NDL exchange was complete successfully, consider the + * conditional entries as committed, as this is expected by the spec. + */ + if (peer->ndl->status == NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Add conditional entry as committed"); + type_for_conditional = NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED; + } + + return nan_add_avail_attrs(nan, sched->sequence_id, + sched->map_ids_bitmap, + type_for_conditional, + sched->n_chans, sched->chans, buf, true); +} + + +/** + * nan_ndl_add_ndl_attr - Add NDL attribute to frame + * @nan: NAN module context from nan_init() + * @peer: NAN peer for NDL establishment + * @buf: Frame buffer to which the attribute would be added + * Returns: 0 on success, negative on failure. + */ +int nan_ndl_add_ndl_attr(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndl *ndl; + struct nan_schedule *sched = &nan->sched; + u16 ndl_ctrl = 0; + u8 *len_ptr; + u8 type; + + if (!peer || !peer->ndl) + return -1; + + ndl = peer->ndl; + + wpa_printf(MSG_DEBUG, "NAN: Add NDL attribute. state=%s, status=%u", + nan_ndl_state_str(ndl->state), ndl->status); + + if (nan->cfg->max_ndl_idle_period) { + wpa_printf(MSG_DEBUG, "NAN: NDL: max idle period=%u", + nan->cfg->max_ndl_idle_period); + + ndl_ctrl |= NAN_NDL_CTRL_MAX_IDLE_PERIOD_PRESENT; + } + + switch (ndl->state) { + case NAN_NDL_STATE_NONE: + case NAN_NDL_STATE_REQ_SENT: + case NAN_NDL_STATE_RES_SENT: + case NAN_NDL_STATE_CON_SENT: + default: + return -1; + case NAN_NDL_STATE_START: + type = NAN_NDL_TYPE_REQUEST; + if (sched->ndc.len) + ndl_ctrl |= NAN_NDL_CTRL_NDC_ATTR_PRESENT; + break; + case NAN_NDL_STATE_REQ_RECV: + type = NAN_NDL_TYPE_RESPONSE; + if (sched->ndc.len && + ndl->status != NAN_NDL_STATUS_REJECTED) + ndl_ctrl |= NAN_NDL_CTRL_NDC_ATTR_PRESENT; + break; + case NAN_NDL_STATE_RES_RECV: + type = NAN_NDL_TYPE_CONFIRM; + if (sched->ndc.len && + ndl->status != NAN_NDL_STATUS_REJECTED) + ndl_ctrl |= NAN_NDL_CTRL_NDC_ATTR_PRESENT; + break; + case NAN_NDL_STATE_DONE: + wpa_printf(MSG_DEBUG, + "NAN: NDL: Done. Not adding NDL attribute"); + return 0; + } + + /* QoS attribute is going to be added */ + if (ndl->local_qos.max_latency != NAN_QOS_MAX_LATENCY_NO_PREF || + ndl->local_qos.min_slots != NAN_QOS_MIN_SLOTS_NO_PREF) + ndl_ctrl |= NAN_NDL_CTRL_NDL_QOS_ATTR_PRESENT; + + wpabuf_put_u8(buf, NAN_ATTR_NDL); + len_ptr = wpabuf_put(buf, 2); + + wpabuf_put_u8(buf, ndl->dialog_token); + wpabuf_put_u8(buf, type | (ndl->status << NAN_NDL_STATUS_POS)); + wpabuf_put_u8(buf, ndl->reason); + wpabuf_put_u8(buf, ndl_ctrl); + + if (nan->cfg->max_ndl_idle_period) + wpabuf_put_le16(buf, nan->cfg->max_ndl_idle_period); + + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + + return 0; +} + + +/** + * nan_ndl_add_ndc_attr - Add NDC attribute to frame + * @nan: NAN module context from nan_init() + * @peer: NAN peer for NDL establishment + * @buf: Frame buffer to which the attribute would be added + * Returns: 0 on success, negative on failure. + */ +int nan_ndl_add_ndc_attr(struct nan_data *nan, const struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndl *ndl; + struct nan_schedule *sched = &nan->sched; + u8 ndc_ctrl = NAN_NDC_CTRL_SELECTED; + u16 sched_entry_ctrl = 0; + + if (!peer || !peer->ndl) + return -1; + + ndl = peer->ndl; + + if (ndl->state != NAN_NDL_STATE_START && + ndl->state != NAN_NDL_STATE_REQ_RECV && + ndl->state != NAN_NDL_STATE_RES_RECV) + return 0; + + wpa_printf(MSG_DEBUG, "NAN: Add NDC attribute. state=%s, status=%u", + nan_ndl_state_str(ndl->state), ndl->status); + + /* NDC attribute is optional in case of reject */ + if (ndl->status == NAN_NDL_STATUS_REJECTED) + return 0; + + /* + * NDC attribute for NDP Request is optional. In all other cases it is + * mandatory + */ + if (!sched->ndc.len) { + if (ndl->state != NAN_NDL_STATE_START) { + wpa_printf(MSG_DEBUG, "NAN: NDL: No NDC to add"); + return -1; + } + + return 0; + } + + wpabuf_put_u8(buf, NAN_ATTR_NDC); + wpabuf_put_le16(buf, sizeof(struct ieee80211_ndc) + + sizeof(struct nan_sched_entry) + + sched->ndc.len); + + wpabuf_put_data(buf, ndl->ndc_id, sizeof(ndl->ndc_id)); + wpabuf_put_u8(buf, ndc_ctrl); + + /* Add the schedule entry */ + wpabuf_put_u8(buf, sched->ndc_map_id); + + sched_entry_ctrl |= sched->ndc.duration << + NAN_TIME_BM_CTRL_BIT_DURATION_POS; + sched_entry_ctrl |= sched->ndc.period << + NAN_TIME_BM_CTRL_PERIOD_POS; + sched_entry_ctrl |= sched->ndc.offset << + NAN_TIME_BM_CTRL_START_OFFSET_POS; + + wpabuf_put_le16(buf, sched_entry_ctrl); + + /* Add the time bitmap */ + wpabuf_put_u8(buf, sched->ndc.len); + wpabuf_put_data(buf, sched->ndc.bitmap, sched->ndc.len); + + return 0; +} + + +/** + * nan_ndl_add_qos_attr - Add QOS attribute to frame + * @nan: NAN module context from nan_init() + * @peer: NAN peer for NDL establishment + * @buf: Frame buffer to which the attribute would be added + * Returns: 0 on success, negative on failure. + */ +int nan_ndl_add_qos_attr(struct nan_data *nan, + const struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndl *ndl; + + if (!peer || !peer->ndl) + return -1; + + ndl = peer->ndl; + + wpa_printf(MSG_DEBUG, "NAN: Add QoS attribute. state=%s, status=%u", + nan_ndl_state_str(ndl->state), ndl->status); + + switch (ndl->state) { + case NAN_NDL_STATE_START: + case NAN_NDL_STATE_REQ_RECV: + case NAN_NDL_STATE_RES_RECV: + break; + case NAN_NDL_STATE_NONE: + case NAN_NDL_STATE_REQ_SENT: + case NAN_NDL_STATE_RES_SENT: + case NAN_NDL_STATE_CON_SENT: + case NAN_NDL_STATE_CON_RECV: + case NAN_NDL_STATE_DONE: + default: + return 0; + } + + if (ndl->local_qos.max_latency == NAN_QOS_MAX_LATENCY_NO_PREF && + ndl->local_qos.min_slots == NAN_QOS_MIN_SLOTS_NO_PREF) + return 0; + + wpabuf_put_u8(buf, NAN_ATTR_NDL_QOS); + wpabuf_put_le16(buf, sizeof(struct ieee80211_nan_qos)); + wpabuf_put_u8(buf, ndl->local_qos.min_slots); + wpabuf_put_le16(buf, ndl->local_qos.max_latency); + + return 0; +} + + +/** + * nan_ndl_naf_sent - Indicate a NAF has been sent + * @nan: NAN module context from nan_init() + * @peer: The peer with whom the NDL is being setup + * @subtype: The NAN OUI subtype + * + * A notification indicating to the NDL state machine that a NAF was sent, so + * the NDL state machine can update its state. + * + * In case the NDL setup negotiation is successfully done, the final schedule + * is applied and the NDL is active. If the negotiation is done and failed, the + * NDL state is reset. + */ +int nan_ndl_naf_sent(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype) +{ + struct nan_ndl *ndl; + + if (!peer || !peer->ndl) + return -1; + + ndl = peer->ndl; + + if (ndl->state == NAN_NDL_STATE_DONE) + return 0; + + wpa_printf(MSG_DEBUG, + "NAN: NDL: Tx done with peer=" MACSTR " state=%s, status=%u", + MAC2STR(peer->nmi_addr), nan_ndl_state_str(ndl->state), + ndl->status); + + /* Note: Due to races between the Tx status and Rx path, it is possible + * that the Tx status is received after the peer response was already + * processed (which can result with another frame being sent). In such a + * case the logic above fast-forwards the state, and the transitions + * here need to take this into consideration. + */ + switch (ndl->state) { + case NAN_NDL_STATE_START: + if (subtype != NAN_SUBTYPE_DATA_PATH_REQUEST) + return 0; + if (ndl->status != NAN_NDL_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Tx sent: invalid continue status"); + return -1; + } + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_REQ_SENT); + return 0; + case NAN_NDL_STATE_REQ_RECV: + if (subtype != NAN_SUBTYPE_DATA_PATH_RESPONSE) + return 0; + if (ndl->status == NAN_NDL_STATUS_CONTINUED) { + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_RES_SENT); + return 0; + } + break; + case NAN_NDL_STATE_RES_RECV: + if (subtype != NAN_SUBTYPE_DATA_PATH_CONFIRM) + return 0; + if (ndl->status == NAN_NDL_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Tx sent: invalid continue status"); + return -1; + } + break; + case NAN_NDL_STATE_CON_RECV: + case NAN_NDL_STATE_REQ_SENT: + case NAN_NDL_STATE_RES_SENT: + case NAN_NDL_STATE_CON_SENT: + case NAN_NDL_STATE_DONE: + default: + wpa_printf(MSG_DEBUG, "NAN: NDL: Tx sent: unexpected state %d", + ndl->state); + return 0; + } + + if (ndl->status == NAN_NDL_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, "NAN: NDL: Schedule setup success"); + nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_DONE); + return 0; + } + + /* NDL is rejected and NAF already sent. Higher layer is expected to + * handle it. + */ + return 0; +} + + +/* + * nan_ndl_add_elem_container_attr - Add NAN element container attribute + * + * @nan: NAN module context from nan_init() + * @peer: The peer with whom the NDL is being setup + * @buf: wpabuf to which the attribute is added + */ +void nan_ndl_add_elem_container_attr(const struct nan_data *nan, + const struct nan_peer *peer, + struct wpabuf *buf) +{ + const struct nan_ndl *ndl; + + if (!peer || !peer->ndl || !nan->sched.elems) + return; + + ndl = peer->ndl; + + wpa_printf(MSG_DEBUG, "NAN: Add element container. state=%s, status=%u", + nan_ndl_state_str(ndl->state), ndl->status); + + if (peer->ndl->status == NAN_NDL_STATUS_REJECTED) + return; + + /* Element container is expected only in NDP request/response */ + if (ndl->state != NAN_NDL_STATE_START && + ndl->state != NAN_NDL_STATE_REQ_RECV) + return; + + wpabuf_put_u8(buf, NAN_ATTR_ELEM_CONTAINER); + wpabuf_put_le16(buf, 1 + wpabuf_len(nan->sched.elems)); + wpabuf_put_u8(buf, 0); + wpabuf_put_buf(buf, nan->sched.elems); +} diff --git a/contrib/wpa/src/nan/nan_ndp.c b/contrib/wpa/src/nan/nan_ndp.c new file mode 100644 index 000000000000..74bd263070d8 --- /dev/null +++ b/contrib/wpa/src/nan/nan_ndp.c @@ -0,0 +1,1214 @@ +/* + * Wi-Fi Aware - NAN Data Path + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "nan_i.h" + + +static const char * nan_ndp_state_str(enum nan_ndp_state state) +{ +#define C2S(x) case x: return #x; + switch (state) { + C2S(NAN_NDP_STATE_NONE) + C2S(NAN_NDP_STATE_START) + C2S(NAN_NDP_STATE_REQ_SENT) + C2S(NAN_NDP_STATE_REQ_RECV) + C2S(NAN_NDP_STATE_RES_SENT) + C2S(NAN_NDP_STATE_RES_RECV) + C2S(NAN_NDP_STATE_CON_SENT) + C2S(NAN_NDP_STATE_CON_RECV) + C2S(NAN_NDP_STATE_DONE) + default: + return "Invalid NAN NDP state"; + } +} + + +static void nan_ndp_set_state(struct nan_data *nan, + struct nan_ndp_setup *ndp_setup, + enum nan_ndp_state state) +{ + wpa_printf(MSG_DEBUG, + "NAN: NDP: state: %s (%u) --> %s (%u)", + nan_ndp_state_str(ndp_setup->state), + ndp_setup->state, nan_ndp_state_str(state), + state); + + ndp_setup->state = state; +} + + +static struct nan_ndp * +nan_ndp_alloc(struct nan_data *nan, struct nan_peer *peer, + u8 initiator, u8 *init_ndi, u8 ndp_id, u8 min_slots, + u16 max_latency) +{ + struct nan_ndp *ndp = os_zalloc(sizeof(*ndp)); + + if (!ndp) + return NULL; + + ndp->peer = peer; + ndp->initiator = initiator; + ndp->ndp_id = ndp_id; + + os_memcpy(ndp->init_ndi, init_ndi, sizeof(ndp->init_ndi)); + + ndp->qos.min_slots = min_slots; + ndp->qos.max_latency = max_latency; + + return ndp; +} + + +static int nan_ndp_ssi(struct nan_data *nan, struct nan_ndp_setup *ndp_setup, + const u8 *ssi, u16 ssi_len) +{ + os_free(ndp_setup->ssi); + ndp_setup->ssi = NULL; + ndp_setup->ssi_len = 0; + + if (!ssi || !ssi_len) + return 0; + + ndp_setup->ssi = os_memdup(ssi, ssi_len); + if (!ndp_setup->ssi) { + wpa_printf(MSG_INFO, "NAN: NDP: Failed to allocate NDP ssi"); + return -1; + } + + ndp_setup->ssi_len = ssi_len; + return 0; +} + + +/** + * nan_ndp_setup_req - Start handling of NDP setup request + * @nan: NAN module context from nan_init() + * @peer: The peer to initiate the NDP setup with + * @params: NDP setup request parameters + * Returns: 0 on success, negative on failure + * + * On successful request, the data structures would be ready to + * continue the NDP establishment. + */ +int nan_ndp_setup_req(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_params *params) +{ + int ret; + + if (peer->ndp_setup.ndp) { + wpa_printf(MSG_DEBUG, "NAN: NDP: already WIP with peer"); + return -1; + } + + peer->ndp_setup.ndp = nan_ndp_alloc(nan, peer, 1, + params->ndp_id.init_ndi, + params->ndp_id.id, + params->qos.min_slots, + params->qos.max_latency); + if (!peer->ndp_setup.ndp) { + wpa_printf(MSG_DEBUG, "NAN: NDP: Failed allocation"); + return -1; + } + + peer->ndp_setup.dialog_token = nan_get_next_dialog_token(nan); + peer->ndp_setup.publish_inst_id = params->u.req.publish_inst_id; + os_memcpy(peer->ndp_setup.service_id, params->u.req.service_id, + NAN_SERVICE_ID_LEN); + + /* Require confirmation for all locally initiated NDPs */ + peer->ndp_setup.conf_req = 1; + + /* Store service specific information */ + ret = nan_ndp_ssi(nan, &peer->ndp_setup, params->ssi, params->ssi_len); + if (ret) { + os_free(peer->ndp_setup.ndp); + peer->ndp_setup.ndp = NULL; + return ret; + } + + nan_sec_reset(nan, &peer->ndp_setup.sec); + + if (params->sec.csid) { + peer->ndp_setup.sec.i_csid = params->sec.csid; + os_memcpy(peer->ndp_setup.sec.pmk, params->sec.pmk, PMK_LEN); + + peer->ndp_setup.sec.present = true; + peer->ndp_setup.sec.valid = true; + + peer->ndp_setup.sec.i_instance_id = + peer->ndp_setup.publish_inst_id; + + os_memcpy(&peer->ndp_setup.sec.local_gtk, ¶ms->sec.gtk, + sizeof(peer->ndp_setup.sec.local_gtk)); + } + + if (params->interface_id) { + wpa_printf(MSG_DEBUG, + "NAN: NDP setup request with local interface id"); + + os_memcpy(peer->ndp_setup.local_interface_id, + params->interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + + peer->ndp_setup.local_interface_id_valid = true; + } + + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_START); + peer->ndp_setup.status = NAN_NDP_STATUS_CONTINUED; + return 0; +} + + +/** + * nan_ndp_setup_resp - Handle higher layer response for an NDP request + * @nan: NAN module context from nan_init() + * @peer: Peer that originated the NDP setup request + * @params: NDP setup parameters + * Returns: 0 on success, negative on failure + * + * The response status can be either ACCEPTED or REJECTED. The internal logic of + * the NDP state machine adjusts its own state according to the response status + * and its internal state. + */ +int nan_ndp_setup_resp(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_params *params) +{ + int ret; + + if (!peer->ndp_setup.ndp || + peer->ndp_setup.ndp->ndp_id != params->ndp_id.id || + !ether_addr_equal(peer->ndp_setup.ndp->init_ndi, + params->ndp_id.init_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: No matching NDP found for NDP response"); + return -1; + } + + if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Unexpected state for response"); + return -1; + } + + if (params->u.resp.status != NAN_NDP_STATUS_ACCEPTED && + params->u.resp.status != NAN_NDP_STATUS_REJECTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Unexpected response status=%u", + params->u.resp.status); + return -1; + } + + peer->ndp_setup.status = params->u.resp.status; + peer->ndp_setup.reason = params->u.resp.reason_code; + + if (peer->ndp_setup.status == NAN_NDP_STATUS_ACCEPTED) { + if (peer->ndp_setup.conf_req) + peer->ndp_setup.status = NAN_NDP_STATUS_CONTINUED; + + os_memcpy(peer->ndp_setup.ndp->resp_ndi, + params->u.resp.resp_ndi, ETH_ALEN); + + if (!peer->ndp_setup.sec.present && params->sec.csid) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Security not requested by peer"); + return -1; + } + + if (peer->ndp_setup.sec.present) { + if (params->sec.csid != peer->ndp_setup.sec.i_csid) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Different cipher suite specified."); + return -1; + } + + peer->ndp_setup.sec.r_csid = params->sec.csid; + os_memcpy(peer->ndp_setup.sec.pmk, params->sec.pmk, + PMK_LEN); + os_memcpy(&peer->ndp_setup.sec.local_gtk, + ¶ms->sec.gtk, + sizeof(peer->ndp_setup.sec.local_gtk)); + + ret = nan_sec_init_resp(nan, peer); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Failed to init responder security"); + + peer->ndp_setup.status = + NAN_NDP_STATUS_REJECTED; + peer->ndp_setup.reason = + NAN_REASON_INVALID_PARAMETERS; + return 0; + } + + peer->ndp_setup.status = NAN_NDP_STATUS_CONTINUED; + } + } + + /* Store service specific information */ + ret = nan_ndp_ssi(nan, &peer->ndp_setup, params->ssi, params->ssi_len); + if (ret) + return ret; + + if (params->interface_id) { + wpa_printf(MSG_DEBUG, + "NAN: NDP setup response with local interface id"); + + os_memcpy(peer->ndp_setup.local_interface_id, + params->interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + + peer->ndp_setup.local_interface_id_valid = true; + } + + return 0; +} + + +static int nan_ndp_attr_handle_tlvs(struct nan_data *nan, + struct nan_peer *peer, + const u8 *tlvs, u16 tlvs_len) +{ + wpa_printf(MSG_DEBUG, "NAN: NDP: Handle NDPE TLVs len=%u", tlvs_len); + + while (tlvs_len > 3) { + u8 tlv_type = tlvs[0]; + u16 tlv_len = WPA_GET_LE16(tlvs + 1); + const u8 *tlv_data = tlvs + 3; + int ret; + + if (tlv_len + 3 > tlvs_len) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Invalid TLV len=%u for type=%u", + tlv_len, tlv_type); + return -1; + } + + switch (tlv_type) { + case NAN_NDPE_TLV_IPV6_LINK_LOCAL: + if (tlv_len != NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Invalid interface ID tlv len=%u", + tlv_len); + break; + } + + peer->ndp_setup.peer_interface_id_valid = true; + os_memcpy(peer->ndp_setup.peer_interface_id, tlv_data, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + break; + case NAN_NDPE_TLV_SRV_INFO: + wpa_printf(MSG_DEBUG, + "NAN: NDP: Handle NDP service specific information"); + + if (tlv_len >= 4 && WPA_GET_BE24(tlv_data) == OUI_WFA && + tlv_data[3] == NAN_SRV_PROTO_GENERIC) + ret = nan_ndp_ssi(nan, &peer->ndp_setup, + tlv_data + 4, tlv_len - 4); + else + ret = nan_ndp_ssi(nan, &peer->ndp_setup, + tlv_data, tlv_len); + if (ret) + wpa_printf(MSG_DEBUG, + "NAN: NDP: Failed to save ssi - continue"); + break; + default: + wpa_printf(MSG_DEBUG, "NAN: NDP: unknown TLV type=%u", + tlv_type); + break; + } + + tlvs += 3 + tlv_len; + tlvs_len -= 3 + tlv_len; + } + + if (tlvs_len) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: TLV parsing ended with %u left octets", + tlvs_len); + return -1; + } + + return 0; +} + + +static int nan_ndp_attr_handle_req(struct nan_data *nan, struct nan_peer *peer, + struct ieee80211_ndp *ndp_attr, u16 ndp_len, + u8 status, bool ndpe) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + u16 exp_len = sizeof(struct ieee80211_ndp); + u8 publish_inst_id; + + if (ndp_setup->ndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: While already WIP with peer"); + return -1; + } + + if (!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Without publish ID"); + return -1; + } + + exp_len++; + if (ndp_len < exp_len) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Length too short: ndp_len=%u, exp_len=%u", + ndp_len, exp_len); + return -1; + } + + publish_inst_id = *ndp_attr->optional; + if (!nan_publish_instance_id_valid(nan, publish_inst_id, + ndp_setup->service_id)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Invalid publish instance ID=%u", + publish_inst_id); + return -1; + } + + /* Note: The QoS setting should be set in the response. */ + ndp_setup->ndp = nan_ndp_alloc(nan, peer, 0, + ndp_attr->initiator_ndi, + ndp_attr->ndp_id, + NAN_QOS_MIN_SLOTS_NO_PREF, + NAN_QOS_MAX_LATENCY_NO_PREF); + if (!ndp_setup->ndp) + return -1; + + nan_sec_reset(nan, &peer->ndp_setup.sec); + nan_ndp_set_state(nan, ndp_setup, NAN_NDP_STATE_REQ_RECV); + + ndp_setup->status = NAN_NDP_STATUS_CONTINUED; + ndp_setup->conf_req = + !!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_CONFIRM_REQUIRED); + ndp_setup->dialog_token = ndp_attr->dialog_token; + ndp_setup->publish_inst_id = publish_inst_id; + ndp_setup->sec.present = + !!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT); + + /* Handle service specific information */ + ndp_len -= exp_len; + + if (!ndpe && (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SPEC_INFO_PRESENT) && + ndp_len) { + int ret; + + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Handle NDP service specific information"); + + ret = nan_ndp_ssi(nan, &peer->ndp_setup, + (const u8 *) ndp_attr->optional + 1, + ndp_len); + if (ret) + wpa_printf(MSG_DEBUG, + "NAN: NDP: req: Failed to save ssi. continue"); + return ret; + } + + if (ndpe) + return nan_ndp_attr_handle_tlvs(nan, peer, + ndp_attr->optional + 1, + ndp_len); + + return 0; +} + + +static int nan_ndp_attr_handle_res(struct nan_data *nan, struct nan_peer *peer, + struct ieee80211_ndp *ndp_attr, u16 ndp_len, + u8 status, bool ndpe) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + u16 opt_len; + bool sec_present; + + if (!ndp_setup->ndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: While no NDP WIP with peer"); + return -1; + } + + if (ndp_setup->state != NAN_NDP_STATE_REQ_SENT) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: While not expecting one"); + + if (ndp_setup->state != NAN_NDP_STATE_START) + return -1; + + /* + * Due to races with the driver, it is possible that the + * response is received before an ACK is indicated. Allow the + * processing of the attribute, and if all parameters are OK, + * fast forward the state machine below. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: Received before Tx status."); + } + + if (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: Unexpected publish ID"); + return -1; + } + + opt_len = 0; + if (status == NAN_NDP_STATUS_CONTINUED || + status == NAN_NDP_STATUS_ACCEPTED) { + if (!(ndp_attr->ndp_ctrl & + NAN_NDP_CTRL_RESPONDER_NDI_PRESENT)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: without responder NDI"); + return -1; + } + opt_len += ETH_ALEN; + } + + if (ndp_len < (sizeof(struct ieee80211_ndp) + opt_len)) { + wpa_printf(MSG_DEBUG, "NAN: NDP: resp: Length too short"); + return -1; + } + + if (ndp_setup->ndp->ndp_id != ndp_attr->ndp_id || + ndp_setup->dialog_token != ndp_attr->dialog_token || + !ether_addr_equal(ndp_setup->ndp->init_ndi, + ndp_attr->initiator_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: Invalid NDP ID, dialog token or addr"); + return -1; + } + + if (ndp_setup->state == NAN_NDP_STATE_START) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: Continue though no TX status yet."); + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_REQ_SENT); + } + + ndp_setup->status = status; + + if (status == NAN_NDP_STATUS_REJECTED) { + ndp_setup->reason = ndp_attr->reason_code; + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_DONE); + goto store_ssi; + } + + sec_present = !!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT); + if (ndp_setup->sec.present != sec_present) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Security present mismatch"); + return -1; + } + + if ((sec_present || ndp_setup->conf_req) && + status != NAN_NDP_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Security present or confirm required and status != continued"); + return -1; + } + + if (status == NAN_NDP_STATUS_ACCEPTED) + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_DONE); + else + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_RES_RECV); + + os_memcpy(ndp_setup->ndp->resp_ndi, &ndp_attr->optional[0], + sizeof(ndp_setup->ndp->resp_ndi)); + + /* + * In case that security is not configured, move the status to accepted. + * The state machine would transition to the 'done' state after the + * confirm is acked. + * TODO: Once security is configured, need to validate the security must + * be present. + */ + if (!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT)) + ndp_setup->status = NAN_NDP_STATUS_ACCEPTED; + +store_ssi: + /* Handle service specific information */ + ndp_len -= sizeof(struct ieee80211_ndp) + opt_len; + if (!ndpe && (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SPEC_INFO_PRESENT) && + ndp_len) { + int ret; + + wpa_printf(MSG_DEBUG, "NAN: NDP: resp: Handle NDP ssi"); + ret = nan_ndp_ssi(nan, &peer->ndp_setup, + ndp_attr->optional + opt_len, ndp_len); + if (ret) + wpa_printf(MSG_DEBUG, + "NAN: NDP: resp: Failed to save ssi. continue"); + return ret; + } + + if (ndpe) + return nan_ndp_attr_handle_tlvs(nan, peer, + ndp_attr->optional + opt_len, + ndp_len); + + return 0; +} + + +static int nan_ndp_attr_handle_confirm(struct nan_data *nan, + struct nan_peer *peer, + struct ieee80211_ndp *ndp_attr, + u8 status) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + bool sec_present; + + if (!ndp_setup->ndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Received confirm while no NDP WIP with peer"); + return -1; + } + + if (ndp_setup->state != NAN_NDP_STATE_RES_SENT) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: confirm: While not expecting one"); + + if (ndp_setup->state != NAN_NDP_STATE_REQ_RECV || + ndp_setup->status != NAN_NDP_STATUS_CONTINUED) + return -1; + + /* + * Due to races with the driver, it is possible that the + * confirm is received before an ACK is indicated. Allow the + * processing of the attribute, and if all parameters are OK, + * fast forward the state machine below. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDP: Confirm received before Tx status"); + } + + if (ndp_setup->ndp->ndp_id != ndp_attr->ndp_id || + ndp_setup->dialog_token != ndp_attr->dialog_token || + !ether_addr_equal(ndp_setup->ndp->init_ndi, + ndp_attr->initiator_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: confirm: Invalid NDP ID, dialog token or init ID"); + return -1; + } + + sec_present = !!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT); + if (ndp_setup->sec.present != sec_present) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: confirm: Security present mismatch"); + return -1; + } + + if (sec_present && status != NAN_NDP_STATUS_CONTINUED) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: confirm: status != continued with security"); + return -1; + } + + if (ndp_setup->state == NAN_NDP_STATE_REQ_RECV) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: confirm: Continue though no TX status yet."); + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_RES_SENT); + } + + ndp_setup->status = status; + + if (sec_present) + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_CON_RECV); + else + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_DONE); + + return 0; +} + + +static struct nan_ndp * nan_ndp_find_ndp(struct nan_peer *peer, + u8 ndp_id, const u8 *init_ndi) +{ + struct nan_ndp *pndp; + + dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) { + if (pndp->ndp_id == ndp_id && + ether_addr_equal(pndp->init_ndi, init_ndi)) + return pndp; + } + + return NULL; +} + + +static int nan_ndp_attr_sec_install(struct nan_data *nan, struct nan_peer *peer, + const struct ieee80211_ndp *ndp_attr, + u8 status) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + bool sec_present; + + if (!ndp_setup->ndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install while no NDP WIP with peer"); + return -1; + } + + if (ndp_setup->state != NAN_NDP_STATE_CON_SENT) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install while not expecting one"); + + if (ndp_setup->state != NAN_NDP_STATE_RES_RECV || + ndp_setup->status != NAN_NDP_STATUS_CONTINUED) + return -1; + + /* Due to races with the driver, it is possible that the + * install is received before an ACK is indicated. Allow the + * processing of the attribute, and if all parameters are OK, + * fast forward the state machine below. + */ + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install received before Tx status."); + } + + if (ndp_setup->ndp->ndp_id != ndp_attr->ndp_id || + ndp_setup->dialog_token != ndp_attr->dialog_token || + !ether_addr_equal(ndp_setup->ndp->init_ndi, + ndp_attr->initiator_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install: Invalid NDP parameters"); + return -1; + } + + sec_present = !!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT); + if (ndp_setup->sec.present != sec_present) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install: Security present mismatch"); + return -1; + } + + if (status != NAN_NDP_STATUS_ACCEPTED) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: sec install: status != ACCEPTED"); + return -1; + } + + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_DONE); + return 0; +} + + +static int nan_ndp_attr_handle_term(struct nan_data *nan, struct nan_peer *peer, + struct ieee80211_ndp *ndp_attr, u8 status) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + struct nan_ndp_id ndp_id; + const u8 *local_ndi, *peer_ndi; + struct nan_ndp *pndp; + + wpa_printf(MSG_DEBUG, + "NAN: NDP: Termination peer=" MACSTR " ndp_id=%u, init_ndi=" + MACSTR, + MAC2STR(peer->nmi_addr), ndp_attr->ndp_id, + MAC2STR(ndp_attr->initiator_ndi)); + + /* + * This should not really happen, but just in case, terminate the + * establishment. Since the NDP establishment is not yet done, the NDP + * is not added to the list of NDPs, so just reject the establishment. + */ + if (ndp_setup->ndp && ndp_setup->ndp->ndp_id == ndp_attr->ndp_id && + ether_addr_equal(ndp_setup->ndp->init_ndi, + ndp_attr->initiator_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Termination while NDP is in progress"); + + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_DONE); + ndp_setup->status = NAN_NDP_STATUS_REJECTED; + ndp_setup->reason = NAN_REASON_UNSPECIFIED_REASON; + return 0; + } + + /* Find the NDP in the list of active NDPs */ + pndp = nan_ndp_find_ndp(peer, ndp_attr->ndp_id, + ndp_attr->initiator_ndi); + if (!pndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Termination but NDP does not exist"); + return 1; + } + + wpa_printf(MSG_DEBUG, "NAN: NDP: Terminating"); + + os_memcpy(ndp_id.peer_nmi, peer->nmi_addr, ETH_ALEN); + os_memcpy(ndp_id.init_ndi, pndp->init_ndi, ETH_ALEN); + ndp_id.id = pndp->ndp_id; + + if (pndp->initiator) { + local_ndi = pndp->init_ndi; + peer_ndi = pndp->resp_ndi; + } else { + local_ndi = pndp->resp_ndi; + peer_ndi = pndp->init_ndi; + } + + /* + * Remove the NDP from the list of active NDPs before calling + * nan_ndp_terminated() as the function checks the list of NDPs to + * determine if the NDL should be reset as well. Free the NDP only after + * the call as the NDI addresses are still referenced. + */ + dl_list_del(&pndp->list); + + nan_ndp_terminated(nan, peer, &ndp_id, local_ndi, peer_ndi, + ndp_attr->reason_code, pndp->gtk_id); + + os_free(pndp); + + /* Indicate that no further processing is needed */ + return 1; +} + + +/** + * nan_ndp_handle_ndp_attr - Handle NDP attribute and update local state + * @nan: NAN module context from nan_init() + * @peer: The peer from which the original message was received + * @msg: Parsed NAN Action frame + * Returns: 0 on success processing indicating that processing can continue; 1 + * in case of successful processing but no further processing is needed; + * negative on failure. + */ +int nan_ndp_handle_ndp_attr(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg) +{ + struct ieee80211_ndp *ndp_attr = NULL; + size_t ndp_attr_len = 0; + bool ndpe_supported; + u8 type, status; + int ret; + + if (!msg || !peer || (!msg->attrs.ndp && !msg->attrs.ndpe)) + return -1; + + ndpe_supported = nan_is_ndpe_supported(nan, peer); + wpa_printf(MSG_DEBUG, + "NAN: NDP: Handle NDP attribute. ndpe_supported=%u", + ndpe_supported); + + /* + * The NDP attribute and the NDPE attribute are very similar in + * structure so handle them in the same way where possible. + */ + if (ndpe_supported) { + ndp_attr = (struct ieee80211_ndp *) msg->attrs.ndpe; + ndp_attr_len = msg->attrs.ndpe_len; + } + + if (!ndp_attr || !ndp_attr_len) { + ndp_attr = (struct ieee80211_ndp *) msg->attrs.ndp; + ndp_attr_len = msg->attrs.ndp_len; + ndpe_supported = false; + if (!ndp_attr || !ndp_attr_len) + return -1; + } + + type = BITS(ndp_attr->type_and_status, NAN_NDP_TYPE_MASK, + NAN_NDP_TYPE_POS); + status = BITS(ndp_attr->type_and_status, NAN_NDP_STATUS_MASK, + NAN_NDP_STATUS_POS); + + if (peer->ndp_setup.ndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: attr: state=%s (%d), status=%d", + nan_ndp_state_str(peer->ndp_setup.state), + peer->ndp_setup.state, + peer->ndp_setup.status); + } else { + wpa_printf(MSG_DEBUG, "NAN: NDP: attr: no active NDP setup"); + } + + wpa_printf(MSG_DEBUG, "NAN: NDP: attr: type=0x%x, status=0x%x", + type, status); + + switch (type) { + case NAN_NDP_TYPE_REQUEST: + ret = nan_ndp_attr_handle_req(nan, peer, ndp_attr, + ndp_attr_len, status, + ndpe_supported); + break; + case NAN_NDP_TYPE_RESPONSE: + ret = nan_ndp_attr_handle_res(nan, peer, ndp_attr, + ndp_attr_len, status, + ndpe_supported); + break; + case NAN_NDP_TYPE_CONFIRM: + ret = nan_ndp_attr_handle_confirm(nan, peer, ndp_attr, status); + break; + case NAN_NDP_TYPE_SECURITY_INSTALL: + ret = nan_ndp_attr_sec_install(nan, peer, ndp_attr, status); + break; + case NAN_NDP_TYPE_TERMINATE: + return nan_ndp_attr_handle_term(nan, peer, ndp_attr, status); + default: + return -1; + } + + /* Error or no security.. We are done. */ + if (ret || !peer->ndp_setup.sec.present || + peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED) + return ret; + + ret = nan_sec_rx(nan, peer, msg); + if (ret) + return ret; + + /* Processing of confirm is successful, so to the overall status is + * success. */ + if (type == NAN_NDP_TYPE_CONFIRM) + peer->ndp_setup.status = NAN_NDP_STATUS_ACCEPTED; + + return 0; +} + + +/** + * nan_ndp_add_ndp_attr - Add NDP attribute to frame + * @nan: NAN module context from nan_init() + * @peer: The peer to which the NAF should be sent + * @buf: wpabuf to which the attribute would be added + * Returns: 0 on success, negative on failure + */ +int nan_ndp_add_ndp_attr(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_setup *ndp_setup; + u8 type, ndp_ctrl = 0; + u8 *len_ptr; + bool ndpe_supported; + bool add_srv_info = false; + + if (!peer || !peer->ndp_setup.ndp) + return -1; + + ndpe_supported = nan_is_ndpe_supported(nan, peer); + ndp_setup = &peer->ndp_setup; + + switch (ndp_setup->state) { + case NAN_NDP_STATE_START: + type = NAN_NDP_TYPE_REQUEST; + ndp_ctrl = NAN_NDP_CTRL_PUBLISH_ID_PRESENT; + if (ndp_setup->conf_req) + ndp_ctrl |= NAN_NDP_CTRL_CONFIRM_REQUIRED; + if (ndp_setup->ssi && ndp_setup->ssi_len) { + add_srv_info = true; + if (!ndpe_supported) + ndp_ctrl |= NAN_NDP_CTRL_SPEC_INFO_PRESENT; + } + break; + case NAN_NDP_STATE_REQ_RECV: + type = NAN_NDP_TYPE_RESPONSE; + if (ndp_setup->status != NAN_NDP_STATUS_REJECTED) + ndp_ctrl |= NAN_NDP_CTRL_RESPONDER_NDI_PRESENT; + if (ndp_setup->ssi && ndp_setup->ssi_len) { + add_srv_info = true; + if (!ndpe_supported) + ndp_ctrl |= NAN_NDP_CTRL_SPEC_INFO_PRESENT; + } + break; + case NAN_NDP_STATE_RES_RECV: + type = NAN_NDP_TYPE_CONFIRM; + break; + case NAN_NDP_STATE_CON_RECV: + /* TODO: Integrate NDP security flows */ + type = NAN_NDP_TYPE_SECURITY_INSTALL; + break; + case NAN_NDP_STATE_NONE: + case NAN_NDP_STATE_DONE: + if (ndp_setup->status == NAN_NDP_STATUS_REJECTED) + type = NAN_NDP_TYPE_TERMINATE; + else + return -1; + break; + case NAN_NDP_STATE_REQ_SENT: + case NAN_NDP_STATE_RES_SENT: + case NAN_NDP_STATE_CON_SENT: + default: + return 0; + } + + if (ndp_setup->sec.present) + ndp_ctrl |= NAN_NDP_CTRL_SECURITY_PRESENT; + + /* + * The NDP attribute and the NDPE attribute are very similar in + * structure so handle them in the same way where possible. + */ + if (ndpe_supported) + wpabuf_put_u8(buf, NAN_ATTR_NDP_EXT); + else + wpabuf_put_u8(buf, NAN_ATTR_NDP); + + len_ptr = wpabuf_put(buf, 2); + + wpabuf_put_u8(buf, ndp_setup->dialog_token); + wpabuf_put_u8(buf, type | + (ndp_setup->status << NAN_NDP_STATUS_POS)); + wpabuf_put_u8(buf, ndp_setup->reason); + wpabuf_put_data(buf, ndp_setup->ndp->init_ndi, ETH_ALEN); + wpabuf_put_u8(buf, ndp_setup->ndp->ndp_id); + wpabuf_put_u8(buf, ndp_ctrl); + + if (ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT) + wpabuf_put_u8(buf, ndp_setup->publish_inst_id); + + if (ndp_ctrl & NAN_NDP_CTRL_RESPONDER_NDI_PRESENT) + wpabuf_put_data(buf, ndp_setup->ndp->resp_ndi, ETH_ALEN); + + if (add_srv_info) { + if (!ndpe_supported) { + wpabuf_put_data(buf, ndp_setup->ssi, + ndp_setup->ssi_len); + } else { + /* + * TODO: For the service specific info use the WFA + * format. If there is a need to support other vendor + * OUIs, this would need to be extended. + */ + wpabuf_put_u8(buf, NAN_NDPE_TLV_SRV_INFO); + wpabuf_put_le16(buf, 4 + ndp_setup->ssi_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, NAN_SRV_PROTO_GENERIC); + wpabuf_put_data(buf, ndp_setup->ssi, + ndp_setup->ssi_len); + } + } + + if (ndpe_supported && ndp_setup->local_interface_id_valid) { + wpabuf_put_u8(buf, NAN_NDPE_TLV_IPV6_LINK_LOCAL); + wpabuf_put_le16(buf, NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + wpabuf_put_data(buf, ndp_setup->local_interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + } + + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + return 0; +} + + +/** + * nan_ndp_setup_reset - Reset the ndp_setup state + * @nan: NAN module context from nan_init() + * @peer: The peer that requires ndp setup reset + */ +void nan_ndp_setup_reset(struct nan_data *nan, struct nan_peer *peer) +{ + if (!peer) + return; + + os_free(peer->ndp_setup.ssi); + os_free(peer->ndp_setup.ndp); + + os_memset(&peer->ndp_setup, 0, sizeof(peer->ndp_setup)); + nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_NONE); +} + + +/** + * nan_ndp_setup_failure - Indicate failure during NDP setup + * @nan: NAN module context from nan_init() + * @peer: The peer from which the original message was received + * @reason: The failure reason + * @reset_state: Reset the NDP state iff equals to true. + */ +void nan_ndp_setup_failure(struct nan_data *nan, struct nan_peer *peer, + enum nan_reason reason, bool reset_state) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + + wpa_printf(MSG_DEBUG, "NAN: NDP: setup failure: peer " MACSTR + ". state=%s (%u). reason=%u", + MAC2STR(peer->nmi_addr), nan_ndp_state_str(ndp_setup->state), + ndp_setup->state, reason); + + if (reset_state) { + nan_ndp_setup_reset(nan, peer); + } else { + ndp_setup->status = NAN_NDP_STATUS_REJECTED; + ndp_setup->reason = reason; + } +} + + +/** + * nan_ndp_naf_sent - Indicate a NAF has been sent + * @nan: NAN module context from nan_init() + * @peer: The peer with whom the NDP is being setup + * @subtype: The NAN OUI subtype. See &enum nan_subtype + * + * Notification, indicating to the NDP SM to that a NAF was sent, so the + * NDP SM could update its state. + */ +int nan_ndp_naf_sent(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype) +{ + struct nan_ndp_setup *ndp_setup; + + if (!peer || !peer->ndp_setup.ndp) + return -1; + + ndp_setup = &peer->ndp_setup; + + wpa_printf(MSG_DEBUG, "NAN: NDP: Tx done: state=%s (%d), status=%d", + nan_ndp_state_str(ndp_setup->state), + ndp_setup->state, ndp_setup->status); + + /* + * Note: Due to races between the Tx status and Rx path, it is possible + * that the Tx status is received after the peer response was already + * processed (which can result with another frame being sent). In such a + * case the logic above fast-forwards the state, and the transitions + * here need to take this into consideration. + */ + switch (ndp_setup->state) { + case NAN_NDP_STATE_START: + if (subtype == NAN_SUBTYPE_DATA_PATH_REQUEST) + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_REQ_SENT); + break; + case NAN_NDP_STATE_REQ_RECV: + if (subtype == NAN_SUBTYPE_DATA_PATH_RESPONSE) { + if (ndp_setup->status == NAN_NDP_STATUS_ACCEPTED) + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_DONE); + else + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_RES_SENT); + } + break; + case NAN_NDP_STATE_RES_RECV: + if (subtype == NAN_SUBTYPE_DATA_PATH_CONFIRM) { + if (ndp_setup->status == NAN_NDP_STATUS_ACCEPTED) + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_DONE); + else + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_CON_SENT); + } + break; + case NAN_NDP_STATE_CON_RECV: + if (subtype == NAN_SUBTYPE_DATA_PATH_KEY_INSTALL && + ndp_setup->status == NAN_NDP_STATUS_ACCEPTED) + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_DONE); + break; + case NAN_NDP_STATE_REQ_SENT: + case NAN_NDP_STATE_RES_SENT: + case NAN_NDP_STATE_CON_SENT: + case NAN_NDP_STATE_DONE: + default: + break; + } + + return 0; +} + + +/* + * nan_ndp_term_req - Handle local NDP termination request + * @nan: NAN module context from nan_init() + * @peer: The peer with whom the NDP is being setup + * @ndp_id: NDP identifier + * Returns: 0 on success, -1 on failure + */ +int nan_ndp_term_req(struct nan_data *nan, struct nan_peer *peer, + struct nan_ndp_id *ndp_id) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + struct nan_ndp *pndp; + + wpa_printf(MSG_DEBUG, + "NAN: NDP: Terminate request with peer=" MACSTR + " ndp_id=%u, init_ndi=" MACSTR, + MAC2STR(peer->nmi_addr), ndp_id->id, + MAC2STR(ndp_id->init_ndi)); + + if (ndp_setup->ndp) { + if (ndp_setup->ndp->ndp_id == ndp_id->id && + ether_addr_equal(ndp_setup->ndp->init_ndi, + ndp_id->init_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: WIP with peer. Terminate"); + + nan_ndp_set_state(nan, &peer->ndp_setup, + NAN_NDP_STATE_DONE); + ndp_setup->status = NAN_NDP_STATUS_REJECTED; + ndp_setup->reason = NAN_REASON_UNSPECIFIED_REASON; + return 0; + } + + wpa_printf(MSG_DEBUG, + "NAN: NDP: Cannot terminate NDP while NDP establishment is WIP"); + return -1; + } + + /* Find the NDP in the list of active NDPs */ + pndp = nan_ndp_find_ndp(peer, ndp_id->id, ndp_id->init_ndi); + if (!pndp) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: Termination request for unknown NDP"); + return -1; + } + + /* Remove the NDP from the list and setup data for the termination */ + dl_list_del(&pndp->list); + + peer->ndp_setup.ndp = pndp; + peer->ndp_setup.status = NAN_NDP_STATUS_REJECTED; + peer->ndp_setup.reason = NAN_REASON_UNSPECIFIED_REASON; + return 0; +} + + +/** + * nan_ndp_requested_gtk_csid - Get the GTK CSID requested by peer for NDP setup + * @nan: NAN module context from nan_init() + * @ndp_id: NDP identifier + * Returns: The GTK CSID requested by peer, or NAN_CS_NONE if no matching NDP is + * found or GTK is not requested by peer. + */ +int nan_ndp_requested_gtk_csid(struct nan_data *nan, + const struct nan_ndp_id *ndp_id) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan, ndp_id->peer_nmi); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: No matching peer found for GTK CSID request"); + return NAN_CS_NONE; + } + + if (!peer->ndp_setup.ndp || + peer->ndp_setup.ndp->ndp_id != ndp_id->id || + !ether_addr_equal(peer->ndp_setup.ndp->init_ndi, + ndp_id->init_ndi)) { + wpa_printf(MSG_DEBUG, + "NAN: NDP: No matching NDP found for GTK CSID request"); + return NAN_CS_NONE; + } + + if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) + return NAN_CS_NONE; + + return peer->ndp_setup.sec.peer_gtk.csid; +} diff --git a/contrib/wpa/src/nan/nan_pairing.c b/contrib/wpa/src/nan/nan_pairing.c new file mode 100644 index 000000000000..cb3b854fb64b --- /dev/null +++ b/contrib/wpa/src/nan/nan_pairing.c @@ -0,0 +1,1625 @@ +/* + * Wi-Fi Aware - NAN pairing module + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "pasn/pasn_common.h" +#include "nan/nan_i.h" + +static void nan_pairing_prepare_pasn_elems(struct nan_data *nan_data, + struct nan_peer *peer, + struct wpabuf *extra_ies, + int publish_id, int auth_mode); +static int nan_pairing_pasn_initialize(struct nan_data *nan_data, + struct nan_peer *peer, u8 auth_mode, + int cipher, const char *password, + enum nan_pairing_role self_role); + +/** + * nan_nira_get_tag_nonce - Generate NIRA nonce and compute NIRA tag + * @nan: Pointer to NAN configuration structure + * @nonce: Buffer to store the generated NIRA nonce (output) + * @tag: Buffer to store the computed NIRA tag (output) + * Returns: 0 on success, -1 on failure + * + * This function generates a random NIRA (NAN Identity Resolution Attribute) + * nonce and derives the corresponding NIRA tag using the NIK (NAN Identity + * Key), NMI address, and the generated nonce. + * + * The caller must ensure that nonce buffer is at least NAN_NIRA_NONCE_LEN bytes + * and tag buffer is at least NAN_NIRA_TAG_LEN bytes. + */ +int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nonce, u8 *tag) +{ + struct wpabuf *tag_buf; + + if (os_get_random(nonce, NAN_NIRA_NONCE_LEN) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to generate NIRA nonce"); + return -1; + } + + tag_buf = nan_crypto_derive_nira_tag(nan->nik, NAN_NIK_LEN, + nan->nmi_addr, nonce); + if (!tag_buf) + return -1; + + os_memcpy(tag, wpabuf_head(tag_buf), NAN_NIRA_TAG_LEN); + wpabuf_free(tag_buf); + + wpa_hexdump_key(MSG_DEBUG, "NAN: NIK", nan->nik, NAN_NIK_LEN); + wpa_hexdump(MSG_DEBUG, "NAN: NIRA-NONCE", nonce, NAN_NIRA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "NAN: NIRA-TAG", tag, NAN_NIRA_TAG_LEN); + return 0; +} + + +/** + * nan_pairing_add_attrs - Add NAN pairing attributes to a buffer + * @nan: Pointer to NAN data structure containing configuration + * @buf: Pointer to wpabuf where attributes will be added + * Returns: 0 on success, -1 otherwise + * + * This function adds NAN attributes that indicate pairing capabilities + * to the provided buffer. + */ +int nan_pairing_add_attrs(struct nan_data *nan, struct wpabuf *buf) +{ + if (!nan || !buf) + return -1; + + nan_add_dev_capa_ext_attr(nan, buf); + + if (nan->cfg->pairing_cfg.pairing_verification) { + if (nan_add_nira(buf, nan->nira_tag, nan->nira_nonce)) { + wpa_printf(MSG_INFO, "NAN: Failed to add NIRA"); + return -1; + } + } + + return 0; +} + + +void nan_pairing_deinit_peer(struct nan_peer *peer) +{ + wpabuf_free(peer->pairing.pending_auth1); + peer->pairing.pending_auth1 = NULL; + + if (!peer->pairing.pasn) + return; + + wpa_pasn_reset(peer->pairing.pasn); + pasn_data_deinit(peer->pairing.pasn); + peer->pairing.pasn = NULL; + peer->pairing.self_pairing_role = NAN_PAIRING_ROLE_IDLE; +} + + +int nan_pairing_abort(struct nan_data *nan_data, const u8 *peer_addr) +{ + struct nan_peer *peer; + int cipher; + struct wpabuf *extra_ies; + int ret = -1; + + peer = nan_get_peer(nan_data, peer_addr); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing abort: Peer " MACSTR " not found", + MAC2STR(peer_addr)); + return -1; + } + + if (!peer->pairing.pasn && !peer->pairing.pending_auth1) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing abort: No PASN in progress with peer " + MACSTR, MAC2STR(peer_addr)); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: Aborting pairing with peer " MACSTR, + MAC2STR(peer_addr)); + + if (!peer->pairing.pending_auth1) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing abort: No pending Auth1 frame for peer " + MACSTR, MAC2STR(peer_addr)); + ret = 0; + goto done; + } + + /* The auth mode and cipher are not important when rejecting. + * Just make sure to use a supported cipher so + * nan_pairing_pasn_initialize() won't fail. + */ + cipher = (nan_data->cfg->pairing_cfg.cipher_suites & + NAN_PAIRING_PASN_128) ? WPA_CIPHER_CCMP : WPA_CIPHER_GCMP_256; + + if (nan_pairing_pasn_initialize(nan_data, peer, NAN_PASN_AUTH_MODE_PASN, + cipher, "", + NAN_PAIRING_ROLE_RESPONDER)) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Initialize failed"); + goto done; + } + + extra_ies = wpabuf_alloc(NAN_ELEMENT_MAX_SIZE); + if (!extra_ies) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to allocate buffer for extra elements"); + goto done; + } + + nan_pairing_prepare_pasn_elems(nan_data, peer, extra_ies, + peer->pairing.peer_instance_id, + NAN_PASN_AUTH_MODE_PASN); + pasn_set_extra_ies(peer->pairing.pasn, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + wpabuf_free(extra_ies); + + nan_configure_peer_schedule(nan_data, peer, &nan_data->sched); + + ret = handle_auth_pasn_resp(peer->pairing.pasn, nan_data->cfg->nmi_addr, + peer_addr, NULL, + WLAN_STATUS_UNSPECIFIED_FAILURE); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing abort: Failed to send response"); + nan_clear_peer_schedule(nan_data, peer); + } + +done: + nan_pairing_deinit_peer(peer); + return ret; +} + + +static bool nan_pairing_is_supported(struct nan_data *nan_data, + struct nan_peer *peer, u8 auth_mode) +{ + if (auth_mode == NAN_PASN_AUTH_MODE_PASN || + auth_mode == NAN_PASN_AUTH_MODE_SAE) { + if (!nan_data->cfg->pairing_cfg.pairing_setup) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Device doesn't support pairing setup"); + return false; + } + + if (!peer->pairing.pairing_cfg.pairing_setup) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Peer doesn't support pairing setup"); + return false; + } + } else if (auth_mode == NAN_PASN_AUTH_MODE_PMK) { + if (!nan_data->cfg->pairing_cfg.pairing_verification) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Device doesn't support pairing verification"); + return false; + } + + if (!peer->pairing.pairing_cfg.pairing_verification) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Peer doesn't support pairing verification"); + return false; + } + + if (!nan_data->cfg->get_npk_akmp) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: get_npk_akmp callback not set"); + return false; + } + } + + return true; +} + + +static int nan_pairing_set_password(struct pasn_data *pasn, + const char *passphrase) +{ +#ifdef CONFIG_SAE + struct sae_pt *pt; + + pt = sae_derive_pt(pasn->pasn_groups, (const u8 *) NAN_PASN_SSID, + os_strlen(NAN_PASN_SSID), (const u8 *) passphrase, + os_strlen(passphrase), NULL, 0); + if (pasn_set_pt(pasn, pt) < 0) { + wpa_printf(MSG_INFO, "NAN: Pairing: Failed to set SAE pt"); + sae_deinit_pt(pt); + return -1; + } + + return 0; +#else /* CONFIG_SAE */ + return -1; +#endif /* CONFIG_SAE */ +} + + +static struct wpabuf * nan_pairing_generate_rsnxe(int akmp) +{ + /* According to Wi-Fi Aware Specification version 4.0, Table 26, + * the RSNXE's capabilities field in NAN PASN Authentication frames is + * 16 bits long. + */ + u16 capab = 1; /* bit 0-3 = Field length (n - 1) */ + + struct wpabuf *buf; + + if (wpa_key_mgmt_sae(akmp)) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + + /* Element header (2 octets) + capabilities field (2 octets) */ + buf = wpabuf_alloc(4); + if (!buf) + return NULL; + + wpa_printf(MSG_DEBUG, "NAN: RSNXE capabilities: %04x", capab); + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, capab); + return buf; +} + + +static int nan_pairing_send_cb(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, unsigned int wait) +{ + struct nan_data *nan_data = (struct nan_data *) ctx; + + return nan_data->cfg->send_pasn(nan_data->cfg->cb_ctx, data, data_len); +} + + +/** + * nan_pasn_verification_init - Initialize PASN data for pairing verification + * @nan_data: Pointer to NAN data structure containing configuration + * @peer: Pointer to the NAN peer structure + * Returns: 0 on success, -1 on failure + * + * This function gets the NPK and AKMP for the given peer and sets it as the + * PASN PMK and AKMP. It also generates the NIRA nonce and tag to be used as the + * custom PMKID for the PASN verification process. + */ +static int nan_pasn_verification_init(struct nan_data *nan_data, + struct nan_peer *peer) +{ + struct nan_pairing_peer_data *pairing_data; + const struct wpabuf *npk; + int akmp; + u8 npkid[NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN]; + + pairing_data = &peer->pairing; + + if (!pairing_data->nonce_tag_valid) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: NIK ID not available for verification"); + return -1; + } + + npk = nan_data->cfg->get_npk_akmp(nan_data->cfg->cb_ctx, peer->nmi_addr, + pairing_data->nonce, + pairing_data->tag, &akmp); + if (!npk) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to get NPK AKMP for verification"); + return -1; + } + + pasn_set_akmp(pairing_data->pasn, akmp); + + if (pairing_data->self_pairing_role == NAN_PAIRING_ROLE_INITIATOR) + pasn_initiator_pmksa_cache_add(nan_data->initiator_pmksa, + nan_data->cfg->nmi_addr, + peer->nmi_addr, + wpabuf_head_u8(npk), + wpabuf_len(npk), NULL, akmp); + else + pasn_responder_pmksa_cache_add(nan_data->responder_pmksa, + nan_data->cfg->nmi_addr, + peer->nmi_addr, + wpabuf_head_u8(npk), + wpabuf_len(npk), NULL, akmp); + + /* + * According to Wi-Fi Aware Specification v4.0, section 7.6.5, pairing + * verification uses NPKID constructed from NIRA nonce and tag. The same + * nonce and tag should be used in the NIRA added to PASN first and + * second frames. + */ + if (nan_nira_get_tag_nonce(nan_data->cfg, npkid, + &npkid[NAN_NIRA_NONCE_LEN]) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Failed to get NIRA tag and nonce"); + return -1; + } + + pasn_set_custom_pmkid(pairing_data->pasn, npkid); + return 0; +} + + +static int nan_validate_custom_pmkid(void *ctx, const u8 *addr, const u8 *pmkid) +{ + /* + * In NAN pairing, custom PMKID is constructed from NIRA nonce and tag. + * Matching the tag to a known NIK is done during NIRA validation so + * here we just accept any PMKID. + */ + return 0; +} + + +static int nan_pairing_pasn_initialize(struct nan_data *nan_data, + struct nan_peer *peer, u8 auth_mode, + int cipher, const char *password, + enum nan_pairing_role self_role) +{ + struct wpabuf *rsnxe = NULL; + struct pasn_data *pasn; + struct nan_pairing_peer_data *pairing; + + pairing = &peer->pairing; + if (pairing->pasn) { + wpa_pasn_reset(pairing->pasn); + } else { + pairing->pasn = pasn_data_init(); + if (!pairing->pasn) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to initialize PASN data"); + return -1; + } + } + + pasn = pairing->pasn; + pasn_set_own_addr(pasn, nan_data->cfg->nmi_addr); + pasn_set_peer_addr(pasn, peer->nmi_addr); + pasn_set_bssid(pasn, nan_data->cluster_id); + + if (self_role == NAN_PAIRING_ROLE_INITIATOR) + pasn->pmksa = nan_data->initiator_pmksa; + else + pasn->pmksa = nan_data->responder_pmksa; + + if (cipher == WPA_CIPHER_GCMP_256 && + (nan_data->cfg->pairing_cfg.cipher_suites & NAN_PAIRING_PASN_256)) { + pasn->group = 20; + pasn->cipher = WPA_CIPHER_GCMP_256; + } else if (cipher == WPA_CIPHER_CCMP && + (nan_data->cfg->pairing_cfg.cipher_suites & + NAN_PAIRING_PASN_128)) { + pasn->group = 19; + pasn->cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, + "NAN: Pairing: Unsupported cipher suite %s", + wpa_cipher_txt(cipher)); + goto fail; + } + + pasn_enable_kdk_derivation(pasn); + + /* Set allowed PASN groups. This is needed for all modes */ + os_free(pasn->pasn_groups); + pasn->pasn_groups = os_calloc(2, sizeof(*pasn->pasn_groups)); + if (!pasn->pasn_groups) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to allocate PASN groups"); + goto fail; + } + pasn->pasn_groups[0] = pasn->group; + + if (auth_mode == NAN_PASN_AUTH_MODE_SAE) { + pasn_set_akmp(pasn, WPA_KEY_MGMT_SAE); + if (!password) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Password not available"); + goto fail; + } + + if (nan_pairing_set_password(pasn, password) < 0) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to set password"); + goto fail; + } + } else if (auth_mode == NAN_PASN_AUTH_MODE_PASN) { + pasn_set_akmp(pasn, WPA_KEY_MGMT_PASN); + pasn_set_noauth(pasn, true); + } else if (auth_mode == NAN_PASN_AUTH_MODE_PMK) { + if (nan_pasn_verification_init(nan_data, peer)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: PASN verification init failed"); + goto fail; + } + } else { + wpa_printf(MSG_INFO, + "NAN: Pairing: Unsupported authentication mode %u", + auth_mode); + goto fail; + } + + pasn_set_rsn_pairwise(pasn, pasn->cipher); + pasn_set_wpa_key_mgmt(pasn, pasn->akmp); + + if (auth_mode != NAN_PASN_AUTH_MODE_PASN) { + rsnxe = nan_pairing_generate_rsnxe(pasn->akmp); + if (!rsnxe) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to generate RSNXE"); + goto fail; + } + + pasn_set_rsnxe_ie(pairing->pasn, wpabuf_head_u8(rsnxe)); + wpabuf_free(rsnxe); + } + + pasn_register_callbacks(pasn, nan_data, nan_pairing_send_cb, + nan_validate_custom_pmkid, NULL, NULL); + return 0; + +fail: + pasn_data_deinit(pasn); + pairing->pasn = NULL; + return -1; +} + + +/* + * nan_pairing_prepare_pasn_elems - Prepare NAN element for pairing PASN frames + * @nan_data: Pointer to NAN data structure + * @peer: Pointer to NAN peer structure + * @extra_ies: Buffer to which the NAN element is appended + * @publish_id: Publish ID to use in the CSIA + * @auth_mode: Pairing authentication mode + * + * This function adds a NAN element containing the NAN attributes that shall be + * included in the first and second PASN frames for NAN pairing. + * The added attributes are: + * - Device Capability Extension attribute (DCEA) + * - Cipher suite information attribute (CSIA) with appropriate PASN cipher + * (either GCMP-256 or GCMP-128) + * - NAN Pairing Bootstrapping Attribute (NPBA) if available + */ +static void nan_pairing_prepare_pasn_elems(struct nan_data *nan_data, + struct nan_peer *peer, + struct wpabuf *extra_ies, + int publish_id, int auth_mode) +{ + u8 *len_ptr; + struct nan_cipher_suite cs; + size_t initial_len = wpabuf_len(extra_ies); + + wpabuf_put_u8(extra_ies, WLAN_EID_VENDOR_SPECIFIC); + + /* placeholder for length - to be filled later */ + len_ptr = wpabuf_put(extra_ies, 1); + + /* OUI + OUI Type */ + wpabuf_put_be32(extra_ies, NAN_IE_VENDOR_TYPE); + + if (peer->pairing.pasn->cipher == WPA_CIPHER_GCMP_256) + cs.csid = NAN_CS_PK_PASN_256; + else + cs.csid = NAN_CS_PK_PASN_128; + + cs.instance_id = publish_id; + + nan_add_csia(extra_ies, nan_data->cfg->security_capab, 1, &cs); + + if (auth_mode == NAN_PASN_AUTH_MODE_SAE || + auth_mode == NAN_PASN_AUTH_MODE_PASN) { + nan_add_dev_capa_ext_attr(nan_data, extra_ies); + if (peer->bootstrap.npba) + wpabuf_put_buf(extra_ies, peer->bootstrap.npba); + } else { + const u8 *npkid = peer->pairing.pasn->custom_pmkid; + + /* + * Add NIRA with the same nonce and tag as in the NPKID. + * NPKID is: NONCE || TAG. + */ + if (nan_add_nira(extra_ies, &npkid[NAN_NIRA_NONCE_LEN], + npkid)) { + wpa_printf(MSG_DEBUG, "NAN: Failed to add NIRA"); + } + } + + *len_ptr = wpabuf_len(extra_ies) - initial_len - 2; +} + + +/** + * nan_pairing_initiate_pasn_auth - Initiate PASN authentication for NAN pairing + * @nan_data: NAN data context + * @addr: MAC address of the peer device + * @auth_mode: Authentication mode to be used (PASN, SAE, or PMK) + * @cipher: Cipher suite to be used for the pairing + * @handle: Handle of the service instance for which pairing is requested + * @peer_instance_id: Instance ID of the peer service for which pairing is + * requested + * @responder: Whether this device is acting as PASN responder + * @password: Password to be used for authentication (if applicable) + * Returns: 0 on success, -1 on failure + */ +int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr, + u8 auth_mode, int cipher, int handle, + u8 peer_instance_id, bool responder, + const char *password, + const struct nan_schedule *sched) +{ + int ret = 0; + struct pasn_data *pasn; + struct nan_peer *peer; + struct wpabuf *extra_ies; + + if (!addr) { + wpa_printf(MSG_INFO, "NAN: Pairing: Peer address missing"); + return -1; + } + + peer = nan_get_peer(nan_data, addr); + if (!peer) { + wpa_printf(MSG_INFO, "NAN: Pairing: Peer not known"); + return -1; + } + + if (peer->bootstrap.in_progress) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Bootstrap in progress with peer"); + return -1; + } + + if (!nan_pairing_is_supported(nan_data, peer, auth_mode)) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Invalid params to initiate authentication"); + return -1; + } + + peer->pairing.self_pairing_role = responder ? + NAN_PAIRING_ROLE_RESPONDER : NAN_PAIRING_ROLE_INITIATOR; + + if (nan_pairing_pasn_initialize(nan_data, peer, auth_mode, cipher, + password, + peer->pairing.self_pairing_role)) { + wpa_printf(MSG_INFO, "NAN: Pairing: Initialize failed"); + return -1; + } + + pasn = peer->pairing.pasn; + + extra_ies = wpabuf_alloc(NAN_ELEMENT_MAX_SIZE); + if (!extra_ies) + return -1; + + /* TODO: Add support for NAN element fragmentation if it's larger than + * 255 octets, as defined in Wi-Fi Aware Specification v4.0 section 9.1. + */ + nan_pairing_prepare_pasn_elems(nan_data, peer, extra_ies, handle, + auth_mode); + pasn_set_extra_ies(pasn, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + wpabuf_free(extra_ies); + + peer->pairing.handle = handle; + peer->pairing.peer_instance_id = peer_instance_id; + peer->pairing.flags = 0; + + if (nan_configure_peer_schedule(nan_data, peer, sched)) + wpa_printf(MSG_DEBUG, "NAN: Could not configure peer schedule"); + + if (responder) { + if (peer->pairing.pending_auth1) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Responder - process pending Auth1"); + ret = nan_pairing_auth_rx( + nan_data, + wpabuf_head(peer->pairing.pending_auth1), + wpabuf_len(peer->pairing.pending_auth1)); + wpabuf_free(peer->pairing.pending_auth1); + peer->pairing.pending_auth1 = NULL; + + return ret; + } + return 0; + } + + if (auth_mode == NAN_PASN_AUTH_MODE_PMK) { + peer->pairing.flags |= NAN_PAIRING_FLAG_NPK_VERIFICATION; + ret = wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, + pasn->bssid, pasn->akmp, pasn->cipher, + pasn->group, 0, NULL, 0, NULL, 0, NULL); + } else { + ret = wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, + pasn->bssid, pasn->akmp, pasn->cipher, + pasn->group, 0, NULL, 0, NULL, 0, NULL); + } + + if (ret) { + wpa_printf(MSG_INFO, "NAN: Pairing: Failed to start PASN"); + nan_pairing_deinit_peer(peer); + } + + return ret; +} + + +/** + * nan_pairing_done - Derive NPK caching related keys after successful pairing + * @nan_data: NAN interface data + * @peer: NAN peer with which pairing is being completed + * + * This function completes the NAN pairing process by deriving the necessary + * cryptographic keys (KEK and NPK for opportunistic pairing) when NPK caching + * is enabled. + */ +static void nan_pairing_done(struct nan_data *nan_data, struct nan_peer *peer) +{ + u8 npk[NAN_NPK_LEN]; + struct pasn_data *pasn = peer->pairing.pasn; + int cipher = pasn_get_cipher(pasn); + u8 *initiator_nmi, *responder_nmi; + int ret; + + peer->pairing.flags |= NAN_PAIRING_FLAG_PAIRED; + + peer->pairing.pairing_csid = cipher == WPA_CIPHER_GCMP_256 ? + NAN_CS_PK_PASN_256 : NAN_CS_PK_PASN_128; + peer->pairing.pairing_akmp = pasn_get_akmp(pasn); + + if (!nan_data->cfg->pairing_cfg.npk_caching || + !peer->pairing.pairing_cfg.npk_caching || + (peer->pairing.flags & NAN_PAIRING_FLAG_NPK_VERIFICATION)) + return; + + wpa_printf(MSG_DEBUG, "NAN: Pairing: Derive KEK after PASN pairing"); + + if (peer->pairing.self_pairing_role == NAN_PAIRING_ROLE_INITIATOR) { + initiator_nmi = nan_data->cfg->nmi_addr; + responder_nmi = peer->nmi_addr; + } else { + initiator_nmi = peer->nmi_addr; + responder_nmi = nan_data->cfg->nmi_addr; + } + + ret = nan_crypto_derive_kek(pasn->ptk.kdk, pasn->ptk.kdk_len, + peer->pairing.pairing_csid, + initiator_nmi, responder_nmi, + &pasn->ptk); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Failed to derive KEK"); + return; + } + + /* For SAE AKMP, NPK was already derived inside the PASN module and + * stored in pasn->pmk. For PASN AKMP, derive NPK here and configure it + * to the PASN module. The NPK will be stored alongside the peer's NIK + * when the NIK is received from the peer. + */ + if (pasn_get_akmp(pasn) != WPA_KEY_MGMT_PASN) + return; + + wpa_printf(MSG_DEBUG, "NAN: Pairing: Derive NPK after PASN pairing"); + + ret = nan_crypto_derive_npk(pasn->ptk.kdk, pasn->ptk.kdk_len, + peer->pairing.pairing_csid, + initiator_nmi, responder_nmi, npk, + sizeof(npk)); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Failed to derive NPK"); + return; + } + + os_memcpy(pasn->pmk, npk, NAN_NPK_LEN); + pasn->pmk_len = NAN_NPK_LEN; +} + + +/** + * nan_nik_build_key_data - Build NAN Identity Key (NIK) key data buffer + * @nan_data: Pointer to NAN data structure containing configuration + * Returns: Pointer to allocated wpabuf containing the key data, or NULL + * on failure. + * + * This function constructs a buffer containing NAN key data elements including: + * - NIK KDE (Key Data Encapsulation) with cipher version and NIK value + * - Key Lifetime KDE indicating the NIK key lifetime + * + * Note: Caller is responsible for freeing the returned buffer. + */ +static struct wpabuf * nan_nik_build_key_data(struct nan_data *nan_data) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(KDE_HDR_LEN + sizeof(struct nan_nik_kde) + + KDE_HDR_LEN + sizeof(struct nan_key_lifetime_kde)); + if (!buf) + return NULL; + + nan_add_kde_hdr(buf, NAN_KEY_DATA_NIK, sizeof(struct nan_nik_kde)); + wpabuf_put_u8(buf, NAN_NIRA_CIPHER_VER_128); + wpabuf_put_data(buf, nan_data->cfg->nik, sizeof(nan_data->cfg->nik)); + + nan_add_kde_hdr(buf, NAN_KEY_DATA_LIFETIME, + sizeof(struct nan_key_lifetime_kde)); + wpabuf_put_le16(buf, NAN_KEY_LIFETIME_NIK); + wpabuf_put_be32(buf, nan_data->cfg->nik_lifetime); + + return buf; +} + + +/** + * nan_send_nik - Send NAN Identity Key (NIK) to a peer + * @nan_data: Pointer to NAN data structure containing configuration and state + * @peer: Pointer to the NAN peer structure to send the NIK to + * Returns: 0 on success, -1 in case of an error + * + * This function sends the NAN Identity Key (NIK) and the NIK lifetime to a peer + * device as part of the NAN pairing process. The NIK is encrypted using the KEK + * (Key Encryption Key) derived from PASN and sent in a Shared Key Descriptor + * Attribute (SKDA) within a follow-up message. + */ +static int nan_send_nik(struct nan_data *nan_data, struct nan_peer *peer) +{ + struct wpabuf *skda, *key_data; + struct wpa_eapol_key *key_desc; + u16 info, key_len; + int ret; + struct wpabuf *encrypted_key_data = NULL; + size_t skda_len; + + if (!nan_data->cfg->pairing_cfg.npk_caching) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Local NPK caching not enabled, don't send NIK"); + return 0; + } + + if (!peer->pairing.pairing_cfg.npk_caching) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Peer NPK caching not enabled, don't send NIK"); + return 0; + } + + if (peer->pairing.flags & NAN_PAIRING_FLAG_NPK_VERIFICATION) + return 0; + + if (!peer->pairing.pasn || !peer->pairing.pasn->ptk.kek_len) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: KEK not available for NIK encryption"); + return -1; + } + + key_data = nan_nik_build_key_data(nan_data); + if (!key_data) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to build NIK key data"); + return -1; + } + + /* Encrypt the key data using the KEK from the PASN data */ + encrypted_key_data = nan_crypto_encrypt_key_data( + key_data, peer->pairing.pasn->ptk.kek, + peer->pairing.pasn->ptk.kek_len); + wpabuf_clear_free(key_data); + if (!encrypted_key_data) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to encrypt NIK key data"); + return -1; + } + + skda_len = sizeof(struct nan_shared_key) + + sizeof(struct wpa_eapol_key) + 2 + + wpabuf_len(encrypted_key_data); + + skda = wpabuf_alloc(NAN_ATTR_HDR_LEN + skda_len); + if (!skda) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to allocate SKDA buffer"); + wpabuf_free(encrypted_key_data); + return -1; + } + + wpabuf_put_u8(skda, NAN_ATTR_SHARED_KEY_DESCR); + wpabuf_put_le16(skda, skda_len); + wpabuf_put_u8(skda, peer->pairing.handle); + + key_desc = wpabuf_put(skda, sizeof(*key_desc)); + os_memset(key_desc, 0, sizeof(*key_desc)); + + key_desc->type = NAN_KEY_DESC; + info = WPA_KEY_INFO_TYPE_AKM_DEFINED | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_ENCR_KEY_DATA; + WPA_PUT_BE16(key_desc->key_info, info); + + key_len = wpa_cipher_key_len(peer->pairing.pasn->cipher); + WPA_PUT_BE16(key_desc->key_length, key_len); + + wpabuf_put_be16(skda, wpabuf_len(encrypted_key_data)); + wpabuf_put_buf(skda, encrypted_key_data); + + ret = nan_data->cfg->transmit_followup(nan_data->cfg->cb_ctx, + peer->nmi_addr, skda, + peer->pairing.handle, + peer->pairing.peer_instance_id); + + wpabuf_free(encrypted_key_data); + wpabuf_free(skda); + + return ret; +} + + +static int nan_pairing_derive_nd_pmk(struct nan_data *nan_data, + struct nan_peer *peer, u8 *nd_pmk) +{ + struct pasn_data *pasn = peer->pairing.pasn; + int cipher = pasn_get_cipher(pasn); + enum nan_cipher_suite_id csid; + const u8 *initiator_nmi, *responder_nmi; + int ret; + + wpa_printf(MSG_DEBUG, "NAN: Pairing: Derive ND-PMK after PASN pairing"); + + if (peer->pairing.self_pairing_role == NAN_PAIRING_ROLE_INITIATOR) { + initiator_nmi = nan_data->cfg->nmi_addr; + responder_nmi = peer->nmi_addr; + } else { + initiator_nmi = peer->nmi_addr; + responder_nmi = nan_data->cfg->nmi_addr; + } + + csid = cipher == WPA_CIPHER_GCMP_256 ? NAN_CS_PK_PASN_256 : + NAN_CS_PK_PASN_128; + + ret = nan_crypto_derive_nd_pmk_from_kdk(pasn->ptk.kdk, + pasn->ptk.kdk_len, csid, + initiator_nmi, responder_nmi, + nd_pmk); + if (ret) + wpa_printf(MSG_INFO, + "NAN: Pairing: Failed to derive ND PMK"); + return ret; +} + + +/** + * nan_pairing_pasn_auth_tx_status - Handle PASN Authentication frame TX status + * @nan: Pointer to NAN data structure + * @data: Pointer to the transmitted frame data + * @data_len: Length of the transmitted frame data in bytes + * @acked: Whether the frame was acknowledged + * Returns: 0 on success, -1 on error + * + * This function processes the transmission status of a PASN Authentication + * frame used in NAN pairing and triggers the pairing result callback in case + * PASN is done. + */ +int nan_pairing_pasn_auth_tx_status(struct nan_data *nan, const u8 *data, + size_t data_len, bool acked) +{ + int ret; + struct nan_peer *peer; + struct pasn_data *pasn; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + + if (!nan || !data || + data_len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -1; + + peer = nan_get_peer(nan, mgmt->da); + if (!peer) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Peer not found " MACSTR, + MAC2STR(mgmt->da)); + return -1; + } + + /* Pairing was rejected. Clear peer schedule if no active NDPs */ + if (!peer->pairing.pasn) { + if (dl_list_empty(&peer->ndps) && !peer->ndp_setup.ndp) + nan_clear_peer_schedule(nan, peer); + + return 0; + } + + pasn = peer->pairing.pasn; + + ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked); + if (ret == 1) { + u8 nd_pmk[PMK_LEN]; + + if (pasn->status == WLAN_STATUS_SUCCESS && + nan_pairing_derive_nd_pmk(nan, peer, nd_pmk)) { + pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to derive ND PMK"); + } + + ret = nan->cfg->pairing_result_cb(nan->cfg->cb_ctx, + peer->nmi_addr, pasn->akmp, + pasn->cipher, pasn->status, + &pasn->ptk, + pasn->status == + WLAN_STATUS_SUCCESS ? nd_pmk : + NULL); + forced_memzero(nd_pmk, PMK_LEN); + if (pasn->status != WLAN_STATUS_SUCCESS || ret < 0) { + nan_pairing_deinit_peer(peer); + return -1; + } + + nan_pairing_done(nan, peer); + + /* + * Allow the peer to install the keys before transmitting the + * follow-up. + */ + /* FIX: A blocking sleep should not really be used here, i.e., + * this needs to be removed or replace with a registered eloop + * timeout to avoid blocking the process. */ + os_sleep(0, 30000); + + if (nan_send_nik(nan, peer) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to send NIK"); + nan_pairing_deinit_peer(peer); + return -1; + } + } + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + return 0; +} + + +/** + * nan_parse_csia - Parse NAN Cipher Suite Info Attribute + * @csia: Pointer to the CSIA data buffer + * @len: Length of the CSIA data buffer + * @cs: Pointer to nan_cipher_suite structure to store parsed information + * Returns: 0 on success, -1 on failure + * + * Parses the NAN Cipher Suite Info Attribute (CSIA) and extracts the cipher + * suite ID (csid) and instance ID from the attribute. It is assumed that only + * one cipher suite is present in the attribute (which is the case for NAN + * pairing). + */ +static int nan_parse_csia(const u8 *csia, size_t len, + struct nan_cipher_suite *cs) +{ + /* Capabilities (1) + Cipher Suite list (2) */ + if (len < sizeof(struct nan_cipher_suite_info) + + sizeof(struct nan_cipher_suite)) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: CSIA too short"); + return -1; + } + + cs->csid = csia[1]; + cs->instance_id = csia[2]; + + if (cs->csid != NAN_CS_PK_PASN_128 && cs->csid != NAN_CS_PK_PASN_256) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Unsupported cipher suite in CSIA: %u", + cs->csid); + return -1; + } + + return 0; +} + + +/** + * nan_pairing_process_elems - Process NAN pairing information elements + * @nan_data: NAN state data + * @peer: NAN peer information structure + * @mgmt: PASN Authentication frame + * @len: Length of the PASN Authentication frame + * @cs: Output cipher suite structure to be filled + * Returns: 0 on success, -1 on failure + * + * This function processes NAN pairing information elements from a PASN + * Authentication frame. It extracts the selected cipher suite and intance ID. + */ +static int nan_pairing_process_elems(struct nan_data *nan_data, + struct nan_peer *peer, + const struct ieee80211_mgmt *mgmt, + size_t len, struct nan_cipher_suite *cs) +{ + const u8 *ies; + size_t ies_len; + const u8 *buf; + struct wpabuf *ie_buf; + struct nan_attrs attrs; + int ret; + + if (len < offsetof(struct ieee80211_mgmt, u.auth.variable)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: PASN frame too short for NAN elements"); + return -1; + } + + ies = mgmt->u.auth.variable; + ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + buf = get_vendor_ie(ies, ies_len, NAN_IE_VENDOR_TYPE); + if (!buf) + return -1; + + ie_buf = ieee802_11_defrag(buf + 2, buf[1], false); + if (!ie_buf) + return -1; + + buf = wpabuf_head(ie_buf); + ret = nan_parse_attrs(nan_data, &buf[4], wpabuf_len(ie_buf) - 4, + &attrs); + if (ret) + goto fail; + + nan_parse_peer_dev_capa_ext(nan_data, peer, &attrs); + + if (!attrs.cipher_suite_info || !attrs.cipher_suite_info_len || + nan_parse_csia(attrs.cipher_suite_info, attrs.cipher_suite_info_len, + cs) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: CSIA missing or invalid"); + ret = -1; + } + + + nan_attrs_clear(nan_data, &attrs); +fail: + wpabuf_free(ie_buf); + return ret; +} + + +/** + * nan_pairing_handle_auth_1 - Handle the first PASN frame in NAN pairing + * @nan_data: Pointer to NAN data structure + * @own_addr: Own MAC address + * @peer: Pointer to NAN peer structure + * @mgmt: Pointer to the received PASN frame + * @len: Length of the PASN frame + * Returns: 0 on success, -1 on failure + * + * This function processes the first PASN Authentication frame during NAN + * pairing as a responder. It initializes the PASN data structure, prepares + * the necessary information elements, and delegates to the PASN module to + * handle the authentication. + */ +static int nan_pairing_handle_auth_1(struct nan_data *nan_data, u8 *own_addr, + struct nan_peer *peer, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct nan_cipher_suite cs; + struct pasn_data *pasn; + int cipher; + + if (peer->pairing.self_pairing_role != NAN_PAIRING_ROLE_RESPONDER) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Unexpected Auth1 frame"); + return -1; + } + + pasn = peer->pairing.pasn; + + if (nan_pairing_process_elems(nan_data, peer, mgmt, len, &cs)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Handle Auth1 NAN attributes failed"); + return -1; + } + + cipher = cs.csid == NAN_CS_PK_PASN_256 ? WPA_CIPHER_GCMP_256 : + WPA_CIPHER_CCMP; + + if (cipher != pasn->cipher) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Cipher suite mismatch (CSIA: %s, PASN: %s)", + wpa_cipher_txt(cipher), + wpa_cipher_txt(pasn->cipher)); + return -1; + } + + if (handle_auth_pasn_1(pasn, own_addr, peer->nmi_addr, mgmt, len, + false) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Handle Auth1 failed"); + return -1; + } + + return 0; +} + + +static int nan_pairing_handle_auth_2(struct nan_data *nan_data, + struct nan_peer *peer, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct wpa_pasn_params_data pasn_data; + struct pasn_data *pasn = peer->pairing.pasn; + + if (wpa_pasn_auth_rx(peer->pairing.pasn, (const u8 *)mgmt, len, + &pasn_data) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: wpa_pasn_auth_rx() failed"); + nan_data->cfg->pairing_result_cb( + nan_data->cfg->cb_ctx, peer->nmi_addr, pasn->akmp, + pasn->cipher, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, + NULL); + nan_pairing_deinit_peer(peer); + return -1; + } + + return 0; +} + + +static int nan_pairing_handle_auth_3(struct nan_data *nan_data, + struct nan_peer *peer, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct pasn_data *pasn = peer->pairing.pasn; + int ret; + u16 status = WLAN_STATUS_SUCCESS; + u8 nd_pmk[PMK_LEN]; + + ret = handle_auth_pasn_3(pasn, nan_data->cfg->nmi_addr, peer->nmi_addr, + mgmt, len); + if (ret < 0) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_printf(MSG_DEBUG, "NAN: Pairing: Handle Auth3 failed"); + } else { + if (nan_pairing_derive_nd_pmk(nan_data, peer, nd_pmk)) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to derive ND PMK"); + } + } + + ret = nan_data->cfg->pairing_result_cb(nan_data->cfg->cb_ctx, + peer->nmi_addr, pasn->akmp, + pasn->cipher, status, + &pasn->ptk, + status == WLAN_STATUS_SUCCESS ? + nd_pmk : NULL); + forced_memzero(nd_pmk, PMK_LEN); + if (ret < 0 || status != WLAN_STATUS_SUCCESS) + nan_pairing_deinit_peer(peer); + else if (status == WLAN_STATUS_SUCCESS) + nan_pairing_done(nan_data, peer); + + /* Don't clear PASN data if pairing is successful. If caching is + * enabled, it will still be needed when the NIK is received from + * the peer. + */ + return status == WLAN_STATUS_SUCCESS ? ret : -1; +} + + +/** + * nan_pairing_auth_rx - Handle received NAN pairing Authentication frames + * @nan_data: Pointer to NAN data structure + * @mgmt: Pointer to the PASN Authentication frame + * @len: Length of the PASN Authentication frame in bytes + * Returns: 0 on success, -1 on failure + */ +int nan_pairing_auth_rx(struct nan_data *nan_data, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct nan_peer *peer; + u16 auth_alg, auth_transaction, status_code; + int ret; + struct wpabuf *nan_ie; + const u8 *buf; + + if (len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -1; + + if (!ether_addr_equal(mgmt->da, nan_data->cfg->nmi_addr)) { + wpa_printf(MSG_DEBUG, "NAN: Pairing: Not our frame"); + return -1; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + + if (auth_alg != WLAN_AUTH_PASN) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Not a PASN frame, auth_alg=%d", + auth_alg); + return -1; + } + + buf = get_vendor_ie(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + NAN_IE_VENDOR_TYPE); + if (!buf) + return -1; + + nan_ie = ieee802_11_defrag(buf + 2, buf[1], false); + if (!nan_ie) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: No NAN element in PASN Authentication frame"); + return -1; + } + + ret = nan_add_peer(nan_data, mgmt->sa, wpabuf_head_u8(nan_ie) + 4, + wpabuf_len(nan_ie) - 4); + wpabuf_free(nan_ie); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to add peer from PASN"); + return -1; + } + + peer = nan_get_peer(nan_data, mgmt->sa); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to get a peer that was just added"); + return -1; + } + + if (!peer->pairing.pasn) { + if (status_code == WLAN_STATUS_SUCCESS && + auth_transaction == 1) { + struct nan_cipher_suite cs; + const u8 *rsne; + struct wpa_ie_data rsn_data; + + if (nan_pairing_process_elems(nan_data, peer, mgmt, len, + &cs)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Handle Auth1 NAN attributes failed"); + return -1; + } + + rsne = get_ie(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + WLAN_EID_RSN); + if (!rsne) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: RSNE missing in Auth1"); + return -1; + } + + if (wpa_parse_wpa_ie_rsn(rsne, rsne[1] + 2, + &rsn_data)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to parse RSNE in Auth1"); + return -1; + } + + wpabuf_free(peer->pairing.pending_auth1); + peer->pairing.pending_auth1 = + wpabuf_alloc_copy(mgmt, len); + if (!peer->pairing.pending_auth1) + return -1; + + nan_data->cfg->pairing_request(nan_data->cfg->cb_ctx, + peer->nmi_addr, cs.csid, + cs.instance_id, + &rsn_data); + return 0; + } + + wpa_printf(MSG_DEBUG, + "NAN: Pairing: PASN data not initialized for peer"); + return -1; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + struct pasn_data *pasn = peer->pairing.pasn; + + nan_data->cfg->pairing_result_cb(nan_data->cfg->cb_ctx, + peer->nmi_addr, pasn->akmp, + pasn->cipher, status_code, + NULL, NULL); + nan_pairing_deinit_peer(peer); + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Authentication rejected - status=%u", + status_code); + return -1; + } + + if (auth_transaction == 1) + return nan_pairing_handle_auth_1(nan_data, + nan_data->cfg->nmi_addr, peer, + mgmt, len); + if (auth_transaction == 2) + return nan_pairing_handle_auth_2(nan_data, peer, mgmt, len); + if (auth_transaction == 3) + return nan_pairing_handle_auth_3(nan_data, peer, mgmt, len); + + return -1; +} + + +/** + * nan_pairing_followup_rx - Process received NAN pairing follow-up frame + * @nan_data: NAN data context + * @peer_addr: MAC address of the peer device + * @shared_key_descr: Pointer to the shared key descriptor attribute + * @attr_len: Length of the shared key descriptor attribute + * Returns: true if the follow-up frame was processed, false otherwise. + * + * This function processes a received NAN pairing follow-up frame. It extracts + * the NIK (NAN Identity Key) from the frame and notifies about the received + * NIK. + * + * If the local device acted as the responder in the pairing process, it also + * sends the local NIK to the peer. + */ +bool nan_pairing_followup_rx(struct nan_data *nan_data, const u8 *peer_addr, + const struct nan_shared_key *shared_key_descr, + size_t attr_len) +{ + struct nan_peer *peer; + struct pasn_data *pasn; + const struct wpa_eapol_key *key_desc; + struct wpa_eapol_ie_parse ie; + const struct nan_nik_kde *nik_kde; + const struct nan_key_lifetime_kde *lifetime_kde; + const u8 *pos; + struct wpabuf *key_data = NULL; + u16 key_data_len, key_info; + bool ret = false; + u16 lifetime_bitmap; + + peer = nan_get_peer(nan_data, peer_addr); + if (!peer) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Follow-up frame from unknown peer"); + return false; + } + + pasn = peer->pairing.pasn; + if (!pasn || !pasn->ptk.kek_len) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: No PASN data for follow-up frame"); + return false; + } + + if (!nan_data->cfg->pairing_cfg.npk_caching) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: NPK caching not enabled, ignore follow-up frame"); + return false; + } + + key_desc = (const struct wpa_eapol_key *) shared_key_descr->key; + key_info = WPA_GET_BE16(key_desc->key_info); + + if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Follow-up frame does not contain pairwise key"); + return false; + } + + if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Follow-up frame does not contain encrypted key data"); + return false; + } + + if (attr_len < sizeof(*shared_key_descr) + sizeof(*key_desc) + 2) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Follow-up frame too short for Key Data Length field"); + return false; + } + + pos = shared_key_descr->key + sizeof(*key_desc); + key_data_len = WPA_GET_BE16(pos); + + if (attr_len < sizeof(*shared_key_descr) + sizeof(*key_desc) + 2 + + key_data_len) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Follow-up frame too short for Key Data field"); + return false; + } + + pos += 2; + + key_data = nan_crypto_decrypt_key_data(pasn->ptk.kek, pasn->ptk.kek_len, + pos, key_data_len); + if (!key_data) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to decrypt key data in follow-up frame"); + goto fail; + } + + if (wpa_parse_kde_ies(wpabuf_head(key_data), wpabuf_len(key_data), + &ie) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Failed to parse decrypted key data in follow-up frame"); + goto fail; + } + + if (!ie.nan_nik) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: NIK KDE missing in decrypted key data"); + goto fail; + } + + nik_kde = (const struct nan_nik_kde *) ie.nan_nik; + if (nik_kde->cipher_ver != NAN_NIRA_CIPHER_VER_128) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Unsupported NIK cipher version: %u", + nik_kde->cipher_ver); + goto fail; + } + + if (!ie.nan_key_lifetime) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Key Lifetime KDE missing in decrypted key data"); + goto fail; + } + + lifetime_kde = (const struct nan_key_lifetime_kde *) + ie.nan_key_lifetime; + lifetime_bitmap = le_to_host16(lifetime_kde->key_bitmap); + if (!(lifetime_bitmap & NAN_KEY_LIFETIME_NIK)) { + wpa_printf(MSG_DEBUG, + "NAN: Pairing: Unexpected key bitmap in Key " + "Lifetime KDE: 0x%02x", + lifetime_bitmap); + goto fail; + } + + nan_data->cfg->update_pairing_credentials( + nan_data->cfg->cb_ctx, nik_kde->nik, NAN_NIK_LEN, + nik_kde->cipher_ver, be_to_host32(lifetime_kde->lifetime_sec), + pasn_get_akmp(pasn), + pasn_get_pmk(pasn), pasn_get_pmk_len(pasn)); + + if (peer->pairing.self_pairing_role == NAN_PAIRING_ROLE_RESPONDER) + nan_send_nik(nan_data, peer); + + ret = true; +fail: + nan_pairing_deinit_peer(peer); + wpabuf_free(key_data); + return ret; +} + + +int nan_pairing_set_pairing_setup(struct nan_data *nan, bool value) +{ + wpa_printf(MSG_DEBUG, "NAN: SET: Pairing setup: %d -> %d", + nan->cfg->pairing_cfg.pairing_setup, value); + nan->cfg->pairing_cfg.pairing_setup = value; + return 0; +} + + +int nan_pairing_set_npk_caching(struct nan_data *nan, bool value) +{ + wpa_printf(MSG_DEBUG, "NAN: SET: NPK caching: %d -> %d", + nan->cfg->pairing_cfg.npk_caching, value); + nan->cfg->pairing_cfg.npk_caching = value; + return 0; +} + + +int nan_pairing_set_pairing_verification(struct nan_data *nan, bool value) +{ + wpa_printf(MSG_DEBUG, "NAN: SET: Pairing verification: %d -> %d", + nan->cfg->pairing_cfg.pairing_verification, value); + + if (!nan->cfg->pairing_cfg.pairing_verification && value && + nan_nira_get_tag_nonce(nan->cfg, nan->nira_nonce, + nan->nira_tag) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to enable pairing verification"); + return -1; + } + + nan->cfg->pairing_cfg.pairing_verification = value; + + return 0; +} + + +int nan_pairing_set_cipher_suites(struct nan_data *nan, u32 value) +{ + if (value & ~(NAN_PAIRING_PASN_128 | NAN_PAIRING_PASN_256)) { + wpa_printf(MSG_INFO, + "NAN: Pairing: Invalid cipher suites 0x%08x", value); + return -1; + } + + wpa_printf(MSG_DEBUG, + "NAN: SET: Pairing cipher suites: 0x%08x -> 0x%08x", + nan->cfg->pairing_cfg.cipher_suites, value); + + nan->cfg->pairing_cfg.cipher_suites = value; + return 0; +} + + +int nan_pairing_set_nik(struct nan_data *nan, const u8 *nik, size_t nik_len) +{ + u8 nonce[NAN_NIRA_NONCE_LEN]; + u8 tag[NAN_NIRA_TAG_LEN]; + + if (!nik || nik_len != NAN_NIK_LEN) { + wpa_printf(MSG_INFO, "NAN: Pairing: Invalid NIK (len=%zu)", + nik_len); + return -1; + } + + os_memcpy(nan->cfg->nik, nik, NAN_NIK_LEN); + + if (nan->cfg->pairing_cfg.pairing_verification) { + if (nan_nira_get_tag_nonce(nan->cfg, nonce, tag) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to set NIRA for new NIK"); + return -1; + } + os_memcpy(nan->nira_nonce, nonce, NAN_NIRA_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: NIRA nonce", + nan->nira_nonce, NAN_NIRA_NONCE_LEN); + os_memcpy(nan->nira_tag, tag, NAN_NIRA_TAG_LEN); + wpa_hexdump_key(MSG_DEBUG, "NAN: NIRA tag", + nan->nira_tag, NAN_NIRA_TAG_LEN); + } else { + os_memset(nan->nira_nonce, 0, NAN_NIRA_NONCE_LEN); + os_memset(nan->nira_tag, 0, NAN_NIRA_TAG_LEN); + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: New NIK", nan->cfg->nik, NAN_NIK_LEN); + + return 0; +} + + +int nan_pairing_set_nik_lifetime(struct nan_data *nan, u32 lifetime) +{ + if (!lifetime) { + wpa_printf(MSG_INFO, "NAN: Pairing: Invalid NIK lifetime (%u)", + lifetime); + return -1; + } + + nan->cfg->nik_lifetime = lifetime; + wpa_printf(MSG_DEBUG, "NAN: SET: NIK lifetime: %u seconds", + lifetime); + return 0; +} + + +bool nan_pairing_is_peer_paired(struct nan_data *nan_data, const u8 *peer_addr) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan_data, peer_addr); + if (!peer) + return false; + + return !!(peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED); +} + + +void nan_pairing_unpair_peer(struct nan_data *nan_data, const u8 *peer_addr) +{ + struct nan_peer *peer; + + peer = nan_get_peer(nan_data, peer_addr); + if (!peer) + return; + + wpa_printf(MSG_DEBUG, "NAN: Unpair peer " MACSTR, + MAC2STR(peer->nmi_addr)); + + peer->pairing.flags &= ~NAN_PAIRING_FLAG_PAIRED; + nan_pairing_deinit_peer(peer); +} diff --git a/contrib/wpa/src/nan/nan_sec.c b/contrib/wpa/src/nan/nan_sec.c new file mode 100644 index 000000000000..8561c7cd3495 --- /dev/null +++ b/contrib/wpa/src/nan/nan_sec.c @@ -0,0 +1,1653 @@ +/* + * Wi-Fi Aware - NAN Data link security + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "common/ieee802_11_common.h" +#include "nan_i.h" + + +/* + * nan_sec_reset - Reset security state + * @nan: NAN module context from nan_init() + * @ndp_sec: NDP security context to reset + */ +void nan_sec_reset(struct nan_data *nan, struct nan_ndp_sec *ndp_sec) +{ + os_memset(ndp_sec, 0, sizeof(*ndp_sec)); +} + + +static void nan_sec_dump(const struct nan_data *nan, + const struct nan_peer *peer) +{ + const struct nan_ndp_sec *s = &peer->ndp_setup.sec; + + wpa_printf(MSG_DEBUG, "NAN: SEC: present=%u, valid=%u", + s->present, s->valid); + wpa_printf(MSG_DEBUG, "NAN: SEC: i_csid=%u, i_instance_id=%u", + s->i_csid, s->i_instance_id); + wpa_printf(MSG_DEBUG, "NAN: SEC: r_csid=%u, r_instance_id=%u", + s->r_csid, s->r_instance_id); +} + + +static int nan_sec_rx_m1_verify(struct nan_data *nan, + const struct nan_attrs *attrs) +{ + const struct nan_sec_ctxt *sc; + size_t expected_len; + + expected_len = sizeof(struct nan_cipher_suite_info) + + sizeof(struct nan_cipher_suite); + + if (!attrs->cipher_suite_info || + attrs->cipher_suite_info_len < expected_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Missing valid cipher suite attribute"); + return -1; + } + + /* Need at least one PMKID */ + expected_len = sizeof(struct nan_sec_ctxt) + PMKID_LEN; + + if (!attrs->sec_ctxt_info || attrs->sec_ctxt_info_len < expected_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Missing valid security context attribute"); + return -1; + } + + sc = (const struct nan_sec_ctxt *) attrs->sec_ctxt_info; + if (sc->scid != NAN_SEC_CTX_TYPE_ND_PMKID || + le_to_host16(sc->len) != PMKID_LEN) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Got unknown security context"); + return -1; + } + + return 0; +} + + +static int nan_sec_parse_csia(const u8 *csia, size_t csia_len, u8 *instance_id, + u8 *capab, u8 *csid, u8 *gtk_csid) +{ + const struct nan_cipher_suite_info *cs_info = + (const struct nan_cipher_suite_info *) csia; + const u8 *cs_buf = cs_info->cs; + const struct nan_cipher_suite *cs; + + if (!csia || csia_len < sizeof(*cs_info)) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Invalid CSIA attribute"); + return -1; + } + + *instance_id = 0; + *capab = cs_info->capab; + csia_len -= sizeof(*cs_info); + + *csid = NAN_CS_NONE; + *gtk_csid = NAN_CS_NONE; + + while (csia_len >= sizeof(*cs)) { + cs = (const struct nan_cipher_suite *) cs_buf; + if (*instance_id && cs->instance_id != *instance_id) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Multiple instance IDs in CSIA"); + return -1; + } + + *instance_id = cs->instance_id; + + if (cs->csid == NAN_CS_GTK_CCMP_128 || + cs->csid == NAN_CS_GTK_GCMP_256) { + if (*gtk_csid != NAN_CS_NONE) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Multiple GTK CSIDs in CSIA"); + return -1; + } + + *gtk_csid = cs->csid; + } else { + if (*csid != NAN_CS_NONE) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Multiple CSIDs in CSIA"); + return -1; + } + + if (!NAN_CS_IS_VALID_NDP(cs->csid)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Unsupported cipher suite=%u", + cs->csid); + return -1; + } + + *csid = cs->csid; + } + + cs_buf += sizeof(*cs); + csia_len -= sizeof(*cs); + } + + if (*csid == NAN_CS_NONE) { + wpa_printf(MSG_DEBUG, "NAN: SEC: No valid CSID in CSIA"); + return -1; + } + + return 0; +} + + +static int nan_sec_rx_m1(struct nan_data *nan, struct nan_peer *peer, + const struct nan_msg *msg, + const struct wpa_eapol_key *key) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + const struct nan_sec_ctxt *sc; + u8 instance_id; + int ret; + + if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m1"); + return -1; + } + + ret = nan_sec_rx_m1_verify(nan, &msg->attrs); + if (ret) + return ret; + + sc = (const struct nan_sec_ctxt *) msg->attrs.sec_ctxt_info; + + instance_id = sc->instance_id; + if (ndp_sec->i_instance_id && instance_id && + ndp_sec->i_instance_id != instance_id) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Mismatched instance ID"); + return -1; + } + + os_memcpy(ndp_sec->i_pmkid, sc->ctxt, PMKID_LEN); + + /* Save initiator's nonce */ + os_memcpy(ndp_sec->i_nonce, key->key_nonce, WPA_NONCE_LEN); + + /* Save the replay counter */ + os_memcpy(ndp_sec->replaycnt, key->replay_counter, + sizeof(key->replay_counter)); + + /* Store the authentication token, that is required for m3 MIC */ + if (msg->len < 24) + return -1; + ret = nan_crypto_calc_auth_token(ndp_sec->i_csid, + (const u8 *) &msg->mgmt->u, + msg->len - 24, ndp_sec->auth_token); + if (ret) + return ret; + + /* The flow will continue, once higher layer ACK the NDP setup */ + ndp_sec->valid = true; + return 0; +} + + +static int nan_sec_key_mic_ver(struct nan_data *nan, const u8 *buf, size_t len, + const struct wpa_eapol_key *key, + const u8 *kck, size_t kck_len, u8 csid) +{ + u8 mic[NAN_KEY_MIC_24_LEN]; + u8 *pos = (u8 *) (key + 1); + u8 mic_len; + int ret; + + os_memset(mic, 0, sizeof(mic)); + + if (NAN_CS_IS_128(csid)) + mic_len = NAN_KEY_MIC_LEN; + else if (NAN_CS_IS_256(csid)) + mic_len = NAN_KEY_MIC_24_LEN; + else + return -1; + + os_memcpy(mic, pos, mic_len); + os_memset(pos, 0, mic_len); + + ret = nan_crypto_key_mic(buf, len, kck, kck_len, csid, pos); + if (ret) + return ret; + + if (os_memcmp_const(mic, pos, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "NAN: SEC: MIC verification failed"); + return -1; + } + + return 0; +} + + +static int nan_sec_rx_m2(struct nan_data *nan, struct nan_peer *peer, + const struct nan_msg *msg, + const struct wpa_eapol_key *key) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct nan_ndp *ndp = peer->ndp_setup.ndp; + struct nan_ptk tptk; + const u8 *pos; + int ret; + + if (peer->ndp_setup.state != NAN_NDP_STATE_RES_RECV) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m2"); + return -1; + } + + if (ndp_sec->i_csid != ndp_sec->r_csid) { + wpa_printf(MSG_DEBUG, "NAN: SEC: i_csid != r_csid (%u, %u)", + ndp_sec->i_csid, ndp_sec->r_csid); + return -1; + } + + /* Save responder's nonce */ + os_memcpy(ndp_sec->r_nonce, key->key_nonce, WPA_NONCE_LEN); + + /* Note: Replay counter should have been verified by the caller */ + + /* PTK should be derived using NDI addresses */ + ret = nan_crypto_pmk_to_ptk(ndp_sec->pmk, + ndp->init_ndi, ndp->resp_ndi, + ndp_sec->i_nonce, ndp_sec->r_nonce, + &tptk, ndp_sec->i_csid); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: SEC: m2 failed to derive PTK"); + return ret; + } + + /* + * Due to the different MIC size, need to handle the fields starting + * with the MIC differently. + */ + pos = (const u8 *) (key + 1); + if (NAN_CS_IS_128(ndp_sec->i_csid)) + pos += NAN_KEY_MIC_LEN; + else + pos += NAN_KEY_MIC_24_LEN; + + if (WPA_GET_BE16(pos)) + wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m2 key data"); + + /* Verify MIC */ + if (msg->len < 24) + return -1; + ret = nan_sec_key_mic_ver(nan, (const u8 *) &msg->mgmt->u, + msg->len - 24, key, + tptk.kck, tptk.kck_len, + ndp_sec->i_csid); + if (ret) + return ret; + + /* MIC is verified; save PTK */ + os_memcpy(&ndp_sec->ptk, &tptk, sizeof(tptk)); + forced_memzero(&tptk, sizeof(tptk)); + + /* Increment the replay counter here to prevent replays */ + WPA_PUT_BE64(ndp_sec->replaycnt, + WPA_GET_BE64(ndp_sec->replaycnt) + 1); + return 0; +} + + +static int nan_sec_rx_m3(struct nan_data *nan, struct nan_peer *peer, + const struct nan_msg *msg, + const struct wpa_eapol_key *key) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + u8 mic[NAN_KEY_MIC_24_LEN]; + u8 *buf; + u8 mic_len; + u8 *pos; + int ret; + + if (peer->ndp_setup.state != NAN_NDP_STATE_CON_RECV) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m3"); + return -1; + } + + /* Note: Replay counter should have been verified by caller */ + + /* Verify the i_nonce did not change */ + if (os_memcmp(key->key_nonce, ndp_sec->i_nonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "NAN: SEC: m3 key nonce mismatch"); + return -1; + } + + /* + * In case of m3, the MIC is calculated over the frame body + * concatenated with the authentication token. + */ + if (msg->len < 24) + return -1; + buf = os_malloc(msg->len - 24 + NAN_AUTH_TOKEN_LEN); + if (!buf) + return -ENOBUFS; + + /* + * Due to the different MIC size, need to handle the fields starting + * with the mic differently + */ + pos = (u8 *) (key + 1); + if (NAN_CS_IS_128(ndp_sec->i_csid)) + mic_len = NAN_KEY_MIC_LEN; + else + mic_len = NAN_KEY_MIC_24_LEN; + + if (WPA_GET_BE16(pos + mic_len)) + wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m3 key data"); + + /* Copy MIC and os_memset it */ + os_memcpy(mic, pos, mic_len); + os_memset(pos, 0, mic_len); + os_memcpy(buf, ndp_sec->auth_token, NAN_AUTH_TOKEN_LEN); + os_memcpy(buf + NAN_AUTH_TOKEN_LEN, (const u8 *) &msg->mgmt->u, + msg->len - 24); + + ret = nan_crypto_key_mic(buf, msg->len - 24 + NAN_AUTH_TOKEN_LEN, + ndp_sec->ptk.kck, ndp_sec->ptk.kck_len, + ndp_sec->i_csid, pos); + os_free(buf); + + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: SEC: m3 MIC calculation failed"); + return ret; + } + + if (os_memcmp_const(mic, pos, mic_len) != 0) { + wpa_printf(MSG_DEBUG, "NAN: SEC: m3 MIC verification failed"); + return -1; + } + + forced_memzero(mic, sizeof(mic)); + + /* Save replay counter */ + os_memcpy(ndp_sec->replaycnt, key->replay_counter, + sizeof(key->replay_counter)); + return 0; +} + + +static int nan_sec_rx_m4(struct nan_data *nan, struct nan_peer *peer, + const struct nan_msg *msg, + const struct wpa_eapol_key *key) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + u8 *pos = (u8 *) (key + 1); + int ret; + + if (peer->ndp_setup.state != NAN_NDP_STATE_DONE) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m4"); + return -1; + } + + /* Note: replay counter should have been verified by caller */ + + /* + * Due to the different MIC size, need to handle the fields starting + * with the mic differently + */ + if (NAN_CS_IS_128(ndp_sec->i_csid)) + pos += NAN_KEY_MIC_LEN; + else + pos += NAN_KEY_MIC_24_LEN; + + if (WPA_GET_BE16(pos)) + wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m4 key data"); + + /* Verify MIC */ + if (msg->len < 24) + return -1; + ret = nan_sec_key_mic_ver(nan, (const u8 *) &msg->mgmt->u, + msg->len - 24, key, + ndp_sec->ptk.kck, ndp_sec->ptk.kck_len, + ndp_sec->i_csid); + if (ret) + return ret; + + /* Increment the replay counter here to prevent replays */ + WPA_PUT_BE64(ndp_sec->replaycnt, + WPA_GET_BE64(ndp_sec->replaycnt) + 1); + + /* Security negotiation done */ + return 0; +} + + +static int nan_sec_rx_key_data(struct nan_data *nan, + struct nan_peer *peer, u8 peer_capab, + const u8 *enc_key_data, size_t key_data_len) +{ + struct wpabuf *key_data = NULL; + struct wpa_eapol_ie_parse ie; + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + int ret = -1; + int cipher; + unsigned int key_len; + enum wpa_alg alg; + + if (((peer_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) == NAN_CS_INFO_CAPA_GTK_SUPP_NONE && + ndp_sec->peer_gtk.csid == NAN_CS_NONE) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Peer does not support GTK/IGTK/BIGTK, ignore key data"); + return 0; + } + + if (peer_capab & NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256) { + cipher = WPA_CIPHER_BIP_GMAC_256; + alg = WPA_ALG_BIP_GMAC_256; + } else { + cipher = WPA_CIPHER_AES_128_CMAC; + alg = WPA_ALG_BIP_CMAC_128; + } + + key_len = wpa_cipher_key_len(cipher); + + key_data = nan_crypto_decrypt_key_data(ndp_sec->ptk.kek, + ndp_sec->ptk.kek_len, + enc_key_data, key_data_len); + if (!key_data) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Failed to decrypt key data"); + return -1; + } + + if (wpa_parse_kde_ies(wpabuf_head(key_data), wpabuf_len(key_data), + &ie) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Failed to parse decrypted key data"); + goto fail; + } + + if (ie.igtk && ie.igtk_len) { + const struct wpa_igtk_kde *igtk_kde = + (const struct wpa_igtk_kde *) ie.igtk; + u16 key_idx; + + if (ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + key_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid IGTK KDE length: %zu (expected %u)", + ie.igtk_len, + WPA_IGTK_KDE_PREFIX_LEN + key_len); + goto fail; + } + + /* Key ID must be 4 or 5, see Wi-Fi Aware Specification v4.0, + * section 7.1.3.3 + */ + key_idx = WPA_GET_LE16(igtk_kde->keyid); + if (key_idx < 4 || key_idx > 5) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid IGTK key index: %u", + key_idx); + goto fail; + } + + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, alg, + peer->nmi_addr, key_idx, + igtk_kde->pn, igtk_kde->igtk, + key_len, KEY_FLAG_GROUP_RX) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Failed to install IGTK"); + goto fail; + } + + peer->igtk_id = key_idx; + wpa_hexdump_key(MSG_DEBUG, "NAN: SEC: Received IGTK", + igtk_kde->igtk, key_len); + } + + if (ie.bigtk && ie.bigtk_len) { + const struct wpa_bigtk_kde *bigtk_kde = + (const struct wpa_bigtk_kde *) ie.bigtk; + u16 key_idx; + + if (ie.bigtk_len != WPA_BIGTK_KDE_PREFIX_LEN + key_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid BIGTK KDE length: %zu (expected %d)", + ie.bigtk_len, + WPA_BIGTK_KDE_PREFIX_LEN + key_len); + goto fail; + } + + key_idx = WPA_GET_LE16(bigtk_kde->keyid); + if (key_idx < 6 || key_idx > 7) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid BIGTK key index: %u", + key_idx); + goto fail; + } + + if (nan->cfg->set_group_key(nan->cfg->cb_ctx, alg, + peer->nmi_addr, key_idx, + bigtk_kde->pn, bigtk_kde->bigtk, + key_len, KEY_FLAG_GROUP_RX) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Failed to install BIGTK"); + goto fail; + } + + peer->bigtk_id = key_idx; + wpa_hexdump_key(MSG_DEBUG, "NAN: SEC: Received BIGTK", + bigtk_kde->bigtk, key_len); + } + + if (ie.gtk && ie.gtk_len) { + const struct wpa_gtk_kde *gtk_kde = + (const struct wpa_gtk_kde *) ie.gtk; + int gtk_cipher = ndp_sec->peer_gtk.csid == NAN_CS_GTK_GCMP_256 ? + WPA_CIPHER_GCMP_256 : WPA_CIPHER_CCMP; + size_t gtk_len = wpa_cipher_key_len(gtk_cipher); + + if (ie.gtk_len != WPA_GTK_KDE_PREFIX_LEN + gtk_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid GTK KDE length: %zu (expected %zu)", + ie.gtk_len, + WPA_GTK_KDE_PREFIX_LEN + gtk_len); + goto fail; + } + + /* GTK key ID must be 1 or 2, see Wi-Fi Aware Specification + * v4.0, section 7.1.3.2. + */ + if (gtk_kde->keyid < 1 || gtk_kde->keyid > 2) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid GTK key index: %u", + gtk_kde->keyid); + goto fail; + } + + ndp_sec->peer_gtk.id = gtk_kde->keyid; + os_memcpy(ndp_sec->peer_gtk.gtk.gtk, gtk_kde->gtk, gtk_len); + ndp_sec->peer_gtk.gtk.gtk_len = gtk_len; + wpa_hexdump_key(MSG_DEBUG, "NAN: SEC: Received GTK", + gtk_kde->gtk, gtk_len); + } + + ret = 0; +fail: + wpabuf_clear_free(key_data); + return ret; +} + + +/** + * nan_sec_rx - Handle security context for Rx frames + * @nan: NAN module context from nan_init() + * @peer: Peer from which the original message was received + * @msg: Parsed NAN Action frame + * Returns: 0 on success, negative on failure + */ +int nan_sec_rx(struct nan_data *nan, struct nan_peer *peer, + struct nan_msg *msg) +{ + struct nan_ndp_setup *ndp_setup = &peer->ndp_setup; + struct nan_ndp_sec *ndp_sec = &ndp_setup->sec; + struct wpa_eapol_key *key; + struct nan_shared_key *shared_key_desc; + size_t shared_key_desc_len; + u16 info, desc, key_data_len; + size_t total_len; + u8 instance_id, cipher, capab, gtk_csid = NAN_CS_NONE; + u8 *pos; + int ret; + + wpa_printf(MSG_DEBUG, "NAN: SEC: NDP security Rx"); + nan_sec_dump(nan, peer); + + if (!ndp_sec->present) { + if (!ndp_sec->valid) + return 0; + + wpa_printf(MSG_DEBUG, + "NAN: SEC: NDP with security but present=0"); + return -1; + } + + shared_key_desc = (struct nan_shared_key *) msg->attrs.shared_key_desc; + shared_key_desc_len = msg->attrs.shared_key_desc_len; + + /* Shared key descriptor mandatory in all messages */ + if (!shared_key_desc || !shared_key_desc_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: No shared key descriptor attribute"); + return -1; + } + + /* + * As the shared key type depends on the cipher suite negotiated, need + * to get the cipher suite to validate the proper length of the + * descriptor. + */ + if (msg->oui_subtype == NAN_SUBTYPE_DATA_PATH_REQUEST || + msg->oui_subtype == NAN_SUBTYPE_DATA_PATH_RESPONSE) { + if (nan_sec_parse_csia(msg->attrs.cipher_suite_info, + msg->attrs.cipher_suite_info_len, + &instance_id, &capab, &cipher, + >k_csid)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Missing/bad cipher suite attribute"); + return -1; + } + } else { + cipher = ndp_sec->i_csid; + } + + key = (struct wpa_eapol_key *) shared_key_desc->key; + pos = (u8 *) (key + 1); + + total_len = sizeof(*key) + 2; + + if (NAN_CS_IS_128(cipher)) { + if (shared_key_desc_len < + sizeof(struct nan_shared_key) + sizeof(*key) + + NAN_KEY_MIC_LEN + 2) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Shared key descriptor too small"); + return -1; + } + + key_data_len = WPA_GET_BE16(pos + NAN_KEY_MIC_LEN); + total_len += NAN_KEY_MIC_LEN + key_data_len; + pos += NAN_KEY_MIC_LEN + 2; + + if (total_len > + (shared_key_desc_len - sizeof(struct nan_shared_key))) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key: payload len"); + return -1; + } + } else { + if (shared_key_desc_len < + (sizeof(struct nan_shared_key) + sizeof(*key) + + NAN_KEY_MIC_24_LEN + 2)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Shared key (24) descriptor too small"); + return -1; + } + + key_data_len = WPA_GET_BE16(pos + NAN_KEY_MIC_24_LEN); + total_len += NAN_KEY_MIC_24_LEN + key_data_len; + pos += NAN_KEY_MIC_24_LEN + 2; + + if (total_len > + (shared_key_desc_len - sizeof(struct nan_shared_key))) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key (24): payload len"); + return -1; + } + } + + /* + * Note: Only the fields before the MIC are accessed in the function, so + * it is safe to continue with the key pointer. + */ + + /* Note: According to the NAN specification the following fields should + * be ignored: + * key->len: as the key length is derived from the cipher suite. + * key->iv: not needed for AES Key WRAP + */ + if (key->type != NAN_KEY_DESC) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key: key descriptor=0x%x", + key->type); + return -1; + } + + info = WPA_GET_BE16(key->key_info); + + /* Discard EAPOL-Key frames with an unknown descriptor version */ + desc = info & WPA_KEY_INFO_TYPE_MASK; + if (desc != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key: invalid key version=0x%x", + desc); + return -1; + } + + if (ndp_sec->replaycnt_ok && + WPA_GET_BE64(key->replay_counter) < + WPA_GET_BE64(ndp_sec->replaycnt)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Replay counter did not increase"); + return -1; + } + + if (info & WPA_KEY_INFO_REQUEST) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key: key request not supported"); + return -1; + } + + if (!(info & WPA_KEY_INFO_KEY_TYPE)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key: group handshake not supported"); + return -1; + } + + if (gtk_csid != NAN_CS_NONE) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Peer GTK CSID=%u", gtk_csid); + + os_memcpy(ndp_sec->peer_gtk_rsc, key->key_rsc, + sizeof(key->key_rsc)); + ndp_sec->peer_gtk.csid = gtk_csid; + } + + switch (msg->oui_subtype) { + case NAN_SUBTYPE_DATA_PATH_REQUEST: + if (!(info & WPA_KEY_INFO_ACK)) + return -1; + + ndp_sec->i_capab = capab; + ndp_sec->i_csid = cipher; + ndp_sec->i_instance_id = instance_id; + ret = nan_sec_rx_m1(nan, peer, msg, key); + break; + case NAN_SUBTYPE_DATA_PATH_RESPONSE: + if (!(info & WPA_KEY_INFO_MIC)) + return -1; + + ndp_sec->r_capab = capab; + ndp_sec->r_csid = cipher; + ndp_sec->r_instance_id = instance_id; + ret = nan_sec_rx_m2(nan, peer, msg, key); + break; + case NAN_SUBTYPE_DATA_PATH_CONFIRM: + if (!(info & WPA_KEY_INFO_MIC) || + !(info & WPA_KEY_INFO_ACK) || + !(info & WPA_KEY_INFO_SECURE)) + return -1; + ret = nan_sec_rx_m3(nan, peer, msg, key); + + /* Ignore unencrypted key data */ + if (!ret && key_data_len > 0 && + (info & WPA_KEY_INFO_ENCR_KEY_DATA)) + ret = nan_sec_rx_key_data(nan, peer, + ndp_sec->i_capab, pos, + key_data_len); + break; + case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL: + if (!(info & WPA_KEY_INFO_MIC) || + !(info & WPA_KEY_INFO_SECURE)) + return -1; + ret = nan_sec_rx_m4(nan, peer, msg, key); + + /* Ignore unencrypted key data */ + if (!ret && key_data_len > 0 && + (info & WPA_KEY_INFO_ENCR_KEY_DATA)) + ret = nan_sec_rx_key_data(nan, peer, + ndp_sec->r_capab, pos, + key_data_len); + break; + default: + wpa_printf(MSG_DEBUG, "NAN: SEC: Invalid frame OUI subtype"); + return -1; + } + + if (ret) + return ret; + + instance_id = shared_key_desc->publish_id; + if (ndp_sec->i_instance_id && instance_id && + ndp_sec->i_instance_id != instance_id) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Mismatch instance ID in shared key descriptor"); + return -1; + } + + return 0; +} + + +/* + * nan_sec_add_m1_attrs - Add security attributes to NAN message 1 + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @buf: Buffer to which the attribute should be added + * Returns: 0 on success, negative on failure + * + * In addition to building the attributes, the function also initializes the + * security context for the NDP security exchange. Assumes that the following + * are already set: + * - initiator CSID + * - PMK + * - NDP puslish ID + * - initiator address + * - peer_nmi + */ +static int nan_sec_add_m1_attrs(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct wpa_eapol_key *key; + struct nan_cipher_suite cs[2]; + size_t cs_len = 1; + u16 info; + size_t key_len = sizeof(struct wpa_eapol_key) + 2; + int ret; + + if (NAN_CS_IS_128(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_LEN; + else if (NAN_CS_IS_256(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_24_LEN; + else + return -1; + + /* Initialize the initiator security state */ + if (os_get_random(ndp_sec->i_nonce, sizeof(ndp_sec->i_nonce)) < 0) + return -1; + ndp_sec->i_capab = nan->cfg->security_capab; + ndp_sec->i_instance_id = peer->ndp_setup.publish_inst_id; + + /* Compute the PMKID */ + ret = nan_crypto_calc_pmkid(ndp_sec->pmk, + nan->cfg->nmi_addr, + peer->nmi_addr, + peer->ndp_setup.service_id, + ndp_sec->i_csid, ndp_sec->i_pmkid); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Failed to compute PMKID (m1)"); + return ret; + } + + /* Cipher suite information */ + cs[0].csid = ndp_sec->i_csid; + cs[0].instance_id = ndp_sec->i_instance_id; + + if (ndp_sec->local_gtk.csid != NAN_CS_NONE) { + cs[1].csid = ndp_sec->local_gtk.csid; + cs[1].instance_id = ndp_sec->i_instance_id; + cs_len++; + } + + nan_add_csia(buf, ndp_sec->i_capab, cs_len, cs); + + /* Security context information */ + wpabuf_put_u8(buf, NAN_ATTR_SCIA); + wpabuf_put_le16(buf, sizeof(struct nan_sec_ctxt) + PMKID_LEN); + + wpabuf_put_le16(buf, PMKID_LEN); + wpabuf_put_u8(buf, NAN_SEC_CTX_TYPE_ND_PMKID); + wpabuf_put_u8(buf, ndp_sec->i_instance_id); + wpabuf_put_data(buf, ndp_sec->i_pmkid, PMKID_LEN); + + /* Shared key descriptor */ + wpabuf_put_u8(buf, NAN_ATTR_SHARED_KEY_DESCR); + wpabuf_put_le16(buf, sizeof(struct nan_shared_key) + key_len); + wpabuf_put_u8(buf, ndp_sec->i_instance_id); + + key = (struct wpa_eapol_key *) wpabuf_put(buf, key_len); + os_memset(key, 0, key_len); + + key->type = NAN_KEY_DESC; + info = WPA_KEY_INFO_TYPE_AKM_DEFINED | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_ACK; + WPA_PUT_BE16(key->key_info, info); + + /* Copy the initiator nonce */ + os_memcpy(key->key_nonce, ndp_sec->i_nonce, WPA_NONCE_LEN); + + /* Key length is zero (it can be deduced from the cipher suite) */ + + /* Initialize replay counter */ + WPA_PUT_BE64(ndp_sec->replaycnt, 1ULL); + os_memcpy(key->replay_counter, ndp_sec->replaycnt, + sizeof(key->replay_counter)); + ndp_sec->replaycnt_ok = true; + + ndp_sec->valid = true; + return 0; +} + + +/* + * nan_sec_add_m2_attrs - Add security attributes to NAN message 2 + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @buf: Buffer to which the attribute should be added + * Returns: 0 on success, negative on failure + */ +static int nan_sec_add_m2_attrs(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct wpa_eapol_key *key; + struct nan_cipher_suite cs[2]; + size_t cs_len = 1; + + u16 info; + size_t key_len; + + key_len = sizeof(struct wpa_eapol_key) + 2; + if (NAN_CS_IS_128(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_LEN; + else if (NAN_CS_IS_256(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_24_LEN; + else + return -1; + + /* Cipher suite information */ + cs[0].csid = ndp_sec->r_csid; + cs[0].instance_id = ndp_sec->r_instance_id; + + if (ndp_sec->local_gtk.csid != NAN_CS_NONE) { + cs[1].csid = ndp_sec->local_gtk.csid; + cs[1].instance_id = ndp_sec->r_instance_id; + cs_len++; + } + nan_add_csia(buf, ndp_sec->r_capab, cs_len, cs); + + /* Security context information */ + wpabuf_put_u8(buf, NAN_ATTR_SCIA); + wpabuf_put_le16(buf, sizeof(struct nan_sec_ctxt) + PMKID_LEN); + + wpabuf_put_le16(buf, PMKID_LEN); + wpabuf_put_u8(buf, NAN_SEC_CTX_TYPE_ND_PMKID); + wpabuf_put_u8(buf, ndp_sec->r_instance_id); + wpabuf_put_data(buf, ndp_sec->r_pmkid, PMKID_LEN); + + if (peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED) + return 0; + + /* Shared key descriptor */ + wpabuf_put_u8(buf, NAN_ATTR_SHARED_KEY_DESCR); + wpabuf_put_le16(buf, sizeof(struct nan_shared_key) + key_len); + wpabuf_put_u8(buf, ndp_sec->r_instance_id); + + key = (struct wpa_eapol_key *) wpabuf_put(buf, key_len); + os_memset(key, 0, key_len); + + key->type = NAN_KEY_DESC; + info = WPA_KEY_INFO_TYPE_AKM_DEFINED | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_MIC; + WPA_PUT_BE16(key->key_info, info); + + /* Copy the responders's nonce */ + os_memcpy(key->key_nonce, ndp_sec->r_nonce, WPA_NONCE_LEN); + + /* + * Key length is zero (it can be deduced from the cipher suite). + * No additional data is added. + */ + + /* Copy replay counter */ + os_memcpy(key->replay_counter, ndp_sec->replaycnt, + sizeof(key->replay_counter)); + ndp_sec->replaycnt_ok = true; + + return 0; +} + + +static int nan_sec_igtk_kde(struct nan_data *nan, struct wpabuf *buf) +{ + u8 tsc[RSN_PN_LEN]; + + if (nan->cfg->get_seqnum(nan->cfg->cb_ctx, nan->igtk_id, tsc, + NULL) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to get IGTK seqnum"); + return -1; + } + + nan_add_kde_hdr(buf, RSN_KEY_DATA_IGTK, + WPA_IGTK_KDE_PREFIX_LEN + nan->igtk.igtk_len); + wpabuf_put_le16(buf, nan->igtk_id); + wpabuf_put_data(buf, tsc, RSN_PN_LEN); + wpabuf_put_data(buf, nan->igtk.igtk, nan->igtk.igtk_len); + return 0; +} + + +static int nan_sec_bigtk_kde(struct nan_data *nan, struct nan_ndp_sec *ndp_sec, + struct wpabuf *buf) +{ + u8 tsc[RSN_PN_LEN]; + + if (((ndp_sec->i_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) != NAN_CS_INFO_CAPA_GTK_SUPP_ALL) { + wpa_printf(MSG_DEBUG, + "NAN: BIGTK not supported by initiator"); + return 0; + } + + if (((ndp_sec->r_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) != NAN_CS_INFO_CAPA_GTK_SUPP_ALL) { + wpa_printf(MSG_DEBUG, + "NAN: BIGTK not supported by responder"); + return 0; + } + + if (nan->cfg->get_seqnum(nan->cfg->cb_ctx, nan->bigtk_id, tsc, + NULL) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Failed to get BIGTK seqnum"); + return -1; + } + + nan_add_kde_hdr(buf, RSN_KEY_DATA_BIGTK, + WPA_BIGTK_KDE_PREFIX_LEN + nan->bigtk.bigtk_len); + wpabuf_put_le16(buf, nan->bigtk_id); + wpabuf_put_data(buf, tsc, sizeof(tsc)); + wpabuf_put_data(buf, nan->bigtk.bigtk, nan->bigtk.bigtk_len); + return 0; +} + + +static int nan_sec_gtk_kde(struct nan_data *nan, struct wpabuf *buf, + struct nan_ndp_sec *ndp_sec) +{ + if (!ndp_sec->local_gtk.gtk.gtk_len) + return 0; + + if (ndp_sec->local_gtk.id > 3) { + wpa_printf(MSG_DEBUG, "NAN: Invalid GTK Key ID %u", + ndp_sec->local_gtk.id); + return -1; + } + + nan_add_kde_hdr(buf, RSN_KEY_DATA_GROUPKEY, + WPA_GTK_KDE_PREFIX_LEN + + ndp_sec->local_gtk.gtk.gtk_len); + wpabuf_put_u8(buf, ndp_sec->local_gtk.id); + wpabuf_put_u8(buf, 0); + wpabuf_put_data(buf, ndp_sec->local_gtk.gtk.gtk, + ndp_sec->local_gtk.gtk.gtk_len); + + return 0; +} + + +static bool nan_sec_igtk_supported(struct nan_ndp_sec *ndp_sec) +{ + return ((ndp_sec->i_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) != + NAN_CS_INFO_CAPA_GTK_SUPP_NONE && + ((ndp_sec->r_capab & NAN_CS_INFO_CAPA_GTK_SUPP_MASK) >> + NAN_CS_INFO_CAPA_GTK_SUPP_POS) != + NAN_CS_INFO_CAPA_GTK_SUPP_NONE; +} + + +#define NAN_KDES_MAX_LEN \ + (KDE_HDR_LEN + sizeof(struct wpa_igtk_kde) + KDE_HDR_LEN + \ + sizeof(struct wpa_bigtk_kde) + KDE_HDR_LEN + \ + sizeof(struct wpa_gtk_kde)) + +static int nan_sec_add_kdes(struct nan_data *nan, struct nan_ndp_sec *ndp_sec, + struct wpabuf *buf) +{ + struct wpabuf *kde_buf; + struct wpabuf *enc_kde; + int ret = -1; + + if (!nan_sec_igtk_supported(ndp_sec) && + ndp_sec->local_gtk.gtk.gtk_len == 0) { + wpa_printf(MSG_DEBUG, + "NAN: GTK/IGTK not supported for this NDP"); + return 0; + } + + if (!ndp_sec->ptk.kek_len) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: No KEK available to encrypt KDEs"); + return -1; + } + + kde_buf = wpabuf_alloc(NAN_KDES_MAX_LEN); + if (!kde_buf) { + wpa_printf(MSG_INFO, "NAN: SEC: Failed to allocate KDE buffer"); + return -1; + } + + if (nan_sec_igtk_kde(nan, kde_buf) < 0) + goto fail; + + if (nan_sec_bigtk_kde(nan, ndp_sec, kde_buf) < 0) + goto fail; + + if (nan_sec_gtk_kde(nan, kde_buf, ndp_sec) < 0) + goto fail; + + enc_kde = nan_crypto_encrypt_key_data(kde_buf, ndp_sec->ptk.kek, + ndp_sec->ptk.kek_len); + if (!enc_kde) { + wpa_printf(MSG_INFO, "NAN: SEC: Failed to encrypt KDEs"); + goto fail; + } + + wpabuf_put_buf(buf, enc_kde); + ret = wpabuf_len(enc_kde); + wpabuf_free(enc_kde); +fail: + wpabuf_clear_free(kde_buf); + return ret; +} + + +/* + * nan_sec_add_key_attrs - Add security key attributes to NAN message + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @buf: Buffer to which the attribute should be added + * @instance_id: Instance ID to use + * @nonce: Nonce to use + * @is_ack: Whether to include ACK flag in key info + * Returns: 0 on success, negative on failure + */ +static int nan_sec_add_key_attrs(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf, u8 instance_id, + const u8 *nonce, bool is_ack) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct wpa_eapol_key *key; + u16 info; + size_t key_len = sizeof(struct wpa_eapol_key); + u8 *key_len_pos; + int kde_len; + u8 *key_data_len_pos; + + if (NAN_CS_IS_128(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_LEN; + else if (NAN_CS_IS_256(ndp_sec->i_csid)) + key_len += NAN_KEY_MIC_24_LEN; + else + return -1; + + /* Shared key descriptor */ + wpabuf_put_u8(buf, NAN_ATTR_SHARED_KEY_DESCR); + key_len_pos = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, instance_id); + + key = (struct wpa_eapol_key *) wpabuf_put(buf, key_len); + os_memset(key, 0, key_len); + + key->type = NAN_KEY_DESC; + + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + /* + * Copy replay counter. It was already incremented while processing m2 + * so no need to increment it again. + */ + os_memcpy(key->replay_counter, ndp_sec->replaycnt, + sizeof(key->replay_counter)); + + /* Add KDEs to the key data and set key length accordingly */ + key_data_len_pos = wpabuf_put(buf, 2); + + kde_len = nan_sec_add_kdes(nan, ndp_sec, buf); + if (kde_len < 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Failed to add KDEs to m3"); + return -1; + } + + /* When GTK is present, the Key RSC field is set to the GTK RSC */ + if (ndp_sec->local_gtk.gtk.gtk_len) { + const u8 *local_ndi; + struct nan_ndp *pndp = peer->ndp_setup.ndp; + + if (pndp->initiator) + local_ndi = pndp->init_ndi; + else + local_ndi = pndp->resp_ndi; + + if (nan->cfg->get_seqnum(nan->cfg->cb_ctx, + ndp_sec->local_gtk.id, key->key_rsc, + local_ndi) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Failed to get GTK seqnum"); + return -1; + } + } + + info = WPA_KEY_INFO_TYPE_AKM_DEFINED | WPA_KEY_INFO_KEY_TYPE | + WPA_KEY_INFO_MIC | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SECURE; + if (is_ack) + info |= WPA_KEY_INFO_ACK; + if (kde_len) + info |= WPA_KEY_INFO_ENCR_KEY_DATA; + + WPA_PUT_BE16(key->key_info, info); + + WPA_PUT_LE16(key_len_pos, + sizeof(struct nan_shared_key) + key_len + 2 + kde_len); + WPA_PUT_BE16(key_data_len_pos, kde_len); + return 0; +} + + +/* + * nan_sec_add_m3_attrs - Add security attributes to NAN message 3 + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @buf: Buffer to which the attribute should be added + * Returns: 0 on success, negative on failure + */ +static int nan_sec_add_m3_attrs(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + + return nan_sec_add_key_attrs(nan, peer, buf, ndp_sec->i_instance_id, + ndp_sec->i_nonce, true); +} + + +/* + * nan_sec_add_m4_attrs - Add security attributes to NAN message 4 + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @buf: Buffer to which the attribute should be added + * Returns: 0 on success, negative on failure + */ +static int nan_sec_add_m4_attrs(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + + return nan_sec_add_key_attrs(nan, peer, buf, ndp_sec->r_instance_id, + ndp_sec->r_nonce, false); +} + + +/** + * nan_sec_add_attrs - Add security attributes to NAN message + * @nan: NAN module context from nan_init() + * @peer: Peer which is the recipient of the message + * @subtype: Frame subtype + * @buf: Buffer to which the attribute should be added + * Returns: 0 on success, negative on failure + */ +int nan_sec_add_attrs(struct nan_data *nan, struct nan_peer *peer, + enum nan_subtype subtype, struct wpabuf *buf) +{ + /* NDP establishment is not in progress */ + if (!peer->ndp_setup.ndp) + return 0; + + wpa_printf(MSG_DEBUG, "NAN: SEC: Add security attributes"); + nan_sec_dump(nan, peer); + + /* No security configuration */ + if (!NAN_CS_IS_VALID_NDP(peer->ndp_setup.sec.i_csid)) + return 0; + + switch (subtype) { + case NAN_SUBTYPE_DATA_PATH_REQUEST: + return nan_sec_add_m1_attrs(nan, peer, buf); + case NAN_SUBTYPE_DATA_PATH_RESPONSE: + return nan_sec_add_m2_attrs(nan, peer, buf); + case NAN_SUBTYPE_DATA_PATH_CONFIRM: + return nan_sec_add_m3_attrs(nan, peer, buf); + case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL: + return nan_sec_add_m4_attrs(nan, peer, buf); + case NAN_SUBTYPE_DATA_PATH_TERMINATION: + break; + default: + return -1; + } + + return 0; +} + + +/** + * nan_sec_init_resp - Initialize security context for responder + * @nan: NAN module context from nan_init() + * @peer: Peer with whom the NDP is being established + * Returns: 0 on success, negative on failure + * + * The function initializes the security context for the NDP security + * exchange for the responder. Assumes that the following re already set: + * - Initiator CSID + * - Responder CSID + * - PMK + * - NDP publish ID + * - Initiator address + * - Responder address + */ +int nan_sec_init_resp(struct nan_data *nan, struct nan_peer *peer) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct nan_ndp *ndp = peer->ndp_setup.ndp; + int ret; + + if (ndp_sec->i_csid != ndp_sec->r_csid) + return -1; + + /* Initialize the responder's security state */ + if (os_get_random(ndp_sec->r_nonce, sizeof(ndp_sec->r_nonce)) < 0) + return -1; + ndp_sec->r_capab = nan->cfg->security_capab; + ndp_sec->r_instance_id = peer->ndp_setup.publish_inst_id; + + if (ndp_sec->i_instance_id != ndp_sec->r_instance_id) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Service instance IDs are different (m2)"); + return -1; + } + + /* Compute the PMKID */ + ret = nan_crypto_calc_pmkid(ndp_sec->pmk, peer->nmi_addr, + nan->cfg->nmi_addr, + peer->ndp_setup.service_id, + ndp_sec->r_csid, ndp_sec->r_pmkid); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: SEC: Failed to compute PMKID (m2)"); + return -1; + } + + /* Sanity check */ + if (os_memcmp(ndp_sec->i_pmkid, ndp_sec->r_pmkid, PMKID_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: m2: Local PMKID differs from remote"); + return -1; + } + + /* PTK should be derived using the NDI address */ + ret = nan_crypto_pmk_to_ptk(ndp_sec->pmk, + ndp->init_ndi, ndp->resp_ndi, + ndp_sec->i_nonce, ndp_sec->r_nonce, + &ndp_sec->ptk, ndp_sec->i_csid); + + wpa_printf(MSG_DEBUG, + "NAN: SEC: Derived PTK for responder (m2). ret=%d", + ret); + + return ret; +} + + +/* + * nan_sec_pre_tx - Handle security aspects before sending a NDP NAF + * @nan: NAN module context from nan_init() + * @peer: Peer with whom the NDP is being established + * @buf: Buffer holding the NAF body (not including the IEEE 802.11 header) + * Returns: 0 on success, and a negative error value on failure. + * + * Note: The NAF content should not be altered after the function returns, + * as the function might have signed the frame body, i.e., updated the MIC + * field. + */ +int nan_sec_pre_tx(struct nan_data *nan, struct nan_peer *peer, + struct wpabuf *buf) +{ + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct nan_attrs attrs; + struct nan_shared_key *shared_key_desc; + struct wpa_eapol_key *key; + u8 *data, *tmp, *mic_ptr; + size_t len; + u8 subtype; + int ret; + + /* NDP establishment is not in progress */ + if (!peer->ndp_setup.ndp || + peer->ndp_setup.status == NAN_NDP_STATUS_REJECTED) + return 0; + + /* No security configuration */ + if (!ndp_sec->valid) + return 0; + + wpa_printf(MSG_DEBUG, "NAN: SEC: NDP setup state=%u (pre Tx)", + peer->ndp_setup.state); + + data = wpabuf_mhead_u8(buf); + len = wpabuf_len(buf); + + if (len < 7) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Buffer is too short=%zu (pre Tx)", len); + return -1; + } + + /* The subtype is the 7th octet. See nan_action_build_header() */ + subtype = data[6]; + + wpa_printf(MSG_DEBUG, "NAN: SEC: subtype=0x%x (pre Tx)", subtype); + + switch (subtype) { + case NAN_SUBTYPE_DATA_PATH_REQUEST: + case NAN_SUBTYPE_DATA_PATH_RESPONSE: + case NAN_SUBTYPE_DATA_PATH_CONFIRM: + case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL: + break; + default: + return -1; + } + + /* + * First get a pointer to the shared key descriptor attribute and + * validate it. + */ + ret = nan_parse_attrs(nan, data + 7, len - 7, &attrs); + if (ret) + return ret; + + if (!attrs.shared_key_desc || + attrs.shared_key_desc_len < + sizeof(*shared_key_desc) + (sizeof(*key) + 2 + NAN_KEY_MIC_LEN)) { + wpa_printf(MSG_DEBUG, + "NAN: SEC: Invalid shared key descriptor attribute"); + return -1; + } + + shared_key_desc = (struct nan_shared_key *) attrs.shared_key_desc; + key = (struct wpa_eapol_key *) shared_key_desc->key; + mic_ptr = (u8 *) (key + 1); + nan_attrs_clear(nan, &attrs); + + switch (subtype) { + case NAN_SUBTYPE_DATA_PATH_REQUEST: + if (peer->ndp_setup.state != NAN_NDP_STATE_START) + wpa_printf(MSG_DEBUG, + "NAN: SEC: Request invalid state (pre Tx)"); + + /* Save the authentication token for m3. */ + ret = nan_crypto_calc_auth_token(ndp_sec->i_csid, data, len, + ndp_sec->auth_token); + break; + case NAN_SUBTYPE_DATA_PATH_RESPONSE: + if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) + wpa_printf(MSG_DEBUG, + "NAN: SEC: Pre Tx response invalid state"); + + /* Calculate MIC over the frame body. */ + ret = nan_crypto_key_mic(data, len, + ndp_sec->ptk.kck, + ndp_sec->ptk.kck_len, + ndp_sec->i_csid, mic_ptr); + break; + case NAN_SUBTYPE_DATA_PATH_CONFIRM: + if (peer->ndp_setup.state != NAN_NDP_STATE_RES_RECV) + wpa_printf(MSG_DEBUG, + "NAN: SEC: Confirm invalid state (pre Tx)"); + + /* + * Calculate MIC over the frame body concatenated with + * authentication token. + */ + tmp = os_malloc(len + NAN_AUTH_TOKEN_LEN); + if (!tmp) + return -1; + + os_memcpy(tmp, ndp_sec->auth_token, NAN_AUTH_TOKEN_LEN); + os_memcpy(tmp + NAN_AUTH_TOKEN_LEN, data, len); + + ret = nan_crypto_key_mic(tmp, len + NAN_AUTH_TOKEN_LEN, + ndp_sec->ptk.kck, + ndp_sec->ptk.kck_len, + ndp_sec->i_csid, mic_ptr); + os_free(tmp); + break; + case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL: + if (peer->ndp_setup.state != NAN_NDP_STATE_CON_RECV) + wpa_printf(MSG_DEBUG, + "NAN: SEC: Key install invalid state (pre Tx)"); + + /* Calculate MIC over the frame body */ + ret = nan_crypto_key_mic(data, len, + ndp_sec->ptk.kck, + ndp_sec->ptk.kck_len, + ndp_sec->i_csid, mic_ptr); + break; + } + + return ret; +} + + +/** + * nan_sec_get_strength - Get security strength level for a cipher suite + * @csid: Cipher suite ID + * @pairing_akmp: AKMP used for pairing (to distinguish SAE vs opportunistic) + * Returns: Security strength level (higher = stronger), 0 for no security + * + * Per Wi-Fi Aware Specification v4.0 section 7.4, security strength ordering + * (from highest to lowest): + * - CSID 8 (NCS-PK-PASN-256) using a password (SAE) + * - CSID 7 (NCS-PK-PASN-128) using a password (SAE) + * - CSID 2 (NCS-SK-256) using a PSK/Passphrase + * - CSID 1 (NCS-SK-128) using a PSK/Passphrase + * - CSID 8 (NCS-PK-PASN-256) using opportunistic bootstrapping (PASN) + * - CSID 7 (NCS-PK-PASN-128) using opportunistic bootstrapping (PASN) + * - No security + */ +static int nan_sec_get_strength(enum nan_cipher_suite_id csid, int pairing_akmp) +{ + bool is_opportunistic = pairing_akmp == WPA_KEY_MGMT_PASN; + + switch (csid) { + case NAN_CS_PK_PASN_256: + return is_opportunistic ? 2 : 6; + case NAN_CS_PK_PASN_128: + return is_opportunistic ? 1 : 5; + case NAN_CS_SK_GCM_256: + return 4; + case NAN_CS_SK_CCM_128: + return 3; + case NAN_CS_NONE: + default: + return 0; + } +} + + +/* + * nan_sec_ndp_store_keys - Store the NDP keys after successful NDP + * establishment + * + * @nan: NAN module context from nan_init() + * @peer: NAN peer for which the NDP was established + * @peer_ndi: NDI address of the peer for the NDP that was just established + * @local_ndi: Local NDI address for the NDP that was just established + * + * Returns: true if keys were stored, false otherwise + */ +bool nan_sec_ndp_store_keys(struct nan_data *nan, struct nan_peer *peer, + const u8 *peer_ndi, const u8 *local_ndi) +{ + struct nan_ndp *ndp = peer->ndp_setup.ndp; + struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec; + struct nan_peer_sec_info_entry *cur, *next; + int new_strength, cur_strength; + int new_akmp = 0; + + if (!ndp || !ndp_sec->valid || !ndp_sec->i_csid || + peer->ndp_setup.state != NAN_NDP_STATE_DONE) + return false; + + if (!NAN_CS_IS_VALID_NDP(ndp_sec->i_csid)) + return false; + + /* Get AKMP for the new security association */ + if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED) + new_akmp = peer->pairing.pairing_akmp; + + new_strength = nan_sec_get_strength(ndp_sec->i_csid, new_akmp); + + dl_list_for_each_safe(cur, next, &peer->info.sec, + struct nan_peer_sec_info_entry, list) { + if (!ether_addr_equal(peer_ndi, cur->peer_ndi) || + !ether_addr_equal(local_ndi, cur->local_ndi)) + continue; + + /* + * Per Wi-Fi Aware Specification v4.0 section 7.4: + * The security configuration should be updated if the new + * security strength is same or greater than the existing SA. + * Otherwise, the existing higher-strength SA continues to be + * used and any key material derived for the NDP setup shall be + * discarded. + */ + cur_strength = nan_sec_get_strength(cur->csid, + cur->pairing_akmp); + + wpa_printf(MSG_DEBUG, + "NAN: SEC: Comparing strength: new=%d (csid=%u, akmp=0x%x) vs. cur=%d (csid=%u, akmp=0x%x)", + new_strength, ndp_sec->i_csid, new_akmp, + cur_strength, cur->csid, cur->pairing_akmp); + + if (new_strength >= cur_strength) + goto store; + + wpa_printf(MSG_DEBUG, + "NAN: SEC: New security weaker than existing, discarding keys"); + return false; + } + + cur = os_zalloc(sizeof(*cur)); + if (!cur) { + wpa_printf(MSG_INFO, + "NAN: SEC: Failed memory allocation for security info"); + return false; + } + + dl_list_add(&peer->info.sec, &cur->list); + os_memcpy(cur->peer_ndi, peer_ndi, ETH_ALEN); + os_memcpy(cur->local_ndi, local_ndi, ETH_ALEN); + +store: + wpa_printf(MSG_DEBUG, "NAN: SEC: Store security information"); + + cur->csid = ndp_sec->i_csid; + if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED) + cur->pairing_akmp = peer->pairing.pairing_akmp; + os_memcpy(cur->pmkid, ndp_sec->i_pmkid, PMKID_LEN); + os_memcpy(cur->pmk, ndp_sec->pmk, PMK_LEN); + os_memcpy(&cur->ptk, &ndp_sec->ptk, sizeof(cur->ptk)); + + return true; +} + + +int nan_sec_get_tk(struct nan_data *nan, struct nan_peer *peer, + const u8 *peer_ndi, const u8 *local_ndi, + u8 *tk, size_t *tk_len, enum nan_cipher_suite_id *csid) +{ + struct nan_peer_sec_info_entry *cur; + + dl_list_for_each(cur, &peer->info.sec, + struct nan_peer_sec_info_entry, list) { + if (!ether_addr_equal(peer_ndi, cur->peer_ndi) || + !ether_addr_equal(local_ndi, cur->local_ndi)) + continue; + + os_memcpy(tk, &cur->ptk.tk, cur->ptk.tk_len); + *tk_len = cur->ptk.tk_len; + *csid = cur->csid; + return 0; + } + + return -1; +} diff --git a/contrib/wpa/src/nan/nan_util.c b/contrib/wpa/src/nan/nan_util.c new file mode 100644 index 000000000000..7464a00b18d1 --- /dev/null +++ b/contrib/wpa/src/nan/nan_util.c @@ -0,0 +1,2043 @@ +/* + * Wi-Fi Aware - NAN module utils + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "utils/bitfield.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_common.h" +#include "nan_i.h" + + +static void nan_attrs_clear_list(struct nan_data *nan, + struct dl_list *list) +{ + struct nan_attrs_entry *entry, *pentry; + + dl_list_for_each_safe(entry, pentry, list, struct nan_attrs_entry, + list) { + dl_list_del(&entry->list); + os_free(entry); + } +} + + +/* + * nan_attrs_clear - Free data from NAN parsing + * @nan: NAN module context from nan_init() + * @attrs: Parsed nan_attrs + */ +void nan_attrs_clear(struct nan_data *nan, struct nan_attrs *attrs) +{ + nan_attrs_clear_list(nan, &attrs->serv_desc_ext); + nan_attrs_clear_list(nan, &attrs->avail); + nan_attrs_clear_list(nan, &attrs->ndc); + nan_attrs_clear_list(nan, &attrs->ulw); + nan_attrs_clear_list(nan, &attrs->dev_capa); + nan_attrs_clear_list(nan, &attrs->element_container); + + os_memset(attrs, 0, sizeof(*attrs)); +} + + +/* + * nan_parse_attrs - Parse NAN attributes + * @nan: NAN module context from nan_init() + * @data: Buffer holding the attributes + * @len: Length of &data + * @attrs: On return would hold the parsed attributes + * Returns: 0 on success; positive or negative indicate an error + * + * Note: In case of success, the caller must free temporary memory allocations + * by calling nan_attrs_clear() when the parsed data is not needed anymore. + */ +int nan_parse_attrs(struct nan_data *nan, const u8 *data, size_t len, + struct nan_attrs *attrs) +{ + struct nan_attrs_entry *entry; + const u8 *pos = data; + const u8 *end = pos + len; + + os_memset(attrs, 0, sizeof(*attrs)); + + dl_list_init(&attrs->serv_desc_ext); + dl_list_init(&attrs->avail); + dl_list_init(&attrs->ndc); + dl_list_init(&attrs->ulw); + dl_list_init(&attrs->dev_capa); + dl_list_init(&attrs->element_container); + + while (end - pos > 3) { + u8 id = *pos++; + u16 attr_len = WPA_GET_LE16(pos); + + pos += 2; + if (attr_len > end - pos) + goto fail; + + switch (id) { + case NAN_ATTR_SDEA: + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->serv_desc_ext, &entry->list); + break; + case NAN_ATTR_DEVICE_CAPABILITY: + /* Validate Device Capability attribute length */ + if (attr_len < sizeof(struct nan_device_capa)) + break; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->dev_capa, &entry->list); + break; + case NAN_ATTR_NDP: + /* Validate minimal NDP attribute length */ + if (attr_len < sizeof(struct ieee80211_ndp)) + break; + + attrs->ndp = pos; + attrs->ndp_len = attr_len; + break; + case NAN_ATTR_NAN_AVAILABILITY: + /* Validate minimal Availability attribute length */ + if (attr_len < sizeof(struct nan_avail)) + break; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->avail, &entry->list); + break; + case NAN_ATTR_NDC: + /* Validate minimal NDC attribute length */ + if (attr_len < sizeof(struct ieee80211_ndc)) + break; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->ndc, &entry->list); + break; + case NAN_ATTR_UNALIGNED_SCHEDULE: + if (attr_len < sizeof(struct nan_unaligned_sched)) + break; + + entry = os_malloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->ulw, &entry->list); + break; + case NAN_ATTR_NDL: + /* Validate minimal NDL attribute length */ + if (attr_len < sizeof(struct ieee80211_ndl)) + break; + + attrs->ndl = pos; + attrs->ndl_len = attr_len; + break; + case NAN_ATTR_NDL_QOS: + /* Validate QoS attribute length */ + if (attr_len < sizeof(struct ieee80211_nan_qos)) + break; + + attrs->ndl_qos = pos; + attrs->ndl_qos_len = attr_len; + break; + case NAN_ATTR_ELEM_CONTAINER: + /* Validate minimal Element Container attribute length + */ + if (attr_len < 1) + break; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + + entry->ptr = pos; + entry->len = attr_len; + dl_list_add_tail(&attrs->element_container, + &entry->list); + break; + case NAN_ATTR_CSIA: + if (attr_len < sizeof(struct nan_cipher_suite_info) + + sizeof(struct nan_cipher_suite)) + break; + + attrs->cipher_suite_info = pos; + attrs->cipher_suite_info_len = attr_len; + break; + case NAN_ATTR_SCIA: + if (attr_len < sizeof(struct nan_sec_ctxt)) + break; + + attrs->sec_ctxt_info = pos; + attrs->sec_ctxt_info_len = attr_len; + break; + case NAN_ATTR_SHARED_KEY_DESCR: + if (attr_len < sizeof(struct nan_shared_key) + + sizeof(struct wpa_eapol_key)) + break; + + attrs->shared_key_desc = pos; + attrs->shared_key_desc_len = attr_len; + break; + case NAN_ATTR_DCEA: + attrs->dev_capa_ext = pos; + attrs->dev_capa_ext_len = attr_len; + break; + case NAN_ATTR_NPBA: + /* + * Validate minimal NPBA length: Dialog Token (1) + + * Type and Statuss (1) + Reason Code (1) + + * Pairing Bootstrapping Method (2) + */ + if (attr_len < 5) + break; + attrs->npba = pos; + attrs->npba_len = attr_len; + break; + case NAN_ATTR_NIRA: + if (pos[0] != NAN_NIRA_CIPHER_VER_128) + break; + + /* Cipher Version (1) + Nonce (8) + Tag (8) */ + if (attr_len != + 1 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN) + break; + + attrs->nira = pos; + attrs->nira_len = attr_len; + break; + case NAN_ATTR_NDP_EXT: + /* + * Validate minimal NDPE attribute length. NDP and NDPE + * attributes have the common structure and thus the + * same minimal length requirement based on the common + * fields (see struct ieee80211_ndp). + */ + if (attr_len < sizeof(struct ieee80211_ndp)) + break; + + attrs->ndpe = pos; + attrs->ndpe_len = attr_len; + break; + case NAN_ATTR_MASTER_INDICATION: + case NAN_ATTR_CLUSTER: + case NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST: + case NAN_ATTR_SDA: + case NAN_ATTR_CONN_CAPA: + case NAN_ATTR_WLAN_INFRA: + case NAN_ATTR_P2P_OPER: + case NAN_ATTR_IBSS: + case NAN_ATTR_MESH: + case NAN_ATTR_FURTHER_NAN_SD: + case NAN_ATTR_FURTHER_AVAIL_MAP: + case NAN_ATTR_COUNTRY_CODE: + case NAN_ATTR_RANGING: + case NAN_ATTR_CLUSTER_DISCOVERY: + case NAN_ATTR_RANGING_INFO: + case NAN_ATTR_RANGING_SETUP: + case NAN_ATTR_FTM_RANGING_REPORT: + case NAN_ATTR_EXT_WLAN_INFRA: + case NAN_ATTR_EXT_P2P_OPER: + case NAN_ATTR_EXT_IBSS: + case NAN_ATTR_EXT_MESH: + case NAN_ATTR_PUBLIC_AVAILABILITY: + case NAN_ATTR_SUBSC_SERVICE_ID_LIST: + case NAN_ATTR_S3: + case NAN_ATTR_TPEA: + case NAN_ATTR_VENDOR_SPECIFIC: + wpa_printf(MSG_DEBUG, "NAN: ignore attr=%u", id); + break; + default: + wpa_printf(MSG_DEBUG, "NAN: unknown attr=%u", id); + break; + } + + pos += attr_len; + } + + /* Parsing is considered success only if all attributes were consumed */ + if (pos == end) + return 0; + +fail: + nan_attrs_clear(nan, attrs); + return -1; +} + + +/* + * nan_is_naf - Check if a given frame is a NAN Action frame + * @mgmt: NAN Action frame + * @len: Length of the Management frame in octets + * Returns: true if NAF; otherwise false + */ +bool nan_is_naf(const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 subtype; + + /* + * 802.11 header + category + NAN Action frame minimal + subtype (1) + */ + if (len < IEEE80211_MIN_ACTION_LEN(naf)) { + wpa_printf(MSG_DEBUG, "NAN: Too short NAN frame"); + return false; + } + + if (mgmt->u.action.u.naf.action != WLAN_PA_VENDOR_SPECIFIC || + WPA_GET_BE24(mgmt->u.action.u.naf.oui) != OUI_WFA || + mgmt->u.action.u.naf.oui_type != NAN_NAF_OUI_TYPE) + return false; + + subtype = mgmt->u.action.u.naf.subtype; + + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + !(subtype >= NAN_SUBTYPE_DATA_PATH_REQUEST && + subtype <= NAN_SUBTYPE_DATA_PATH_TERMINATION && + mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL)) { + wpa_printf(MSG_DEBUG, "NAN: Invalid action category for NAF"); + return false; + } + + return true; +} + + +/* + * nan_parse_naf - Parse a NAN Action frame content + * @nan: NAN module context from nan_init() + * @mgmt: NAN action frame + * @len: Length of the management frame in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success; positive or negative indicate an error + * + * Note: in case of success, the caller must free temporary memory allocations + * by calling nan_attrs_clear() when the parsed data is not needed anymore. In + * addition, as the &mgmt is referenced from the returned structure, the caller + * must ensure that the frame buffer remains valid and unmodified as long as the + * &msg object is used. + */ +int nan_parse_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt, + size_t len, struct nan_msg *msg) +{ + if (!nan_is_naf(mgmt, len)) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: Parse NAF"); + + msg->oui_type = mgmt->u.action.u.naf.oui_type; + msg->oui_subtype = mgmt->u.action.u.naf.subtype; + + msg->mgmt = mgmt; + msg->len = len; + + return nan_parse_attrs(nan, + mgmt->u.action.u.naf.variable, + len - IEEE80211_MIN_ACTION_LEN(naf), + &msg->attrs); +} + + +/* + * nan_add_dev_capa_attr - Add Device Capability attribute + * @nan: NAN module context from nan_init() + * @buf: wpabuf to which the attribute would be added + */ +void nan_add_dev_capa_attr(struct nan_data *nan, struct wpabuf *buf) +{ + wpabuf_put_u8(buf, NAN_ATTR_DEVICE_CAPABILITY); + wpabuf_put_le16(buf, sizeof(struct nan_device_capa)); + + /* Device capabilities apply to the device, so set map ID = 0 */ + wpabuf_put_u8(buf, 0); + wpabuf_put_le16(buf, nan->cfg->dev_capa.cdw_info); + wpabuf_put_u8(buf, nan->cfg->dev_capa.supported_bands); + wpabuf_put_u8(buf, nan->cfg->dev_capa.op_mode); + wpabuf_put_u8(buf, nan->cfg->dev_capa.n_antennas); + wpabuf_put_le16(buf, nan->cfg->dev_capa.channel_switch_time); + wpabuf_put_u8(buf, nan->cfg->dev_capa.capa); +} + + +/* + * nan_add_csia - Add Cipher Suite Information Attribute (CSIA) to a buffer + * @buf: Buffer to add the attribute to + * @capab: Capabilities field (1 octet) + * @cs_list_len: Number of cipher suites in the list + * @cs_list: Array of cipher suite structures + * + * This function constructs and appends a NAN Cipher Suite Information Attribute + * to the provided buffer. + * + * Returns: 0 on success, -1 on failure (insufficient buffer space) + */ +int nan_add_csia(struct wpabuf *buf, u8 capab, size_t cs_list_len, + const struct nan_cipher_suite *cs_list) +{ + size_t i; + /* Capabilities (1 octet) + Cipher Suite List */ + size_t attr_len = sizeof(capab) + cs_list_len * sizeof(*cs_list); + + if (wpabuf_tailroom(buf) < + (size_t) (NAN_ATTR_HDR_LEN + attr_len)) { + wpa_printf(MSG_DEBUG, + "NAN: Not enough space to add CSIA attribute"); + return -1; + } + + wpabuf_put_u8(buf, NAN_ATTR_CSIA); + wpabuf_put_le16(buf, attr_len); + wpabuf_put_u8(buf, capab); + + for (i = 0; i < cs_list_len; i++) { + wpabuf_put_u8(buf, cs_list[i].csid); + wpabuf_put_u8(buf, cs_list[i].instance_id); + } + + return 0; +} + + +/** + * nan_add_dev_capa_ext_attr - Add NAN Device Capability Extension attribute + * @nan: NAN module context from nan_init() + * @buf: wpabuf to which the attribute would be added + */ +void nan_add_dev_capa_ext_attr(struct nan_data *nan, struct wpabuf *buf) +{ + u8 pairing_and_npk_caching = 0; + + if (nan->cfg->pairing_cfg.pairing_setup) + pairing_and_npk_caching |= + NAN_DEV_CAPA_EXT_INFO_1_PAIRING_SETUP; + if (nan->cfg->pairing_cfg.npk_caching) + pairing_and_npk_caching |= + NAN_DEV_CAPA_EXT_INFO_1_NPK_NIK_CACHING; + + if (!nan->cfg->dev_capa_ext_reg_info && + !pairing_and_npk_caching) + return; + + wpabuf_put_u8(buf, NAN_ATTR_DCEA); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, nan->cfg->dev_capa_ext_reg_info); + wpabuf_put_u8(buf, pairing_and_npk_caching); +} + + +/** + * nan_add_nira - Add NIRA (NAN Identity Resolution Attribute) to a buffer + * @buf: Buffer to which the NIRA is appended + * @tag: Pointer to NIRA tag data (NAN_NIRA_TAG_LEN bytes) + * @nonce: Pointer to NIRA nonce data (NAN_NIRA_NONCE_LEN bytes) + * Returns: 0 on success, -1 if there is insufficient space in the buffer + * + * This function constructs and appends a NAN Identity Resolution Attribute + * (NIRA) to the provided buffer. + */ +int nan_add_nira(struct wpabuf *buf, const u8 *tag, const u8 *nonce) +{ + u16 attr_len = 1 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN; + + if (wpabuf_tailroom(buf) < (size_t) (NAN_ATTR_HDR_LEN + attr_len)) { + wpa_printf(MSG_INFO, "NAN: Not enough space to add NIRA"); + return -1; + } + + wpabuf_put_u8(buf, NAN_ATTR_NIRA); + wpabuf_put_le16(buf, attr_len); + wpabuf_put_u8(buf, NAN_NIRA_CIPHER_VER_128); + wpabuf_put_data(buf, nonce, NAN_NIRA_NONCE_LEN); + wpabuf_put_data(buf, tag, NAN_NIRA_TAG_LEN); + + return 0; +} + + +/** + * nan_chan_to_chan_idx_map - Convert an op_class and chan to channel bitmap + * @nan: NAN module context from nan_init() + * @op_class: the operating class + * @channel: channel number + * @chan_idx_map: On success, would hold the channel index bitmap + * Returns: 0 on success, otherwise a negative value + */ +int nan_chan_to_chan_idx_map(struct nan_data *nan, + u8 op_class, u8 channel, u16 *chan_idx_map) +{ + int ret; + const struct oper_class_map *op_c; + + if (!chan_idx_map) + return -1; + + op_c = get_oper_class(NULL, op_class); + if (!op_c) + return -1; + + ret = op_class_chan_to_idx(op_c, channel); + if (ret < 0) + return ret; + + if ((size_t) ret >= (sizeof(*chan_idx_map) * 8)) + return -1; + + *chan_idx_map = BIT(ret); + return 0; +} + + +static u16 nan_add_avail_entry(struct nan_data *nan, + struct nan_time_bitmap *tbm, + u8 type, u8 op_class, u16 chan_bm, + u8 prim_chan_bm, struct wpabuf *buf) +{ + u16 ctrl; + u8 chan_ctrl; + u8 *len_ptr; + u8 nss = BITS(nan->cfg->dev_capa.n_antennas, NAN_DEV_CAPA_RX_ANT_MASK, + NAN_DEV_CAPA_RX_ANT_POS); + + len_ptr = wpabuf_put(buf, 2); + + /* Potential availability entries are handled separately */ + if (type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED && + type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot add non committed/conditional entry"); + return 0; + } + + /* + * Add the entry control field + * - usage preference is not set for committed and conditional + * - utilization is max. + */ + ctrl = type; + ctrl |= NAN_AVAIL_ENTRY_DEF_UTIL << NAN_AVAIL_ENTRY_CTRL_UTIL_POS; + ctrl |= nss << NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS; + ctrl |= NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT; + wpabuf_put_le16(buf, ctrl); + + /* Add the time bitmap control field */ + ctrl = tbm->duration << NAN_TIME_BM_CTRL_BIT_DURATION_POS; + ctrl |= tbm->period << NAN_TIME_BM_CTRL_PERIOD_POS; + ctrl |= tbm->offset << NAN_TIME_BM_CTRL_START_OFFSET_POS; + wpabuf_put_le16(buf, ctrl); + + wpabuf_put_u8(buf, tbm->len); + wpabuf_put_data(buf, tbm->bitmap, tbm->len); + + /* Add the channel entry: single contiguous channel entry */ + chan_ctrl = NAN_BAND_CHAN_CTRL_TYPE; + chan_ctrl |= 1 << NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS; + wpabuf_put_u8(buf, chan_ctrl); + wpabuf_put_u8(buf, op_class); + wpabuf_put_le16(buf, chan_bm); + wpabuf_put_u8(buf, prim_chan_bm); + + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + return (u8 *) wpabuf_put(buf, 0) - len_ptr; +} + + +int nan_get_chan_bm(struct nan_data *nan, const struct nan_sched_chan *chan, + u8 *op_class, u16 *chan_bm, u16 *pri_chan_bm) +{ + u8 channel; + enum hostapd_hw_mode mode; + int ret, sec_channel_offset; + int freq_offsset = chan->freq - chan->center_freq1; + u32 idx; + enum oper_chan_width bandwidth; + + switch (chan->bandwidth) { + case 20: + case 40: + default: + *pri_chan_bm = 0; + bandwidth = CONF_OPER_CHWIDTH_USE_HT; + break; + case 80: + bandwidth = CONF_OPER_CHWIDTH_80MHZ; + + idx = (freq_offsset + 30) / 20; + *pri_chan_bm = BIT(idx); + break; + case 160: + if (chan->center_freq2) { + bandwidth = CONF_OPER_CHWIDTH_80P80MHZ; + + /* TODO: Need to support auxiliary channel bitmap */ + idx = (freq_offsset + 30) / 20; + *pri_chan_bm = BIT(idx); + } else { + bandwidth = CONF_OPER_CHWIDTH_160MHZ; + idx = (freq_offsset + 70) / 20; + *pri_chan_bm = BIT(idx); + } + break; + } + + if (freq_offsset > 0) + sec_channel_offset = 1; + else if (freq_offsset < 0) + sec_channel_offset = -1; + else + sec_channel_offset = 0; + + wpa_printf(MSG_DEBUG, + "NAN: Get chan bm: freq=%d, center_freq1=%d, bandwidth=%u, sec_channel_offset=%d", + chan->freq, chan->center_freq1, chan->bandwidth, + freq_offsset); + + /* For bandwidths >= 80 need to use the center frequency */ + mode = ieee80211_freq_to_channel_ext(bandwidth == + CONF_OPER_CHWIDTH_USE_HT ? + chan->freq : chan->center_freq1, + sec_channel_offset, + bandwidth, op_class, &channel); + if (mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot get channel and op_class"); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: Derived op_class=%u, channel=%u", + *op_class, channel); + + ret = nan_chan_to_chan_idx_map(nan, *op_class, channel, chan_bm); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Failed to derive channel bitmap"); + return -1; + } + + return 0; +} + + +static void nan_add_pot_avail_entry(struct nan_data *nan, + struct nan_chan_entry *entries, + unsigned int n_entries, u8 pref, + struct wpabuf *buf) +{ + u16 ctrl; + u8 chan_ctrl; + size_t i; + u8 nss = BITS(nan->cfg->dev_capa.n_antennas, NAN_DEV_CAPA_RX_ANT_MASK, + NAN_DEV_CAPA_RX_ANT_POS); + + wpa_printf(MSG_DEBUG, "NAN: Adding potential entry: n_entries=%u", + n_entries); + + if (!n_entries) + return; + + /* The number of channel entries can be too big for the buffer */ + if (wpabuf_tailroom(buf) < 2 + 2 + 1 + n_entries * 4) { + n_entries = (wpabuf_tailroom(buf) - 5) / 4; + + wpa_printf(MSG_DEBUG, + "NAN: Not enough space to add potential entries, reduce to %u", + n_entries); + } + + /* + * ctrl (2) + chan control (1) + n_entries * (nan_chan_entry without + * the aux bitmap). + */ + wpabuf_put_le16(buf, 3 + n_entries * 4); + + ctrl = NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL; + ctrl |= NAN_AVAIL_ENTRY_DEF_UTIL << NAN_AVAIL_ENTRY_CTRL_UTIL_POS; + ctrl |= nss << NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS; + ctrl |= pref << NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS; + wpabuf_put_le16(buf, ctrl); + + /* Add all channel entries */ + chan_ctrl = NAN_BAND_CHAN_CTRL_TYPE; + chan_ctrl |= n_entries << NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS; + wpabuf_put_u8(buf, chan_ctrl); + + for (i = 0; i < n_entries; i++) { + struct nan_chan_entry *cur = &entries[i]; + + wpabuf_put_u8(buf, cur->op_class); + wpabuf_put_le16(buf, cur->chan_bitmap); + wpabuf_put_u8(buf, 0); + } +} + + +static void +nan_build_pot_avail_entry_with_chans(struct nan_data *nan, + const struct nan_channels *pot_chans, + struct wpabuf *buf, u8 map_id) +{ + struct nan_chan_entry chan_entries[global_op_class_size]; + size_t i, n_entries; + + os_memset(chan_entries, 0, sizeof(chan_entries)); + n_entries = 0; + + wpa_printf(MSG_DEBUG, "NAN: Adding potential entries: n_chans=%u", + pot_chans->n_chans); + + for (i = 0; i < pot_chans->n_chans; i++) { + struct nan_channel_info *chan = &pot_chans->chans[i]; + struct nan_chan_entry *cur; + u16 cbm = 0; + size_t j; + int ret; + + if (i > 0 && pot_chans->chans[i - 1].pref != chan->pref) { + nan_add_pot_avail_entry(nan, chan_entries, n_entries, + pot_chans->chans[i - 1].pref, + buf); + + os_memset(chan_entries, 0, sizeof(chan_entries)); + n_entries = 0; + } + + ret = nan_chan_to_chan_idx_map(nan, chan->op_class, + chan->channel, &cbm); + if (ret) + continue; + + /* Try to find and entry that matches the operating class */ + for (j = 0, cur = NULL; j < n_entries; j++) { + cur = &chan_entries[j]; + + if (!cur->op_class || cur->op_class == chan->op_class) + break; + } + + if (!n_entries) + cur = &chan_entries[n_entries++]; + else if (j == n_entries && n_entries < global_op_class_size) + cur = &chan_entries[n_entries++]; + else if (!cur) + continue; + + cur->op_class = chan->op_class; + cur->chan_bitmap |= cbm; + } + + if (n_entries) + nan_add_pot_avail_entry(nan, chan_entries, n_entries, + pot_chans->chans[i - 1].pref, buf); + + wpa_printf(MSG_DEBUG, "NAN: Added potential entries: done"); +} + + +static void nan_build_pot_avail_entry(struct nan_data *nan, struct wpabuf *buf, + u8 map_id) +{ + struct nan_channels pot_chans; + + os_memset(&pot_chans, 0, sizeof(pot_chans)); + + if (!nan->cfg->get_chans || + nan->cfg->get_chans(nan->cfg->cb_ctx, map_id, &pot_chans) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to get channels. Not adding potential"); + return; + } + + if (pot_chans.n_chans != 0) + nan_build_pot_avail_entry_with_chans(nan, &pot_chans, buf, + map_id); + else + wpa_printf(MSG_DEBUG, + "NAN: No channels available. Not adding potential: map_id=%u", + map_id); + + os_free(pot_chans.chans); +} + + +/** + * nan_add_avail_attrs - Add NAN availability attributes + * @nan: NAN module context from nan_init() + * @sequence_id: Sequence ID to be used in the availability attributes + * @map_ids_bitmap: Bitmap of map IDs to be included in the availability + * attributes + * @type_for_conditional: Type field to be used for conditional entries + * @n_chans: Number of channels in chans + * @chans: Channel schedules + * @buf: Frame buffer to which the attribute would be added + * @include_potential: Whether to add potential availability entries + * Returns: 0 on success, negative on failure. + * + * An availability attribute is added for each map (identified by map ID) in the + * schedule. All channels with the same map ID are added to the same + * availability attribute. Each attribute will hold an availability entry for + * committed slots and an availability entry for conditional slots. + */ +int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id, + u32 map_ids_bitmap, u8 type_for_conditional, + size_t n_chans, struct nan_chan_schedule *chans, + struct wpabuf *buf, bool include_potential) +{ + u8 last_map_id = NAN_INVALID_MAP_ID; + u32 handled_map_ids = 0; + u8 *len_ptr = NULL; + u8 i; + + wpa_printf(MSG_DEBUG, "NAN: Add availability attrs. n_chans=%zu", + n_chans); + + for (i = 0; i < n_chans; i++) { + struct nan_chan_schedule *chan = &chans[i]; + u8 op_class; + u16 chan_bm, pri_chan_bm; + int ret; + + if (!chan->conditional.len && !chan->committed.len) { + wpa_printf(MSG_DEBUG, + "NAN: committed and conditional are empty"); + continue; + } + + ret = nan_get_chan_bm(nan, &chan->chan, &op_class, + &chan_bm, &pri_chan_bm); + if (ret) + continue; + + /* + * All channels with the same map ID should be added to the same + * availability attribute, so verify that the map IDs are + * sorted. + */ + if (last_map_id != NAN_INVALID_MAP_ID && + last_map_id > chan->map_id) { + wpa_printf(MSG_DEBUG, + "NAN: Map IDs not sorted properly"); + return -1; + } + + if (!(map_ids_bitmap & BIT(chan->map_id))) { + wpa_printf(MSG_DEBUG, + "NAN: Skip adding availability for map_id=%u", + chan->map_id); + continue; + } + + if (last_map_id != chan->map_id) { + u16 ctrl; + + if (last_map_id != NAN_INVALID_MAP_ID) { + wpa_printf(MSG_DEBUG, + "NAN: Add avail attr done: map_id=%u", + last_map_id); + + if (include_potential) + nan_build_pot_avail_entry(nan, buf, + last_map_id); + WPA_PUT_LE16(len_ptr, + (u8 *) wpabuf_put(buf, 0) - + len_ptr - 2); + } + + last_map_id = chan->map_id; + handled_map_ids |= BIT(last_map_id); + + wpa_printf(MSG_DEBUG, "NAN: Add avail attr map_id=%u", + last_map_id); + + wpabuf_put_u8(buf, NAN_ATTR_NAN_AVAILABILITY); + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, sequence_id); + + ctrl = last_map_id << NAN_AVAIL_CTRL_MAP_ID_POS; + + /* + * The spec states that this bit should be set if the + * committed changed or if conditional is included. Set + * it anyway, as it is not known what information the + * peer has on our schedule. Similarly, always set the + * potential changed bit. + */ + ctrl |= NAN_AVAIL_CTRL_COMMITTED_CHANGED | + NAN_AVAIL_CTRL_POTENTIAL_CHANGED; + wpabuf_put_le16(buf, ctrl); + } + + /* TODO: handle primary channel configuration */ + if (chan->committed.len) + nan_add_avail_entry(nan, &chan->committed, + NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED, + op_class, chan_bm, pri_chan_bm, + buf); + + if (chan->conditional.len) + nan_add_avail_entry(nan, &chan->conditional, + type_for_conditional, + op_class, chan_bm, pri_chan_bm, + buf); + } + + if (last_map_id != NAN_INVALID_MAP_ID) { + if (include_potential) + nan_build_pot_avail_entry(nan, buf, last_map_id); + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + + wpa_printf(MSG_DEBUG, "NAN: Add avail attr done: map_id=%u", + last_map_id); + } else { + wpa_printf(MSG_DEBUG, + "NAN: No committed/conditional entries were added"); + } + + if (!include_potential) + return 0; + + /* + * Add NAN availability attributes with a single potential availability + * entry for map IDs that are not included in the schedule. + */ + map_ids_bitmap &= ~handled_map_ids; + wpa_printf(MSG_DEBUG, + "NAN: Add avail attrs for remaining map IDs: bitmap=0x%x", + map_ids_bitmap); + + while (map_ids_bitmap) { + struct nan_channels pot_chans; + u8 map_id = ffs(map_ids_bitmap) - 1; + u16 ctrl = map_id << NAN_AVAIL_CTRL_MAP_ID_POS | + NAN_AVAIL_CTRL_POTENTIAL_CHANGED; + + map_ids_bitmap &= ~BIT(map_id); + + wpa_printf(MSG_DEBUG, "NAN: Add avail attr for map_id=%u", + map_id); + + os_memset(&pot_chans, 0, sizeof(pot_chans)); + + if (!nan->cfg->get_chans || + nan->cfg->get_chans(nan->cfg->cb_ctx, map_id, + &pot_chans) < 0 || + !pot_chans.chans) { + wpa_printf(MSG_DEBUG, + "NAN: No channels available. Not adding potential: map_id=%u", + map_id); + continue; + } + + wpabuf_put_u8(buf, NAN_ATTR_NAN_AVAILABILITY); + len_ptr = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, sequence_id); + wpabuf_put_le16(buf, ctrl); + + nan_build_pot_avail_entry_with_chans(nan, &pot_chans, buf, + map_id); + os_free(pot_chans.chans); + + WPA_PUT_LE16(len_ptr, (u8 *) wpabuf_put(buf, 0) - len_ptr - 2); + } + + return 0; +} + + +/** + * nan_del_avail_entry - Delete an availability entry + * @entry: The availability entry to delete + */ +void nan_del_avail_entry(struct nan_avail_entry *entry) +{ + if (!entry) + return; + os_free(entry->band_chan); + os_free(entry); +} + + +/** + * nan_flush_avail_entries - Flush a list of availability entries + * @avail_entries: List of availability entries + */ +void nan_flush_avail_entries(struct dl_list *avail_entries) +{ + struct nan_avail_entry *cur, *next; + + dl_list_for_each_safe(cur, next, avail_entries, + struct nan_avail_entry, list) { + dl_list_del(&cur->list); + nan_del_avail_entry(cur); + } +} + + +/** + * nan_sched_entries_to_avail_entries - Convert NAN schedule entries to NAN + * availability entries + * + * @nan: NAN module context from nan_init() + * @avail_entries: On successful return would hold a valid list of availability + * entries + * @sched_entries: Buffer holding the schedule entries, each of type + * &struct nan_sched_entry + * @sched_entries_len: Length of the sched_entries buffer + */ +int nan_sched_entries_to_avail_entries(struct nan_data *nan, + struct dl_list *avail_entries, + const u8 *sched_entries, + u16 sched_entries_len) +{ + dl_list_init(avail_entries); + + if (!sched_entries || !sched_entries_len) + return 0; + + if (sched_entries_len < sizeof(struct nan_sched_entry)) { + wpa_printf(MSG_DEBUG, "NAN: Schedule entry too short=%u", + sched_entries_len); + return -1; + } + + while (sched_entries_len > 0) { + const struct nan_sched_entry *sched_entry = + (const struct nan_sched_entry *) sched_entries; + struct nan_avail_entry *avail_entry; + u16 ctrl; + size_t elen; + + if (sched_entries_len < sizeof(struct nan_sched_entry)) + goto fail; + elen = sizeof(struct nan_sched_entry) + sched_entry->len; + if (sched_entries_len < elen) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid schedule entry len=%u", + sched_entry->len); + goto fail; + } + + if (sched_entry->len > NAN_TIME_BITMAP_MAX_LEN) + goto fail; + + avail_entry = os_zalloc(sizeof(struct nan_avail_entry)); + if (!avail_entry) + goto fail; + + avail_entry->map_id = sched_entry->map_id; + ctrl = le_to_host16(sched_entry->control); + + avail_entry->tbm.duration = + BITS(ctrl, + NAN_TIME_BM_CTRL_BIT_DURATION_MASK, + NAN_TIME_BM_CTRL_BIT_DURATION_POS); + avail_entry->tbm.period = + BITS(ctrl, + NAN_TIME_BM_CTRL_PERIOD_MASK, + NAN_TIME_BM_CTRL_PERIOD_POS); + avail_entry->tbm.offset = + BITS(ctrl, + NAN_TIME_BM_CTRL_START_OFFSET_MASK, + NAN_TIME_BM_CTRL_START_OFFSET_POS); + + avail_entry->tbm.len = sched_entry->len; + os_memcpy(avail_entry->tbm.bitmap, sched_entry->bm, + sched_entry->len); + + dl_list_init(&avail_entry->list); + dl_list_add(avail_entries, &avail_entry->list); + + sched_entries_len -= elen; + sched_entries += elen; + } + + return 0; + +fail: + nan_flush_avail_entries(avail_entries); + return -1; +} + + +/** + * nan_tbm_to_bf - Convert a time bitmap to bitfield + * @nan: NAN module context from nan_init() + * @tbm: Time bitmap + * Returns: The converted bitfield on success; otherwise, NULL + * + * The function takes a time bitmap and converts it to a bitfield that + * represents a time bitmap with 16 TUs slots that covers a period of 8192 TUs. + * The conversion takes into account the duration, period, and offset fields of + * the time bitmap. + */ +struct bitfield * nan_tbm_to_bf(struct nan_data *nan, + const struct nan_time_bitmap *tbm) +{ + struct bitfield *bf, *base; + u32 slot_duration, period, len; + u32 dur_factor, i, j, iter, max_iter; + + wpa_printf(MSG_DEBUG, + "NAN: Convert time bitmap: len=%u, dur=%u, period=%u, offset=%u", + tbm->len, tbm->duration, tbm->period, tbm->offset); + + /* Calculate the length and make sure it is less than the period */ + dur_factor = 1 << tbm->duration; + slot_duration = 16 * dur_factor; + + if (tbm->period == 0) + period = tbm->len * 8 * slot_duration; + else + period = 128 * (1 << (tbm->period - 1)); + + len = tbm->len; + if (tbm->len * 8 * slot_duration > period) { + wpa_printf(MSG_DEBUG, + "NAN: Time bitmap length is bigger than duration. Chop it"); + len = period / slot_duration / 8; + } + + /* The 'base' bitfield holds the original bitmap */ + base = bitfield_alloc_data(tbm->bitmap, tbm->len); + if (!base) { + wpa_printf(MSG_DEBUG, "NAN: Failed to allocate base bitmap"); + return NULL; + } + + if (!len) { + wpa_printf(MSG_DEBUG, "NAN: Empty time bitmap"); + return base; + } + + /* Allocate a time bitmap to cover a 8192 TUs period */ + bf = bitfield_alloc(NAN_MAX_TIME_BITMAP_SLOTS); + if (!bf) { + bitfield_free(base); + return NULL; + } + + /* + * Convert the original map to a map of 16 TU slots taking into account + * the time bitmap offset and the period. Note that during availability + * attribute parsing, it was verified that offset is smaller than the + * period. + */ + max_iter = NAN_MAX_PERIOD_TUS / period; + for (iter = 0; iter < max_iter; iter++) { + u32 start_slot = tbm->offset + iter * (period / 16); + + for (i = 0; i < len * 8; i++) { + bool slot_set = bitfield_is_set(base, i); + + for (j = 0; j < dur_factor; j++) { + u32 target_slot = + start_slot + (i * dur_factor + j); + + if (target_slot >= NAN_MAX_TIME_BITMAP_SLOTS) + goto done; + + if (slot_set) + bitfield_set(bf, target_slot); + } + } + } + +done: + bitfield_free(base); + + wpa_printf(MSG_DEBUG, "NAN: Done converting bitmap"); + + return bf; +} + + +/** + * nan_sched_to_bf - Convert schedule to bitfield + * @nan: NAN module context from nan_init() + * @sched: List of availability entries representing the schedule entries + * @map_id: On return holds the map_id covered by the schedule entries + * @reason: In case of failure contains the reason + * Returns: A bitfield representing the schedule on success; otherwise NULL + * + * Note: The function only supports converting a schedule where all map IDs are + * identical. There is no support for a schedule that uses different maps. + */ +struct bitfield * nan_sched_to_bf(struct nan_data *nan, struct dl_list *sched, + u8 *map_id, enum nan_reason *reason) +{ + struct bitfield *sched_bf = NULL; + struct nan_avail_entry *cur; + + *map_id = NAN_INVALID_MAP_ID; + + /* Convert all schedule availability entries to bf */ + dl_list_for_each(cur, sched, struct nan_avail_entry, list) { + struct bitfield *tmp; + + if (*map_id == NAN_INVALID_MAP_ID) { + *map_id = cur->map_id; + } else if (cur->map_id != *map_id) { + wpa_printf(MSG_DEBUG, + "NAN: No support for multiple maps"); + *reason = NAN_REASON_RESOURCE_LIMITATION; + goto fail; + } + + tmp = nan_tbm_to_bf(nan, &cur->tbm); + if (!tmp) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to convert sched to bf"); + *reason = NAN_REASON_UNSPECIFIED_REASON; + goto fail; + } + + bitfield_dump(tmp, "NAN: Schedule entry bitmap"); + + if (!sched_bf) { + sched_bf = tmp; + } else { + int res; + + if (bitfield_intersects(sched_bf, tmp)) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid availability: TBMs intersect"); + *reason = NAN_REASON_INVALID_AVAILABILITY; + bitfield_free(tmp); + goto fail; + } + + res = bitfield_union_in_place(sched_bf, tmp); + bitfield_free(tmp); + if (res) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to union sched bf"); + *reason = NAN_REASON_UNSPECIFIED_REASON; + goto fail; + } + } + } + + return sched_bf; + +fail: + bitfield_free(sched_bf); + *map_id = NAN_INVALID_MAP_ID; + return NULL; +} + + +/** + * nan_sched_covered_by_avail_entry - Check if schedule is covered by the + * availability entry + * + * @nan: NAN module context from nan_init() + * @avail: Availability entry + * @sched_bf: A bitfield representing the schedule + * @map_id: Map ID corresponding to the schedule + * Returns true of schedule is covered by the entry; false otherwise + */ +bool nan_sched_covered_by_avail_entry(struct nan_data *nan, + struct nan_avail_entry *avail, + struct bitfield *sched_bf, u8 map_id) +{ + struct bitfield *avail_bf = NULL; + int ret; + + /* No schedule entries, avail_entry is good.. */ + if (!sched_bf) + return true; + + wpa_printf(MSG_DEBUG, + "NAN: Check if schedule covered by availability entry"); + + /* Schedule can only be covered by committed/conditional */ + if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED && + avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) + return false; + + if (avail->map_id != map_id) + return false; + + /* Convert the availability entry to bf */ + avail_bf = nan_tbm_to_bf(nan, &avail->tbm); + if (!avail_bf) + return false; + + bitfield_dump(avail_bf, "NAN: Availability entry bitmap"); + + ret = bitfield_is_subset(avail_bf, sched_bf); + wpa_printf(MSG_DEBUG, "NAN: Is schedule subset of entry=%d", ret); + + bitfield_free(avail_bf); + + return ret == 1; +} + + +static struct bitfield * +nan_sched_bf_from_avail_and_chan(struct nan_data *nan, + const struct dl_list *avail_entries, + u8 map_id, u8 op_class, u16 cbm) +{ + struct nan_avail_entry *avail; + struct bitfield *res_bf = NULL; + + dl_list_for_each(avail, avail_entries, struct nan_avail_entry, + list) { + struct bitfield *avail_bf; + + if (avail->map_id != map_id) + continue; + + /* Schedule can only be covered by committed/conditional */ + if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED && + avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) + continue; + + /* Now check channel, if it is given */ + if (op_class && cbm && + (avail->n_band_chan < 1 || + avail->band_chan_type != NAN_TYPE_CHANNEL || + avail->band_chan[0].u.chan.op_class != op_class || + !(avail->band_chan[0].u.chan.chan_bitmap & cbm))) + continue; + + /* Convert the availability entry to bitfield */ + avail_bf = nan_tbm_to_bf(nan, &avail->tbm); + if (!avail_bf) + goto fail; + + bitfield_dump(avail_bf, "NAN: Availability entry bitmap"); + if (!res_bf) { + res_bf = avail_bf; + } else { + struct bitfield *tmp_bf; + + tmp_bf = bitfield_union(res_bf, avail_bf); + bitfield_free(avail_bf); + + if (!tmp_bf) + goto fail; + + bitfield_free(res_bf); + res_bf = tmp_bf; + } + } + + return res_bf; + +fail: + bitfield_free(res_bf); + return NULL; +} + + +/** + * nan_sched_covered_by_avail_entries - Check if schedule is covered by the + * list of availability attributes + * + * @nan: NAN module context from nan_init() + * @avail_entries: A list of availability entries (see &struct nan_avail_entry) + * @sched: An array with 0 or more &struct nan_sched_entry entries + * @sched_len: Length of the &sched array + * Returns: true if schedule is covered by the entries; otherwise false. + */ +bool nan_sched_covered_by_avail_entries(struct nan_data *nan, + struct dl_list *avail_entries, + const u8 *sched, size_t sched_len) +{ + struct dl_list sched_entries; + struct bitfield *sched_bf, *avail_bf; + u8 map_id; + bool ret = false; + enum nan_reason reason; + + if (!sched || !sched_len) + return true; + + dl_list_init(&sched_entries); + if (nan_sched_entries_to_avail_entries(nan, + &sched_entries, + sched, sched_len)) + return false; + + sched_bf = nan_sched_to_bf(nan, &sched_entries, &map_id, &reason); + if (!sched_bf) { + nan_flush_avail_entries(&sched_entries); + return false; + } + + nan_flush_avail_entries(&sched_entries); + + avail_bf = nan_sched_bf_from_avail_and_chan(nan, avail_entries, + map_id, 0, 0); + if (avail_bf) + ret = bitfield_is_subset(avail_bf, sched_bf) ? true : false; + + wpa_printf(MSG_DEBUG, "NAN: Schedule is %sa subset of entries", + ret ? "" : "NOT "); + + bitfield_free(avail_bf); + bitfield_free(sched_bf); + + return ret; +} + + +/** + * nan_sched_bf_covered_by_avail_entries_and_chan - Check if schedule is covered + * by the list of availability attributes matching the channel configurations + * + * @nan: NAN module context from nan_init() + * @avail_entries: A list of availability entries. See &struct nan_avail_entry + * @sched_bf: the bitfield representing the schedule + * @map_id: Map ID associated with the schedule + * @op_class: Operating class to match against + * @cbm: Channel bitmap to match against + * Returns: true of schedule is covered by the entries; otherwise false + */ +bool nan_sched_bf_covered_by_avail_entries_and_chan( + struct nan_data *nan, const struct dl_list *avail_entries, + struct bitfield *sched_bf, u8 map_id, u8 op_class, u16 cbm) +{ + struct bitfield *avail_bf; + bool ret = false; + + /* + * Build a schedule bitfield from all the availability entries matching + * the map ID and channel configuration. + */ + avail_bf = nan_sched_bf_from_avail_and_chan(nan, avail_entries, + map_id, op_class, cbm); + + /* + * If there is such a schedule, verify that it is a superset of the + * given schedule. + */ + if (avail_bf && bitfield_is_subset(avail_bf, sched_bf)) + ret = true; + + wpa_printf(MSG_DEBUG, + "NAN: Is schedule covered by entries and chan=%u", ret); + + bitfield_free(avail_bf); + return ret; +} + + +static int nan_get_control_channel(struct nan_data *nan, u8 op_class, + u16 cbm, u16 pri_cbm) +{ + const struct oper_class_map *op = get_oper_class(NULL, op_class); + int freq = 0, idx; + u8 chan_id; + + if (!op || op_class > 130) + return -1; + + idx = ffs(cbm) - 1; + if (idx < 0) { + wpa_printf(MSG_DEBUG, + "NAN: No channel found in chan_bitmap 0x%04x for oper_class %u", + cbm, op_class); + return -1; + } + + chan_id = op_class_idx_to_chan(op, idx); + if (!chan_id) { + wpa_printf(MSG_DEBUG, + "NAN: No channel found for oper_class %u idx %u", + op_class, idx); + return -1; + } + + freq = ieee80211_chan_to_freq(NULL, op_class, chan_id); + + /* + * For operating classes with bandwidth < 80 MHz, the frequency is the + * control channel frequency. For operating classes with + * bandwidth >= 80 MHz, the frequency is the center frequency of the + * primary segment, so we need to derive the control channel frequency + * from the primary channel bitmap. + */ + if (op->bw == BW20 || op->bw == BW40 || + op->bw == BW40PLUS || op->bw == BW40MINUS) + return freq; + + if (!pri_cbm) { + wpa_printf(MSG_DEBUG, + "NAN: No primary channel bitmap provided for oper_class %u", + op_class); + return -1; + } + + idx = ffs(pri_cbm) - 1; + + if (op->bw == BW80 || op->bw == BW80P80) + return freq - 30 + idx * 20; + + if (op->bw == BW160) + return freq - 70 + idx * 20; + + return -1; +} + + +/** + * nan_avail_entries_to_bf - Convert availability entries that match the given + * channel configuration to a bitfield. + * + * @nan: NAN module context from nan_init() + * @avail_entries: A list of availability entries. See &struct nan_avail_entry + * @op_class: Operating class to match against + * @cbm: Channel bitmap to match against + * @pri_cbm: Primary channel bitmap to match against + * Returns: NULL on error or no match; otherwise returns a bitfield describing + * all the available slots. + */ +struct bitfield * nan_avail_entries_to_bf(struct nan_data *nan, + const struct dl_list *avail_entries, + u8 op_class, u16 cbm, u16 pri_cbm) +{ + struct nan_avail_entry *avail; + struct bitfield *res_bf = NULL; + + dl_list_for_each(avail, avail_entries, struct nan_avail_entry, + list) { + struct bitfield *avail_bf; + + /* Schedule can only be covered by committed/conditional. */ + if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED && + avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) + continue; + + /* + * Committed/conditional entries should have only a single + * channel entry. + */ + if (avail->n_band_chan != 1 || + avail->band_chan_type != NAN_TYPE_CHANNEL) + continue; + + /* + * Check that the availability entry channel matches. If it does + * not match, check if the channels are compatible, i.e., have + * the same control channel. + */ + if (avail->band_chan[0].u.chan.op_class != op_class || + avail->band_chan[0].u.chan.chan_bitmap != cbm || + avail->band_chan[0].u.chan.pri_chan_bitmap != pri_cbm) { + int freq1, freq2; + u16 chan_bitmap, pri_chan_bitmap; + + freq1 = nan_get_control_channel(nan, op_class, + cbm, pri_cbm); + + chan_bitmap = le_to_host16( + avail->band_chan[0].u.chan.chan_bitmap); + pri_chan_bitmap = le_to_host16( + avail->band_chan[0].u.chan.pri_chan_bitmap); + freq2 = nan_get_control_channel( + nan, avail->band_chan[0].u.chan.op_class, + chan_bitmap, pri_chan_bitmap); + + if (freq2 == -1 || freq1 != freq2) + continue; + + wpa_printf(MSG_DEBUG, + "NAN: Availability entry channel is compatible. Control channel freq=%d MHz", + freq1); + } + + /* Convert the availability entry to a bitfield */ + avail_bf = nan_tbm_to_bf(nan, &avail->tbm); + if (!avail_bf) + goto fail; + + if (!res_bf) { + res_bf = avail_bf; + } else { + struct bitfield *tmp_bf; + + tmp_bf = bitfield_union(res_bf, avail_bf); + if (!tmp_bf) + goto fail; + + bitfield_free(res_bf); + bitfield_free(avail_bf); + res_bf = tmp_bf; + } + } + + return res_bf; + +fail: + bitfield_free(res_bf); + return NULL; +} + + +/** + * nan_peer_dump_sched_to_buf - Dump peer schedule to a buffer + * @sched: Peer schedule + * @buf: Output buffer + * @buflen: The length of &buf in bytes + * + * Returns: The number of characters written to the buffer, or -1 on error, + * which indicates that the buffer was too small. + */ +int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched, + char *buf, size_t buflen) +{ + int i, j, ret; + char *pos = buf; + char *end = buf + buflen; + + for (i = 0; i < sched->n_maps; i++) { + struct nan_map *map = &sched->maps[i]; + + ret = wpa_scnprintf(pos, end - pos, + "MAP [%u]\n\tmap_id=%u\n\tn_chans=%u\n", + i, map->map_id, map->n_chans); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + for (j = 0; j < map->n_chans; j++) { + struct nan_map_chan *chan = &map->chans[j]; + + ret = wpa_scnprintf(pos, end - pos, + "\tchannel[%u]: committed=%u rx_nss=%u freq=%u bw=%u cfreq1=%u cfreq2=%u\n", + j, chan->committed, chan->rx_nss, + chan->chan.freq, + chan->chan.bandwidth, + chan->chan.center_freq1, + chan->chan.center_freq2); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_scnprintf(pos, end - pos, + "\t\tbitmap: period=%u duration=%u offset=%u ", + BIT(6 + chan->tbm.period), + BIT(4 + chan->tbm.duration), + 16 * chan->tbm.offset); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_scnprintf(pos, end - pos, "bitmap="); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_snprintf_hex(pos, end - pos, chan->tbm.bitmap, + chan->tbm.len); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_scnprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + } + + ret = wpa_scnprintf(pos, end - pos, + "\tndc: period=%u duration=%u offset=%u bitmap=", + BIT(6 + map->ndc.period), + BIT(4 + map->ndc.duration), + 16 * map->ndc.offset); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_snprintf_hex(pos, end - pos, map->ndc.bitmap, + map->ndc.len); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_scnprintf(pos, end - pos, + "\n\timmutable: period=%u duration=%u offset=%u bitmap=", + 1 << (6 + map->immutable.period), + 1 << (4 + map->immutable.duration), + 16 * map->immutable.offset); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_snprintf_hex(pos, end - pos, map->immutable.bitmap, + map->immutable.len); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + ret = wpa_scnprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + } + + ret = wpa_scnprintf(pos, end - pos, "max_idle_period=%u", + sched->max_idle_period); + + if (os_snprintf_error(end - pos, ret)) + goto err; + + pos += ret; + + return pos - buf; + +err: + wpa_printf(MSG_DEBUG, "NAN: Buffer too small to dump peer schedule"); + return -1; +} + + +/** + * nan_peer_dump_pot_avail_to_buf - Dump peer potential availability to a text + * buffer + * + * @pot_avail: Peer potential availability + * @buf: Output buffer + * @buflen: Length of &buf in bytes + * + * Returns: The number of characters written to the buffer, or -1 on error, + * which indicates that the buffer was too small. + */ +int nan_peer_dump_pot_avail_to_buf(struct nan_peer_potential_avail *pot_avail, + char *buf, size_t buflen) +{ + unsigned int i, j; + int ret; + char *pos = buf; + char *end = buf + buflen; + + for (i = 0; i < pot_avail->n_maps; i++) { + struct pot_entry *pot = &pot_avail->maps[i]; + + ret = wpa_scnprintf(pos, end - pos, + "entry[%u]: rx_nss=%u pref=%u util=%u\n", + i, pot->rx_nss, pot->preference, + pot->utilization); + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + + for (j = 0; j < pot->n_band_chan; j++) { + if (pot->is_band) { + ret = wpa_scnprintf(pos, end - pos, + "\tband[%u]: band_id=%u\n", + j, pot->entries[j].band_id); + } else { + ret = wpa_scnprintf( + pos, end - pos, + "\tchan[%u]: op_class=%u chan_bitmap=0x%04x\n", + j, pot->entries[j].op_class, + pot->entries[j].chan_bitmap); + } + if (os_snprintf_error(end - pos, ret)) + goto err; + pos += ret; + } + } + + return pos - buf; + +err: + wpa_printf(MSG_DEBUG, + "NAN: Buffer too small to dump peer potential availability"); + return -1; +} + + +/** + * nan_get_peer_ndc_freq - Get peer NDC frequency from schedule + * @nan: Pointer to NAN data struct + * @peer_sched: Pointer to peer schedule struct + * @map_idx: Index of the availability map to check + * Returns: Frequency of the peer channel that intersects with NDC, + * or -1 on failure or if no intersection found + * + * In case NDC bitmap spans across multiple channels, only one channel is + * returned (that corresponds to the first NDC bit). + */ +int nan_get_peer_ndc_freq(struct nan_data *nan, + const struct nan_peer_schedule *peer_sched, + u8 map_idx) +{ + struct bitfield *ndc_bf; + int i; + + if (map_idx >= peer_sched->n_maps) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid map index %u for peer schedule", + map_idx); + return -1; + } + + ndc_bf = nan_tbm_to_bf(nan, &peer_sched->maps[map_idx].ndc); + if (!ndc_bf) + return -1; + + for (i = 0; i < peer_sched->maps[map_idx].n_chans; i++) { + struct bitfield *peer_chan_map; + + /* + * Check all peer channel entries (committed or conditional). + * Potential entries are not part of the peer schedule. + */ + peer_chan_map = + nan_tbm_to_bf(nan, + &peer_sched->maps[map_idx].chans[i].tbm); + if (!peer_chan_map) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to convert peer channel TBM to bitfield"); + bitfield_free(ndc_bf); + return -1; + } + + if (bitfield_intersects(ndc_bf, peer_chan_map)) { + bitfield_free(ndc_bf); + bitfield_free(peer_chan_map); + return peer_sched->maps[map_idx].chans[i].chan.freq; + } + + bitfield_free(peer_chan_map); + } + + bitfield_free(ndc_bf); + return -1; +} + + +/** + * nan_get_chan_entry - Get channel entry for a given NAN scheduled channel + * @nan: NAN module context from nan_init() + * @chan: NAN scheduled channel + * @chan_entry: On successful return holds the channel entry. + * Returns: 0 on success; otherwise -1 + */ +int nan_get_chan_entry(struct nan_data *nan, const struct nan_sched_chan *chan, + struct nan_chan_entry *chan_entry) +{ + u8 op_class; + u16 chan_bm, pri_chan_bm; + int ret; + + if (!chan || !chan_entry) + return -1; + + ret = nan_get_chan_bm(nan, chan, &op_class, &chan_bm, &pri_chan_bm); + if (ret) + return ret; + + os_memset(chan_entry, 0, sizeof(*chan_entry)); + chan_entry->op_class = op_class; + chan_entry->chan_bitmap = host_to_le16(chan_bm); + chan_entry->pri_chan_bitmap = pri_chan_bm & 0xff; + + return 0; +} + + +/** + * nan_convert_chan_sched_to_bf - Convert channel schedule to bitfield + * and get the channel information. + * + * @nan: NAN module context from nan_init() + * @chan: Channel schedule to convert + * @avail_bf: On successful return holds the availability bitmap of the given + * channel schedule + * @map_id: On successful return holds the map ID for the schedule + * @op_class: On successful return holds the operating class for the schedule + * with the peer + * @cbm: On successful return holds the channel bitmap for the operating class + * @pcbm: On successful return holds the primary channel bitmap for the + * channel in case of bandwidth greater than 40 MHz + * Returns: 0 on success; -1 on failure + */ +int nan_convert_chan_sched_to_bf(struct nan_data *nan, + const struct nan_chan_schedule *chan, + struct bitfield **avail_bf, u8 *map_id, + u8 *op_class, u16 *cbm, u16 *pcbm) +{ + struct bitfield *committed_bf, *conditional_bf; + int ret; + + *op_class = 0; + *cbm = 0; + *pcbm = 0; + *map_id = chan->map_id; + + ret = nan_get_chan_bm(nan, &chan->chan, op_class, cbm, pcbm); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to convert channel info"); + return -1; + } + + committed_bf = nan_tbm_to_bf(nan, &chan->committed); + if (!committed_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to build committed bitfield"); + return -1; + } + + conditional_bf = nan_tbm_to_bf(nan, &chan->conditional); + if (!conditional_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to build conditional bitfield"); + bitfield_free(committed_bf); + return -1; + } + + *avail_bf = bitfield_union(committed_bf, conditional_bf); + bitfield_free(committed_bf); + bitfield_free(conditional_bf); + + if (!*avail_bf) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to unify committed and conditional bitfields"); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: NDL: map_id=%u, op_class=%u, cbm=0x%x", + *map_id, *op_class, *cbm); + return 0; +} + + +/** + * nan_peer_schedule_intersection - Get local and peer schedules intersection + * @nan: NAN module context from nan_init() + * @peer: The peer with whom to intersect the schedule + * @sched: Local device schedule + * Returns: A bitfield representing the intersection of schedules, or NULL if + * no intersection + * + * The function checks if the local device schedule intersects with the peer + * device schedule and returns a bitfield representing the intersection, or + * NULL if no intersection. + */ +struct bitfield * nan_peer_schedule_intersection( + struct nan_data *nan, const struct nan_peer *peer, + const struct nan_schedule *sched) +{ + size_t i; + struct bitfield *common_bf = NULL; + bool intersects = false; + + /* + * Iterate over all the channels included in the local schedule. For + * each channel convert the committed and conditional slots to a + * bitfield object and extract the operating class and channel bitmap. + * + * Using the operating class and channel bitmap find the peer + * availability on that channel and check if it intersect with the + * local one. + */ + wpa_printf(MSG_DEBUG, "NAN: n_chans=%u, ndc_map_id=%u", + sched->n_chans, sched->ndc_map_id); + + for (i = 0; i < sched->n_chans; i++) { + struct bitfield *own_chan_bf = NULL, *peer_chan_bf = NULL; + u16 cbm, pri_cbm; + u8 map_id, op_class; + int ret; + + /* Convert the schedule for the current channel to bitfield */ + ret = nan_convert_chan_sched_to_bf(nan, &sched->chans[i], + &own_chan_bf, &map_id, + &op_class, &cbm, &pri_cbm); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: NDL: Failed to convert chan sched to bitfield"); + return NULL; + } + + /* Get the peer availability for the current channel */ + peer_chan_bf = + nan_avail_entries_to_bf(nan, + &peer->info.avail_entries, + op_class, cbm, pri_cbm); + if (!peer_chan_bf) { + bitfield_free(own_chan_bf); + continue; + } + + intersects |= bitfield_intersects(own_chan_bf, peer_chan_bf); + + ret = bitfield_intersect_in_place(own_chan_bf, peer_chan_bf); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to intersect own and peer chan bitfields"); + bitfield_free(own_chan_bf); + bitfield_free(peer_chan_bf); + bitfield_free(common_bf); + return NULL; + } + + bitfield_free(peer_chan_bf); + + if (common_bf) { + ret = bitfield_union_in_place(common_bf, own_chan_bf); + if (ret) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to unify own chan bitfields"); + + bitfield_free(own_chan_bf); + bitfield_free(common_bf); + return NULL; + } + } else { + common_bf = bitfield_dup(own_chan_bf); + if (!common_bf) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to dup own chan bitfield"); + + bitfield_free(own_chan_bf); + bitfield_free(common_bf); + return NULL; + } + } + + bitfield_free(own_chan_bf); + } + + if (!intersects) { + bitfield_free(common_bf); + return NULL; + } + + return common_bf; +} + + +void nan_add_kde_hdr(struct wpabuf *buf, u32 kde, size_t data_len) +{ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, RSN_SELECTOR_LEN + data_len); + RSN_SELECTOR_PUT(wpabuf_put(buf, RSN_SELECTOR_LEN), kde); +} diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 8e0fc35dfbf0..0ec6c6870e6f 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -13,9 +13,13 @@ #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_common.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/crypto.h" +#include "pasn/pasn_common.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" @@ -242,8 +246,16 @@ void p2p_go_neg_failed(struct p2p_data *p2p, int status) peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; +#ifdef CONFIG_PASN + if (peer->p2p2 && peer->pasn) + wpa_pasn_reset(peer->pasn); + os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password)); + os_memset(p2p->peer_sae_password, 0, sizeof(p2p->peer_sae_password)); +#endif /* CONFIG_PASN */ + os_memset(&res, 0, sizeof(res)); res.status = status; + res.p2p2 = peer->p2p2; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); @@ -954,6 +966,17 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) dev->info.wps_vendor_ext[i] = NULL; } + os_free(dev->bootstrap_params); + + wpabuf_free(dev->action_frame_wrapper); + +#ifdef CONFIG_PASN + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + pasn_data_deinit(dev->pasn); + } +#endif /* CONFIG_PASN */ + wpabuf_free(dev->info.wfd_subelems); wpabuf_free(dev->info.vendor_elems); wpabuf_free(dev->go_neg_conf); @@ -1405,7 +1428,17 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", force_freq, pref_freq, go); - if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { + + if (p2p->cfg->is_p2p_dfs_chan && + p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, freq, 0, 0) && + p2p->dfs_ap_connected) { + if (ieee80211_chaninfo_to_channel( + freq, p2p->sta_connected_chan_width, 0, + &op_class, &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); + return -1; + } + } else if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); return -1; } @@ -1553,6 +1586,20 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, } else { p2p_prepare_channel_best(p2p); } + + if (p2p->cfg->is_p2p_dfs_chan && + p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0, + p2p->op_reg_class, p2p->op_channel)) { + struct p2p_channels p2p_chanlist; + + p2p_dfs_channel_filter(p2p, &p2p->channels, &p2p_chanlist, go); + p2p_channels_dump(p2p, + "Filtered channel list with allowed DFS channels", + &p2p_chanlist); + p2p_copy_channels(&p2p->channels, &p2p_chanlist, + p2p->allow_6ghz); + } + p2p_channels_dump(p2p, "prepared channels", &p2p->channels); if (go) p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); @@ -1599,7 +1646,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id, + bool p2p2, u16 bootstrap, const char *password) { struct p2p_device *dev; @@ -1683,6 +1731,10 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; + dev->p2p2 = p2p2; + dev->req_bootstrap_method = bootstrap; + if (password && os_strlen(password) < sizeof(dev->password)) + os_strlcpy(dev->password, password, sizeof(dev->password)); dev->status = P2P_SC_SUCCESS; if (p2p->p2p_scan_running) { @@ -1701,7 +1753,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - unsigned int pref_freq, u16 oob_pw_id) + unsigned int pref_freq, u16 oob_pw_id, u16 bootstrap, + const char *password) { struct p2p_device *dev; @@ -1735,6 +1788,10 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, dev->flags &= ~P2P_DEV_USER_REJECTED; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; + dev->req_bootstrap_method = bootstrap; + + if (password && os_strlen(password) < sizeof(dev->password)) + os_strlcpy(dev->password, password, sizeof(dev->password)); p2p_set_dev_persistent(dev, persistent_group); p2p->go_intent = go_intent; os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); @@ -1825,12 +1882,30 @@ int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) } p2p->ssid_set = 0; + params->cipher = WPA_CIPHER_CCMP; + if (p2p->cfg->pairing_config.pasn_type & 0xc) + params->cipher |= WPA_CIPHER_GCMP_256; + p2p_random(params->passphrase, p2p->cfg->passphrase_len); params->passphrase[p2p->cfg->passphrase_len] = '\0'; + + if (params->p2p2) { + os_strlcpy(p2p->dev_sae_password, params->passphrase, + sizeof(p2p->dev_sae_password)); + os_strlcpy(params->sae_password, p2p->dev_sae_password, + sizeof(params->sae_password)); + } + return 0; } +void p2p_set_go_role(struct p2p_data *p2p, bool val) +{ + p2p->go_role = val; +} + + void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) { struct p2p_go_neg_results res; @@ -1892,8 +1967,58 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) wpabuf_free(peer->go_neg_conf); peer->go_neg_conf = NULL; - p2p_set_state(p2p, P2P_PROVISIONING); - p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +#ifdef CONFIG_PASN + if (peer->p2p2 && peer->pasn) { + res.p2p2 = peer->p2p2; + res.akmp = peer->pasn->akmp; + res.cipher = peer->pasn->cipher; + + if (res.akmp == WPA_KEY_MGMT_PASN) { + if (go) { + os_strlcpy(res.sae_password, + p2p->dev_sae_password, + sizeof(res.sae_password)); + } else { + if (!os_strlen(p2p->peer_sae_password)) { + p2p_dbg(p2p, "No password from peer GO for P2P2 group formation"); + return; + } + os_strlcpy(res.sae_password, + p2p->peer_sae_password, + sizeof(res.sae_password)); + } + } else if (res.akmp == WPA_KEY_MGMT_SAE) { + if (peer->role == P2P_ROLE_PAIRING_INITIATOR) { + pasn_initiator_pmksa_cache_get( + peer->pasn->pmksa, + peer->pasn->peer_addr, + res.pmkid, res.pmk, &res.pmk_len); + } else { + pasn_responder_pmksa_cache_get( + peer->pasn->pmksa, + peer->pasn->peer_addr, + res.pmkid, res.pmk, &res.pmk_len); + } + } + + os_memset(p2p->dev_sae_password, 0, + sizeof(p2p->dev_sae_password)); + os_memset(p2p->peer_sae_password, 0, + sizeof(p2p->peer_sae_password)); + wpa_pasn_reset(peer->pasn); + } +#endif /* CONFIG_PASN */ + + if (p2p->go_role && peer->p2p2) { + p2p_set_state(p2p, P2P_IDLE); + p2p->cfg->set_go_security_config(p2p->cfg->cb_ctx, &res); + p2p->go_role = false; + } else { + p2p_set_state(p2p, P2P_PROVISIONING); + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); + } + + forced_memzero(&res, sizeof(res)); } @@ -1908,26 +2033,25 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, switch (data[0]) { case P2P_GO_NEG_REQ: - p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); + p2p_handle_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_RESP: - p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); + p2p_handle_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_CONF: - p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); + p2p_handle_go_neg_conf(p2p, sa, data + 1, len - 1, false); break; case P2P_INVITATION_REQ: - p2p_process_invitation_req(p2p, sa, data + 1, len - 1, - rx_freq); + p2p_handle_invitation_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_INVITATION_RESP: p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: - p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + p2p_handle_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_PROV_DISC_RESP: - p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); + p2p_handle_prov_disc_resp(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_DEV_DISC_REQ: p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); @@ -2968,6 +3092,77 @@ bool is_p2p_6ghz_disabled(struct p2p_data *p2p) } +void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr) +{ + if (p2p && addr) + os_memcpy(p2p->cfg->dev_addr, addr, ETH_ALEN); +} + + +void p2p_update_dfs_ap_info(struct p2p_data *p2p, int freq, + enum chan_width ap_ch_width, bool disconnect_evt) +{ + if (disconnect_evt) { + p2p->dfs_ap_connected = false; + p2p->sta_connected_freq = 0; + p2p->sta_connected_chan_width = CHAN_WIDTH_UNKNOWN; + return; + } + + p2p->sta_connected_freq = freq; + p2p->sta_connected_chan_width = ap_ch_width; + p2p->dfs_ap_connected = true; +} + + +static void p2p_pairing_info_deinit(struct p2p_data *p2p) +{ +#ifdef CONFIG_PASN + pasn_initiator_pmksa_cache_deinit(p2p->initiator_pmksa); + pasn_responder_pmksa_cache_deinit(p2p->responder_pmksa); +#endif /* CONFIG_PASN */ + os_free(p2p->pairing_info); +} + + +static int p2p_pairing_info_init(struct p2p_data *p2p) +{ + struct p2p_pairing_info *pairing_info; + + if (p2p->cfg->pairing_config.dik_len > DEVICE_IDENTITY_KEY_MAX_LEN) + return -1; + + pairing_info = os_zalloc(sizeof(struct p2p_pairing_info)); + if (!pairing_info) + return -1; + + pairing_info->enable_pairing_setup = + p2p->cfg->pairing_config.enable_pairing_setup; + pairing_info->enable_pairing_cache = + p2p->cfg->pairing_config.enable_pairing_cache; + pairing_info->supported_bootstrap = + p2p->cfg->pairing_config.bootstrap_methods; + + pairing_info->dev_ik.cipher_version = + p2p->cfg->pairing_config.dik_cipher; + pairing_info->dev_ik.dik_len = p2p->cfg->pairing_config.dik_len; + os_memcpy(pairing_info->dev_ik.dik_data, + p2p->cfg->pairing_config.dik_data, + p2p->cfg->pairing_config.dik_len); + pairing_info->dev_ik.expiration = 24; /* hours */ + + p2p_pairing_info_deinit(p2p); + + p2p->pairing_info = pairing_info; +#ifdef CONFIG_PASN + p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + p2p->responder_pmksa = pasn_responder_pmksa_cache_init(); +#endif /* CONFIG_PASN */ + + return 0; +} + + struct p2p_data * p2p_init(const struct p2p_config *cfg) { struct p2p_data *p2p; @@ -3023,6 +3218,10 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->go_timeout = 100; p2p->client_timeout = 20; p2p->num_p2p_sd_queries = 0; + /* Default comeback after one second */ + if (!p2p->cfg->comeback_after) + p2p->cfg->comeback_after = 977; /* TUs */ + p2p_pairing_info_init(p2p); p2p_dbg(p2p, "initialized"); p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); @@ -3066,6 +3265,7 @@ void p2p_deinit(struct p2p_data *p2p) p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); p2p_service_flush_asp(p2p); + p2p_pairing_info_deinit(p2p); os_free(p2p); } @@ -3399,7 +3599,7 @@ static void p2p_retry_pd(struct p2p_data *p2p) if (!ether_addr_equal(p2p->pending_pd_devaddr, dev->info.p2p_device_addr)) continue; - if (!dev->req_config_methods) + if (!dev->req_config_methods && !dev->req_bootstrap_method) continue; p2p_dbg(p2p, "Send pending Provision Discovery Request to " @@ -3847,7 +4047,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_invitation_req_cb(p2p, success); break; case P2P_PENDING_INVITATION_RESPONSE: - p2p_invitation_resp_cb(p2p, success); + p2p_invitation_resp_cb(p2p, dst, success); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); @@ -3965,6 +4165,31 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) } +void p2p_listen_failed(struct p2p_data *p2p, unsigned int freq) +{ + if (freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, + "Unexpected listen failed callback for freq=%u (pending_listen_freq=%u)", + freq, p2p->pending_listen_freq); + return; + } + + p2p_dbg(p2p, "Listen failed on freq=%u", freq); + p2p->pending_listen_freq = 0; + p2p->pending_listen_wait_drv = false; + + if (p2p->state == P2P_SEARCH || + p2p->state == P2P_CONNECT_LISTEN || + p2p->state == P2P_WAIT_PEER_CONNECT || + p2p->state == P2P_WAIT_PEER_IDLE || + p2p->state == P2P_INVITE_LISTEN) { + p2p_dbg(p2p, + "Schedule timeout after listen failure to continue state machine"); + p2p_set_timeout(p2p, 0, 100000); + } +} + + static void p2p_timeout_connect(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); @@ -4145,9 +4370,11 @@ static void p2p_timeout_invite_listen(struct p2p_data *p2p) p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( - p2p->cfg->cb_ctx, -1, NULL, NULL, + p2p->cfg->cb_ctx, -1, NULL, 0, NULL, + NULL, p2p->invite_peer->info.p2p_device_addr, - 0, 0); + 0, 0, NULL, NULL, 0, + p2p->invite_go_dev_addr); } p2p_set_state(p2p, P2P_IDLE); } @@ -4787,6 +5014,70 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) } +void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods) +{ + p2p_dbg(p2p, "Bootstraping methods: 0x%x", bootstrap_methods); + p2p->cfg->pairing_config.bootstrap_methods = bootstrap_methods; + if (p2p->pairing_info) + p2p->pairing_info->supported_bootstrap = bootstrap_methods; +} + + +void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type) +{ + p2p_dbg(p2p, "PASN type: 0x%x", pasn_type); + p2p->cfg->pairing_config.pasn_type = pasn_type; +} + + +void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after) +{ + p2p_dbg(p2p, "Comeback after: %d", comeback_after); + p2p->cfg->comeback_after = comeback_after; +} + + +void p2p_set_reg_info(struct p2p_data *p2p, u8 val) +{ + p2p->cfg->reg_info = val; +} + + +void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val) +{ + p2p_dbg(p2p, "TWT-based P2P Power Mgmt: %s", + val ? "Enabled" : "Disabled"); + if (val) + p2p->cfg->twt_power_mgmt = true; + else + p2p->cfg->twt_power_mgmt = false; +} + + +void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val) +{ + p2p->cfg->chan_switch_req_enable = val; +} + + +void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq) +{ + u8 op_class, channel; + + if (freq == -1) { + p2p->cfg->inv_op_class = 0; + p2p->cfg->inv_op_channel = 0; + return; + } + + if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) + return; + + p2p->cfg->inv_op_class = op_class; + p2p->cfg->inv_op_channel = channel; +} + + int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, u8 *op_channel, struct wpa_freq_range_list *avoid_list, @@ -4917,8 +5208,13 @@ int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { struct p2p_device *dev = p2p_get_device(p2p, dev_addr); - if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) + + if (!dev || is_zero_ether_addr(dev->interface_addr)) { + p2p_dbg(p2p, + "P2P: Failed to get interface address from device addr " + MACSTR, MAC2STR(dev_addr)); return -1; + } os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); return 0; } @@ -4928,13 +5224,40 @@ int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); - if (dev == NULL) + + if (!dev) { + p2p_dbg(p2p, + "P2P: Failed to get device address from interface address " + MACSTR, MAC2STR(iface_addr)); return -1; + } os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); return 0; } +int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr, + const u8 **dik_data, size_t *dik_len, u8 *cipher) +{ + if (!p2p || !p2p->peer_dik_len) { + wpa_printf(MSG_DEBUG, + "P2P2: Failed to get device identity key for " + MACSTR, MAC2STR(dev_addr)); + return -1; + } + + *dik_data = p2p->peer_dik_data; + *dik_len = p2p->peer_dik_len; + *cipher = p2p->dik_cipher_version; + + /* Reset DIK length to invalidate DIK for successive iteration of a new + * peer. */ + p2p->peer_dik_len = 0; + + return 0; +} + + void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); @@ -5688,3 +6011,1418 @@ void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value) { p2p->allow_6ghz = value; } + + +static int p2p_derive_nonce_tag(struct p2p_data *p2p) +{ + u8 dira_nonce[DEVICE_IDENTITY_NONCE_LEN]; + u8 dira_tag[DEVICE_MAX_HASH_LEN]; + u8 data[DIR_STR_LEN + DEVICE_IDENTITY_NONCE_LEN + ETH_ALEN]; + struct p2p_id_key *dev_ik; + + dev_ik = &p2p->pairing_info->dev_ik; + + if (dev_ik->cipher_version != DIRA_CIPHER_VERSION_128) { + wpa_printf(MSG_INFO, + "P2P: Unsupported DIRA Cipher version = %d", + dev_ik->cipher_version); + return -1; + } + + if (dev_ik->dik_len != DEVICE_IDENTITY_KEY_LEN) { + wpa_printf(MSG_INFO, "P2P: Invalid DIK length = %zu", + dev_ik->dik_len); + return -1; + } + + os_memset(data, 0, sizeof(data)); + + if (os_get_random(dira_nonce, DEVICE_IDENTITY_NONCE_LEN) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to generate DIRA nonce"); + return -1; + } + + /* Tag = Truncate-64(HMAC-SHA-256(DevIK, + * "DIR" || P2P Device Address || Nonce)) + */ + os_memcpy(data, "DIR", DIR_STR_LEN); + os_memcpy(&data[DIR_STR_LEN], p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(&data[DIR_STR_LEN + ETH_ALEN], dira_nonce, + DEVICE_IDENTITY_NONCE_LEN); + + if (hmac_sha256(dev_ik->dik_data, dev_ik->dik_len, data, sizeof(data), + dira_tag) < 0) { + wpa_printf(MSG_ERROR, "P2P: Could not derive DIRA tag"); + return -1; + } + + dev_ik->dira_nonce_len = DEVICE_IDENTITY_NONCE_LEN; + os_memcpy(dev_ik->dira_nonce, dira_nonce, DEVICE_IDENTITY_NONCE_LEN); + dev_ik->dira_tag_len = DEVICE_IDENTITY_TAG_LEN; + os_memcpy(dev_ik->dira_tag, dira_tag, DEVICE_IDENTITY_TAG_LEN); + + wpa_hexdump_key(MSG_DEBUG, "P2P: DIK", dev_ik->dik_data, + dev_ik->dik_len); + wpa_hexdump_key(MSG_DEBUG, "P2P: DIRA-NONCE", dev_ik->dira_nonce, + dev_ik->dira_nonce_len); + wpa_hexdump_key(MSG_DEBUG, "P2P: DIRA-TAG", dev_ik->dira_tag, + dev_ik->dira_tag_len); + return 0; +} + + +static int p2p_validate_dira(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *dira, u16 dira_len) +{ + if (dira_len < 1 || dira[0] != DIRA_CIPHER_VERSION_128) { + p2p_dbg(p2p, "Unsupported DIRA cipher version %d", + dira[0]); + return 0; + } + + if (dira_len < 1 + DEVICE_IDENTITY_NONCE_LEN + DEVICE_IDENTITY_TAG_LEN) + { + p2p_dbg(p2p, "Truncated DIRA (length %u)", dira_len); + return 0; + } + + if (p2p->cfg->validate_dira) { + const u8 *nonce = &dira[1]; + const u8 *tag = &dira[1 + DEVICE_IDENTITY_NONCE_LEN]; + + return p2p->cfg->validate_dira(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + nonce, tag); + } + + return 0; +} + + +void p2p_usd_service_hash(struct p2p_data *p2p, const char *service_name) +{ + u8 buf[P2PS_HASH_LEN]; + + p2p->usd_service = false; + + if (!service_name) + return; + + if (!p2ps_gen_hash(p2p, service_name, buf)) + return; + p2p_dbg(p2p, "USD service %s hash " MACSTR, + service_name, MAC2STR(buf)); + p2p->usd_service = true; + os_memcpy(&p2p->p2p_service_hash, buf, P2PS_HASH_LEN); +} + + +struct wpabuf * p2p_usd_elems(struct p2p_data *p2p) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + + buf = wpabuf_alloc(1000); + if (!buf) + return NULL; + + len = p2p_buf_add_ie_hdr(buf); + + /* P2P Capability attribute */ + group_capab = 0; + if (p2p->num_groups) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + + /* P2P Device Info attribute */ + p2p_buf_add_device_info(buf, p2p, NULL); + + p2p_buf_update_ie_hdr(buf, len); + + len = p2p_buf_add_p2p2_ie_hdr(buf); + + /* P2P Capability Extension attribute */ + p2p_buf_add_pcea(buf, p2p); + + /* P2P Pairing Bootstrapping Method attribute */ + p2p_buf_add_pbma(buf, p2p->cfg->pairing_config.bootstrap_methods, NULL, + 0, 0); + + /* P2P Device Identity Resolution attribute */ + if (p2p->pairing_info && + p2p->cfg->pairing_config.pairing_capable && + p2p->cfg->pairing_config.enable_pairing_cache && + p2p_derive_nonce_tag(p2p) == 0) + p2p_buf_add_dira(buf, p2p); + + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len, + const u8 *peer_addr, unsigned int freq) +{ + struct p2p_device *dev; + struct p2p_message msg; + const u8 *p2p_dev_addr; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); + p2p_parse_free(&msg); + return; + } + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else + p2p_dev_addr = peer_addr; + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (!dev) { + p2p_parse_free(&msg); + p2p_dbg(p2p, "Failed to add a peer P2P Device"); + return; + } + + if (msg.device_name[0]) + os_memcpy(dev->info.device_name, msg.device_name, + sizeof(dev->info.device_name)); + + dev->p2p2 = true; + /* Reset info from old IEs */ + dev->info.reg_info = 0; + os_memset(&dev->info.pairing_config, 0, + sizeof(struct p2p_pairing_config)); + + os_get_reltime(&dev->last_seen); + dev->listen_freq = freq; + dev->oper_freq = freq; + + if (msg.capability) { + /* + * P2P Client Discoverability bit is reserved in all frames + * that use this function, so do not change its value here. + */ + dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= msg.capability[0] & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.group_capab = msg.capability[1]; + } + + if (msg.pcea_info && msg.pcea_info_len >= 2) + p2p_process_pcea(p2p, &msg, dev); + + if (msg.pbma_info && msg.pbma_info_len == 2) + dev->info.pairing_config.bootstrap_methods = + WPA_GET_LE16(msg.pbma_info); + + if (!ether_addr_equal(peer_addr, p2p_dev_addr)) + os_memcpy(dev->interface_addr, peer_addr, ETH_ALEN); + + if (msg.dira && msg.dira_len) { + dev->info.nonce_tag_valid = false; + dev->info.dik_id = p2p_validate_dira(p2p, dev, msg.dira, + msg.dira_len); + if (dev->info.dik_id) { + os_memcpy(dev->info.nonce, &msg.dira[1], + DEVICE_IDENTITY_NONCE_LEN); + os_memcpy(dev->info.tag, + &msg.dira[1 + DEVICE_IDENTITY_NONCE_LEN], + DEVICE_IDENTITY_TAG_LEN); + dev->info.pairing_config.dik_cipher = msg.dira[0]; + dev->info.nonce_tag_valid = true; + } + } + + p2p_dbg(p2p, "Updated device entry based on USD frame: " MACSTR + " dev_capab=0x%x group_capab=0x%x listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, + dev->info.group_capab, dev->listen_freq); + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + + p2p_parse_free(&msg); +} + + +int p2p_get_dik_id(struct p2p_data *p2p, const u8 *peer) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer); + if (!dev) + return 0; + + return dev->info.dik_id; +} + + +#ifdef CONFIG_PASN + +int p2p_config_sae_password(struct p2p_data *p2p, const char *pw) +{ + os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password)); + if (os_strlen(pw) >= sizeof(p2p->dev_sae_password)) + return -1; + + os_strlcpy(p2p->dev_sae_password, pw, sizeof(p2p->dev_sae_password)); + return 0; +} + + +static int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p, + struct wpabuf *extra_ies, + const struct wpabuf *frame, bool add_dira) +{ + struct wpabuf *buf, *buf2; + size_t len; + + len = 100; + if (frame) + len += wpabuf_len(frame); + buf = wpabuf_alloc(len); + if (!buf) + return -1; + + /* P2P Capability Extension attribute */ + p2p_buf_add_pcea(buf, p2p); + + if (add_dira) { + /* Device Identity Resolution attribute */ + p2p_buf_add_dira(buf, p2p); + } + + if (frame) { + p2p_dbg(p2p, "Add Action frame wrapper for PASN"); + wpabuf_put_u8(buf, P2P_ATTR_ACTION_FRAME_WRAPPER); + wpabuf_put_le16(buf, wpabuf_len(frame)); + wpabuf_put_buf(buf, frame); + } + + buf2 = p2p_encaps_ie(buf, P2P2_IE_VENDOR_TYPE); + wpabuf_free(buf); + + if (wpabuf_tailroom(extra_ies) < wpabuf_len(buf2)) { + p2p_err(p2p, "Not enough room for P2P2 IE in PASN extra IEs"); + wpabuf_free(buf2); + return -1; + } + wpabuf_put_buf(extra_ies, buf2); + wpabuf_free(buf2); + + return 0; +} + + +static struct wpabuf * p2p_pasn_service_hash(struct p2p_data *p2p, + struct wpabuf *extra_ies) +{ + struct wpabuf *buf; + u8 *ie_len = NULL; + + if (!p2p->usd_service) + return extra_ies; + + p2p_dbg(p2p, "Add P2P2 USD service hash in extra IE"); + buf = wpabuf_alloc(100); + if (!buf) { + wpabuf_free(extra_ies); + return NULL; + } + + ie_len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_usd_service_hash(buf, p2p); + p2p_buf_update_ie_hdr(buf, ie_len); + + return wpabuf_concat(buf, extra_ies); +} + + +static struct wpabuf * p2p_pairing_generate_rsnxe(struct p2p_data *p2p, + int akmp) +{ + u32 capab; + size_t flen = 0; + struct wpabuf *buf; + + capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + + if (wpa_key_mgmt_sae(akmp)) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + + while (capab >> flen * 8) + flen++; + + buf = wpabuf_alloc(2 + flen); + if (!buf) + return NULL; + + if (wpabuf_tailroom(buf) < 2 + flen) { + p2p_dbg(p2p, "wpabuf tail room too small"); + wpabuf_free(buf); + return NULL; + } + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + p2p_dbg(p2p, "RSNXE capabilities: %04x", capab); + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, flen); + while (flen--) { + wpabuf_put_u8(buf, (capab & 0xff)); + capab = capab >> 8; + } + return buf; +} + + +/* SSID used for deriving SAE pt for pairing */ +#define P2P_PAIRING_SSID "516F9A020000" + +static void p2p_pairing_set_password(struct pasn_data *pasn, u8 pasn_type, + const char *passphrase) +{ + int pasn_groups[4] = { 0 }; + size_t len; + + if (!passphrase) + return; + + len = os_strlen(passphrase); + + if (pasn_type & 0xc && pasn_type & 0x3) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & 0xc) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) P2P_PAIRING_SSID, + os_strlen(P2P_PAIRING_SSID), + (const u8 *) passphrase, len, NULL, 0); +} + + +void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *addr, int freq, bool verify, bool derive_kek) +{ + struct pasn_data *pasn; + struct wpabuf *rsnxe; + + if (!p2p || !dev) + return; + + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + } else { + dev->pasn = pasn_data_init(); + if (!dev->pasn) + return; + } + + pasn = dev->pasn; + + os_memcpy(pasn->own_addr, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(pasn->peer_addr, addr, ETH_ALEN); + + os_memcpy(pasn->bssid, dev->role == P2P_ROLE_PAIRING_INITIATOR ? + pasn->peer_addr : pasn->own_addr, ETH_ALEN); + + pasn->noauth = 1; + + if ((p2p->cfg->pairing_config.pasn_type & 0xc) && + (dev->info.pairing_config.pasn_type & 0xc)) { + pasn->group = 20; + pasn->cipher = WPA_CIPHER_GCMP_256; + pasn->kek_len = 32; + pasn->derive_kek = true; + } else { + pasn->group = 19; + pasn->cipher = WPA_CIPHER_CCMP; + pasn->kek_len = 16; + pasn->derive_kek = true; + } + + if (!derive_kek) { + pasn->derive_kek = false; + pasn->kek_len = 0; + } + + if (dev->password[0]) { + pasn->akmp = WPA_KEY_MGMT_SAE; + p2p_pairing_set_password(pasn, + p2p->cfg->pairing_config.pasn_type, + dev->password); + } else if (verify) { + pasn->akmp = WPA_KEY_MGMT_SAE; + if (p2p->cfg->set_pmksa) + p2p->cfg->set_pmksa(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + dev->info.dik_id); + } else { + pasn->akmp = WPA_KEY_MGMT_PASN; + } + + pasn->rsn_pairwise = pasn->cipher; + pasn->wpa_key_mgmt = pasn->akmp; + + rsnxe = p2p_pairing_generate_rsnxe(p2p, pasn->akmp); + if (rsnxe) { + os_free(pasn->rsnxe_ie); + pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe), + wpabuf_len(rsnxe)); + if (!pasn->rsnxe_ie) { + wpabuf_free(rsnxe); + return; + } + wpabuf_free(rsnxe); + } + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR) + pasn->pmksa = p2p->initiator_pmksa; + else + pasn->pmksa = p2p->responder_pmksa; + + pasn->cb_ctx = p2p->cfg->cb_ctx; + pasn->send_mgmt = p2p->cfg->pasn_send_mgmt; + pasn->prepare_data_element = p2p->cfg->prepare_data_element; + pasn->parse_data_element = p2p->cfg->parse_data_element; + pasn->validate_custom_pmkid = p2p->cfg->pasn_validate_pmkid; + + pasn->freq = freq; +} + + +int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr) +{ + int freq; + struct p2p_device *dev; + + if (!peer_addr) { + p2p_dbg(p2p, "Peer address NULL"); + return -1; + } + + dev = p2p_get_device(p2p, peer_addr); + if (!dev) { + p2p_dbg(p2p, "Peer not known"); + return -1; + } + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) + freq = dev->oob_go_neg_freq; + if (freq <= 0) { + p2p_dbg(p2p, "No listen/operating frequency known for the peer " + MACSTR, MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + return freq; +} + + +int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr, + int freq, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + unsigned int pref_freq) +{ + struct pasn_data *pasn; + struct p2p_device *dev; + struct wpabuf *extra_ies, *req; + int ret = 0; + u8 *pasn_extra_ies = NULL; + u8 pmkid[PMKID_LEN]; + + if (!peer_addr) { + p2p_dbg(p2p, "Peer address NULL"); + return -1; + } + + dev = p2p_get_device(p2p, peer_addr); + if (!dev) { + p2p_dbg(p2p, "Peer not known"); + return -1; + } + + if (p2p_invite(p2p, peer_addr, role, bssid, ssid, ssid_len, force_freq, + go_dev_addr, 1, pref_freq, -1, 1)) { + p2p_dbg(p2p, "p2p_invite() failed"); + return -1; + } + + dev->role = P2P_ROLE_PAIRING_INITIATOR; + p2p_pasn_initialize(p2p, dev, peer_addr, freq, true, true); + pasn = dev->pasn; + + req = p2p_build_invitation_req(p2p, dev, go_dev_addr, -1); + if (!req) + return -1; + + p2p_set_state(p2p, P2P_INVITE); + p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; + p2p->invite_peer = dev; + dev->invitation_reqs++; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies) { + wpabuf_free(req); + p2p_dbg(p2p, "Memory allocation failed for extra_ies"); + return -1; + } + + if (os_get_random(pmkid, PMKID_LEN) < 0) { + wpabuf_free(req); + wpabuf_free(extra_ies); + return -1; + } + wpa_hexdump(MSG_DEBUG, + "P2P2: Use new random PMKID for pairing verification", + pmkid, PMKID_LEN); + pasn_set_custom_pmkid(pasn, pmkid); + + if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, true)) { + p2p_dbg(p2p, "Prepare PASN extra IEs failed"); + ret = -1; + goto out; + } + + extra_ies = p2p_pasn_service_hash(p2p, extra_ies); + if (!extra_ies) + goto out; + + pasn_extra_ies = os_memdup(wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + if (!pasn_extra_ies) { + p2p_dbg(p2p, "Memory allocation failed for PASN extra IEs"); + ret = -1; + goto out; + } + + pasn->extra_ies = pasn_extra_ies; + pasn->extra_ies_len = wpabuf_len(extra_ies); + + /* Start PASN verify */ + if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid, + pasn->akmp, pasn->cipher, pasn->group, pasn->freq, + NULL, 0, NULL, 0, NULL)) { + p2p_dbg(p2p, "PASN verify failed"); + ret = -1; + } else { + dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK; + } +out: + pasn->extra_ies = NULL; + pasn->extra_ies_len = 0; + os_free(pasn_extra_ies); + wpabuf_free(req); + wpabuf_free(extra_ies); + return ret; +} + + +int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq) +{ + struct pasn_data *pasn; + struct p2p_device *dev; + struct wpabuf *extra_ies, *req; + u8 *ies = NULL; + int ret = 0; + size_t ies_len; + + if (!addr) { + p2p_dbg(p2p, "Peer address NULL"); + return -1; + } + + dev = p2p_get_device(p2p, addr); + if (!dev) { + p2p_dbg(p2p, "Peer not known"); + return -1; + } + + if (freq == 0) + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + + dev->role = P2P_ROLE_PAIRING_INITIATOR; + p2p_pasn_initialize(p2p, dev, addr, freq, false, true); + pasn = dev->pasn; + + pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr); + + req = p2p_build_go_neg_req(p2p, dev); + if (!req) + return -1; + + p2p->go_neg_peer = dev; + dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies) { + wpabuf_free(req); + return -1; + } + + if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, false)) { + p2p_dbg(p2p, "Failed to prepare PASN extra elements"); + ret = -1; + goto out; + } + + extra_ies = p2p_pasn_service_hash(p2p, extra_ies); + if (!extra_ies) + goto out; + + ies_len = wpabuf_len(extra_ies); + ies = os_memdup(wpabuf_head_u8(extra_ies), ies_len); + if (!ies) { + ret = -1; + goto out; + } + + pasn->extra_ies = ies; + pasn->extra_ies_len = ies_len; + + /* Start PASN authentication */ + if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid, + pasn->akmp, pasn->cipher, pasn->group, pasn->freq, + NULL, 0, NULL, 0, NULL)) { + p2p_dbg(p2p, "Failed to start PASN"); + ret = -1; + } +out: + os_free(ies); + pasn->extra_ies = NULL; + pasn->extra_ies_len = 0; + wpabuf_free(req); + wpabuf_free(extra_ies); + return ret; +} + + +static int p2p_pasn_handle_action_wrapper(struct p2p_data *p2p, + struct p2p_device *dev, + const struct ieee80211_mgmt *mgmt, + size_t len, int freq, int trans_seq) +{ + const u8 *ies; + size_t ies_len; + size_t data_len = 0; + bool derive_kek; + const u8 *data = NULL; + struct p2p_message msg; + struct ieee802_11_elems elems; + + ies = mgmt->u.auth.variable; + ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + p2p_dbg(p2p, + "Failed to parse P2P IE from PASN Authentication frame"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.action_frame_wrapper && msg.action_frame_wrapper_len) { + data = msg.action_frame_wrapper; + data_len = msg.action_frame_wrapper_len; + if (data_len >= 2 && + data[0] == WLAN_ACTION_PUBLIC && + data[1] == WLAN_PA_VENDOR_SPECIFIC) { + data += 2; + data_len -= 2; + if (data_len < 4 || + WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) { + p2p_parse_free(&msg); + return -1; + } + data += 4; + data_len -= 4; + } else { + p2p_dbg(p2p, + "Invalid category in Action frame wrapper in Authentication frame seq %d", + trans_seq); + p2p_parse_free(&msg); + return -1; + } + } + + if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return -1; + } + derive_kek = ieee802_11_rsnx_capab_len( + elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_KEK_IN_PASN); + if (data && data_len >= 1 && data[0] == P2P_INVITATION_REQ) { + struct wpabuf *resp; + + resp = p2p_process_invitation_req(p2p, mgmt->sa, + data + 1, + data_len - 1, freq, + true); + if (!resp) + p2p_dbg(p2p, "No Invitation Response found"); + + dev->role = P2P_ROLE_PAIRING_RESPONDER; + p2p_pasn_initialize(p2p, dev, mgmt->sa, freq, true, + derive_kek); + wpabuf_free(dev->action_frame_wrapper); + dev->action_frame_wrapper = resp; + if (msg.dira && msg.dira_len && + p2p_validate_dira(p2p, dev, msg.dira, + msg.dira_len)) { + struct wpa_ie_data rsn_data; + + if (p2p->cfg->set_pmksa) + p2p->cfg->set_pmksa( + p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + dev->info.dik_id); + + if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, + elems.rsn_ie_len + 2, + &rsn_data) == 0 && + rsn_data.num_pmkid) + pasn_set_custom_pmkid(dev->pasn, + rsn_data.pmkid); + } + } else if (data && data_len >= 1 && data[0] == P2P_GO_NEG_REQ) { + struct wpabuf *resp; + + if (!derive_kek) { + p2p_dbg(p2p, "KEK-in-PASN not set in RSNXE"); + return -1; + } + resp = p2p_process_go_neg_req(p2p, mgmt->sa, data + 1, + data_len - 1, freq, true); + if (!resp) + p2p_dbg(p2p, + "No GO Negotiation Response found"); + wpabuf_free(dev->action_frame_wrapper); + dev->action_frame_wrapper = resp; + } else { + p2p_dbg(p2p, "Invalid action frame wrapper in Auth1"); + } + } else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + if (data && data_len >= 1 && data[0] == P2P_INVITATION_RESP) { + p2p_process_invitation_resp(p2p, mgmt->sa, data + 1, + data_len - 1); + wpabuf_free(dev->action_frame_wrapper); + dev->action_frame_wrapper = NULL; + } else if (data && data_len >= 1 && + data[0] == P2P_GO_NEG_RESP) { + struct wpabuf *conf; + + conf = p2p_process_go_neg_resp(p2p, mgmt->sa, data + 1, + data_len - 1, freq, + true); + if (!conf) + p2p_dbg(p2p, "No GO Negotiation Confirm found"); + wpabuf_free(dev->action_frame_wrapper); + dev->action_frame_wrapper = conf; + } else { + p2p_dbg(p2p, "Invalid action frame wrapper in Auth2"); + } + } else if (trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { + if (data && data_len >= 1 && data[0] == P2P_GO_NEG_CONF) + p2p_handle_go_neg_conf(p2p, mgmt->sa, data + 1, + data_len - 1, true); + else + p2p_invitation_resp_cb(p2p, mgmt->sa, + P2P_SEND_ACTION_SUCCESS); + } + p2p_parse_free(&msg); + return 0; +} + + +static int p2p_pasn_add_encrypted_data(struct p2p_data *p2p, + struct p2p_device *dev, + struct wpabuf *buf) +{ + struct pasn_data *pasn; + struct wpabuf *p2p2_ie; + u8 *dika_len, *p2p2_ie_len; + int ret; + + if (!p2p || !dev || !dev->pasn) + return 0; + + pasn = dev->pasn; + + if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC && + !p2p->pairing_info->enable_pairing_cache) + return 0; + + p2p2_ie = wpabuf_alloc(100); + if (!p2p2_ie) + return -1; + + p2p2_ie_len = p2p_buf_add_p2p2_ie_hdr(p2p2_ie); + + if (p2p->pairing_info->enable_pairing_cache) { + wpabuf_put_u8(p2p2_ie, P2P_ATTR_DEVICE_IDENTITY_KEY); + dika_len = wpabuf_put(p2p2_ie, 2); + + wpabuf_put_u8(p2p2_ie, + p2p->pairing_info->dev_ik.cipher_version); + wpabuf_put_data(p2p2_ie, p2p->pairing_info->dev_ik.dik_data, + p2p->pairing_info->dev_ik.dik_len); + wpabuf_put_be32(p2p2_ie, p2p->pairing_info->dev_ik.expiration); + + WPA_PUT_LE16(dika_len, + (u8 *) wpabuf_put(p2p2_ie, 0) - dika_len - 2); + } + + if (dev->req_bootstrap_method == P2P_PBMA_OPPORTUNISTIC) { + if (!p2p->dev_sae_password[0]) { + int password_len; + + /* SAE password is not available as the request is not + * for an existing GO. Pick a random SAE password of + * length between 10 and 20. */ + password_len = 10 + os_random() % 10; + if (p2p_random(p2p->dev_sae_password, + password_len) < 0) { + wpabuf_free(p2p2_ie); + return -1; + } + p2p->dev_sae_password[password_len] = '\0'; + } + + wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD); + wpabuf_put_le16(p2p2_ie, os_strlen(p2p->dev_sae_password)); + wpabuf_put_str(p2p2_ie, p2p->dev_sae_password); + } + + p2p_buf_update_ie_hdr(p2p2_ie, p2p2_ie_len); + + ret = pasn_add_encrypted_data(pasn, buf, wpabuf_mhead_u8(p2p2_ie), + wpabuf_len(p2p2_ie)); + wpabuf_free(p2p2_ie); + return ret; +} + + +int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr) +{ + int ret = -1; + struct p2p_device *dev; + struct pasn_data *pasn; + struct wpabuf *extra_ies; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, peer_addr); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "PASN: Peer not found " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + pasn = dev->pasn; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies || + p2p_prepare_pasn_extra_ie(p2p, extra_ies, + dev->action_frame_wrapper, false)) { + p2p_dbg(p2p, "Failed to prepare PASN extra elements"); + goto out; + } + + if (p2p_pasn_add_encrypted_data(p2p, dev, extra_ies) < 0) + p2p_dbg(p2p, "Failed to add PASN encrypted elements"); + + ret = pasn_set_extra_ies(pasn, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + +out: + wpabuf_free(extra_ies); + wpabuf_free(dev->action_frame_wrapper); + dev->action_frame_wrapper = NULL; + + return ret; +} + + +int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len) +{ + u8 attr_id; + const u8 *pos, *next; + u16 rem_len, attr_len; + + if (!p2p || !data || !len) + return -1; + + pos = data; + rem_len = len; + + if (rem_len < 6 || + pos[0] != WLAN_EID_VENDOR_SPECIFIC || + pos[1] < 4 || + rem_len < 2 + pos[1] || + WPA_GET_BE32(&pos[2]) != P2P2_IE_VENDOR_TYPE) { + p2p_dbg(p2p, + "P2P: P2P2 IE not present in PASN Encrypted Data element"); + return -1; + } + + pos += 6; + rem_len -= 6; + + while (rem_len >= 3) { + attr_id = *pos++; + attr_len = WPA_GET_LE16(pos); + pos += 2; + rem_len -= 3; + if (rem_len < attr_len) + return -1; + next = pos + attr_len; + rem_len -= attr_len; + + switch (attr_id) { + case P2P_ATTR_DEVICE_IDENTITY_KEY: + if (attr_len < 1) { + p2p_dbg(p2p, + "Too short Device Identity Key attribute"); + return -1; + } + p2p->dik_cipher_version = *pos++; + attr_len--; + if (p2p->dik_cipher_version == + DIRA_CIPHER_VERSION_128) { + if (attr_len < DEVICE_IDENTITY_KEY_LEN) { + p2p_dbg(p2p, "Too short DevIK"); + return -1; + } + os_memcpy(p2p->peer_dik_data, pos, + DEVICE_IDENTITY_KEY_LEN); + p2p->peer_dik_len = DEVICE_IDENTITY_KEY_LEN; + pos += DEVICE_IDENTITY_KEY_LEN; + attr_len -= DEVICE_IDENTITY_KEY_LEN; + } else { + p2p_dbg(p2p, + "Unsupported cipher version %u in Device Identity Key attribute", + p2p->dik_cipher_version); + return -1; + } + if (attr_len < 4) { + p2p_dbg(p2p, + "Not enough room for DevIK lifetime"); + return -1; + } + p2p->peer_dik_lifetime = WPA_GET_BE32(pos); + p2p_dbg(p2p, + "Received peer DevIK of length %zu octets and lifetime %u", + p2p->peer_dik_len, p2p->peer_dik_lifetime); + break; + case P2P_ATTR_PASSWORD: + if (attr_len < 1 || + attr_len > sizeof(p2p->peer_sae_password) - 1) { + p2p_dbg(p2p, + "P2P: Invalid password length %d", + attr_len); + return -1; + } + os_memset(p2p->peer_sae_password, 0, + sizeof(p2p->peer_sae_password)); + os_memcpy(p2p->peer_sae_password, pos, attr_len); + break; + default: + p2p_dbg(p2p, + "Unsupported Attribute ID %u in P2P2 IE in PASN Encrypted Data element", + attr_id); + break; + } + pos = next; + } + + return 0; +} + + +static int p2p_validate_custom_pmkid(struct p2p_data *p2p, + struct p2p_device *dev, const u8 *pmkid) +{ + if (dev->pasn->custom_pmkid_valid && + os_memcmp(dev->pasn->custom_pmkid, pmkid, PMKID_LEN) == 0) { + p2p_dbg(p2p, "Customized PMKID valid"); + return 0; + } + return -1; +} + + +static int p2p_pasn_pmksa_get_pmk(struct p2p_data *p2p, const u8 *addr, + u8 *pmkid, u8 *pmk, size_t *pmk_len) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (!dev) { + p2p_dbg(p2p, "PASN: Peer not found " MACSTR, MAC2STR(addr)); + return -1; + } + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR) + return pasn_initiator_pmksa_cache_get(p2p->initiator_pmksa, + addr, pmkid, pmk, + pmk_len); + else + return pasn_responder_pmksa_cache_get(p2p->responder_pmksa, + addr, pmkid, pmk, + pmk_len); +} + + +int p2p_pasn_validate_and_update_pmkid(struct p2p_data *p2p, const u8 *addr, + const u8 *rsn_pmkid) +{ + size_t pmk_len; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + struct p2p_device *dev; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, addr); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "P2P PASN: Peer not found " MACSTR, + MAC2STR(addr)); + return -1; + } + + if (p2p_validate_custom_pmkid(p2p, dev, rsn_pmkid)) + return -1; + + if (p2p_pasn_pmksa_get_pmk(p2p, addr, pmkid, pmk, &pmk_len)) { + p2p_dbg(p2p, "P2P PASN: Failed to get PMK from cache"); + return -1; + } + + p2p_pasn_pmksa_set_pmk(p2p, p2p->cfg->dev_addr, addr, pmk, pmk_len, + rsn_pmkid); + return 0; +} + + +int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data, + size_t data_len, bool acked, bool verify) +{ + int ret = 0; + struct p2p_device *dev; + struct pasn_data *pasn; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, mgmt->da); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "P2P PASN: Peer not found " MACSTR, + MAC2STR(mgmt->da)); + return -1; + } + + pasn = dev->pasn; + + ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked); + if (ret == 2 && pasn->frame) + return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame), + wpabuf_len(pasn->frame), 0, pasn->freq, + 1000); + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + if (ret != 1) + return ret; + + if (verify && dev == p2p->invite_peer) + p2p_start_invitation_connect(p2p, dev); + else if (dev == p2p->go_neg_peer) + p2p_go_complete(p2p, dev); + + return 0; +} + + +static int p2p_handle_pasn_auth(struct p2p_data *p2p, struct p2p_device *dev, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct pasn_data *pasn; + u8 pasn_type; + int pasn_groups[4] = { 0 }; + u16 auth_alg, auth_transaction, status_code; + + if (!p2p || !dev || !dev->pasn) + return -1; + + if (os_memcmp(mgmt->da, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "PASN Responder: Not our frame"); + return -1; + } + + if (len < offsetof(struct ieee80211_mgmt, u.auth.variable)) + return -1; + + pasn = dev->pasn; + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + status_code = le_to_host16(mgmt->u.auth.status_code); + + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + if (status_code != WLAN_STATUS_SUCCESS && + status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + p2p_dbg(p2p, "PASN: Authentication rejected - status=%u", + status_code); + return -1; + } + + if (auth_alg != WLAN_AUTH_PASN || + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + p2p_dbg(p2p, + "PASN Responder: Not a PASN frame or unexpected Authentication frame, auth_alg=%d", + auth_alg); + return -1; + } + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + pasn_type = p2p->cfg->pairing_config.pasn_type; + if (pasn_type & 0xc && pasn_type & 0x3) { + pasn_groups[0] = 20; + pasn_groups[1] = 19; + } else if (pasn_type & 0xc) { + pasn_groups[0] = 20; + } else { + pasn_groups[0] = 19; + } + os_free(pasn->pasn_groups); + pasn->pasn_groups = int_array_dup(pasn_groups); + + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, + "PASN Responder: Handle Auth 1 action wrapper failed"); + return -1; + } + if (handle_auth_pasn_1(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt, + len, false) < 0) { + p2p_dbg(p2p, + "PASN Responder: Handle Auth 1 failed"); + return -1; + } + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { + if (handle_auth_pasn_3(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt, + len) < 0) { + p2p_dbg(p2p, + "PASN Responder: Handle Auth 3 failed"); + return -1; + } +#ifdef CONFIG_TESTING_OPTIONS + p2p_pasn_store_ptk(p2p, &pasn->ptk); +#endif /* CONFIG_TESTING_OPTIONS */ + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, + "PASN Responder: Handle Auth 3 action wrapper failed"); + /* Drop keying material from a failed pairing attempt */ + os_memset(p2p->peer_dik_data, 0, + sizeof(p2p->peer_dik_data)); + os_memset(p2p->peer_sae_password, 0, + sizeof(p2p->peer_sae_password)); + return -1; + } + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + } + return 0; +} + + +int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt, + size_t len, int freq) +{ + int ret = 0; + u8 auth_transaction; + struct p2p_device *dev; + struct pasn_data *pasn; + struct wpa_pasn_params_data pasn_data; + + dev = p2p_get_device(p2p, mgmt->sa); + if (!dev) { + p2p_dbg(p2p, "PASN: Peer not found " MACSTR, + MAC2STR(mgmt->sa)); + return -1; + } + + if (!dev->pasn) { + dev->pasn = pasn_data_init(); + if (!dev->pasn) { + p2p_dbg(p2p, "PASN: Uninitialized"); + return -1; + } + } + + pasn = dev->pasn; + + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + pasn_register_callbacks(pasn, p2p->cfg->cb_ctx, + p2p->cfg->pasn_send_mgmt, + p2p->cfg->pasn_validate_pmkid, + NULL, NULL); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR && + auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) { + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, + "PASN Initiator: Handle Auth 2 action wrapper failed"); + return -1; + } + ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, + &pasn_data); + if (ret < 0) { + p2p_dbg(p2p, "PASN: wpa_pasn_auth_rx() failed"); + dev->role = P2P_ROLE_IDLE; + } +#ifdef CONFIG_TESTING_OPTIONS + p2p_pasn_store_ptk(p2p, &pasn->ptk); +#endif /* CONFIG_TESTING_OPTIONS */ + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + } else { + ret = p2p_handle_pasn_auth(p2p, dev, mgmt, len, freq); + } + return ret; +} + + +void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst, + const u8 *pmk, size_t pmk_len, const u8 *pmkid) +{ + pasn_initiator_pmksa_cache_add(p2p->initiator_pmksa, src, dst, pmk, + pmk_len, pmkid, WPA_KEY_MGMT_SAE); + pasn_responder_pmksa_cache_add(p2p->responder_pmksa, src, dst, pmk, + pmk_len, pmkid, WPA_KEY_MGMT_SAE); +} + + +#ifdef CONFIG_TESTING_OPTIONS + +void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk) +{ + u8 *pos; + + if (ptk->ptk_len > sizeof(p2p->pasn_ptk)) { + p2p_dbg(p2p, "P2P PASN PTK exceeds: (len=%ld)", ptk->ptk_len); + return; + } + + pos = p2p->pasn_ptk; + p2p->pasn_ptk_len = ptk->ptk_len; + if (ptk->kck_len) { + os_memcpy(pos, ptk->kck, ptk->kck_len); + pos += ptk->kck_len; + } + if (ptk->kek_len) { + os_memcpy(pos, ptk->kek, ptk->kek_len); + pos += ptk->kek_len; + } + if (ptk->tk_len) { + os_memcpy(pos, ptk->tk, ptk->tk_len); + pos += ptk->tk_len; + } + if (ptk->kdk_len) { + os_memcpy(pos, ptk->kdk, ptk->kdk_len); + pos += ptk->kdk_len; + } +} + + +int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len) +{ + if (!p2p || !p2p->pasn_ptk_len) + return -1; + + *buf_len = p2p->pasn_ptk_len; + *buf = p2p->pasn_ptk; + return 0; +} + +#endif /* CONFIG_TESTING_OPTIONS */ + +#endif /* CONFIG_PASN */ + + +int p2p_get_dira_info(struct p2p_data *p2p, char *buf, size_t buflen) +{ + int res; + char *pos, *end; + struct p2p_id_key *dev_ik; + + if (!p2p->pairing_info || + !p2p->cfg->pairing_config.pairing_capable || + !p2p->cfg->pairing_config.enable_pairing_cache) + return 0; + + if (p2p_derive_nonce_tag(p2p)) + return 0; + + pos = buf; + end = buf + buflen; + dev_ik = &p2p->pairing_info->dev_ik; + + res = os_snprintf(pos, end - pos, MACSTR, + MAC2STR(p2p->cfg->dev_addr)); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + res = os_snprintf(pos, end - pos, " "); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, dev_ik->dira_nonce, + dev_ik->dira_nonce_len); + + res = os_snprintf(pos, end - pos, " "); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, dev_ik->dira_tag, + dev_ik->dira_tag_len); + + res = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + return pos - buf; +} diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h index 27bdac3bf51c..7c89c8c35ba9 100644 --- a/contrib/wpa/src/p2p/p2p.h +++ b/contrib/wpa/src/p2p/p2p.h @@ -11,6 +11,17 @@ #include "common/ieee802_11_defs.h" #include "wps/wps.h" +#include "common/wpa_common.h" + +#define DEVICE_IDENTITY_KEY_MAX_LEN 64 +#define DEVICE_IDENTITY_KEY_LEN 16 +#define DEVICE_IDENTITY_TAG_LEN 8 +#define DEVICE_IDENTITY_NONCE_LEN 8 +#define DEVICE_MAX_HASH_LEN 32 +#define DIR_STR_LEN 3 + +/* DIRA Cipher versions */ +#define DIRA_CIPHER_VERSION_128 0 struct weighted_pcl; @@ -33,7 +44,7 @@ struct weighted_pcl; /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ -#define P2P_MAX_REG_CLASSES 15 +#define P2P_MAX_REG_CLASSES 20 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class @@ -171,6 +182,41 @@ struct p2p_go_neg_results { * peer_config_timeout - Peer configuration timeout (in 10 msec units) */ unsigned int peer_config_timeout; + + /** + * p2p2 - Whether this group uses P2P2 + */ + bool p2p2; + + /** + * akmp - The negotiated PASN AKMP for P2P2 + */ + int akmp; + + /** + * cipher - Pairwise cipher(s) for the group for P2P2 + */ + int cipher; + + /** + * pmkid - PMKID for P2P2 when PMK is derived as part of pairing + */ + u8 pmkid[PMKID_LEN]; + + /** + * pmk - PMK for P2P2 when PMK is derived as part of pairing + */ + u8 pmk[PMK_LEN_MAX]; + + /** + * pmk_len - Length of @pmk in octets + */ + size_t pmk_len; + + /** + * sae_password - SAE password for the group (P2P2) + */ + char sae_password[100]; }; struct p2ps_provision { @@ -320,6 +366,45 @@ enum p2p_scan_type { #define P2P_MAX_WPS_VENDOR_EXT 10 +/** + * struct p2p_pairing_config - P2P pairing configuration + */ +struct p2p_pairing_config { + /** + * Pairing capable + */ + bool pairing_capable; + + /** + * Enable P2P pairing setup + */ + bool enable_pairing_setup; + + /** + * Enable pairing cache to allow verification + */ + bool enable_pairing_cache; + + /** + * P2P bootstrapping methods supported + */ + u16 bootstrap_methods; + + /** + * Bitmap of supported PASN types + */ + u8 pasn_type; + + /* Cipher version type */ + int dik_cipher; + + /* Buffer to hold the DevIK */ + u8 dik_data[DEVICE_IDENTITY_KEY_MAX_LEN]; + + /* Length of DevIK in octets */ + size_t dik_len; +}; + /** * struct p2p_peer_info - P2P peer information */ @@ -411,6 +496,41 @@ struct p2p_peer_info { * p2ps_instance - P2PS Application Service Info */ struct wpabuf *p2ps_instance; + + /** + * pcea_cap_info - Capability info in PCEA + */ + u16 pcea_cap_info; + + /** + * The regulatory info encoding for operation in 6 GHz band + */ + u8 reg_info; + + /** + * p2p_pairing_config - P2P pairing configuration + */ + struct p2p_pairing_config pairing_config; + + /** + * dik_id - For paired peers Identity block ID with PMK + */ + int dik_id; + + /** + * nonce_tag_valid - Whether nonce and tag are valid + */ + bool nonce_tag_valid; + + /** + * nonce - Valid nonce received in a recent discovery frame + */ + u8 nonce[DEVICE_IDENTITY_NONCE_LEN]; + + /** + * tag - Valid tag received in a recent discovery frame + */ + u8 tag[DEVICE_IDENTITY_TAG_LEN]; }; enum p2p_prov_disc_status { @@ -589,6 +709,48 @@ struct p2p_config { */ unsigned int passphrase_len; + /** + * p2p_pairing_config - P2P pairing configuration + */ + struct p2p_pairing_config pairing_config; + + /** + * reg_info - Regulatory info encoding for operation in 6 GHz band + */ + u8 reg_info; + + /** + * dfs_owner - Enable P2P GO to act as DFS Owner + */ + bool dfs_owner; + + /** + * twt_power_mgmt - Enable TWT based power management for P2P + */ + bool twt_power_mgmt; + + /** + * comeback_after - Bootstrap request unauthorized for peer + * + * Ask to come back after this many TUs. + */ + u16 comeback_after; + + /** + * chan_switch_req_enable - Enable P2P client channel switch request + */ + bool chan_switch_req_enable; + + /** + * Operating class for own operational channel in Invitation Response + */ + u8 inv_op_class; + + /** + * inv_op_channel - Own operational channel in Invitation Response + */ + u8 inv_op_channel; + /** * cb_ctx - Context to use with callback functions */ @@ -816,6 +978,20 @@ struct p2p_config { */ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); + /** + * set_go_security_config - Set security configuration for P2P GO + * @ctx: Callback context from cb_ctx + * @res: GO Negotiation results + * + * This callback is used to set PMK/passphrase derived during PASN + * authentication with a P2P client. This will fetch an active P2P group + * owner instance and configure PMKSA in case of password based PASN, or + * configures the passphrase and derive PT in case of unauthenticated + * PASN. + */ + void (*set_go_security_config)(void *ctx, + struct p2p_go_neg_results *res); + /** * sd_request - Callback on Service Discovery Request * @ctx: Callback context from cb_ctx @@ -926,6 +1102,10 @@ struct p2p_config { * @channels: Available operating channels for the group * @dev_pw_id: Device Password ID for NFC static handover or -1 if not * used + * @p2p2: Whether invitation request was wrapped in PASN authentication + * received from a P2P2 device + * @new_ssid: Pointer to hold new SSID + * @new_ssid_len: Length of new SSID buffer in octets * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect @@ -948,7 +1128,8 @@ struct p2p_config { size_t ssid_len, int *go, u8 *group_bssid, int *force_freq, int persistent_group, const struct p2p_channels *channels, - int dev_pw_id); + int dev_pw_id, bool p2p2, const u8 **new_ssid, + size_t *new_ssid_len); /** * invitation_received - Callback on Invitation Request RX @@ -970,18 +1151,25 @@ struct p2p_config { void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *go_dev_addr, u8 status, - int op_freq); + int op_freq, const u8 *pmkid, const u8 *pmk, + size_t pmk_len); /** * invitation_result - Callback on Invitation result * @ctx: Callback context from cb_ctx * @status: Negotiation result (Status Code) + * @new_ssid: New SSID received in invitation response + * @new_ssid_len: Length of new SSID received * @bssid: P2P Group BSSID or %NULL if not received * @channels: Available operating channels for the group * @addr: Peer address * @freq: Frequency (in MHz) indicated during invitation or 0 * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer * during invitation or 0 + * @pmkid: PMKID used during invitation handshake + * @pmk: The derived PMK + * @pmk_len: PMK length in octets + * @go_dev_addr: The P2P Device Address of the GO * * This callback is used to indicate result of an Invitation procedure * started with a call to p2p_invite(). The indicated status code is @@ -989,9 +1177,12 @@ struct p2p_config { * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a * local failure in transmitting the Invitation Request. */ - void (*invitation_result)(void *ctx, int status, const u8 *bssid, + void (*invitation_result)(void *ctx, int status, const u8 *new_ssid, + size_t new_ssid_len, const u8 *bssid, const struct p2p_channels *channels, - const u8 *addr, int freq, int peer_oper_freq); + const u8 *addr, int freq, int peer_oper_freq, + const u8 *pmkid, const u8 *pmk, + size_t pmk_len, const u8 *go_dev_addr); /** * go_connected - Check whether we are connected to a GO @@ -1084,7 +1275,8 @@ struct p2p_config { * When P2PS provisioning completes (successfully or not) we must * transmit all of the results to the upper layers. */ - void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev, + void (*p2ps_prov_complete)(void *ctx, enum p2p_status_code status, + const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, @@ -1136,6 +1328,121 @@ struct p2p_config { int (*get_pref_freq_list)(void *ctx, int go, unsigned int *len, struct weighted_pcl *freq_list); + + /** + * register_bootstrap_comeback - Register timeout to initiate bootstrap + * comeback request + * @ctx: Callback context from cb_ctx + * @addr: P2P Device Address to which comeback request is to be sent + * @comeback_after: Time in TUs after which comeback request is sent + * + * This function can be used to send comeback request after given + * timeout. + */ + void (*register_bootstrap_comeback)(void *ctx, const u8 *addr, + u16 comeback_after); + + /** + * bootstrap_req_rx - Indicate bootstrap request from a P2P peer + * @ctx: Callback context from cb_ctx + * @addr: P2P device address from which bootstrap request was received + * @bootstrap_method: Bootstrapping method request by the peer device + * + * This function can be used to notify that bootstrap request is + * received from a P2P peer. + */ + void (*bootstrap_req_rx)(void *ctx, const u8 *addr, + u16 bootstrap_method); + + /** + * bootstrap_rsp_rx - Indicate bootstrapping response from a P2P peer + * @ctx: Callback context from cb_ctx + * @addr: P2P device address with which bootstrapping is completed + * @status: P2P Status Code of bootstrapping handshake + * @freq: Frequency in which bootstrapping is done + * @bootstrap_method: Bootstrapping method by the peer device + * + * This function can be used to notify the status of bootstrapping + * handshake. + */ + void (*bootstrap_rsp_rx)(void *ctx, const u8 *addr, + enum p2p_status_code status, int freq, + u16 bootstrap_method); + + /** + * validate_dira - Indicate reception of DIRA to be validated against a + * list of available device identity keys + * @ctx: Callback context from cb_ctx + * @peer_addr: P2P Device address of the peer + * @dira_nonce: DIRA Nonce + * @dira_tag: DIRA Tag + * Returns: Identity block ID on success, 0 on failure + * + * This function can be used to validate DIRA. + */ + int (*validate_dira)(void *ctx, const u8 *peer_addr, + const u8 *dira_nonce, const u8 *dira_tag); + + /** + * set_pmksa - Configure PMK of a paired/persistent peer from + * configuration + * @ctx: Callback context from cb_ctx + * @peer_addr: P2P Device address of the peer + * @dik_id: Identity block ID + * Returns: 0 on success + * + * It is expected to call p2p_pasn_pmksa_set_pmk() to set the PMK/PMKID + * for given dik_id. + */ + int (*set_pmksa)(void *ctx, const u8 *peer_addr, int dik_id); + + /** + * pasn_send_mgmt - Function handler to transmit a Management frame + * @ctx: Callback context from cb_ctx + * @data: Frame to transmit + * @data_len: Length of frame to transmit + * @noack: No ack flag + * @freq: Frequency in MHz for the channel on which to transmit + * @wait: How many milliseconds to wait for a response frame + * Returns: 0 on success, -1 on failure + */ + int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, unsigned int wait); + + /** + * prepare_data_element - Function handler to update protocol specific + * elements in PASN authentication frames + * @ctx: Callback context from cb_ctx + * @peer_addr: Peer MAC address + * Returns: 0 on success, -1 on failure + */ + int (*prepare_data_element)(void *ctx, const u8 *peer_addr); + + /** + * parse_data_element - Function handler to parse P2P data element + * @ctx: Callback context from cb_ctx + * @data: Data to be parsed + * @len: Length of data + * Returns: 0 on success, -1 on failure + */ + int (*parse_data_element)(void *ctx, const u8 *data, size_t len); + + /** + * pasn_validate_pmkid - Function handler to validate RSN PMKID + * @ctx: Callback context from cb_ctx + * @addr: Peer MAC address + * @pmkid: PMKID in the PASN frame + * Returns: 0 on success, -1 on failure + */ + int (*pasn_validate_pmkid)(void *ctx, const u8 *addr, const u8 *pmkid); + + /** + * + * is_p2p_dfs_chan - DFS channel check + * + * To check if a channel is a DFS channel. + */ + bool (*is_p2p_dfs_chan)(void *ctx, int freq, u8 op_class, u8 chan); }; @@ -1319,6 +1626,10 @@ void p2p_stop_listen(struct p2p_data *p2p); * formation * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) + * @oob_pw_id: OOB password identifier + * @p2p2: Device supports P2P2 features + * @bootstrap: Bootstrapping method requested for P2P2 provision discovery + * @password: P2P2 pairing password or %NULL for opportunistic method * Returns: 0 on success, -1 on failure */ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, @@ -1326,7 +1637,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id, + bool p2p2, u16 bootstrap, const char *password); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) @@ -1344,6 +1656,9 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, * @force_ssid_len: Length of $force_ssid buffer * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if * force_freq == 0) + * @oob_pw_id: OOB password identifier + * @bootstrap: Bootstrapping method requested for P2P2 provision discovery + * @password: P2P2 pairing password or %NULL for opportunistic method * Returns: 0 on success, -1 on failure * * This is like p2p_connect(), but the actual group negotiation is not @@ -1354,7 +1669,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - unsigned int pref_freq, u16 oob_pw_id); + unsigned int pref_freq, u16 oob_pw_id, u16 bootstrap, + const char *password); /** * p2p_reject - Reject peer device (explicitly block connection attempts) @@ -1364,6 +1680,17 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, */ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); +/** + * p2p_set_req_bootstrap_method - Send Provision Discovery Request to initiate + * bootstrapping + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @boostrap: Bootstrapping method + * Returns: 0 on success, -1 on failure + */ +int p2p_set_req_bootstrap_method(struct p2p_data *p2p, const u8 *peer_addr, + u16 bootstrap); + /** * p2p_prov_disc_req - Send Provision Discovery Request * @p2p: P2P module context from p2p_init() @@ -1460,12 +1787,14 @@ enum p2p_invite_role { * force_freq == 0) * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover * case or -1 if not used + * @p2p2: Operating in P2P2 mode * Returns: 0 on success, -1 on failure */ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group, unsigned int pref_freq, int dev_pw_id); + int persistent_group, unsigned int pref_freq, int dev_pw_id, + bool p2p2); /** * p2p_presence_req - Request GO presence @@ -1687,6 +2016,8 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, */ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); +void p2p_listen_failed(struct p2p_data *p2p, unsigned int freq); + void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len); @@ -1705,6 +2036,11 @@ struct p2p_group; * the per-group information with p2p_group_init(). */ struct p2p_group_config { + /** + * p2p2 - Whether the group was formed using P2P2 + */ + bool p2p2; + /** * persistent_group - Whether the group is persistent * 0 = not a persistent group @@ -1943,6 +2279,14 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, */ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); +/** + * p2p_build_ssid - Generate a random P2P SSID + * @p2p: P2P module context from p2p_init() + * @ssid: Buffer for SSID + * @ssid_len: Pointer to hold SSID length + */ +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); + /** * p2p_go_params - Generate random P2P group parameters * @p2p: P2P module context from p2p_init() @@ -1951,6 +2295,18 @@ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); */ int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); +/** + * p2p_set_go_role - Set the current role of P2P device + * @p2p: P2P module context from p2p_init() + * @val: 1 if P2P GO, 0 to reset the role variable + * + * This role is configured as P2P GO when authorizing a P2P Client to join the + * group. Once PASN authentication with GO negotiation with predefined GO intent + * values (15 for P2P GO) is completed, the role helps to configure PMK derived + * during the PASN authentication. + */ +void p2p_set_go_role(struct p2p_data *p2p, bool val); + /** * p2p_get_group_capab - Get Group Capability from P2P IE data * @p2p_ie: P2P IE(s) contents @@ -2051,6 +2407,8 @@ int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr); int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr); +int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr, + const u8 **dik_data, size_t *dik_len, u8 *cipher); void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); @@ -2429,5 +2787,45 @@ bool is_p2p_allow_6ghz(struct p2p_data *p2p); void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value); int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size); int p2p_channel_to_freq(int op_class, int channel); +struct wpabuf * p2p_usd_elems(struct p2p_data *p2p); +void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len, + const u8 *peer_addr, unsigned int freq); +int p2p_get_dik_id(struct p2p_data *p2p, const u8 *peer); + +void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods); +void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type); +void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after); +void p2p_set_reg_info(struct p2p_data *p2p, u8 val); +void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val); +void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr); +void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val); +void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq); + +int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr); +int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq); +int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr, + int freq, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + unsigned int pref_freq); +int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt, + size_t len, int freq); +int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr); +int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len); +int p2p_pasn_validate_and_update_pmkid(struct p2p_data *p2p, const u8 *addr, + const u8 *pmkid); +int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data, + size_t data_len, bool acked, bool verify); +int p2p_config_sae_password(struct p2p_data *p2p, const char *pw); +void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst, + const u8 *pmk, size_t pmk_len, const u8 *pmkid); +void p2p_set_store_pasn_ptk(struct p2p_data *p2p, u8 val); +void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk); +int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len); +void p2p_usd_service_hash(struct p2p_data *p2p, const char *service_name); +int p2p_get_dira_info(struct p2p_data *p2p, char *buf, size_t buflen); + +void p2p_update_dfs_ap_info(struct p2p_data *p2p, int freq, + enum chan_width ap_ch_width, bool disconnect_evt); #endif /* P2P_H */ diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c index e4f40fe8fcc0..bc67ec201202 100644 --- a/contrib/wpa/src/p2p/p2p_build.c +++ b/contrib/wpa/src/p2p/p2p_build.c @@ -55,11 +55,24 @@ u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len) { - /* Update P2P IE Length */ + /* Update P2P/P2P2 IE Length */ *len = (u8 *) wpabuf_put(buf, 0) - len - 1; } +u8 * p2p_buf_add_p2p2_ie_hdr(struct wpabuf *buf) +{ + u8 *len; + + /* P2P2 IE header */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); /* IE length to be filled */ + wpabuf_put_be32(buf, P2P2_IE_VENDOR_TYPE); + wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header"); + return len; +} + + void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab) { /* P2P Capability */ @@ -405,6 +418,20 @@ void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) } +void p2p_buf_add_usd_service_hash(struct wpabuf *buf, struct p2p_data *p2p) +{ + if (!p2p) + return; + + /* USD Service Hash */ + wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); + wpabuf_put_le16(buf, P2PS_HASH_LEN); + wpabuf_put_data(buf, p2p->p2p_service_hash, P2PS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", + p2p->p2p_service_hash, P2PS_HASH_LEN); +} + + void p2p_buf_add_session_info(struct wpabuf *buf, const char *info) { size_t info_len = 0; @@ -709,6 +736,113 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, } +void p2p_buf_add_pcea(struct wpabuf *buf, struct p2p_data *p2p) +{ + u8 *len; + u16 capability_info = 0; + + /* P2P Capability Extension */ + wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY_EXTENSION); + /* Length to be filled */ + len = wpabuf_put(buf, 2); + + if (!p2p->cfg->p2p_6ghz_disable) + capability_info |= P2P_PCEA_6GHZ; + + if (p2p->cfg->reg_info) + capability_info |= P2P_PCEA_REG_INFO; + + if (p2p->cfg->dfs_owner) + capability_info |= P2P_PCEA_DFS_OWNER; + + if (p2p->cfg->chan_switch_req_enable) + capability_info |= P2P_PCEA_CLI_REQ_CS; + + if (p2p->cfg->pairing_config.pairing_capable) + capability_info |= P2P_PCEA_PAIRING_CAPABLE; + + if (p2p->cfg->pairing_config.enable_pairing_setup) + capability_info |= P2P_PCEA_PAIRING_SETUP_ENABLED; + + if (p2p->cfg->pairing_config.enable_pairing_cache) + capability_info |= P2P_PCEA_PMK_CACHING; + + if (p2p->cfg->pairing_config.pasn_type) + capability_info |= P2P_PCEA_PASN_TYPE; + + if (p2p->cfg->twt_power_mgmt) + capability_info |= P2P_PCEA_TWT_POWER_MGMT; + + /* Field length is (n-1), n in octets */ + capability_info |= (2 - 1) & P2P_PCEA_LEN_MASK; + wpabuf_put_le16(buf, capability_info); + + if (capability_info & P2P_PCEA_REG_INFO) + wpabuf_put_u8(buf, p2p->cfg->reg_info); + + if (capability_info & P2P_PCEA_PASN_TYPE) + wpabuf_put_u8(buf, p2p->cfg->pairing_config.pasn_type); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + + wpa_printf(MSG_DEBUG, "P2P: * Capability Extension info=0x%x", + capability_info); +} + + +void p2p_buf_add_pbma(struct wpabuf *buf, u16 bootstrap, const u8 *cookie, + size_t cookie_len, int comeback_after) +{ + u8 *len; + + /* P2P Pairing and Bootstrapping methods */ + wpabuf_put_u8(buf, P2P_ATTR_PAIRING_AND_BOOTSTRAPPING); + /* Length to be filled */ + len = wpabuf_put(buf, 2); + + if (cookie && cookie_len) { + if (comeback_after) + wpabuf_put_le16(buf, comeback_after); + wpabuf_put_u8(buf, cookie_len); + wpabuf_put_data(buf, cookie, cookie_len); + } + wpabuf_put_le16(buf, bootstrap); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + + wpa_printf(MSG_DEBUG, "P2P: * Bootstrapping method=0x%x", + bootstrap); +} + + +void p2p_buf_add_dira(struct wpabuf *buf, struct p2p_data *p2p) +{ + u8 *len; + struct p2p_id_key *dev_ik; + + if (!p2p->cfg->pairing_config.pairing_capable || + !p2p->cfg->pairing_config.enable_pairing_cache) + return; + + dev_ik = &p2p->pairing_info->dev_ik; + /* P2P DIRA */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_IDENTITY_RESOLUTION); + /* Length to be filled */ + len = wpabuf_put(buf, 2); + + wpabuf_put_u8(buf, dev_ik->cipher_version); + wpabuf_put_data(buf, dev_ik->dira_nonce, dev_ik->dira_nonce_len); + wpabuf_put_data(buf, dev_ik->dira_tag, dev_ik->dira_tag_len); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + + wpa_printf(MSG_DEBUG, "P2P: * DIRA"); +} + + static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, const char *val) { @@ -839,3 +973,37 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, return 0; } + + +struct wpabuf * p2p_encaps_ie(const struct wpabuf *subelems, u32 ie_type) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (!subelems) + return NULL; + + len = wpabuf_len(subelems) + 1000; + + ie = wpabuf_alloc(len); + if (!ie) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, ie_type); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 30901b34ddd5..79cdfa7d25e1 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -135,11 +135,11 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) } -static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, - struct p2p_device *peer) +struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer) { struct wpabuf *buf; - u8 *len; + struct wpabuf *subelems; u8 group_capab; size_t extra = 0; u16 pw_id; @@ -159,7 +159,12 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); - len = p2p_buf_add_ie_hdr(buf); + subelems = wpabuf_alloc(500); + if (!subelems) { + wpabuf_free(buf); + return NULL; + } + group_capab = 0; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; @@ -170,17 +175,20 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(subelems, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p_buf_add_go_intent(subelems, + (p2p->go_intent << 1) | peer->tie_breaker); + p2p_buf_add_config_timeout(subelems, p2p->go_timeout, + p2p->client_timeout); + p2p_buf_add_listen_channel(subelems, p2p->cfg->country, + p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) - p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p_buf_add_ext_listen_timing(subelems, p2p->ext_listen_period, p2p->ext_listen_interval); - p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_intended_addr(subelems, p2p->intended_addr); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); if (p2p->num_pref_freq) { @@ -191,16 +199,15 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->num_pref_freq, &pref_chanlist, go); p2p_channels_dump(p2p, "channel list after filtering", &pref_chanlist); - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &pref_chanlist, is_6ghz_capab); } else { - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &p2p->channels, is_6ghz_capab); } - p2p_buf_add_device_info(buf, p2p, peer); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_device_info(subelems, p2p, peer); + p2p_buf_add_operating_channel(subelems, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); - p2p_buf_update_ie_hdr(buf, len); p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, p2p->num_pref_freq); @@ -209,8 +216,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) pw_id = peer->oob_pw_id; - if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + if (!peer->p2p2 && p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(subelems); wpabuf_free(buf); return NULL; } @@ -223,6 +231,8 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + buf = wpabuf_concat(buf, p2p_encaps_ie(subelems, P2P_IE_VENDOR_TYPE)); + wpabuf_free(subelems); return buf; } @@ -244,10 +254,14 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) config_method = WPS_CONFIG_PUSHBUTTON; else if (dev->wps_method == WPS_P2PS) config_method = WPS_CONFIG_P2PS; + else if (dev->p2p2 && dev->req_bootstrap_method) + config_method = WPS_NOT_READY; else return -1; return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, NULL, config_method, 0, 0, 1); + } else if (dev->p2p2) { + return 0; } freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; @@ -291,7 +305,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, u8 tie_breaker) { struct wpabuf *buf; - u8 *len; + struct wpabuf *subelems; u8 group_capab; size_t extra = 0; u16 pw_id; @@ -314,8 +328,13 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); - len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_status(buf, status); + subelems = wpabuf_alloc(500); + if (!subelems) { + wpabuf_free(buf); + return NULL; + } + + p2p_buf_add_status(subelems, status); group_capab = 0; if (peer && peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { @@ -329,24 +348,26 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(subelems, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); + p2p_buf_add_go_intent(subelems, (p2p->go_intent << 1) | tie_breaker); + p2p_buf_add_config_timeout(subelems, p2p->go_timeout, + p2p->client_timeout); if (p2p->override_pref_op_class) { p2p_dbg(p2p, "Override operating channel preference"); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(subelems, p2p->cfg->country, p2p->override_pref_op_class, p2p->override_pref_channel); } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(subelems, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); } - p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_intended_addr(subelems, p2p->intended_addr); + if (p2p->num_pref_freq) { bool go = (peer && peer->go_state == LOCAL_GO) || p2p->go_intent == 15; @@ -360,12 +381,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p->allow_6ghz); } if (status || peer == NULL) { - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &pref_chanlist, false); } else if (peer->go_state == REMOTE_GO) { is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &pref_chanlist, is_6ghz_capab); } else { struct p2p_channels res; @@ -374,22 +395,22 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_channels_intersect(&pref_chanlist, &peer->channels, &res); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, - is_6ghz_capab); + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &res, + is_6ghz_capab); } - p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_add_device_info(subelems, p2p, peer); if (peer && peer->go_state == LOCAL_GO) { - p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p_buf_add_group_id(subelems, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } - p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); if (peer && peer->oob_pw_id) pw_id = peer->oob_pw_id; - if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + if (peer && !peer->p2p2 && p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(subelems); wpabuf_free(buf); return NULL; } @@ -402,6 +423,8 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); + buf = wpabuf_concat(buf, p2p_encaps_ie(subelems, P2P_IE_VENDOR_TYPE)); + wpabuf_free(subelems); return buf; } @@ -711,12 +734,12 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, return; /* Obtain our preferred frequency list from driver based on P2P role. */ - size = P2P_MAX_PREF_CHANNELS; + size = ARRAY_SIZE(p2p->pref_freq_list); if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, - &p2p->num_pref_freq, + &size, p2p->pref_freq_list)) return; - size = p2p->num_pref_freq; + p2p->num_pref_freq = size; if (!size) return; /* Filter out frequencies that are not acceptable for P2P use */ @@ -724,6 +747,10 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, p2p_is_peer_6ghz_capab(p2p, dev->info.p2p_device_addr); i = 0; while (i < size) { + bool is_dfs_freq; + + is_dfs_freq = p2p->cfg->is_p2p_dfs_chan( + p2p->cfg->cb_ctx, p2p->pref_freq_list[i].freq, 0, 0); if (p2p_freq_to_channel(p2p->pref_freq_list[i].freq, &op_class, &op_channel) < 0 || (!p2p_channels_includes(&p2p->cfg->channels, @@ -731,7 +758,10 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, op_channel))) || (is_6ghz_freq(p2p->pref_freq_list[i].freq) && - !is_6ghz_capab)) { + !is_6ghz_capab) || + (is_dfs_freq && + !is_dfs_freq_allowed(p2p, go, + p2p->pref_freq_list[i].freq))) { p2p_dbg(p2p, "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", p2p->pref_freq_list[i].freq, go); @@ -762,12 +792,22 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, size, txt); /* - * Check if peer's preference of operating channel is in - * our preferred channel list. + * Check if peer's preference of operating channel is in our preferred + * channel list. When local device is GO and assisted DFS is active, + * also treat the locally connected DFS frequency as a match candidate. + * The loop scans pref_freq_list[] from the highest to the lowest driver + * priority and breaks on the first allowed match, so the DFS channel + * wins over the peer's oper_freq only when the driver ranks it at a + * lower index (higher preference). If the peer's channel appears first + * in the list it wins instead, which is correct because the driver + * explicitly prefers it over the DFS channel in that case. */ for (i = 0; i < size; i++) { - if (p2p->pref_freq_list[i].freq == - (unsigned int) dev->oper_freq && + if ((p2p->pref_freq_list[i].freq == + (unsigned int) dev->oper_freq || + (go && p2p->dfs_ap_connected && + p2p->pref_freq_list[i].freq == + (unsigned int) p2p->sta_connected_freq)) && p2p_pref_freq_allowed(&p2p->pref_freq_list[i], go)) break; } @@ -799,21 +839,21 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, } -void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +struct wpabuf * p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq, + bool p2p2) { struct p2p_device *dev = NULL; struct wpabuf *resp; struct p2p_message msg; u8 status = P2P_SC_FAIL_INVALID_PARAMS; int tie_breaker = 0; - int freq; p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) - return; + return NULL; if (!msg.capability) { p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); @@ -888,7 +928,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); - return; + return NULL; } goto fail; } @@ -920,7 +960,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; } else if (dev == NULL || - (dev->wps_method == WPS_NOT_READY && + (dev->wps_method == WPS_NOT_READY && !p2p2 && (p2p->authorized_oob_dev_pw_id == 0 || p2p->authorized_oob_dev_pw_id != msg.dev_password_id))) { @@ -966,7 +1006,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); - return; + return NULL; } if (dev->go_neg_req_sent && @@ -974,7 +1014,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent"); p2p_parse_free(&msg); - return; + return NULL; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); @@ -991,6 +1031,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; } + if (p2p2) + goto skip; + switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); @@ -1058,6 +1101,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; } +skip: if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; @@ -1097,18 +1141,8 @@ fail: !tie_breaker); p2p_parse_free(&msg); if (resp == NULL) - return; - p2p_dbg(p2p, "Sending GO Negotiation Response"); - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); - wpabuf_free(resp); - return; - } + return NULL; + if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; @@ -1126,6 +1160,33 @@ fail: } else p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE_FAILURE; + return resp; +} + + +void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + int freq; + struct wpabuf *resp; + + resp = p2p_process_go_neg_req(p2p, sa, data, len, rx_freq, false); + if (!resp) + return; + + p2p_dbg(p2p, "Sending GO Negotiation Response"); + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + return; + } + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 100) < 0) { @@ -1142,7 +1203,7 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, const u8 *resp_chan, int go) { struct wpabuf *buf; - u8 *len; + struct wpabuf *subelems; struct p2p_channels res; u8 group_capab; size_t extra = 0; @@ -1164,8 +1225,13 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); - len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_status(buf, status); + subelems = wpabuf_alloc(500); + if (!subelems) { + wpabuf_free(buf); + return NULL; + } + + p2p_buf_add_status(subelems, status); group_capab = 0; if (peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { @@ -1179,25 +1245,26 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(subelems, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); if (go || resp_chan == NULL) - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(subelems, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); else - p2p_buf_add_operating_channel(buf, (const char *) resp_chan, + p2p_buf_add_operating_channel(subelems, + (const char *) resp_chan, resp_chan[3], resp_chan[4]); p2p_channels_intersect(&p2p->channels, &peer->channels, &res); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, is_6ghz_capab); + p2p_buf_add_channel_list(subelems, p2p->cfg->country, &res, + is_6ghz_capab); if (go) { - p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p_buf_add_group_id(subelems, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } - p2p_buf_update_ie_hdr(buf, len); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) @@ -1207,36 +1274,40 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + buf = wpabuf_concat(buf, p2p_encaps_ie(subelems, P2P_IE_VENDOR_TYPE)); + wpabuf_free(subelems); return buf; } -void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +struct wpabuf * p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, + int rx_freq, bool p2p2) { struct p2p_device *dev; int go = -1; struct p2p_message msg; u8 status = P2P_SC_SUCCESS; int freq; + struct wpabuf *conf = NULL; p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); - if (dev == NULL || dev->wps_method == WPS_NOT_READY || + if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); - return; + return NULL; } if (p2p_parse(data, len, &msg)) - return; + return NULL; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); - return; + return NULL; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; p2p_update_peer_6ghz_capab(dev, &msg); @@ -1245,7 +1316,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); - return; + return NULL; } if (!msg.status) { @@ -1274,7 +1345,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); - return; + return NULL; } if (!msg.capability) { @@ -1375,6 +1446,9 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } else dev->oper_freq = 0; + if (p2p2) + goto skip; + switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); @@ -1430,6 +1504,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, goto fail; } +skip: if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; @@ -1454,8 +1529,10 @@ fail: go); p2p_parse_free(&msg); if (dev->go_neg_conf == NULL) - return; - p2p_dbg(p2p, "Sending GO Negotiation Confirm"); + return NULL; + + conf = wpabuf_dup(dev->go_neg_conf); + if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; @@ -1469,7 +1546,39 @@ fail: dev->go_neg_conf_freq = freq; dev->go_neg_conf_sent = 0; - if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, + if (status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "GO Negotiation failed"); + dev->status = status; + } + + return conf; +} + + +void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + int freq; + struct p2p_device *dev; + struct wpabuf *conf; + + conf = p2p_process_go_neg_resp(p2p, sa, data, len, rx_freq, false); + if (!conf) + return; + wpabuf_free(conf); + + dev = p2p_get_device(p2p, sa); + if (!dev) + return; + + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = dev->listen_freq; + + if (dev->go_neg_conf && + p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(dev->go_neg_conf), wpabuf_len(dev->go_neg_conf), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); @@ -1477,15 +1586,14 @@ fail: p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } else dev->go_neg_conf_sent++; - if (status != P2P_SC_SUCCESS) { - p2p_dbg(p2p, "GO Negotiation failed"); - p2p_go_neg_failed(p2p, status); - } + + if (dev->status != P2P_SC_SUCCESS) + p2p_go_neg_failed(p2p, dev->status); } -void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len) +void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, bool p2p2) { struct p2p_device *dev; struct p2p_message msg; @@ -1493,7 +1601,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); - if (dev == NULL || dev->wps_method == WPS_NOT_READY || + if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c index c036f92a3cba..2659787f372f 100644 --- a/contrib/wpa/src/p2p/p2p_group.c +++ b/contrib/wpa/src/p2p/p2p_group.c @@ -205,11 +205,28 @@ static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) } +struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p, + struct wpabuf *p2p2_ie, int freq) +{ + u8 *len; + + wpabuf_put_u8(p2p2_ie, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(p2p2_ie, 1); + wpabuf_put_be32(p2p2_ie, P2P2_IE_VENDOR_TYPE); + wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header"); + p2p_buf_add_pcea(p2p2_ie, p2p); + *len = (u8 *) wpabuf_put(p2p2_ie, 0) - len - 1; + + return p2p2_ie; +} + + static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; u8 *len; size_t extra = 0; + struct wpabuf *p2p2_ie; #ifdef CONFIG_WIFI_DISPLAY if (group->p2p->wfd_ie_beacon) @@ -220,7 +237,7 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]) extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]); - ie = wpabuf_alloc(257 + extra); + ie = wpabuf_alloc(500 + extra); if (ie == NULL) return NULL; @@ -240,6 +257,17 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) p2p_group_add_noa(ie, group->noa); p2p_buf_update_ie_hdr(ie, len); + if (group->cfg->p2p2) { + p2p2_ie = wpabuf_alloc(255); + if (!p2p2_ie) { + wpabuf_free(ie); + return NULL; + } + + p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq); + ie = wpabuf_concat(p2p2_ie, ie); + } + return ie; } @@ -443,6 +471,7 @@ void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) { struct wpabuf *p2p_subelems, *ie; + struct wpabuf *p2p2_ie; p2p_subelems = wpabuf_alloc(500); if (p2p_subelems == NULL) @@ -474,7 +503,16 @@ static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) ie = wpabuf_concat(wfd, ie); } #endif /* CONFIG_WIFI_DISPLAY */ + if (group->cfg->p2p2) { + p2p2_ie = wpabuf_alloc(255); + if (!p2p2_ie) { + wpabuf_free(ie); + return NULL; + } + p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq); + ie = wpabuf_concat(p2p2_ie, ie); + } return ie; } @@ -648,6 +686,7 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) struct wpabuf *resp; u8 *rlen; size_t extra = 0; + struct wpabuf *p2p2_ie; #ifdef CONFIG_WIFI_DISPLAY if (group->wfd_ie) @@ -683,6 +722,17 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) p2p_buf_add_status(resp, status); p2p_buf_update_ie_hdr(resp, rlen); + if (group->cfg->p2p2) { + p2p2_ie = wpabuf_alloc(255); + if (!p2p2_ie) { + wpabuf_free(resp); + return NULL; + } + + p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq); + resp = wpabuf_concat(p2p2_ie, resp); + } + return resp; } diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index 0a487e031627..cae7c584ff47 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -36,6 +36,33 @@ enum p2p_go_state { REMOTE_GO }; +/* Enumeration for P2P device current role */ +enum p2p_role { + P2P_ROLE_IDLE = 0, + P2P_ROLE_PAIRING_INITIATOR, + P2P_ROLE_PAIRING_RESPONDER, +}; + +/** + * struct bootstrap_params - P2P Device bootstrap request parameters + */ +struct p2p_bootstrap_params { + /* Bootstrap method */ + u16 bootstrap_method; + + /* Status code */ + enum p2p_status_code status; + + /* Cookie for comeback */ + u8 cookie[50]; + + /* Cookie length */ + size_t cookie_len; + + /* Comeback time in TUs after which receiver is requested to retry */ + int comeback_after; +}; + /** * struct p2p_device - P2P Device data (internal to P2P module) */ @@ -150,6 +177,35 @@ struct p2p_device { int sd_pending_bcast_queries; bool support_6ghz; + + /* Supports P2P2 */ + bool p2p2; + + /* Requested bootstrap method */ + u16 req_bootstrap_method; + + /* Bootstrap parameters received from peer */ + struct p2p_bootstrap_params *bootstrap_params; + + /* Password for P2P2 GO negotiation */ + char password[100]; + + /* PASN data structure */ + struct pasn_data *pasn; + struct wpabuf *action_frame_wrapper; + + /* Device role */ + enum p2p_role role; + + /* Invitation parameters for P2P2 */ + bool inv_reject; + u8 inv_status; + int inv_freq; + int inv_peer_oper_freq; + u8 inv_bssid[ETH_ALEN]; + u8 inv_ssid[SSID_MAX_LEN]; + size_t inv_ssid_len; + bool inv_all_channels; }; struct p2p_sd_query { @@ -160,6 +216,41 @@ struct p2p_sd_query { struct wpabuf *tlvs; }; +/* P2P Device Identity Key parameters */ +struct p2p_id_key { + /* AKMP used for DevIK derviation */ + int akmp; + /* Cipher version type */ + int cipher_version; + /* DevIK expiration time in hours */ + u32 expiration; + /* Buffer to hold the DevIK */ + u8 dik_data[DEVICE_IDENTITY_KEY_MAX_LEN]; + /* Length of DevIK */ + size_t dik_len; + /* Nonce used in DIRA attribute */ + u8 dira_nonce[DEVICE_IDENTITY_NONCE_LEN]; + /* Length of nonce */ + size_t dira_nonce_len; + /* Tag computed for nonce using NIK */ + u8 dira_tag[DEVICE_IDENTITY_TAG_LEN]; + /* Length of tag in octets */ + size_t dira_tag_len; +}; + +struct p2p_pairing_info { + /* P2P device own address */ + u8 own_addr[ETH_ALEN]; + /* device capability to enable pairing setup */ + bool enable_pairing_setup; + /* device capability to enable pairing cache */ + bool enable_pairing_cache; + /* device supported bootstrapping */ + u16 supported_bootstrap; + /* P2P Device Identity Key info */ + struct p2p_id_key dev_ik; +}; + /** * struct p2p_data - P2P module data (internal to P2P module) */ @@ -554,6 +645,75 @@ struct p2p_data { bool p2p_6ghz_capable; bool include_6ghz; bool allow_6ghz; + + struct p2p_pairing_info *pairing_info; + + /* Pairing initiator PMKSA cache */ + struct rsn_pmksa_cache *initiator_pmksa; + /* Pairing responder PMKSA cache */ + struct rsn_pmksa_cache *responder_pmksa; + + /* DevIK variables: Cipher version, DevIK, and its lifetime + * These are fetched from the P2P2 included in the PASN Encrypted Data + * element during P2P2 group negotiation with PASN Authentication + * frames. These values are stored in struct p2p_data for an ongoing GO + * negotiation or join-a-group operation with the assumption that these + * operations cannot happen in parallel with multiple peers. After + * successful group formation and connection, these are moved to + * wpa_supplicant configuration if the connection is persistent. */ + u8 dik_cipher_version; + u8 peer_dik_data[DEVICE_IDENTITY_KEY_MAX_LEN]; + size_t peer_dik_len; + unsigned int peer_dik_lifetime; + + /* Password used during an ongoing group formation after opportunistic + * PASN authentication or while joining an existing group. This will be + * moved to a more permanent location from struct p2p_data at the + * conclusion of a successful pairing. */ + char dev_sae_password[100]; + char peer_sae_password[100]; + + /* Variable used to know the role of the device in a given instance. + * go_role variable is set while authorizing a P2P Client for PASN + * authentication with predefined GO intent value for GO (15 for + * P2P-GO). Once the authentication is completed and security + * configuration is done, this variable is reset to false. + */ + bool go_role; + +#ifdef CONFIG_TESTING_OPTIONS + /** + * PASN PTK of recent auth + */ + u8 pasn_ptk[128]; + + /** + * PASN PTK length + */ + size_t pasn_ptk_len; +#endif /* CONFIG_TESTING_OPTIONS */ + + bool usd_service; + u8 p2p_service_hash[P2PS_HASH_LEN]; + + /** + * dfs_ap_connected - Whether STA is connected to a DFS AP + */ + bool dfs_ap_connected; + + /** + * sta_connected_freq - STA connection frequency + * + * Valid only when dfs_ap_connected is true. + */ + int sta_connected_freq; + + /** + * sta_connected_chan_width - STA connection channel width + * + * Valid only when dfs_ap_connected is true. + */ + enum chan_width sta_connected_chan_width; }; /** @@ -561,6 +721,7 @@ struct p2p_data { */ struct p2p_message { struct wpabuf *p2p_attributes; + struct wpabuf *p2p2_attributes; struct wpabuf *wps_attributes; struct wpabuf *wfd_subelems; @@ -659,6 +820,21 @@ struct p2p_message { const u8 *pref_freq_list; size_t pref_freq_list_len; + + const u8 *pcea_info; + size_t pcea_info_len; + + const u8 *pbma_info; + size_t pbma_info_len; + + const u8 *action_frame_wrapper; + size_t action_frame_wrapper_len; + + const u8 *dira; + size_t dira_len; + + const u8 *wlan_ap_info; + size_t wlan_ap_info_len; }; @@ -748,6 +924,7 @@ void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); +u8 * p2p_buf_add_p2p2_ie_hdr(struct wpabuf *buf); void p2p_buf_add_status(struct wpabuf *buf, u8 status); void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, struct p2p_device *peer); @@ -788,11 +965,18 @@ void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask); void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, const u8 *ssid, size_t ssid_len); +void p2p_buf_add_pcea(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_buf_add_pbma(struct wpabuf *buf, u16 bootstrap, const u8 *cookie, + size_t cookie_len, int comeback_after); +void p2p_buf_add_dira(struct wpabuf *buf, struct p2p_data *p2p); int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); void p2p_buf_add_pref_channel_list(struct wpabuf *buf, const struct weighted_pcl *pref_freq_list, unsigned int size); +struct wpabuf * p2p_encaps_ie(const struct wpabuf *subelems, u32 ie_type); +struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p, + struct wpabuf *p2p2_ie, int freq); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -809,15 +993,23 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); /* p2p_go_neg.c */ +struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer); int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len); -void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); -void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); -void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len); +void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq); +void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq); +void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, bool p2p2); +struct wpabuf * p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq, + bool p2p2); +struct wpabuf * p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, + int rx_freq, bool p2p2); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, @@ -826,24 +1018,35 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, struct p2p_device *dev, struct p2p_message *msg); /* p2p_pd.c */ -void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, +void p2p_handle_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_handle_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq); -void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len); int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq); void p2p_reset_pending_pd(struct p2p_data *p2p); void p2ps_prov_free(struct p2p_data *p2p); +void p2p_process_pcea(struct p2p_data *p2p, struct p2p_message *msg, + struct p2p_device *dev); /* p2p_invitation.c */ -void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); +struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, + struct p2p_device *peer, + const u8 *go_dev_addr, int dev_pw_id); +void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_handle_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, + int rx_freq, bool p2p2); void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); -void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); +void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *dst, int success); +void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev); /* p2p_dev_disc.c */ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -884,7 +1087,6 @@ int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, const u8 *query_hash, u8 query_count); -void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); @@ -901,6 +1103,14 @@ void p2p_pref_channel_filter(const struct p2p_channels *a, struct p2p_channels *res, bool go); void p2p_sd_query_cb(struct p2p_data *p2p, int success); +void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *addr, int freq, bool verify, + bool derive_kek); +void p2p_buf_add_usd_service_hash(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_dfs_channel_filter(struct p2p_data *p2p, + const struct p2p_channels *p2p_chan, + struct p2p_channels *res, bool go); +bool is_dfs_freq_allowed(struct p2p_data *p2p, bool go, int freq); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c index 70a7f6fa684d..a24fbb686351 100644 --- a/contrib/wpa/src/p2p/p2p_invitation.c +++ b/contrib/wpa/src/p2p/p2p_invitation.c @@ -11,17 +11,19 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" +#include "crypto/sha384.h" +#include "common/wpa_common.h" +#include "pasn/pasn_common.h" #include "p2p_i.h" #include "p2p.h" -static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, - struct p2p_device *peer, - const u8 *go_dev_addr, - int dev_pw_id) +struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, + struct p2p_device *peer, + const u8 *go_dev_addr, int dev_pw_id) { - struct wpabuf *buf; - u8 *len; + struct wpabuf *buf, *elems, *p2p_ie; const u8 *dev_addr; size_t extra = 0; bool is_6ghz_capab; @@ -60,24 +62,29 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, peer->dialog_token); - len = p2p_buf_add_ie_hdr(buf); + elems = wpabuf_alloc(1000); + if (!elems) { + wpabuf_free(buf); + return NULL; + } + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) - p2p_buf_add_config_timeout(buf, 0, 0); + p2p_buf_add_config_timeout(elems, 0, 0); else - p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p_buf_add_config_timeout(elems, p2p->go_timeout, p2p->client_timeout); - p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? + p2p_buf_add_invitation_flags(elems, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || !(peer->flags & P2P_DEV_NO_PREF_CHAN)) - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(elems, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); if (p2p->inv_bssid_set) - p2p_buf_add_group_bssid(buf, p2p->inv_bssid); + p2p_buf_add_group_bssid(elems, p2p->inv_bssid); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels, + p2p_buf_add_channel_list(elems, p2p->cfg->country, &p2p->channels, is_6ghz_capab); if (go_dev_addr) dev_addr = go_dev_addr; @@ -85,9 +92,18 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, dev_addr = peer->info.p2p_device_addr; else dev_addr = p2p->cfg->dev_addr; - p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); - p2p_buf_add_device_info(buf, p2p, peer); - p2p_buf_update_ie_hdr(buf, len); + p2p_buf_add_group_id(elems, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); + p2p_buf_add_device_info(elems, p2p, peer); + + p2p_ie = p2p_encaps_ie(elems, P2P_IE_VENDOR_TYPE); + wpabuf_free(elems); + if (!p2p_ie) { + wpabuf_free(buf); + return NULL; + } + + wpabuf_put_buf(buf, p2p_ie); + wpabuf_free(p2p_ie); p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, p2p->num_pref_freq); @@ -100,7 +116,7 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); - if (dev_pw_id >= 0) { + if (dev_pw_id >= 0 && !peer->p2p2) { /* WSC IE in Invitation Request for NFC static handover */ p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); } @@ -114,10 +130,11 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, u8 dialog_token, u8 status, const u8 *group_bssid, u8 reg_class, u8 channel, - struct p2p_channels *channels) + struct p2p_channels *channels, + const u8 *ssid, + size_t ssid_len) { - struct wpabuf *buf; - u8 *len; + struct wpabuf *buf, *elems, *p2p_ie; size_t extra = 0; #ifdef CONFIG_WIFI_DISPLAY @@ -151,23 +168,49 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, dialog_token); - len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_status(buf, status); - p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ + elems = wpabuf_alloc(1000); + if (!elems) { + wpabuf_free(buf); + return NULL; + } + + p2p_buf_add_status(elems, status); + p2p_buf_add_config_timeout(elems, 0, 0); /* FIX */ if (reg_class && channel) - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(elems, p2p->cfg->country, reg_class, channel); if (group_bssid) - p2p_buf_add_group_bssid(buf, group_bssid); + p2p_buf_add_group_bssid(elems, group_bssid); + + if (ssid_len && ssid) { + const u8 *dev_addr; + + if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) + dev_addr = peer->info.p2p_device_addr; + else + dev_addr = p2p->cfg->dev_addr; + + p2p_buf_add_group_id(elems, dev_addr, ssid, ssid_len); + } + if (channels) { bool is_6ghz_capab; is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, channels, + p2p_buf_add_channel_list(elems, p2p->cfg->country, channels, is_6ghz_capab); } - p2p_buf_update_ie_hdr(buf, len); + + p2p_ie = p2p_encaps_ie(elems, P2P_IE_VENDOR_TYPE); + wpabuf_free(elems); + if (!p2p_ie) { + wpabuf_free(buf); + return NULL; + } + + wpabuf_put_buf(buf, p2p_ie); + wpabuf_free(p2p_ie); #ifdef CONFIG_WIFI_DISPLAY if (wfd_ie) @@ -181,18 +224,20 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, } -void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, + int rx_freq, bool p2p2) { struct p2p_device *dev; struct p2p_message msg; struct wpabuf *resp = NULL; u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - int freq; int go = 0; u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; u8 reg_class = 0, channel = 0; + const u8 *new_ssid = NULL; + size_t new_ssid_len = 0; struct p2p_channels all_channels, intersection, *channels = NULL; int persistent; @@ -202,7 +247,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) - return; + return NULL; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -268,7 +313,8 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, &go, group_bssid, &op_freq, persistent, &intersection, - msg.dev_password_id_present ? msg.dev_password_id : -1); + msg.dev_password_id_present ? msg.dev_password_id : -1, + p2p2, &new_ssid, &new_ssid_len); } if (go) { @@ -311,6 +357,17 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Own default op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); +#ifdef CONFIG_TESTING_OPTIONS + if (p2p->cfg->inv_op_class) { + /* Override configuration as a starting point */ + p2p->op_reg_class = p2p->cfg->inv_op_class; + p2p->op_channel = p2p->cfg->inv_op_channel; + p2p_dbg(p2p, + "Override Invitation op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + } +#endif /* CONFIG_TESTING_OPTIONS */ + /* Use peer preference if specified and compatible */ if (msg.operating_channel) { int req_freq; @@ -386,20 +443,8 @@ fail: else bssid = NULL; resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, - bssid, reg_class, channel, channels); - - if (resp == NULL) - goto out; - - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); - goto out; - } + bssid, reg_class, channel, channels, + new_ssid, new_ssid_len); /* * Store copy of invitation data to be used when processing TX status @@ -411,7 +456,12 @@ fail: p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; } else p2p->inv_group_bssid_ptr = NULL; - if (msg.group_id) { + + if (p2p2 && new_ssid_len) { + os_memcpy(p2p->inv_ssid, new_ssid, new_ssid_len); + p2p->inv_ssid_len = new_ssid_len; + os_memcpy(p2p->inv_go_dev_addr, p2p->cfg->dev_addr, ETH_ALEN); + } else if (msg.group_id) { if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) { os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN); @@ -424,6 +474,28 @@ fail: } p2p->inv_status = status; p2p->inv_op_freq = op_freq; + p2p_parse_free(&msg); + return resp; +} + + +void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + int freq; + struct wpabuf *resp; + + resp = p2p_process_invitation_req(p2p, sa, data, len, rx_freq, false); + if (!resp) + return; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) + p2p_dbg(p2p, "Unknown regulatory class/channel"); p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, @@ -432,9 +504,7 @@ fail: p2p_dbg(p2p, "Failed to send Action frame"); } -out: wpabuf_free(resp); - p2p_parse_free(&msg); } @@ -444,6 +514,8 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; struct p2p_message msg; struct p2p_channels intersection, *channels = NULL; + bool all_channels = false; + const u8 *go_dev_addr = NULL; p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); @@ -523,14 +595,17 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, #endif /* CONFIG_P2P_STRICT */ /* Try to survive without peer channel list */ channels = &p2p->channels; + all_channels = true; } else if (!msg.channel_list) { /* Non-success cases are not required to include Channel List */ channels = &p2p->channels; + all_channels = true; } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); p2p_parse_free(&msg); + dev->inv_reject = true; return; } else { p2p_channels_intersect(&p2p->channels, &dev->channels, @@ -559,19 +634,93 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, */ p2p_check_pref_chan(p2p, 0, dev, &msg); - p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, - msg.group_bssid, channels, sa, - freq, peer_oper_freq); - } + if (dev->p2p2) { + dev->inv_freq = freq; + dev->inv_status = *msg.status; + dev->inv_all_channels = all_channels; + dev->inv_peer_oper_freq = peer_oper_freq; + if (msg.group_bssid) + os_memcpy(dev->inv_bssid, msg.group_bssid, + ETH_ALEN); + if (msg.group_id) { + dev->inv_ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(dev->inv_ssid, + msg.group_id + ETH_ALEN, + dev->inv_ssid_len); - p2p_parse_free(&msg); + os_memcpy(p2p->invite_go_dev_addr_buf, + msg.group_id, ETH_ALEN); + p2p->invite_go_dev_addr = + p2p->invite_go_dev_addr_buf; + go_dev_addr = p2p->invite_go_dev_addr; + } + goto out; + } + + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, + NULL, 0, + msg.group_bssid, channels, sa, + freq, peer_oper_freq, NULL, NULL, + 0, go_dev_addr); + } p2p_clear_timeout(p2p); p2p_set_state(p2p, P2P_IDLE); p2p->invite_peer = NULL; + +out: + p2p_parse_free(&msg); } +#ifdef CONFIG_PASN +void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev) +{ + size_t pmk_len = 0; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + struct p2p_channels intersection; + const struct p2p_channels *inv_channels; + + if (!p2p || !dev || dev->inv_reject || !dev->pasn) + return; + + if (dev->inv_all_channels) { + inv_channels = &p2p->channels; + } else { + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + inv_channels = &intersection; + } + + pasn_initiator_pmksa_cache_get(dev->pasn->pmksa, dev->pasn->peer_addr, + pmkid, pmk, &pmk_len); + + wpa_pasn_reset(dev->pasn); + p2p_dbg(p2p, "Invitation connect: msg status %d", dev->inv_status); + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, dev->inv_status, + dev->inv_ssid, dev->inv_ssid_len, + dev->inv_bssid, inv_channels, + dev->info.p2p_device_addr, + dev->inv_freq, + dev->inv_peer_oper_freq, pmkid, + pmk, pmk_len, + p2p->invite_go_dev_addr); + + /* Reset PMK and PMKID from stack */ + forced_memzero(pmkid, sizeof(pmkid)); + forced_memzero(pmk, sizeof(pmk)); + + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + os_memset(dev->inv_ssid, 0, sizeof(dev->inv_ssid)); + dev->inv_ssid_len = 0; + p2p->invite_peer = NULL; +} +#endif /* CONFIG_PASN */ + + int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id) { @@ -640,8 +789,26 @@ void p2p_invitation_req_cb(struct p2p_data *p2p, int success) } -void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) +void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *peer, int success) { + size_t pmk_len = 0; + const u8 *pmkid = NULL, *pmk = NULL; + +#ifdef CONFIG_PASN + u8 _pmkid[PMKID_LEN]; + u8 _pmk[PMK_LEN_MAX]; + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer); + if (dev && dev->pasn && dev->pasn->pmksa) { + pasn_responder_pmksa_cache_get(dev->pasn->pmksa, + dev->pasn->peer_addr, _pmkid, + _pmk, &pmk_len); + pmkid = _pmkid; + pmk = _pmk; + } +#endif /* CONFIG_PASN */ + p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); @@ -655,15 +822,23 @@ void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) p2p->inv_ssid, p2p->inv_ssid_len, p2p->inv_go_dev_addr, p2p->inv_status, - p2p->inv_op_freq); + p2p->inv_op_freq, pmkid, pmk, + pmk_len); } + +#ifdef CONFIG_PASN + /* Reset PMK and PMKID from stack */ + forced_memzero(_pmkid, sizeof(_pmkid)); + forced_memzero(_pmk, sizeof(_pmk)); +#endif /* CONFIG_PASN */ } int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group, unsigned int pref_freq, int dev_pw_id) + int persistent_group, unsigned int pref_freq, int dev_pw_id, + bool p2p2) { struct p2p_device *dev; @@ -731,5 +906,8 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; + if (p2p2) + return 0; + return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c index 07d6ca022ff9..cd3332d6ba42 100644 --- a/contrib/wpa/src/p2p/p2p_parse.c +++ b/contrib/wpa/src/p2p/p2p_parse.c @@ -417,6 +417,60 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, msg->persistent_ssid_len)); break; } + case P2P_ATTR_CAPABILITY_EXTENSION: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short PCEA (length %d)", + len); + return -1; + } + msg->pcea_info = data; + msg->pcea_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * PCEA (length=%u)", len); + break; + case P2P_ATTR_PAIRING_AND_BOOTSTRAPPING: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short PBMA (length %d)", + len); + return -1; + } + msg->pbma_info = data; + msg->pbma_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * PBMA (length=%u)", len); + break; + case P2P_ATTR_ACTION_FRAME_WRAPPER: + if (len < 2) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Action Frame Wrapper attribute (length %d)", + len); + return -1; + } + msg->action_frame_wrapper = data; + msg->action_frame_wrapper_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Action frame wrapper (length=%u)", + len); + break; + case P2P_ATTR_DEVICE_IDENTITY_RESOLUTION: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short DIRA (length %d)", + len); + return -1; + } + msg->dira = data; + msg->dira_len = len; + wpa_printf(MSG_DEBUG, "P2P: * DIRA (length=%u)", len); + break; + case P2P_ATTR_WLAN_AP_INFORMATION: + /* One or more AP Info fields (each being 12 octets) is required + * to be included. */ + if (len < 12) { + wpa_printf(MSG_DEBUG, + "P2P: Too short WLAN AP info (length %d)", + len); + return -1; + } + msg->wlan_ap_info = data; + msg->wlan_ap_info_len = len; + break; default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); @@ -573,6 +627,18 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) return -1; } + msg->p2p2_attributes = ieee802_11_vendor_ie_concat(data, len, + P2P2_IE_VENDOR_TYPE); + if (msg->p2p2_attributes && + p2p_parse_p2p_ie(msg->p2p2_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P2 IE data"); + if (msg->p2p2_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P2 IE data", + msg->p2p2_attributes); + p2p_parse_free(msg); + return -1; + } + #ifdef CONFIG_WIFI_DISPLAY if (elems.wfd) { msg->wfd_subelems = ieee802_11_vendor_ie_concat( @@ -647,6 +713,8 @@ void p2p_parse_free(struct p2p_message *msg) { wpabuf_free(msg->p2p_attributes); msg->p2p_attributes = NULL; + wpabuf_free(msg->p2p2_attributes); + msg->p2p2_attributes = NULL; wpabuf_free(msg->wps_attributes); msg->wps_attributes = NULL; #ifdef CONFIG_WIFI_DISPLAY diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c index 542521edb1d6..d9bff5a68cee 100644 --- a/contrib/wpa/src/p2p/p2p_pd.c +++ b/contrib/wpa/src/p2p/p2p_pd.c @@ -181,6 +181,64 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, } +static struct wpabuf * p2p_build_prov_disc_bootstrap_req(struct p2p_data *p2p, + struct p2p_device *dev) +{ + struct wpabuf *buf; + u8 *len; + size_t cookie_len = 0; + const u8 *cookie = NULL; + u8 dialog_token = dev->dialog_token; + u8 group_capab; + + buf = wpabuf_alloc(1000); + if (!buf) + return NULL; + + p2p_dbg(p2p, "P2P2: Building bootstrapping PD Request"); + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + + group_capab = 0; + + if (p2p->num_groups) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (dev->bootstrap_params) { + cookie = dev->bootstrap_params->cookie; + cookie_len = dev->bootstrap_params->cookie_len; + + if (dev->bootstrap_params->status == P2P_SC_COMEBACK) + p2p_buf_add_status(buf, dev->bootstrap_params->status); + } + + p2p_buf_update_ie_hdr(buf, len); + + len = p2p_buf_add_p2p2_ie_hdr(buf); + + p2p_buf_add_pcea(buf, p2p); + p2p_buf_add_pbma(buf, dev->req_bootstrap_method, cookie, cookie_len, 0); + + p2p_buf_update_ie_hdr(buf, len); + + wpa_printf(MSG_DEBUG, "P2P2: Added PCEA and PBMA in PD Request"); + return buf; +} + + static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join) @@ -249,6 +307,42 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, } +static struct wpabuf * +p2p_build_prov_disc_bootstrap_resp(struct p2p_data *p2p, struct p2p_device *dev, + u8 dialog_token, enum p2p_status_code status) +{ + struct wpabuf *buf; + u8 *cookie = NULL; + size_t cookie_len = 0; + int comeback_after = 0; + u8 *len; + + buf = wpabuf_alloc(1000); + if (!buf) + return NULL; + + p2p_dbg(p2p, "P2P2: Building boostrapping PD Response"); + if (status == P2P_SC_COMEBACK && dev->bootstrap_params) { + cookie = dev->bootstrap_params->cookie; + cookie_len = dev->bootstrap_params->cookie_len; + comeback_after = dev->bootstrap_params->comeback_after; + } + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + + len = p2p_buf_add_p2p2_ie_hdr(buf); + + p2p_buf_add_status(buf, status); + p2p_buf_add_pcea(buf, p2p); + p2p_buf_add_pbma(buf, dev->req_bootstrap_method, cookie, cookie_len, + comeback_after); + + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, struct p2p_device *dev, u8 dialog_token, @@ -563,10 +657,234 @@ do { \ } -void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +void p2p_process_pcea(struct p2p_data *p2p, struct p2p_message *msg, + struct p2p_device *dev) +{ + const u8 *pos, *end; + u8 cap_info_len; + + if (!p2p || !dev || !msg || !msg->pcea_info) + return; + + pos = msg->pcea_info; + end = pos + msg->pcea_info_len; + dev->info.pcea_cap_info = WPA_GET_LE16(pos); + cap_info_len = dev->info.pcea_cap_info & P2P_PCEA_LEN_MASK; + + /* Field length is (n-1), n in octets */ + if (end - pos < cap_info_len + 1) + return; + pos += cap_info_len + 1; + + if (dev->info.pcea_cap_info & P2P_PCEA_6GHZ) + dev->support_6ghz = true; + + if (dev->info.pcea_cap_info & P2P_PCEA_REG_INFO) { + if (end - pos < 1) { + p2p_dbg(p2p, "Truncated PCEA"); + return; + } + dev->info.reg_info = *pos++; + } + + if (dev->info.pcea_cap_info & P2P_PCEA_PASN_TYPE) { + if (end - pos < 1) { + p2p_dbg(p2p, "Truncated PCEA"); + return; + } + dev->info.pairing_config.pasn_type = *pos++; + } + + if (dev->info.pcea_cap_info & P2P_PCEA_PAIRING_CAPABLE) + dev->info.pairing_config.pairing_capable = true; + + if (dev->info.pcea_cap_info & P2P_PCEA_PAIRING_SETUP_ENABLED) + dev->info.pairing_config.enable_pairing_setup = true; + + if (dev->info.pcea_cap_info & P2P_PCEA_PMK_CACHING) { + dev->info.pairing_config.enable_pairing_cache = true; + } +} + + +static void p2p_process_prov_disc_bootstrap_req(struct p2p_data *p2p, + struct p2p_message *msg, + const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + struct p2p_device *dev; + int freq; + struct wpabuf *resp; + u16 bootstrap; + size_t cookie_len = 0; + const u8 *pos, *cookie; + enum p2p_status_code status = P2P_SC_FAIL_INVALID_PARAMS; + + p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR + " with bootstrapping Attribute (freq=%d)", + MAC2STR(sa), rx_freq); + + dev = p2p_get_device(p2p, sa); + if (!dev) { + p2p_dbg(p2p, "Provision Discovery Request from unknown peer " + MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data, len, 0)) { + p2p_dbg(p2p, + "Provision Discovery Request add device failed " + MACSTR, MAC2STR(sa)); + return; + } + + dev = p2p_get_device(p2p, sa); + if (!dev) { + p2p_dbg(p2p, + "Provision Discovery device not found " + MACSTR, MAC2STR(sa)); + return; + } + } + dev->p2p2 = true; + + if (p2p->send_action_in_progress) { + p2p_dbg(p2p, "Dropping retry frame as response TX pending"); + return; + } + + p2p_update_peer_6ghz_capab(dev, msg); + + if (msg->pcea_info && msg->pcea_info_len >= 2) + p2p_process_pcea(p2p, msg, dev); + + pos = msg->pbma_info; + + if (msg->pbma_info_len > 2 && msg->status && + *msg->status == P2P_SC_COMEBACK) { + /* PBMA comeback request */ + cookie_len = *pos++; + if (msg->pbma_info_len < 1 + cookie_len) { + p2p_dbg(p2p, "Truncated PBMA"); + return; + } + cookie = pos; + + if (!dev->bootstrap_params || + dev->bootstrap_params->cookie_len != cookie_len || + os_memcmp(cookie, dev->bootstrap_params->cookie, + cookie_len) != 0) { + status = P2P_SC_FAIL_REJECTED_BY_USER; + goto out; + } + + bootstrap = dev->bootstrap_params->bootstrap_method; + + if (!dev->req_bootstrap_method) { + status = P2P_SC_COMEBACK; + goto out; + } + } else { + /* PBMA request */ + bootstrap = WPA_GET_LE16(pos); + + os_free(dev->bootstrap_params); + dev->bootstrap_params = NULL; + + if (!dev->req_bootstrap_method) { + dev->bootstrap_params = + os_zalloc(sizeof(struct p2p_bootstrap_params)); + if (!dev->bootstrap_params) + return; + dev->bootstrap_params->bootstrap_method = bootstrap; + dev->bootstrap_params->cookie_len = 4; + if (os_get_random(dev->bootstrap_params->cookie, + dev->bootstrap_params->cookie_len) < + 0) { + os_free(dev->bootstrap_params); + dev->bootstrap_params = NULL; + return; + } + dev->bootstrap_params->comeback_after = + p2p->cfg->comeback_after; + status = P2P_SC_COMEBACK; + if (p2p->cfg->bootstrap_req_rx) + p2p->cfg->bootstrap_req_rx(p2p->cfg->cb_ctx, + sa, bootstrap); + goto out; + } + } + + if (bootstrap == P2P_PBMA_PIN_CODE_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_PIN_CODE_KEYPAD) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_PIN_CODE_KEYPAD && + dev->req_bootstrap_method == P2P_PBMA_PIN_CODE_DISPLAY) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_PASSPHRASE_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_PASSPHRASE_KEYPAD) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_PASSPHRASE_KEYPAD && + dev->req_bootstrap_method == P2P_PBMA_PASSPHRASE_DISPLAY) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_NFC_TAG && + dev->req_bootstrap_method == P2P_PBMA_NFC_READER) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_NFC_READER && + dev->req_bootstrap_method == P2P_PBMA_NFC_TAG) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_QR_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_QR_SCAN) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_QR_SCAN && + dev->req_bootstrap_method == P2P_PBMA_QR_DISPLAY) + status = P2P_SC_SUCCESS; + else if (bootstrap == P2P_PBMA_OPPORTUNISTIC && + dev->req_bootstrap_method == P2P_PBMA_OPPORTUNISTIC) + status = P2P_SC_SUCCESS; + else + status = P2P_SC_FAIL_INVALID_PARAMS; + + wpa_printf(MSG_ERROR, "Bootstrap received %d", bootstrap); + + if (status == P2P_SC_SUCCESS) { + dev->role = P2P_ROLE_PAIRING_RESPONDER; +#ifdef CONFIG_PASN + p2p_pasn_initialize(p2p, dev, sa, rx_freq, false, true); +#endif /* CONFIG_PASN */ + } +out: + /* Send PD Bootstrapping Response for the PD Request */ + resp = p2p_build_prov_disc_bootstrap_resp(p2p, dev, msg->dialog_token, + status); + if (!resp) + return; + + p2p_dbg(p2p, "Sending Provision Discovery Bootstrap Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown operating class/channel"); + wpabuf_free(resp); + return; + } + p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, wpabuf_head(resp), + wpabuf_len(resp), 50) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + else + p2p->send_action_in_progress = 1; + + wpabuf_free(resp); +} + + +static void p2p_process_prov_disc_req(struct p2p_data *p2p, + struct p2p_message *msg, const u8 *sa, + const u8 *data, size_t len, int rx_freq) { - struct p2p_message msg; struct p2p_device *dev; int freq; enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; @@ -587,21 +905,17 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u8 remote_conncap; u16 method; - if (p2p_parse(data, len, &msg)) - return; - p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", - MAC2STR(sa), msg.wps_config_methods, rx_freq); - group_mac = msg.intended_addr; + MAC2STR(sa), msg->wps_config_methods, rx_freq); + group_mac = msg->intended_addr; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { p2p_dbg(p2p, "Provision Discovery Request from unknown peer " MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, - 0)) { + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data, len, 0)) { p2p_dbg(p2p, "Provision Discovery Request add device failed " MACSTR, MAC2STR(sa)); goto out; @@ -614,29 +928,29 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, MACSTR, MAC2STR(sa)); goto out; } - } else if (msg.wfd_subelems) { + } else if (msg->wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); - dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); } - p2p_update_peer_6ghz_capab(dev, &msg); + p2p_update_peer_6ghz_capab(dev, msg); - if (!msg.adv_id) { + if (!msg->adv_id) { allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; - if (!(msg.wps_config_methods & allowed_config_methods)) { + if (!(msg->wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ - if (msg.group_id) { + if (msg->group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_group_id_match( p2p->groups[i], - msg.group_id, msg.group_id_len)) + msg->group_id, msg->group_id_len)) break; } if (i == p2p->num_groups) { @@ -652,29 +966,29 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, * Set adv_id here, so in case of an error, a P2PS PD Response * will be sent. */ - adv_id = WPA_GET_LE32(msg.adv_id); - if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) { + adv_id = WPA_GET_LE32(msg->adv_id); + if (p2ps_validate_pd_req(p2p, msg, sa) < 0) { reject = P2P_SC_FAIL_INVALID_PARAMS; goto out; } - req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; + req_fcap = (struct p2ps_feature_capab *) msg->feature_cap; - os_memcpy(session_mac, msg.session_mac, ETH_ALEN); - os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + os_memcpy(session_mac, msg->session_mac, ETH_ALEN); + os_memcpy(adv_mac, msg->adv_mac, ETH_ALEN); - session_id = WPA_GET_LE32(msg.session_id); + session_id = WPA_GET_LE32(msg->session_id); - if (msg.conn_cap) - conncap = *msg.conn_cap; + if (msg->conn_cap) + conncap = *msg->conn_cap; /* * We need to verify a P2PS config methog in an initial PD * request or in a follow-on PD request with the status * SUCCESS_DEFERRED. */ - if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) && - !(msg.wps_config_methods & allowed_config_methods)) { + if ((!msg->status || *msg->status == P2P_SC_SUCCESS_DEFERRED) && + !(msg->wps_config_methods & allowed_config_methods)) { p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; @@ -690,18 +1004,18 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, P2P_DEV_PD_PEER_KEYPAD | P2P_DEV_PD_PEER_P2PS); - if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { + if (msg->wps_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; passwd_id = DEV_PW_USER_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + } else if (msg->wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; passwd_id = DEV_PW_REGISTRAR_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + } else if (msg->wps_config_methods & WPS_CONFIG_P2PS) { p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_P2PS; @@ -712,8 +1026,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, if (p2p->cfg->remove_stale_groups) { p2p->cfg->remove_stale_groups( p2p->cfg->cb_ctx, dev->info.p2p_device_addr, - msg.persistent_dev, - msg.persistent_ssid, msg.persistent_ssid_len); + msg->persistent_dev, + msg->persistent_ssid, msg->persistent_ssid_len); } reject = P2P_SC_SUCCESS; @@ -722,15 +1036,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, * End of a legacy P2P PD Request processing, from this point continue * with P2PS one. */ - if (!msg.adv_id) + if (!msg->adv_id) goto out; remote_conncap = conncap; - if (!msg.status) { + if (!msg->status) { unsigned int forced_freq, pref_freq; - if (!ether_addr_equal(p2p->cfg->dev_addr, msg.adv_mac)) { + if (!ether_addr_equal(p2p->cfg->dev_addr, msg->adv_mac)) { p2p_dbg(p2p, "P2PS PD adv mac does not match the local one"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; @@ -767,12 +1081,12 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, "Incompatible P2PS feature capability CPT bitmask"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; } else if (p2ps_adv->config_methods && - !(msg.wps_config_methods & + !(msg->wps_config_methods & p2ps_adv->config_methods)) { p2p_dbg(p2p, "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", p2ps_adv->config_methods, - msg.wps_config_methods); + msg->wps_config_methods); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; } else if (!p2ps_adv->state) { p2p_dbg(p2p, "P2PS state unavailable"); @@ -782,24 +1096,24 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; } - if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + if (msg->wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Keypad - always defer"); auto_accept = 0; } if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || - msg.persistent_dev) && conncap != P2PS_SETUP_NEW && - msg.channel_list && msg.channel_list_len && + msg->persistent_dev) && conncap != P2PS_SETUP_NEW && + msg->channel_list && msg->channel_list_len && p2p_peer_channels_check(p2p, &p2p->channels, dev, - msg.channel_list, - msg.channel_list_len) < 0) { + msg->channel_list, + msg->channel_list_len) < 0) { p2p_dbg(p2p, "No common channels - force deferred flow"); auto_accept = 0; } if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) || - msg.persistent_dev) && msg.operating_channel) { + msg->persistent_dev) && msg->operating_channel) { struct p2p_channels intersect; /* @@ -810,15 +1124,15 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, */ if (dev->channels.reg_classes == 0 || !p2p_channels_includes(&dev->channels, - msg.operating_channel[3], - msg.operating_channel[4])) { + msg->operating_channel[3], + msg->operating_channel[4])) { struct p2p_channels *ch = &dev->channels; os_memset(ch, 0, sizeof(*ch)); ch->reg_class[0].reg_class = - msg.operating_channel[3]; + msg->operating_channel[3]; ch->reg_class[0].channel[0] = - msg.operating_channel[4]; + msg->operating_channel[4]; ch->reg_class[0].channels = 1; ch->reg_classes = 1; } @@ -837,7 +1151,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, struct p2ps_provision *tmp; if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, - msg.wps_config_methods, + msg->wps_config_methods, session_mac, adv_mac) < 0) { reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; goto out; @@ -859,7 +1173,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, } } - if (!msg.status && !auto_accept && + if (!msg->status && !auto_accept && (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { struct p2ps_provision *tmp; @@ -869,7 +1183,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, } if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, - msg.wps_config_methods, + msg->wps_config_methods, session_mac, adv_mac) < 0) { reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; goto out; @@ -880,26 +1194,26 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, } /* Not a P2PS Follow-on PD */ - if (!msg.status) + if (!msg->status) goto out; - if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) { - reject = *msg.status; + if (*msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg->status; goto out; } - if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) + if (*msg->status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) goto out; if (p2p->p2ps_prov->adv_id != adv_id || - !ether_addr_equal(p2p->p2ps_prov->adv_mac, msg.adv_mac)) { + !ether_addr_equal(p2p->p2ps_prov->adv_mac, msg->adv_mac)) { p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Advertisement ID/MAC"); goto out; } if (p2p->p2ps_prov->session_id != session_id || - !ether_addr_equal(p2p->p2ps_prov->session_mac, msg.session_mac)) { + !ether_addr_equal(p2p->p2ps_prov->session_mac, msg->session_mac)) { p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC"); goto out; } @@ -930,7 +1244,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, else if (method & WPS_CONFIG_KEYPAD) method = WPS_CONFIG_DISPLAY; - if (!conncap || !(msg.wps_config_methods & method)) { + if (!conncap || !(msg->wps_config_methods & method)) { /* * Reject this "Deferred Accept* * if incompatible conncap or method @@ -941,11 +1255,11 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, "Incompatible P2PS feature capability CPT bitmask"); reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || - msg.persistent_dev) && conncap != P2PS_SETUP_NEW && - msg.channel_list && msg.channel_list_len && + msg->persistent_dev) && conncap != P2PS_SETUP_NEW && + msg->channel_list && msg->channel_list_len && p2p_peer_channels_check(p2p, &p2p->channels, dev, - msg.channel_list, - msg.channel_list_len) < 0) { + msg->channel_list, + msg->channel_list_len) < 0) { p2p_dbg(p2p, "No common channels in Follow-On Provision Discovery Request"); reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; @@ -957,10 +1271,10 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) { u8 tmp; - if (msg.operating_channel) + if (msg->operating_channel) dev->oper_freq = - p2p_channel_to_freq(msg.operating_channel[3], - msg.operating_channel[4]); + p2p_channel_to_freq(msg->operating_channel[3], + msg->operating_channel[4]); if ((conncap & P2PS_SETUP_GROUP_OWNER) && p2p_go_select_channel(p2p, dev, &tmp) < 0) @@ -973,7 +1287,7 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, out: if (reject == P2P_SC_SUCCESS || reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) - config_methods = msg.wps_config_methods; + config_methods = msg->wps_config_methods; else config_methods = 0; @@ -981,18 +1295,18 @@ out: * Send PD Response for an initial PD Request or for follow-on * PD Request with P2P_SC_SUCCESS_DEFERRED status. */ - if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) { - resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, + if (!msg->status || *msg->status == P2P_SC_SUCCESS_DEFERRED) { + resp = p2p_build_prov_disc_resp(p2p, dev, msg->dialog_token, reject, config_methods, adv_id, - msg.group_id, msg.group_id_len, - msg.persistent_ssid, - msg.persistent_ssid_len, + msg->group_id, + msg->group_id_len, + msg->persistent_ssid, + msg->persistent_ssid_len, (const u8 *) &resp_fcap, sizeof(resp_fcap)); - if (!resp) { - p2p_parse_free(&msg); + if (!resp) return; - } + p2p_dbg(p2p, "Sending Provision Discovery Response"); if (rx_freq > 0) freq = rx_freq; @@ -1002,7 +1316,6 @@ out: if (freq < 0) { p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); - p2p_parse_free(&msg); return; } p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; @@ -1017,10 +1330,8 @@ out: wpabuf_free(resp); } - if (!dev) { - p2p_parse_free(&msg); + if (!dev) return; - } freq = 0; if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) { @@ -1032,17 +1343,17 @@ out: if (!p2p->cfg->p2ps_prov_complete) { /* Don't emit anything */ - } else if (msg.status && *msg.status != P2P_SC_SUCCESS && - *msg.status != P2P_SC_SUCCESS_DEFERRED) { - reject = *msg.status; + } else if (msg->status && *msg->status != P2P_SC_SUCCESS && + *msg->status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg->status; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, sa, adv_mac, session_mac, NULL, adv_id, session_id, - 0, 0, msg.persistent_ssid, - msg.persistent_ssid_len, + 0, 0, msg->persistent_ssid, + msg->persistent_ssid_len, 0, 0, NULL, NULL, 0, freq, NULL, 0); - } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + } else if (msg->status && *msg->status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; p2p->p2ps_prov->conncap = conncap; @@ -1052,77 +1363,77 @@ out: sa, adv_mac, session_mac, NULL, adv_id, session_id, conncap, 0, - msg.persistent_ssid, - msg.persistent_ssid_len, 0, - 0, NULL, NULL, 0, freq, + msg->persistent_ssid, + msg->persistent_ssid_len, + 0, 0, NULL, NULL, 0, freq, NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, - *msg.status, + *msg->status, sa, adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, - msg.persistent_ssid, - msg.persistent_ssid_len, 0, - 0, NULL, + msg->persistent_ssid, + msg->persistent_ssid_len, + 0, 0, NULL, (const u8 *) &resp_fcap, sizeof(resp_fcap), freq, NULL, 0); - } else if (msg.status && p2p->p2ps_prov) { + } else if (msg->status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; - p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg->status, sa, adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, - msg.persistent_ssid, - msg.persistent_ssid_len, + msg->persistent_ssid, + msg->persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, sizeof(resp_fcap), freq, NULL, 0); - } else if (msg.status) { + } else if (msg->status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, - msg.persistent_ssid, - msg.persistent_ssid_len, + msg->persistent_ssid, + msg->persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, sizeof(resp_fcap), freq, - msg.group_id ? - msg.group_id + ETH_ALEN : NULL, - msg.group_id ? - msg.group_id_len - ETH_ALEN : 0); + msg->group_id ? + msg->group_id + ETH_ALEN : NULL, + msg->group_id ? + msg->group_id_len - ETH_ALEN : 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && - (!msg.session_info || !msg.session_info_len)) { - p2p->p2ps_prov->method = msg.wps_config_methods; + (!msg->session_info || !msg->session_info_len)) { + p2p->p2ps_prov->method = msg->wps_config_methods; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, - msg.persistent_ssid, - msg.persistent_ssid_len, + msg->persistent_ssid, + msg->persistent_ssid_len, 0, 1, NULL, (const u8 *) &resp_fcap, sizeof(resp_fcap), freq, NULL, 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { - size_t buf_len = msg.session_info_len; + size_t buf_len = msg->session_info_len; char *buf = os_malloc(2 * buf_len + 1); if (buf) { - p2p->p2ps_prov->method = msg.wps_config_methods; + p2p->p2ps_prov->method = msg->wps_config_methods; - utf8_escape((char *) msg.session_info, buf_len, + utf8_escape((char *) msg->session_info, buf_len, buf, 2 * buf_len + 1); p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, adv_mac, session_mac, group_mac, adv_id, session_id, conncap, passwd_id, - msg.persistent_ssid, msg.persistent_ssid_len, + msg->persistent_ssid, msg->persistent_ssid_len, 0, 1, buf, (const u8 *) &resp_fcap, sizeof(resp_fcap), freq, NULL, 0); @@ -1150,29 +1461,30 @@ out: * seeker: KEYPAD, response status: SUCCESS */ if (p2p->cfg->prov_disc_req && - ((reject == P2P_SC_SUCCESS && !msg.adv_id) || - (!msg.status && + ((reject == P2P_SC_SUCCESS && !msg->adv_id) || + (!msg->status && (reject == P2P_SC_SUCCESS || reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && passwd_id == DEV_PW_USER_SPECIFIED) || - (!msg.status && + (!msg->status && reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || (reject == P2P_SC_SUCCESS && - msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + msg->status && *msg->status == P2P_SC_SUCCESS_DEFERRED && passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { const u8 *dev_addr = sa; - if (msg.p2p_device_addr) - dev_addr = msg.p2p_device_addr; + if (msg->p2p_device_addr) + dev_addr = msg->p2p_device_addr; p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, - msg.wps_config_methods, - dev_addr, msg.pri_dev_type, - msg.device_name, msg.config_methods, - msg.capability ? msg.capability[0] : 0, - msg.capability ? msg.capability[1] : + msg->wps_config_methods, + dev_addr, msg->pri_dev_type, + msg->device_name, msg->config_methods, + msg->capability ? msg->capability[0] : 0, - msg.group_id, msg.group_id_len); + msg->capability ? msg->capability[1] : + 0, + msg->group_id, msg->group_id_len); } if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) @@ -1197,10 +1509,28 @@ out: break; } - if (msg.intended_addr) - os_memcpy(dev->interface_addr, msg.intended_addr, + if (msg->intended_addr) + os_memcpy(dev->interface_addr, msg->intended_addr, ETH_ALEN); } +} + + +void p2p_handle_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.pcea_info && msg.pbma_info) + p2p_process_prov_disc_bootstrap_req(p2p, &msg, sa, data + 1, + len - 1, rx_freq); + else + p2p_process_prov_disc_req(p2p, &msg, sa, data + 1, len - 1, + rx_freq); + p2p_parse_free(&msg); } @@ -1303,13 +1633,140 @@ static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, } -void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len) +static void p2p_process_prov_disc_bootstrap_resp(struct p2p_data *p2p, + struct p2p_message *msg, + const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + struct p2p_device *dev; + enum p2p_status_code status = P2P_SC_SUCCESS; + size_t cookie_len = 0; + const u8 *pos, *cookie; + u16 comeback_after; + u16 bootstrap = 0; + + /* Parse the P2P status present */ + if (msg->status) + status = *msg->status; + + p2p_dbg(p2p, "Received Provision Discovery Bootstrap Response from " + MACSTR, MAC2STR(sa)); + + dev = p2p_get_device(p2p, sa); + if (!dev || !dev->req_bootstrap_method) { + p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR + " with no pending request", MAC2STR(sa)); + return; + } + + p2p_update_peer_6ghz_capab(dev, msg); + + if (dev->dialog_token != msg->dialog_token) { + p2p_dbg(p2p, + "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", + msg->dialog_token, dev->dialog_token); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + os_free(dev->bootstrap_params); + dev->bootstrap_params = NULL; + + /* If the response is from the peer to whom a user initiated request + * was sent earlier, we reset that state information here. */ + if (p2p->user_initiated_pd && + ether_addr_equal(p2p->pending_pd_devaddr, sa)) + p2p_reset_pending_pd(p2p); + + if (status == P2P_SC_COMEBACK) { + /* PBMA comeback response */ + pos = msg->pbma_info; + if (msg->pbma_info_len < 2 + 1) + return; + comeback_after = WPA_GET_LE16(pos); + pos += 2; + cookie_len = *pos++; + if (msg->pbma_info_len < 2 + 1 + cookie_len) { + p2p_dbg(p2p, "Truncated PBMA"); + return; + } + + if (cookie_len > sizeof(dev->bootstrap_params->cookie)) { + p2p_dbg(p2p, "Too long PBMA cookie"); + return; + } + + cookie = pos; + + dev->bootstrap_params = + os_zalloc(sizeof(struct p2p_bootstrap_params)); + if (!dev->bootstrap_params) + return; + dev->bootstrap_params->cookie_len = cookie_len; + os_memcpy(dev->bootstrap_params->cookie, cookie, cookie_len); + dev->bootstrap_params->comeback_after = comeback_after; + dev->bootstrap_params->bootstrap_method = + dev->req_bootstrap_method; + dev->bootstrap_params->status = status; + + p2p->cfg->register_bootstrap_comeback(p2p->cfg->cb_ctx, sa, + comeback_after); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (p2p->cfg->bootstrap_rsp_rx) + p2p->cfg->bootstrap_rsp_rx(p2p->cfg->cb_ctx, sa, status, + rx_freq, + dev->req_bootstrap_method); + return; + } + + /* PBMA response */ + if (msg->pbma_info_len >= 2) + bootstrap = WPA_GET_LE16(msg->pbma_info); + + /* Overwrite the status if bootstrap method does not match */ + if (status == P2P_SC_SUCCESS && + !(bootstrap == P2P_PBMA_PIN_CODE_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_PIN_CODE_KEYPAD) && + !(bootstrap == P2P_PBMA_PIN_CODE_KEYPAD && + dev->req_bootstrap_method == P2P_PBMA_PIN_CODE_DISPLAY) && + !(bootstrap == P2P_PBMA_PASSPHRASE_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_PASSPHRASE_KEYPAD) && + !(bootstrap == P2P_PBMA_PASSPHRASE_KEYPAD && + dev->req_bootstrap_method == P2P_PBMA_PASSPHRASE_DISPLAY) && + !(bootstrap == P2P_PBMA_NFC_TAG && + dev->req_bootstrap_method == P2P_PBMA_NFC_READER) && + !(bootstrap == P2P_PBMA_NFC_READER && + dev->req_bootstrap_method == P2P_PBMA_NFC_TAG) && + !(bootstrap == P2P_PBMA_QR_DISPLAY && + dev->req_bootstrap_method == P2P_PBMA_QR_SCAN) && + !(bootstrap == P2P_PBMA_QR_SCAN && + dev->req_bootstrap_method == P2P_PBMA_QR_DISPLAY) && + !(bootstrap == P2P_PBMA_OPPORTUNISTIC && + dev->req_bootstrap_method == P2P_PBMA_OPPORTUNISTIC)) + status = P2P_SC_FAIL_INVALID_PARAMS; + + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + + if (p2p->cfg->bootstrap_rsp_rx) + p2p->cfg->bootstrap_rsp_rx(p2p->cfg->cb_ctx, sa, status, + rx_freq, dev->req_bootstrap_method); +} + + +static void p2p_process_prov_disc_resp(struct p2p_data *p2p, + struct p2p_message *msg, const u8 *sa, + const u8 *data, size_t len) { - struct p2p_message msg; struct p2p_device *dev; u16 report_config_methods = 0, req_config_methods; - u8 status = P2P_SC_SUCCESS; + enum p2p_status_code status = P2P_SC_SUCCESS; u32 adv_id = 0; u8 conncap = P2PS_SETUP_NEW; u8 adv_mac[ETH_ALEN]; @@ -1317,30 +1774,25 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, int passwd_id = DEV_PW_DEFAULT; int p2ps_seeker; - if (p2p_parse(data, len, &msg)) + if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, msg)) return; - if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { - p2p_parse_free(&msg); - return; - } - /* Parse the P2PS members present */ - if (msg.status) - status = *msg.status; + if (msg->status) + status = *msg->status; - group_mac = msg.intended_addr; + group_mac = msg->intended_addr; - if (msg.adv_mac) - os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + if (msg->adv_mac) + os_memcpy(adv_mac, msg->adv_mac, ETH_ALEN); else os_memset(adv_mac, 0, ETH_ALEN); - if (msg.adv_id) - adv_id = WPA_GET_LE32(msg.adv_id); + if (msg->adv_id) + adv_id = WPA_GET_LE32(msg->adv_id); - if (msg.conn_cap) { - conncap = *msg.conn_cap; + if (msg->conn_cap) { + conncap = *msg->conn_cap; /* Switch bits to local relative */ switch (conncap) { @@ -1355,25 +1807,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR " with config methods 0x%x", - MAC2STR(sa), msg.wps_config_methods); + MAC2STR(sa), msg->wps_config_methods); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR " with no pending request", MAC2STR(sa)); - p2p_parse_free(&msg); return; - } else if (msg.wfd_subelems) { + } else if (msg->wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); - dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); } - p2p_update_peer_6ghz_capab(dev, &msg); + p2p_update_peer_6ghz_capab(dev, msg); - if (dev->dialog_token != msg.dialog_token) { + if (dev->dialog_token != msg->dialog_token) { p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", - msg.dialog_token, dev->dialog_token); - p2p_parse_free(&msg); + msg->dialog_token, dev->dialog_token); return; } @@ -1398,14 +1848,13 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, ether_addr_equal(p2p->pending_pd_devaddr, sa)) p2p_reset_pending_pd(p2p); - if (msg.wps_config_methods != req_config_methods) { + if (msg->wps_config_methods != req_config_methods) { p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", - msg.wps_config_methods, req_config_methods); + msg->wps_config_methods, req_config_methods); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); - p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } @@ -1419,13 +1868,13 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, " accepted to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; passwd_id = DEV_PW_REGISTRAR_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + } else if (msg->wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " accepted to write our PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; passwd_id = DEV_PW_USER_SPECIFIED; - } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + } else if (msg->wps_config_methods & WPS_CONFIG_P2PS) { p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_P2PS; @@ -1444,23 +1893,23 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, * fails the flow would continue, although it would probably * fail. Same is true for the operating channel. */ - if (msg.channel_list && msg.channel_list_len && + if (msg->channel_list && msg->channel_list_len && p2p_peer_channels_check(p2p, &p2p->channels, dev, - msg.channel_list, - msg.channel_list_len) < 0) + msg->channel_list, + msg->channel_list_len) < 0) p2p_dbg(p2p, "P2PS PD Response - no common channels"); - if (msg.operating_channel) { + if (msg->operating_channel) { if (p2p_channels_includes(&p2p->channels, - msg.operating_channel[3], - msg.operating_channel[4]) && + msg->operating_channel[3], + msg->operating_channel[4]) && p2p_channels_includes(&dev->channels, - msg.operating_channel[3], - msg.operating_channel[4])) { + msg->operating_channel[3], + msg->operating_channel[4])) { dev->oper_freq = p2p_channel_to_freq( - msg.operating_channel[3], - msg.operating_channel[4]); + msg->operating_channel[3], + msg->operating_channel[4]); } else { p2p_dbg(p2p, "P2PS PD Response - invalid operating channel"); @@ -1492,11 +1941,12 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - conncap, passwd_id, msg.persistent_ssid, - msg.persistent_ssid_len, 1, 0, NULL, - msg.feature_cap, msg.feature_cap_len, freq, - msg.group_id ? msg.group_id + ETH_ALEN : NULL, - msg.group_id ? msg.group_id_len - ETH_ALEN : 0); + conncap, passwd_id, msg->persistent_ssid, + msg->persistent_ssid_len, 1, 0, NULL, + msg->feature_cap, msg->feature_cap_len, freq, + msg->group_id ? msg->group_id + ETH_ALEN : NULL, + msg->group_id ? msg->group_id_len - ETH_ALEN : + 0); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && @@ -1518,16 +1968,15 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, NULL, NULL, 0); } - if (msg.session_info && msg.session_info_len) { - size_t info_len = msg.session_info_len; + if (msg->session_info && msg->session_info_len) { + size_t info_len = msg->session_info_len; char *deferred_sess_resp = os_malloc(2 * info_len + 1); if (!deferred_sess_resp) { - p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } - utf8_escape((char *) msg.session_info, info_len, + utf8_escape((char *) msg->session_info, info_len, deferred_sess_resp, 2 * info_len + 1); if (p2p->cfg->prov_disc_fail) @@ -1549,17 +1998,14 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, P2P_PROV_DISC_REJECTED, adv_id, adv_mac, NULL); - p2p_parse_free(&msg); p2ps_prov_free(p2p); goto out; } /* Store the provisioning info */ - dev->wps_prov_info = msg.wps_config_methods; - if (msg.intended_addr) - os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); - - p2p_parse_free(&msg); + dev->wps_prov_info = msg->wps_config_methods; + if (msg->intended_addr) + os_memcpy(dev->interface_addr, msg->intended_addr, ETH_ALEN); out: dev->req_config_methods = 0; @@ -1603,6 +2049,24 @@ out: } +void p2p_handle_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.pcea_info && msg.pbma_info) + p2p_process_prov_disc_bootstrap_resp(p2p, &msg, sa, data + 1, + len - 1, rx_freq); + else + p2p_process_prov_disc_resp(p2p, &msg, sa, data + 1, len - 1); + + p2p_parse_free(&msg); +} + + int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, int join, int force_freq) { @@ -1632,7 +2096,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, /* TODO: use device discoverability request through GO */ } - if (p2p->p2ps_prov) { + if (!dev->p2p2 && p2p->p2ps_prov) { if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) { if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY) dev->req_config_methods = WPS_CONFIG_KEYPAD; @@ -1662,7 +2126,11 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, return -1; } - req = p2p_build_prov_disc_req(p2p, dev, join); + if (dev->p2p2) + req = p2p_build_prov_disc_bootstrap_req(p2p, dev); + else + req = p2p_build_prov_disc_req(p2p, dev, join); + if (req == NULL) return -1; @@ -1684,6 +2152,24 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, } +int p2p_set_req_bootstrap_method(struct p2p_data *p2p, const u8 *peer_addr, + u16 bootstrap) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + if (!dev) { + p2p_dbg(p2p, "Bootstrap request for peer " MACSTR + " not yet known", MAC2STR(peer_addr)); + return -1; + } + + dev->p2p2 = 1; + dev->req_bootstrap_method = bootstrap; + return 0; +} + + int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, struct p2ps_provision *p2ps_prov, u16 config_methods, int join, int force_freq, @@ -1701,13 +2187,22 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, return -1; } + if (dev->p2p2 && dev->req_bootstrap_method) { + p2p_dbg(p2p, "Provision Discovery Request with " MACSTR + " (bootstrap methods 0x%x)", + MAC2STR(peer_addr), dev->req_bootstrap_method); + goto out; + } + p2p_dbg(p2p, "Provision Discovery Request with " MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); + if (config_methods == 0 && !p2ps_prov) { os_free(p2ps_prov); return -1; } + dev->req_config_methods = config_methods; if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { @@ -1715,12 +2210,12 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, p2ps_prov->method = p2p->p2ps_prov->method; } +out: /* Reset provisioning info */ dev->wps_prov_info = 0; p2ps_prov_free(p2p); p2p->p2ps_prov = p2ps_prov; - dev->req_config_methods = config_methods; if (join) dev->flags |= P2P_DEV_PD_FOR_JOIN; else @@ -1729,8 +2224,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) { p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " - MACSTR " (config methods 0x%x)", - MAC2STR(peer_addr), config_methods); + MACSTR, MAC2STR(peer_addr)); return 0; } diff --git a/contrib/wpa/src/p2p/p2p_utils.c b/contrib/wpa/src/p2p/p2p_utils.c index c1f0084b8d63..ef2f7ac3c9e8 100644 --- a/contrib/wpa/src/p2p/p2p_utils.c +++ b/contrib/wpa/src/p2p/p2p_utils.c @@ -589,7 +589,8 @@ void p2p_pref_channel_filter(const struct p2p_channels *p2p_chan, for (i = 0; i < p2p_chan->reg_classes; i++) { const struct p2p_reg_class *reg = &p2p_chan->reg_class[i]; - struct p2p_reg_class *res_reg = &res->reg_class[i]; + struct p2p_reg_class *res_reg = + &res->reg_class[res->reg_classes]; if (num_channels > 0) { for (j = 0; j < reg->channels; j++) { @@ -611,3 +612,71 @@ void p2p_pref_channel_filter(const struct p2p_channels *p2p_chan, res_reg->reg_class = reg->reg_class; } } + + +bool is_dfs_freq_allowed(struct p2p_data *p2p, bool go, int freq) +{ + if (!go) + return true; + else if (!p2p->dfs_ap_connected) + return false; + + return freq == p2p->sta_connected_freq; +} + + +static int p2p_check_dfs_channel(struct p2p_data *p2p, u8 channel, u8 op_class) +{ + u8 dfs_op_class, dfs_op_chan; + + if (!p2p->dfs_ap_connected) + return -1; + + if (ieee80211_chaninfo_to_channel(p2p->sta_connected_freq, + p2p->sta_connected_chan_width, + 0, &dfs_op_class, &dfs_op_chan) < 0) { + p2p_info(p2p, + "P2P: Frequency to channel conversion failed (%d MHz)", + p2p->sta_connected_freq); + return -1; + } + + if (op_class == dfs_op_class && channel == dfs_op_chan) + return 0; + + return -1; +} + + +void p2p_dfs_channel_filter(struct p2p_data *p2p, + const struct p2p_channels *p2p_chan, + struct p2p_channels *res, bool go) +{ + size_t i, j; + bool is_dfs; + + os_memset(res, 0, sizeof(*res)); + + for (i = 0; i < p2p_chan->reg_classes; i++) { + const struct p2p_reg_class *reg = &p2p_chan->reg_class[i]; + struct p2p_reg_class *res_reg = + &res->reg_class[res->reg_classes]; + + for (j = 0; j < reg->channels; j++) { + is_dfs = p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0, + reg->reg_class, + reg->channel[j]); + if (is_dfs && go && + p2p_check_dfs_channel(p2p, reg->channel[j], + reg->reg_class) < 0) + continue; + + res_reg->channel[res_reg->channels++] = reg->channel[j]; + } + + if (res_reg->channels == 0) + continue; + res->reg_classes++; + res_reg->reg_class = reg->reg_class; + } +} diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.c b/contrib/wpa/src/pae/ieee802_1x_kay.c index b0a418ef0880..57b5e6064b5d 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay.c +++ b/contrib/wpa/src/pae/ieee802_1x_kay.c @@ -1871,7 +1871,7 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) /* Determine if we need space for the ICV Indicator */ if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != - DEFAULT_ICV_LEN) + DEFAULT_ICV_LEN || participant->kay->include_icv_indicator) length = sizeof(struct ieee802_1x_mka_icv_body); else length = 0; @@ -1894,7 +1894,7 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, length = ieee802_1x_mka_get_icv_length(participant); if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != - DEFAULT_ICV_LEN) { + DEFAULT_ICV_LEN || participant->kay->include_icv_indicator) { wpa_printf(MSG_DEBUG, "KaY: ICV Indicator"); body = wpabuf_put(buf, MKA_HDR_LEN); body->type = MKA_ICV_INDICATOR; @@ -3125,9 +3125,9 @@ static int ieee802_1x_kay_mkpdu_validity_check(struct ieee802_1x_kay *kay, be_to_host16(eth_hdr->ethertype)); /* the destination address shall not be an individual address */ - if (!ether_addr_equal(eth_hdr->dest, pae_group_addr)) { + if (!is_multicast_ether_addr(eth_hdr->dest)) { wpa_printf(MSG_DEBUG, - "KaY: ethernet destination address is not PAE group address"); + "KaY: ethernet destination address is not a multicast adddress"); return -1; } @@ -3495,7 +3495,8 @@ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, bool macsec_replay_protect, u32 macsec_replay_window, u8 macsec_offload, u16 port, u8 priority, - u32 macsec_csindex, const char *ifname, const u8 *addr) + u32 macsec_csindex, bool include_icv_indicator, + const char *ifname, const u8 *addr) { struct ieee802_1x_kay *kay; @@ -3533,6 +3534,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->pn_exhaustion = PENDING_PN_EXHAUSTION; kay->macsec_csindex = macsec_csindex; + kay->include_icv_indicator = include_icv_indicator; kay->mka_algindex = DEFAULT_MKA_ALG_INDEX; kay->mka_version = MKA_VERSION_ID; @@ -3858,33 +3860,28 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) dl_list_del(&participant->list); /* remove live peer */ - while (!dl_list_empty(&participant->live_peers)) { - peer = dl_list_entry(participant->live_peers.next, - struct ieee802_1x_kay_peer, list); + while ((peer = dl_list_first(&participant->live_peers, + struct ieee802_1x_kay_peer, list))) { dl_list_del(&peer->list); os_free(peer); } /* remove potential peer */ - while (!dl_list_empty(&participant->potential_peers)) { - peer = dl_list_entry(participant->potential_peers.next, - struct ieee802_1x_kay_peer, list); + while ((peer = dl_list_first(&participant->potential_peers, + struct ieee802_1x_kay_peer, list))) { dl_list_del(&peer->list); os_free(peer); } /* remove sak */ - while (!dl_list_empty(&participant->sak_list)) { - sak = dl_list_entry(participant->sak_list.next, - struct data_key, list); + while ((sak = dl_list_first(&participant->sak_list, + struct data_key, list))) { dl_list_del(&sak->list); ieee802_1x_kay_deinit_data_key(sak); } - while (!dl_list_empty(&participant->rxsc_list)) { - rxsc = dl_list_entry(participant->rxsc_list.next, - struct receive_sc, list); + while ((rxsc = dl_list_first(&participant->rxsc_list, + struct receive_sc, list))) ieee802_1x_kay_deinit_receive_sc(participant, rxsc); - } ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); os_memset(&participant->cak, 0, sizeof(participant->cak)); diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.h b/contrib/wpa/src/pae/ieee802_1x_kay.h index 11464f7fc602..d46f0b285a43 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay.h +++ b/contrib/wpa/src/pae/ieee802_1x_kay.h @@ -206,6 +206,7 @@ struct ieee802_1x_kay { struct ieee802_1x_kay_ctx *ctx; bool is_key_server; bool is_obliged_key_server; + bool include_icv_indicator; /* Always include ICV Indicator */ char if_name[IFNAMSIZ]; u8 macsec_offload; @@ -243,7 +244,8 @@ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, bool macsec_replay_protect, u32 macsec_replay_window, u8 macsec_offload, u16 port, u8 priority, - u32 macsec_csindex, const char *ifname, const u8 *addr); + u32 macsec_csindex, bool include_icv_indicator, + const char *ifname, const u8 *addr); void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); struct ieee802_1x_mka_participant * diff --git a/contrib/wpa/src/pasn/pasn_common.c b/contrib/wpa/src/pasn/pasn_common.c index e2c668136300..ab9e551bb886 100644 --- a/contrib/wpa/src/pasn/pasn_common.c +++ b/contrib/wpa/src/pasn/pasn_common.c @@ -1,7 +1,7 @@ /* * PASN common processing * - * Copyright (C) 2024, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,6 +15,8 @@ #include "crypto/sha384.h" #include "crypto/crypto.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/aes_wrap.h" #include "pasn_common.h" @@ -28,6 +30,14 @@ struct pasn_data * pasn_data_init(void) void pasn_data_deinit(struct pasn_data *pasn) { + if (!pasn) + return; + os_free(pasn->rsnxe_ie); + os_free(pasn->rsn_ie); + wpabuf_free(pasn->frame); + os_free(pasn->pasn_groups); + wpabuf_free(pasn->auth1); + os_free(pasn->dec_pw_id); bin_clear_free(pasn, sizeof(struct pasn_data)); } @@ -39,7 +49,14 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, unsigned int wait), int (*validate_custom_pmkid)(void *ctx, const u8 *addr, - const u8 *pmkid)) + const u8 *pmkid), + int (*eppke_set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, int vlan_id, + const u8 *key, + size_t key_len), + struct rsn_pmksa_cache_entry * + (*pmksa_cache_search)(void *ctx, const u8 *spa, + const u8 *pmkid, bool is_ml)) { if (!pasn) return; @@ -47,6 +64,10 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, pasn->cb_ctx = cb_ctx; pasn->send_mgmt = send_mgmt; pasn->validate_custom_pmkid = validate_custom_pmkid; +#ifdef CONFIG_ENC_ASSOC + pasn->eppke_set_key = eppke_set_key; +#endif /* CONFIG_ENC_ASSOC */ + pasn->pmksa_cache_search = pmksa_cache_search; } @@ -92,6 +113,16 @@ void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr) } +#ifdef CONFIG_ENC_ASSOC +void pasn_set_own_mld_addr(struct pasn_data *pasn, const u8 *addr) +{ + if (!pasn || !addr) + return; + os_memcpy(pasn->mld_addr, addr, ETH_ALEN); +} +#endif /* CONFIG_ENC_ASSOC */ + + void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr) { if (!pasn || !addr) @@ -121,14 +152,6 @@ int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt) } -void pasn_set_password(struct pasn_data *pasn, const char *password) -{ - if (!pasn) - return; - pasn->password = password; -} - - void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt) { if (!pasn) @@ -137,6 +160,13 @@ void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt) } +void pasn_set_mfp(struct pasn_data *pasn, enum mfp_options mfp) +{ + if (pasn) + pasn->ieee80211w = mfp; +} + + void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise) { if (!pasn) @@ -145,7 +175,7 @@ void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise) } -void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab) +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u64 rsnxe_capab) { if (!pasn) return; @@ -157,7 +187,18 @@ void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie) { if (!pasn || !rsnxe_ie) return; - pasn->rsnxe_ie = rsnxe_ie; + os_free(pasn->rsnxe_ie); + pasn->rsnxe_ie = os_memdup(rsnxe_ie, 2 + rsnxe_ie[1]); +} + + +void pasn_set_rsne(struct pasn_data *pasn, const u8 *rsne) +{ + if (!pasn || !rsne) + return; + os_free(pasn->rsn_ie); + pasn->rsn_ie = os_memdup(rsne, 2 + rsne[1]); + pasn->rsn_ie_len = pasn->rsn_ie ? 2 + rsne[1] : 0; } @@ -178,7 +219,7 @@ int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, if (pasn->extra_ies) { os_free((u8 *) pasn->extra_ies); - pasn->extra_ies_len = extra_ies_len; + pasn->extra_ies_len = 0; } pasn->extra_ies = os_memdup(extra_ies, extra_ies_len); @@ -192,6 +233,14 @@ int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, } +void pasn_set_noauth(struct pasn_data *pasn, bool noauth) +{ + if (!pasn) + return; + pasn->noauth = noauth; +} + + int pasn_get_akmp(struct pasn_data *pasn) { if (!pasn) @@ -230,3 +279,100 @@ struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn) return NULL; return &pasn->ptk; } + + +int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf, + const u8 *data, size_t data_len) +{ + int ret; + u8 *encrypted_data, *padded_data = NULL; + u8 *len; + size_t pad_len = 0; + + if (!pasn->ptk.kek_len) { + wpa_printf(MSG_DEBUG, "PASN: KEK not available"); + return -2; + } + + pad_len = data_len % 8; + if (pad_len) { + pad_len = 8 - pad_len; + padded_data = os_zalloc(data_len + pad_len); + if (!padded_data) + return -1; + os_memcpy(padded_data, data, data_len); + data = padded_data; + padded_data[data_len] = 0xdd; + } + data_len += pad_len + 8; + + encrypted_data = os_malloc(data_len); + if (!encrypted_data) { + os_free(padded_data); + return -1; + } + + ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len, + (data_len - 8) / 8, data, encrypted_data); + if (ret) { + wpa_printf(MSG_DEBUG, "PASN: AES wrap failed, ret=%d", ret); + goto out; + } + + if (wpabuf_tailroom(buf) < 1 + 1 + 1 + data_len) { + wpa_printf(MSG_DEBUG, + "PASN: Not enough room in the buffer for PASN Encrypred Data element"); + ret = -1; + goto out; + } + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + len = wpabuf_put(buf, 1); + + wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_DATA); + + wpabuf_put_data(buf, encrypted_data, data_len); + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; + +out: + os_free(padded_data); + os_free(encrypted_data); + return ret; +} + + +int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data, + size_t len) +{ + int ret = -1; + u8 *buf; + u16 buf_len; + struct ieee802_11_elems elems; + if (ieee802_11_parse_elems(data, len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return -1; + } + + if (!elems.pasn_encrypted_data || elems.pasn_encrypted_data_len < 8 || + elems.pasn_encrypted_data_len % 8) { + wpa_printf(MSG_DEBUG, "PASN: No encrypted elements"); + return 0; + } + + buf_len = elems.pasn_encrypted_data_len - 8; + + buf = os_malloc(buf_len); + if (!buf) + return -1; + + ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, buf_len / 8, + elems.pasn_encrypted_data, buf); + if (ret) + wpa_printf(MSG_DEBUG, "PASN: AES unwrap failed, ret=%d", ret); + else if (pasn->parse_data_element && pasn->cb_ctx) + ret = pasn->parse_data_element(pasn->cb_ctx, buf, buf_len); + + os_free(buf); + return ret; +} diff --git a/contrib/wpa/src/pasn/pasn_common.h b/contrib/wpa/src/pasn/pasn_common.h index 36710c2b70e9..96f72c69f641 100644 --- a/contrib/wpa/src/pasn/pasn_common.h +++ b/contrib/wpa/src/pasn/pasn_common.h @@ -3,7 +3,7 @@ * * Copyright (C) 2019, Intel Corporation * Copyright (c) 2022, Jouni Malinen - * Copyright (C) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,10 +12,18 @@ #ifndef PASN_COMMON_H #define PASN_COMMON_H +#include "common/wpa_common.h" +#ifdef CONFIG_SAE +#include "common/sae.h" +#endif /* CONFIG_SAE */ + #ifdef __cplusplus extern "C" { #endif +/* Maximum number of ECC groups supported for PASN */ +#define MAX_NUM_OF_PASN_GROUPS 10 + enum pasn_fils_state { PASN_FILS_STATE_NONE = 0, PASN_FILS_STATE_PENDING_AS, @@ -24,8 +32,8 @@ enum pasn_fils_state { struct pasn_fils { u8 state; - u8 nonce[FILS_NONCE_LEN]; - u8 anonce[FILS_NONCE_LEN]; + u8 nonce[NONCE_LEN]; + u8 anonce[NONCE_LEN]; u8 session[FILS_SESSION_LEN]; u8 erp_pmkid[PMKID_LEN]; bool completed; @@ -44,19 +52,34 @@ struct pasn_data { bool derive_kdk; size_t kdk_len; void *cb_ctx; + unsigned int auth_alg; + u8 mld_addr[ETH_ALEN]; + bool is_ml_peer; + int group_cipher; + int group_mgmt_cipher; + u16 rsn_capab; #ifdef CONFIG_SAE struct sae_pt *pt; #endif /* CONFIG_SAE */ /* Responder */ - const char *password; int wpa_key_mgmt; int rsn_pairwise; - u16 rsnxe_capab; - const u8 *rsnxe_ie; + u64 rsnxe_capab; + u8 *rsnxe_ie; bool custom_pmkid_valid; u8 custom_pmkid[PMKID_LEN]; + enum mfp_options ieee80211w; + + /* Counter from the decrypted password identifier blob (set when + * get_pt_for_pw_id() resolves an encrypted identifier) */ + unsigned int sae_pw_id_counter; + /* Decrypted (real) password identifier resolved from an encrypted + * identifier blob; NULL for plaintext identifiers. Owned by pasn_data + * and freed by pasn_data_deinit(). */ + u8 *dec_pw_id; + size_t dec_pw_id_len; /* * Extra elements to add into Authentication frames. These can be used, @@ -66,7 +89,13 @@ struct pasn_data { size_t extra_ies_len; /* External modules do not access below variables */ + bool derive_kek; + size_t kek_len; u16 group; + u16 rejected_groups[MAX_NUM_OF_PASN_GROUPS]; + unsigned int rejected_group_idx; + u16 ap_supported_groups[MAX_NUM_OF_PASN_GROUPS]; + unsigned int ap_supported_group_idx; bool secure_ltf; int freq; @@ -76,8 +105,9 @@ struct pasn_data { size_t pmk_len; u8 pmk[PMK_LEN_MAX]; bool using_pmksa; + enum rsn_hash_alg hash_alg; - u8 hash[SHA384_MAC_LEN]; + struct wpabuf *auth1; struct wpabuf *beacon_rsne_rsnxe; struct wpa_ptk ptk; @@ -110,25 +140,42 @@ struct pasn_data { int fast_reauth; #ifdef CONFIG_TESTING_OPTIONS int corrupt_mic; + /* + * Override Supported Groups element in the second PASN Authentication + * frame for group negotiation testing. + */ + const int *pasn_test_groups; #endif /* CONFIG_TESTING_OPTIONS */ int network_id; + void *network_ctx; u8 wrapped_data_format; struct wpabuf *secret; /* Responder */ bool noauth; /* Whether PASN without mutual authentication is enabled */ +#ifdef CONFIG_ENC_ASSOC + bool eppke_unauth; /* Whether unauthenticated EPPKE is enabled */ +#endif /* CONFIG_ENC_ASSOC */ int disable_pmksa_caching; int *pasn_groups; - struct wpabuf *wrapped_data; int use_anti_clogging; - const u8 *rsn_ie; + u8 *rsn_ie; size_t rsn_ie_len; u8 *comeback_key; struct os_reltime last_comeback_key_update; u16 comeback_idx; u16 *comeback_pending_idx; + struct wpabuf *frame; +#ifdef CONFIG_ENC_ASSOC + bool authorized; + bool tk_configured; +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_PMKSA_PRIVACY + bool pmksa_caching_privacy; + u8 epp_pmkid_cur[PMKID_LEN]; +#endif /* CONFIG_PMKSA_PRIVACY */ /** * send_mgmt - Function handler to transmit a Management frame @@ -150,6 +197,47 @@ struct pasn_data { */ int (*validate_custom_pmkid)(void *ctx, const u8 *addr, const u8 *pmkid); + + int (*prepare_data_element)(void *ctx, const u8 *peer_addr); + + int (*parse_data_element)(void *ctx, const u8 *data, size_t len); +#ifdef CONFIG_ENC_ASSOC + int (*eppke_set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, + int vlan_id, const u8 *key, size_t key_len); +#endif /* CONFIG_ENC_ASSOC */ + struct rsn_pmksa_cache_entry * + (*pmksa_cache_search)(void *ctx, const u8 *spa, const u8 *pmkid, + bool is_ml); +#ifdef CONFIG_SAE + /** + * get_pt_for_pw_id - Look up SAE PT for a given password identifier + * @ctx: Callback context from cb_ctx + * @pw_id: Password identifier received in the SAE commit frame + * @pw_id_len: Length of the password identifier + * @group: SAE group being used + * @password: Output pointer to the matching password string + * @counter: Output counter value from the decrypted identifier blob + * (set to 0 for plaintext identifiers) + * @dec_pw_id: Output pointer to the decrypted (real) password + * identifier for encrypted blobs; set to NULL for plaintext + * identifiers. The caller takes ownership and must free with + * os_free(). + * @dec_pw_id_len: Output length of the decrypted password identifier + * Returns: SAE PT on success, NULL if not found + * + * This callback is invoked by the PASN responder when processing an + * SAE commit frame that contains a password identifier, allowing the + * AP to look up the correct PT at commit-processing time rather than + * at PASN-setup time. + */ + struct sae_pt * (*get_pt_for_pw_id)(void *ctx, + const u8 *pw_id, size_t pw_id_len, + int group, + const char **password, + unsigned int *counter, + u8 **dec_pw_id, + size_t *dec_pw_id_len); +#endif /* CONFIG_SAE */ }; /* Initiator */ @@ -160,6 +248,10 @@ int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr, int freq, const u8 *beacon_rsne, u8 beacon_rsne_len, const u8 *beacon_rsnxe, u8 beacon_rsnxe_len, const struct wpabuf *comeback); +struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, + const struct wpabuf *comeback, + bool verify, bool full_hdr); +struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn, bool full_hdr); int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr, const u8 *peer_addr, const u8 *bssid, int akmp, int cipher, u16 group, @@ -170,11 +262,16 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, struct wpa_pasn_params_data *pasn_params); int wpa_pasn_auth_tx_status(struct pasn_data *pasn, const u8 *data, size_t data_len, u8 acked); +int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type, + u16 auth_transaction, u16 status_code, + const u8 *frame_data, size_t frame_data_len, + struct wpa_pasn_params_data *pasn_params); /* Responder */ int handle_auth_pasn_1(struct pasn_data *pasn, const u8 *own_addr, const u8 *peer_addr, - const struct ieee80211_mgmt *mgmt, size_t len); + const struct ieee80211_mgmt *mgmt, size_t len, + bool reject); int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, const u8 *peer_addr, const struct ieee80211_mgmt *mgmt, size_t len); @@ -191,13 +288,23 @@ void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx, unsigned int wait), int (*validate_custom_pmkid)(void *ctx, const u8 *addr, - const u8 *pmkid)); + const u8 *pmkid), + int (*eppke_set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, int vlan_id, + const u8 *key, + size_t key_len), + struct rsn_pmksa_cache_entry * + (*pmksa_cache_search)(void *ctx, const u8 *spa, + const u8 *pmkid, + bool is_ml)); + void pasn_enable_kdk_derivation(struct pasn_data *pasn); void pasn_disable_kdk_derivation(struct pasn_data *pasn); void pasn_set_akmp(struct pasn_data *pasn, int akmp); void pasn_set_cipher(struct pasn_data *pasn, int cipher); void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr); +void pasn_set_own_mld_addr(struct pasn_data *pasn, const u8 *addr); void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr); void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr); void pasn_set_initiator_pmksa(struct pasn_data *pasn, @@ -205,22 +312,53 @@ void pasn_set_initiator_pmksa(struct pasn_data *pasn, void pasn_set_responder_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa); int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt); +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void); +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid, int akmp); +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len); +void pasn_initiator_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid); +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); /* Responder */ -void pasn_set_password(struct pasn_data *pasn, const char *password); +void pasn_set_noauth(struct pasn_data *pasn, bool noauth); void pasn_set_wpa_key_mgmt(struct pasn_data *pasn, int key_mgmt); void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise); -void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab); +void pasn_set_rsne(struct pasn_data *pasn, const u8 *rsne); +void pasn_set_rsnxe_caps(struct pasn_data *pasn, u64 rsnxe_capab); void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie); void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid); int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, size_t extra_ies_len); +void pasn_set_mfp(struct pasn_data *pasn, enum mfp_options mfp); + +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void); +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid, int akmp); +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len); +void pasn_responder_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid); +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); int pasn_get_akmp(struct pasn_data *pasn); int pasn_get_cipher(struct pasn_data *pasn); size_t pasn_get_pmk_len(struct pasn_data *pasn); u8 * pasn_get_pmk(struct pasn_data *pasn); struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn); +int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf, + const u8 *data, size_t data_len); +int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data, + size_t len); #ifdef __cplusplus } diff --git a/contrib/wpa/src/pasn/pasn_initiator.c b/contrib/wpa/src/pasn/pasn_initiator.c index d273067b7078..ff07906d48ac 100644 --- a/contrib/wpa/src/pasn/pasn_initiator.c +++ b/contrib/wpa/src/pasn/pasn_initiator.c @@ -2,7 +2,7 @@ * PASN initiator processing * * Copyright (C) 2019, Intel Corporation - * Copyright (C) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +17,7 @@ #include "common/ieee802_11_defs.h" #include "common/dragonfly.h" #include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "eap_common/eap_defs.h" @@ -26,6 +27,66 @@ #include "pasn_common.h" +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void) +{ + return pmksa_cache_init(NULL, NULL, NULL, NULL, NULL); +} + + +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_deinit(pmksa); +} + + +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, + size_t pmk_len, const u8 *pmkid, int akmp) +{ + if (pmksa_cache_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, + own_addr, NULL, akmp, NULL, 0)) + return 0; + return -1; +} + + +void pasn_initiator_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL, 0); + if (!entry) + return; + + pmksa_cache_remove(pmksa, entry); +} + + +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL, 0); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_flush(pmksa, NULL, NULL, 0, false, NULL); +} + + void pasn_set_initiator_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa) { @@ -40,6 +101,8 @@ static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn) { struct wpabuf *buf = NULL; int ret; + const u8 *pw_id = NULL; + size_t pw_id_len = 0; ret = sae_set_group(&pasn->sae, pasn->group); if (ret) { @@ -47,6 +110,7 @@ static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn) return NULL; } + pasn->sae.akmp = pasn->akmp; ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, pasn->own_addr, pasn->peer_addr, NULL, NULL); @@ -66,7 +130,12 @@ static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn) wpabuf_put_le16(buf, 1); wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); - sae_write_commit(&pasn->sae, buf, NULL, 0); + /* Include the password identifier if the PT was derived with one */ + if (pasn->pt && pasn->pt->password_id) { + pw_id = wpabuf_head(pasn->pt->password_id); + pw_id_len = wpabuf_len(pasn->pt->password_id); + } + sae_write_commit(&pasn->sae, buf, NULL, pw_id, pw_id_len); pasn->sae.state = SAE_COMMITTED; return buf; @@ -115,8 +184,8 @@ static int wpas_pasn_wd_sae_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups, - 1, NULL); + res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, NULL, + groups, 1, NULL); if (res != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit"); return -1; @@ -211,12 +280,12 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) return NULL; } - if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 || + if (random_get_bytes(pasn->fils.nonce, NONCE_LEN) < 0 || random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0) goto fail; wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce, - FILS_NONCE_LEN); + NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session, FILS_SESSION_LEN); @@ -229,7 +298,7 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); /* Authentication Transaction seq# */ - wpabuf_put_le16(buf, 1); + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH1); /* Status Code */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); @@ -239,9 +308,9 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, 1 + NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, pasn->fils.nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); @@ -304,7 +373,7 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) struct wpa_ie_data rsne_data; u8 rmsk[ERP_MAX_KEY_LEN]; size_t rmsk_len; - u8 anonce[FILS_NONCE_LEN]; + u8 anonce[NONCE_LEN]; const u8 *data; size_t buf_len; struct wpabuf *fils_wd = NULL; @@ -348,7 +417,7 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce || + if (!elems.rsn_ie || !elems.nonce || !elems.nonce || !elems.wrapped_data) { wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs"); return -1; @@ -361,7 +430,8 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - ret = wpa_pasn_validate_rsne(&rsne_data); + ret = wpa_pasn_validate_rsne(&rsne_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE"); return -1; @@ -373,9 +443,8 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) return -1; } - wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce, - FILS_NONCE_LEN); - os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.nonce, NONCE_LEN); + os_memcpy(anonce, elems.nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session, FILS_SESSION_LEN); @@ -439,7 +508,7 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd) pasn->pmk_len, pasn->fils.erp_pmkid, NULL, 0, pasn->peer_addr, pasn->own_addr, NULL, - pasn->akmp, 0); + pasn->akmp, NULL, pasn->auth_alg); pasn->fils.completed = true; return 0; @@ -457,11 +526,18 @@ static struct wpabuf * wpas_pasn_get_wrapped_data(struct pasn_data *pasn) case WPA_KEY_MGMT_PASN: /* no wrapped data */ return NULL; +#ifdef CONFIG_ENC_ASSOC + case WPA_KEY_MGMT_EPPKE: + /* EPPKE without base AKM: no wrapped data per + * IEEE P802.11bi/D4.0, 12.16.9.1 */ + return NULL; +#endif /* CONFIG_ENC_ASSOC */ case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: #ifdef CONFIG_SAE if (pasn->trans_seq == 0) return wpas_pasn_wd_sae_commit(pasn); - if (pasn->trans_seq == 2) + if (pasn->trans_seq == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) return wpas_pasn_wd_sae_confirm(pasn); #endif /* CONFIG_SAE */ wpa_printf(MSG_ERROR, @@ -498,6 +574,7 @@ static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn) /* Note: Valid AKMP is expected to already be validated */ switch (pasn->akmp) { case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: return WPA_PASN_WRAPPED_DATA_SAE; case WPA_KEY_MGMT_FILS_SHA256: case WPA_KEY_MGMT_FILS_SHA384: @@ -518,14 +595,46 @@ static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn) } -static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, - const struct wpabuf *comeback, - bool verify) +static void wpas_pasn_add_mle(struct pasn_data *pasn, struct wpabuf *buf) +{ +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) { + wpa_printf(MSG_DEBUG, "EPPKE: Include Multi Link element"); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 10); + wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK); + wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC); + wpabuf_put_u8(buf, ETH_ALEN + 1); + wpabuf_put_data(buf, pasn->own_addr, ETH_ALEN); + } +#endif /* CONFIG_ENC_ASSOC */ +} + + +static u8 * pasn_prepend_auth_alg(u16 auth_alg, const u8 *buf, size_t len) +{ + u8 *new_buf = os_malloc(len + 2); + + if (!new_buf) + return NULL; + + WPA_PUT_LE16(new_buf, auth_alg); + os_memcpy(new_buf + 2, buf, len); + + return new_buf; +} + + +struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, + const struct wpabuf *comeback, + bool verify, bool full_hdr) { struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; const u8 *pmkid; u8 wrapped_data; - int ret; + const u8 *data; + size_t data_len; + u8 *copy = NULL; wpa_printf(MSG_DEBUG, "PASN: Building frame 1"); @@ -546,9 +655,16 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); - wpa_pasn_build_auth_header(buf, pasn->bssid, - pasn->own_addr, pasn->peer_addr, - pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + if (full_hdr) { + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + pasn->trans_seq + 1, + WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); + } else { + wpabuf_put_le16(buf, pasn->trans_seq + 1); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } pmkid = NULL; if (wpa_key_mgmt_ft(pasn->akmp)) { @@ -557,11 +673,15 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, #else /* CONFIG_IEEE80211R */ goto fail; #endif /* CONFIG_IEEE80211R */ - } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) { + } else { struct rsn_pmksa_cache_entry *pmksa; pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, pasn->own_addr, NULL, NULL, pasn->akmp); + + wpa_printf(MSG_DEBUG, "PASN: PMKSA: akmp=0x%x, found=0x%x", + pasn->akmp, pmksa ? pmksa->akmp : 0); + if (pmksa && pasn->custom_pmkid_valid) pmkid = pasn->custom_pmkid; else if (pmksa) @@ -571,32 +691,51 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, * Note: Even when PMKSA is available, also add wrapped data as * it is possible that the PMKID is no longer valid at the AP. */ - if (!verify) + if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO && !verify) wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); } - if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0) + if (pasn->rsn_ie && pasn->rsn_ie_len) + wpabuf_put_data(buf, pasn->rsn_ie, pasn->rsn_ie_len); + else if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0) goto fail; if (!wrapped_data_buf) wrapped_data = WPA_PASN_WRAPPED_DATA_NO; - wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, - pubkey, true, comeback, -1); - if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) goto fail; - wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab); + if (pasn->rsnxe_ie) + wpabuf_put_data(buf, pasn->rsnxe_ie, 2 + pasn->rsnxe_ie[1]); + else + wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab); + + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + pubkey, true, comeback, -1); wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); - ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, - wpabuf_head_u8(buf) + IEEE80211_HDRLEN, - wpabuf_len(buf) - IEEE80211_HDRLEN, - pasn->hash); - if (ret) { - wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + wpas_pasn_add_mle(pasn, buf); + + if (full_hdr) { + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + } else { + copy = pasn_prepend_auth_alg(pasn->auth_alg, + wpabuf_head_u8(buf), + wpabuf_len(buf)); + if (!copy) + goto fail; + data = copy; + data_len = wpabuf_len(buf) + 2; + } + + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(data, data_len); + + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); goto fail; } @@ -604,6 +743,7 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, wpabuf_free(wrapped_data_buf); wpabuf_free(pubkey); + os_free(copy); wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success"); return buf; @@ -612,23 +752,27 @@ fail: wpabuf_free(wrapped_data_buf); wpabuf_free(pubkey); wpabuf_free(buf); + os_free(copy); return NULL; } -static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) +struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn, bool full_hdr) { struct wpabuf *buf, *wrapped_data_buf = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN]; - u8 mic_len, data_len; + u8 mic_len; + size_t data_len; const u8 *data; u8 *ptr; u8 wrapped_data; int ret; + u8 hash[SHA512_MAC_LEN]; + u8 *copy = NULL; wpa_printf(MSG_DEBUG, "PASN: Building frame 3"); - if (pasn->trans_seq != 2) + if (pasn->trans_seq != WLAN_AUTH_TR_SEQ_PASN_AUTH2) return NULL; buf = wpabuf_alloc(1500); @@ -637,37 +781,69 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) wrapped_data = wpas_pasn_get_wrapped_data_format(pasn); - wpa_pasn_build_auth_header(buf, pasn->bssid, - pasn->own_addr, pasn->peer_addr, - pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); + if (full_hdr) { + wpa_pasn_build_auth_header(buf, pasn->bssid, + pasn->own_addr, pasn->peer_addr, + WLAN_AUTH_TR_SEQ_PASN_AUTH3, + WLAN_STATUS_SUCCESS, + pasn->auth_alg == WLAN_AUTH_EPPKE); + } else { + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH3); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn); if (!wrapped_data_buf) wrapped_data = WPA_PASN_WRAPPED_DATA_NO; - wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, - NULL, false, NULL, -1); - if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) goto fail; wpabuf_free(wrapped_data_buf); wrapped_data_buf = NULL; + wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data, + NULL, false, NULL, -1); + + if (pasn->prepare_data_element && pasn->cb_ctx) + pasn->prepare_data_element(pasn->cb_ctx, pasn->peer_addr); + + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + + wpas_pasn_add_mle(pasn, buf); + /* Add the MIC */ - mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); + mic_len = pasn_mic_len(pasn->hash_alg); wpabuf_put_u8(buf, WLAN_EID_MIC); wpabuf_put_u8(buf, mic_len); ptr = wpabuf_put(buf, mic_len); os_memset(ptr, 0, mic_len); - data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; - data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + if (full_hdr) { + data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; + data_len = wpabuf_len(buf) - IEEE80211_HDRLEN; + } else { + copy = pasn_prepend_auth_alg(pasn->auth_alg, + wpabuf_head_u8(buf), + wpabuf_len(buf)); + if (!copy) + goto fail; + data = copy; + data_len = wpabuf_len(buf) + 2; + } - ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, + + if (!pasn->auth1 || + pasn_auth_frame_hash(pasn->hash_alg, wpabuf_head(pasn->auth1), + wpabuf_len(pasn->auth1), hash)) { + wpa_printf(MSG_INFO, "PASN: Failed to calculate Auth1 hash"); + goto fail; + } + + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, pasn->own_addr, pasn->peer_addr, - pasn->hash, mic_len * 2, data, data_len, mic); + hash, mic_len * 2, data, data_len, mic); if (ret) { wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation"); goto fail; @@ -683,6 +859,7 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) os_memcpy(ptr, mic, mic_len); pasn->trans_seq++; + os_free(copy); wpa_printf(MSG_DEBUG, "PASN: frame 3: Success"); return buf; @@ -690,6 +867,7 @@ fail: pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; wpabuf_free(wrapped_data_buf); wpabuf_free(buf); + os_free(copy); return NULL; } @@ -701,7 +879,10 @@ void wpa_pasn_reset(struct pasn_data *pasn) crypto_ecdh_deinit(pasn->ecdh); pasn->ecdh = NULL; - + pasn->derive_kdk = false; + pasn->kdk_len = 0; + pasn->derive_kek = false; + pasn->kek_len = 0; pasn->akmp = 0; pasn->cipher = 0; pasn->group = 0; @@ -711,7 +892,9 @@ void wpa_pasn_reset(struct pasn_data *pasn) forced_memzero(pasn->pmk, sizeof(pasn->pmk)); forced_memzero(&pasn->ptk, sizeof(pasn->ptk)); - forced_memzero(&pasn->hash, sizeof(pasn->hash)); + + wpabuf_free(pasn->auth1); + pasn->auth1 = NULL; wpabuf_free(pasn->beacon_rsne_rsnxe); pasn->beacon_rsne_rsnxe = NULL; @@ -740,13 +923,18 @@ void wpa_pasn_reset(struct pasn_data *pasn) #endif /* CONFIG_IEEE80211R */ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE; pasn->pmksa_entry = NULL; + os_memset(pasn->ap_supported_groups, 0, + sizeof(pasn->ap_supported_groups)); + pasn->ap_supported_group_idx = 0; #ifdef CONFIG_TESTING_OPTIONS pasn->corrupt_mic = 0; #endif /* CONFIG_TESTING_OPTIONS */ pasn->network_id = 0; pasn->derive_kdk = false; + os_free(pasn->rsn_ie); pasn->rsn_ie = NULL; pasn->rsn_ie_len = 0; + os_free(pasn->rsnxe_ie); pasn->rsnxe_ie = NULL; pasn->custom_pmkid_valid = false; @@ -754,6 +942,9 @@ void wpa_pasn_reset(struct pasn_data *pasn) os_free((u8 *) pasn->extra_ies); pasn->extra_ies = NULL; } + + wpabuf_free(pasn->frame); + pasn->frame = NULL; } @@ -767,8 +958,9 @@ static int wpas_pasn_set_pmk(struct pasn_data *pasn, os_memset(pasn->pmk, 0, sizeof(pasn->pmk)); pasn->pmk_len = 0; - if (pasn->akmp == WPA_KEY_MGMT_PASN) { - wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); + if ((pasn->akmp == WPA_KEY_MGMT_PASN || + pasn->akmp == WPA_KEY_MGMT_EPPKE) && !rsn_data->num_pmkid) { + wpa_printf(MSG_DEBUG, "PASN/EPPKE: Using default PMK"); pasn->pmk_len = WPA_PASN_PMK_LEN; os_memcpy(pasn->pmk, pasn_default_pmk, @@ -822,7 +1014,8 @@ static int wpas_pasn_set_pmk(struct pasn_data *pasn, } #ifdef CONFIG_SAE - if (pasn->akmp == WPA_KEY_MGMT_SAE) { + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { int ret; ret = wpas_pasn_wd_sae_rx(pasn, wrapped_data); @@ -834,15 +1027,17 @@ static int wpas_pasn_set_pmk(struct pasn_data *pasn, } wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE"); - pasn->pmk_len = PMK_LEN; - os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN); + pasn->pmk_len = pasn->sae.pmk_len; + os_memcpy(pasn->pmk, pasn->sae.pmk, pasn->pmk_len); pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk, pasn->pmk_len, pasn->sae.pmkid, NULL, 0, pasn->peer_addr, - pasn->own_addr, NULL, - pasn->akmp, 0); + pasn->own_addr, + pasn->network_ctx, + pasn->akmp, NULL, + pasn->auth_alg); return 0; } #endif /* CONFIG_SAE */ @@ -918,22 +1113,26 @@ static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr, MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher, pasn->group); - frame = wpas_pasn_build_auth_1(pasn, comeback, verify); + frame = wpas_pasn_build_auth_1(pasn, comeback, verify, true); if (!frame) { wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame"); goto fail; } + wpabuf_free(pasn->frame); + pasn->frame = NULL; + ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(frame), wpabuf_len(frame), 0, pasn->freq, 1000); - wpabuf_free(frame); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame"); + wpabuf_free(frame); goto fail; } + pasn->frame = frame; return 0; fail: @@ -958,8 +1157,13 @@ int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr, switch (akmp) { case WPA_KEY_MGMT_PASN: break; +#ifdef CONFIG_ENC_ASSOC + case WPA_KEY_MGMT_EPPKE: + break; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: if (beacon_rsnxe && !ieee802_11_rsnx_capab(beacon_rsnxe, @@ -1048,70 +1252,160 @@ static bool is_pasn_auth_frame(struct pasn_data *pasn, !ether_addr_equal(mgmt->da, pasn->peer_addr))) return false; - /* Not PASN; do nothing */ - if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN)) + /* Not PASN/EPPKE; do nothing */ + if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN) && + mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_EPPKE)) return false; return true; } -int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, - struct wpa_pasn_params_data *pasn_params) +static void +wpas_pasn_store_ap_supported_groups(struct pasn_data *pasn, + const struct ieee802_11_elems *elems) +{ + unsigned int i, num_groups; + pasn->ap_supported_group_idx = 0; + + if (!elems->supported_groups || elems->supported_groups_len < 2) + return; + + num_groups = elems->supported_groups_len / 2; + if (num_groups > MAX_NUM_OF_PASN_GROUPS) { + wpa_printf(MSG_DEBUG, + "PASN: AP supported groups count %u exceeds max %u - ignoring", + num_groups, MAX_NUM_OF_PASN_GROUPS); + return; + } + + wpa_hexdump(MSG_DEBUG, "PASN: Supported groups advertised by responder", + elems->supported_groups, elems->supported_groups_len); + + for (i = 0; i < num_groups; i++) + pasn->ap_supported_groups[pasn->ap_supported_group_idx++] = + WPA_GET_LE16(elems->supported_groups + i * 2); + + wpa_printf(MSG_DEBUG, "PASN: Stored %u AP supported groups", + num_groups); +} + + +static int +wpas_pasn_validate_supported_groups_rejection(struct pasn_data *pasn) +{ + unsigned int i; + + if (!pasn->ap_supported_group_idx) { + wpa_printf(MSG_DEBUG, + "PASN: Supported Groups element not present - skipping validation"); + return 0; + } + + for (i = 0; i < pasn->ap_supported_group_idx; i++) { + if (pasn->ap_supported_groups[i] == pasn->group) { + wpa_printf(MSG_INFO, + "PASN: Rejected group %u is listed as supported by responder - aborting PASN", + pasn->group); + return -1; + } + } + + return 0; +} + + +static int +wpas_pasn_validate_supported_groups(struct pasn_data *pasn) +{ + unsigned int i, j; + bool current_group_found = false; + + if (!pasn->ap_supported_group_idx) { + wpa_printf(MSG_DEBUG, + "PASN: Supported Groups element not present - skipping validation"); + return 0; + } + + for (i = 0; i < pasn->ap_supported_group_idx; i++) { + u16 group = pasn->ap_supported_groups[i]; + + if (group == pasn->group) + current_group_found = true; + + for (j = 0; j < pasn->rejected_group_idx; j++) { + if (group == pasn->rejected_groups[j]) { + wpa_printf(MSG_INFO, + "PASN: Previously rejected group %u found in responder supported list", + group); + return -1; + } + } + } + + if (!current_group_found) { + wpa_printf(MSG_INFO, + "PASN: Negotiated group %u not found in responder supported list - aborting PASN", + pasn->group); + return -1; + } + + return 0; +} + + +int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type, + u16 auth_transaction, u16 status, + const u8 *auth_data, size_t auth_data_len, + struct wpa_pasn_params_data *pasn_params) { struct ieee802_11_elems elems; struct wpa_ie_data rsn_data; - const struct ieee80211_mgmt *mgmt = - (const struct ieee80211_mgmt *) data; - struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL; + struct wpabuf *wrapped_data = NULL, *secret = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; u8 mic_len; - u16 status; int ret, inc_y; u8 *copy = NULL; size_t mic_offset, copy_len; - if (!is_pasn_auth_frame(pasn, mgmt, len, true)) - return -2; - - if (mgmt->u.auth.auth_transaction != - host_to_le16(pasn->trans_seq + 1)) { + if (auth_transaction != pasn->trans_seq + 1) { wpa_printf(MSG_DEBUG, "PASN: RX: Invalid transaction sequence: (%u != %u)", - le_to_host16(mgmt->u.auth.auth_transaction), - pasn->trans_seq + 1); + auth_transaction, pasn->trans_seq + 1); return -3; } - status = le_to_host16(mgmt->u.auth.status_code); - if (status != WLAN_STATUS_SUCCESS && + status != WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { wpa_printf(MSG_DEBUG, "PASN: Authentication rejected - status=%u", status); goto fail; } - if (ieee802_11_parse_elems(mgmt->u.auth.variable, - len - offsetof(struct ieee80211_mgmt, - u.auth.variable), - &elems, 0) == ParseFailed) { + if (ieee802_11_parse_elems(auth_data, auth_data_len, &elems, 0) == + ParseFailed) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing Authentication frame"); goto fail; } - /* Check that the MIC IE exists. Save it and zero out the memory */ - mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); - if (status == WLAN_STATUS_SUCCESS) { - if (!elems.mic || elems.mic_len != mic_len) { - wpa_printf(MSG_DEBUG, - "PASN: Invalid MIC. Expecting len=%u", - mic_len); + wpas_pasn_store_ap_supported_groups(pasn, &elems); + + if (status == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + wpa_printf(MSG_DEBUG, + "PASN: AP rejected group %u", pasn->group); + + if (wpas_pasn_validate_supported_groups_rejection(pasn) < 0) goto fail; - } - os_memcpy(mic, elems.mic, mic_len); + + if (pasn->rejected_group_idx < + ARRAY_SIZE(pasn->rejected_groups)) + pasn->rejected_groups[pasn->rejected_group_idx++] = + pasn->group; + + return 2; } if (!elems.pasn_params || !elems.pasn_params_len) { @@ -1169,7 +1463,8 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, goto fail; } - ret = wpa_pasn_validate_rsne(&rsn_data); + ret = wpa_pasn_validate_rsne(&rsn_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); goto fail; @@ -1229,11 +1524,20 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, goto fail; } + if (pasn->derive_kek) { + wpa_printf(MSG_DEBUG, "PASN: Derive PTK-KEK"); + if (!pasn->kek_len) + pasn->kek_len = wpa_kek_len(pasn->akmp, + pasn->pmk_len); + wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len); + } + ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len, pasn->own_addr, pasn->peer_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, pasn->cipher, - pasn->kdk_len); + pasn->kdk_len, pasn->kek_len, &pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); goto fail; @@ -1253,21 +1557,42 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, wpabuf_free(secret); secret = NULL; + /* Check that the MIC IE exists. Save it and zero out the memory */ + mic_len = pasn_mic_len(pasn->hash_alg); + if (status == WLAN_STATUS_SUCCESS) { + if (!elems.mic || elems.mic_len != mic_len) { + wpa_printf(MSG_DEBUG, + "PASN: Invalid MIC. Expecting len=%u", + mic_len); + goto fail; + } + os_memcpy(mic, elems.mic, mic_len); + } + /* Use a copy of the message since we need to clear the MIC field */ if (!elems.mic) goto fail; - mic_offset = elems.mic - (const u8 *) &mgmt->u.auth; - copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); - if (mic_offset + mic_len > copy_len) - goto fail; - copy = os_memdup(&mgmt->u.auth, copy_len); + mic_offset = elems.mic - auth_data; + + /* 6 octets for the fixed fields Authentic Algorithm, Authentication + * Transaction and Status Code (2 octets each). */ + copy = os_malloc(auth_data_len + 6); if (!copy) goto fail; - os_memset(copy + mic_offset, 0, mic_len); + + WPA_PUT_LE16(copy, auth_type); + WPA_PUT_LE16(copy + 2, auth_transaction); + WPA_PUT_LE16(copy + 4, status); + os_memcpy(copy + 6, auth_data, auth_data_len); + copy_len = auth_data_len + 6; + if (mic_offset + mic_len > auth_data_len) + goto fail; + + os_memset(copy + 6 + mic_offset, 0, mic_len); if (pasn->beacon_rsne_rsnxe) { /* Verify the MIC */ - ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, pasn->peer_addr, pasn->own_addr, wpabuf_head(pasn->beacon_rsne_rsnxe), wpabuf_len(pasn->beacon_rsne_rsnxe), @@ -1302,7 +1627,7 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, rsne_rsnxe, rsne_rsnxe_len); /* Verify the MIC */ - ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, pasn->peer_addr, pasn->own_addr, rsne_rsnxe, rsne_rsnxe_len, @@ -1319,25 +1644,88 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, goto fail; } + if (wpas_pasn_validate_supported_groups(pasn) < 0) + goto fail; + pasn->trans_seq++; wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame"); - frame = wpas_pasn_build_auth_3(pasn); + if (pasn_parse_encrypted_data(pasn, auth_data, auth_data_len) < 0) { + wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); + goto fail; + } + + return 0; + +fail: + wpabuf_free(wrapped_data); + wpabuf_free(secret); + os_free(copy); + return -1; +} + + +int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, + struct wpa_pasn_params_data *pasn_params) +{ + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + struct wpabuf *frame = NULL; + int ret; + u16 status; + + if (!is_pasn_auth_frame(pasn, mgmt, len, true)) + return -2; + + status = le_to_host16(mgmt->u.auth.status_code); + + ret = wpas_parse_pasn_frame(pasn, le_to_host16(mgmt->u.auth.auth_alg), + le_to_host16(mgmt->u.auth.auth_transaction), + status, + mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), pasn_params); + + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing PASN M2 auth frame"); + if (ret < -1) + return ret; + goto fail; + } + + if (ret == 1) { + wpa_printf(MSG_DEBUG, "PASN: Temporary rejection, Retry"); + return 1; + } + + if (ret == 2) { + wpa_printf(MSG_DEBUG, + "PASN: Group %u rejected - retry with next group", + pasn->group); + return 2; + } + + frame = wpas_pasn_build_auth_3(pasn, true); if (!frame) { wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame"); goto fail; } + wpabuf_free(pasn->frame); + pasn->frame = NULL; + ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(frame), wpabuf_len(frame), 0, pasn->freq, 100); - wpabuf_free(frame); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame"); + wpabuf_free(frame); goto fail; } + pasn->frame = frame; wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK"); pasn->status = WLAN_STATUS_SUCCESS; @@ -1345,9 +1733,6 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, return 0; fail: wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating"); - wpabuf_free(wrapped_data); - wpabuf_free(secret); - os_free(copy); /* * TODO: In case of an error the standard allows to silently drop @@ -1392,7 +1777,7 @@ int wpa_pasn_auth_tx_status(struct pasn_data *pasn, * try to complete the flow, relying on the PASN timeout callback to * clean up. */ - if (pasn->trans_seq == 3) { + if (pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH3) { wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR, MAC2STR(pasn->peer_addr)); /* @@ -1402,5 +1787,8 @@ int wpa_pasn_auth_tx_status(struct pasn_data *pasn, return 1; } + if (pasn->trans_seq == WLAN_AUTH_TR_SEQ_PASN_AUTH1 && !acked) + return 2; + return 0; } diff --git a/contrib/wpa/src/pasn/pasn_responder.c b/contrib/wpa/src/pasn/pasn_responder.c index b99136492fa6..c669271c2ccb 100644 --- a/contrib/wpa/src/pasn/pasn_responder.c +++ b/contrib/wpa/src/pasn/pasn_responder.c @@ -2,7 +2,7 @@ * PASN responder processing * * Copyright (C) 2019, Intel Corporation - * Copyright (C) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,15 +17,77 @@ #include "common/ieee802_11_defs.h" #include "crypto/sha384.h" #include "crypto/sha256.h" +#include "crypto/sha512.h" #include "crypto/random.h" #include "crypto/crypto.h" #include "ap/hostapd.h" #include "ap/comeback_token.h" #include "ap/ieee802_1x.h" #include "ap/pmksa_cache_auth.h" +#include "ap/wpa_auth.h" #include "pasn_common.h" +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void) +{ + return pmksa_cache_auth_init(NULL, NULL); +} + + +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_deinit(pmksa); +} + + +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, + const u8 *own_addr, const u8 *bssid, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid, int akmp) +{ + if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, own_addr, + bssid, 0, NULL, akmp)) + return 0; + return -1; +} + + +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *bssid, u8 *pmkid, u8 *pmk, + size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_get(pmksa, bssid, NULL); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_responder_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, + const u8 *bssid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_get(pmksa, bssid, NULL); + if (!entry) + return; + + pmksa_cache_free_entry(pmksa, entry); +} + + +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_flush(pmksa); +} + + void pasn_set_responder_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa) { @@ -46,6 +108,7 @@ static int pasn_wd_handle_sae_commit(struct pasn_data *pasn, u16 res, alg, seq, status; int groups[] = { pasn->group, 0 }; int ret; + struct sae_pt *pt = NULL; if (!wd) return -1; @@ -81,25 +144,70 @@ static int pasn_wd_handle_sae_commit(struct pasn_data *pasn, return -1; } - if (!pasn->password || !pasn->pt) { + pasn->sae.akmp = pasn->akmp; + + res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, NULL, + groups, 1, NULL); + if (res != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); + return -1; + } + + /* + * If the commit contained a password identifier, look up the + * matching PT now that we know the identifier. + */ + if (pasn->sae.tmp && pasn->sae.tmp->parsed_pw_id && + pasn->sae.tmp->parsed_pw_id_len) { + const char *pw = NULL; + unsigned int counter = 0; + u8 *dec_pw_id = NULL; + size_t dec_pw_id_len = 0; + + if (!pasn->get_pt_for_pw_id) { + wpa_printf(MSG_DEBUG, + "PASN: No callback to resolve password identifier"); + return -2; + } + + pt = pasn->get_pt_for_pw_id(pasn->cb_ctx, + pasn->sae.tmp->parsed_pw_id, + pasn->sae.tmp->parsed_pw_id_len, + pasn->group, &pw, &counter, + &dec_pw_id, &dec_pw_id_len); + if (!pt) { + wpa_printf(MSG_DEBUG, + "PASN: Unknown password identifier"); + return -2; + } + + wpa_printf(MSG_DEBUG, + "PASN: Using PT for received password identifier"); + pasn->sae_pw_id_counter = counter; + os_free(pasn->dec_pw_id); + pasn->dec_pw_id = dec_pw_id; + pasn->dec_pw_id_len = dec_pw_id_len; + } + + if (!pt && !pasn->pt) { wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); return -1; } - ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr, - NULL, NULL); +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + + ret = sae_prepare_commit_pt(&pasn->sae, pt ? pt : pasn->pt, + own_addr, peer_addr, NULL, NULL); + /* Free the on-the-fly derived PT now that it has been used */ + sae_deinit_pt(pt); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); return -1; } - res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, - groups, 0, NULL); - if (res != WLAN_STATUS_SUCCESS) { - wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); - return -1; - } - /* Process the commit message and derive the PMK */ ret = sae_process_commit(&pasn->sae); if (ret) { @@ -165,6 +273,10 @@ static int pasn_wd_handle_sae_confirm(struct pasn_data *pasn, if (!pasn->sae.akmp) pasn->sae.akmp = WPA_KEY_MGMT_SAE; +#ifdef CONFIG_PMKSA_PRIVACY + if (pasn->auth_alg == WLAN_AUTH_EPPKE) + os_memcpy(pasn->epp_pmkid_cur, pasn->sae.pmkid, PMKID_LEN); +#endif /* CONFIG_PMKSA_PRIVACY */ pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len, pasn->sae.pmkid, NULL, 0, pasn->own_addr, peer_addr, 0, NULL, pasn->sae.akmp); @@ -177,6 +289,8 @@ static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn) struct wpabuf *buf = NULL; u8 *len_ptr; size_t len; + const u8 *pw_id = NULL; + size_t pw_id_len = 0; /* Need to add the entire Authentication frame body */ buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); @@ -191,8 +305,18 @@ static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn) wpabuf_put_le16(buf, 1); wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); + if (pasn->sae.tmp) { + if (pasn->sae.tmp->parsed_pw_id) { + pw_id = pasn->sae.tmp->parsed_pw_id; + pw_id_len = pasn->sae.tmp->parsed_pw_id_len; + } else if (pasn->sae.tmp->pw_id) { + pw_id = pasn->sae.tmp->pw_id; + pw_id_len = pasn->sae.tmp->pw_id_len; + } + } + /* Write the actual commit and update the length accordingly */ - sae_write_commit(&pasn->sae, buf, NULL, 0); + sae_write_commit(&pasn->sae, buf, NULL, pw_id, pw_id_len); len = wpabuf_len(buf); WPA_PUT_LE16(len_ptr, len - 2); @@ -233,7 +357,7 @@ static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); /* Authentication Transaction seq# */ - wpabuf_put_le16(buf, 2); + wpabuf_put_le16(buf, WLAN_AUTH_TR_SEQ_PASN_AUTH2); /* Status Code */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); @@ -243,9 +367,9 @@ static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, 1 + NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, fils->anonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); @@ -270,7 +394,14 @@ static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn) case WPA_KEY_MGMT_PASN: /* no wrapped data */ return NULL; +#ifdef CONFIG_ENC_ASSOC + case WPA_KEY_MGMT_EPPKE: + /* EPPKE without base AKM: no wrapped data per + * IEEE P802.11bi/D4.0, 12.16.9.1 */ + return NULL; +#endif /* CONFIG_ENC_ASSOC */ case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: #ifdef CONFIG_SAE return pasn_get_sae_wd(pasn); #else /* CONFIG_SAE */ @@ -315,23 +446,25 @@ pasn_derive_keys(struct pasn_data *pasn, if (!cached_pmk || !cached_pmk_len) wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); - if (pasn->akmp == WPA_KEY_MGMT_PASN) { - wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); - - pmk_len = WPA_PASN_PMK_LEN; - os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); - } else if (cached_pmk && cached_pmk_len) { + if (cached_pmk && cached_pmk_len) { wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); pmk_len = cached_pmk_len; os_memcpy(pmk, cached_pmk, cached_pmk_len); + } else if (pasn->akmp == WPA_KEY_MGMT_PASN || + pasn->akmp == WPA_KEY_MGMT_EPPKE) { + wpa_printf(MSG_DEBUG, "PASN/EPPKE: Using default PMK"); + + pmk_len = WPA_PASN_PMK_LEN; + os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); } else { switch (pasn->akmp) { #ifdef CONFIG_SAE case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_SAE_EXT_KEY: if (pasn->sae.state == SAE_COMMITTED) { - pmk_len = PMK_LEN; - os_memcpy(pmk, pasn->sae.pmk, PMK_LEN); + pmk_len = pasn->sae.pmk_len; + os_memcpy(pmk, pasn->sae.pmk, pmk_len); break; } #endif /* CONFIG_SAE */ @@ -346,10 +479,25 @@ pasn_derive_keys(struct pasn_data *pasn, pasn->pmk_len = pmk_len; os_memcpy(pasn->pmk, pmk, pmk_len); + +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) + own_addr = pasn->mld_addr; + + if (pasn->derive_kek) { + if (!pasn->kek_len) + pasn->kek_len = wpa_kek_len(pasn->akmp, + pasn->pmk_len); + wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len); + } +#endif /* CONFIG_ENC_ASSOC */ + ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, - pasn->cipher, pasn->kdk_len); + pasn->cipher, pasn->kdk_len, pasn->kek_len, + &pasn->hash_alg, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); return -1; @@ -386,7 +534,8 @@ static void handle_auth_pasn_comeback(struct pasn_data *pasn, return; wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, - WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY, + pasn->auth_alg == WLAN_AUTH_EPPKE); /* * Do not include the group as a part of the token since it is not going @@ -414,7 +563,7 @@ static void handle_auth_pasn_comeback(struct pasn_data *pasn, "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr)); ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), - wpabuf_len(buf), 0, 0, 0); + wpabuf_len(buf), 0, pasn->freq, 0); if (ret) wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); @@ -444,17 +593,34 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, goto fail; wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, - status); + status, pasn->auth_alg == WLAN_AUTH_EPPKE); - if (status != WLAN_STATUS_SUCCESS) + if (status == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + const int *groups = pasn->pasn_groups; + +#ifdef CONFIG_TESTING_OPTIONS + if (pasn->pasn_test_groups) { + wpa_printf(MSG_INFO, + "PASN: Override supported groups with test data"); + groups = pasn->pasn_test_groups; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_add_supported_groups(buf, groups); + } + if (status != WLAN_STATUS_SUCCESS) { + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, + pasn->extra_ies_len); goto done; + } if (pmksa && pasn->custom_pmkid_valid) pmkid = pasn->custom_pmkid; else if (pmksa) { pmkid = pmksa->pmkid; #ifdef CONFIG_SAE - } else if (pasn->akmp == WPA_KEY_MGMT_SAE) { + } else if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID"); pmkid = pasn->sae.pmkid; #endif /* CONFIG_SAE */ @@ -466,10 +632,26 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, #endif /* CONFIG_FILS */ } - if (wpa_pasn_add_rsne(buf, pmkid, - pasn->akmp, pasn->cipher) < 0) - goto fail; + if (false) { +#ifdef CONFIG_ENC_ASSOC + } else if (pasn->auth_alg == WLAN_AUTH_EPPKE) { + int res; + res = wpa_write_eppke_rsne(pasn->rsn_ie, pasn->rsn_ie_len, + wpabuf_put(buf, 0), + wpabuf_tailroom(buf), + pmkid, pasn->akmp, pasn->cipher, + pasn->ieee80211w); + if (res < 0) + goto fail; + + wpabuf_put(buf, res); +#endif /* CONFIG_ENC_ASSOC */ + } else { + if (wpa_pasn_add_rsne(buf, pmkid, + pasn->akmp, pasn->cipher) < 0) + goto fail; + } /* No need to derive PMK if PMKSA is given */ if (!pmksa) wrapped_data_buf = pasn_get_wrapped_data(pasn); @@ -485,27 +667,45 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, goto fail; } - wpa_pasn_add_parameter_ie(buf, pasn->group, - pasn->wrapped_data_format, - pubkey, true, NULL, 0); - if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) goto fail; wpabuf_free(wrapped_data_buf); wrapped_data_buf = NULL; - wpabuf_free(pubkey); - pubkey = NULL; /* Add RSNXE if needed */ rsnxe_ie = pasn->rsnxe_ie; if (rsnxe_ie) wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); + wpa_pasn_add_parameter_ie(buf, pasn->group, + pasn->wrapped_data_format, + pubkey, true, NULL, 0); + + wpabuf_free(pubkey); + pubkey = NULL; + + if (pasn->prepare_data_element && pasn->cb_ctx) + pasn->prepare_data_element(pasn->cb_ctx, peer_addr); + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + wpa_add_supported_groups(buf, pasn->pasn_groups); + +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) { + wpa_printf(MSG_DEBUG, "EPPKE: Add Multi Link element"); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 10); + wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK); + wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC); + wpabuf_put_u8(buf, ETH_ALEN + 1); + wpabuf_put_data(buf, pasn->mld_addr, ETH_ALEN); + } +#endif /* CONFIG_ENC_ASSOC */ + /* Add the mic */ - mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); + mic_len = pasn_mic_len(pasn->hash_alg); wpabuf_put_u8(buf, WLAN_EID_MIC); wpabuf_put_u8(buf, mic_len); ptr = wpabuf_put(buf, mic_len); @@ -555,7 +755,12 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, data = rsn_ie; } - ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, own_addr, peer_addr, data, data_len, frame, frame_len, mic); os_free(data_buf); @@ -577,14 +782,16 @@ done: wpa_printf(MSG_DEBUG, "PASN: Building frame 2: success; resp STA=" MACSTR, MAC2STR(peer_addr)); + wpabuf_free(pasn->frame); + pasn->frame = NULL; ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), - wpabuf_len(buf), 0, 0, 0); + wpabuf_len(buf), 0, pasn->freq, 0); if (ret) wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); wpabuf_free(rsn_buf); - wpabuf_free(buf); + pasn->frame = buf; return ret; fail: wpabuf_free(wrapped_data_buf); @@ -595,9 +802,41 @@ fail: } +static struct rsn_pmksa_cache_entry * +pasn_resp_find_pmksa(struct pasn_data *pasn, const struct ieee80211_mgmt *mgmt, + const struct ieee802_11_elems *elems, const u8 *peer_addr, + const u8 *pmkid) +{ +#ifdef CONFIG_PMKSA_PRIVACY + /* Per IEEE 802.11bi/D4.0, 12.16.7 (PMKSA caching privacy), a STA can + * randomize its MAC address or MLD MAC address to avoid tracking based + * on the MAC address. With PMKSA caching privacy, PMKSA entries can + * still be found using the latest derived PMKID. In this cases, the MAC + * address matching is not used. */ + if (pasn->pmksa_cache_search && + mgmt->u.auth.auth_alg == WLAN_AUTH_EPPKE && + pasn->pmksa_caching_privacy && + ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) { + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = pasn->pmksa_cache_search(pasn->cb_ctx, NULL, pmkid, + pasn->is_ml_peer); + if (pmksa) { + os_memcpy(pasn->epp_pmkid_cur, pmkid, PMKID_LEN); + return pmksa; + } + } +#endif /* CONFIG_PMKSA_PRIVACY */ + + return pmksa_cache_auth_get(pasn->pmksa, peer_addr, pmkid); +} + + int handle_auth_pasn_1(struct pasn_data *pasn, const u8 *own_addr, const u8 *peer_addr, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + bool reject) { struct ieee802_11_elems elems; struct wpa_ie_data rsn_data; @@ -616,6 +855,12 @@ int handle_auth_pasn_1(struct pasn_data *pasn, if (!groups) groups = default_groups; + if (reject) { + wpa_printf(MSG_DEBUG, "PASN: Received Rejection"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } + if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - offsetof(struct ieee80211_mgmt, u.auth.variable), @@ -628,7 +873,7 @@ int handle_auth_pasn_1(struct pasn_data *pasn, if (!elems.rsn_ie) { wpa_printf(MSG_DEBUG, "PASN: No RSNE"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto send_resp; } @@ -636,27 +881,50 @@ int handle_auth_pasn_1(struct pasn_data *pasn, &rsn_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto send_resp; } - ret = wpa_pasn_validate_rsne(&rsn_data); + ret = wpa_pasn_validate_rsne(&rsn_data, + pasn->auth_alg == WLAN_AUTH_EPPKE); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto send_resp; } +#ifdef CONFIG_ENC_ASSOC + /* IEEE 802.11bi/D4.0, 12.16.9.1 (Enhanced privacy protection key + * exchange) allows the use of SAE-EXT/FT-SAE-EXT as the base AKMP. + * When not using mutual authentication, the EPPKE AKMP itself is + * used as the selected AKMP in the RSNE (no base AKM case). + */ + if (mgmt->u.auth.auth_alg == WLAN_AUTH_EPPKE && + !wpa_key_mgmt_sae_ext_key(rsn_data.key_mgmt) && + rsn_data.key_mgmt != WPA_KEY_MGMT_EPPKE) { + wpa_printf(MSG_DEBUG, "EPPKE: Invalid base AKM"); + status = WLAN_STATUS_INVALID_RSNE; + goto send_resp; + } +#endif /* CONFIG_ENC_ASSOC */ + if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) || !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) { wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto send_resp; } pasn->akmp = rsn_data.key_mgmt; pasn->cipher = rsn_data.pairwise_cipher; +#ifdef CONFIG_ENC_ASSOC + /* For EPPKE without base AKM, use eppke_unauth to control whether + * unauthenticated EPPKE is allowed. */ + if (pasn->akmp == WPA_KEY_MGMT_EPPKE) + pasn->noauth = pasn->eppke_unauth; +#endif /* CONFIG_ENC_ASSOC */ + if (pasn->derive_kdk && ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, WLAN_RSNX_CAPAB_SECURE_LTF)) @@ -669,6 +937,27 @@ int handle_auth_pasn_1(struct pasn_data *pasn, wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len); + if (!pasn->derive_kek || + !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_KEK_IN_PASN)) { + pasn->kek_len = 0; + pasn->derive_kek = false; + } + + wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len); + +#ifdef CONFIG_ENC_ASSOC + if (mgmt->u.auth.auth_alg == WLAN_AUTH_EPPKE && + !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) + { + wpa_printf(MSG_DEBUG, + "EPPKE: Missing (Re)Association Request/Response frame encryption support in RSNXE"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; + } +#endif /* CONFIG_ENC_ASSOC */ + if (!elems.pasn_params || !elems.pasn_params_len) { wpa_printf(MSG_DEBUG, "PASN: No PASN Parameters element found"); @@ -686,6 +975,17 @@ int handle_auth_pasn_1(struct pasn_data *pasn, goto send_resp; } +#ifdef CONFIG_TESTING_OPTIONS + /* + * To simulate a downgrade attack, reject the group based on + * pasn_test_groups and send a tampered Supported Groups element + * instead of the real AP Supported Groups list in the rejection + * frame. + */ + if (pasn->pasn_test_groups) + groups = pasn->pasn_test_groups; +#endif /* CONFIG_TESTING_OPTIONS */ + for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) ; @@ -723,6 +1023,10 @@ int handle_auth_pasn_1(struct pasn_data *pasn, return -1; } + if (pasn->ecdh) { + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = NULL; + } pasn->ecdh = crypto_ecdh_init(pasn_params.group); if (!pasn->ecdh) { wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); @@ -754,8 +1058,10 @@ int handle_auth_pasn_1(struct pasn_data *pasn, goto send_resp; } - if (!pasn->noauth && pasn->akmp == WPA_KEY_MGMT_PASN) { - wpa_printf(MSG_DEBUG, "PASN: Refuse PASN-UNAUTH"); + if (!pasn->noauth && (pasn->akmp == WPA_KEY_MGMT_PASN || + pasn->akmp == WPA_KEY_MGMT_EPPKE) && + (!rsn_data.num_pmkid || !pasn->pmksa)) { + wpa_printf(MSG_DEBUG, "PASN/EPPKE: Refuse UNAUTH"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } @@ -771,14 +1077,18 @@ int handle_auth_pasn_1(struct pasn_data *pasn, } #ifdef CONFIG_SAE - if (pasn->akmp == WPA_KEY_MGMT_SAE) { + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { ret = pasn_wd_handle_sae_commit(pasn, own_addr, peer_addr, wrapped_data); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed processing SAE commit"); - status = WLAN_STATUS_UNSPECIFIED_FAILURE; + if (ret == -2) + status = WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + else + status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } } @@ -807,11 +1117,11 @@ int handle_auth_pasn_1(struct pasn_data *pasn, pasn->wrapped_data_format = pasn_params.wrapped_data_format; - ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, - ((const u8 *) mgmt) + IEEE80211_HDRLEN, - len - IEEE80211_HDRLEN, pasn->hash); - if (ret) { - wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN); + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; goto send_resp; } @@ -861,12 +1171,18 @@ int handle_auth_pasn_1(struct pasn_data *pasn, pmkid = rsn_data.pmkid; } - pmksa = pmksa_cache_auth_get(pasn->pmksa, + pmksa = pasn_resp_find_pmksa(pasn, mgmt, &elems, peer_addr, pmkid); if (pmksa) { cached_pmk = pmksa->pmk; cached_pmk_len = pmksa->pmk_len; + } else if (!pasn->noauth && + pasn->akmp == WPA_KEY_MGMT_PASN) { + wpa_printf(MSG_DEBUG, + "PASN: No PMKSA entry found for PASN-UNAUTH"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto send_resp; } } } @@ -879,15 +1195,31 @@ int handle_auth_pasn_1(struct pasn_data *pasn, &pasn_params, wrapped_data, secret); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); - status = WLAN_STATUS_PASN_BASE_AKMP_FAILED; + if (rsn_data.num_pmkid && !wrapped_data && !cached_pmk) + status = WLAN_STATUS_INVALID_PMKID; + else + status = WLAN_STATUS_PASN_BASE_AKMP_FAILED; goto send_resp; } - ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, - ((const u8 *) mgmt) + IEEE80211_HDRLEN, - len - IEEE80211_HDRLEN, pasn->hash); - if (ret) { - wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && !pasn->authorized && + pasn->eppke_set_key) { + /* TODO: Support VLAN ID assignment based on configured + * SAE passwords. */ + pasn->eppke_set_key(pasn->cb_ctx, + wpa_cipher_to_alg(pasn->cipher), + peer_addr, 0, pasn->ptk.tk, + pasn->ptk.tk_len); + pasn->tk_configured = true; + } +#endif /* CONFIG_ENC_ASSOC */ + + wpabuf_free(pasn->auth1); + pasn->auth1 = wpabuf_alloc_copy(((const u8 *) mgmt) + IEEE80211_HDRLEN, + len - IEEE80211_HDRLEN); + if (!pasn->auth1) { + wpa_printf(MSG_DEBUG, "PASN: Failed to store a copy of Auth1"); status = WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -923,6 +1255,7 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, int ret; u8 *copy = NULL; size_t copy_len, mic_offset; + u8 hash[SHA512_MAC_LEN]; if (ieee802_11_parse_elems(mgmt->u.auth.variable, len - offsetof(struct ieee80211_mgmt, @@ -934,7 +1267,7 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, } /* Check that the MIC IE exists. Save it and zero out the memory. */ - mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); + mic_len = pasn_mic_len(pasn->hash_alg); if (!elems.mic || elems.mic_len != mic_len) { wpa_printf(MSG_DEBUG, "PASN: Invalid MIC. Expecting len=%u", mic_len); @@ -963,6 +1296,11 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, goto fail; } +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE && pasn->is_ml_peer) + own_addr = pasn->mld_addr; +#endif /* CONFIG_ENC_ASSOC */ + /* Verify the MIC */ copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); mic_offset = elems.mic - (const u8 *) &mgmt->u.auth; @@ -973,9 +1311,14 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, if (!copy) goto fail; os_memset(copy + mic_offset, 0, mic_len); - ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, - peer_addr, own_addr, - pasn->hash, mic_len * 2, + if (!pasn->auth1 || + pasn_auth_frame_hash(pasn->hash_alg, wpabuf_head(pasn->auth1), + wpabuf_len(pasn->auth1), hash)) { + wpa_printf(MSG_INFO, "PASN: Failed to calculate Auth1 hash"); + goto fail; + } + ret = pasn_mic(pasn->hash_alg, pasn->ptk.kck, pasn->ptk.kck_len, + peer_addr, own_addr, hash, mic_len * 2, copy, copy_len, out_mic); os_free(copy); copy = NULL; @@ -997,7 +1340,8 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, } #ifdef CONFIG_SAE - if (pasn->akmp == WPA_KEY_MGMT_SAE) { + if (pasn->akmp == WPA_KEY_MGMT_SAE || + pasn->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { ret = pasn_wd_handle_sae_confirm(pasn, peer_addr, wrapped_data); if (ret) { @@ -1020,6 +1364,13 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, wpabuf_free(wrapped_data); } + if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable)) < 0) { + wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed"); + goto fail; + } + wpa_printf(MSG_INFO, "PASN: Success handling transaction == 3. Store PTK"); return 0; diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c index 37aa216b170b..40c037dcc0ad 100644 --- a/contrib/wpa/src/radius/radius.c +++ b/contrib/wpa/src/radius/radius.c @@ -469,8 +469,10 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, return -1; msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), pos) < 0) + wpabuf_len(msg->buf), pos) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); return -1; + } } else msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); @@ -497,8 +499,10 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), pos) < 0) + wpabuf_len(msg->buf), pos) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); return -1; + } /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = (u8 *) msg->hdr; @@ -509,7 +513,10 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; - md5_vector(4, addr, len, msg->hdr->authenticator); + if (md5_vector(4, addr, len, msg->hdr->authenticator) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", @@ -535,16 +542,20 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), pos) < 0) + wpabuf_len(msg->buf), pos) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); return -1; + } /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = wpabuf_head_u8(msg->buf); len[0] = wpabuf_len(msg->buf); addr[1] = secret; len[1] = secret_len; - if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); return -1; + } if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", @@ -555,8 +566,8 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, } -void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, - size_t secret_len) +int radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, + size_t secret_len) { const u8 *addr[2]; size_t len[2]; @@ -567,17 +578,22 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, len[0] = wpabuf_len(msg->buf); addr[1] = secret; len[1] = secret_len; - md5_vector(2, addr, len, msg->hdr->authenticator); + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", (unsigned long) wpabuf_len(msg->buf)); + return -1; } + return 0; } -void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, - size_t secret_len, const u8 *req_authenticator) +int radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator) { const u8 *addr[2]; size_t len[2]; @@ -588,12 +604,17 @@ void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, len[0] = wpabuf_len(msg->buf); addr[1] = secret; len[1] = secret_len; - md5_vector(2, addr, len, msg->hdr->authenticator); + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } if (wpabuf_len(msg->buf) > 0xffff) { wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", (unsigned long) wpabuf_len(msg->buf)); + return -1; } + return 0; } @@ -614,7 +635,10 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; - md5_vector(4, addr, len, hash); + if (md5_vector(4, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return 1; + } return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; } @@ -642,7 +666,10 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; - md5_vector(4, addr, len, hash); + if (md5_vector(4, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return 1; + } if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) return 1; @@ -674,8 +701,11 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, sizeof(orig_authenticator)); os_memset(msg->hdr->authenticator, 0, sizeof(msg->hdr->authenticator)); - hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), auth); + if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return 1; + } os_memcpy(attr + 1, orig, MD5_MAC_LEN); os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); @@ -777,7 +807,7 @@ struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type, ext->length = sizeof(*ext) + 1 + alen; ext->ext_type = ext_type; wpabuf_put_u8(msg->buf, data_len > alen ? 0x80 : 0); - wpabuf_put_data(msg->buf, data, data_len); + wpabuf_put_data(msg->buf, data, alen); data += alen; data_len -= alen; if (radius_msg_add_attr_to_array( @@ -972,8 +1002,10 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, sizeof(msg->hdr->authenticator)); } if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), auth) < 0) + wpabuf_len(msg->buf), auth) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); return 1; + } os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, @@ -1185,6 +1217,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, elen[1] = MD5_MAC_LEN; } if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); os_free(plain); return NULL; } @@ -1213,10 +1246,10 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, } -static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, - const u8 *req_authenticator, - const u8 *secret, size_t secret_len, - u8 *ebuf, size_t *elen) +static int encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + u8 *ebuf, size_t *elen) { int i, len, first = 1; u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; @@ -1250,7 +1283,10 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, addr[1] = pos - MD5_MAC_LEN; _len[1] = MD5_MAC_LEN; } - md5_vector(first ? 3 : 2, addr, _len, hash); + if (md5_vector(first ? 3 : 2, addr, _len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } first = 0; for (i = 0; i < MD5_MAC_LEN; i++) @@ -1258,6 +1294,8 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, len -= MD5_MAC_LEN; } + + return 0; } @@ -1375,8 +1413,9 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, salt |= 0x8000; WPA_PUT_BE16(pos, salt); pos += 2; - encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, - secret_len, pos, &elen); + if (encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, + secret, secret_len, pos, &elen) < 0) + return 0; vhdr->vendor_length = hlen + elen - sizeof(vendor_id); attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, @@ -1400,8 +1439,9 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, salt ^= 1; WPA_PUT_BE16(pos, salt); pos += 2; - encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, - secret_len, pos, &elen); + if (encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, + secret, secret_len, pos, &elen) < 0) + return 0; vhdr->vendor_length = hlen + elen - sizeof(vendor_id); attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, @@ -1492,7 +1532,10 @@ int radius_user_password_hide(struct radius_msg *msg, len[0] = secret_len; addr[1] = msg->hdr->authenticator; len[1] = 16; - md5_vector(2, addr, len, hash); + if (md5_vector(2, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } for (i = 0; i < 16; i++) buf[i] ^= hash[i]; @@ -1503,7 +1546,10 @@ int radius_user_password_hide(struct radius_msg *msg, len[0] = secret_len; addr[1] = &buf[pos - 16]; len[1] = 16; - md5_vector(2, addr, len, hash); + if (md5_vector(2, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + return -1; + } for (i = 0; i < 16; i++) buf[pos + i] ^= hash[i]; @@ -1792,7 +1838,10 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, len[0] = secret_len; addr[1] = pos - 16; len[1] = 16; - md5_vector(2, addr, len, hash); + if (md5_vector(2, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + goto out; + } for (i = 0; i < 16; i++) pos[i] ^= hash[i]; @@ -1809,7 +1858,10 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, len[1] = 16; addr[2] = salt; len[2] = 2; - md5_vector(3, addr, len, hash); + if (md5_vector(3, addr, len, hash) < 0) { + wpa_printf(MSG_INFO, "RADIUS: MD5 not available"); + goto out; + } for (i = 0; i < 16; i++) pos[i] ^= hash[i]; diff --git a/contrib/wpa/src/radius/radius.h b/contrib/wpa/src/radius/radius.h index 05fddbaf25bf..09d3591c535e 100644 --- a/contrib/wpa/src/radius/radius.h +++ b/contrib/wpa/src/radius/radius.h @@ -221,7 +221,6 @@ enum { #define RADIUS_VENDOR_ID_WFA 40808 enum { - RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2, RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3, RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4, @@ -276,11 +275,10 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, size_t secret_len, const struct radius_hdr *req_hdr); -void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, - size_t secret_len); -void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, - size_t secret_len, - const u8 *req_authenticator); +int radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator); int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, diff --git a/contrib/wpa/src/radius/radius_client.c b/contrib/wpa/src/radius/radius_client.c index 2a7f36170264..1d98f76f698d 100644 --- a/contrib/wpa/src/radius/radius_client.c +++ b/contrib/wpa/src/radius/radius_client.c @@ -482,8 +482,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, wpa_printf(MSG_DEBUG, "RADIUS: Updated Acct-Delay-Time to %u for retransmission", delay_time); - radius_msg_finish_acct(entry->msg, entry->shared_secret, - entry->shared_secret_len); + if (radius_msg_finish_acct(entry->msg, entry->shared_secret, + entry->shared_secret_len) < 0) { + wpa_printf(MSG_INFO, "Failed to build RADIUS message"); + return -1; + } if (radius->conf->msg_dumps) radius_msg_dump(entry->msg); } @@ -792,7 +795,7 @@ static void radius_close_auth_socket(struct radius_client_data *radius) { if (radius->auth_sock >= 0) { #ifdef CONFIG_RADIUS_TLS - if (radius->conf->auth_server->tls) + if (radius->auth_tls) eloop_unregister_sock(radius->auth_sock, EVENT_TYPE_WRITE); #endif /* CONFIG_RADIUS_TLS */ @@ -807,7 +810,7 @@ static void radius_close_acct_socket(struct radius_client_data *radius) { if (radius->acct_sock >= 0) { #ifdef CONFIG_RADIUS_TLS - if (radius->conf->acct_server->tls) + if (radius->acct_tls) eloop_unregister_sock(radius->acct_sock, EVENT_TYPE_WRITE); #endif /* CONFIG_RADIUS_TLS */ @@ -878,7 +881,14 @@ int radius_client_send(struct radius_client_data *radius, } shared_secret = conf->acct_server->shared_secret; shared_secret_len = conf->acct_server->shared_secret_len; - radius_msg_finish_acct(msg, shared_secret, shared_secret_len); + if (radius_msg_finish_acct(msg, shared_secret, + shared_secret_len) < 0) { + hostapd_logger(radius->ctx, NULL, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Failed to build RADIUS accounting message"); + return -1; + } name = "accounting"; s = radius->acct_sock; conf->acct_server->requests++; @@ -900,7 +910,14 @@ int radius_client_send(struct radius_client_data *radius, } shared_secret = conf->auth_server->shared_secret; shared_secret_len = conf->auth_server->shared_secret_len; - radius_msg_finish(msg, shared_secret, shared_secret_len); + if (radius_msg_finish(msg, shared_secret, shared_secret_len) < + 0) { + hostapd_logger(radius->ctx, NULL, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Failed to build RADIUS authentication message"); + return -1; + } name = "authentication"; s = radius->auth_sock; conf->auth_server->requests++; @@ -1099,7 +1116,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) struct radius_hdr *hdr; struct radius_rx_handler *handlers; size_t num_handlers, i; - struct radius_msg_list *req, *prev_req; + struct radius_msg_list *req, *prev_req, *r; struct os_reltime now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; @@ -1224,7 +1241,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) break; } - prev_req = NULL; req = radius->msgs; while (req) { /* TODO: also match by src addr:port of the packet when using @@ -1236,7 +1252,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) hdr->identifier) break; - prev_req = req; req = req->next; } @@ -1259,13 +1274,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) roundtrip / 100, roundtrip % 100); rconf->round_trip_time = roundtrip; - /* Remove ACKed RADIUS packet from retransmit list */ - if (prev_req) - prev_req->next = req->next; - else - radius->msgs = req->next; - radius->num_msgs--; - for (i = 0; i < num_handlers; i++) { RadiusRxResult res; res = handlers[i].handler(msg, req->msg, req->shared_secret, @@ -1276,6 +1284,19 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) radius_msg_free(msg); /* fall through */ case RADIUS_RX_QUEUED: + /* Remove ACKed RADIUS packet from retransmit list */ + prev_req = NULL; + for (r = radius->msgs; r; r = r->next) { + if (r == req) + break; + prev_req = r; + } + if (prev_req) + prev_req->next = req->next; + else + radius->msgs = req->next; + radius->num_msgs--; + radius_client_msg_free(req); return; case RADIUS_RX_INVALID_AUTHENTICATOR: @@ -1297,7 +1318,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) msg_type, hdr->code, hdr->identifier, invalid_authenticator ? " [INVALID AUTHENTICATOR]" : ""); - radius_client_msg_free(req); fail: radius_msg_free(msg); @@ -1509,8 +1529,10 @@ static void radius_client_update_acct_msgs(struct radius_client_data *radius, if (entry->msg_type == RADIUS_ACCT) { entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; - radius_msg_finish_acct(entry->msg, shared_secret, - shared_secret_len); + if (radius_msg_finish_acct(entry->msg, shared_secret, + shared_secret_len) < 0) + wpa_printf(MSG_INFO, + "RADIUS: Failed to update accounting message"); } } } diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c index fa36915489cb..742b46a9ad07 100644 --- a/contrib/wpa/src/radius/radius_server.c +++ b/contrib/wpa/src/radius/radius_server.c @@ -86,7 +86,6 @@ struct radius_session { struct radius_msg *last_reply; u8 last_authenticator[16]; - unsigned int remediation:1; unsigned int macacl:1; unsigned int t_c_filtering:1; @@ -147,7 +146,8 @@ struct radius_server_data { /** * conf_ctx - Context pointer for callbacks * - * This is used as the ctx argument in get_eap_user() calls. + * This is used as the ctx argument in get_eap_user() and acct_req_cb() + * calls. */ void *conf_ctx; @@ -194,6 +194,27 @@ struct radius_server_data { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); + /** + * acct_req_cb - Callback for processing received RADIUS accounting + * requests + * @ctx: Context data from conf_ctx + * @msg: Received RADIUS accounting request + * @status_type: Status type from the message (parsed Acct-Status-Type + * attribute) + * Returns: 0 on success, -1 on failure + * + * This can be used to log accounting information into file, database, + * syslog server, etc. + * Callback should not modify the message. + * If 0 is returned, response is automatically created. Otherwise, + * no response is created. + * + * acct_req_cb can be set to null to omit any custom processing of + * account requests. Statistics counters will be incremented in any + * case. + */ + int (*acct_req_cb)(void *ctx, struct radius_msg *msg, u32 status_type); + /** * eap_req_id_text - Optional data for EAP-Request/Identity * @@ -215,10 +236,6 @@ struct radius_server_data { char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ - char *subscr_remediation_url; - u8 subscr_remediation_method; - char *hs20_sim_provisioning_url; - char *t_c_server_url; #ifdef CONFIG_SQLITE @@ -243,44 +260,6 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx); -#ifdef CONFIG_SQLITE -#ifdef CONFIG_HS20 - -static int db_table_exists(sqlite3 *db, const char *name) -{ - char cmd[128]; - - os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); - return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; -} - - -static int db_table_create_sim_provisioning(sqlite3 *db) -{ - char *err = NULL; - const char *sql = - "CREATE TABLE sim_provisioning(" - " mobile_identifier_hash TEXT PRIMARY KEY," - " imsi TEXT," - " mac_addr TEXT," - " eap_method TEXT," - " timestamp TEXT" - ");"; - - RADIUS_DEBUG("Adding database table for SIM provisioning information"); - if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { - RADIUS_ERROR("SQLite error: %s", err); - sqlite3_free(err); - return -1; - } - - return 0; -} - -#endif /* CONFIG_HS20 */ -#endif /* CONFIG_SQLITE */ - - void srv_log(struct radius_session *sess, const char *fmt, ...) PRINTF_FORMAT(2, 3); @@ -395,7 +374,8 @@ static void radius_server_session_free(struct radius_server_data *data, os_free(sess->username); os_free(sess->nas_ip); os_free(sess); - data->num_sess--; + if (data) + data->num_sess--; } @@ -780,117 +760,6 @@ static void db_update_last_msk(struct radius_session *sess, const char *msk) } -#ifdef CONFIG_HS20 - -static int radius_server_is_sim_method(struct radius_session *sess) -{ - const char *name; - - name = eap_get_method(sess->eap); - return name && - (os_strcmp(name, "SIM") == 0 || - os_strcmp(name, "AKA") == 0 || - os_strcmp(name, "AKA'") == 0); -} - - -static int radius_server_hs20_missing_sim_pps(struct radius_msg *request) -{ - u8 *buf, *pos, *end, type, sublen; - size_t len; - - buf = NULL; - for (;;) { - if (radius_msg_get_attr_ptr(request, - RADIUS_ATTR_VENDOR_SPECIFIC, - &buf, &len, buf) < 0) - return 0; - if (len < 6) - continue; - pos = buf; - end = buf + len; - if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) - continue; - pos += 4; - - type = *pos++; - sublen = *pos++; - if (sublen < 2) - continue; /* invalid length */ - sublen -= 2; /* skip header */ - if (pos + sublen > end) - continue; /* invalid WFA VSA */ - - if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION) - continue; - - RADIUS_DUMP("HS2.0 mobile device version", pos, sublen); - if (sublen < 1 + 2) - continue; - if (pos[0] == 0) - continue; /* Release 1 STA does not support provisioning - - */ - /* UpdateIdentifier 0 indicates no PPS MO */ - return WPA_GET_BE16(pos + 1) == 0; - } -} - - -#define HS20_MOBILE_ID_HASH_LEN 16 - -static int radius_server_sim_provisioning_session(struct radius_session *sess, - const u8 *hash) -{ -#ifdef CONFIG_SQLITE - char *sql; - char addr_txt[ETH_ALEN * 3]; - char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1]; - struct os_time now; - int res; - const char *imsi, *eap_method; - - if (!sess->server->db || - (!db_table_exists(sess->server->db, "sim_provisioning") && - db_table_create_sim_provisioning(sess->server->db) < 0)) - return -1; - - imsi = eap_get_imsi(sess->eap); - if (!imsi) - return -1; - - eap_method = eap_get_method(sess->eap); - if (!eap_method) - return -1; - - os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, - MAC2STR(sess->mac_addr)); - wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash, - HS20_MOBILE_ID_HASH_LEN); - - os_get_time(&now); - sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)", - hash_txt, imsi, addr_txt, eap_method, now.sec); - if (!sql) - return -1; - - if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != - SQLITE_OK) { - RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s", - sqlite3_errmsg(sess->server->db)); - res = -1; - } else { - res = 0; - } - sqlite3_free(sql); - return res; -#endif /* CONFIG_SQLITE */ - return -1; -} - -#endif /* CONFIG_HS20 */ - - static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, @@ -992,74 +861,6 @@ radius_server_encapsulate_eap(struct radius_server_data *data, } #ifdef CONFIG_HS20 - if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation && - data->subscr_remediation_url) { - u8 *buf; - size_t url_len = os_strlen(data->subscr_remediation_url); - buf = os_malloc(1 + url_len); - if (buf == NULL) { - radius_msg_free(msg); - return NULL; - } - buf[0] = data->subscr_remediation_method; - os_memcpy(&buf[1], data->subscr_remediation_url, url_len); - if (!radius_msg_add_wfa( - msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, - buf, 1 + url_len)) { - RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); - } - os_free(buf); - } else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) { - u8 buf[1]; - if (!radius_msg_add_wfa( - msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, - buf, 0)) { - RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); - } - } else if (code == RADIUS_CODE_ACCESS_ACCEPT && - data->hs20_sim_provisioning_url && - radius_server_is_sim_method(sess) && - radius_server_hs20_missing_sim_pps(request)) { - u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN]; - size_t prefix_len, url_len; - - RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning"); - - if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) { - radius_msg_free(msg); - return NULL; - } - RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash", - hash, HS20_MOBILE_ID_HASH_LEN); - - if (radius_server_sim_provisioning_session(sess, hash) < 0) { - radius_msg_free(msg); - return NULL; - } - - prefix_len = os_strlen(data->hs20_sim_provisioning_url); - url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN; - buf = os_malloc(1 + url_len + 1); - if (!buf) { - radius_msg_free(msg); - return NULL; - } - pos = buf; - *pos++ = data->subscr_remediation_method; - os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len); - pos += prefix_len; - wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1, - hash, HS20_MOBILE_ID_HASH_LEN); - RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s", - (char *) &buf[1]); - if (!radius_msg_add_wfa( - msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, - buf, 1 + url_len)) { - RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); - } - os_free(buf); - } - if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ const char *url = data->t_c_server_url, *pos; @@ -1148,6 +949,8 @@ radius_server_encapsulate_eap(struct radius_server_data *data, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + radius_msg_free(msg); + return NULL; } if (code == RADIUS_CODE_ACCESS_ACCEPT) @@ -1237,6 +1040,8 @@ radius_server_macacl(struct radius_server_data *data, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + radius_msg_free(msg); + return NULL; } return msg; @@ -1288,6 +1093,8 @@ static int radius_server_reject(struct radius_server_data *data, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + radius_msg_free(msg); + return -1; } if (wpa_debug_level <= MSG_MSGDUMP) { @@ -1815,6 +1622,7 @@ static void radius_server_receive_acct(int sock, void *eloop_ctx, int from_port = 0; struct radius_hdr *hdr; struct wpabuf *rbuf; + u32 status_type; buf = os_malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { @@ -1896,7 +1704,20 @@ static void radius_server_receive_acct(int sock, void *eloop_ctx, goto fail; } - /* TODO: Write accounting information to a file or database */ + /* Parse Acct-Status-Type from Accounting-Request */ + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, + &status_type) != 0) { + RADIUS_DEBUG("Unable to parse Acct-Status-Type from %s", abuf); + goto fail; + } + + /* Process accounting information by configured callback */ + if (data->acct_req_cb && + data->acct_req_cb(data->conf_ctx, msg, status_type) != 0) { + RADIUS_DEBUG("Accounting request callback returned non-zero code indicating processing failure (from %s)", + abuf); + goto fail; + } hdr = radius_msg_get_hdr(msg); @@ -1904,9 +1725,12 @@ static void radius_server_receive_acct(int sock, void *eloop_ctx, if (resp == NULL) goto fail; - radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret, - client->shared_secret_len, - hdr->authenticator); + if (radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret, + client->shared_secret_len, + hdr->authenticator) < 0) { + RADIUS_ERROR("Failed to add Message-Authenticator attribute"); + goto fail; + } RADIUS_DEBUG("Reply to %s:%d", abuf, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { @@ -2221,6 +2045,7 @@ radius_server_init(struct radius_server_conf *conf) conf->eap_cfg->eap_server = 1; data->ipv6 = conf->ipv6; data->get_eap_user = conf->get_eap_user; + data->acct_req_cb = conf->acct_req_cb; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (!data->eap_req_id_text) @@ -2231,20 +2056,6 @@ radius_server_init(struct radius_server_conf *conf) } data->erp_domain = conf->erp_domain; - if (conf->subscr_remediation_url) { - data->subscr_remediation_url = - os_strdup(conf->subscr_remediation_url); - if (!data->subscr_remediation_url) - goto fail; - } - data->subscr_remediation_method = conf->subscr_remediation_method; - if (conf->hs20_sim_provisioning_url) { - data->hs20_sim_provisioning_url = - os_strdup(conf->hs20_sim_provisioning_url); - if (!data->hs20_sim_provisioning_url) - goto fail; - } - if (conf->t_c_server_url) { data->t_c_server_url = os_strdup(conf->t_c_server_url); if (!data->t_c_server_url) @@ -2359,8 +2170,6 @@ void radius_server_deinit(struct radius_server_data *data) #ifdef CONFIG_RADIUS_TEST os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ - os_free(data->subscr_remediation_url); - os_free(data->hs20_sim_provisioning_url); os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE @@ -2528,7 +2337,6 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, phase2, user); if (ret == 0 && user) { sess->accept_attr = user->accept_attr; - sess->remediation = user->remediation; sess->macacl = user->macacl; sess->t_c_timestamp = user->t_c_timestamp; } @@ -2827,8 +2635,12 @@ int radius_server_dac_request(struct radius_server_data *data, const char *req) return -1; } - radius_msg_finish_acct(msg, (u8 *) client->shared_secret, - client->shared_secret_len); + if (radius_msg_finish_acct(msg, (u8 *) client->shared_secret, + client->shared_secret_len) < 0) { + RADIUS_ERROR("Failed to add Message-Authenticator attribute"); + radius_msg_free(msg); + return -1; + } if (wpa_debug_level <= MSG_MSGDUMP) radius_msg_dump(msg); diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h index 43192e58596c..5440558d785c 100644 --- a/contrib/wpa/src/radius/radius_server.h +++ b/contrib/wpa/src/radius/radius_server.h @@ -10,6 +10,7 @@ #define RADIUS_SERVER_H struct radius_server_data; +struct radius_msg; struct eap_user; /** @@ -47,7 +48,8 @@ struct radius_server_conf { /** * conf_ctx - Context pointer for callbacks * - * This is used as the ctx argument in get_eap_user() calls. + * This is used as the ctx argument in get_eap_user() and acct_req_cb() + * calls. */ void *conf_ctx; @@ -75,6 +77,27 @@ struct radius_server_conf { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); + /** + * acct_req_cb - Callback for processing received RADIUS accounting + * requests + * @ctx: Context data from conf_ctx + * @msg: Received RADIUS accounting request + * @status_type: Status type from the message (parsed Acct-Status-Type + * attribute) + * Returns: 0 on success, -1 on failure + * + * This can be used to log accounting information into file, database, + * syslog server, etc. + * Callback should not modify the message. + * If 0 is returned, response is automatically created. Otherwise, + * no response is created. + * + * acct_req_cb can be set to NULL to omit any custom processing of + * accounting requests. Statistics counters will be incremented in any + * case. + */ + int (*acct_req_cb)(void *ctx, struct radius_msg *msg, u32 status_type); + /** * eap_req_id_text - Optional data for EAP-Request/Identity * @@ -96,10 +119,6 @@ struct radius_server_conf { const char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ - char *subscr_remediation_url; - u8 subscr_remediation_method; - char *hs20_sim_provisioning_url; - char *t_c_server_url; struct eap_config *eap_cfg; diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c index 5bfcbd27e870..9506dfdf1271 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.c +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -221,6 +221,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @network_ctx: Network configuration context for this PMK * @akmp: WPA_KEY_MGMT_* used in key derivation * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised + * @auth_alg: Authentication algorithm used for PMK derivation * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA @@ -232,7 +233,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, - const u8 *cache_id) + const u8 *cache_id, u16 auth_alg) { struct rsn_pmksa_cache_entry *entry; struct os_reltime now; @@ -274,6 +275,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, entry->reauth_time = now.sec + pmk_lifetime * pmk_reauth_threshold / 100; entry->akmp = akmp; + entry->auth_alg = auth_alg; if (cache_id) { entry->fils_cache_id_set = 1; os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN); @@ -286,6 +288,49 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, } +/** + * pmksa_cache_add_to_driver - Add PMKSA entry to driver with remaining lifetime + * @pmksa: Pointer to PMKSA cache data + * @entry: PMKSA cache entry + * + * Calculate the remaining PMK lifetime and reauthentication threshold based + * on the entry's expiration and reauthentication times relative to the current + * time, and configure the PMKSA entry to the driver with the current values. + */ +static void pmksa_cache_add_to_driver(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct os_reltime now; + u32 remaining_lifetime; + u8 remaining_reauth_threshold; + + if (!pmksa->sm) + return; + + os_get_reltime(&now); + + /* Do not configure entries that have expired or require immediate + * reauthentication. */ + if (entry->expiration - now.sec < 1 || + entry->reauth_time - now.sec < 1) + return; + + remaining_lifetime = entry->expiration - now.sec; + remaining_reauth_threshold = ((entry->reauth_time - now.sec) * 100) / + remaining_lifetime; + + /* Do not configure entries that have already reached reauthentication + * threshold. */ + if (!remaining_reauth_threshold) + return; + + wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, + entry->fils_cache_id_set ? entry->fils_cache_id : NULL, + entry->pmk, entry->pmk_len, remaining_lifetime, + remaining_reauth_threshold, entry->akmp); +} + + struct rsn_pmksa_cache_entry * pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) @@ -329,7 +374,7 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, if (!pos->opportunistic) pmksa_cache_flush(pmksa, entry->network_ctx, pos->pmk, pos->pmk_len, - false); + false, NULL); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } @@ -390,12 +435,7 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, if (pmksa->notify_cb) pmksa->notify_cb(entry, pmksa->ctx); - wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, - entry->fils_cache_id_set ? entry->fils_cache_id : NULL, - entry->pmk, entry->pmk_len, - pmksa->sm->dot11RSNAConfigPMKLifetime, - pmksa->sm->dot11RSNAConfigPMKReauthThreshold, - entry->akmp); + pmksa_cache_add_to_driver(pmksa, entry); return entry; } @@ -409,9 +449,11 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, * @pmk_len: PMK length * @external_only: Flush only PMKSA cache entries configured by external * applications + * @addr: AA to match or %NULL to match all addresses */ void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len, bool external_only) + const u8 *pmk, size_t pmk_len, bool external_only, + const u8 *addr) { struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; int removed = 0; @@ -518,7 +560,8 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp, old_entry->fils_cache_id_set ? - old_entry->fils_cache_id : NULL); + old_entry->fils_cache_id : NULL, + old_entry->auth_alg); if (new_entry == NULL) return NULL; @@ -637,6 +680,7 @@ void pmksa_cache_clear_current(struct wpa_sm *sm) * @network_ctx: Network configuration context * @try_opportunistic: Whether to allow opportunistic PMKSA caching * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used + * @akmp: Specific AKMP to search for or 0 for any * @associated: Whether the device is associated * Returns: 0 if PMKSA was found or -1 if no matching entry was found */ @@ -823,33 +867,12 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa) { struct rsn_pmksa_cache_entry *entry; - struct os_reltime now; if (!pmksa || !pmksa->pmksa) return; - os_get_reltime(&now); - for (entry = pmksa->pmksa; entry; entry = entry->next) { - u32 life_time; - u8 reauth_threshold; - - if (entry->expiration - now.sec < 1 || - entry->reauth_time - now.sec < 1) - continue; - - life_time = entry->expiration - now.sec; - reauth_threshold = (entry->reauth_time - now.sec) * 100 / - life_time; - if (!reauth_threshold) - continue; - - wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, - entry->pmkid, - entry->fils_cache_id_set ? - entry->fils_cache_id : NULL, - entry->pmk, entry->pmk_len, life_time, - reauth_threshold, entry->akmp); - } + for (entry = pmksa->pmksa; entry; entry = entry->next) + pmksa_cache_add_to_driver(pmksa, entry); } #else /* IEEE8021X_EAPOL */ @@ -912,7 +935,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, - const u8 *cache_id) + const u8 *cache_id, u16 auth_alg) { return NULL; } @@ -932,7 +955,8 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len, bool external_only) + const u8 *pmk, size_t pmk_len, bool external_only, + const u8 *addr) { } diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h index b1203ad2f0b3..24235264bff6 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.h +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -54,6 +54,7 @@ struct rsn_pmksa_cache_entry { * PMKSA for SAE. */ bool sae_reauth_scheduled; + u16 auth_alg; }; struct rsn_pmksa_cache; @@ -84,7 +85,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp, - const u8 *cache_id); + const u8 *cache_id, u16 auth_alg); struct rsn_pmksa_cache_entry * pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); @@ -98,7 +99,8 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa, int akmp); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *pmk, size_t pmk_len, bool external_only); + const u8 *pmk, size_t pmk_len, bool external_only, + const u8 *addr); void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa); diff --git a/contrib/wpa/src/rsn_supp/preauth.c b/contrib/wpa/src/rsn_supp/preauth.c index 026a98d9ffb0..f234751165c1 100644 --- a/contrib/wpa/src/rsn_supp/preauth.c +++ b/contrib/wpa/src/rsn_supp/preauth.c @@ -108,7 +108,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, - WPA_KEY_MGMT_IEEE8021X, NULL); + WPA_KEY_MGMT_IEEE8021X, NULL, 0); } else { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c index 65960b7bcdf2..06e1560e5c79 100644 --- a/contrib/wpa/src/rsn_supp/tdls.c +++ b/contrib/wpa/src/rsn_supp/tdls.c @@ -988,8 +988,14 @@ static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, return 0; } + if (len < sizeof(struct wpa_tdls_frame) + 2 /* Reason Code */) { + wpa_printf(MSG_INFO, "TDLS: Too short Teardown frame (len=%zu)", + len); + return -1; + } + pos = buf; - pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + pos += sizeof(struct wpa_tdls_frame); reason_code = WPA_GET_LE16(pos); pos += 2; @@ -2132,13 +2138,13 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " "TPK M1"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto error; } @@ -2148,7 +2154,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); - status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + status = WLAN_STATUS_INVALID_PAIRWISE_CIPHER; goto error; } @@ -2157,7 +2163,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, WPA_CAPABILITY_PEERKEY_ENABLED) { wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " "TPK M1"); - status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; + status = WLAN_STATUS_INVALID_RSNE_CAPABILITIES; goto error; } @@ -2461,8 +2467,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, kde.lnkid, kde.lnkid_len); lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; - if (!ether_addr_equal(sm->bssid, - wpa_tdls_get_link_bssid(sm, peer->mld_link_id))) { + if (!ether_addr_equal(wpa_tdls_get_link_bssid(sm, peer->mld_link_id), + lnkid->bssid)) { wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); status = WLAN_STATUS_NOT_IN_SAME_BSS; goto error; @@ -2517,7 +2523,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { wpa_printf(MSG_INFO, "TDLS: Too long Responder RSN IE in TPK M2"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto error; } @@ -2535,13 +2541,13 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, peer->rsnie_i, peer->rsnie_i_len); wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto error; } if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); - status = WLAN_STATUS_INVALID_RSNIE; + status = WLAN_STATUS_INVALID_RSNE; goto error; } @@ -2551,7 +2557,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, cipher = WPA_CIPHER_CCMP; } else { wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); - status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + status = WLAN_STATUS_INVALID_PAIRWISE_CIPHER; goto error; } diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index f23979cd9c98..adf88ebdf829 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -36,6 +36,8 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static const u8 * wpa_sm_get_ap_rsnxe(struct wpa_sm *sm, size_t *len); + static void _wpa_hexdump_link(int level, u8 link_id, const char *title, const void *buf, size_t len, bool key) @@ -93,7 +95,7 @@ int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, u8 *msg, size_t msg_len, u8 *key_mic) { int ret = -1; - size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR " ver=%d mic_len=%d key_mgmt=0x%x", @@ -121,8 +123,9 @@ int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, goto out; if (key_mic && - wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver, - msg, msg_len, key_mic)) { + wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, + sm->hash_alg, ver, msg, msg_len, + key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", ver, sm->key_mgmt); @@ -248,15 +251,14 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; - reply->type = (sm->proto == WPA_PROTO_RSN || - sm->proto == WPA_PROTO_OSEN) ? + reply->type = (sm->proto == WPA_PROTO_RSN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; key_info |= WPA_KEY_INFO_SECURE; @@ -321,7 +323,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr, pmkid, - NULL, 0); + sm->network_ctx, sm->key_mgmt); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); @@ -405,7 +407,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt, - fils_cache_id); + fils_cache_id, 0); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } @@ -427,13 +429,20 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, if (sm->proto == WPA_PROTO_RSN && !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { + u16 auth_alg = 0; + +#ifdef CONFIG_IEEE8021X_AUTH + if (eapol_sm_get_eap_over_auth_frame(sm->eapol)) + auth_alg = WLAN_AUTH_802_1X; +#endif /* CONFIG_IEEE8021X_AUTH */ + sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, NULL, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt, - fils_cache_id); + fils_cache_id, auth_alg); } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr, @@ -481,8 +490,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && !wpa_key_mgmt_suite_b(sm->key_mgmt) && - !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) - { + !wpa_key_mgmt_ft(sm->key_mgmt)) { +#ifdef CONFIG_IEEE8021X_AUTH + if (eapol_sm_get_eap_over_auth_frame(sm->eapol)) { + wpa_printf(MSG_DEBUG, + "RSN: EAP over auth frame - skip EAPOL-Start"); + return 0; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + /* Send EAPOL-Start to trigger full EAP authentication. */ u8 *buf; size_t buflen; @@ -531,7 +547,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, size_t mic_len, hdrlen, rlen, extra_len = 0; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; - u8 *rsn_ie_buf = NULL; + u8 *rsn_ie_buf = NULL, *buf2 = NULL; u16 key_info; #ifdef CONFIG_TESTING_OPTIONS size_t pad_len = 0; @@ -581,6 +597,37 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, } #endif /* CONFIG_IEEE80211R */ + if (sm->rsn_override != RSN_OVERRIDE_NOT_USED) { + u8 *pos; + + buf2 = os_malloc(wpa_ie_len + 2 + 4 + 1); + if (!buf2) { + os_free(rsn_ie_buf); + return -1; + } + os_memcpy(buf2, wpa_ie, wpa_ie_len); + pos = buf2 + wpa_ie_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 1; + WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE); + pos += 4; + if (sm->rsn_override == RSN_OVERRIDE_RSNE) { + *pos++ = RSN_SELECTION_RSNE; + } else if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE) { + *pos++ = RSN_SELECTION_RSNE_OVERRIDE; + } else if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE_2) { + *pos++ = RSN_SELECTION_RSNE_OVERRIDE_2; + } else { + os_free(rsn_ie_buf); + os_free(buf2); + return -1; + } + + wpa_ie = buf2; + wpa_ie_len += 2 + 4 + 1; + + } + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); #ifdef CONFIG_TESTING_OPTIONS @@ -594,18 +641,18 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, } #endif /* CONFIG_TESTING_OPTIONS */ - mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + wpa_ie_len + extra_len, &rlen, (void *) &reply); if (rbuf == NULL) { os_free(rsn_ie_buf); + os_free(buf2); return -1; } - reply->type = (sm->proto == WPA_PROTO_RSN || - sm->proto == WPA_PROTO_OSEN) ? + reply->type = (sm->proto == WPA_PROTO_RSN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = ver | WPA_KEY_INFO_KEY_TYPE; if (sm->ptk_set && sm->proto != WPA_PROTO_WPA) @@ -617,9 +664,11 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, #ifdef CONFIG_TESTING_OPTIONS if (sm->encrypt_eapol_m2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sm->eapol_2_key_info_set_mask) + key_info |= sm->eapol_2_key_info_set_mask; #endif /* CONFIG_TESTING_OPTIONS */ WPA_PUT_BE16(reply->key_info, key_info); - if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) + if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); @@ -633,6 +682,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len + extra_len); os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */ os_free(rsn_ie_buf); + os_free(buf2); #ifdef CONFIG_TESTING_OPTIONS if (sm->test_eapol_m2_elems) { os_memcpy(key_mic + mic_len + 2 + wpa_ie_len, @@ -684,10 +734,12 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { int ret; - const u8 *z = NULL; - size_t z_len = 0, kdk_len; + const u8 *z = NULL, *ap_rsnxe; + size_t z_len = 0, kdk_len, ap_rsnxe_len; int akmp; + ap_rsnxe = wpa_sm_get_ap_rsnxe(sm, &ap_rsnxe_len); + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); @@ -712,7 +764,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, if (sm->force_kdk_derivation || (sm->secure_ltf && - ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))) + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))) kdk_len = WPA_KDK_MAX_LEN; else kdk_len = 0; @@ -729,7 +781,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, #ifdef CONFIG_PASN if (sm->secure_ltf && - ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) ret = wpa_ltf_keyseed(ptk, akmp, sm->pairwise_cipher); #endif /* CONFIG_PASN */ @@ -901,6 +953,7 @@ static void wpa_supplicant_process_1_of_4_wpa(struct wpa_sm *sm, if (sm->pairwise_cipher == WPA_CIPHER_TKIP) wpas_swap_tkip_mic_keys(ptk); sm->tptk_set = 1; + sm->hash_alg = ptk->hash_alg; if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver, sm->snonce, sm->assoc_wpa_ie, @@ -990,6 +1043,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, "WPA: Failed to get random data for SNonce"); goto failed; } + if (wpa_sm_rsn_overriding_supported(sm)) + rsn_set_snonce_cookie(sm->snonce); sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", sm->snonce, WPA_NONCE_LEN); @@ -1003,6 +1058,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, if (sm->pairwise_cipher == WPA_CIPHER_TKIP) wpas_swap_tkip_mic_keys(ptk); sm->tptk_set = 1; + sm->hash_alg = ptk->hash_alg; /* Add MLO Link KDE and MAC KDE in M2 for ML connection */ if (sm->mlo.valid_links) @@ -1189,15 +1245,21 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; +#ifdef CONFIG_PASN + const u8 *ap_rsnxe; + size_t ap_rsnxe_len; +#endif /* CONFIG_PASN */ - if (sm->ptk.installed) { + if (sm->ptk.installed || + (sm->ptk.installed_rx && (key_flag & KEY_FLAG_NEXT))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Do not re-install same PTK to the driver"); return 0; } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "WPA: Installing PTK to the driver"); + "WPA: Installing %sTK to the driver", + (key_flag & KEY_FLAG_NEXT) ? "next " : ""); if (sm->pairwise_cipher == WPA_CIPHER_NONE) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " @@ -1221,7 +1283,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); - if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; @@ -1231,6 +1293,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm), sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) { + if (key_flag & KEY_FLAG_NEXT) + return 0; /* Not all drivers support this, so do not + * report failures on the RX-only set_key */ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr=" MACSTR " idx=%d key_flag=0x%x)", @@ -1240,8 +1305,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } #ifdef CONFIG_PASN + ap_rsnxe = wpa_sm_get_ap_rsnxe(sm, &ap_rsnxe_len); if (sm->secure_ltf && - ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) && + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) && wpa_sm_set_ltf_keyseed(sm, sm->own_addr, sm->bssid, sm->ptk.ltf_keyseed_len, sm->ptk.ltf_keyseed) < 0) { @@ -1253,14 +1319,20 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } #endif /* CONFIG_PASN */ - wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + wpa_sm_store_ptk(sm, wpa_sm_get_auth_addr(sm), sm->pairwise_cipher, sm->dot11RSNAConfigPMKLifetime, &sm->ptk); - /* TK is not needed anymore in supplicant */ - os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); - sm->ptk.tk_len = 0; - sm->ptk.installed = 1; - sm->tk_set = true; + if (key_flag & KEY_FLAG_NEXT) { + sm->ptk.installed_rx = true; + } else { +#ifndef CONFIG_TESTING_OPTIONS + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.tk_len = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + sm->ptk.installed = 1; + sm->tk_set = true; + } if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); @@ -1321,11 +1393,30 @@ static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, struct wpa_gtk_data { enum wpa_alg alg; int tx, key_rsc_len, keyidx; - u8 gtk[32]; + u8 gtk[WPA_GTK_MAX_LEN]; int gtk_len; }; +static int wpa_supplicant_validate_gtk_kde_len(size_t gtk_len) +{ + if (gtk_len < 2 || gtk_len - 2 > WPA_GTK_MAX_LEN) + return -1; + + return 0; +} + + +static int wpa_supplicant_validate_mlo_gtk_kde_len(size_t gtk_len) +{ + if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH || + gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > WPA_GTK_MAX_LEN) + return -1; + + return 0; +} + + static int wpa_supplicant_install_gtk(struct wpa_sm *sm, const struct wpa_gtk_data *gd, const u8 *key_rsc, int wnm_sleep) @@ -1519,8 +1610,7 @@ static int wpa_supplicant_mlo_gtk(struct wpa_sm *sm, u8 link_id, const u8 *gtk, "RSN: received GTK in pairwise handshake", gtk, gtk_len); - if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH || - gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > sizeof(gd.gtk)) + if (wpa_supplicant_validate_mlo_gtk_kde_len(gtk_len) < 0) return -1; gd.keyidx = gtk[0] & 0x3; @@ -1596,7 +1686,7 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", gtk, gtk_len); - if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) + if (wpa_supplicant_validate_gtk_kde_len(gtk_len) < 0) return -1; gd.keyidx = gtk[0] & 0x3; @@ -2193,6 +2283,68 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, return -1; } + if (sm->proto == WPA_PROTO_RSN && wpa_sm_rsn_overriding_supported(sm)) { + if ((sm->ap_rsne_override && !ie->rsne_override) || + (!sm->ap_rsne_override && ie->rsne_override) || + (sm->ap_rsne_override && ie->rsne_override && + (sm->ap_rsne_override_len != ie->rsne_override_len || + os_memcmp(sm->ap_rsne_override, ie->rsne_override, + sm->ap_rsne_override_len) != 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: RSNE Override element mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); + wpa_hexdump(MSG_INFO, + "RSNE Override element in Beacon/ProbeResp", + sm->ap_rsne_override, + sm->ap_rsne_override_len); + wpa_hexdump(MSG_INFO, + "RSNE Override element in EAPOL-Key msg 3/4", + ie->rsne_override, ie->rsne_override_len); + wpa_sm_deauthenticate(sm, + WLAN_REASON_IE_IN_4WAY_DIFFERS); + return -1; + } + + if ((sm->ap_rsne_override_2 && !ie->rsne_override_2) || + (!sm->ap_rsne_override_2 && ie->rsne_override_2) || + (sm->ap_rsne_override_2 && ie->rsne_override_2 && + (sm->ap_rsne_override_2_len != ie->rsne_override_2_len || + os_memcmp(sm->ap_rsne_override_2, ie->rsne_override_2, + sm->ap_rsne_override_2_len) != 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: RSNE Override 2 element mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); + wpa_hexdump(MSG_INFO, + "RSNE Override 2 element in Beacon/ProbeResp", + sm->ap_rsne_override_2, + sm->ap_rsne_override_2_len); + wpa_hexdump(MSG_INFO, + "RSNE Override 2 element in EAPOL-Key msg 3/4", + ie->rsne_override_2, ie->rsne_override_2_len); + wpa_sm_deauthenticate(sm, + WLAN_REASON_IE_IN_4WAY_DIFFERS); + return -1; + } + + if ((sm->ap_rsnxe_override && !ie->rsnxe_override) || + (!sm->ap_rsnxe_override && ie->rsnxe_override) || + (sm->ap_rsnxe_override && ie->rsnxe_override && + (sm->ap_rsnxe_override_len != ie->rsnxe_override_len || + os_memcmp(sm->ap_rsnxe_override, ie->rsnxe_override, + sm->ap_rsnxe_override_len) != 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: RSNXE Override element mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); + wpa_hexdump(MSG_INFO, + "RSNXE Override element in Beacon/ProbeResp", + sm->ap_rsnxe_override, + sm->ap_rsnxe_override_len); + wpa_hexdump(MSG_INFO, + "RSNXE Override element in EAPOL-Key msg 3/4", + ie->rsnxe_override, ie->rsnxe_override_len); + wpa_sm_deauthenticate(sm, + WLAN_REASON_IE_IN_4WAY_DIFFERS); + return -1; + } + } + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt) && wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) @@ -2253,7 +2405,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, } #endif /* CONFIG_TESTING_OPTIONS */ - mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + kde_len + extra_len, &rlen, @@ -2263,8 +2415,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, return -1; } - reply->type = (sm->proto == WPA_PROTO_RSN || - sm->proto == WPA_PROTO_OSEN) ? + reply->type = (sm->proto == WPA_PROTO_RSN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; key_info |= ver | WPA_KEY_INFO_KEY_TYPE; @@ -2277,7 +2428,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; #endif /* CONFIG_TESTING_OPTIONS */ WPA_PUT_BE16(reply->key_info, key_info); - if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) + if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); @@ -2339,10 +2490,14 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id, const u8 *link_kde, - size_t link_kde_len) + size_t link_kde_len, + const u8 *rsn_override_link_kde, + size_t rsn_override_link_kde_len) { - size_t rsne_len = 0, rsnxe_len = 0; - const u8 *rsne = NULL, *rsnxe = NULL; + size_t rsne_len = 0, rsnxe_len = 0, rsnoe_len = 0, rsno2e_len = 0, + rsnxoe_len = 0; + const u8 *rsne = NULL, *rsnxe = NULL, *rsnoe = NULL, *rsno2e = NULL, + *rsnxoe = NULL; if (!link_kde || link_kde_len < RSN_MLO_LINK_KDE_LINK_MAC_INDEX + ETH_ALEN) { @@ -2403,14 +2558,14 @@ static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id, sm->mlo.links[link_id].ap_rsne_len, rsne, rsne_len)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN MLO: IE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u", + "RSN MLO: RSNE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u", link_id); wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp", sm->mlo.links[link_id].ap_rsne, sm->mlo.links[link_id].ap_rsne_len); wpa_hexdump(MSG_INFO, "RSNE in EAPOL-Key msg 3/4", rsne, rsne_len); - return -1; + goto fail; } if ((sm->mlo.links[link_id].ap_rsnxe && !rsnxe) || @@ -2427,11 +2582,89 @@ static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id, sm->mlo.links[link_id].ap_rsnxe_len); wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4", rsnxe, rsnxe_len); - wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); - return -1; + goto fail; + } + + if (!wpa_sm_rsn_overriding_supported(sm)) + return 0; + + if (rsn_override_link_kde) { + rsnoe = get_vendor_ie(rsn_override_link_kde + 1, + rsn_override_link_kde_len - 1, + RSNE_OVERRIDE_IE_VENDOR_TYPE); + if (rsnoe) + rsnoe_len = 2 + rsnoe[1]; + + rsno2e = get_vendor_ie(rsn_override_link_kde + 1, + rsn_override_link_kde_len - 1, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (rsno2e) + rsno2e_len = 2 + rsno2e[1]; + + rsnxoe = get_vendor_ie(rsn_override_link_kde + 1, + rsn_override_link_kde_len - 1, + RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (rsnxoe) + rsnxoe_len = 2 + rsnxoe[1]; + } + + if ((sm->mlo.links[link_id].ap_rsnoe && !rsnoe) || + (!sm->mlo.links[link_id].ap_rsnoe && rsnoe) || + (sm->mlo.links[link_id].ap_rsnoe && rsnoe && + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->mlo.links[link_id].ap_rsnoe, + sm->mlo.links[link_id].ap_rsnoe_len, + rsnoe, rsnoe_len))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN MLO: RSNOE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u", + link_id); + wpa_hexdump(MSG_INFO, "RSNOE in Beacon/ProbeResp", + sm->mlo.links[link_id].ap_rsnoe, + sm->mlo.links[link_id].ap_rsnoe_len); + wpa_hexdump(MSG_INFO, "RSNOE in EAPOL-Key msg 3/4", + rsnoe, rsnoe_len); + goto fail; + } + + if ((sm->mlo.links[link_id].ap_rsno2e && !rsno2e) || + (!sm->mlo.links[link_id].ap_rsno2e && rsno2e) || + (sm->mlo.links[link_id].ap_rsno2e && rsno2e && + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->mlo.links[link_id].ap_rsno2e, + sm->mlo.links[link_id].ap_rsno2e_len, + rsno2e, rsno2e_len))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN MLO: RSNO2E in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u", + link_id); + wpa_hexdump(MSG_INFO, "RSNO2E in Beacon/ProbeResp", + sm->mlo.links[link_id].ap_rsno2e, + sm->mlo.links[link_id].ap_rsno2e_len); + wpa_hexdump(MSG_INFO, "RSNOE in EAPOL-Key msg 3/4", + rsno2e, rsno2e_len); + goto fail; + } + + if ((sm->mlo.links[link_id].ap_rsnxoe && !rsnxoe) || + (!sm->mlo.links[link_id].ap_rsnxoe && rsnxoe) || + (sm->mlo.links[link_id].ap_rsnxoe && rsnxoe && + (sm->mlo.links[link_id].ap_rsnxoe_len != rsnxoe_len || + os_memcmp(sm->mlo.links[link_id].ap_rsnxoe, rsnxoe, + sm->mlo.links[link_id].ap_rsnxoe_len) != 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN MLO: RSNXOE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4 for link ID %u", + link_id); + wpa_hexdump(MSG_INFO, "RSNXOE in Beacon/ProbeResp", + sm->mlo.links[link_id].ap_rsnxoe, + sm->mlo.links[link_id].ap_rsnxoe_len); + wpa_hexdump(MSG_INFO, "RSNXOE in EAPOL-Key msg 3/4", + rsnxoe, rsnxoe_len); + goto fail; } return 0; +fail: + wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); + return -1; } @@ -2531,6 +2764,52 @@ failed: } +static void wpa_sm_sae_pw_id_change(struct wpa_sm *sm, const u8 *kde, + size_t kde_len) +{ + const u8 *pos = kde, *end = kde + kde_len; + u8 flags; + struct wpabuf_array *wa; + + wpa_hexdump(MSG_DEBUG, "RSN: Received SAE Password Identifiers KDE", + kde, kde_len); + + if (end - pos < 1) + return; + flags = *pos++; + wpa_printf(MSG_DEBUG, "RSN: SAE Password Identifiers Flags: 0x%x", + flags); + + if (!sm->ctx->sae_pw_id_change) + return; + + wa = wpabuf_array_alloc(); + if (!wa) + return; + + while (end > pos) { + u8 len; + + len = *pos++; + if (end - pos < len) { + wpa_printf(MSG_INFO, + "RSN: Truncated SAE Password Identifier Tuple"); + wpabuf_array_free(wa); + return; + } + wpa_hexdump(MSG_DEBUG, "RSN: SAE Password Identifier", + pos, len); + if (wpabuf_array_add(wa, wpabuf_alloc_copy(pos, len))) { + wpabuf_array_free(wa); + return; + } + pos += len; + } + + sm->ctx->sae_pw_id_change(sm->ctx->ctx, wa); +} + + static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, u16 ver, const u8 *key_data, @@ -2599,8 +2878,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (!(sm->mlo.req_links & BIT(i))) continue; - if (wpa_supplicant_validate_link_kde(sm, i, ie.mlo_link[i], - ie.mlo_link_len[i]) < 0) + if (wpa_supplicant_validate_link_kde( + sm, i, ie.mlo_link[i], ie.mlo_link_len[i], + ie.rsn_override_link[i], + ie.rsn_override_link_len[i]) < 0) goto failed; if (!(sm->mlo.valid_links & BIT(i))) @@ -2612,6 +2893,14 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } + if (wpa_supplicant_validate_mlo_gtk_kde_len( + ie.mlo_gtk_len[i]) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Invalid MLO GTK KDE length %zu for link ID %u", + ie.mlo_gtk_len[i], i); + goto failed; + } + if (sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED && wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && wpa_validate_mlo_ieee80211w_kdes(sm, i, &ie) < 0) @@ -2629,6 +2918,14 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, "WPA: GTK IE in unencrypted key data"); goto failed; } + + if (!mlo && ie.gtk && + wpa_supplicant_validate_gtk_kde_len(ie.gtk_len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid GTK KDE length %zu", ie.gtk_len); + goto failed; + } + if (!mlo && ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: IGTK KDE in unencrypted key data"); @@ -2716,6 +3013,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX)) goto failed; + if (!sm->use_ext_key_id && + wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX | KEY_FLAG_NEXT)) { + /* Continue anyway since the many drivers do not support + * configuration of the TK for RX-only purposes for cases where + * multiple keys might be in use in parallel and this being an + * optional optimization to avoid race condition during TK + * changes that could result in some protected frames getting + * discarded. */ + } + if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver, key_info, &sm->ptk) < 0) goto failed; @@ -2792,13 +3099,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, wpa_sm_get_auth_addr(sm), sm->own_addr, - sm->network_ctx, sm->key_mgmt, NULL); + sm->network_ctx, sm->key_mgmt, NULL, 0); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } if (ie.transition_disable) wpa_sm_transition_disable(sm, ie.transition_disable[0]); + if (ie.sae_pw_ids && wpa_key_mgmt_sae(sm->key_mgmt) && + sm->sae_pw_id_change) + wpa_sm_sae_pw_id_change(sm, ie.sae_pw_ids, ie.sae_pw_ids_len); sm->msg_3_of_4_ok = 1; return; @@ -2828,15 +3138,14 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, kde_len = OCV_OCI_KDE_LEN; #endif /* CONFIG_OCV */ - mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; - reply->type = (sm->proto == WPA_PROTO_RSN || - sm->proto == WPA_PROTO_OSEN) ? + reply->type = (sm->proto == WPA_PROTO_RSN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; key_info |= ver | WPA_KEY_INFO_SECURE; @@ -2845,7 +3154,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, else key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); - if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) + if (sm->proto == WPA_PROTO_RSN) WPA_PUT_BE16(reply->key_length, 0); else os_memcpy(reply->key_length, key->key_length, 2); @@ -2900,7 +3209,9 @@ static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm, u8 i; struct wpa_eapol_ie_parse ie; - if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm) && + !wpa_eppke_is_completed(sm) && + !wpa_eap_over_auth_frame_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "MLO RSN: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -3136,7 +3447,9 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; u16 gtk_len; - if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm) && + !wpa_eppke_is_completed(sm) && + !wpa_eap_over_auth_frame_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -3168,18 +3481,13 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, goto failed; } gtk_len = ie.gtk_len; - if (gtk_len < 2) { + if (wpa_supplicant_validate_gtk_kde_len(gtk_len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2", gtk_len); goto failed; } gtk_len -= 2; - if (gtk_len > sizeof(gd.gtk)) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN: Too long GTK in GTK KDE (len=%u)", gtk_len); - goto failed; - } maxkeylen = gd.gtk_len = gtk_len; #ifdef CONFIG_OCV @@ -3244,6 +3552,29 @@ failed: } +static void wpa_sm_tptk_to_ptk(struct wpa_sm *sm) +{ + sm->tptk_set = 0; + sm->ptk_set = 1; + os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + sm->hash_alg = sm->ptk.hash_alg; + + if (wpa_sm_pmf_enabled(sm)) { + os_memcpy(sm->last_kck, sm->ptk.kck, sm->ptk.kck_len); + sm->last_kck_len = sm->ptk.kck_len; + sm->last_kck_pmk_len = sm->pmk_len; + sm->last_kck_key_mgmt = sm->key_mgmt; + sm->last_kck_eapol_key_ver = sm->last_eapol_key_ver; + os_memcpy(sm->last_kck_aa, wpa_sm_get_auth_addr(sm), ETH_ALEN); + } else { + os_memset(sm->last_kck, 0, sizeof(sm->last_kck)); + sm->last_kck_len = 0; + os_memset(sm->last_kck_aa, 0, ETH_ALEN); + } +} + + static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_eapol_key *key, u16 ver, @@ -3251,13 +3582,13 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, { u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; int ok = 0; - size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len, sm->hash_alg); os_memcpy(mic, key + 1, mic_len); if (sm->tptk_set) { os_memset(key + 1, 0, mic_len); if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, - sm->key_mgmt, + sm->key_mgmt, sm->hash_alg, ver, buf, len, (u8 *) (key + 1)) < 0 || os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -3273,10 +3604,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, continue_fuzz: #endif /* TEST_FUZZ */ ok = 1; - sm->tptk_set = 0; - sm->ptk_set = 1; - os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); - os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + wpa_sm_tptk_to_ptk(sm); /* * This assures the same TPTK in sm->tptk can never be * copied twice to sm->ptk as the new PTK. In @@ -3291,7 +3619,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, if (!ok && sm->ptk_set) { os_memset(key + 1, 0, mic_len); if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, - sm->key_mgmt, + sm->key_mgmt, sm->hash_alg, ver, buf, len, (u8 *) (key + 1)) < 0 || os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -3529,12 +3857,8 @@ static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len, WPA_PUT_BE16(pos, *key_data_len); bin_clear_free(tmp, *key_data_len); - if (sm->tptk_set) { - sm->tptk_set = 0; - sm->ptk_set = 1; - os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); - os_memset(&sm->tptk, 0, sizeof(sm->tptk)); - } + if (sm->tptk_set) + wpa_sm_tptk_to_ptk(sm); os_memcpy(sm->rx_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); @@ -3634,7 +3958,8 @@ static int wpa_sm_rx_eapol_wpa(struct wpa_sm *sm, const u8 *src_addr, * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * @encrypted: Whether the frame was encrypted - * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure, + * -2 = reply counter did not increase. * * This function is called for each received EAPOL frame. Other than EAPOL-Key * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is @@ -3663,7 +3988,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, pmk_len = sm->pmk_len; if (!pmk_len && sm->cur_pmksa) pmk_len = sm->cur_pmksa->pmk_len; - mic_len = wpa_mic_len(sm->key_mgmt, pmk_len); + mic_len = wpa_mic_len(sm->key_mgmt, pmk_len, sm->hash_alg); keyhdrlen = sizeof(*key) + mic_len + 2; if (len < sizeof(*hdr) + keyhdrlen) { @@ -3743,6 +4068,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not increase - dropping packet"); + ret = -2; goto out; } @@ -3857,6 +4183,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } + sm->last_eapol_key_ver = ver; + if ((key_info & WPA_KEY_INFO_MIC) && wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) goto out; @@ -3868,7 +4196,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } #endif /* CONFIG_FILS */ - if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && + if (sm->proto == WPA_PROTO_RSN && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) { /* * Only decrypt the Key Data field if the frame's authenticity @@ -3938,8 +4266,7 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) { switch (sm->key_mgmt) { case WPA_KEY_MGMT_IEEE8021X: - return ((sm->proto == WPA_PROTO_RSN || - sm->proto == WPA_PROTO_OSEN) ? + return ((sm->proto == WPA_PROTO_RSN) ? RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); case WPA_KEY_MGMT_PSK: @@ -4173,9 +4500,15 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); os_free(sm->ap_rsnxe); + os_free(sm->ap_rsne_override); + os_free(sm->ap_rsne_override_2); + os_free(sm->ap_rsnxe_override); for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { os_free(sm->mlo.links[i].ap_rsne); os_free(sm->mlo.links[i].ap_rsnxe); + os_free(sm->mlo.links[i].ap_rsnoe); + os_free(sm->mlo.links[i].ap_rsno2e); + os_free(sm->mlo.links[i].ap_rsnxoe); } wpa_sm_drop_sa(sm); os_free(sm->ctx); @@ -4186,6 +4519,8 @@ void wpa_sm_deinit(struct wpa_sm *sm) wpabuf_free(sm->test_assoc_ie); wpabuf_free(sm->test_eapol_m2_elems); wpabuf_free(sm->test_eapol_m4_elems); + wpabuf_free(sm->test_rsnxe_data); + wpabuf_free(sm->test_rsnxe_mask); #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS_SK_PFS crypto_ecdh_deinit(sm->fils_ecdh); @@ -4199,6 +4534,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_DPP2 wpabuf_clear_free(sm->dpp_z); #endif /* CONFIG_DPP2 */ + os_memset(sm->last_kck, 0, sizeof(sm->last_kck)); os_free(sm); } @@ -4287,6 +4623,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) clear_keys = 0; } #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + if (sm->eppke_completed || sm->eap_over_auth_frame_completed) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + clear_keys = 0; + } +#endif /* CONFIG_ENC_ASSOC */ if (clear_keys) { /* @@ -4295,6 +4641,7 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); wpa_sm_clear_ptk(sm); + sm->hash_alg = RSN_HASH_NOT_SPECIFIED; } #ifdef CONFIG_TDLS @@ -4334,6 +4681,10 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) sm->ft_reassoc_completed = 0; sm->ft_protocol = 0; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_ENC_ASSOC + sm->eppke_completed = 0; + sm->eap_over_auth_frame_completed = 0; +#endif /* CONFIG_ENC_ASSOC */ /* Keys are not needed in the WPA state machine anymore */ wpa_sm_drop_sa(sm); @@ -4341,6 +4692,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) sm->msg_3_of_4_ok = 0; os_memset(sm->bssid, 0, ETH_ALEN); + sm->hash_alg = RSN_HASH_NOT_SPECIFIED; } @@ -4365,6 +4717,17 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt)) { + if (pmk_len == 32) + sm->hash_alg = RSN_HASH_SHA256; + else if (pmk_len == 48) + sm->hash_alg = RSN_HASH_SHA384; + else if (pmk_len == 64) + sm->hash_alg = RSN_HASH_SHA512; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_IEEE80211R /* Set XXKey to be PSK for FT key derivation */ sm->xxkey_len = pmk_len; @@ -4376,7 +4739,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, - NULL); + NULL, 0); } } @@ -4505,14 +4868,36 @@ void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len) } +static void wpa_sm_clear_mlo_group_keys(struct wpa_sm *sm, int link_id) +{ + if (!sm) + return; + + os_memset(&sm->mlo.links[link_id].gtk, 0, + sizeof(sm->mlo.links[link_id].gtk)); + os_memset(&sm->mlo.links[link_id].gtk_wnm_sleep, 0, + sizeof(sm->mlo.links[link_id].gtk_wnm_sleep)); + os_memset(&sm->mlo.links[link_id].igtk, 0, + sizeof(sm->mlo.links[link_id].igtk)); + os_memset(&sm->mlo.links[link_id].igtk_wnm_sleep, 0, + sizeof(sm->mlo.links[link_id].igtk_wnm_sleep)); + os_memset(&sm->mlo.links[link_id].bigtk, 0, + sizeof(sm->mlo.links[link_id].bigtk)); + os_memset(&sm->mlo.links[link_id].bigtk_wnm_sleep, 0, + sizeof(sm->mlo.links[link_id].bigtk_wnm_sleep)); +} + + int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo) { int i; + u16 removed_links; if (!sm) return -1; os_memcpy(sm->mlo.ap_mld_addr, mlo->ap_mld_addr, ETH_ALEN); + removed_links = ~mlo->valid_links & sm->mlo.valid_links; sm->mlo.assoc_link_id = mlo->assoc_link_id; sm->mlo.valid_links = mlo->valid_links; sm->mlo.req_links = mlo->req_links; @@ -4578,6 +4963,76 @@ int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo) } sm->mlo.links[i].ap_rsnxe_len = len; } + + ie = mlo->links[i].ap_rsnoe; + len = mlo->links[i].ap_rsnoe_len; + os_free(sm->mlo.links[i].ap_rsnoe); + if (!ie || len == 0) { + if (sm->mlo.links[i].ap_rsnoe) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing MLO link[%u] AP RSNOE", + i); + sm->mlo.links[i].ap_rsnoe = NULL; + sm->mlo.links[i].ap_rsnoe_len = 0; + } else { + wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNOE", + ie, len); + sm->mlo.links[i].ap_rsnoe = os_memdup(ie, len); + if (!sm->mlo.links[i].ap_rsnoe) { + sm->mlo.links[i].ap_rsnoe_len = 0; + return -1; + } + sm->mlo.links[i].ap_rsnoe_len = len; + } + + ie = mlo->links[i].ap_rsno2e; + len = mlo->links[i].ap_rsno2e_len; + os_free(sm->mlo.links[i].ap_rsno2e); + if (!ie || len == 0) { + if (sm->mlo.links[i].ap_rsno2e) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing MLO link[%u] AP RSNO2E", + i); + sm->mlo.links[i].ap_rsno2e = NULL; + sm->mlo.links[i].ap_rsno2e_len = 0; + } else { + wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNO2E", + ie, len); + sm->mlo.links[i].ap_rsno2e = os_memdup(ie, len); + if (!sm->mlo.links[i].ap_rsno2e) { + sm->mlo.links[i].ap_rsno2e_len = 0; + return -1; + } + sm->mlo.links[i].ap_rsno2e_len = len; + } + + ie = mlo->links[i].ap_rsnxoe; + len = mlo->links[i].ap_rsnxoe_len; + os_free(sm->mlo.links[i].ap_rsnxoe); + if (!ie || len == 0) { + if (sm->mlo.links[i].ap_rsnxoe) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing MLO link[%u] AP RSNXOE", + i); + sm->mlo.links[i].ap_rsnxoe = NULL; + sm->mlo.links[i].ap_rsnxoe_len = 0; + } else { + wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXOE", + ie, len); + sm->mlo.links[i].ap_rsnxoe = os_memdup(ie, len); + if (!sm->mlo.links[i].ap_rsnxoe) { + sm->mlo.links[i].ap_rsnxoe_len = 0; + return -1; + } + sm->mlo.links[i].ap_rsnxoe_len = len; + } + + if (removed_links & BIT(i)) { + wpa_printf(MSG_DEBUG, + "RSN: Clearing MLO group keys for link[%u]", + i); + wpa_sm_clear_mlo_group_keys(sm, i); + } } return 0; @@ -4697,6 +5152,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_USE_EXT_KEY_ID: sm->use_ext_key_id = value; break; + case WPA_PARAM_SPP_AMSDU: + sm->spp_amsdu = !!value; + break; #ifdef CONFIG_TESTING_OPTIONS case WPA_PARAM_FT_RSNXE_USED: sm->ft_rsnxe_used = value; @@ -4722,7 +5180,16 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_ENCRYPT_EAPOL_M4: sm->encrypt_eapol_m4 = value; break; + case WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK: + sm->eapol_2_key_info_set_mask = value; + break; #endif /* CONFIG_TESTING_OPTIONS */ + case WPA_PARAM_URNM_MFPR: + sm->prot_range_neg = value; + break; + case WPA_PARAM_URNM_MFPR_X20: + sm->prot_range_neg_x20 = value; + break; #ifdef CONFIG_DPP2 case WPA_PARAM_DPP_PFS: sm->dpp_pfs = value; @@ -4737,6 +5204,21 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_SSID_PROTECTION: sm->ssid_protection = value; break; + case WPA_PARAM_RSN_OVERRIDE: + sm->rsn_override = value; + break; + case WPA_PARAM_RSN_OVERRIDE_SUPPORT: + sm->rsn_override_support = value; + break; + case WPA_PARAM_SAE_PW_ID_CHANGE: + sm->sae_pw_id_change = !!value; + break; + case WPA_PARAM_ASSOC_ENC: + sm->assoc_encryption = !!value; + break; + case WPA_PARAM_PMKSA_CACHING_PRIVACY: + sm->pmksa_privacy = !!value; + break; default: break; } @@ -4745,6 +5227,37 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, } +static const u8 * wpa_sm_get_ap_rsne(struct wpa_sm *sm, size_t *len) +{ + if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE) { + *len = sm->ap_rsne_override_len; + return sm->ap_rsne_override; + } + + if (sm->rsn_override == RSN_OVERRIDE_RSNE_OVERRIDE_2) { + *len = sm->ap_rsne_override_2_len; + return sm->ap_rsne_override_2; + } + + *len = sm->ap_rsn_ie_len; + return sm->ap_rsn_ie; +} + + +static const u8 * wpa_sm_get_ap_rsnxe(struct wpa_sm *sm, size_t *len) +{ + if (sm->rsn_override != RSN_OVERRIDE_NOT_USED && + sm->rsn_override != RSN_OVERRIDE_RSNE && + sm->ap_rsnxe_override && sm->ap_rsnxe_override_len) { + *len = sm->ap_rsnxe_override_len; + return sm->ap_rsnxe_override; + } + + *len = sm->ap_rsnxe_len; + return sm->ap_rsnxe; +} + + /** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -4762,6 +5275,10 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, { char *pos = buf, *end = buf + buflen; int ret; + const u8 *rsne; + size_t rsne_len; + + rsne = wpa_sm_get_ap_rsne(sm, &rsne_len); ret = os_snprintf(pos, end - pos, "pairwise_cipher=%s\n" @@ -4783,10 +5300,10 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, } #endif /* CONFIG_DPP2 */ - if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && rsne) { struct wpa_ie_data rsn; - if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) - >= 0 && + + if (wpa_parse_wpa_ie_rsn(rsne, rsne_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { ret = os_snprintf(pos, end - pos, "pmf=%d\n" @@ -4808,11 +5325,15 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int wpa_sm_pmf_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; + const u8 *rsne; + size_t rsne_len; - if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) + rsne = wpa_sm_get_ap_rsne(sm, &rsne_len); + + if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !rsne) return 0; - if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && + if (wpa_parse_wpa_ie_rsn(rsne, rsne_len, &rsn) >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) return 1; @@ -4820,6 +5341,17 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm) } +bool wpa_sm_rsn_overriding_supported(struct wpa_sm *sm) +{ + const u8 *rsne; + size_t rsne_len; + + rsne = wpa_sm_get_ap_rsne(sm, &rsne_len); + + return sm->rsn_override_support && rsne; +} + + int wpa_sm_ext_key_id(struct wpa_sm *sm) { return sm ? sm->ext_key_id : 0; @@ -4835,12 +5367,14 @@ int wpa_sm_ext_key_id_active(struct wpa_sm *sm) int wpa_sm_ocv_enabled(struct wpa_sm *sm) { struct wpa_ie_data rsn; + const u8 *rsne; + size_t rsne_len; - if (!sm->ocv || !sm->ap_rsn_ie) + rsne = wpa_sm_get_ap_rsne(sm, &rsne_len); + if (!sm->ocv || !rsne) return 0; - return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, - &rsn) >= 0 && + return wpa_parse_wpa_ie_rsn(rsne, rsne_len, &rsn) >= 0 && (rsn.capabilities & WPA_CAPABILITY_OCVC); } @@ -4935,6 +5469,76 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) } +#ifdef CONFIG_TESTING_OPTIONS + +int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data, + struct wpabuf *mask) +{ + size_t data_len = 0, mask_len = 0; + + wpabuf_free(sm->test_rsnxe_data); + sm->test_rsnxe_data = NULL; + wpabuf_free(sm->test_rsnxe_mask); + sm->test_rsnxe_mask = NULL; + + if (!data && !mask) + return 0; + + if (data) + data_len = wpabuf_len(data); + if (mask) + mask_len = wpabuf_len(mask); + + if (data_len != mask_len || data_len > 255) + return -1; + + sm->test_rsnxe_data = data; + sm->test_rsnxe_mask = mask; + + return 0; +} + + +static int wpa_set_test_rsnxe_data(struct wpa_sm *sm, u8 *rsnxe, + size_t orig_len, size_t max_len) +{ + const u8 *data, *mask; + size_t i, data_len; + + if (!sm->test_rsnxe_data || !sm->test_rsnxe_mask) + return orig_len; + + mask = wpabuf_head(sm->test_rsnxe_mask); + data = wpabuf_head(sm->test_rsnxe_data); + data_len = wpabuf_len(sm->test_rsnxe_data); + if (max_len < data_len + 2) { + wpa_printf(MSG_ERROR, "Couldn't fit RSNXE test data"); + return -1; + } + + /* Set data after original RSNXE to zero */ + if (orig_len < data_len + 2) + os_memset(&rsnxe[orig_len], 0, data_len + 2 - orig_len); + + /* Set EID and length fields */ + *rsnxe++ = WLAN_EID_RSNX; + *rsnxe++ = data_len; + + /* Preserve original RSNXE bit value when mask bit is zero */ + for (i = 0; i < data_len; i++) { + if (!mask[i]) + continue; + + rsnxe[i] &= ~mask[i]; + rsnxe[i] |= data[i] & mask[i]; + } + + return data_len + 2; +} + +#endif /* CONFIG_TESTING_OPTIONS */ + + /** * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -4953,6 +5557,11 @@ int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe, res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len); if (res < 0) return -1; +#ifdef CONFIG_TESTING_OPTIONS + res = wpa_set_test_rsnxe_data(sm, rsnxe, res, *rsnxe_len); + if (res < 0) + return -1; +#endif /* CONFIG_TESTING_OPTIONS */ *rsnxe_len = res; wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len); @@ -5121,6 +5730,81 @@ int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) } +int wpa_sm_set_ap_rsne_override(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (!sm) + return -1; + + os_free(sm->ap_rsne_override); + if (!ie || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing AP RSNE Override element"); + sm->ap_rsne_override = NULL; + sm->ap_rsne_override_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "RSN: Set AP RSNE Override element", + ie, len); + sm->ap_rsne_override = os_memdup(ie, len); + if (!sm->ap_rsne_override) + return -1; + + sm->ap_rsne_override_len = len; + } + + return 0; +} + + +int wpa_sm_set_ap_rsne_override_2(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (!sm) + return -1; + + os_free(sm->ap_rsne_override_2); + if (!ie || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing AP RSNE Override 2 element"); + sm->ap_rsne_override_2 = NULL; + sm->ap_rsne_override_2_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "RSN: Set AP RSNE Override 2 element", + ie, len); + sm->ap_rsne_override_2 = os_memdup(ie, len); + if (!sm->ap_rsne_override_2) + return -1; + + sm->ap_rsne_override_2_len = len; + } + + return 0; +} + + +int wpa_sm_set_ap_rsnxe_override(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (!sm) + return -1; + + os_free(sm->ap_rsnxe_override); + if (!ie || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Clearing AP RSNXE Override element"); + sm->ap_rsnxe_override = NULL; + sm->ap_rsnxe_override_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "RSN: Set AP RSNXE Override element", + ie, len); + sm->ap_rsnxe_override = os_memdup(ie, len); + if (!sm->ap_rsnxe_override) + return -1; + + sm->ap_rsnxe_override_len = len; + } + + return 0; +} + + /** * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -5172,7 +5856,7 @@ void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, { sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, - sm->key_mgmt, fils_cache_id); + sm->key_mgmt, fils_cache_id, 0); } @@ -5195,6 +5879,25 @@ struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, } +int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk, + size_t *pmk_len, const u8 **pmkid) +{ + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = wpa_sm_pmksa_cache_get(sm, aa, NULL, NULL, 0); + if (!pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR, + MAC2STR(aa)); + return -1; + } + + *pmk = pmksa->pmk; + *pmk_len = pmksa->pmk_len; + *pmkid = pmksa->pmkid; + return 0; +} + + void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm, struct rsn_pmksa_cache_entry *entry) { @@ -5258,13 +5961,20 @@ void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { - pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false); + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false, NULL); +} + + +void wpa_sm_pmksa_cache_flush_addr(struct wpa_sm *sm, void *network_ctx, + const u8 *addr) +{ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false, addr); } void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { - pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true); + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true, NULL); } @@ -5358,7 +6068,7 @@ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) } -void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, enum rsn_hash_alg hash, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len) { @@ -5372,12 +6082,29 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, sm->ptk.kek_len = ptk_kek_len; wpa_printf(MSG_DEBUG, "Updated PTK KEK"); } + sm->ptk.hash_alg = hash; + if (sm->hash_alg != hash) { + wpa_printf(MSG_DEBUG, + "Updated hash algorithm for PTKSA: %d -> %d", + sm->hash_alg, hash); + sm->hash_alg = hash; + } sm->ptk_set = 1; } #ifdef CONFIG_TESTING_OPTIONS +void wpa_sm_set_ptk_tk(struct wpa_sm *sm, const u8 *ptk_tk, size_t ptk_tk_len) +{ + if (ptk_tk && ptk_tk_len <= WPA_TK_MAX_LEN) { + os_memcpy(sm->ptk.tk, ptk_tk, ptk_tk_len); + sm->ptk.tk_len = ptk_tk_len; + wpa_printf(MSG_DEBUG, "Updated PTK TK"); + } +} + + void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) { wpabuf_free(sm->test_assoc_ie); @@ -5444,12 +6171,12 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) goto fail; } - if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 || + if (random_get_bytes(sm->fils_nonce, NONCE_LEN) < 0 || random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0) goto fail; wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce", - sm->fils_nonce, FILS_NONCE_LEN); + sm->fils_nonce, NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session", sm->fils_session, FILS_SESSION_LEN); @@ -5514,10 +6241,10 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) /* FILS Nonce */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ - wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */ + wpabuf_put_u8(buf, 1 + NONCE_LEN); /* Length */ /* Element ID Extension */ - wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); - wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, sm->fils_nonce, NONCE_LEN); /* FILS Session */ wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ @@ -5642,12 +6369,12 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, goto fail; } - if (!elems.fils_nonce) { + if (!elems.nonce) { wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); goto fail; } - os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); + os_memcpy(sm->fils_anonce, elems.nonce, NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, NONCE_LEN); #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { @@ -5788,7 +6515,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, sm->fils_erp_pmkid, NULL, 0, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, - NULL); + NULL, 0); } if (!sm->cur_pmksa) { @@ -6106,11 +6833,9 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, *kek_len = sm->ptk.kek_len; wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len); *snonce = sm->fils_nonce; - wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", - *snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", *snonce, NONCE_LEN); *anonce = sm->fils_anonce; - wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", - *anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", *anonce, NONCE_LEN); return buf; } @@ -6259,6 +6984,29 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) goto fail; } + if ((sm->ap_rsnxe && !elems.rsnxe) || + (!sm->ap_rsnxe && elems.rsnxe) || + (sm->ap_rsnxe && elems.rsnxe && sm->ap_rsnxe_len >= 2 && + (sm->ap_rsnxe_len != 2U + elems.rsnxe_len || + os_memcmp(sm->ap_rsnxe + 2, elems.rsnxe, sm->ap_rsnxe_len - 2) != + 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FILS: RSNXE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_INFO, "FILS: RSNXE in Beacon/Probe Response", + sm->ap_rsnxe, sm->ap_rsnxe_len); + wpa_hexdump(MSG_INFO, "RSNXE in (Re)Association Response", + elems.rsnxe, elems.rsnxe_len); + /* As an interop workaround, allow this for now if we did not + * include the RSNXE in (Re)Association Request frame since + * IEEE Std 802.11-2020 does not say anything about verifying + * the RSNXE in FILS cases and there have been hostapd releases + * that might omit the RSNXE in cases where the STA did not + * include it in the Association Request frame. This workaround + * might eventually be removed. */ + if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) + goto fail; + } + /* TODO: FILS Public Key */ if (!elems.fils_key_confirm) { @@ -6389,7 +7137,7 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) goto fail; } - wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher, + wpa_sm_store_ptk(sm, wpa_sm_get_auth_addr(sm), sm->pairwise_cipher, sm->dot11RSNAConfigPMKLifetime, &sm->ptk); /* TODO: TK could be cleared after auth frame exchange now that driver @@ -6637,7 +7385,7 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, - NULL); + NULL, 0); return 0; } @@ -6689,6 +7437,12 @@ void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm) } +bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm) +{ + return sm ? sm->spp_amsdu : false; +} + + struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm) { return sm ? sm->pmksa : NULL; @@ -6709,3 +7463,379 @@ void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm, if (sm) sm->driver_bss_selection = driver_bss_selection; } + + +struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa, + u64 timestamp) +{ + struct wpabuf *ie; + unsigned int mic_len; + const u8 *start; + u8 *mic; + + if (!sm || sm->last_kck_len == 0) + return NULL; + + if (!ether_addr_equal(aa, sm->last_kck_aa)) + return NULL; + + mic_len = wpa_mic_len(sm->last_kck_key_mgmt, sm->last_kck_pmk_len, + sm->hash_alg); + + ie = wpabuf_alloc(3 + 8 + 1 + mic_len); + if (!ie) + return NULL; + + wpabuf_put_u8(ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(ie, 1 + 8 + 1 + mic_len); + wpabuf_put_u8(ie, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION); + start = wpabuf_put(ie, 0); + wpabuf_put_le64(ie, timestamp); + wpabuf_put_u8(ie, mic_len); + mic = wpabuf_put(ie, mic_len); + if (wpa_eapol_key_mic(sm->last_kck, sm->last_kck_len, + sm->last_kck_key_mgmt, sm->hash_alg, + sm->last_kck_eapol_key_ver, + start, 8, mic) < 0) { + wpabuf_free(ie); + return NULL; + } + + return ie; +} + + +static int mlo_ieee80211w_set_keys_for_new_links(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie, + u16 added_links_bitmap) +{ + int i; + + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) || + sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED) + return 0; + + for_each_link(added_links_bitmap, i) { + if (_mlo_ieee80211w_set_keys(sm, i, ie)) + return -1; + } + + return 0; +} + + +static int wpa_supplicant_install_mlo_gtk_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie, + u16 added_links_bitmap) +{ + int i; + + for_each_link(added_links_bitmap, i) { + if (!ie->mlo_gtk[i]) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "MLO RSN: GTK not found for link ID %u", i); + return -1; + } + + if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i], + ie->mlo_gtk_len[i], 0)) + return -1; + } + + return 0; +} + + +int wpa_sm_install_mlo_group_keys(struct wpa_sm *sm, const u8 *key_data, + size_t key_data_len, u16 added_links_bitmap) +{ + struct wpa_eapol_ie_parse ie; + + if (!sm) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "RSN: IE KeyData for MLO link reconfig", + key_data, key_data_len); + + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + return -1; + + if (wpa_supplicant_install_mlo_gtk_keys( + sm, &ie, added_links_bitmap) < 0) + return -1; + + if (mlo_ieee80211w_set_keys_for_new_links(sm, &ie, + added_links_bitmap) < 0) + return -1; + + return 0; +} + + +#ifdef CONFIG_ENC_ASSOC + +static int process_key_delivery_link(struct wpa_sm *sm, u8 i, + struct wpa_eapol_ie_parse *kde) +{ + if (wpa_supplicant_mlo_gtk(sm, i, kde->mlo_gtk[i], + kde->mlo_gtk_len[i], 0)) + return -1; + + if (_mlo_ieee80211w_set_keys(sm, i, kde) < 0) { + wpa_printf(MSG_DEBUG, + "ENC_ASSOC: Failed to set MLO IGTK/BIGTK (link=%u)", + i); + return -1; + } + + return 0; +} + + +static int process_key_delivery_ml(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *kde, + int valid_links) +{ + u8 i; + + for_each_link(valid_links, i) { + if (process_key_delivery_link(sm, i, kde) < 0) + return -1; + } + + return 0; +} + + +static int process_key_delivery(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *kde, + const u8 *rsc) +{ + struct wpa_gtk_data gd; + int ret = -1; + int maxkeylen; + + os_memset(&gd, 0, sizeof(gd)); + + if (!kde->gtk) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: No GTK KDE"); + goto fail; + } + + maxkeylen = gd.gtk_len = kde->gtk_len - 2; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + wpa_hexdump_key(MSG_DEBUG, "ENC_ASSOC: Received GTK", + kde->gtk, kde->gtk_len); + gd.keyidx = kde->gtk[0] & 0x3; + if (kde->gtk_len - 2 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, + "ENC_ASSOC: Too long GTK in GTK KDE (len=%zu)", + kde->gtk_len - 2); + goto fail; + } + os_memcpy(gd.gtk, kde->gtk + 2, kde->gtk_len - 2); + + wpa_printf(MSG_DEBUG, "ENC_ASSOC: Set GTK to driver"); + if (wpa_supplicant_install_gtk(sm, &gd, rsc, 0) < 0) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: Failed to set GTK"); + goto fail; + } + + if (ieee80211w_set_keys(sm, kde) < 0) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: Failed to set IGTK/BIGTK"); + goto fail; + } + + ret = 0; +fail: + forced_memzero(&gd, sizeof(gd)); + return ret; +} + + +int process_encrypted_assoc_resp(struct wpa_sm *sm, int valid_links, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + struct wpa_eapol_ie_parse kde; + const u8 *rsc; + struct wpabuf *buf = NULL; + int ret = -1; + + if (!sm || !sm->ptk_set) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: No KEK available"); + return -1; + } + + sm->eppke_completed = 0; + sm->eap_over_auth_frame_completed = 0; + wpa_hexdump_key(MSG_DEBUG, "ENC_ASSOC: (Re)Association Response frame", + ies, ies_len); + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return -1; + + if (!elems.rsn_ie) { + wpa_printf(MSG_DEBUG, + "ENC_ASSOC: No RSNE in (Re)Association Response"); + return -1; + } + + if (wpa_compare_rsn_ie(wpa_key_mgmt_sae(sm->key_mgmt) || + wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + elems.rsn_ie - 2, elems.rsn_ie_len + 2)) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "ENC_ASSOC: RSNE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_DEBUG, + "ENC_ASSOC: RSNE in Beacon/Probe Response", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + wpa_hexdump(MSG_DEBUG, + "ENC_ASSOC: RSNE in (Re)Association Response", + elems.rsn_ie, elems.rsn_ie_len); + return -1; + } + + if ((sm->ap_rsnxe && !elems.rsnxe) || + (!sm->ap_rsnxe && elems.rsnxe) || + (sm->ap_rsnxe && elems.rsnxe && sm->ap_rsnxe_len >= 2 && + (sm->ap_rsnxe_len != 2U + elems.rsnxe_len || + os_memcmp(sm->ap_rsnxe + 2, elems.rsnxe, sm->ap_rsnxe_len - 2) != + 0))) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "ENC_ASSOC: RSNXE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_DEBUG, + "ENC_ASSOC: RSNXE in Beacon/Probe Response", + sm->ap_rsnxe, sm->ap_rsnxe_len); + wpa_hexdump(MSG_DEBUG, + "ENC_ASSOC: RSNXE in (Re)Association Response", + elems.rsnxe, elems.rsnxe_len); + if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) + return -1; + } + + /* TODO: Check for RSNE/RSNXE mismatch for per-STA profile for MLO */ + + /* Key Delivery element */ + buf = ieee802_11_defrag(elems.key_delivery, elems.key_delivery_len, + true); + if (!buf) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: No Key Delivery element"); + return -1; + } + elems.key_delivery = wpabuf_head(buf); + elems.key_delivery_len = wpabuf_len(buf); + + /* Parse Key Delivery element: RSC followed by group key KDEs */ + rsc = elems.key_delivery; + if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, + elems.key_delivery_len - WPA_KEY_RSC_LEN, + &kde) < 0) { + wpa_printf(MSG_DEBUG, "ENC_ASSOC: Failed to parse KDEs"); + goto fail; + } + + if ((valid_links == -1 && + process_key_delivery(sm, &kde, rsc) < 0) || + (valid_links != -1 && + process_key_delivery_ml(sm, &kde, valid_links) < 0)) + goto fail; + + /* Process SAE Password Identifiers KDE if present in the encrypted + * (Re)Association Response frame (EPPKE path). */ + if (kde.sae_pw_ids && wpa_key_mgmt_sae(sm->key_mgmt) && + sm->sae_pw_id_change) + wpa_sm_sae_pw_id_change(sm, kde.sae_pw_ids, + kde.sae_pw_ids_len); + + wpa_sm_set_rekey_offload(sm); + + wpa_printf(MSG_DEBUG, "ENC_ASSOC: Association completed successfully"); + sm->eppke_completed = 1; + sm->eap_over_auth_frame_completed = 1; + + ret = 0; +fail: + wpabuf_free(buf); + return ret; +} + +#endif /* CONFIG_ENC_ASSOC */ + + +bool wpa_eppke_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_ENC_ASSOC + return sm && sm->eppke_completed; +#else /* CONFIG_ENC_ASSOC */ + return false; +#endif /* CONFIG_ENC_ASSOC */ +} + + +bool wpa_eap_over_auth_frame_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_ENC_ASSOC + return sm && sm->eap_over_auth_frame_completed; +#else /* CONFIG_ENC_ASSOC */ + return false; +#endif /* CONFIG_ENC_ASSOC */ +} + + +bool wpa_sm_pmksa_privacy_supported(struct wpa_sm *sm) +{ + return sm && sm->pmksa_privacy; +} + + +#ifdef CONFIG_IEEE8021X_AUTH + +void wpa_sm_set_802_1x_auth_caps(struct wpa_sm *sm, u64 flags2) +{ + sm->eap_over_auth_frame = + !!(flags2 & WPA_DRIVER_FLAGS2_802_1X_AUTH); +} + + +const u8 * wpa_sm_get_pmk(struct wpa_sm *sm, const u8 *addr, const u8 *pmkid, + size_t *pmk_len) +{ + if (wpa_supplicant_get_pmk(sm, addr, pmkid) < 0 || + sm->pmk_len == 0) + return NULL; + + *pmk_len = sm->pmk_len; + return sm->pmk; +} + +#endif /* CONFIG_IEEE8021X_AUTH */ + + +#ifdef CONFIG_TESTING_OPTIONS +/** + * wpa_sm_get_cached_tk - Get cached TK for testing purposes + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @tk: Buffer to store the TK + * @tk_len: Pointer to store the TK length + * Returns: 0 on success, -1 if TK is not available + * + * This function retrieves the cached TK from wpa_sm. The TK is only available + * if PTK is set and TK is not null. + */ +int wpa_sm_get_cached_tk(struct wpa_sm *sm, u8 *tk, size_t *tk_len) +{ + if (!sm || !sm->ptk_set || + sm->ptk.tk_len == 0 || sm->ptk.tk_len > WPA_TK_MAX_LEN) + return -1; + + os_memcpy(tk, sm->ptk.tk, sm->ptk.tk_len); + *tk_len = sm->ptk.tk_len; + + return 0; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h index 2bef093e3fa0..d42a7c102a9a 100644 --- a/contrib/wpa/src/rsn_supp/wpa.h +++ b/contrib/wpa/src/rsn_supp/wpa.h @@ -105,6 +105,7 @@ struct wpa_sm_ctx { void (*notify_pmksa_cache_entry)(void *ctx, struct rsn_pmksa_cache_entry *entry); void (*ssid_verified)(void *ctx); + void (*sae_pw_id_change)(void *ctx, struct wpabuf_array *wa); }; @@ -137,6 +138,22 @@ enum wpa_sm_conf_params { WPA_PARAM_ENCRYPT_EAPOL_M4, WPA_PARAM_FT_PREPEND_PMKID, WPA_PARAM_SSID_PROTECTION, + WPA_PARAM_RSN_OVERRIDE, + WPA_PARAM_RSN_OVERRIDE_SUPPORT, + WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK, + WPA_PARAM_SPP_AMSDU, + WPA_PARAM_URNM_MFPR, + WPA_PARAM_URNM_MFPR_X20, + WPA_PARAM_SAE_PW_ID_CHANGE, + WPA_PARAM_ASSOC_ENC, + WPA_PARAM_PMKSA_CACHING_PRIVACY, +}; + +enum wpa_rsn_override { + RSN_OVERRIDE_NOT_USED, + RSN_OVERRIDE_RSNE, + RSN_OVERRIDE_RSNE_OVERRIDE, + RSN_OVERRIDE_RSNE_OVERRIDE_2, }; struct rsn_supp_config { @@ -160,8 +177,9 @@ struct rsn_supp_config { struct wpa_sm_link { u8 addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; - u8 *ap_rsne, *ap_rsnxe; - size_t ap_rsne_len, ap_rsnxe_len; + u8 *ap_rsne, *ap_rsnxe, *ap_rsnoe, *ap_rsno2e, *ap_rsnxoe; + size_t ap_rsne_len, ap_rsnxe_len, ap_rsnoe_len, ap_rsno2e_len, + ap_rsnxoe_len;; struct wpa_gtk gtk; struct wpa_gtk gtk_wnm_sleep; struct wpa_igtk igtk; @@ -204,6 +222,9 @@ int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsne_override(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsne_override_2(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsnxe_override(struct wpa_sm *sm, const u8 *ie, size_t len); int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, @@ -242,6 +263,8 @@ struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *pmkid, const void *network_ctx, int akmp); +int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk, + size_t *pmk_len, const u8 **pmkid); void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm, struct rsn_pmksa_cache_entry *entry); bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md); @@ -250,17 +273,26 @@ int wpa_sm_has_ptk_installed(struct wpa_sm *sm); void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); +void wpa_sm_pmksa_cache_flush_addr(struct wpa_sm *sm, void *network_ctx, + const u8 *addr); void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); -void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, enum rsn_hash_alg hash, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len); +void wpa_sm_set_ptk_tk(struct wpa_sm *sm, const u8 *ptk_tk, size_t ptk_tk_len); +int wpa_sm_get_cached_tk(struct wpa_sm *sm, u8 *tk, size_t *tk_len); int wpa_fils_is_completed(struct wpa_sm *sm); +bool wpa_eppke_is_completed(struct wpa_sm *sm); +bool wpa_eap_over_auth_frame_is_completed(struct wpa_sm *sm); void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm); int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo); +void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm, + bool driver_bss_selection); +bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm); #else /* CONFIG_NO_WPA */ @@ -304,6 +336,11 @@ static inline void wpa_sm_set_config(struct wpa_sm *sm, { } +static inline void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, + size_t ssid_len) +{ +} + static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) { } @@ -348,6 +385,24 @@ static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, return -1; } +static inline int wpa_sm_set_ap_rsne_override(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_rsne_override_2(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_rsnxe_override(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) { return 0; @@ -435,7 +490,7 @@ wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid, return NULL; } -static inline int wpa_sm_has_ptk(struct wpa_sm *sm) +static inline int wpa_sm_has_ptk_installed(struct wpa_sm *sm) { return 0; } @@ -450,6 +505,12 @@ static inline void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, { } +static inline void wpa_sm_external_pmksa_cache_flush_addr(struct wpa_sm *sm, + void *network_ctx, + const u8 *addr) +{ +} + static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) { @@ -471,6 +532,16 @@ static inline int wpa_fils_is_completed(struct wpa_sm *sm) return 0; } +static inline bool wpa_eppke_is_completed(struct wpa_sm *sm) +{ + return false; +} + +static inline bool wpa_eap_over_auth_frame_is_completed(struct wpa_sm *sm) +{ + return false; +} + static inline void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm) { } @@ -481,6 +552,16 @@ static inline int wpa_sm_set_mlo_params(struct wpa_sm *sm, return 0; } +static inline void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm, + bool driver_bss_selection) +{ +} + +static inline bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm) +{ + return false; +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_IEEE80211R @@ -594,6 +675,8 @@ void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); void wpa_sm_set_test_eapol_m2_elems(struct wpa_sm *sm, struct wpabuf *buf); void wpa_sm_set_test_eapol_m4_elems(struct wpa_sm *sm, struct wpabuf *buf); const u8 * wpa_sm_get_anonce(struct wpa_sm *sm); +int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data, + struct wpabuf *mask); unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm); struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md); @@ -605,6 +688,8 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, const struct wpabuf **hlp, unsigned int num_hlp); int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); +int process_encrypted_assoc_resp(struct wpa_sm *sm, int link_id, + const u8 *ies, size_t ies_len); struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group); int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, @@ -619,7 +704,14 @@ struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm); void wpa_sm_set_cur_pmksa(struct wpa_sm *sm, struct rsn_pmksa_cache_entry *entry); const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm); -void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm, - bool driver_bss_selection); +struct wpabuf * wpa_sm_known_sta_identification(struct wpa_sm *sm, const u8 *aa, + u64 timestamp); +int wpa_sm_install_mlo_group_keys(struct wpa_sm *sm, const u8 *key_data, + size_t key_data_len, u16 added_links_bitmap); +bool wpa_sm_pmksa_privacy_supported(struct wpa_sm *sm); + +void wpa_sm_set_802_1x_auth_caps(struct wpa_sm *sm, u64 flags2); +const u8 * wpa_sm_get_pmk(struct wpa_sm *sm, const u8 *addr, const u8 *pmkid, + size_t *pmk_len); #endif /* WPA_H */ diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 9fb5668efc91..fb46fa2f4051 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -215,7 +215,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, struct rsn_mdie *mdie; struct rsn_ie_hdr *rsnie; int mdie_len; - u8 rsnxe[10]; + u8 rsnxe[257]; size_t rsnxe_len; int rsnxe_used; int res; @@ -695,6 +695,12 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name) < 0) goto fail; sm->pmk_r1_len = sm->pmk_r0_len; + if (sm->pmk_r1_len == SHA512_MAC_LEN) + sm->hash_alg = RSN_HASH_SHA512; + else if (sm->pmk_r1_len == SHA384_MAC_LEN) + sm->hash_alg = RSN_HASH_SHA384; + else + sm->hash_alg = RSN_HASH_SHA256; bssid = target_ap; diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h index d7e780519cdb..fc99d20412da 100644 --- a/contrib/wpa/src/rsn_supp/wpa_i.h +++ b/contrib/wpa/src/rsn_supp/wpa_i.h @@ -26,6 +26,7 @@ struct wpa_sm { u8 pmk[PMK_LEN_MAX]; size_t pmk_len; struct wpa_ptk ptk, tptk; + enum rsn_hash_alg hash_alg; int ptk_set, tptk_set; bool tk_set; /* Whether any TK is configured to the driver */ unsigned int msg_3_of_4_ok:1; @@ -69,7 +70,7 @@ struct wpa_sm { u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; - int wpa_deny_ptk0_rekey:1; + unsigned int wpa_deny_ptk0_rekey:1; int p2p; int wpa_rsc_relaxation; int owe_ptk_workaround; @@ -112,7 +113,13 @@ struct wpa_sm { unsigned int secure_ltf:1; unsigned int secure_rtt:1; unsigned int prot_range_neg:1; + unsigned int prot_range_neg_x20:1; unsigned int ssid_protection:1; + unsigned int spp_amsdu:1; + unsigned int sae_pw_id_change:1; + unsigned int assoc_encryption:1; + unsigned int pmksa_privacy:1; + unsigned int eap_over_auth_frame:1; u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; @@ -120,6 +127,9 @@ struct wpa_sm { size_t assoc_rsnxe_len; u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe; size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len; + u8 *ap_rsne_override, *ap_rsne_override_2, *ap_rsnxe_override; + size_t ap_rsne_override_len, ap_rsne_override_2_len, + ap_rsnxe_override_len; #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; @@ -185,20 +195,23 @@ struct wpa_sm { struct wpabuf *test_assoc_ie; struct wpabuf *test_eapol_m2_elems; struct wpabuf *test_eapol_m4_elems; + struct wpabuf *test_rsnxe_data; + struct wpabuf *test_rsnxe_mask; int ft_rsnxe_used; unsigned int oci_freq_override_eapol; unsigned int oci_freq_override_eapol_g2; unsigned int oci_freq_override_ft_assoc; unsigned int oci_freq_override_fils_assoc; unsigned int disable_eapol_g2_tx; + unsigned int eapol_2_key_info_set_mask; bool encrypt_eapol_m2; bool encrypt_eapol_m4; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS - u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_nonce[NONCE_LEN]; u8 fils_session[FILS_SESSION_LEN]; - u8 fils_anonce[FILS_NONCE_LEN]; + u8 fils_anonce[NONCE_LEN]; u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; size_t fils_key_auth_len; @@ -214,6 +227,10 @@ struct wpa_sm { u8 fils_ft[FILS_FT_MAX_LEN]; size_t fils_ft_len; #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + unsigned int eppke_completed:1; + unsigned int eap_over_auth_frame_completed:1; +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_OWE struct crypto_ecdh *owe_ecdh; @@ -229,6 +246,17 @@ struct wpa_sm { bool wmm_enabled; bool driver_bss_selection; bool ft_prepend_pmkid; + + bool rsn_override_support; + enum wpa_rsn_override rsn_override; + + u8 last_kck[WPA_KCK_MAX_LEN]; + size_t last_kck_len; + size_t last_kck_pmk_len; + unsigned int last_kck_key_mgmt; + int last_kck_eapol_key_ver; + u8 last_kck_aa[ETH_ALEN]; + int last_eapol_key_ver; }; @@ -542,5 +570,6 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, void wpa_tdls_assoc(struct wpa_sm *sm); void wpa_tdls_disassoc(struct wpa_sm *sm); +bool wpa_sm_rsn_overriding_supported(struct wpa_sm *sm); #endif /* WPA_I_H */ diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index eeedf8ba54a8..b3119d3f52fc 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -31,10 +31,14 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && - wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + wpa_ie[1] >= 4 && + WPA_GET_BE32(&wpa_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE) return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); - else - return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && + WPA_GET_BE32(&wpa_ie[2]) == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); } @@ -126,10 +130,10 @@ u16 rsn_supp_capab(struct wpa_sm *sm) } -static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, - int pairwise_cipher, int group_cipher, - int key_mgmt, int mgmt_group_cipher, - struct wpa_sm *sm) +int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm) { u8 *pos; struct rsn_ie_hdr *hdr; @@ -226,14 +230,14 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } else if (key_mgmt & WPA_KEY_MGMT_DPP) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); #endif /* CONFIG_DPP */ -#ifdef CONFIG_HS20 - } else if (key_mgmt & WPA_KEY_MGMT_OSEN) { - RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); -#endif /* CONFIG_HS20 */ #ifdef CONFIG_SHA384 } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA384) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384); #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_ENC_ASSOC + } else if (key_mgmt == WPA_KEY_MGMT_EPPKE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_EPPKE); +#endif /* CONFIG_ENC_ASSOC */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -275,64 +279,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } -#ifdef CONFIG_HS20 -static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, - int pairwise_cipher, int group_cipher, - int key_mgmt) -{ - u8 *pos, *len; - u32 suite; - - if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + - 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) - return -1; - - pos = wpa_ie; - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - len = pos++; /* to be filled */ - WPA_PUT_BE24(pos, OUI_WFA); - pos += 3; - *pos++ = HS20_OSEN_OUI_TYPE; - - /* Group Data Cipher Suite */ - suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); - if (suite == 0) { - wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", - group_cipher); - return -1; - } - RSN_SELECTOR_PUT(pos, suite); - pos += RSN_SELECTOR_LEN; - - /* Pairwise Cipher Suite Count and List */ - WPA_PUT_LE16(pos, 1); - pos += 2; - suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); - if (suite == 0 || - (!wpa_cipher_valid_pairwise(pairwise_cipher) && - pairwise_cipher != WPA_CIPHER_NONE)) { - wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", - pairwise_cipher); - return -1; - } - RSN_SELECTOR_PUT(pos, suite); - pos += RSN_SELECTOR_LEN; - - /* AKM Suite Count and List */ - WPA_PUT_LE16(pos, 1); - pos += 2; - RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); - pos += RSN_SELECTOR_LEN; - - *len = pos - len - 1; - - WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); - - return pos - wpa_ie; -} -#endif /* CONFIG_HS20 */ - - /** * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -348,13 +294,6 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) sm->group_cipher, sm->key_mgmt, sm->mgmt_group_cipher, sm); -#ifdef CONFIG_HS20 - else if (sm->proto == WPA_PROTO_OSEN) - return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, - sm->pairwise_cipher, - sm->group_cipher, - sm->key_mgmt); -#endif /* CONFIG_HS20 */ else return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, sm->pairwise_cipher, @@ -363,10 +302,121 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) } +/* + * wpa_external_auth_add_rsne - Build an RSNE for external authentication + * @rsne: Buffer in which the RSNE will be written + * @rsne_len: Length of the RSNE buffer + * @akmp: Authentication and key management protocol + * @pairwise_cipher: The pairwise cipher suite + * @group_cipher: The group addressed data cipher suite + * @group_mgmt_cipher: The group addressed management cipher suite + * @rsn_capab: RSN capabilities field + * @pmkid: PMKID to include in the RSNE, or %NULL if no PMKID + * Returns: Length of the RSNE or -1 on failure + */ +int wpa_external_auth_add_rsne(u8 *rsne, size_t rsne_len, int akmp, + int pairwise_cipher, int group_cipher, + int group_mgmt_cipher, u16 rsn_capab, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + u32 suite; + u8 *pos; + + wpa_printf(MSG_DEBUG, "RSN: Ext-Auth: Build RSNE"); + + if (rsne_len < sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + (pmkid ? 2 + PMKID_LEN : 0) + + (wpa_cipher_valid_mgmt_group(group_mgmt_cipher) ? + (RSN_SELECTOR_LEN + (!pmkid ? 2 : 0)) : 0)) { + wpa_printf(MSG_DEBUG, "Ext-Auth: Too short RSNE buffer (%lu bytes)", + (unsigned long) rsne_len); + return -1; + } + + hdr = (struct rsn_ie_hdr *) rsne; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + /* Group cipher */ + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (!suite || !wpa_cipher_valid_group(group_cipher)) { + wpa_printf(MSG_INFO, + "RSN: Ext-Auth: Invalid group cipher 0x%x", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Pairwise cipher */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (!suite || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_INFO, + "RSN: Ext-Auth: Invalid pairwise cipher 0x%x", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* AKM suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_akm_to_suite(akmp); + if (!suite) { + wpa_printf(MSG_INFO, "RSN: Ext-Auth: Invalid AKMP 0x%x", akmp); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + + if (pmkid) { + wpa_printf(MSG_DEBUG, "RSN: Ext-Auth: Adding PMKID"); + /* PMKID Count (2 octets, little endian) */ + WPA_PUT_LE16(pos, 1); + pos += 2; + /* PMKID */ + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + + /* Group Management Cipher Suite */ + if (wpa_cipher_valid_mgmt_group(group_mgmt_cipher)) { + if (!pmkid) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + group_mgmt_cipher)); + pos += RSN_SELECTOR_LEN; + } + + hdr->len = (pos - rsne) - 2; + + WPA_ASSERT((size_t) (pos - rsne) <= rsne_len); + + return pos - rsne; +} + + int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) { u8 *pos = rsnxe; - u32 capab = 0, tmp; + u64 capab = 0, tmp; size_t flen; if (wpa_key_mgmt_sae(sm->key_mgmt) && @@ -385,8 +435,29 @@ int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); if (sm->prot_range_neg) capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + if (sm->prot_range_neg_x20) + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20); if (sm->ssid_protection) capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION); + if (sm->spp_amsdu) + capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); + if (sm->sae_pw_id_change) + capab |= BIT_ULL(WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE); +#ifdef CONFIG_ENC_ASSOC + if (sm->assoc_encryption) + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION) | + BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_PMKSA_PRIVACY + if (sm->pmksa_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); +#endif /* CONFIG_PMKSA_PRIVACY */ +#ifdef CONFIG_IEEE8021X_AUTH + if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt & + ~WPA_KEY_MGMT_IEEE8021X) && + sm->eap_over_auth_frame) + capab |= BIT(WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES); +#endif /* CONFIG_IEEE8021X_AUTH */ if (!capab) return 0; /* no supported extended RSN capabilities */ diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h index 83a6727fe014..2847107e3bd7 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.h +++ b/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -12,7 +12,15 @@ struct wpa_sm; int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); +int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm); int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len); u16 rsn_supp_capab(struct wpa_sm *sm); +int wpa_external_auth_add_rsne(u8 *rsne, size_t rsne_len, int akmp, + int pairwise_cipher, int group_cipher, + int group_mgmt_cipher, u16 rsn_capab, + const u8 *pmkid); #endif /* WPA_IE_H */ diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index 486da16fd3c9..49af5a7a2009 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/src/tls/tlsv1_client.c @@ -604,7 +604,7 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen) { - char *cipher; + const char *cipher; switch (conn->rl.cipher_suite) { case TLS_RSA_WITH_RC4_128_MD5: diff --git a/contrib/wpa/src/tls/tlsv1_common.c b/contrib/wpa/src/tls/tlsv1_common.c index 0dd8e279994f..c675d02eb232 100644 --- a/contrib/wpa/src/tls/tlsv1_common.c +++ b/contrib/wpa/src/tls/tlsv1_common.c @@ -76,16 +76,8 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { static const struct tls_cipher_data tls_ciphers[] = { { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0, CRYPTO_CIPHER_NULL }, - { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8, - CRYPTO_CIPHER_NULL }, - { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0, - CRYPTO_CIPHER_ALG_RC2 }, - { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0, - CRYPTO_CIPHER_ALG_RC4 }, { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, CRYPTO_CIPHER_ALG_RC4 }, - { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, - CRYPTO_CIPHER_ALG_DES }, { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, CRYPTO_CIPHER_ALG_DES }, { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, diff --git a/contrib/wpa/src/tls/tlsv1_common.h b/contrib/wpa/src/tls/tlsv1_common.h index 4cfdc2d55159..b68115260069 100644 --- a/contrib/wpa/src/tls/tlsv1_common.h +++ b/contrib/wpa/src/tls/tlsv1_common.h @@ -194,11 +194,7 @@ typedef enum { typedef enum { TLS_CIPHER_NULL, - TLS_CIPHER_RC4_40, TLS_CIPHER_RC4_128, - TLS_CIPHER_RC2_CBC_40, - TLS_CIPHER_IDEA_CBC, - TLS_CIPHER_DES40_CBC, TLS_CIPHER_DES_CBC, TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_AES_128_CBC, diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index b006e99fd538..e4e641206a43 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -459,7 +459,7 @@ int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, } -static char * x509_name_attr_str(enum x509_name_attr_type type) +static const char * x509_name_attr_str(enum x509_name_attr_type type) { switch (type) { case X509_NAME_ATTR_NOT_USED: diff --git a/contrib/wpa/src/utils/bitfield.c b/contrib/wpa/src/utils/bitfield.c index 8dcec3907e9e..b79f43516c7a 100644 --- a/contrib/wpa/src/utils/bitfield.c +++ b/contrib/wpa/src/utils/bitfield.c @@ -31,6 +31,39 @@ struct bitfield * bitfield_alloc(size_t max_bits) } +struct bitfield * bitfield_alloc_data(const u8 *data, size_t len) +{ + struct bitfield *bf; + + bf = os_zalloc(sizeof(*bf) + len); + if (!bf) + return NULL; + + bf->bits = (u8 *) (bf + 1); + os_memcpy(bf->bits, data, len); + bf->max_bits = len * 8; + + return bf; +} + + +struct bitfield * bitfield_dup(const struct bitfield *orig) +{ + struct bitfield *bf; + + if (!orig) + return NULL; + + bf = os_memdup(orig, sizeof(*orig) + (orig->max_bits + 7) / 8); + if (!bf) + return NULL; + + bf->bits = (u8 *) (bf + 1); + + return bf; +} + + void bitfield_free(struct bitfield *bf) { os_free(bf); @@ -53,7 +86,7 @@ void bitfield_clear(struct bitfield *bf, size_t bit) } -int bitfield_is_set(struct bitfield *bf, size_t bit) +int bitfield_is_set(const struct bitfield *bf, size_t bit) { if (bit >= bf->max_bits) return 0; @@ -87,3 +120,128 @@ int bitfield_get_first_zero(struct bitfield *bf) return -1; return i; } + + +int bitfield_union_in_place(struct bitfield *a, const struct bitfield *b) +{ + size_t i, upper; + + if (!a || !b || a->max_bits < b->max_bits) + return -1; + + upper = (b->max_bits + 7) / 8; + + for (i = 0 ; i < upper; i++) + a->bits[i] |= b->bits[i]; + + return 0; +} + + +struct bitfield * bitfield_union(const struct bitfield *a, + const struct bitfield *b) +{ + struct bitfield *res; + int ret; + + if (!a || !b) + return NULL; + + if (a->max_bits > b->max_bits) { + res = bitfield_dup(a); + ret = bitfield_union_in_place(res, b); + } else { + res = bitfield_dup(b); + ret = bitfield_union_in_place(res, a); + } + + if (!ret) + return res; + + os_free(res); + return NULL; +} + + +int bitfield_intersect_in_place(struct bitfield *a, + const struct bitfield *b) +{ + size_t i, upper; + + if (!a || !b) + return -1; + + if (a->max_bits < b->max_bits) + upper = (a->max_bits + 7) / 8; + else + upper = (b->max_bits + 7) / 8; + + + for (i = 0 ; i < upper; i++) + a->bits[i] &= b->bits[i]; + + upper = (a->max_bits + 7) / 8; + for (; i < upper; i++) + a->bits[i] = 0; + + return 0; +} + + +int bitfield_is_subset(const struct bitfield *a, const struct bitfield *b) +{ + size_t i, upper; + + if (!a || !b) + return -1; + + if (a->max_bits < b->max_bits) + return 0; + + upper = (b->max_bits + 7) / 8; + + for (i = 0; i < upper; i++) { + u8 res = a->bits[i] & b->bits[i]; + + if (res != b->bits[i]) + return 0; + } + + return 1; +} + + +size_t bitfield_size(const struct bitfield *bf) +{ + if (!bf) + return 0; + return bf->max_bits; +} + + +int bitfield_intersects(const struct bitfield *a, const struct bitfield *b) +{ + size_t i, upper; + + if (!a || !b) + return -1; + + if (a->max_bits < b->max_bits) + upper = (a->max_bits + 7) / 8; + else + upper = (b->max_bits + 7) / 8; + + for (i = 0; i < upper; i++) + if (a->bits[i] & b->bits[i]) + return 1; + + return 0; +} + + +void bitfield_dump(struct bitfield *bf, const char *title) +{ + wpa_printf(MSG_DEBUG, "bitfield: %s: max_bits=%zu", + title, bf->max_bits); + wpa_hexdump(MSG_DEBUG, "bits: ", bf->bits, bf->max_bits / 8); +} diff --git a/contrib/wpa/src/utils/bitfield.h b/contrib/wpa/src/utils/bitfield.h index 7050a208c8f6..db136cd176ac 100644 --- a/contrib/wpa/src/utils/bitfield.h +++ b/contrib/wpa/src/utils/bitfield.h @@ -12,10 +12,20 @@ struct bitfield; struct bitfield * bitfield_alloc(size_t max_bits); +struct bitfield * bitfield_alloc_data(const u8 *data, size_t len); +struct bitfield * bitfield_dup(const struct bitfield *orig); void bitfield_free(struct bitfield *bf); void bitfield_set(struct bitfield *bf, size_t bit); void bitfield_clear(struct bitfield *bf, size_t bit); -int bitfield_is_set(struct bitfield *bf, size_t bit); +int bitfield_is_set(const struct bitfield *bf, size_t bit); int bitfield_get_first_zero(struct bitfield *bf); +int bitfield_union_in_place(struct bitfield *a, const struct bitfield *b); +struct bitfield * bitfield_union(const struct bitfield *a, + const struct bitfield *b); +int bitfield_intersect_in_place(struct bitfield *a, const struct bitfield *b); +int bitfield_is_subset(const struct bitfield *a, const struct bitfield *b); +size_t bitfield_size(const struct bitfield *bf); +int bitfield_intersects(const struct bitfield *a, const struct bitfield *b); +void bitfield_dump(struct bitfield *bf, const char *title); #endif /* BITFIELD_H */ diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c index d62dec721b56..1f34adf0af5f 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -990,7 +990,7 @@ void int_array_add_unique(int **res, int a) } -bool int_array_includes(int *arr, int val) +bool int_array_includes(const int *arr, int val) { int i; @@ -1003,6 +1003,36 @@ bool int_array_includes(int *arr, int val) } +bool int_array_equal(const int *a, const int *b) +{ + size_t alen, blen, i; + + if (!a || !b) + return false; + + alen = int_array_len(a); + blen = int_array_len(b); + + if (alen != blen) + return false; + + for (i = 0; i < alen; i++) { + if (!int_array_includes(b, a[i])) + return false; + } + + return true; +} + + +int * int_array_dup(const int *a) +{ + if (!a) + return NULL; + return os_memdup(a, (int_array_len(a) + 1) * sizeof(int)); +} + + void str_clear_free(char *str) { if (str) { diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index dc2177537674..076f26697a07 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -282,6 +282,23 @@ static inline void WPA_PUT_LE32(u8 *a, u32 val) a[0] = val & 0xff; } +static inline u64 WPA_GET_LE48(const u8 *a) +{ + return (((u64) a[5]) << 40) | (((u64) a[4]) << 32) | + (((u64) a[3]) << 24) | (((u64) a[2]) << 16) | + (((u64) a[1]) << 8) | ((u64) a[0]); +} + +static inline void WPA_PUT_LE48(u8 *a, u64 val) +{ + a[5] = val >> 40; + a[4] = val >> 32; + a[3] = val >> 24; + a[2] = val >> 16; + a[1] = val >> 8; + a[0] = val & 0xff; +} + static inline u64 WPA_GET_BE64(const u8 *a) { return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) | @@ -441,6 +458,12 @@ void perror(const char *s); #define BIT(x) (1U << (x)) #endif +#ifndef BIT_ULL +#define BIT_ULL(x) (1ULL << (x)) +#endif + +#define BITS(src, mask, pos) ((src & mask) >> pos) + #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif @@ -577,7 +600,9 @@ size_t int_array_len(const int *a); void int_array_concat(int **res, const int *a); void int_array_sort_unique(int *a); void int_array_add_unique(int **res, int a); -bool int_array_includes(int *arr, int val); +bool int_array_includes(const int *arr, int val); +bool int_array_equal(const int *a, const int *b); +int * int_array_dup(const int *a); #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -607,7 +632,7 @@ char * get_param(const char *cmd, const char *param); /* Iterate all links, or, if no link is defined, iterate given index */ #define for_each_link_default(_links, _i, _def_idx) \ for ((_i) = (_links) ? 0 : (_def_idx); \ - (_i) < MAX_NUM_MLD_LINKS || \ + ((_links) && (_i) < MAX_NUM_MLD_LINKS) || \ (!(_links) && (_i) == (_def_idx)); \ (_i)++) \ if (!(_links) || (_links) & BIT(_i)) diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c index 00b0beff0b78..fdbb95fa430a 100644 --- a/contrib/wpa/src/utils/eloop.c +++ b/contrib/wpa/src/utils/eloop.c @@ -713,6 +713,7 @@ static void eloop_sock_table_destroy(struct eloop_sock_table *table) table->table[i].handler); wpa_trace_dump("eloop sock", &table->table[i]); } + eloop_trace_sock_remove_ref(table); os_free(table->table); } } @@ -971,6 +972,9 @@ static void eloop_handle_alarm(int sig) "is a bug that ends up in a busy loop that " "prevents clean shutdown.\n" "Killing program forcefully.\n"); +#ifdef WPA_TRACE + wpa_trace_show("eloop: could not process SIGINT or SIGTERM in two seconds"); +#endif /* WPA_TRACE */ exit(1); } #endif /* CONFIG_NATIVE_WINDOWS */ @@ -1263,6 +1267,7 @@ void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; struct os_reltime now; + size_t i; os_get_reltime(&now); dl_list_for_each_safe(timeout, prev, &eloop.timeout, @@ -1286,6 +1291,10 @@ void eloop_destroy(void) eloop_sock_table_destroy(&eloop.readers); eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.exceptions); + + for (i = 0; i < eloop.signal_count; i++) + signal(eloop.signals[i].sig, SIG_DFL); + os_free(eloop.signals); #ifdef CONFIG_ELOOP_POLL diff --git a/contrib/wpa/src/utils/ext_password_file.c b/contrib/wpa/src/utils/ext_password_file.c index 4bb0095f3f28..158500ced205 100644 --- a/contrib/wpa/src/utils/ext_password_file.c +++ b/contrib/wpa/src/utils/ext_password_file.c @@ -9,7 +9,6 @@ #include "includes.h" #include "utils/common.h" -#include "utils/config.h" #include "ext_password_i.h" @@ -84,6 +83,7 @@ static struct wpabuf * ext_password_file_get(void *ctx, const char *name) struct ext_password_file_data *data = ctx; struct wpabuf *password = NULL; char buf[512], *pos; + size_t name_len; int line = 0; FILE *f; @@ -95,11 +95,23 @@ static struct wpabuf * ext_password_file_get(void *ctx, const char *name) return NULL; } + name_len = os_strlen(name); + wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name); - while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { - char *sep = os_strchr(pos, '='); + while ((pos = fgets(buf, sizeof(buf), f))) { + char *sep; + line++; + + /* Strip newline characters */ + pos[strcspn(pos, "\r\n")] = 0; + + /* Skip comments and empty lines */ + if (*pos == '#' || *pos == '\0') + continue; + + sep = os_strchr(pos, '='); if (!sep) { wpa_printf(MSG_ERROR, "Invalid password line %d.", line); @@ -112,7 +124,8 @@ static struct wpabuf * ext_password_file_get(void *ctx, const char *name) } - if (os_strncmp(name, pos, sep - pos) != 0) + if (name_len != (size_t) (sep - pos) || + os_strncmp(name, pos, sep - pos) != 0) continue; password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1)); diff --git a/contrib/wpa/src/utils/http-utils.h b/contrib/wpa/src/utils/http-utils.h index 23e9ecd9bb63..14efaf813734 100644 --- a/contrib/wpa/src/utils/http-utils.h +++ b/contrib/wpa/src/utils/http-utils.h @@ -11,38 +11,6 @@ struct http_ctx; -struct http_othername { - char *oid; - u8 *data; - size_t len; -}; - -#define HTTP_MAX_CERT_LOGO_HASH 32 - -struct http_logo { - char *alg_oid; - u8 *hash; - size_t hash_len; - char *uri; -}; - -struct http_cert { - char **dnsname; - size_t num_dnsname; - struct http_othername *othername; - size_t num_othername; - struct http_logo *logo; - size_t num_logo; - const char *url; -}; - -int soap_init_client(struct http_ctx *ctx, const char *address, - const char *ca_fname, const char *username, - const char *password, const char *client_cert, - const char *client_key); -int soap_reinit_client(struct http_ctx *ctx); -xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node); - struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx); void http_ocsp_set(struct http_ctx *ctx, int val); void http_deinit_ctx(struct http_ctx *ctx); @@ -55,10 +23,6 @@ char * http_post(struct http_ctx *ctx, const char *url, const char *data, const char *username, const char *password, const char *client_cert, const char *client_key, size_t *resp_len); -void http_set_cert_cb(struct http_ctx *ctx, - int (*cb)(void *ctx, struct http_cert *cert), - void *cb_ctx); const char * http_get_err(struct http_ctx *ctx); -void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname); #endif /* HTTP_UTILS_H */ diff --git a/contrib/wpa/src/utils/http_curl.c b/contrib/wpa/src/utils/http_curl.c index 77d5b35788e0..1cf2f7e56408 100644 --- a/contrib/wpa/src/utils/http_curl.c +++ b/contrib/wpa/src/utils/http_curl.c @@ -31,31 +31,15 @@ #endif /* EAP_TLS_OPENSSL */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L -static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) -{ - return ASN1_STRING_data((ASN1_STRING *) x); -} -#endif /* OpenSSL < 1.1.0 */ - - struct http_ctx { void *ctx; struct xml_node_ctx *xml; CURL *curl; struct curl_slist *curl_hdr; char *svc_address; - char *svc_ca_fname; - char *svc_username; - char *svc_password; - char *svc_client_cert; - char *svc_client_key; char *curl_buf; size_t curl_buf_len; - int (*cert_cb)(void *ctx, struct http_cert *cert); - void *cert_cb_ctx; - enum { NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP } ocsp; @@ -81,16 +65,6 @@ static void clear_curl(struct http_ctx *ctx) } -static void clone_str(char **dst, const char *src) -{ - os_free(*dst); - if (src) - *dst = os_strdup(src); - else - *dst = NULL; -} - - static void debug_dump(struct http_ctx *ctx, const char *title, const char *buf, size_t len) { @@ -202,773 +176,6 @@ static void debug_dump_cert(const char *title, X509 *cert) } -static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert, - OTHERNAME *o) -{ - char txt[100]; - int res; - struct http_othername *on; - ASN1_TYPE *val; - - on = os_realloc_array(cert->othername, cert->num_othername + 1, - sizeof(struct http_othername)); - if (on == NULL) - return; - cert->othername = on; - on = &on[cert->num_othername]; - os_memset(on, 0, sizeof(*on)); - - res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1); - if (res < 0 || res >= (int) sizeof(txt)) - return; - - on->oid = os_strdup(txt); - if (on->oid == NULL) - return; - - val = o->value; - on->data = val->value.octet_string->data; - on->len = val->value.octet_string->length; - - cert->num_othername++; -} - - -static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert, - ASN1_STRING *name) -{ - char *buf; - char **n; - - buf = NULL; - if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0) - return; - - n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1, - sizeof(char *)); - if (n == NULL) - return; - - cert->dnsname = n; - n[cert->num_dnsname] = buf; - cert->num_dnsname++; -} - - -static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert, - const GENERAL_NAME *name) -{ - switch (name->type) { - case GEN_OTHERNAME: - add_alt_name_othername(ctx, cert, name->d.otherName); - break; - case GEN_DNS: - add_alt_name_dns(ctx, cert, name->d.dNSName); - break; - } -} - - -static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert, - GENERAL_NAMES *names) -{ - int num, i; - - num = sk_GENERAL_NAME_num(names); - for (i = 0; i < num; i++) { - const GENERAL_NAME *name; - name = sk_GENERAL_NAME_value(names, i); - add_alt_name(ctx, cert, name); - } -} - - -/* RFC 3709 */ - -typedef struct { - X509_ALGOR *hashAlg; - ASN1_OCTET_STRING *hashValue; -} HashAlgAndValue; - -typedef struct { - STACK_OF(HashAlgAndValue) *refStructHash; - STACK_OF(ASN1_IA5STRING) *refStructURI; -} LogotypeReference; - -typedef struct { - ASN1_IA5STRING *mediaType; - STACK_OF(HashAlgAndValue) *logotypeHash; - STACK_OF(ASN1_IA5STRING) *logotypeURI; -} LogotypeDetails; - -typedef struct { - int type; - union { - ASN1_INTEGER *numBits; - ASN1_INTEGER *tableSize; - } d; -} LogotypeImageResolution; - -typedef struct { - ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */ - ASN1_INTEGER *fileSize; - ASN1_INTEGER *xSize; - ASN1_INTEGER *ySize; - LogotypeImageResolution *resolution; - ASN1_IA5STRING *language; -} LogotypeImageInfo; - -typedef struct { - LogotypeDetails *imageDetails; - LogotypeImageInfo *imageInfo; -} LogotypeImage; - -typedef struct { - ASN1_INTEGER *fileSize; - ASN1_INTEGER *playTime; - ASN1_INTEGER *channels; - ASN1_INTEGER *sampleRate; - ASN1_IA5STRING *language; -} LogotypeAudioInfo; - -typedef struct { - LogotypeDetails *audioDetails; - LogotypeAudioInfo *audioInfo; -} LogotypeAudio; - -typedef struct { - STACK_OF(LogotypeImage) *image; - STACK_OF(LogotypeAudio) *audio; -} LogotypeData; - -typedef struct { - int type; - union { - LogotypeData *direct; - LogotypeReference *indirect; - } d; -} LogotypeInfo; - -typedef struct { - ASN1_OBJECT *logotypeType; - LogotypeInfo *info; -} OtherLogotypeInfo; - -typedef struct { - STACK_OF(LogotypeInfo) *communityLogos; - LogotypeInfo *issuerLogo; - LogotypeInfo *subjectLogo; - STACK_OF(OtherLogotypeInfo) *otherLogos; -} LogotypeExtn; - -ASN1_SEQUENCE(HashAlgAndValue) = { - ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR), - ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING) -} ASN1_SEQUENCE_END(HashAlgAndValue); - -ASN1_SEQUENCE(LogotypeReference) = { - ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue), - ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING) -} ASN1_SEQUENCE_END(LogotypeReference); - -ASN1_SEQUENCE(LogotypeDetails) = { - ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING), - ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue), - ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING) -} ASN1_SEQUENCE_END(LogotypeDetails); - -ASN1_CHOICE(LogotypeImageResolution) = { - ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1), - ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2) -} ASN1_CHOICE_END(LogotypeImageResolution); - -ASN1_SEQUENCE(LogotypeImageInfo) = { - ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0), - ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER), - ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER), - ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER), - ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution), - ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4), -} ASN1_SEQUENCE_END(LogotypeImageInfo); - -ASN1_SEQUENCE(LogotypeImage) = { - ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails), - ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo) -} ASN1_SEQUENCE_END(LogotypeImage); - -ASN1_SEQUENCE(LogotypeAudioInfo) = { - ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER), - ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER), - ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER), - ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3), - ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4) -} ASN1_SEQUENCE_END(LogotypeAudioInfo); - -ASN1_SEQUENCE(LogotypeAudio) = { - ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails), - ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo) -} ASN1_SEQUENCE_END(LogotypeAudio); - -ASN1_SEQUENCE(LogotypeData) = { - ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage), - ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1) -} ASN1_SEQUENCE_END(LogotypeData); - -ASN1_CHOICE(LogotypeInfo) = { - ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0), - ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1) -} ASN1_CHOICE_END(LogotypeInfo); - -ASN1_SEQUENCE(OtherLogotypeInfo) = { - ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT), - ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo) -} ASN1_SEQUENCE_END(OtherLogotypeInfo); - -ASN1_SEQUENCE(LogotypeExtn) = { - ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0), - ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1), - ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2), - ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3) -} ASN1_SEQUENCE_END(LogotypeExtn); - -IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st)) -#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) -#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) -#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i)) -#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st)) -#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i)) -#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st)) -#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) -#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) -#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) -#else -DEFINE_STACK_OF(LogotypeInfo) -DEFINE_STACK_OF(LogotypeImage) -DEFINE_STACK_OF(LogotypeAudio) -DEFINE_STACK_OF(HashAlgAndValue) -DEFINE_STACK_OF(ASN1_IA5STRING) -#endif - - -static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, - HashAlgAndValue *hash, ASN1_IA5STRING *uri) -{ - char txt[100]; - int res, len; - struct http_logo *n; - - if (hash == NULL || uri == NULL) - return; - - res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1); - if (res < 0 || res >= (int) sizeof(txt)) - return; - - n = os_realloc_array(hcert->logo, hcert->num_logo + 1, - sizeof(struct http_logo)); - if (n == NULL) - return; - hcert->logo = n; - n = &hcert->logo[hcert->num_logo]; - os_memset(n, 0, sizeof(*n)); - - n->alg_oid = os_strdup(txt); - if (n->alg_oid == NULL) - return; - - n->hash_len = ASN1_STRING_length(hash->hashValue); - n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue), - n->hash_len); - if (n->hash == NULL) { - os_free(n->alg_oid); - return; - } - - len = ASN1_STRING_length(uri); - n->uri = os_malloc(len + 1); - if (n->uri == NULL) { - os_free(n->alg_oid); - os_free(n->hash); - return; - } - os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len); - n->uri[len] = '\0'; - - hcert->num_logo++; -} - - -static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert, - LogotypeData *data) -{ - int i, num; - - if (data->image == NULL) - return; - - num = sk_LogotypeImage_num(data->image); - for (i = 0; i < num; i++) { - LogotypeImage *image; - LogotypeDetails *details; - int j, hash_num, uri_num; - HashAlgAndValue *found_hash = NULL; - - image = sk_LogotypeImage_value(data->image, i); - if (image == NULL) - continue; - - details = image->imageDetails; - if (details == NULL) - continue; - - hash_num = sk_HashAlgAndValue_num(details->logotypeHash); - for (j = 0; j < hash_num; j++) { - HashAlgAndValue *hash; - char txt[100]; - int res; - hash = sk_HashAlgAndValue_value(details->logotypeHash, - j); - if (hash == NULL) - continue; - res = OBJ_obj2txt(txt, sizeof(txt), - hash->hashAlg->algorithm, 1); - if (res < 0 || res >= (int) sizeof(txt)) - continue; - if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) { - found_hash = hash; - break; - } - } - - if (!found_hash) { - wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo"); - continue; - } - - uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI); - for (j = 0; j < uri_num; j++) { - ASN1_IA5STRING *uri; - uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j); - add_logo(ctx, hcert, found_hash, uri); - } - } -} - - -static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert, - LogotypeReference *ref) -{ - int j, hash_num, uri_num; - - hash_num = sk_HashAlgAndValue_num(ref->refStructHash); - uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI); - if (hash_num != uri_num) { - wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d", - hash_num, uri_num); - return; - } - - for (j = 0; j < hash_num; j++) { - HashAlgAndValue *hash; - ASN1_IA5STRING *uri; - hash = sk_HashAlgAndValue_value(ref->refStructHash, j); - uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j); - add_logo(ctx, hcert, hash, uri); - } -} - - -static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent) -{ - int i; - const unsigned char *data; - - BIO_printf(out, "%*shashAlg: ", indent, ""); - i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm); - BIO_printf(out, "\n"); - - BIO_printf(out, "%*shashValue: ", indent, ""); - data = hash->hashValue->data; - for (i = 0; i < hash->hashValue->length; i++) - BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]); - BIO_printf(out, "\n"); -} - -static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent) -{ - int i, num; - - BIO_printf(out, "%*sLogotypeDetails\n", indent, ""); - if (details->mediaType) { - BIO_printf(out, "%*smediaType: ", indent, ""); - ASN1_STRING_print(out, details->mediaType); - BIO_printf(out, "\n"); - } - - num = details->logotypeHash ? - sk_HashAlgAndValue_num(details->logotypeHash) : 0; - for (i = 0; i < num; i++) { - HashAlgAndValue *hash; - hash = sk_HashAlgAndValue_value(details->logotypeHash, i); - i2r_HashAlgAndValue(hash, out, indent); - } - - num = details->logotypeURI ? - sk_ASN1_IA5STRING_num(details->logotypeURI) : 0; - for (i = 0; i < num; i++) { - ASN1_IA5STRING *uri; - uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i); - BIO_printf(out, "%*slogotypeURI: ", indent, ""); - ASN1_STRING_print(out, uri); - BIO_printf(out, "\n"); - } -} - -static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent) -{ - long val; - - BIO_printf(out, "%*sLogotypeImageInfo\n", indent, ""); - if (info->type) { - val = ASN1_INTEGER_get(info->type); - BIO_printf(out, "%*stype: %ld\n", indent, "", val); - } else { - BIO_printf(out, "%*stype: default (1)\n", indent, ""); - } - val = ASN1_INTEGER_get(info->fileSize); - BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val); - val = ASN1_INTEGER_get(info->xSize); - BIO_printf(out, "%*sxSize: %ld\n", indent, "", val); - val = ASN1_INTEGER_get(info->ySize); - BIO_printf(out, "%*sySize: %ld\n", indent, "", val); - if (info->resolution) { - BIO_printf(out, "%*sresolution [%d]\n", indent, "", - info->resolution->type); - switch (info->resolution->type) { - case 0: - val = ASN1_INTEGER_get(info->resolution->d.numBits); - BIO_printf(out, "%*snumBits: %ld\n", indent, "", val); - break; - case 1: - val = ASN1_INTEGER_get(info->resolution->d.tableSize); - BIO_printf(out, "%*stableSize: %ld\n", indent, "", val); - break; - } - } - if (info->language) { - BIO_printf(out, "%*slanguage: ", indent, ""); - ASN1_STRING_print(out, info->language); - BIO_printf(out, "\n"); - } -} - -static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent) -{ - BIO_printf(out, "%*sLogotypeImage\n", indent, ""); - if (image->imageDetails) { - i2r_LogotypeDetails(image->imageDetails, out, indent + 4); - } - if (image->imageInfo) { - i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4); - } -} - -static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out, - int indent) -{ - int i, num; - - BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title); - - num = data->image ? sk_LogotypeImage_num(data->image) : 0; - for (i = 0; i < num; i++) { - LogotypeImage *image = sk_LogotypeImage_value(data->image, i); - i2r_LogotypeImage(image, out, indent + 4); - } - - num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0; - for (i = 0; i < num; i++) { - BIO_printf(out, "%*saudio: TODO\n", indent, ""); - } -} - -static void i2r_LogotypeReference(LogotypeReference *ref, const char *title, - BIO *out, int indent) -{ - int i, hash_num, uri_num; - - BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title); - - hash_num = ref->refStructHash ? - sk_HashAlgAndValue_num(ref->refStructHash) : 0; - uri_num = ref->refStructURI ? - sk_ASN1_IA5STRING_num(ref->refStructURI) : 0; - if (hash_num != uri_num) { - BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n", - indent, "", hash_num, uri_num); - return; - } - - for (i = 0; i < hash_num; i++) { - HashAlgAndValue *hash; - ASN1_IA5STRING *uri; - - hash = sk_HashAlgAndValue_value(ref->refStructHash, i); - i2r_HashAlgAndValue(hash, out, indent); - - uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i); - BIO_printf(out, "%*srefStructURI: ", indent, ""); - ASN1_STRING_print(out, uri); - BIO_printf(out, "\n"); - } -} - -static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out, - int indent) -{ - switch (info->type) { - case 0: - i2r_LogotypeData(info->d.direct, title, out, indent); - break; - case 1: - i2r_LogotypeReference(info->d.indirect, title, out, indent); - break; - } -} - -static void debug_print_logotypeext(LogotypeExtn *logo) -{ - BIO *out; - int i, num; - int indent = 0; - - out = BIO_new_fp(stdout, BIO_NOCLOSE); - if (out == NULL) - return; - - if (logo->communityLogos) { - num = sk_LogotypeInfo_num(logo->communityLogos); - for (i = 0; i < num; i++) { - LogotypeInfo *info; - info = sk_LogotypeInfo_value(logo->communityLogos, i); - i2r_LogotypeInfo(info, "communityLogo", out, indent); - } - } - - if (logo->issuerLogo) { - i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent ); - } - - if (logo->subjectLogo) { - i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent); - } - - if (logo->otherLogos) { - BIO_printf(out, "%*sotherLogos - TODO\n", indent, ""); - } - - BIO_free(out); -} - - -static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, - X509 *cert) -{ - ASN1_OBJECT *obj; - int pos; - X509_EXTENSION *ext; - ASN1_OCTET_STRING *os; - LogotypeExtn *logo; - const unsigned char *data; - int i, num; - - obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0); - if (obj == NULL) - return; - - pos = X509_get_ext_by_OBJ(cert, obj, -1); - if (pos < 0) { - wpa_printf(MSG_INFO, "No logotype extension included"); - return; - } - - wpa_printf(MSG_INFO, "Parsing logotype extension"); - ext = X509_get_ext(cert, pos); - if (!ext) { - wpa_printf(MSG_INFO, "Could not get logotype extension"); - return; - } - - os = X509_EXTENSION_get_data(ext); - if (os == NULL) { - wpa_printf(MSG_INFO, "Could not get logotype extension data"); - return; - } - - wpa_hexdump(MSG_DEBUG, "logotypeExtn", - ASN1_STRING_get0_data(os), ASN1_STRING_length(os)); - - data = ASN1_STRING_get0_data(os); - logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os)); - if (logo == NULL) { - wpa_printf(MSG_INFO, "Failed to parse logotypeExtn"); - return; - } - - if (wpa_debug_level < MSG_INFO) - debug_print_logotypeext(logo); - - if (!logo->communityLogos) { - wpa_printf(MSG_INFO, "No communityLogos included"); - LogotypeExtn_free(logo); - return; - } - - num = sk_LogotypeInfo_num(logo->communityLogos); - for (i = 0; i < num; i++) { - LogotypeInfo *info; - info = sk_LogotypeInfo_value(logo->communityLogos, i); - switch (info->type) { - case 0: - add_logo_direct(ctx, hcert, info->d.direct); - break; - case 1: - add_logo_indirect(ctx, hcert, info->d.indirect); - break; - } - } - - LogotypeExtn_free(logo); -} - - -static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert, - X509 *cert, GENERAL_NAMES **names) -{ - os_memset(hcert, 0, sizeof(*hcert)); - hcert->url = ctx->url ? ctx->url : ctx->svc_address; - - *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - if (*names) - add_alt_names(ctx, hcert, *names); - - add_logotype_ext(ctx, hcert, cert); -} - - -static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names) -{ - unsigned int i; - - for (i = 0; i < hcert->num_dnsname; i++) - OPENSSL_free(hcert->dnsname[i]); - os_free(hcert->dnsname); - - for (i = 0; i < hcert->num_othername; i++) - os_free(hcert->othername[i].oid); - os_free(hcert->othername); - - for (i = 0; i < hcert->num_logo; i++) { - os_free(hcert->logo[i].alg_oid); - os_free(hcert->logo[i].hash); - os_free(hcert->logo[i].uri); - } - os_free(hcert->logo); - - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); -} - - -static int validate_server_cert(struct http_ctx *ctx, X509 *cert) -{ - GENERAL_NAMES *names; - struct http_cert hcert; - int ret; - - if (ctx->cert_cb == NULL) { - wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__); - return 0; - } - - if (0) { - BIO *out; - out = BIO_new_fp(stdout, BIO_NOCLOSE); - X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); - BIO_free(out); - } - - parse_cert(ctx, &hcert, cert, &names); - ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert); - parse_cert_free(&hcert, names); - - return ret; -} - - -void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname) -{ - BIO *in, *out; - X509 *cert; - GENERAL_NAMES *names; - struct http_cert hcert; - unsigned int i; - - in = BIO_new_file(fname, "r"); - if (in == NULL) { - wpa_printf(MSG_ERROR, "Could not read '%s'", fname); - return; - } - - cert = d2i_X509_bio(in, NULL); - BIO_free(in); - - if (cert == NULL) { - wpa_printf(MSG_ERROR, "Could not parse certificate"); - return; - } - - out = BIO_new_fp(stdout, BIO_NOCLOSE); - if (out) { - X509_print_ex(out, cert, XN_FLAG_COMPAT, - X509_FLAG_COMPAT); - BIO_free(out); - } - - wpa_printf(MSG_INFO, "Additional parsing information:"); - parse_cert(ctx, &hcert, cert, &names); - for (i = 0; i < hcert.num_othername; i++) { - if (os_strcmp(hcert.othername[i].oid, - "1.3.6.1.4.1.40808.1.1.1") == 0) { - char *name = os_zalloc(hcert.othername[i].len + 1); - if (name) { - os_memcpy(name, hcert.othername[i].data, - hcert.othername[i].len); - wpa_printf(MSG_INFO, - "id-wfa-hotspot-friendlyName: %s", - name); - os_free(name); - } - wpa_hexdump_ascii(MSG_INFO, - "id-wfa-hotspot-friendlyName", - hcert.othername[i].data, - hcert.othername[i].len); - } else { - wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s", - hcert.othername[i].oid); - wpa_hexdump_ascii(MSG_INFO, "unknown othername", - hcert.othername[i].data, - hcert.othername[i].len); - } - } - parse_cert_free(&hcert, names); - - X509_free(cert); -} - - static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) { struct http_ctx *ctx; @@ -1011,9 +218,6 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) depth, err, err_str, buf); debug_dump_cert("Server certificate chain - certificate", cert); - if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0) - return 0; - #ifdef OPENSSL_IS_BORINGSSL if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) { enum ocsp_result res; @@ -1387,91 +591,6 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, } -static int post_init_client(struct http_ctx *ctx, const char *address, - const char *ca_fname, const char *username, - const char *password, const char *client_cert, - const char *client_key) -{ - char *pos; - int count; - - clone_str(&ctx->svc_address, address); - clone_str(&ctx->svc_ca_fname, ca_fname); - clone_str(&ctx->svc_username, username); - clone_str(&ctx->svc_password, password); - clone_str(&ctx->svc_client_cert, client_cert); - clone_str(&ctx->svc_client_key, client_key); - - /* - * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname - * 'foo' provided via HTTP are different. - */ - for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos; - pos++) { - if (*pos == '/') - count++; - *pos = tolower(*pos); - } - - ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username, - password, client_cert, client_key); - if (ctx->curl == NULL) - return -1; - - return 0; -} - - -int soap_init_client(struct http_ctx *ctx, const char *address, - const char *ca_fname, const char *username, - const char *password, const char *client_cert, - const char *client_key) -{ - if (post_init_client(ctx, address, ca_fname, username, password, - client_cert, client_key) < 0) - return -1; - - ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, - "Content-Type: application/soap+xml"); - ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: "); - ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:"); - curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr); - - return 0; -} - - -int soap_reinit_client(struct http_ctx *ctx) -{ - char *address = NULL; - char *ca_fname = NULL; - char *username = NULL; - char *password = NULL; - char *client_cert = NULL; - char *client_key = NULL; - int ret; - - clear_curl(ctx); - - clone_str(&address, ctx->svc_address); - clone_str(&ca_fname, ctx->svc_ca_fname); - clone_str(&username, ctx->svc_username); - clone_str(&password, ctx->svc_password); - clone_str(&client_cert, ctx->svc_client_cert); - clone_str(&client_key, ctx->svc_client_key); - - ret = soap_init_client(ctx, address, ca_fname, username, password, - client_cert, client_key); - os_free(address); - os_free(ca_fname); - str_clear_free(username); - str_clear_free(password); - os_free(client_cert); - os_free(client_key); - return ret; -} - - static void free_curl_buf(struct http_ctx *ctx) { os_free(ctx->curl_buf); @@ -1480,73 +599,6 @@ static void free_curl_buf(struct http_ctx *ctx) } -xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node) -{ - char *str; - xml_node_t *envelope, *ret, *resp, *n; - CURLcode res; - long http = 0; - - ctx->last_err = NULL; - - wpa_printf(MSG_DEBUG, "SOAP: Sending message"); - envelope = soap_build_envelope(ctx->xml, node); - str = xml_node_to_str(ctx->xml, envelope); - xml_node_free(ctx->xml, envelope); - wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str); - - curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str); - free_curl_buf(ctx); - - res = curl_easy_perform(ctx->curl); - if (res != CURLE_OK) { - if (!ctx->last_err) - ctx->last_err = curl_easy_strerror(res); - wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", - ctx->last_err); - os_free(str); - free_curl_buf(ctx); - return NULL; - } - os_free(str); - - curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http); - wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http); - if (http != 200) { - ctx->last_err = "HTTP download failed"; - wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http); - free_curl_buf(ctx); - return NULL; - } - - if (ctx->curl_buf == NULL) - return NULL; - - wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf); - resp = xml_node_from_buf(ctx->xml, ctx->curl_buf); - free_curl_buf(ctx); - if (resp == NULL) { - wpa_printf(MSG_INFO, "Could not parse SOAP response"); - ctx->last_err = "Could not parse SOAP response"; - return NULL; - } - - ret = soap_get_body(ctx->xml, resp); - if (ret == NULL) { - wpa_printf(MSG_INFO, "Could not get SOAP body"); - ctx->last_err = "Could not get SOAP body"; - return NULL; - } - - wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'", - xml_node_get_localname(ctx->xml, ret)); - n = xml_node_copy(ctx->xml, ret); - xml_node_free(ctx->xml, resp); - - return n; -} - - struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx) { struct http_ctx *ctx; @@ -1582,11 +634,6 @@ void http_deinit_ctx(struct http_ctx *ctx) curl_global_cleanup(); os_free(ctx->svc_address); - os_free(ctx->svc_ca_fname); - str_clear_free(ctx->svc_username); - str_clear_free(ctx->svc_password); - os_free(ctx->svc_client_cert); - os_free(ctx->svc_client_key); os_free(ctx); } @@ -1726,15 +773,6 @@ fail: } -void http_set_cert_cb(struct http_ctx *ctx, - int (*cb)(void *ctx, struct http_cert *cert), - void *cb_ctx) -{ - ctx->cert_cb = cb; - ctx->cert_cb_ctx = cb_ctx; -} - - const char * http_get_err(struct http_ctx *ctx) { return ctx->last_err; diff --git a/contrib/wpa/src/utils/json.c b/contrib/wpa/src/utils/json.c index dd12f1b6ed3e..ebcc969cb8f4 100644 --- a/contrib/wpa/src/utils/json.c +++ b/contrib/wpa/src/utils/json.c @@ -157,24 +157,72 @@ fail: } -static int json_parse_number(const char **json_pos, const char *end, - int *ret_val) +static size_t json_get_number_len(const char *json_pos, const char *end, + bool *is_double) { - const char *pos = *json_pos; + const char *pos = json_pos; size_t len; - char *str; for (; pos < end; pos++) { - if (*pos != '-' && (*pos < '0' || *pos > '9')) { + switch (*pos) { + case '.': + case 'e': + case 'E': + *is_double = true; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + continue; + default: pos--; break; } + break; } if (pos == end) pos--; - if (pos < *json_pos) + if (pos < json_pos) + return 0; + len = pos - json_pos + 1; + + return len; +} + + +static int json_parse_double(const char **json_pos, size_t len, double *ret_val) +{ + char *str; + char *endptr; + + str = os_malloc(len + 1); + if (!str) return -1; - len = pos - *json_pos + 1; + os_memcpy(str, *json_pos, len); + str[len] = '\0'; + + *ret_val = strtod(str, &endptr); + if (endptr == str) { + os_free(str); + return -1; + } + os_free(str); + *json_pos += len - 1; + return 0; +} + + +static int json_parse_number(const char **json_pos, size_t len, int *ret_val) +{ + char *str; + str = os_malloc(len + 1); if (!str) return -1; @@ -183,7 +231,7 @@ static int json_parse_number(const char **json_pos, const char *end, *ret_val = atoi(str); os_free(str); - *json_pos = pos; + *json_pos += len - 1; return 0; } @@ -223,8 +271,11 @@ struct json_token * json_parse(const char *data, size_t data_len) const char *pos, *end; char *str; int num; + double dnum; + bool is_double; unsigned int depth = 0; unsigned int tokens = 0; + size_t len; pos = data; end = data + data_len; @@ -269,7 +320,8 @@ struct json_token * json_parse(const char *data, size_t data_len) case ']': /* end array */ case '}': /* end object */ if (!curr_token || !curr_token->parent || - curr_token->parent->state != JSON_STARTED) { + curr_token->parent->state != JSON_STARTED || + depth == 0) { wpa_printf(MSG_DEBUG, "JSON: Invalid state for end array/object"); goto fail; @@ -420,33 +472,65 @@ struct json_token * json_parse(const char *data, size_t data_len) case '8': case '9': /* number */ - if (json_parse_number(&pos, end, &num) < 0) + is_double = false; + len = json_get_number_len(pos, end, &is_double); + if (!len) goto fail; + if (is_double) { + if (json_parse_double(&pos, len, &dnum) < 0) + goto fail; + } else { + if (json_parse_number(&pos, len, &num) < 0) + goto fail; + } + if (!curr_token) { token = json_alloc_token(&tokens); if (!token) goto fail; - token->type = JSON_NUMBER; - token->number = num; + if (is_double) { + token->dnumber = dnum; + token->type = JSON_DOUBLE; + } else { + token->number = num; + token->type = JSON_NUMBER; + } token->state = JSON_COMPLETED; } else if (curr_token->state == JSON_WAITING_VALUE) { - curr_token->number = num; curr_token->state = JSON_COMPLETED; - curr_token->type = JSON_NUMBER; - wpa_printf(MSG_MSGDUMP, - "JSON: Number value: '%s' = '%d'", - curr_token->name, - curr_token->number); + if (is_double) { + curr_token->dnumber = dnum; + curr_token->type = JSON_DOUBLE; + wpa_printf(MSG_MSGDUMP, + "JSON: Double value: '%s' = '%f'", + curr_token->name, + curr_token->dnumber); + } else { + curr_token->number = num; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: '%s' = '%d'", + curr_token->name, + curr_token->number); + } } else if (curr_token->parent && curr_token->parent->type == JSON_ARRAY && curr_token->parent->state == JSON_STARTED && curr_token->state == JSON_EMPTY) { - curr_token->number = num; curr_token->state = JSON_COMPLETED; - curr_token->type = JSON_NUMBER; - wpa_printf(MSG_MSGDUMP, - "JSON: Number value: %d", - curr_token->number); + if (is_double) { + curr_token->dnumber = dnum; + curr_token->type = JSON_DOUBLE; + wpa_printf(MSG_MSGDUMP, + "JSON: Double value: %f", + curr_token->dnumber); + } else { + curr_token->number = num; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: %d", + curr_token->number); + } } else { wpa_printf(MSG_DEBUG, "JSON: Invalid state for a number"); @@ -563,6 +647,8 @@ static const char * json_type_str(enum json_type type) return "STRING"; case JSON_NUMBER: return "NUMBER"; + case JSON_DOUBLE: + return "DOUBLE"; case JSON_BOOLEAN: return "BOOLEAN"; case JSON_NULL: @@ -606,6 +692,12 @@ void json_add_int(struct wpabuf *json, const char *name, int val) } +void json_add_double(struct wpabuf *json, const char *name, double val) +{ + wpabuf_printf(json, "\"%s\":%f", name, val); +} + + void json_add_string(struct wpabuf *json, const char *name, const char *val) { wpabuf_printf(json, "\"%s\":\"%s\"", name, val); diff --git a/contrib/wpa/src/utils/json.h b/contrib/wpa/src/utils/json.h index 8448bb0c5c76..c1b8abd78fb7 100644 --- a/contrib/wpa/src/utils/json.h +++ b/contrib/wpa/src/utils/json.h @@ -16,6 +16,7 @@ struct json_token { JSON_ARRAY, JSON_STRING, JSON_NUMBER, + JSON_DOUBLE, JSON_BOOLEAN, JSON_NULL, } type; @@ -28,6 +29,7 @@ struct json_token { char *name; char *string; int number; + double dnumber; struct json_token *parent, *child, *sibling; }; @@ -41,6 +43,7 @@ struct wpabuf * json_get_member_base64(struct json_token *json, const char *name); void json_print_tree(struct json_token *root, char *buf, size_t buflen); void json_add_int(struct wpabuf *json, const char *name, int val); +void json_add_double(struct wpabuf *json, const char *name, double val); void json_add_string(struct wpabuf *json, const char *name, const char *val); int json_add_string_escape(struct wpabuf *json, const char *name, const void *val, size_t len); diff --git a/contrib/wpa/src/utils/module_tests.h b/contrib/wpa/src/utils/module_tests.h index 3bfe4ad026cc..14f26a139895 100644 --- a/contrib/wpa/src/utils/module_tests.h +++ b/contrib/wpa/src/utils/module_tests.h @@ -16,5 +16,6 @@ int utils_module_tests(void); int wps_module_tests(void); int common_module_tests(void); int crypto_module_tests(void); +int nan_module_tests(void); #endif /* MODULE_TESTS_H */ diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h index 1bbaea3a4bdc..9d6c340966c1 100644 --- a/contrib/wpa/src/utils/os.h +++ b/contrib/wpa/src/utils/os.h @@ -685,16 +685,19 @@ int os_exec(const char *program, const char *arg, int wait_completion); #define strcpy OS_DO_NOT_USE_strcpy #endif /* OS_REJECT_C_LIB_FUNCTIONS */ - -#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) #define TEST_FAIL() testing_test_fail(NULL, false) #define TEST_FAIL_TAG(tag) testing_test_fail(tag, false) + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) int testing_test_fail(const char *tag, bool is_alloc); int testing_set_fail_pattern(bool is_alloc, char *patterns); int testing_get_fail_pattern(bool is_alloc, char *buf, size_t buflen); #else -#define TEST_FAIL() 0 -#define TEST_FAIL_TAG(tag) 0 +static inline int testing_test_fail(const char *tag, bool is_alloc) +{ + return 0; +} + static inline int testing_set_fail_pattern(bool is_alloc, char *patterns) { return -1; diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c index 15a05958c970..42d26e19936c 100644 --- a/contrib/wpa/src/utils/os_unix.c +++ b/contrib/wpa/src/utils/os_unix.c @@ -95,26 +95,42 @@ int os_get_reltime(struct os_reltime *t) t->usec = ts.tv_nsec / 1000; return 0; } - switch (clock_id) { -#ifdef CLOCK_BOOTTIME - case CLOCK_BOOTTIME: - clock_id = CLOCK_MONOTONIC; - break; -#endif -#ifdef CLOCK_MONOTONIC -/* - * FreeBSD has both BOOTTIME and MONOTONIC defined to the same value, since they - * mean the same thing. FreeBSD 14.1 and ealier don't, so need this case. - */ -#if !(defined(CLOCK_BOOTTIME) && CLOCK_BOOTTIME == CLOCK_MONOTONIC) - case CLOCK_MONOTONIC: + + /* Try to fall back to alternative clockid_t values, if any */ + +#if defined(CLOCK_BOOTTIME) && defined(CLOCK_MONOTONIC) + /* Some systems (e.g., FreeBSD 15.0) define both CLOCK_BOOTTIME + * and CLOCK_MONOTONIC to have the same value, so handle that + * as a special case. */ + if (CLOCK_BOOTTIME == CLOCK_MONOTONIC && + clock_id == CLOCK_BOOTTIME) { clock_id = CLOCK_REALTIME; - break; -#endif -#endif - case CLOCK_REALTIME: - return -1; + continue; } + + if (clock_id == CLOCK_BOOTTIME) { + clock_id = CLOCK_MONOTONIC; + continue; + } +#elif CLOCK_BOOTTIME + /* CLOCK_MONOTONIC is not defined. Unknown whether such cases + * exists, but it is safe to fall back to CLOCK_REALTIME. + */ + if (clock_id == CLOCK_BOOTTIME) { + clock_id = CLOCK_REALTIME; + continue; + } +#endif + +#ifdef CLOCK_MONOTONIC + if (clock_id == CLOCK_MONOTONIC) { + clock_id = CLOCK_REALTIME; + continue; + } +#endif + + /* No additional clockid_t values to fall back to */ + return -1; } #else /* __MACH__ */ uint64_t abstime, nano; @@ -547,10 +563,11 @@ void * os_memdup(const void *src, size_t len) #ifdef WPA_TRACE #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) -struct wpa_trace_test_fail { +static struct wpa_trace_test_fail { unsigned int fail_after; char pattern[256]; -} wpa_trace_test_fail[5][2]; + bool uses_tag; +} wpa_trace_test_fail[2][5]; int testing_test_fail(const char *tag, bool is_alloc) { @@ -565,6 +582,9 @@ int testing_test_fail(const char *tag, bool is_alloc) is_alloc = !!is_alloc; + if (wpa_trace_test_fail[is_alloc][0].uses_tag && !tag) + return 0; + for (idx = 0; idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc]); idx++) { if (wpa_trace_test_fail[is_alloc][idx].fail_after != 0) break; @@ -681,6 +701,15 @@ int testing_set_fail_pattern(bool is_alloc, char *patterns) return -1; } + if (idx == 0) { + const char *p1, *p2; + + p1 = os_strchr(token, '-'); + p2 = os_strchr(token, ';'); + if (p1 && p2 && p1 < p2) + wpa_trace_test_fail[is_alloc][0].uses_tag = + true; + } os_strlcpy(wpa_trace_test_fail[is_alloc][idx].pattern, token + 1, sizeof(wpa_trace_test_fail[is_alloc][0].pattern)); @@ -721,15 +750,7 @@ int testing_get_fail_pattern(bool is_alloc, char *buf, size_t buflen) return -1; #endif /* WPA_TRACE_BFD */ } - -#else /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */ - -static inline int testing_test_fail(const char *tag, bool is_alloc) -{ - return 0; -} - -#endif +#endif /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */ void * os_malloc(size_t size) { @@ -838,7 +859,7 @@ int os_exec(const char *program, const char *arg, int wait_completion) if (pid == 0) { /* run the external command in the child process */ - const int MAX_ARG = 30; +#define MAX_ARG 30 char *_program, *_arg, *pos; char *argv[MAX_ARG + 1]; int i; diff --git a/contrib/wpa/src/utils/trace.c b/contrib/wpa/src/utils/trace.c index 7c9a17ff8f7c..0b735433f076 100644 --- a/contrib/wpa/src/utils/trace.c +++ b/contrib/wpa/src/utils/trace.c @@ -17,6 +17,8 @@ #ifdef WPA_TRACE +const char *current_btrace_ctx; + static struct dl_list active_references = { &active_references, &active_references }; @@ -197,6 +199,8 @@ static void wpa_trace_bfd_addr(void *pc) if (abfd == NULL) return; + if (start_offset > (uintptr_t) pc) + return; data.pc = (uintptr_t) ((u8 *) pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -238,6 +242,8 @@ static const char * wpa_trace_bfd_addr2func(void *pc) if (abfd == NULL) return NULL; + if (start_offset > (uintptr_t) pc) + return NULL; data.pc = (uintptr_t) ((u8 *) pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -310,6 +316,8 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len) for (i = 0; i < btrace_num; i++) { struct bfd_data data; + if (start_offset > (uintptr_t) btrace_res[i]) + continue; data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -340,7 +348,8 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len) #endif /* WPA_TRACE_BFD */ -void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) +void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num, + const char *btrace_ctx) { char **sym; int i; @@ -372,6 +381,9 @@ void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) state = TRACE_TAIL; } free(sym); + if (btrace_ctx) + wpa_printf(MSG_INFO, "WPA_TRACE: External context: %s", + btrace_ctx); wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); } @@ -412,8 +424,41 @@ void wpa_trace_check_ref(const void *addr) void wpa_trace_deinit(void) { #ifdef WPA_TRACE_BFD + free(prg_fname); + prg_fname = NULL; + free(syms); syms = NULL; + if (cached_abfd) { + bfd_close(cached_abfd); + cached_abfd = NULL; + } +#endif /* WPA_TRACE_BFD */ +} + +struct btrace_ctx_entry { + struct dl_list list; + char ctx[]; +}; +static struct dl_list old_btrace_ctxs = DL_LIST_HEAD_INIT(old_btrace_ctxs); + +void wpa_trace_set_context(const char *ctx) +{ +#ifdef WPA_TRACE_BFD + /* + * Old values might still be referenced, keep them in a list so that + * they are not freed and so that a reference exists for the sanitizer. + */ + if (ctx) { + struct btrace_ctx_entry *entry; + + entry = malloc(sizeof(*entry) + os_strlen(ctx) + 1); + strcpy(entry->ctx, ctx); + dl_list_add(&old_btrace_ctxs, &entry->list); + + current_btrace_ctx = entry->ctx; + } else + current_btrace_ctx = NULL; #endif /* WPA_TRACE_BFD */ } diff --git a/contrib/wpa/src/utils/trace.h b/contrib/wpa/src/utils/trace.h index d1636de07728..f35234e7d346 100644 --- a/contrib/wpa/src/utils/trace.h +++ b/contrib/wpa/src/utils/trace.h @@ -16,7 +16,12 @@ #include "list.h" -#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num; +extern const char *current_btrace_ctx; + +#define WPA_TRACE_INFO \ + void *btrace[WPA_TRACE_LEN]; \ + int btrace_num; \ + const char *btrace_ctx; struct wpa_trace_ref { struct dl_list list; @@ -26,10 +31,15 @@ struct wpa_trace_ref { #define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name #define wpa_trace_dump(title, ptr) \ - wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num) -void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num); -#define wpa_trace_record(ptr) \ - (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN) + wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num, \ + (ptr)->btrace_ctx) +void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num, + const char *btrace_ctx); +#define wpa_trace_record(ptr) \ + do { \ + (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN); \ + (ptr)->btrace_ctx = current_btrace_ctx; \ + } while (0) void wpa_trace_show(const char *title); #define wpa_trace_add_ref(ptr, name, addr) \ wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr)) @@ -41,6 +51,7 @@ void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr); } while (0) void wpa_trace_check_ref(const void *addr); size_t wpa_trace_calling_func(const char *buf[], size_t len); +void wpa_trace_set_context(const char *ctx); #else /* WPA_TRACE */ @@ -53,6 +64,10 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len); #define wpa_trace_remove_ref(ptr, name, addr) do { } while (0) #define wpa_trace_check_ref(addr) do { } while (0) +static inline void wpa_trace_set_context(const char *ctx) +{ +} + #endif /* WPA_TRACE */ diff --git a/contrib/wpa/src/utils/utils_module_tests.c b/contrib/wpa/src/utils/utils_module_tests.c index 365f21fb11c6..d0fd89250e84 100644 --- a/contrib/wpa/src/utils/utils_module_tests.c +++ b/contrib/wpa/src/utils/utils_module_tests.c @@ -127,9 +127,11 @@ static int printf_encode_decode_tests(void) static int bitfield_tests(void) { - struct bitfield *bf; + struct bitfield *bf, *bf_a = NULL, *bf_b = NULL, *bf_c = NULL; int i; int errors = 0; + u8 data_a[4] = { 0xff, 0x3f, 0x01, 0xf0 }; + u8 data_b[4] = { 0xff, 0xc0, 0xfe, 0x0f }; wpa_printf(MSG_INFO, "bitfield tests"); @@ -210,6 +212,76 @@ static int bitfield_tests(void) errors++; bitfield_free(bf); + bf_a = bitfield_alloc_data(data_a, sizeof(data_a)); + if (!bf_a) + goto fail; + + bf_b = bitfield_alloc_data(data_b, sizeof(data_b)); + if (!bf_b) + goto fail; + + bf_c = bitfield_dup(bf_a); + if (!bf_c) + goto fail; + + /* test intersection */ + if (bitfield_intersect_in_place(bf_c, bf_b)) + goto fail; + + for (i = 0; i < 8; i++) + if (!bitfield_is_set(bf_c, i)) + goto fail; + + for (; i < 32; i++) + if (bitfield_is_set(bf_c, i)) + goto fail; + + bitfield_free(bf_c); + + /* test union */ + bf_c = bitfield_union(bf_a, bf_b); + if (!bf_c) + goto fail; + + if (!bitfield_intersects(bf_a, bf_c) || + !bitfield_intersects(bf_b, bf_c) || + !bitfield_intersects(bf_c, bf_a) || + !bitfield_intersects(bf_c, bf_b) || + !bitfield_intersects(bf_a, bf_b)) + goto fail; + + for (i = 0; i < 32; i++) + if (!bitfield_is_set(bf_c, i)) + goto fail; + + if (!bitfield_is_subset(bf_c, bf_a) || + !bitfield_is_subset(bf_c, bf_b) || + bitfield_is_subset(bf_a, bf_c) || + bitfield_is_subset(bf_b, bf_c) || + bitfield_is_subset(bf_a, bf_b)) + goto fail; + + /* test in place union */ + if (bitfield_union_in_place(bf_a, bf_b)) + goto fail; + + if (bitfield_size(bf_a) != 32 || + bitfield_size(bf_b) != 32 || + bitfield_size(bf_c) != 32) + goto fail; + + for (i = 0; i < 32; i++) + if (!bitfield_is_set(bf_c, i)) + goto fail; + goto out; + +fail: + errors++; +out: + bitfield_free(bf_a); + bitfield_free(bf_c); + bitfield_free(bf_b); + if (errors) { wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors); return -1; @@ -864,6 +936,9 @@ static const struct json_test_data json_test_cases[] = { { "{\"t\":truetrue}", NULL }, { "\"test\"", "[1:STRING:]" }, { "123", "[1:NUMBER:]" }, + { "4.5", "[1:DOUBLE:]" }, + { "-6.7e-3", "[1:DOUBLE:]" }, + { "8.9e4", "[1:DOUBLE:]" }, { "true", "[1:BOOLEAN:]" }, { "false", "[1:BOOLEAN:]" }, { "null", "[1:NULL:]" }, @@ -891,13 +966,16 @@ static int json_tests(void) unsigned int i; struct json_token *root; char buf[1000]; + static const char *dval[] = { "-6.78e-2", "8.9e3" }; + double exp_dval[] = { -6.78e-2, 8.9e3 }; + int res; wpa_printf(MSG_INFO, "JSON tests"); for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) { const struct json_test_data *test = &json_test_cases[i]; - int res = 0; + res = 0; root = json_parse(test->json, os_strlen(test->json)); if ((root && !test->tree) || (!root && test->tree)) { wpa_printf(MSG_INFO, "JSON test %u failed", i); @@ -916,6 +994,21 @@ static int json_tests(void) return -1; } + + for (i = 0; i < ARRAY_SIZE(dval); i++) { + root = json_parse(dval[i], os_strlen(dval[i])); + if (!root) { + wpa_printf(MSG_INFO, "JSON test dval %u failed", i); + return -1; + } + if (root->type != JSON_DOUBLE || root->dnumber != exp_dval[i]) { + wpa_printf(MSG_INFO, "JSON test dval %u failed", i); + res = -1; + } + json_free(root); + if (res < 0) + return -1; + } #endif /* CONFIG_JSON */ return 0; } diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index 7f3dd185f100..0cd94a038cb1 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -128,9 +128,10 @@ static int syslog_priority(int level) int wpa_debug_open_linux_tracing(void) { int mounts, trace_fd; + char path[128] = {}; char buf[4096] = {}; ssize_t buflen; - char *line, *tmp1, *path = NULL; + char *line, *tmp1; mounts = open("/proc/mounts", O_RDONLY); if (mounts < 0) { @@ -146,29 +147,42 @@ int wpa_debug_open_linux_tracing(void) } buf[buflen] = '\0'; - line = strtok_r(buf, "\n", &tmp1); - while (line) { + for (line = strtok_r(buf, "\n", &tmp1); + line; + line = strtok_r(NULL, "\n", &tmp1)) { char *tmp2, *tmp_path, *fstype; /* " ..." */ strtok_r(line, " ", &tmp2); tmp_path = strtok_r(NULL, " ", &tmp2); fstype = strtok_r(NULL, " ", &tmp2); - if (fstype && strcmp(fstype, "debugfs") == 0) { - path = tmp_path; - break; + + if (!line[0] || !tmp_path || !fstype) + continue; + + if (os_strcmp(fstype, "debugfs") == 0) { + os_snprintf(path, sizeof(path) - 1, + "%s/tracing/trace_marker", + tmp_path); + /* + * Don't break to prefer tracefs, which if present may + * mean debugfs doesn't have tracing/ (depending on the + * kernel version). + */ } - line = strtok_r(NULL, "\n", &tmp1); + if (os_strcmp(fstype, "tracefs") == 0) { + os_snprintf(path, sizeof(path) - 1, "%s/trace_marker", + tmp_path); + break; + } } - if (path == NULL) { - printf("debugfs mountpoint not found\n"); + if (!path[0]) { + printf("tracefs/debugfs not found\n"); return -1; } - snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); - - trace_fd = open(buf, O_WRONLY); + trace_fd = open(path, O_WRONLY); if (trace_fd < 0) { printf("failed to open trace_marker file\n"); return -1; diff --git a/contrib/wpa/src/utils/wpa_debug.h b/contrib/wpa/src/utils/wpa_debug.h index 4c02ad3c775d..77f132264206 100644 --- a/contrib/wpa/src/utils/wpa_debug.h +++ b/contrib/wpa/src/utils/wpa_debug.h @@ -27,17 +27,47 @@ enum { #define wpa_debug_print_timestamp() do { } while (0) #define wpa_printf(args...) do { } while (0) -#define wpa_hexdump(l,t,b,le) do { } while (0) -#define wpa_hexdump_buf(l,t,b) do { } while (0) -#define wpa_hexdump_key(l,t,b,le) do { } while (0) -#define wpa_hexdump_buf_key(l,t,b) do { } while (0) -#define wpa_hexdump_ascii(l,t,b,le) do { } while (0) -#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) #define wpa_debug_open_file(p) do { } while (0) #define wpa_debug_close_file() do { } while (0) #define wpa_debug_setup_stdout() do { } while (0) +#define wpa_debug_stop_log() do { } while (0) #define wpa_dbg(args...) do { } while (0) +static inline void wpa_hexdump(int level, const char *title, + const void *buf, size_t len) +{ +} + +static inline void wpa_hexdump_buf(int level, const char *title, + const struct wpabuf *buf) +{ +} + +static inline void wpa_hexdump_key(int level, const char *title, + const void *buf, size_t len) +{ +} + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ +} + +static inline void wpa_hexdump_ascii(int level, const char *title, + const void *buf, size_t len) +{ +} + +static inline void wpa_hexdump_ascii_buf(int level, const char *title, + const struct wpabuf *buf) +{ +} + +static inline void wpa_hexdump_ascii_key(int level, const char *title, + const void *buf, size_t len) +{ +} + static inline int wpa_debug_reopen_file(void) { return 0; @@ -132,6 +162,13 @@ static inline void wpa_hexdump_buf_key(int level, const char *title, void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); +static inline void wpa_hexdump_ascii_buf(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_ascii(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); +} + /** * wpa_hexdump_ascii_key - conditional hex dump, hide keys * @level: priority level (MSG_*) of the message diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c index 77ee47288007..df94fe68aa4f 100644 --- a/contrib/wpa/src/utils/wpabuf.c +++ b/contrib/wpa/src/utils/wpabuf.c @@ -168,7 +168,11 @@ struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len) struct wpabuf * wpabuf_dup(const struct wpabuf *src) { - struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src)); + struct wpabuf *buf; + + if (!src) + return NULL; + buf = wpabuf_alloc(wpabuf_len(src)); if (buf) wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); return buf; @@ -338,3 +342,54 @@ struct wpabuf * wpabuf_parse_bin(const char *buf) return ret; } + + +struct wpabuf_array * wpabuf_array_alloc(void) +{ + struct wpabuf_array *wa; + + wa = os_zalloc(sizeof(*wa)); + return wa; +} + + +void wpabuf_array_free(struct wpabuf_array *wa) +{ + unsigned int idx; + + if (!wa) + return; + for (idx = 0; idx < wa->num; idx++) + wpabuf_free(wa->buf[idx]); + os_free(wa->buf); + os_free(wa); +} + + +int wpabuf_array_add(struct wpabuf_array *wa, struct wpabuf *buf) +{ + struct wpabuf **n; + + if (!wa || !buf) + return -1; + n = os_realloc(wa->buf, (wa->num + 1) * sizeof(struct wpabuf *)); + if (!n) + return -1; + wa->buf = n; + wa->buf[wa->num++] = buf; + return 0; +} + + +void wpabuf_array_remove(struct wpabuf_array *wa, unsigned int idx) +{ + if (!wa || wa->num == 0 || idx >= wa->num) + return; + wpabuf_free(wa->buf[idx]); + while (idx + 1 < wa->num) { + wa->buf[idx] = wa->buf[idx + 1]; + idx++; + } + wa->num--; + wa->buf[wa->num] = NULL; +} diff --git a/contrib/wpa/src/utils/wpabuf.h b/contrib/wpa/src/utils/wpabuf.h index 88d72bd685e5..284abbf54156 100644 --- a/contrib/wpa/src/utils/wpabuf.h +++ b/contrib/wpa/src/utils/wpabuf.h @@ -194,4 +194,15 @@ static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) wpabuf_put_data(dst, str, os_strlen(str)); } + +struct wpabuf_array { + unsigned int num; + struct wpabuf **buf; +}; + +struct wpabuf_array * wpabuf_array_alloc(void); +void wpabuf_array_free(struct wpabuf_array *wa); +int wpabuf_array_add(struct wpabuf_array *wa, struct wpabuf *buf); +void wpabuf_array_remove(struct wpabuf_array *wa, unsigned int idx); + #endif /* WPABUF_H */ diff --git a/contrib/wpa/src/utils/xml-utils.c b/contrib/wpa/src/utils/xml-utils.c index dae91fee46f6..52803827e68e 100644 --- a/contrib/wpa/src/utils/xml-utils.c +++ b/contrib/wpa/src/utils/xml-utils.c @@ -438,34 +438,3 @@ xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds) return NULL; return tnds_to_mo_iter(ctx, NULL, node, NULL); } - - -xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node) -{ - xml_node_t *envelope, *body; - xml_namespace_t *ns; - - envelope = xml_node_create_root( - ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns, - "Envelope"); - if (envelope == NULL) - return NULL; - body = xml_node_create(ctx, envelope, ns, "Body"); - xml_node_add_child(ctx, body, node); - return envelope; -} - - -xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap) -{ - xml_node_t *body, *child; - - body = get_node_uri(ctx, soap, "Envelope/Body"); - if (body == NULL) - return NULL; - xml_node_for_each_child(ctx, child, body) { - xml_node_for_each_check(ctx, child); - return child; - } - return NULL; -} diff --git a/contrib/wpa/src/utils/xml-utils.h b/contrib/wpa/src/utils/xml-utils.h index fb6208cdac32..eb83bd45ac49 100644 --- a/contrib/wpa/src/utils/xml-utils.h +++ b/contrib/wpa/src/utils/xml-utils.h @@ -15,19 +15,11 @@ typedef struct xml_namespace_foo xml_namespace_t; /* XML library wrappers */ -int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node, - const char *xml_schema_fname, char **ret_err); -int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node, - const char *dtd_fname, char **ret_err); void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node); -xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node); xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf); const char * xml_node_get_localname(struct xml_node_ctx *ctx, xml_node_t *node); char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node); -void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node); -void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent, - xml_node_t *child); xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri, const char *ns_prefix, xml_namespace_t **ret_ns, const char *name); @@ -41,13 +33,6 @@ xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx, const char *name, const char *value); void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node, const char *value); -int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node, - xml_namespace_t *ns, const char *name, const char *value); -char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, - char *name); -char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node, - const char *ns_uri, char *name); -void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val); xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx, xml_node_t *parent); xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx, @@ -57,7 +42,6 @@ char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node); void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val); char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node, int *ret_len); -xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node); #define xml_node_for_each_child(ctx, child, parent) \ for (child = xml_node_first_child(ctx, parent); \ @@ -91,7 +75,4 @@ xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo, int use_path, const char *urn, const char *ns_uri); xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds); -xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node); -xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap); - #endif /* XML_UTILS_H */ diff --git a/contrib/wpa/src/utils/xml_libxml2.c b/contrib/wpa/src/utils/xml_libxml2.c index d73654eb522d..0aa71d563fb6 100644 --- a/contrib/wpa/src/utils/xml_libxml2.c +++ b/contrib/wpa/src/utils/xml_libxml2.c @@ -21,161 +21,12 @@ struct xml_node_ctx { }; -struct str_buf { - char *buf; - size_t len; -}; - -#define MAX_STR 1000 - -static void add_str(void *ctx_ptr, const char *fmt, ...) -{ - struct str_buf *str = ctx_ptr; - va_list ap; - char *n; - int len; - - n = os_realloc(str->buf, str->len + MAX_STR + 2); - if (n == NULL) - return; - str->buf = n; - - va_start(ap, fmt); - len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap); - va_end(ap); - if (len >= MAX_STR) - len = MAX_STR - 1; - str->len += len; - str->buf[str->len] = '\0'; -} - - -int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node, - const char *xml_schema_fname, char **ret_err) -{ - xmlDocPtr doc; - xmlNodePtr n; - xmlSchemaParserCtxtPtr pctx; - xmlSchemaValidCtxtPtr vctx; - xmlSchemaPtr schema; - int ret; - struct str_buf errors; - - if (ret_err) - *ret_err = NULL; - - doc = xmlNewDoc((xmlChar *) "1.0"); - if (doc == NULL) - return -1; - n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); - if (n == NULL) { - xmlFreeDoc(doc); - return -1; - } - xmlDocSetRootElement(doc, n); - - os_memset(&errors, 0, sizeof(errors)); - - pctx = xmlSchemaNewParserCtxt(xml_schema_fname); - xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str, - (xmlSchemaValidityWarningFunc) add_str, - &errors); - schema = xmlSchemaParse(pctx); - xmlSchemaFreeParserCtxt(pctx); - - vctx = xmlSchemaNewValidCtxt(schema); - xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str, - (xmlSchemaValidityWarningFunc) add_str, - &errors); - - ret = xmlSchemaValidateDoc(vctx, doc); - xmlSchemaFreeValidCtxt(vctx); - xmlFreeDoc(doc); - xmlSchemaFree(schema); - - if (ret == 0) { - os_free(errors.buf); - return 0; - } else if (ret > 0) { - if (ret_err) - *ret_err = errors.buf; - else - os_free(errors.buf); - return -1; - } else { - if (ret_err) - *ret_err = errors.buf; - else - os_free(errors.buf); - return -1; - } -} - - -int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node, - const char *dtd_fname, char **ret_err) -{ - xmlDocPtr doc; - xmlNodePtr n; - xmlValidCtxt vctx; - xmlDtdPtr dtd; - int ret; - struct str_buf errors; - - if (ret_err) - *ret_err = NULL; - - doc = xmlNewDoc((xmlChar *) "1.0"); - if (doc == NULL) - return -1; - n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); - if (n == NULL) { - xmlFreeDoc(doc); - return -1; - } - xmlDocSetRootElement(doc, n); - - os_memset(&errors, 0, sizeof(errors)); - - dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname); - if (dtd == NULL) { - xmlFreeDoc(doc); - return -1; - } - - os_memset(&vctx, 0, sizeof(vctx)); - vctx.userData = &errors; - vctx.error = add_str; - vctx.warning = add_str; - ret = xmlValidateDtd(&vctx, doc, dtd); - xmlFreeDoc(doc); - xmlFreeDtd(dtd); - - if (ret == 1) { - os_free(errors.buf); - return 0; - } else { - if (ret_err) - *ret_err = errors.buf; - else - os_free(errors.buf); - return -1; - } -} - - void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node) { xmlFreeNode((xmlNodePtr) node); } -xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node) -{ - return (xml_node_t *) ((xmlNodePtr) node)->parent; -} - - xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf) { xmlDocPtr doc; @@ -242,19 +93,6 @@ char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node) } -void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node) -{ - xmlUnlinkNode((xmlNodePtr) node); -} - - -void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent, - xml_node_t *child) -{ - xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child); -} - - xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri, const char *ns_prefix, xml_namespace_t **ret_ns, const char *name) @@ -322,47 +160,6 @@ void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node, } -int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node, - xml_namespace_t *ns, const char *name, const char *value) -{ - xmlAttrPtr attr; - - if (ns) { - attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns, - (const xmlChar *) name, - (const xmlChar *) value); - } else { - attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name, - (const xmlChar *) value); - } - - return attr ? 0 : -1; -} - - -char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, - char *name) -{ - return (char *) xmlGetNoNsProp((xmlNodePtr) node, - (const xmlChar *) name); -} - - -char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node, - const char *ns_uri, char *name) -{ - return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name, - (const xmlChar *) ns_uri); -} - - -void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val) -{ - if (val) - xmlFree((xmlChar *) val); -} - - xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx, xml_node_t *parent) { @@ -422,18 +219,11 @@ char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node, } os_memcpy(txt, ret, len); txt[len] = '\0'; + os_free(ret); return txt; } -xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node) -{ - if (node == NULL) - return NULL; - return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1); -} - - struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx, const void *env) { diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c index 507abe870780..59e1caf5a308 100644 --- a/contrib/wpa/src/wps/http_server.c +++ b/contrib/wpa/src/wps/http_server.c @@ -65,12 +65,15 @@ static struct http_request * http_request_init(struct http_server *srv, int fd, if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) { wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests"); + close(fd); return NULL; } req = os_zalloc(sizeof(*req)); - if (req == NULL) + if (!req) { + close(fd); return NULL; + } req->srv = srv; req->fd = fd; @@ -214,10 +217,8 @@ static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx) inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); req = http_request_init(srv, conn, &addr); - if (req == NULL) { - close(conn); + if (!req) return; - } req->next = srv->requests; srv->requests = req; diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c index dede64b0136a..92cf20742d68 100644 --- a/contrib/wpa/src/wps/wps_er.c +++ b/contrib/wpa/src/wps/wps_er.c @@ -1195,11 +1195,14 @@ static void wps_er_http_notify(struct wps_er *er, struct http_request *req) if (event_id != er->event_id) { wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an " "unknown event id %u", event_id); + http_request_deinit(req); return; } pos = os_strchr(uri + 7, '/'); - if (pos == NULL) + if (!pos) { + http_request_deinit(req); return; + } pos++; wps_er_http_event(er, req, atoi(pos)); } else { diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c index b6c9478ffe9b..573eb592ff48 100644 --- a/contrib/wpa/src/wps/wps_upnp_ap.c +++ b/contrib/wpa/src/wps/wps_upnp_ap.c @@ -51,7 +51,7 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, s->dev_password_id = attr.dev_password_id ? WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; s->config_methods = attr.sel_reg_config_methods ? - WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + WPA_GET_BE16(attr.sel_reg_config_methods) : 0xffff; if (attr.authorized_macs) { int count = attr.authorized_macs_len / ETH_ALEN; if (count > WPS_MAX_AUTHORIZED_MACS) diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk index 3aadcb2bb921..cbb839780b74 100644 --- a/contrib/wpa/wpa_supplicant/Android.mk +++ b/contrib/wpa/wpa_supplicant/Android.mk @@ -138,7 +138,7 @@ OBJS_c += src/utils/trace.c LDFLAGS += -rdynamic L_CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD -L_CFLAGS += -DWPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls LIBS += -lbfd LIBS_p += -lbfd LIBS_c += -lbfd @@ -242,9 +242,9 @@ L_CFLAGS += -DCONFIG_SAE OBJS += src/common/sae.c ifdef CONFIG_SAE_PK L_CFLAGS += -DCONFIG_SAE_PK -NEED_AES_SIV=y OBJS += src/common/sae_pk.c endif +NEED_AES_SIV=y NEED_ECC=y NEED_DH_GROUPS=y NEED_HMAC_SHA256_KDF=y @@ -283,12 +283,38 @@ L_CFLAGS += -DCONFIG_DPP3 endif endif +ifdef CONFIG_NAN +NEED_NAN=y +L_CFLAGS += -DCONFIG_NAN +endif + ifdef CONFIG_NAN_USD -OBJS += src/common/nan_de.c -OBJS += nan_usd.c +NEED_OFFCHANNEL=y +NEED_NAN=y L_CFLAGS += -DCONFIG_NAN_USD endif +ifdef CONFIG_PR +OBJS += src/common/proximity_ranging.c +OBJS += pr_supplicant.c +L_CFLAGS += -DCONFIG_PR +endif + +ifdef NEED_NAN +OBJS += nan_supplicant.c +OBJS += src/nan/nan.c +OBJS += src/common/nan_de.c +OBJS += src/nan/nan_util.c +OBJS += src/nan/nan_ndp.c +OBJS += src/nan/nan_ndl.c +OBJS += src/nan/nan_crypto.c +OBJS += src/nan/nan_sec.c +OBJS += src/nan/nan_bootstrap.c +ifdef CONFIG_PASN +OBJS += src/nan/nan_pairing.c +endif +endif + ifdef CONFIG_OWE L_CFLAGS += -DCONFIG_OWE NEED_ECC=y @@ -383,6 +409,21 @@ OBJS += wifi_display.c endif endif +ifdef CONFIG_PMKSA_PRIVACY +CONFIG_ENC_ASSOC=y +L_CFLAGS += -DCONFIG_PMKSA_PRIVACY +endif + +ifdef CONFIG_IEEE8021X_AUTH +CONFIG_ENC_ASSOC=y +L_CFLAGS += -DCONFIG_IEEE8021X_AUTH +endif + +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +L_CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_PASN L_CFLAGS += -DCONFIG_PASN L_CFLAGS += -DCONFIG_PTKSA_CACHE @@ -390,6 +431,7 @@ NEED_HMAC_SHA256_KDF=y NEED_HMAC_SHA384_KDF=y NEED_SHA256=y NEED_SHA384=y +NEED_DRAGONFLY=y OBJS += src/pasn/pasn_initiator.c OBJS += src/pasn/pasn_common.c OBJS += pasn_supplicant.c @@ -716,7 +758,7 @@ EAPDYN += src/eap_peer/eap_teap.so EAPDYN += src/eap_common/eap_teap_common.c else L_CFLAGS += -DEAP_TEAP -OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c +OBJS += src/eap_peer/eap_teap.c OBJS += src/eap_common/eap_teap_common.c endif TLS_FUNCS=y @@ -772,8 +814,10 @@ OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c CONFIG_IEEE8021X_EAPOL=y NEED_ECC=y NEED_DRAGONFLY=y +ifndef CONFIG_FIPS MS_FUNCS=y endif +endif ifdef CONFIG_EAP_EKE # EAP-EKE @@ -983,6 +1027,7 @@ OBJS += src/pasn/pasn_responder.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c OBJS += src/ap/dfs.c +OBJS += src/ap/interference.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -1711,6 +1756,11 @@ OBJS += src/utils/json.c L_CFLAGS += -DCONFIG_JSON endif +ifdef CONFIG_PROCESS_COORDINATION +L_CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += src/common/proc_coord.c +endif + OBJS += src/drivers/driver_common.c OBJS += wpa_supplicant.c events.c bssid_ignore.c wpas_glue.c scan.c diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index 3f4162eb0b86..3918da133c9c 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,37 @@ ChangeLog for wpa_supplicant +2026-08-07 - v2.12 + * support RSN overriding (e.g., WPA3-Personal Compatibility Mode) + * improve BSS transition management support + * EHT/IEEE 802.11be/Wi-Fi 7 + - more complete support + - fix message validation issues that could enable DoS attacks + * support Wi-Fi Direct R2 + * support Wi-Fi Aware (add synchronized NAN; extend USD support) + * support Proximity Ranging + * support SPP A-MSDU negotiation + * support IEEE 802.11bi functionality + - changing SAE password identifiers + - EPPKE + - IEEE 802.1X/EAP in Authentication frames + - Association frame encryption + - PMKID privacy + * enable layer 2/Wi-Fi multicast filtering for all networks (not + just some Passpoint networks which enabled this before) + * wpa_gui: port to Qt6 + * support OpenSSL 3.0 API changes + * EAP-TEAP: protocol changes based on RFC 9930; this is not compatible + with previous versions + * maintain configuration file permissions when writing updated + configuration + * add option to validate PKCS#11/OpenSC engine and module paths + * fix PMKSA caching to enforce network context to avoid misuse of + unexpected PMKSA cache entries + * fix a potential DoS attack in SAE processing of an unexpected element + * fix incomplete bounds checking of mesh AMPE messages that could have + resulted in DoS attacks and memory corruption + * a large number of other fixes, cleanup, and extensions + 2024-07-20 - v2.11 * Wi-Fi Easy Connect - add support for DPP release 3 diff --git a/contrib/wpa/wpa_supplicant/Makefile b/contrib/wpa/wpa_supplicant/Makefile index 743c8acd683f..67f337df33da 100644 --- a/contrib/wpa/wpa_supplicant/Makefile +++ b/contrib/wpa/wpa_supplicant/Makefile @@ -72,7 +72,7 @@ mkconfig: echo '.config exists - did not replace it'; \ exit 1; \ fi - echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_NL80211=y >> .config echo CONFIG_DRIVER_WEXT=y >> .config $(DESTDIR)$(BINDIR)/%: % @@ -151,7 +151,7 @@ LIBCTRLSO += ../src/utils/trace.c LDFLAGS += -rdynamic CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD -CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD +CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD -fno-inline -fno-optimize-sibling-calls LIBS += -lbfd -ldl -liberty -lz LIBS_p += -lbfd -ldl -liberty -lz LIBS_c += -lbfd -ldl -liberty -lz @@ -276,9 +276,9 @@ CFLAGS += -DCONFIG_SAE OBJS += ../src/common/sae.o ifdef CONFIG_SAE_PK CFLAGS += -DCONFIG_SAE_PK -NEED_AES_SIV=y OBJS += ../src/common/sae_pk.o endif +NEED_AES_SIV=y NEED_ECC=y NEED_DH_GROUPS=y NEED_HMAC_SHA256_KDF=y @@ -317,12 +317,38 @@ CFLAGS += -DCONFIG_DPP3 endif endif +ifdef CONFIG_NAN +NEED_NAN=y +CFLAGS += -DCONFIG_NAN +endif + ifdef CONFIG_NAN_USD -OBJS += ../src/common/nan_de.o -OBJS += nan_usd.o +NEED_OFFCHANNEL=y +NEED_NAN=y CFLAGS += -DCONFIG_NAN_USD endif +ifdef NEED_NAN +OBJS += nan_supplicant.o +OBJS += ../src/nan/nan.o +OBJS += ../src/common/nan_de.o +OBJS += ../src/nan/nan_util.o +OBJS += ../src/nan/nan_ndp.o +OBJS += ../src/nan/nan_ndl.o +OBJS += ../src/nan/nan_crypto.o +OBJS += ../src/nan/nan_sec.o +OBJS += ../src/nan/nan_bootstrap.o +ifdef CONFIG_PASN +OBJS += ../src/nan/nan_pairing.o +endif +endif + +ifdef CONFIG_PR +OBJS += ../src/common/proximity_ranging.o +OBJS += pr_supplicant.o +CFLAGS += -DCONFIG_PR +endif + ifdef CONFIG_OWE CFLAGS += -DCONFIG_OWE NEED_ECC=y @@ -425,6 +451,21 @@ OBJS += wifi_display.o endif endif +ifdef CONFIG_PMKSA_PRIVACY +CONFIG_ENC_ASSOC=y +CFLAGS += -DCONFIG_PMKSA_PRIVACY +endif + +ifdef CONFIG_IEEE8021X_AUTH +CONFIG_ENC_ASSOC=y +CFLAGS += -DCONFIG_IEEE8021X_AUTH +endif + +ifdef CONFIG_ENC_ASSOC +CONFIG_PASN=y +CFLAGS += -DCONFIG_ENC_ASSOC +endif + ifdef CONFIG_PASN CFLAGS += -DCONFIG_PASN CFLAGS += -DCONFIG_PTKSA_CACHE @@ -432,6 +473,7 @@ NEED_HMAC_SHA256_KDF=y NEED_HMAC_SHA384_KDF=y NEED_SHA256=y NEED_SHA384=y +NEED_DRAGONFLY=y OBJS += ../src/pasn/pasn_initiator.o OBJS += ../src/pasn/pasn_common.o OBJS += pasn_supplicant.o @@ -759,7 +801,7 @@ endif ifdef CONFIG_EAP_TEAP # EAP-TEAP -SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c ../src/eap_peer/eap_teap_pac.c +SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c SRC_EAP_TEAP += ../src/eap_common/eap_teap_common.c ifeq ($(CONFIG_EAP_TEAP), dyn) CFLAGS += -DEAP_TEAP_DYNAMIC @@ -827,8 +869,10 @@ OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o CONFIG_IEEE8021X_EAPOL=y NEED_ECC=y NEED_DRAGONFLY=y +ifndef CONFIG_FIPS MS_FUNCS=y endif +endif ifdef CONFIG_EAP_EKE # EAP-EKE @@ -1057,6 +1101,7 @@ OBJS += ../src/pasn/pasn_responder.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o OBJS += ../src/ap/dfs.o +OBJS += ../src/ap/interference.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -1189,6 +1234,7 @@ TLS_FUNCS=y endif ifeq ($(CONFIG_TLS), wolfssl) +CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ifdef TLS_FUNCS CFLAGS += -DWOLFSSL_DER_LOAD OBJS += ../src/crypto/tls_wolfssl.o @@ -1908,6 +1954,11 @@ OBJS += ../src/utils/json.o CFLAGS += -DCONFIG_JSON endif +ifdef CONFIG_PROCESS_COORDINATION +CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += ../src/common/proc_coord.o +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += wpas_module_tests.o @@ -1917,6 +1968,10 @@ OBJS += ../src/crypto/crypto_module_tests.o ifdef CONFIG_WPS OBJS += ../src/wps/wps_module_tests.o endif +ifdef CONFIG_NAN +OBJS += ../src/nan/nan_module_tests.o +OBJS += ../src/nan/nan_module_test_cases.o +endif endif OBJS += ../src/drivers/driver_common.o @@ -2184,6 +2239,10 @@ LIBPASNSO += ../src/utils/os_$(CONFIG_OS).c LIBPASNSO += ../src/utils/config.c LIBPASNSO += ../src/utils/common.c +ifndef CONFIG_NO_RANDOM_POOL +LIBPASNSO += ../src/crypto/random.c +endif + ifdef NEED_BASE64 LIBPASNSO += ../src/utils/base64.c endif @@ -2251,7 +2310,7 @@ LIBPASNSO += ../src/crypto/crypto_wolfssl.c ifdef TLS_FUNCS LIBPASNSO += ../src/crypto/tls_wolfssl.c NEED_TLS_PRF_SHA256=y -LIBPASNSO += -lwolfssl -lm +LIBPASNSO_DEP += -lwolfssl -lm endif endif @@ -2259,7 +2318,7 @@ ifeq ($(CONFIG_TLS), openssl) LIBPASNSO += ../src/crypto/crypto_openssl.c ifdef TLS_FUNCS LIBPASNSO += ../src/crypto/tls_openssl.c -LIBPASNSO += -lssl -lcrypto +LIBPASNSO_DEP += -lssl -lcrypto NEED_TLS_PRF_SHA256=y endif endif @@ -2268,8 +2327,8 @@ ifeq ($(CONFIG_TLS), gnutls) LIBPASNSO += ../src/crypto/crypto_$(CONFIG_CRYPTO).c ifdef TLS_FUNCS LIBPASNSO += ../src/crypto/tls_gnutls.c -LIBPASNSO += -lgnutls -lgpg-error -LIBPASNSO += -lgcrypt +LIBPASNSO_DEP += -lgnutls -lgpg-error +LIBPASNSO_DEP += -lgcrypt endif endif @@ -2324,7 +2383,7 @@ LIBPASNSO += ../src/pasn/pasn_common.c libpasn.so: $(LIBPASNSO) @$(E) " CC $@ ($^)" - $(Q)$(CC) $(LDFLAGS) -o $@ $(PASN_CFLAGS) -shared -fPIC -lcrypto $^ + $(Q)$(CC) $(LDFLAGS) -o $@ $(PASN_CFLAGS) -shared -fPIC $^ -lcrypto $(LIBPASNSO_DEP) clean: common-clean $(MAKE) -C ../src clean diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README index 49e971e218b4..f7f3680175e8 100644 --- a/contrib/wpa/wpa_supplicant/README +++ b/contrib/wpa/wpa_supplicant/README @@ -1,7 +1,7 @@ wpa_supplicant ============== -Copyright (c) 2003-2024, Jouni Malinen and contributors +Copyright (c) 2003-2026, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -143,7 +143,7 @@ Current hardware/software requirements: wpa_supplicant was designed to be portable for different drivers and operating systems. Hopefully, support for more wlan cards and OSes will be added in the future. See developer's documentation -(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the +(https://w1.fi/wpa_supplicant/devel/) for more information about the design of wpa_supplicant and porting to other drivers. One main goal is to add full WPA/WPA2 support to Linux wireless extensions to allow new drivers to be supported without having to implement new diff --git a/contrib/wpa/wpa_supplicant/README-HS20 b/contrib/wpa/wpa_supplicant/README-HS20 index 7d30e23c6d66..f5488f204102 100644 --- a/contrib/wpa/wpa_supplicant/README-HS20 +++ b/contrib/wpa/wpa_supplicant/README-HS20 @@ -618,28 +618,6 @@ OK <3>ANQP fetch completed -Hotspot 2.0 Rel 2 online signup and OSEN ----------------------------------------- - -Following parameters can be used to create a network profile for -link-layer protected Hotspot 2.0 online signup connection with -OSEN. Note that ssid and identify (NAI) values need to be set based on -the information for the selected provider in the OSU Providers list -ANQP-element. - -network={ - ssid="HS 2.0 OSU" - proto=OSEN - key_mgmt=OSEN - pairwise=CCMP - group=GTK_NOT_USED - eap=WFA-UNAUTH-TLS - identity="anonymous@example.com" - ca_cert="osu-ca.pem" - ocsp=2 -} - - Hotspot 2.0 connection with external network selection ------------------------------------------------------ diff --git a/contrib/wpa/wpa_supplicant/README-NAN-USD b/contrib/wpa/wpa_supplicant/README-NAN-USD index 72c379fc976a..b006819b25dd 100644 --- a/contrib/wpa/wpa_supplicant/README-NAN-USD +++ b/contrib/wpa/wpa_supplicant/README-NAN-USD @@ -56,6 +56,14 @@ NAN_UPDATE_PUBLISH publish_id= [ssi= + +This command is an implementation specific mechanism to allow a service +to stop driver listen operation during an ongoing SDF exchange, e.g., to +allow optimized radio use for parallel operations during provisioning +sequence when no SDF is expected to be received from the peer during the +other operations. NAN_TRANSMIT can used to re-enable listen operations. + NAN_SUBSCRIBE service_name= [active=1] [ttl=] [freq=] [srv_proto_type=] [ssi=] If ttl=0 or the parameter is not included, operation is terminated once @@ -75,6 +83,14 @@ NAN_CANCEL_SUBSCRIBE subscribe_id= This command maps to the CancelSubscribe() method in the NAN Discovery Engine. +NAN_SUBSCRIBE_STOP_LISTEN subscribe_id= + +This command is an implementation specific mechanism to allow a service +to stop driver listen operation during an ongoing SDF exchange, e.g., to +allow optimized radio use for parallel operations during provisioning +sequence when no SDF is expected to be received from the peer during the +other operations. NAN_TRANSMIT can used to re-enable listen operations. + NAN_TRANSMIT handle= req_instance_id= address= [ssi=] This command maps to the Transmit() method in the NAN Discovery Engine. diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 69a0e5ee16af..d0a1a96fd5b4 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -328,6 +328,9 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, ssid->he = 0; #endif /* CONFIG_HE_OVERRIDES */ + if (ssid->disable_eht) + ssid->eht = 0; + if (!ssid->ht) { wpa_printf(MSG_DEBUG, "HT not enabled in network profile"); @@ -521,9 +524,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, list[0] = 60; list[1] = 120; list[2] = 240; - list[3] = -1; + list[3] = 0; } - conf->basic_rates = list; + bss->basic_rates = list; list = os_malloc(9 * sizeof(int)); if (list) { @@ -535,17 +538,11 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, list[5] = 360; list[6] = 480; list[7] = 540; - list[8] = -1; + list[8] = 0; } - conf->supported_rates = list; + bss->supported_rates = list; } -#ifdef CONFIG_IEEE80211AX - if (ssid->mode == WPAS_MODE_P2P_GO || - ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) - conf->ieee80211ax = ssid->he; -#endif /* CONFIG_IEEE80211AX */ - bss->isolate = !wpa_s->conf->p2p_intra_bss; bss->extended_key_id = wpa_s->conf->extended_key_id; bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; @@ -582,6 +579,19 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, else bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; + +#ifdef CONFIG_P2P + if (ssid->p2p_mode == WPA_P2P_MODE_WFD_PCC) { + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + bss->rsn_override_key_mgmt = WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_PASN; + bss->wpa_pairwise = WPA_CIPHER_CCMP; + bss->rsn_override_pairwise = WPA_CIPHER_CCMP; + bss->rsn_override_mfp = 2; + bss->rsn_override_omit_rsnxe = 1; + } +#endif /* CONFIG_P2P */ + if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) { bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); } else if (ssid->psk_set) { @@ -638,10 +648,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->sae_passwords = pw; } - if (ssid->sae_pwe != DEFAULT_SAE_PWE) - bss->sae_pwe = ssid->sae_pwe; - else - bss->sae_pwe = wpa_s->conf->sae_pwe; + bss->sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); #endif /* CONFIG_SAE */ if (wpa_s->conf->go_interworking) { @@ -1036,8 +1043,10 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO || - ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { params.p2p = 1; + params.p2p_mode = wpa_s->p2p_mode; + } #endif /* CONFIG_P2P */ if (wpa_s->p2pdev->set_ap_uapsd) @@ -1072,6 +1081,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask; hapd_iface->extended_capa_len = wpa_s->extended_capa_len; hapd_iface->drv_max_acl_mac_addrs = wpa_s->drv_max_acl_mac_addrs; + hapd_iface->assisted_dfs = wpa_s->assisted_dfs; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { @@ -1150,6 +1160,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, wpa_s->ext_eapol_frame_io; #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_NAN_USD + /* + * Copy NAN DE pointer from wpa_supplicant to hostapd for AP + * to continue processing NAN USD frames. + */ + hapd_iface->bss[i]->nan_de = wpa_s->nan_de; +#endif /* CONFIG_NAN_USD */ + #ifdef CONFIG_WNM_AP if (ssid->mode == WPAS_MODE_AP) hapd_iface->bss[i]->conf->bss_transition = 1; @@ -1838,6 +1856,8 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, if (!iface || !iface->bss[0]) return -1; + hostapd_chan_switch_config(iface->bss[0], &settings->freq_params); + return hostapd_switch_channel(iface->bss[0], settings); #else /* NEED_AP_MLME */ return -1; @@ -1849,8 +1869,22 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) { struct csa_settings settings; - int ret = hostapd_parse_csa_settings(pos, &settings); + struct hostapd_iface *iface = NULL; + int ret; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + iface = wpa_s->ap_iface; +#endif /* CONFIG_AP */ +#ifdef CONFIG_MESH + if (!iface && wpa_s->ifmsh) + iface = wpa_s->ifmsh; +#endif /* CONFIG_MESH */ + + if (!iface) + return -1; + + ret = hostapd_parse_csa_settings(iface, pos, &settings); if (ret) return ret; @@ -2122,7 +2156,7 @@ void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s, if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq); - hostapd_dfs_start_cac(iface, radar->freq, + hostapd_dfs_start_cac(iface->bss[0], radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } diff --git a/contrib/wpa/wpa_supplicant/bgscan_learn.c b/contrib/wpa/wpa_supplicant/bgscan_learn.c index cab4ae2ac77c..bc9f324054ab 100644 --- a/contrib/wpa/wpa_supplicant/bgscan_learn.c +++ b/contrib/wpa/wpa_supplicant/bgscan_learn.c @@ -280,6 +280,11 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) params.num_ssids = 1; params.ssids[0].ssid = data->ssid->ssid; params.ssids[0].ssid_len = data->ssid->ssid_len; + + /* Add OWE transition mode SSID of the current network */ + wpa_add_owe_scan_ssid(wpa_s, ¶ms, data->ssid, + wpa_s->max_scan_ssids - params.num_ssids); + if (data->ssid->scan_freq) params.freqs = data->ssid->scan_freq; else { diff --git a/contrib/wpa/wpa_supplicant/bgscan_simple.c b/contrib/wpa/wpa_supplicant/bgscan_simple.c index a90cf86e5eac..d9aaa634f018 100644 --- a/contrib/wpa/wpa_supplicant/bgscan_simple.c +++ b/contrib/wpa/wpa_supplicant/bgscan_simple.c @@ -89,6 +89,10 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) params.ssids[0].ssid_len = data->ssid->ssid_len; params.freqs = data->ssid->scan_freq; + /* Add OWE transition mode SSID of the current network */ + wpa_add_owe_scan_ssid(wpa_s, ¶ms, data->ssid, + wpa_s->max_scan_ssids - params.num_ssids); + /* * A more advanced bgscan module would learn about most like channels * over time and request scans only for some channels (probing others diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index e8aaf6fe1848..c594f2523da8 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -91,9 +91,6 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) ANQP_DUP(hs20_wan_metrics); ANQP_DUP(hs20_connection_capability); ANQP_DUP(hs20_operating_class); - ANQP_DUP(hs20_osu_providers_list); - ANQP_DUP(hs20_operator_icon_metadata); - ANQP_DUP(hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ #undef ANQP_DUP @@ -176,9 +173,6 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->hs20_wan_metrics); wpabuf_free(anqp->hs20_connection_capability); wpabuf_free(anqp->hs20_operating_class); - wpabuf_free(anqp->hs20_osu_providers_list); - wpabuf_free(anqp->hs20_operator_icon_metadata); - wpabuf_free(anqp->hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ os_free(anqp); @@ -220,6 +214,7 @@ void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const char *reason) { struct wpa_connect_work *cwork; + unsigned int j; if (wpa_s->last_scan_res) { unsigned int i; @@ -245,6 +240,45 @@ void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, wpa_ssid_txt(bss->ssid, bss->ssid_len), reason); wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); wpa_bss_anqp_free(bss->anqp); + + if (wpa_s->current_bss == bss) { + wpa_printf(MSG_DEBUG, + "BSS: Clear current_bss due to bss removal"); + wpa_s->current_bss = NULL; + } + +#ifdef CONFIG_INTERWORKING + if (wpa_s->interworking_gas_bss == bss) { + wpa_printf(MSG_DEBUG, + "BSS: Clear interworking_gas_bss due to bss removal"); + wpa_s->interworking_gas_bss = NULL; + } +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_WNM + if (wpa_s->wnm_target_bss == bss) { + wpa_printf(MSG_DEBUG, + "BSS: Clear wnm_target_bss due to bss removal"); + wpa_s->wnm_target_bss = NULL; + } +#endif /* CONFIG_WNM */ + + if (wpa_s->ml_connect_probe_bss == bss) { + wpa_printf(MSG_DEBUG, + "BSS: Clear ml_connect_probe_bss due to bss removal"); + wpa_s->ml_connect_probe_bss = NULL; + } + + for (j = 0; j < MAX_NUM_MLD_LINKS; j++) { + if (wpa_s->links[j].bss == bss) { + wpa_printf(MSG_DEBUG, + "BSS: Clear links[%d].bss due to bss removal", + j); + wpa_s->valid_links &= ~BIT(j); + wpa_s->links[j].bss = NULL; + } + } + os_free(bss); } @@ -273,6 +307,58 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, return NULL; } +/** + * wpa_bss_get_connection - Fetch a BSS table entry based on BSSID and SSID. + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID, or %NULL to match any BSSID + * @ssid: SSID + * @ssid_len: Length of @ssid + * Returns: Pointer to the BSS entry or %NULL if not found + * + * This function is similar to wpa_bss_get() but it will also return OWE + * transition mode encrypted networks for which transition-element matches + * @ssid. + */ +struct wpa_bss * wpa_bss_get_connection(struct wpa_supplicant *wpa_s, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; +#ifdef CONFIG_OWE + const u8 *owe, *owe_bssid, *owe_ssid; + size_t owe_ssid_len; +#endif /* CONFIG_OWE */ + + if (bssid && !wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bssid && !ether_addr_equal(bss->bssid, bssid)) + continue; + + if (bss->ssid_len == ssid_len && + os_memcmp(bss->ssid, ssid, ssid_len) == 0) + return bss; + +#ifdef CONFIG_OWE + /* Check if OWE transition mode element is present and matches + * the SSID */ + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe) + continue; + + if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid, + &owe_ssid_len)) + continue; + + if (bss->ssid_len && + owe_ssid_len == ssid_len && + os_memcmp(owe_ssid, ssid, ssid_len) == 0) + return bss; +#endif /* CONFIG_OWE */ + } + return NULL; +} + void calculate_update_time(const struct os_reltime *fetch_time, unsigned int age_ms, @@ -371,6 +457,28 @@ static bool is_p2p_pending_bss(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_OWE +static int wpa_bss_owe_trans_known(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + const u8 *entry_ssid, size_t entry_ssid_len) +{ + const u8 *owe, *owe_bssid, *owe_ssid; + size_t owe_ssid_len; + + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe) + return 0; + + if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid, + &owe_ssid_len)) + return 0; + + return entry_ssid_len == owe_ssid_len && + os_memcmp(owe_ssid, entry_ssid, owe_ssid_len) == 0; +} +#endif /* CONFIG_OWE */ + + static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpa_ssid *ssid; @@ -384,6 +492,11 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) if (ssid->ssid_len == bss->ssid_len && os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0) return 1; +#ifdef CONFIG_OWE + if (wpa_bss_owe_trans_known(wpa_s, bss, ssid->ssid, + ssid->ssid_len)) + return 1; +#endif /* CONFIG_OWE */ } return 0; @@ -434,6 +547,7 @@ static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!wpa_bss_known(wpa_s, bss) && + !wpa_bss_in_use(wpa_s, bss) && !wpa_bss_is_wps_candidate(wpa_s, bss)) { wpa_bss_remove(wpa_s, bss, __func__); return 0; @@ -476,10 +590,8 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, { struct wpa_bss *bss; char extra[100]; - const u8 *ml_ie; char *pos, *end; int ret = 0; - const u8 *mld_addr; bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (bss == NULL) @@ -494,13 +606,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len); wpa_bss_set_hessid(bss); - os_memset(bss->mld_addr, 0, ETH_ALEN); - ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC); - if (ml_ie) { - mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1); - if (mld_addr) - os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN); - } + wpa_bss_parse_basic_ml_element(wpa_s, bss); if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count && wpa_bss_remove_oldest(wpa_s) != 0) { @@ -744,9 +850,23 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_bss *nbss; struct dl_list *prev = bss->list_id.prev; struct wpa_connect_work *cwork; - unsigned int i; + unsigned int i, j; bool update_current_bss = wpa_s->current_bss == bss; bool update_ml_probe_bss = wpa_s->ml_connect_probe_bss == bss; + int update_link_bss = -1; +#ifdef CONFIG_WNM + bool update_wnm_target = wpa_s->wnm_target_bss == bss; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + bool update_interworking = wpa_s->interworking_gas_bss == bss; +#endif /* CONFIG_INTERWORKING */ + + for (j = 0; j < MAX_NUM_MLD_LINKS; j++) { + if (wpa_s->links[j].bss == bss) { + update_link_bss = j; + break; + } + } cwork = wpa_bss_check_pending_connect(wpa_s, bss); @@ -768,6 +888,18 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (update_ml_probe_bss) wpa_s->ml_connect_probe_bss = nbss; + if (update_link_bss >= 0) + wpa_s->links[update_link_bss].bss = nbss; + +#ifdef CONFIG_WNM + if (update_wnm_target) + wpa_s->wnm_target_bss = nbss; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + if (update_interworking) + wpa_s->interworking_gas_bss = nbss; +#endif /* CONFIG_INTERWORKING */ + if (cwork) wpa_bss_update_pending_connect(cwork, nbss); @@ -780,17 +912,9 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, dl_list_add(prev, &bss->list_id); } if (changes & WPA_BSS_IES_CHANGED_FLAG) { - const u8 *ml_ie, *mld_addr; - wpa_bss_set_hessid(bss); - os_memset(bss->mld_addr, 0, ETH_ALEN); - ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC); - if (ml_ie) { - mld_addr = get_basic_mle_mld_addr(&ml_ie[3], - ml_ie[1] - 1); - if (mld_addr) - os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN); - } + + wpa_bss_parse_basic_ml_element(wpa_s, bss); } dl_list_add_tail(&wpa_s->bss, &bss->list); @@ -1032,6 +1156,7 @@ void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) if (wpa_s->reassoc_same_ess && wpa_s->wpa_state != WPA_COMPLETED && wpa_s->last_ssid && + wpa_s->last_ssid->ssid && bss->ssid_len == wpa_s->last_ssid->ssid_len && os_memcmp(bss->ssid, wpa_s->last_ssid->ssid, bss->ssid_len) == 0) @@ -1511,10 +1636,9 @@ int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab) static void wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, u8 mbssid_idx, + struct wpa_bss *bss, u8 ap_mld_id, const struct ieee80211_neighbor_ap_info *ap_info, - size_t len, u16 *seen, u16 *missing, - struct wpa_ssid *ssid) + size_t len, u16 *seen) { const u8 *pos, *end; const u8 *mld_params; @@ -1537,58 +1661,309 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s, end = pos + len; pos += sizeof(*ap_info); - for (i = 0; i < count; i++) { - u8 bss_params; - + for (i = 0; i < count; i++, pos += ap_info->tbtt_info_len) { if (end - pos < ap_info->tbtt_info_len) break; - bss_params = pos[1 + ETH_ALEN + 4]; mld_params = pos + mld_params_offset; link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK; if (link_id >= MAX_NUM_MLD_LINKS) - return; + continue; - if (*mld_params != mbssid_idx) { + if (*mld_params != ap_mld_id) { wpa_printf(MSG_DEBUG, "MLD: Reported link not part of MLD"); } else if (!(BIT(link_id) & *seen)) { - struct wpa_bss *neigh_bss; - - if (ssid && ssid->ssid_len) - neigh_bss = wpa_bss_get(wpa_s, pos + 1, - ssid->ssid, - ssid->ssid_len); - else - neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1); + struct mld_link *l; *seen |= BIT(link_id); wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u", *mld_params, link_id); - if (!neigh_bss) { - *missing |= BIT(link_id); - } else if ((!ssid || - (bss_params & (RNR_BSS_PARAM_SAME_SSID | - RNR_BSS_PARAM_CO_LOCATED)) || - wpa_scan_res_match(wpa_s, 0, neigh_bss, - ssid, 1, 0)) && - !wpa_bssid_ignore_is_listed( - wpa_s, neigh_bss->bssid)) { - struct mld_link *l; + bss->valid_links |= BIT(link_id); + l = &bss->mld_links[link_id]; + os_memcpy(l->bssid, pos + 1, ETH_ALEN); + l->disabled = mld_params[2] & + RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED; + l->freq = ieee80211_chan_to_freq(NULL, + ap_info->op_class, + ap_info->channel); + } + } +} - bss->valid_links |= BIT(link_id); - l = &bss->mld_links[link_id]; - os_memcpy(l->bssid, pos + 1, ETH_ALEN); - l->freq = neigh_bss->freq; - l->disabled = mld_params[2] & - RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED; + +/** + * wpa_bss_validate_rsne_ml - Validate RSN IEs (RSNE/RSNOE/RSNO2E) of a BSS + * @wpa_s: Pointer to wpa_supplicant data + * @ssid: Network config + * @bss: BSS table entry + * @rsne_type_p: Type of RSNE to validate. If -1 is given, choose as per the + * presence of RSN elements (association link); otherwise, validate + * against the requested type (other affiliated links). + * @ref_rsne: Buffer for RSNE data; filled in from the main link to compare + * against and used internally + * Returns: true if the BSS configuration matches local profile and the elements + * meet MLO requirements, false otherwise + */ +static bool +wpa_bss_validate_rsne_ml(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss, int *rsne_type_p, + struct wpa_ie_data *ref_rsne) +{ + struct ieee802_11_elems elems; + struct wpa_ie_data wpa_ie; + const u8 *rsne; + size_t rsne_len; + int rsne_type; + const u8 *ies_pos = wpa_bss_ie_ptr(bss); + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + + if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 0) == + ParseFailed) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements"); + return false; + } + + if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s, ssid)) { + rsne = elems.rsne_override_2; + rsne_len = elems.rsne_override_2_len; + rsne_type = 2; + } else if (elems.rsne_override && wpas_rsn_overriding(wpa_s, ssid)) { + rsne = elems.rsne_override; + rsne_len = elems.rsne_override_len; + rsne_type = 1; + } else { + rsne = elems.rsn_ie; + rsne_len = elems.rsn_ie_len; + rsne_type = 0; + } + + if (!rsne || + wpa_parse_wpa_ie(rsne - 2, 2 + rsne_len, &wpa_ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RSN element"); + return false; + } + + if (*rsne_type_p != -1 && *rsne_type_p != rsne_type) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: No matching RSN element (RSNO mismatch)"); + return false; + } + + if (!(wpa_ie.capabilities & WPA_CAPABILITY_MFPC) || + wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: No management frame protection"); + return false; + } + + wpa_ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256); + if (!(wpa_ie.key_mgmt & ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No valid key management"); + return false; + } + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: key_mgmt=0x%x", wpa_ie.key_mgmt); + + wpa_ie.pairwise_cipher &= ~(WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | + WPA_CIPHER_WEP104 | WPA_CIPHER_TKIP); + if (!(wpa_ie.pairwise_cipher & ssid->pairwise_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No valid pairwise cipher"); + return false; + } + + if (*rsne_type_p == -1) { + os_memcpy(ref_rsne, &wpa_ie, sizeof(wpa_ie)); + + *rsne_type_p = rsne_type; + } else { + /* Verify the neighbor given rsne_type_p and ref_rsne */ + if (!(wpa_ie.key_mgmt & ref_rsne->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor without common AKM"); + return false; + } + + if (!(wpa_ie.pairwise_cipher & ref_rsne->pairwise_cipher)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor without common pairwise cipher"); + return false; + } + } + + return true; +} + + +/* + * SME-based association with an AP MLD requires the driver to have a + * sufficiently recent scan entry for every requested link. With nl80211-based + * drivers, cfg80211 rejects the association if the BSS entry for any link is + * older than 30 seconds (IEEE80211_SCAN_RESULT_EXPIRE). Since the BSS table in + * wpa_supplicant keeps entries considerably longer than that + * (bss_expiration_age, 180 seconds by default) and does not refresh entries for + * the links of the current AP MLD while associated, consider an affiliated link + * with an older scan entry to be missing so that an ML probe request gets used + * to refresh the information before association. Leave some margin below the + * kernel limit to cover the time needed for authentication. + */ +#define WPA_BSS_ML_LINK_FRESH_AGE 25 /* seconds */ + +/** + * wpa_bss_get_usable_links - Retrieve the usable links of the AP MLD + * @wpa_s: Pointer to wpa_supplicant data + * @bss: BSS table entry + * @ssid: Target SSID (or %NULL) + * @missing_links: Result bitmask of links that were not discovered (or %NULL) + * Returns: Bitmap of links that are usable, or 0 for non-MLD or failure + * + * Validate each link of the MLD to verify that it is compatible and connection + * to each of the links is allowed. + */ +u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_ssid *ssid, u16 *missing_links) +{ + struct wpa_ie_data rsne; + struct os_reltime now; + int rsne_type; + u16 usable_links = 0; + u8 link_id; + + if (!bss->valid_links) + return 0; + + os_get_reltime(&now); + + rsne_type = -1; + os_memset(&rsne, 0, sizeof(rsne)); + if (ssid && + !wpa_bss_validate_rsne_ml(wpa_s, ssid, bss, &rsne_type, &rsne)) { + wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No valid key management"); + return 0; + } + + usable_links = BIT(bss->mld_link_id); + + for_each_link(bss->valid_links, link_id) { + struct wpa_bss *neigh_bss; + u16 ext_mld_capa_mask; + + if (link_id == bss->mld_link_id) + continue; + + if (ssid && ssid->ssid_len) + neigh_bss = wpa_bss_get(wpa_s, + bss->mld_links[link_id].bssid, + ssid->ssid, + ssid->ssid_len); + else + neigh_bss = wpa_bss_get_bssid(wpa_s, + bss->mld_links[link_id].bssid); + + if (!neigh_bss) { + if (missing_links) + *missing_links |= BIT(link_id); + continue; + } + + if (os_reltime_expired(&now, &neigh_bss->last_update, + WPA_BSS_ML_LINK_FRESH_AGE)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Scan entry for neighbor " MACSTR + " is too old for association; consider link %u missing", + MAC2STR(neigh_bss->bssid), link_id); + if (missing_links) + *missing_links |= BIT(link_id); + continue; + } + + /* Check that the affiliated links are for the same AP MLD and + * the information matches */ + if (!neigh_bss->valid_links) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor without Multi-Link support"); + continue; + } + + if (neigh_bss->mld_link_id != link_id) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor has unexpected link ID (%d != %d)", + neigh_bss->mld_link_id, link_id); + continue; + } + + if (!ether_addr_equal(bss->mld_addr, neigh_bss->mld_addr)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor has a different MLD MAC address (" + MACSTR " != " MACSTR ")", + MAC2STR(neigh_bss->mld_addr), + MAC2STR(bss->mld_addr)); + continue; + } + + if ((bss->mld_capa & ~EHT_ML_MLD_CAPA_RESERVED) != + (neigh_bss->mld_capa & ~EHT_ML_MLD_CAPA_RESERVED)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor's MLD Capabilities do not match (0x%04x != 0x%04x)", + neigh_bss->mld_capa, bss->mld_capa); + continue; + } + + if ((bss->eml_capa & ~EHT_ML_EML_CAPA_RESERVED) != + (neigh_bss->eml_capa & ~EHT_ML_EML_CAPA_RESERVED)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbor's EML Capabilities do not match (0x%04x != 0x%04x", + neigh_bss->eml_capa, bss->eml_capa); + continue; + } + + /* + * Check well-defined values in Extended MLD Capabilities. + * In particular the Recommended Max Simultaneous Links + * subfield may change over time and is reserved depending on + * the frame that it is carried in. + * See IEEE Std 802.11be-2024, Table 9-417o. + */ + ext_mld_capa_mask = + EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | + EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | + EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ONE_LINK; + if ((bss->ext_mld_capa & ext_mld_capa_mask) != + (neigh_bss->ext_mld_capa & ext_mld_capa_mask)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "MLD: Neighbors Extended MLD Capabilities do not match (0x%04x != 0x%04x)", + neigh_bss->ext_mld_capa, bss->ext_mld_capa); + continue; + } + + if (ssid) { + /* As per IEEE Std 802.11be-2024, 12.6.2 (RSNA + * selection), all APs affiliated with an AP MLD shall + * advertise at least one common AKM suite selector in + * the AKM Suite List field of the RSNE. Discard links + * that do not have compatible configuration with the + * association link. + */ + if (!wpa_bss_validate_rsne_ml(wpa_s, ssid, neigh_bss, + &rsne_type, &rsne)) { + wpa_printf(MSG_DEBUG, + "MLD: Discard link %u due to RSN parameter mismatch", + link_id); + continue; } } - pos += ap_info->tbtt_info_len; + if ((!ssid || + wpa_scan_res_match(wpa_s, 0, neigh_bss, ssid, 1, 0, + true)) && + !wpa_bssid_ignore_is_listed(wpa_s, neigh_bss->bssid)) { + usable_links |= BIT(link_id); + } } + + return usable_links; } @@ -1596,34 +1971,22 @@ wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s, * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element * @wpa_s: Pointer to wpa_supplicant data * @bss: BSS table entry - * @mld_addr: AP MLD address (or %NULL) - * @link_info: Array to store link information (or %NULL), - * should be initialized and #MAX_NUM_MLD_LINKS elements long - * @missing_links: Result bitmask of links that were not discovered (or %NULL) - * @ssid: Target SSID (or %NULL) - * @ap_mld_id: On return would hold the corresponding AP MLD ID (or %NULL) - * Returns: 0 on success or -1 for non-MLD or parsing failures * * Parses the Basic Multi-Link element of the BSS into @link_info using the scan * information stored in the wpa_supplicant data to fill in information for - * links where possible. The @missing_links out parameter will contain any links - * for which no corresponding BSS was found. + * links where possible. */ -int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, - u8 *ap_mld_addr, - u16 *missing_links, - struct wpa_ssid *ssid, - u8 *ap_mld_id) +void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) { struct ieee802_11_elems elems; - struct wpabuf *mlbuf; + struct wpabuf *mlbuf = NULL; const struct element *elem; - u8 mbssid_idx = 0; size_t ml_ie_len; const struct ieee80211_eht_ml *eht_ml; const struct eht_ml_basic_common_info *ml_basic_common_info; - u8 i, link_id; + const u8 *mbssid_idx_elem; + u8 i, pos, link_id, ap_mld_id; const u16 control_mask = MULTI_LINK_CONTROL_TYPE_MASK | BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | @@ -1634,58 +1997,29 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT | BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA; - u16 missing = 0; u16 seen; const u8 *ies_pos = wpa_bss_ie_ptr(bss); size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; - int ret = -1; struct mld_link *l; if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) == ParseFailed) { wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements"); - return ret; + goto out; } mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true); if (!mlbuf) { wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No Multi-Link element"); - return ret; + goto out; } ml_ie_len = wpabuf_len(mlbuf); - if (ssid) { - struct wpa_ie_data ie; - - if (!elems.rsn_ie || - wpa_parse_wpa_ie(elems.rsn_ie - 2, 2 + elems.rsn_ie_len, - &ie)) { - wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RSN element"); - goto out; - } - - if (!(ie.capabilities & WPA_CAPABILITY_MFPC) || - wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) { - wpa_dbg(wpa_s, MSG_DEBUG, - "MLD: No management frame protection"); - goto out; - } - - ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_PSK_SHA256); - if (!(ie.key_mgmt & ssid->key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, - "MLD: No valid key management"); - goto out; - } - } - /* - * for ext ID + 2 control + common info len + MLD address + - * link info + * for ext ID + 2 control + common info len */ - if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL) + if (ml_ie_len < sizeof(*eht_ml) + sizeof(*ml_basic_common_info)) goto out; eht_ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf); @@ -1700,49 +2034,135 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, ml_basic_common_info = (const struct eht_ml_basic_common_info *) eht_ml->variable; - /* Common info length should be valid */ - if (ml_basic_common_info->len < ETH_ALEN + 1UL) + if (ml_ie_len < sizeof(*eht_ml) + ml_basic_common_info->len) goto out; - /* Get the MLD address and MLD link ID */ - if (ap_mld_addr) - os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr, - ETH_ALEN); + /* Minimum Common info length to be valid */ + if (ml_basic_common_info->len < + sizeof(*ml_basic_common_info) + 1 + 1 + 2) + goto out; + + /* Link ID Info, BSS Parameters Change Count (see control/control_mask) + */ + pos = 1 + 1; + + /* Medium Synchronization Delay Information */ + if (le_to_host16(eht_ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) { + if (ml_basic_common_info->len < + sizeof(*ml_basic_common_info) + pos + 2) + goto out; + pos += 2; + } + + /* EML Capabilities */ + bss->eml_capa = 0; + if (le_to_host16(eht_ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) { + if (ml_basic_common_info->len < + sizeof(*ml_basic_common_info) + pos + 2) + goto out; + bss->eml_capa = + WPA_GET_LE16(&ml_basic_common_info->variable[pos]); + pos += 2; + } + + /* MLD Capabilities And Operations (always present, see + * control/control_mask) */ + if (ml_basic_common_info->len < sizeof(*ml_basic_common_info) + pos + 2) + goto out; + bss->mld_capa = WPA_GET_LE16(&ml_basic_common_info->variable[pos]); + pos += 2; + + /* AP MLD ID from MLE if present (see comment below) */ + if (le_to_host16(eht_ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) { + if (ml_basic_common_info->len < + sizeof(*ml_basic_common_info) + pos + 1) + goto out; + + ap_mld_id = ml_basic_common_info->variable[pos]; + + pos++; + } else { + ap_mld_id = 0; + } + + /* Extended MLD Capabilities And Operations */ + bss->ext_mld_capa = 0; + if (le_to_host16(eht_ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) { + if (ml_basic_common_info->len < + sizeof(*ml_basic_common_info) + pos + 2) + goto out; + + bss->ext_mld_capa = + WPA_GET_LE16(&ml_basic_common_info->variable[pos]); + + pos += 2; + } + + if (ml_basic_common_info->len < sizeof(*ml_basic_common_info) + pos) + goto out; link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK; + if (link_id >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, "MLD: Invalid link ID %u in Basic MLE", + link_id); + goto out; + } + + os_memcpy(bss->mld_addr, ml_basic_common_info->mld_addr, ETH_ALEN); bss->mld_link_id = link_id; - seen = bss->valid_links = BIT(link_id); + bss->valid_links = BIT(link_id); + seen = bss->valid_links; l = &bss->mld_links[link_id]; os_memcpy(l->bssid, bss->bssid, ETH_ALEN); l->freq = bss->freq; + bss->mld_bss_non_transmitted = false; /* - * The AP MLD ID in the RNR corresponds to the MBSSID index, see - * IEEE P802.11be/D4.0, 9.4.2.169.2 (Neighbor AP Information field). + * We should be able to rely on the Multiple BSSID Index element + * to be included if the BSS is nontransmitted. Both if it was + * extracted from a beacon and if it came from an ML probe + * response (i.e. not listed in IEEE Std 802.11be-2024, 35.3.3.4). * - * For the transmitting BSSID it is clear that both the MBSSID index - * and the AP MLD ID in the RNR are zero. + * Note that the AP MLD ID and the Multiple-BSSID Index will be + * identical if the information was reported by the + * corresponding transmitting AP (IEEE Std 802.11be-2024, 9.4.2.169.2). + * As an AP MLD ID will not be explicitly provided we need to + * rely on the Multiple-BSSID Index element. This is generally the case + * when the BSS information was read from a Multiple-BSSID element. * - * For nontransmitted BSSIDs we will have a BSS generated from the - * MBSSID element(s) using inheritance rules. Included in the elements - * is the MBSSID Index Element. The RNR is copied from the Beacon/Probe - * Response frame that was send by the transmitting BSSID. As such, the - * reported AP MLD ID in the RNR will match the value in the MBSSID - * Index Element. + * The alternative scenario is a BSS discovered using a + * Multi-Link Probe Response. In that case, we can still + * determine whether the BSS is nontransmitted or not using the + * Multiple BSSID-Index element. However, the AP MLD ID may be + * different inside the ML Probe Response and the driver also + * needs to deal with this during inheritance. + * + * We assume the driver either + * - includes the appropriate AP MLD ID in the MLE it generates + * (see above), or + * - rewrites the RNR so that the AP MLD ID matches the + * Multiple-BSSID Index element. */ - elem = (const struct element *) - wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX); - if (elem && elem->datalen >= 1) - mbssid_idx = elem->data[0]; + mbssid_idx_elem = wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX); + if (mbssid_idx_elem && mbssid_idx_elem[1] >= 1) { + if (!(le_to_host16(eht_ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID)) + ap_mld_id = mbssid_idx_elem[2]; + bss->mld_bss_non_transmitted = !!mbssid_idx_elem[2]; + } for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, wpa_bss_ie_ptr(bss), bss->ie_len ? bss->ie_len : bss->beacon_ie_len) { const struct ieee80211_neighbor_ap_info *ap_info; - const u8 *pos = elem->data; + const u8 *ap_info_pos = elem->data; size_t len = elem->datalen; /* RNR IE may contain more than one Neighbor AP Info */ @@ -1751,40 +2171,36 @@ int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, u8 count; ap_info = (const struct ieee80211_neighbor_ap_info *) - pos; + ap_info_pos; count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1; ap_info_len += count * ap_info->tbtt_info_len; if (ap_info_len > len) goto out; - wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx, - ap_info, len, &seen, - &missing, ssid); + wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, ap_mld_id, + ap_info, len, &seen); - pos += ap_info_len; + ap_info_pos += ap_info_len; len -= ap_info_len; } } - wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)", - bss->valid_links, missing); + wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%04hx", + bss->valid_links); for_each_link(bss->valid_links, i) { wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR, i, MAC2STR(bss->mld_links[i].bssid)); } - if (missing_links) - *missing_links = missing; - - if (ap_mld_id) - *ap_mld_id = mbssid_idx; - - ret = 0; -out: wpabuf_free(mlbuf); - return ret; + return; + +out: + os_memset(bss->mld_addr, 0, ETH_ALEN); + bss->valid_links = 0; + wpabuf_free(mlbuf); } @@ -1802,8 +2218,9 @@ u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s, const u8 *pos = wpa_bss_ie_ptr(bss); size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; const struct ieee80211_eht_ml *ml; + const struct eht_ml_reconf_common_info *common_info; u16 removed_links = 0; - u8 ml_common_len; + u8 expected_ml_common_len; if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed) return 0; @@ -1818,25 +2235,50 @@ u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s, ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf); len = wpabuf_len(mlbuf); - if (len < sizeof(*ml)) + /* There must be at least one octet for the Common Info Length subfield + */ + if (len < sizeof(*ml) + 1UL) goto out; - ml_common_len = 1; - if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR) - ml_common_len += ETH_ALEN; + expected_ml_common_len = 1; + if (le_to_host16(ml->ml_control) & + RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR) + expected_ml_common_len += ETH_ALEN; - if (len < sizeof(*ml) + ml_common_len) { + common_info = (const struct eht_ml_reconf_common_info *) ml->variable; + if (len < sizeof(*ml) + common_info->len) { wpa_printf(MSG_DEBUG, "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)", - len, sizeof(*ml) + ml_common_len); + len, sizeof(*ml) + common_info->len); goto out; } - pos = ml->variable + ml_common_len; - len -= sizeof(*ml) + ml_common_len; + if (common_info->len < expected_ml_common_len) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid common info len=%u; min expected=%u", + common_info->len, expected_ml_common_len); + goto out; + } + + pos = ml->variable + common_info->len; + len -= sizeof(*ml) + common_info->len; while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) { - size_t sub_elem_len = *(pos + 1); + size_t sub_elem_len; + int num_frag_subelems; + + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &sub_elem_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse MLE subelem"); + break; + } + + if ((size_t) num_frag_subelems * 2 > len) + goto out; + len -= num_frag_subelems * 2; if (2 + sub_elem_len > len) { wpa_printf(MSG_DEBUG, @@ -1845,7 +2287,8 @@ u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s, goto out; } - if (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) { + if (*pos == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE && + sub_elem_len >= 2) { const struct ieee80211_eht_per_sta_profile *sta_prof = (const struct ieee80211_eht_per_sta_profile *) (pos + 2); @@ -1853,7 +2296,8 @@ u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s, u8 link_id; link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK; - removed_links |= BIT(link_id); + if (link_id < MAX_NUM_MLD_LINKS) + removed_links |= BIT(link_id); } pos += 2 + sub_elem_len; @@ -1866,3 +2310,207 @@ out: wpabuf_free(mlbuf); return removed_links; } + + +#ifndef CONFIG_NO_WPA + +static bool wpa_bss_supported_cipher(struct wpa_supplicant *wpa_s, + int pairwise_cipher) +{ + if (!wpa_s->drv_enc) + return true; + + if ((pairwise_cipher & WPA_CIPHER_CCMP) && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP)) + return true; + + if ((pairwise_cipher & WPA_CIPHER_GCMP) && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) + return true; + + if ((pairwise_cipher & WPA_CIPHER_CCMP_256) && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) + return true; + + if ((pairwise_cipher & WPA_CIPHER_GCMP_256) && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) + return true; + + return false; +} + + +static bool wpa_bss_supported_key_mgmt(struct wpa_supplicant *wpa_s, + int key_mgmt) +{ + if (!wpa_s->drv_key_mgmt) + return true; + + if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_PSK) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_PSK) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_PSK_SHA256) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_SAE) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_SAE) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_OWE) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_DPP) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FILS_SHA256) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FILS_SHA384) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256)) + return true; + if ((key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) && + (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384)) + return true; + + return false; +} + + +static bool wpa_bss_supported_rsne(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, const u8 *ie) +{ + struct wpa_ie_data data; + + if (wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) < 0) + return false; + + /* Check that there is a supported AKM and pairwise cipher based on + * overall capabilities */ + if (!data.pairwise_cipher || !data.key_mgmt) + return false; + + if (wpa_s->drv_capa_known) { + if (!wpa_bss_supported_cipher(wpa_s, data.pairwise_cipher) || + !wpa_bss_supported_key_mgmt(wpa_s, data.key_mgmt)) + return false; + } + + if (ssid) { + /* Check that there is a supported AKM and pairwise cipher + * based on the specific network profile. */ + if ((ssid->pairwise_cipher & data.pairwise_cipher) == 0) + return false; + if ((ssid->key_mgmt & data.key_mgmt) == 0) + return false; + } + + return true; +} + +#endif /* CONFIG_NO_WPA */ + + +const u8 * wpa_bss_get_rsne(struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, struct wpa_ssid *ssid, + bool mlo) +{ +#ifndef CONFIG_NO_WPA + const u8 *ie; + + if (wpas_rsn_overriding(wpa_s, ssid)) { + if (!ssid) + ssid = wpa_s->current_ssid; + + /* MLO cases for RSN overriding are required to use RSNE + * Override 2 element and RSNXE Override element together. */ + ie = wpa_bss_get_vendor_ie(bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (mlo && ie && + !wpa_bss_get_vendor_ie(bss, + RSNXE_OVERRIDE_IE_VENDOR_TYPE)) { + wpa_printf(MSG_DEBUG, "BSS " MACSTR + " advertises RSNE Override 2 element without RSNXE Override element - ignore RSNE Override 2 element for MLO", + MAC2STR(bss->bssid)); + } else if (ie && wpa_bss_supported_rsne(wpa_s, ssid, ie)) { + return ie; + } + + if (!mlo) { + ie = wpa_bss_get_vendor_ie( + bss, RSNE_OVERRIDE_IE_VENDOR_TYPE); + if (ie && wpa_bss_supported_rsne(wpa_s, ssid, ie)) + return ie; + } + } +#endif /* CONFIG_NO_WPA */ + + return wpa_bss_get_ie(bss, WLAN_EID_RSN); +} + + +const u8 * wpa_bss_get_rsnxe(struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, struct wpa_ssid *ssid, + bool mlo) +{ + const u8 *ie; + + if (wpas_rsn_overriding(wpa_s, ssid)) { + ie = wpa_bss_get_vendor_ie(bss, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (ie) { + const u8 *tmp; + + tmp = wpa_bss_get_rsne(wpa_s, bss, ssid, mlo); + if (!tmp || tmp[0] == WLAN_EID_RSN) { + /* An acceptable RSNE override element was not + * found, so need to ignore RSNXE overriding. */ + goto out; + } + + return ie; + } + + /* MLO cases for RSN overriding are required to use RSNE + * Override 2 element and RSNXE Override element together. */ + if (mlo && wpa_bss_get_vendor_ie( + bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE)) { + wpa_printf(MSG_DEBUG, "BSS " MACSTR + " advertises RSNXE Override element without RSNE Override 2 element - ignore RSNXE Override element for MLO", + MAC2STR(bss->bssid)); + goto out; + } + } + +out: + return wpa_bss_get_ie(bss, WLAN_EID_RSNX); +} diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index 2079606a9fa4..d9d0401cef32 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -61,9 +61,6 @@ struct wpa_bss_anqp { struct wpabuf *hs20_wan_metrics; struct wpabuf *hs20_connection_capability; struct wpabuf *hs20_operating_class; - struct wpabuf *hs20_osu_providers_list; - struct wpabuf *hs20_operator_icon_metadata; - struct wpabuf *hs20_osu_providers_nai_list; #endif /* CONFIG_HS20 */ }; @@ -128,8 +125,17 @@ struct wpa_bss { u8 mld_addr[ETH_ALEN]; /** Link ID of this affiliated AP of the AP MLD */ u8 mld_link_id; + /** For MLD, denotes whether the BSS is a non-transmitted one; useful + * for ML probe */ + bool mld_bss_non_transmitted; + /** MLD Capabilities And Operations */ + u16 mld_capa; + /** Extended MLD Capabilities And Operations */ + u16 ext_mld_capa; + /** EML Capabilities */ + u16 eml_capa; - /** An array of MLD links */ + /** An array of MLD links, any link found in the RNR is "valid" */ u16 valid_links; struct mld_link { u8 bssid[ETH_ALEN]; @@ -165,6 +171,9 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s); void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age); struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid, size_t ssid_len); +struct wpa_bss * wpa_bss_get_connection(struct wpa_supplicant *wpa_s, + const u8 *bssid, + const u8 *ssid, size_t ssid_len); struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, @@ -217,13 +226,18 @@ void calculate_update_time(const struct os_reltime *fetch_time, unsigned int age_ms, struct os_reltime *update_time); -int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, - u8 *ap_mld_addr, - u16 *missing_links, - struct wpa_ssid *ssid, - u8 *ap_mld_id); +u16 wpa_bss_get_usable_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_ssid *ssid, u16 *missing_links); +void wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); +const u8 * wpa_bss_get_rsne(struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, struct wpa_ssid *ssid, + bool mlo); +const u8 * wpa_bss_get_rsnxe(struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, struct wpa_ssid *ssid, + bool mlo); + #endif /* BSS_H */ diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index 24861c68f0bd..e7fc3f400c39 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -276,6 +276,69 @@ static char * wpa_config_write_int(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_bool(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + int val; + bool *dst; + char *end; + + dst = (bool *) (((u8 *) ssid) + (long) data->param1); + val = strtol(value, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, value); + return -1; + } + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: too small %s (value=%d)", + line, data->name, val); + return -1; + } + + if (val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: too large %s (value=%d)", + line, data->name, val); + return -1; + } + + if (*dst == val) + return 1; + + *dst = val; + wpa_printf(MSG_MSGDUMP, "%s=%d)", data->name, *dst); + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bool(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + bool *src, res; + char *value; + + src = (bool *) (((u8 *) ssid) + (long) data->param1); + + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, "%d", *src); + if (os_snprintf_error(20, res)) { + os_free(value); + return NULL; + } + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_addr_list(const struct parse_data *data, int line, const char *value, u8 **list, size_t *num, char *name, @@ -666,6 +729,43 @@ static char * wpa_config_write_psk(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_alt_sae_password_ids(const struct parse_data *data, + struct wpa_ssid *ssid, + int line, const char *value) +{ + struct wpabuf *tmp; + + if (!value[0]) { + wpabuf_array_free(ssid->alt_sae_password_ids); + ssid->alt_sae_password_ids = NULL; + return 0; + } + + tmp = wpabuf_parse_bin(value); + if (!tmp) { + wpa_printf(MSG_ERROR, "Line %d: invalid alt_sae_password_ids", + line); + return -1; + } + + if (!ssid->alt_sae_password_ids) + ssid->alt_sae_password_ids = wpabuf_array_alloc(); + if (wpabuf_array_add(ssid->alt_sae_password_ids, tmp) < 0) + return -1; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * +wpa_config_write_alt_sae_password_ids(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_proto(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -693,8 +793,6 @@ static int wpa_config_parse_proto(const struct parse_data *data, else if (os_strcmp(start, "RSN") == 0 || os_strcmp(start, "WPA2") == 0) val |= WPA_PROTO_RSN; - else if (os_strcmp(start, "OSEN") == 0) - val |= WPA_PROTO_OSEN; else { wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", line, start); @@ -749,14 +847,6 @@ static char * wpa_config_write_proto(const struct parse_data *data, pos += ret; } - if (ssid->proto & WPA_PROTO_OSEN) { - ret = os_snprintf(pos, end - pos, "%sOSEN", - pos == buf ? "" : " "); - if (os_snprintf_error(end - pos, ret)) - return buf; - pos += ret; - } - if (pos == buf) { os_free(buf); buf = NULL; @@ -831,10 +921,6 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0) val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY; #endif /* CONFIG_SAE */ -#ifdef CONFIG_HS20 - else if (os_strcmp(start, "OSEN") == 0) - val |= WPA_KEY_MGMT_OSEN; -#endif /* CONFIG_HS20 */ #ifdef CONFIG_SUITEB else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; @@ -863,6 +949,10 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "DPP") == 0) val |= WPA_KEY_MGMT_DPP; #endif /* CONFIG_DPP */ +#ifdef CONFIG_ENC_ASSOC + else if (os_strcmp(start, "EPPKE") == 0) + val |= WPA_KEY_MGMT_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -1071,18 +1161,6 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } #endif /* CONFIG_SAE */ -#ifdef CONFIG_HS20 - if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) { - ret = os_snprintf(pos, end - pos, "%sOSEN", - pos == buf ? "" : " "); - if (os_snprintf_error(end - pos, ret)) { - end[-1] = '\0'; - return buf; - } - pos += ret; - } -#endif /* CONFIG_HS20 */ - #ifdef CONFIG_SUITEB if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B", @@ -1172,6 +1250,18 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_ENC_ASSOC + if (ssid->key_mgmt & WPA_KEY_MGMT_EPPKE) { + ret = os_snprintf(pos, end - pos, "%sEPPKE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_ENC_ASSOC */ + if (pos == buf) { os_free(buf); buf = NULL; @@ -1474,6 +1564,29 @@ static int * wpa_config_parse_int_array(const char *value) } +static int wpa_config_print_int_array(char *buf, size_t buflen, int *array) +{ + int res, tmp; + + if (!array) + return -1; + + res = 0; + for (; *array != 0; array++) { + tmp = os_snprintf(buf + res, buflen - res, "%d ", *array); + if (os_snprintf_error(buflen - res, tmp)) + return -1; + + res += tmp; + } + /* strip last space */ + if (res > 0) + buf[--res] = '\0'; + + return res; +} + + static int wpa_config_parse_scan_freq(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -2138,7 +2251,27 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data, } +static int wpa_config_parse_p2p2_client_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *ids = wpa_config_parse_int_array(value); + + if (!ids) { + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p2_client_list '%s'", + line, value); + return -1; + } + + os_free(ssid->p2p2_client_list); + ssid->p2p2_client_list = ids; + + return 0; +} + + #ifndef NO_CONFIG_WRITE + static char * wpa_config_write_p2p_client_list(const struct parse_data *data, struct wpa_ssid *ssid) { @@ -2146,6 +2279,14 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, ssid->num_p2p_clients, "p2p_client_list"); } + + +static char * wpa_config_write_p2p2_client_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->p2p2_client_list); +} + #endif /* NO_CONFIG_WRITE */ @@ -2421,6 +2562,37 @@ static char * wpa_config_write_mac_value(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +#ifdef CONFIG_PASN +static int wpa_config_parse_pasn_groups(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *groups; + + groups = wpa_config_parse_int_array(value); + if (!groups) + return -1; + if (groups[0] == 0) { + os_free(groups); + groups = NULL; + } + os_free(ssid->pasn_groups); + ssid->pasn_groups = groups; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_pasn_groups(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->pasn_groups); +} +#endif /* NO_CONFIG_WRITE */ +#endif /* CONFIG_PASN */ + + /* Helper macros for network block parser */ #ifdef OFFSET @@ -2482,6 +2654,20 @@ static char * wpa_config_write_mac_value(const struct parse_data *data, (void *) 0, (void *) (min), (void *) (max), 0 #endif /* NO_CONFIG_WRITE */ +#ifdef NO_CONFIG_WRITE +#define _BOOL(f) #f, wpa_config_parse_bool, OFFSET(f), (void *) 0 +#define _BOOLe(f, m) #f, wpa_config_parse_bool, OFFSET(eap.m), (void *) 0 +#else /* NO_CONFIG_WRITE */ +#define _BOOL(f) #f, wpa_config_parse_bool, wpa_config_write_bool, \ + OFFSET(f), (void *) 0 +#define _BOOLe(f, m) #f, wpa_config_parse_bool, wpa_config_write_bool, \ + OFFSET(eap.m), (void *) 0 +#endif /* NO_CONFIG_WRITE */ + +/* INT: Define an integer variable */ +#define BOOL(f) _BOOL(f), NULL, NULL, 0 +#define BOOLe(f, m) _BOOLe(f, m), NULL, NULL, 0 + /* FUNC: Define a configuration variable that uses a custom function for * parsing and writing the value. */ #ifdef NO_CONFIG_WRITE @@ -2529,6 +2715,8 @@ static const struct parse_data ssid_fields[] = { { INT(mem_only_psk) }, { STR_KEY(sae_password) }, { STR(sae_password_id) }, + { FUNC(alt_sae_password_ids) }, + { INT(sae_pwe) }, { FUNC(proto) }, { FUNC(key_mgmt) }, { INT(bg_scan_period) }, @@ -2543,7 +2731,7 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(he, 0, 1) }, { INT_RANGE(ht40, -1, 1) }, { INT_RANGE(max_oper_chwidth, CONF_OPER_CHWIDTH_USE_HT, - CONF_OPER_CHWIDTH_80P80MHZ) }, + CONF_OPER_CHWIDTH_320MHZ) }, { INT(vht_center_freq1) }, { INT(vht_center_freq2) }, #ifdef IEEE8021X_EAPOL @@ -2670,7 +2858,9 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_P2P { FUNC(go_p2p_dev_addr) }, { FUNC(p2p_client_list) }, + { FUNC(p2p2_client_list) }, { FUNC(psk_list) }, + { INT(go_dik_id) }, #endif /* CONFIG_P2P */ #ifdef CONFIG_HT_OVERRIDES { INT_RANGE(disable_ht, 0, 1) }, @@ -2721,6 +2911,7 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(macsec_port, 1, 65534) }, { INT_RANGE(mka_priority, 0, 255) }, { INT_RANGE(macsec_csindex, 0, 1) }, + { INT_RANGE(macsec_icv_indicator, 0, 1) }, { FUNC_KEY(mka_cak) }, { FUNC_KEY(mka_ckn) }, #endif /* CONFIG_MACSEC */ @@ -2755,7 +2946,20 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(disable_eht, 0, 1)}, { INT_RANGE(enable_4addr_mode, 0, 1)}, { INT_RANGE(max_idle, 0, 65535)}, - { INT_RANGE(ssid_protection, 0, 1)}, + { BOOL(ssid_protection)}, + { INT_RANGE(rsn_overriding, 0, 2)}, + { BOOL(sae_password_id_change)}, +#ifdef CONFIG_PMKSA_PRIVACY + { INT_RANGE(pmksa_privacy, 0, 1)}, +#endif /* CONFIG_PMKSA_PRIVACY */ +#ifdef CONFIG_IEEE8021X_AUTH + { INT_RANGE(eap_over_auth_frame, 0, 1)}, +#endif /* CONFIG_IEEE8021X_AUTH */ + { BOOL(drop_unicast_ip_in_l2_multicast)}, + { INT_RANGE(always_use_proxy_arp, 0, 2)}, +#ifdef CONFIG_PASN + { FUNC(pasn_groups) }, +#endif /* CONFIG_PASN */ }; #undef OFFSET @@ -2771,6 +2975,10 @@ static const struct parse_data ssid_fields[] = { #undef _INT #undef INT #undef INT_RANGE +#undef BOOLe +#undef BOOL +#undef _BOOLe +#undef _BOOL #undef _FUNC #undef FUNC #undef FUNC_KEY @@ -2928,6 +3136,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->ext_psk); str_clear_free(ssid->sae_password); os_free(ssid->sae_password_id); + wpabuf_array_free(ssid->alt_sae_password_ids); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); #endif /* IEEE8021X_EAPOL */ @@ -2936,6 +3145,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->freq_list); os_free(ssid->bgscan); os_free(ssid->p2p_client_list); + os_free(ssid->p2p2_client_list); os_free(ssid->bssid_ignore); os_free(ssid->bssid_accept); #ifdef CONFIG_HT_OVERRIDES @@ -2959,6 +3169,9 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) #ifdef CONFIG_SAE sae_deinit_pt(ssid->pt); #endif /* CONFIG_SAE */ +#ifdef CONFIG_PASN + os_free(ssid->pasn_groups); +#endif /* CONFIG_PASN */ bin_clear_free(ssid, sizeof(*ssid)); } @@ -3027,6 +3240,7 @@ void wpa_config_free(struct wpa_config *config) { struct wpa_ssid *ssid, *prev = NULL; struct wpa_cred *cred, *cprev; + struct wpa_dev_ik *identity, *iprev; int i; ssid = config->ssid; @@ -3043,6 +3257,13 @@ void wpa_config_free(struct wpa_config *config) wpa_config_free_cred(cprev); } + identity = config->identity; + while (identity) { + iprev = identity; + identity = identity->next; + wpa_config_free_identity(iprev); + } + wpa_config_flush_blobs(config); wpabuf_free(config->wps_vendor_ext_m1); @@ -3081,9 +3302,11 @@ void wpa_config_free(struct wpa_config *config) wpabuf_free(config->wps_nfc_dev_pw); os_free(config->ext_password_backend); os_free(config->sae_groups); +#ifdef CONFIG_PASN + os_free(config->pasn_groups); +#endif /* CONFIG_PASN */ wpabuf_free(config->ap_vendor_elements); wpabuf_free(config->ap_assocresp_elements); - os_free(config->osu_dir); os_free(config->bgscan); os_free(config->wowlan_triggers); os_free(config->fst_group_id); @@ -3095,6 +3318,8 @@ void wpa_config_free(struct wpa_config *config) os_free(config->dpp_mud_url); os_free(config->dpp_extra_conf_req_name); os_free(config->dpp_extra_conf_req_value); + wpabuf_free(config->dik); + wpabuf_free(config->wfa_gen_capa_supp); os_free(config); } @@ -3146,6 +3371,35 @@ struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id) } +#ifdef CONFIG_P2P +/** + * wpa_config_get_network_with_dik_id - Get configured network based on ID of + * device identity block + * @config: Configuration data from wpa_config_read() + * @dik_id: DIK ID to search for + * Returns: Network configuration or %NULL if not found + */ +struct wpa_ssid * wpa_config_get_network_with_dik_id(struct wpa_config *config, + int dik_id) +{ + struct wpa_ssid *ssid; + + for (ssid = config->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled != 2) + continue; + + if (ssid->go_dik_id == dik_id) + return ssid; + + if (int_array_includes(ssid->p2p2_client_list, dik_id)) + return ssid; + } + + return NULL; +} +#endif /* CONFIG_P2P */ + + /** * wpa_config_add_network - Add a new network with empty configuration * @config: Configuration data from wpa_config_read() @@ -3280,6 +3534,8 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) #endif /* CONFIG_MACSEC */ ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET; ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH; + ssid->rsn_overriding = RSN_OVERRIDING_NOT_SET; + ssid->drop_unicast_ip_in_l2_multicast = true; } @@ -3347,6 +3603,11 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, sae_deinit_pt(ssid->pt); ssid->pt = NULL; } + if (os_strcmp(var, "sae_password_id_change") == 0 && + !ssid->sae_password_id_change) { + wpabuf_array_free(ssid->alt_sae_password_ids); + ssid->alt_sae_password_ids = NULL; + } #endif /* CONFIG_SAE */ break; } @@ -4817,6 +5078,46 @@ wpa_global_config_parse_int_range(const struct global_parse_data *data, } +static int wpa_global_config_parse_bool(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + int val; + bool *dst; + char *end; + bool same; + + dst = (bool *) (((u8 *) config) + (long) data->param1); + val = strtol(pos, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, pos); + return -1; + } + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: too small %s (value=%d min_value=0)", + line, data->name, val); + return -1; + } + + if (val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: too large %s (value=%d max_value=1)", + line, data->name, val); + return -1; + } + + same = *dst == val; + *dst = val; + + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); + + return same; +} + + static int wpa_global_config_parse_str(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) @@ -5233,6 +5534,37 @@ static int wpa_config_process_sae_groups( } +#ifdef CONFIG_PASN +static int wpa_config_process_pasn_groups( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int *groups; + + if (!pos || !*pos) { + /* Empty string clears pasn_groups if previously set */ + if (config->pasn_groups) { + os_free(config->pasn_groups); + config->pasn_groups = NULL; + } + return 0; + } + + groups = wpa_config_parse_int_array(pos); + if (!groups) { + wpa_printf(MSG_ERROR, "Line %d: Invalid pasn_groups '%s'", + line, pos); + return -1; + } + + os_free(config->pasn_groups); + config->pasn_groups = groups; + + return 0; +} +#endif /* CONFIG_PASN */ + + static int wpa_config_process_ap_vendor_elements( const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) @@ -5308,6 +5640,18 @@ static int wpa_config_get_int(const char *name, struct wpa_config *config, } +static int wpa_config_get_bool(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + bool *val = (bool*) (((u8 *) config) + (long) offset); + + if (pretty_print) + return os_snprintf(buf, buflen, "%s=%d\n", name, *val); + return os_snprintf(buf, buflen, "%d", *val); +} + + static int wpa_config_get_str(const char *name, struct wpa_config *config, long offset, char *buf, size_t buflen, int pretty_print) @@ -5329,6 +5673,70 @@ static int wpa_config_get_str(const char *name, struct wpa_config *config, } +static int wpa_config_get_bgscan(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + char **val = (char **) (((u8 *) config) + (long) offset); + int res; + + if (pretty_print) { + if (*val) + res = os_snprintf(buf, buflen, "%s=\"%s\"\n", name, + *val); + else + res = os_snprintf(buf, buflen, "%s=null\n", name); + } else if (!*val) { + return -1; + } else { + res = os_snprintf(buf, buflen, "\"%s\"", *val); + } + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} + + +static int wpa_config_get_freq_list(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + int **val = (int **) (((u8 *) config) + (long) offset); + int res, tmp; + + if (pretty_print) { + if (*val) { + res = os_snprintf(buf, buflen, "%s=", name); + if (os_snprintf_error(buflen, res)) + return -1; + + tmp = wpa_config_print_int_array(buf + res, + buflen - res, *val); + if (tmp < 0) + return -1; + res += tmp; + + tmp = os_snprintf(buf + res, buflen - res, "\n"); + if (os_snprintf_error(buflen - res, tmp)) + return -1; + res += tmp; + } else { + res = os_snprintf(buf, buflen, "%s=null\n", name); + } + + } else if (!*val) { + return -1; + } else { + res = wpa_config_print_int_array(buf, buflen, *val); + } + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} + + #ifdef CONFIG_P2P static int wpa_config_get_ipv4(const char *name, struct wpa_config *config, long offset, char *buf, size_t buflen, @@ -5378,11 +5786,15 @@ static int wpa_config_process_mld_connect_bssid_pref( #define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v) #define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL +#define FUNC_WITH_GET(f) #f, wpa_config_process_ ## f, wpa_config_get_ ## f, \ + OFFSET(f), NULL, NULL #define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL #define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f) #define INT(f) _INT(f), NULL, NULL #define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \ wpa_config_get_int, OFFSET(f), (void *) min, (void *) max +#define BOOL(f) #f, wpa_global_config_parse_bool, wpa_config_get_bool, \ + OFFSET(f), NULL, NULL #define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f) #define STR(f) _STR(f), NULL, NULL #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max @@ -5402,7 +5814,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(eapol_version, 1, 2), 0 }, #endif /* CONFIG_MACSEC */ { INT(ap_scan), 0 }, - { FUNC(bgscan), CFG_CHANGED_BGSCAN }, + { FUNC_WITH_GET(bgscan), CFG_CHANGED_BGSCAN }, #ifdef CONFIG_MESH { INT(user_mpm), 0 }, { INT_RANGE(max_peer_links, 0, 255), 0 }, @@ -5473,7 +5885,7 @@ static const struct global_parse_data global_fields[] = { { INT(p2p_go_vht), 0 }, { INT(p2p_go_he), 0 }, { INT(p2p_go_edmg), 0 }, - { INT(p2p_disabled), 0 }, + { INT(p2p_disabled), CFG_CHANGED_P2P_DISABLED }, { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 }, { INT(p2p_no_group_iface), 0 }, { INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 }, @@ -5486,6 +5898,15 @@ static const struct global_parse_data global_fields[] = { { FUNC(p2p_device_persistent_mac_addr), 0 }, { INT(p2p_interface_random_mac_addr), 0 }, { INT(p2p_6ghz_disable), 0 }, + { INT(p2p_bootstrap_methods), 0 }, + { INT(p2p_pasn_type), 0 }, + { INT(p2p_comeback_after), 0 }, + { BOOL(p2p_twt_power_mgmt), 0 }, + { BOOL(p2p_chan_switch_req_enable), 0 }, + { INT(p2p_reg_info), 0 }, + { INT(p2p_assisted_dfs_chan_enable), 0 }, + { INT(dik_cipher), 0}, + { BIN(dik), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, @@ -5528,14 +5949,13 @@ static const struct global_parse_data global_fields[] = { { FUNC(ap_assocresp_elements), 0 }, { FUNC(ap_vendor_elements), 0 }, { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, - { FUNC(freq_list), 0 }, + { FUNC_WITH_GET(freq_list), 0 }, { FUNC(initial_freq_list), 0}, { INT(scan_cur_freq), 0 }, { INT(scan_res_valid_for_connect), 0}, { INT(sched_scan_interval), 0 }, { INT(sched_scan_start_delay), 0 }, { INT(tdls_external_control), 0}, - { STR(osu_dir), 0 }, { STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS }, { INT(p2p_search_delay), 0}, { INT_RANGE(mac_addr, 0, 2), 0 }, @@ -5563,6 +5983,7 @@ static const struct global_parse_data global_fields[] = { { INT(gas_address3), 0 }, { INT_RANGE(ftm_responder, 0, 1), 0 }, { INT_RANGE(ftm_initiator, 0, 1), 0 }, + { BOOL(twt_requester), 0 }, { INT(gas_rand_addr_lifetime), 0 }, { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 }, #ifdef CONFIG_DPP @@ -5579,27 +6000,36 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(extended_key_id, 0, 1), 0 }, #endif /* CONFIG_WNM */ { INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0}, + { INT_RANGE(rsn_overriding, 0, 2), 0}, #ifdef CONFIG_PASN + { FUNC(pasn_groups), 0 }, #ifdef CONFIG_TESTING_OPTIONS { INT_RANGE(force_kdk_derivation, 0, 1), 0 }, { INT_RANGE(pasn_corrupt_mic, 0, 1), 0 }, #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ -#ifdef CONFIG_TESTING_OPTIONS { INT_RANGE(mld_force_single_link, 0, 1), 0 }, +#ifdef CONFIG_TESTING_OPTIONS { INT_RANGE(mld_connect_band_pref, 0, MLD_CONNECT_BAND_PREF_MAX), 0 }, { FUNC(mld_connect_bssid_pref), 0 }, #endif /* CONFIG_TESTING_OPTIONS */ - { INT_RANGE(ft_prepend_pmkid, 0, 1), CFG_CHANGED_FT_PREPEND_PMKID }, + { BOOL(ft_prepend_pmkid), CFG_CHANGED_FT_PREPEND_PMKID }, + { INT_RANGE(wfa_gen_capa, 0, 2), 0}, + { BIN(wfa_gen_capa_supp), 0 }, + { BOOL(disable_op_classes_80_80_mhz), 0 }, + { INT(pr_pasn_type), 0 }, + { INT_RANGE(pr_preferred_role, 0, 1), 0}, /* NOTE: When adding new parameters here, add_interface() in * wpa_supplicant/dbus_new_introspect.c may need to be modified to * increase the size of the iface->xml buffer. */ }; #undef FUNC +#undef FUNC_NO_VAR #undef _INT #undef INT #undef INT_RANGE +#undef BOOL #undef _STR #undef STR #undef STR_RANGE @@ -5674,6 +6104,7 @@ const char * wpa_config_get_global_field_name(unsigned int i, int *no_var) * @config: Pointer to global configuration data * @pos: Name and value in the format "{name}={value}" * @line: Line number in configuration file or 0 if not used + * @show_details: Whether to show parsing errors and other details in debug log * Returns: 0 on success with a possible change in value, 1 on success with no * change to previously configured value, or -1 on failure * @@ -5682,7 +6113,8 @@ const char * wpa_config_get_global_field_name(unsigned int i, int *no_var) * parameter must be in the same format as the text-based configuration file is * using. For example, strings are using double quotation marks. */ -int wpa_config_process_global(struct wpa_config *config, char *pos, int line) +int wpa_config_process_global(struct wpa_config *config, char *pos, int line, + bool show_details) { size_t i; int ret = 0; @@ -5696,8 +6128,10 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) ret = field->parser(field, config, line, pos + flen + 1); if (ret < 0) { - wpa_printf(MSG_ERROR, "Line %d: failed to " - "parse '%s'.", line, pos); + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: failed to parse '%s'.", + line, pos); ret = -1; } if (ret == 1) @@ -5713,7 +6147,7 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) char *tmp = os_strchr(pos, '='); if (!tmp) { - if (line < 0) + if (show_details) wpa_printf(MSG_ERROR, "Line %d: invalid line %s", line, pos); @@ -5722,9 +6156,10 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) *tmp++ = '\0'; if (hostapd_config_tx_queue(config->tx_queue, pos, tmp)) { - wpa_printf(MSG_ERROR, - "Line %d: invalid TX queue item", - line); + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: invalid TX queue item", + line); return -1; } return ret; @@ -5735,15 +6170,19 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) if (tmp == NULL) { if (line < 0) return -1; - wpa_printf(MSG_ERROR, "Line %d: invalid line " - "'%s'", line, pos); + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: invalid line '%s'", + line, pos); return -1; } *tmp++ = '\0'; if (hostapd_config_wmm_ac(config->wmm_ac_params, pos, tmp)) { - wpa_printf(MSG_ERROR, "Line %d: invalid WMM " - "AC item", line); + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: invalid WMM AC item", + line); return -1; } return ret; @@ -5751,10 +6190,138 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) #endif /* CONFIG_AP */ if (line < 0) return -1; - wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", - line, pos); + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: unknown global field '%s'.", + line, pos); ret = -1; } return ret; } + + +int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var, + const char *value, int line) +{ + char *val; + size_t len; + int ret = 0; + + if (os_strcmp(var, "dik_cipher") == 0) { + identity->dik_cipher = atoi(value); + return 0; + } + + val = wpa_config_parse_string(value, &len); + if (!val) { + wpa_printf(MSG_ERROR, + "Line %d: invalid field '%s' string value '%s'.", + line, var, value); + return -1; + } + + if (os_strcmp(var, "dik") == 0) { + wpabuf_free(identity->dik); + identity->dik = wpabuf_alloc_copy(val, len); + if (!identity->dik) + ret = -1; + } else if (os_strcmp(var, "pmk") == 0) { + wpabuf_free(identity->pmk); + identity->pmk = wpabuf_alloc_copy(val, len); + if (!identity->pmk) + ret = -1; + } else if (os_strcmp(var, "pmkid") == 0) { + if (len == PMKID_LEN) { + wpabuf_free(identity->pmkid); + identity->pmkid = wpabuf_alloc_copy(val, len); + if (!identity->pmkid) + ret = -1; + } else { + wpa_printf(MSG_ERROR, + "Line %d: invalid field '%s' string value '%s'.", + line, var, value); + ret = -1; + } + } else if (line) { + wpa_printf(MSG_ERROR, "Line %d: unknown identity field '%s'.", + line, var); + ret = -1; + } + + os_free(val); + return ret; +} + + +void wpa_config_free_identity(struct wpa_dev_ik *identity) +{ + wpabuf_clear_free(identity->dik); + wpabuf_clear_free(identity->pmk); + wpabuf_free(identity->pmkid); + os_free(identity); +} + + +/** + * wpa_config_add_identity - Add a new device identity with empty configuration + * @config: Configuration data from wpa_config_read() + * Returns: The new device identity or %NULL if operation failed + */ +struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config) +{ + int id; + struct wpa_dev_ik *identity, *last = NULL; + + id = 0; + identity = config->identity; + while (identity) { + if (identity->id > id) + id = identity->id; + last = identity; + identity = identity->next; + } + id++; + + identity = os_zalloc(sizeof(*identity)); + if (!identity) + return NULL; + identity->id = id; + if (last) + last->next = identity; + else + config->identity = identity; + + return identity; +} + + +/** + * wpa_config_remove_identity - Remove a configured identity based on id + * @config: Configuration data from wpa_config_read() + * @id: Unique network id to search for + * Returns: 0 on success, or -1 if the network was not found + */ +int wpa_config_remove_identity(struct wpa_config *config, int id) +{ + struct wpa_dev_ik *identity, *prev = NULL; + + identity = config->identity; + while (identity) { + if (id == identity->id) + break; + prev = identity; + identity = identity->next; + } + + if (!identity) + return -1; + + if (prev) + prev->next = identity->next; + else + config->identity = identity->next; + + wpa_config_free_identity(identity); + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 8981305c2a7a..6102764b6e59 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -29,7 +29,7 @@ #define DEFAULT_P2P_INTRA_BSS 1 #define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60) #define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0 -#define DEFAULT_BSS_MAX_COUNT 200 +#define DEFAULT_BSS_MAX_COUNT 1000 #define DEFAULT_BSS_EXPIRATION_AGE 180 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 #define DEFAULT_MAX_NUM_STA 128 @@ -417,6 +417,52 @@ struct wpa_cred { int sim_num; }; +struct wpa_dev_ik { + /** + * next - Next device identity in the list + * + * This pointer can be used to iterate over all device identity keys. + * The head of this list is stored in the dev_ik field of struct + * wpa_config. + */ + struct wpa_dev_ik *next; + + /** + * id - Unique id for the identity + * + * This identifier is used as a unique identifier for each identity + * block when using the control interface. Each identity is allocated + * an id when it is being created, either when reading the + * configuration file or when a new identity is added. + */ + int id; + + /** + * dik_cipher - Device identity key cipher version + */ + int dik_cipher; + + /** + * dik - Device identity key which is unique for the device + */ + struct wpabuf *dik; + + /** + * pmk - PMK associated with the previous connection with the device + */ + struct wpabuf *pmk; + + /** + * pmkid - PMKID used in the previous connection with the device + */ + struct wpabuf *pmkid; + + /** + * akmp - AKMP suite used in the previous connection with the device + */ + int akmp; +}; + #define CFG_CHANGED_DEVICE_NAME BIT(0) #define CFG_CHANGED_CONFIG_METHODS BIT(1) @@ -440,6 +486,7 @@ struct wpa_cred { #define CFG_CHANGED_DISABLE_BTM BIT(19) #define CFG_CHANGED_BGSCAN BIT(20) #define CFG_CHANGED_FT_PREPEND_PMKID BIT(21) +#define CFG_CHANGED_P2P_DISABLED BIT(22) /** * struct wpa_config - wpa_supplicant configuration data @@ -858,6 +905,21 @@ struct wpa_config { int p2p_optimize_listen_chan; int p2p_6ghz_disable; + int p2p_bootstrap_methods; + int p2p_pasn_type; + int p2p_comeback_after; + bool p2p_twt_power_mgmt; + bool p2p_chan_switch_req_enable; + int p2p_reg_info; + + /** + * p2p_assisted_dfs_chan_enable - Enable DFS channels for assisted DFS + * + * Enables DFS channels for AP assisted P2P DFS operations when the + * underlying driver provides assisted P2P DFS support. + * By default: 0 (disabled) + */ + bool p2p_assisted_dfs_chan_enable; struct wpabuf *wps_vendor_ext_m1; @@ -1386,15 +1448,6 @@ struct wpa_config { u8 ip_addr_start[4]; u8 ip_addr_end[4]; - /** - * osu_dir - OSU provider information directory - * - * If set, allow FETCH_OSU control interface command to be used to fetch - * OSU provider information into all APs and store the results in this - * directory. - */ - char *osu_dir; - /** * wowlan_triggers - Wake-on-WLAN triggers * @@ -1774,7 +1827,27 @@ struct wpa_config { */ int wowlan_disconnect_on_deinit; + /** + * rsn_overriding - RSN overriding (default behavior) + */ + enum wpas_rsn_overriding rsn_overriding; + #ifdef CONFIG_PASN + /** + * pasn_groups - Preference list of enabled groups for PASN + * + * As per IEEE Std 802.11-2024, 12.13.1, both STAs must have at least + * one cyclic group in common from the dot11RSNAConfigDLCGroupTable for + * ephemeral key exchange. For interoperability, a STA shall support + * group 19 (ECC group defined over a 256-bit prime order field), which + * is used as the default if this parameter is not set. Only suitable + * ECC groups (e.g., 19, 20, 21) are accepted. + * + * If a per-network parameter is configured, it is used. If not, this + * global parameter is used. If neither is set, group 19 is used. + */ + int *pasn_groups; + #ifdef CONFIG_TESTING_OPTIONS /* * Normally, KDK should be derived if and only if both sides support @@ -1798,9 +1871,74 @@ struct wpa_config { } mld_connect_band_pref; u8 mld_connect_bssid_pref[ETH_ALEN]; +#endif /* CONFIG_TESTING_OPTIONS */ int mld_force_single_link; -#endif /* CONFIG_TESTING_OPTIONS */ + + /* Cipher version type */ + int dik_cipher; + + /* DevIK */ + struct wpabuf *dik; + + /** + * identity - P2P2 peer device identities + * + * This is the head for the list of all the paired devices. + */ + struct wpa_dev_ik *identity; + + /** + * twt_requester - Whether TWT Requester Support is enabled + * + * This is for setting the bit 77 of the Extended Capabilities element. + */ + bool twt_requester; + + /** + * wfa_gen_capa: Whether to indicate Wi-Fi generational capability to + * the AP + * + * 0 = do not indicate (default) + * 1 = indicate in protected Action frame + * 2 = indicate in unprotected (Re)Association Request frame + */ + enum { + WFA_GEN_CAPA_DISABLED = 0, + WFA_GEN_CAPA_PROTECTED = 1, + WFA_GEN_CAPA_UNPROTECTED = 2, + } wfa_gen_capa; + + /** + * wfa_gen_capa_supp: Supported Generations (hexdump of a bit field) + * + * A bit field of supported Wi-Fi generations. This is encoded as an + * little endian octt string. + * bit 0: Wi-Fi 4 + * bit 1: Wi-Fi 5 + * bit 2: Wi-Fi 6 + * bit 3: Wi-Fi 7 + */ + struct wpabuf *wfa_gen_capa_supp; + + /** + * disable_op_classes_80_80_mhz - Disable advertisement of 80+80 MHz + * channel capabilities in the Supported Operating Classes element + * + * By default, %wpa_supplicant tries to advertise 80+80 MHz channel + * capabilities in the Supported Operating Classes element if the driver + * supports this. + */ + bool disable_op_classes_80_80_mhz; + + /* Indicates the types of PASN supported for Proximity Ranging */ + int pr_pasn_type; + + /* Indicates the preferred Proximity Ranging Role + * 0: Prefer ranging initiator role (default) + * 1: Prefer ranging responder role + */ + int pr_preferred_role; }; @@ -1812,6 +1950,8 @@ void wpa_config_foreach_network(struct wpa_config *config, void (*func)(void *, struct wpa_ssid *), void *arg); struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id); +struct wpa_ssid * wpa_config_get_network_with_dik_id(struct wpa_config *config, + int dik_id); struct wpa_ssid * wpa_config_add_network(struct wpa_config *config); int wpa_config_remove_network(struct wpa_config *config, int id); void wpa_config_set_network_defaults(struct wpa_ssid *ssid); @@ -1847,6 +1987,12 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, const char *value, int line); char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var); +int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var, + const char *value, int line); +void wpa_config_free_identity(struct wpa_dev_ik *identity); +struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config); +int wpa_config_remove_identity(struct wpa_config *config, int id); + struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param); #ifndef CONFIG_NO_STDOUT_DEBUG @@ -1857,7 +2003,8 @@ void wpa_config_debug_dump_networks(struct wpa_config *config); /* Prototypes for common functions from config.c */ -int wpa_config_process_global(struct wpa_config *config, char *pos, int line); +int wpa_config_process_global(struct wpa_config *config, char *pos, int line, + bool show_details); int wpa_config_get_num_global_field_names(void); @@ -1871,6 +2018,7 @@ const char * wpa_config_get_global_field_name(unsigned int i, int *no_var); * configuration file) * @cfgp: Pointer to previously allocated configuration data or %NULL if none * @ro: Whether to mark networks from this configuration as read-only + * @show_details: Whether to show parsing errors and other details in debug log * Returns: Pointer to allocated configuration data or %NULL on failure * * This function reads configuration data, parses its contents, and allocates @@ -1880,7 +2028,7 @@ const char * wpa_config_get_global_field_name(unsigned int i, int *no_var); * Each configuration backend needs to implement this function. */ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, - bool ro); + bool ro, bool show_details); /** * wpa_config_write - Write or update configuration data diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index ad37bcf22f33..96facc3818b2 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -11,9 +11,7 @@ */ #include "includes.h" -#ifdef ANDROID #include -#endif /* ANDROID */ #include "common.h" #include "config.h" @@ -296,17 +294,79 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f, #endif /* CONFIG_NO_CONFIG_BLOBS */ +static struct wpa_dev_ik * wpa_config_read_identity(FILE *f, int *line, int id) +{ + struct wpa_dev_ik *identity; + int errors = 0, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new identity block", + *line); + identity = os_zalloc(sizeof(*identity)); + if (!identity) + return NULL; + identity->id = id; + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = os_strchr(pos, '='); + if (!pos2) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid identity line '%s'.", + *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (os_strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, + "Line %d: invalid quotation '%s'.", + *line, pos2); + errors++; + continue; + } + } + + if (wpa_config_set_identity(identity, pos, pos2, *line) < 0) + errors++; + } + + if (!end) { + wpa_printf(MSG_ERROR, + "Line %d: identity block was not terminated properly.", + *line); + errors++; + } + + if (errors) { + wpa_config_free_identity(identity); + identity = NULL; + } + + return identity; +} + + struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, - bool ro) + bool ro, bool show_details) { FILE *f; - char buf[512], *pos; + char buf[1024], *pos; int errors = 0, line = 0; struct wpa_ssid *ssid, *tail, *head; struct wpa_cred *cred, *cred_tail, *cred_head; + struct wpa_dev_ik *identity, *identity_tail, *identity_head; struct wpa_config *config; static int id = 0; static int cred_id = 0; + static int base_identity_id = 0; + int identity_id = base_identity_id; if (name == NULL) return NULL; @@ -325,18 +385,28 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, cred_tail = cred_head = config->cred; while (cred_tail && cred_tail->next) cred_tail = cred_tail->next; + identity_tail = identity_head = config->identity; + while (identity_tail && identity_tail->next) + identity_tail = identity_tail->next; wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); if (f == NULL) { - wpa_printf(MSG_ERROR, "Failed to open config file '%s', " - "error: %s", name, strerror(errno)); + if (show_details) + wpa_printf(MSG_ERROR, + "Failed to open config file '%s', error: %s", + name, strerror(errno)); if (config != cfgp) os_free(config); return NULL; } while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) { +#ifndef WPA_IGNORE_CONFIG_ERRORS + if (errors && !show_details) + break; +#endif /* WPA_IGNORE_CONFIG_ERRORS */ + if (os_strcmp(pos, "network={") == 0) { ssid = wpa_config_read_network(f, &line, id++); if (ssid == NULL) { @@ -345,6 +415,7 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, errors++; continue; } + ssid->go_dik_id += base_identity_id; ssid->ro = ro; if (head == NULL) { head = tail = ssid; @@ -383,9 +454,28 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, continue; } #endif /* CONFIG_NO_CONFIG_BLOBS */ - } else if (wpa_config_process_global(config, pos, line) < 0) { - wpa_printf(MSG_ERROR, "Line %d: Invalid configuration " - "line '%s'.", line, pos); + } else if (os_strcmp(pos, "identity={") == 0) { + identity = wpa_config_read_identity(f, &line, + ++identity_id); + if (!identity) { + wpa_printf(MSG_ERROR, + "Line %d: failed to parse identity block.", + line); + errors++; + continue; + } + if (!identity_head) { + identity_head = identity_tail = identity; + } else { + identity_tail->next = identity; + identity_tail = identity; + } + } else if (wpa_config_process_global(config, pos, line, + show_details) < 0) { + if (show_details) + wpa_printf(MSG_ERROR, + "Line %d: Invalid configuration line '%s'.", + line, pos); errors++; continue; } @@ -396,6 +486,9 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, config->ssid = head; wpa_config_debug_dump_networks(config); config->cred = cred_head; + config->identity = identity_head; + + base_identity_id = identity_id; #ifndef WPA_IGNORE_CONFIG_ERRORS if (errors) { @@ -617,6 +710,17 @@ static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) } +static void write_p2p2_client_list(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "p2p2_client_list"); + + if (!value) + return; + fprintf(f, "\tp2p2_client_list=%s\n", value); + os_free(value); +} + + static void write_psk_list(FILE *f, struct wpa_ssid *ssid) { struct psk_list_entry *psk; @@ -666,7 +770,8 @@ static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid) #endif /* CONFIG_MACSEC */ -static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) +static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid, + struct wpa_config *config) { #define STR(t) write_str(f, #t, ssid) #define INT(t) write_int(f, #t, ssid->t, 0) @@ -685,6 +790,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(sae_password); STR(sae_password_id); write_int(f, "sae_pwe", ssid->sae_pwe, DEFAULT_SAE_PWE); + INT(sae_password_id_change); write_proto(f, ssid); write_key_mgmt(f, ssid); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); @@ -695,6 +801,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(bgscan); STR(autoscan); STR(scan_freq); + STR(freq_list); #ifdef IEEE8021X_EAPOL write_eap(f, ssid); STR(identity); @@ -774,8 +881,10 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEFe(sim_num, sim_num, DEFAULT_USER_SELECTED_SIM); #endif /* IEEE8021X_EAPOL */ INT(mode); +#ifdef CONFIG_MESH INT(no_auto_peer); - INT(mesh_fwding); + INT_DEF(mesh_fwding, DEFAULT_MESH_FWDING); +#endif /* CONFIG_MESH */ INT(frequency); INT(enable_edmg); INT(edmg_channel); @@ -802,7 +911,22 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) #ifdef CONFIG_P2P write_go_p2p_dev_addr(f, ssid); write_p2p_client_list(f, ssid); + write_p2p2_client_list(f, ssid); write_psk_list(f, ssid); + { + struct wpa_dev_ik *dev_ik; + int i = 1, go_dik_id = 0; + + for (dev_ik = config->identity; + dev_ik; + dev_ik = dev_ik->next, i++) { + if (dev_ik->id == ssid->go_dik_id) { + go_dik_id = i; + break; + } + } + write_int(f, "go_dik_id", go_dik_id, 0); + } #endif /* CONFIG_P2P */ INT(ap_max_inactivity); INT(dtim_period); @@ -818,6 +942,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(macsec_port); INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER); INT(macsec_csindex); + INT(macsec_icv_indicator); #endif /* CONFIG_MACSEC */ #ifdef CONFIG_HS20 INT(update_identifier); @@ -895,7 +1020,29 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(enable_4addr_mode); INT(max_idle); INT(ssid_protection); + INT_DEF(rsn_overriding, RSN_OVERRIDING_NOT_SET); +#ifdef CONFIG_SAE + if (ssid->alt_sae_password_ids) { + struct wpabuf_array *ids = ssid->alt_sae_password_ids; + unsigned int idx; + char hex[255 * 2 + 1]; + for (idx = 0; idx < ids->num; idx++) { + wpa_snprintf_hex(hex, sizeof(hex), + wpabuf_head(ids->buf[idx]), + wpabuf_len(ids->buf[idx])); + fprintf(f, "\talt_sae_password_ids=%s\n", hex); + } + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_PMKSA_PRIVACY + INT(pmksa_privacy); +#endif /* CONFIG_PMKSA_PRIVACY */ +#ifdef CONFIG_IEEE8021X_AUTH + INT(eap_over_auth_frame); +#endif /* CONFIG_IEEE8021X_AUTH */ + INT_DEF(drop_unicast_ip_in_l2_multicast, 1); + INT_DEF(always_use_proxy_arp, 0); #undef STR #undef INT #undef INT_DEF @@ -1295,6 +1442,26 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->p2p_6ghz_disable) fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable); + if (config->p2p_bootstrap_methods) + fprintf(f, "p2p_bootstrap_methods=%d\n", + config->p2p_bootstrap_methods); + if (config->p2p_pasn_type) + fprintf(f, "p2p_pasn_type=%d\n", config->p2p_pasn_type); + if (config->p2p_comeback_after) + fprintf(f, "p2p_comeback_after=%d\n", + config->p2p_comeback_after); + if (config->p2p_twt_power_mgmt) + fprintf(f, "p2p_twt_power_mgmt=%d\n", + config->p2p_twt_power_mgmt); + if (config->p2p_chan_switch_req_enable) + fprintf(f, "p2p_chan_switch_req_enable=%d\n", + config->p2p_chan_switch_req_enable); + if (config->p2p_reg_info) + fprintf(f, "p2p_reg_info=%d\n", config->p2p_reg_info); + if (config->p2p_assisted_dfs_chan_enable) + fprintf(f, "p2p_assisted_dfs_chan_enable=%d\n", + config->p2p_assisted_dfs_chan_enable); + if (WPA_GET_BE32(config->ip_addr_go)) fprintf(f, "ip_addr_go=%u.%u.%u.%u\n", config->ip_addr_go[0], config->ip_addr_go[1], @@ -1564,8 +1731,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->ftm_initiator) fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator); - if (config->osu_dir) - fprintf(f, "osu_dir=%s\n", config->osu_dir); + if (config->twt_requester) + fprintf(f, "twt_requester=%d\n", config->twt_requester); if (config->fst_group_id) fprintf(f, "fst_group_id=%s\n", config->fst_group_id); @@ -1615,9 +1782,11 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wowlan_disconnect_on_deinit) fprintf(f, "wowlan_disconnect_on_deinit=%d\n", config->wowlan_disconnect_on_deinit); -#ifdef CONFIG_TESTING_OPTIONS + if (config->rsn_overriding) + fprintf(f, "rsn_overriding=%d\n", config->rsn_overriding); if (config->mld_force_single_link) fprintf(f, "mld_force_single_link=1\n"); +#ifdef CONFIG_TESTING_OPTIONS if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO) fprintf(f, "mld_connect_band_pref=%d\n", config->mld_connect_band_pref); @@ -1626,7 +1795,48 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) MAC2STR(config->mld_connect_bssid_pref)); #endif /* CONFIG_TESTING_OPTIONS */ if (config->ft_prepend_pmkid) - fprintf(f, "ft_prepend_pmkid=%d", config->ft_prepend_pmkid); + fprintf(f, "ft_prepend_pmkid=%d\n", config->ft_prepend_pmkid); + if (config->dik) { + fprintf(f, "dik_cipher=%d\n", config->dik_cipher); + write_global_bin(f, "dik", config->dik); + } + if (config->wfa_gen_capa) + fprintf(f, "wfa_gen_capa=%d\n", config->wfa_gen_capa); + write_global_bin(f, "wfa_gen_capa_supp", config->wfa_gen_capa_supp); + if (config->disable_op_classes_80_80_mhz) + fprintf(f, "disable_op_classes_80_80_mhz=%d\n", + config->disable_op_classes_80_80_mhz); + if (config->pr_pasn_type) + fprintf(f, "pr_pasn_type=%d\n", config->pr_pasn_type); + if (config->pr_preferred_role) + fprintf(f, "pr_preferred_role=%d\n", config->pr_preferred_role); + +#ifdef CONFIG_PASN + if (config->pasn_groups) { + int i; + + fprintf(f, "pasn_groups="); + for (i = 0; config->pasn_groups[i] > 0; i++) { + fprintf(f, "%s%d", i > 0 ? " " : "", + config->pasn_groups[i]); + } + fprintf(f, "\n"); + } +#endif /* CONFIG_PASN */ +} + +static void wpa_config_write_identity(FILE *f, struct wpa_dev_ik *dev_ik) +{ + fprintf(f, "\tdik_cipher=%d\n", dev_ik->dik_cipher); + + if (dev_ik->dik) + write_global_bin(f, "\tdik", dev_ik->dik); + + if (dev_ik->pmk) + write_global_bin(f, "\tpmk", dev_ik->pmk); + + if (dev_ik->pmkid) + write_global_bin(f, "\tpmkid", dev_ik->pmkid); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -1638,35 +1848,54 @@ int wpa_config_write(const char *name, struct wpa_config *config) FILE *f; struct wpa_ssid *ssid; struct wpa_cred *cred; + struct wpa_dev_ik *dev_ik; #ifndef CONFIG_NO_CONFIG_BLOBS struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ int ret = 0; const char *orig_name = name; - int tmp_len; + size_t tmp_len, prefix_len, suffix_len; char *tmp_name; + struct stat file_stat; + mode_t prev; + u8 suffix[8]; if (!name) { wpa_printf(MSG_ERROR, "No configuration file for writing"); return -1; } - tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */ + if (os_get_random(suffix, sizeof(suffix)) < 0) + return -1; + + prefix_len = os_strlen(name) + 5; + suffix_len = 2 * sizeof(suffix); + tmp_len = prefix_len + suffix_len + 1; tmp_name = os_malloc(tmp_len); - if (tmp_name) { - os_snprintf(tmp_name, tmp_len, "%s.tmp", name); - name = tmp_name; - } + if (!tmp_name) + return -1; - wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + os_snprintf(tmp_name, tmp_len, "%s.tmp-", name); + wpa_snprintf_hex(tmp_name + prefix_len, suffix_len + 1, + suffix, sizeof(suffix)); + name = tmp_name; + prev = umask(S_IRWXG | S_IRWXO); f = fopen(name, "w"); + umask(prev); if (f == NULL) { wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); os_free(tmp_name); return -1; } + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + + if (stat(orig_name, &file_stat) == 0) + chmod(name, + (file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | + S_IRUSR | S_IWUSR); + wpa_config_write_global(f, config); for (cred = config->cred; cred; cred = cred->next) { @@ -1685,10 +1914,17 @@ int wpa_config_write(const char *name, struct wpa_config *config) !ssid->psk_set && !ssid->passphrase) continue; /* do not save invalid network */ if (wpa_key_mgmt_sae(ssid->key_mgmt) && - !ssid->passphrase && !ssid->sae_password) + !ssid->passphrase && !ssid->sae_password && + !ssid->pmk_valid) continue; /* do not save invalid network */ fprintf(f, "\nnetwork={\n"); - wpa_config_write_network(f, ssid); + wpa_config_write_network(f, ssid, config); + fprintf(f, "}\n"); + } + + for (dev_ik = config->identity; dev_ik; dev_ik = dev_ik->next) { + fprintf(f, "\nidentity={\n"); + wpa_config_write_identity(f, dev_ik); fprintf(f, "}\n"); } @@ -1704,18 +1940,16 @@ int wpa_config_write(const char *name, struct wpa_config *config) fclose(f); - if (tmp_name) { - int chmod_ret = 0; - #ifdef ANDROID - chmod_ret = chmod(tmp_name, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (!ret) + ret = chmod(tmp_name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); #endif /* ANDROID */ - if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0) - ret = -1; + if (!ret) + ret = rename(tmp_name, orig_name); + if (ret) + unlink(tmp_name); - os_free(tmp_name); - } + os_free(tmp_name); wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", orig_name, ret ? "un" : ""); diff --git a/contrib/wpa/wpa_supplicant/config_none.c b/contrib/wpa/wpa_supplicant/config_none.c index 01e7aad44bd9..ed1d8690292f 100644 --- a/contrib/wpa/wpa_supplicant/config_none.c +++ b/contrib/wpa/wpa_supplicant/config_none.c @@ -18,7 +18,7 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, - bool ro) + bool ro, bool show_details) { struct wpa_config *config; diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index d64c305082c1..7c60d4e38cab 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -79,6 +79,20 @@ enum wpas_mac_addr_style { WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS = 3, }; +/** + * rsn_overriding - RSN overriding + * + * 0 = Disabled + * 1 = Enabled automatically if the driver indicates support + * 2 = Forced to be enabled even without driver capability indication + */ +enum wpas_rsn_overriding { + RSN_OVERRIDING_NOT_SET = -1, + RSN_OVERRIDING_DISABLED = 0, + RSN_OVERRIDING_AUTO = 1, + RSN_OVERRIDING_ENABLED = 2, +}; + /** * struct wpa_ssid - Network configuration data * @@ -232,6 +246,11 @@ struct wpa_ssid { */ char *passphrase; + /** + * pmk_valid - Whether PMK is valid in case of P2P2 derived from PASN + */ + bool pmk_valid; + /** * sae_password - SAE password * @@ -250,6 +269,10 @@ struct wpa_ssid { */ char *sae_password_id; + struct wpabuf_array *alt_sae_password_ids; + unsigned int alt_sae_passwords_ids_idx; + bool alt_sae_passwords_ids_used; + struct sae_pt *pt; /** @@ -677,6 +700,15 @@ struct wpa_ssid { */ size_t num_p2p_clients; + /** + * p2p2_client_list - Array of P2P2 Clients in a persistent group (GO) + * + * This is an int_array of P2P2 Clients (ID of device Identity block) + * that have joined the persistent group. This is maintained on the GO + *for persistent group entries (disabled == 2). + */ + int *p2p2_client_list; + #ifndef P2P_MAX_STORED_CLIENTS #define P2P_MAX_STORED_CLIENTS 100 #endif /* P2P_MAX_STORED_CLIENTS */ @@ -963,6 +995,14 @@ struct wpa_ssid { */ int macsec_csindex; + /** + * macsec_icv_indicator - Always include ICV Indicator + * (for compatibility with older MACsec switches) + * + * Range: 0-1 (default: 0) + */ + int macsec_icv_indicator; + /** * mka_ckn - MKA pre-shared CKN */ @@ -1283,6 +1323,81 @@ struct wpa_ssid { * ssid_protection - Whether to use SSID protection in 4-way handshake */ bool ssid_protection; + + /** + * rsn_overriding - RSN overriding (per-network override for the global + * parameter with the same name) + */ + enum wpas_rsn_overriding rsn_overriding; + + /** + * p2p_mode - P2P R1 only, P2P R2 only, or PCC mode + */ + enum wpa_p2p_mode p2p_mode; + + /** + * go_dik_id - ID of Device Identity block of group owner + */ + int go_dik_id; + + /** + * sae_password_id_change - Whether to use changing SAE password IDs + */ + bool sae_password_id_change; + + /** + * pmksa_privacy - Enable/disable PMKSA caching privacy + * 0 = PMKSA caching privacy enabled + * 1 = PMKSA caching privacy disabled + * + * When enabled, a unique PMKID will be generated for each subsequent + * connection even if the same cached PMKSA is reused. + */ + int pmksa_privacy; + +#ifdef CONFIG_IEEE8021X_AUTH + /** + * eap_over_auth_frame - IEEE 802.1X authentication in Authentication + * frames + * 0 = Disabled (i.e., use EAP in EAPOL frames) (default) + * 1 = Enabled (i.e., use EAP in Authentication frames and encrypt + * associations frames) + */ + int eap_over_auth_frame; +#endif /* CONFIG_IEEE8021X_AUTH */ + + /** + * drop_unicast_ip_in_l2_multicast - Drop unicast IP packets in L2 + * multicast frames in all networks, not just Passpoint non-DGAF + * networks + */ + bool drop_unicast_ip_in_l2_multicast; + + /** + * always_use_proxy_arp - Always rely on proxy ARP even in non-Passpoint + * networks + * 0 = disabled + * 1 = check AP's extended capabilities + * 2 = always enable + */ + int always_use_proxy_arp; + +#ifdef CONFIG_PASN + /** + * pasn_groups - Preference list of enabled groups for PASN + * + * As per IEEE Std 802.11-2024, 12.13.1, both STAs must have at least + * one cyclic group in common from the dot11RSNAConfigDLCGroupTable for + * ephemeral key exchange. For interoperability, a STA shall support + * group 19 (ECC group defined over a 256-bit prime order field), which + * is used as the default if this parameter is not set. Only suitable + * ECC groups (e.g., 19, 20, 21) are accepted. + * + * If this per-network parameter is not configured, the global + * pasn_groups parameter is used. If neither is set, group 19 is used. + */ + int *pasn_groups; +#endif /* CONFIG_PASN */ }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/config_winreg.c b/contrib/wpa/wpa_supplicant/config_winreg.c index 05e6e37944bb..67d4a3201b17 100644 --- a/contrib/wpa/wpa_supplicant/config_winreg.c +++ b/contrib/wpa/wpa_supplicant/config_winreg.c @@ -447,7 +447,7 @@ static int wpa_config_read_networks(struct wpa_config *config, HKEY hk) struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp, - bool ro) + bool ro, bool show_details) { TCHAR buf[256]; int errors = 0; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index bcbcb258f76f..7787225204e2 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -15,6 +15,7 @@ #include "utils/eloop.h" #include "utils/uuid.h" #include "utils/module_tests.h" +#include "utils/trace.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -24,6 +25,7 @@ #endif /* CONFIG_DPP */ #include "common/nan_de.h" #include "common/ptksa_cache.h" +#include "common/proximity_ranging.h" #include "crypto/tls.h" #include "ap/hostapd.h" #include "eap_peer/eap.h" @@ -59,7 +61,8 @@ #include "mesh.h" #include "dpp_supplicant.h" #include "sme.h" -#include "nan_usd.h" +#include "pr_supplicant.h" +#include "nan_supplicant.h" #ifdef __NetBSD__ #include @@ -534,7 +537,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = -1; } else { value[-1] = '='; - wpa_config_process_global(wpa_s->conf, cmd, -1); + wpa_config_process_global(wpa_s->conf, cmd, -1, + true); } } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == 0) { @@ -543,7 +547,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = -1; } else { value[-1] = '='; - wpa_config_process_global(wpa_s->conf, cmd, -1); + wpa_config_process_global(wpa_s->conf, cmd, -1, + true); } } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, @@ -551,7 +556,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = -1; } else { value[-1] = '='; - wpa_config_process_global(wpa_s->conf, cmd, -1); + wpa_config_process_global(wpa_s->conf, cmd, -1, + true); } } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { wpa_s->wps_fragment_size = atoi(value); @@ -825,6 +831,23 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_s->rsnxe_override_eapol = NULL; else wpa_s->rsnxe_override_eapol = wpabuf_parse_bin(value); + } else if (os_strcasecmp(cmd, "link_ies") == 0) { + int link_id = atoi(value); + char *pos; + + if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS) + return -1; + + pos = os_strchr(value, ':'); + if (!pos) + return -1; + pos++; + + wpabuf_free(wpa_s->link_ies[link_id]); + if (os_strcmp(value, "NULL") == 0) + wpa_s->link_ies[link_id] = NULL; + else + wpa_s->link_ies[link_id] = wpabuf_parse_bin(pos); } else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) { wpa_s->reject_btm_req_reason = atoi(value); } else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) { @@ -881,6 +904,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "dpp_test") == 0) { dpp_test = atoi(value); #endif /* CONFIG_DPP */ + } else if (os_strcasecmp(cmd, "eapol_2_key_info_set_mask") == 0) { + wpa_s->eapol_2_key_info_set_mask = strtoul(value, NULL, 0x10); #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_FILS } else if (os_strcasecmp(cmd, "disable_fils") == 0) { @@ -899,7 +924,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = wpas_mbo_update_non_pref_chan(wpa_s, value); if (ret == 0) { value[-1] = '='; - wpa_config_process_global(wpa_s->conf, cmd, -1); + wpa_config_process_global(wpa_s->conf, cmd, -1, + true); } } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { int val = atoi(value); @@ -945,6 +971,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "ric_ies") == 0) { ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value); } else if (os_strcasecmp(cmd, "roaming") == 0) { + wpa_s->sta_roaming_disabled = atoi(value) ? false : true; ret = wpa_drv_roaming(wpa_s, atoi(value), NULL); #ifdef CONFIG_WNM } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) { @@ -959,9 +986,17 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "enable_dscp_policy_capa") == 0) { wpa_s->enable_dscp_policy_capa = !!atoi(value); #endif /* CONFIG_NO_ROBUST_AV */ +#ifdef CONFIG_PASN + } else if (os_strcasecmp(cmd, "urnm_mfpr") == 0) { + wpa_s->disable_urnm_mfpr = !atoi(value); + } else if (os_strcasecmp(cmd, "urnm_mfpr_x20") == 0) { + wpa_s->urnm_mfpr_x20 = !!atoi(value); +#endif /* CONFIG_PASN */ } else { value[-1] = '='; - ret = wpa_config_process_global(wpa_s->conf, cmd, -1); + ret = wpa_config_process_global( + wpa_s->conf, cmd, -1, + wpa_s->global->params.show_details); if (ret == 0) wpa_supplicant_update_config(wpa_s); else if (ret == 1) @@ -2354,6 +2389,20 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, return pos - buf; pos += ret; } + if (wpa_s->connection_set) { + ret = os_snprintf( + pos, end - pos, + "max_nss_rx=%u\n" + "max_nss_tx=%u\n" + "channel_width=%u\n", + wpa_s->connection_max_nss_rx, + wpa_s->connection_max_nss_tx, + channel_width_to_int( + wpa_s->connection_channel_bandwidth)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -3012,14 +3061,6 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, } #endif /* CONFIG_DPP */ - if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { - ret = os_snprintf(pos, end - pos, "%sOSEN", - pos == start ? "" : "+"); - if (os_snprintf_error(end - pos, ret)) - return pos; - pos += ret; - } - #ifdef CONFIG_SHA384 if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) { ret = os_snprintf(pos, end - pos, "%sEAP-SHA384", @@ -3097,7 +3138,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe; + const u8 *ie, *ie2, *p2p, *mesh, *owe, *rsnxe; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); @@ -3119,12 +3160,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); - ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + ie2 = wpa_bss_get_rsne(wpa_s, bss, NULL, false); if (ie2) { pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); } - rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) { ret = os_snprintf(pos, end - pos, "[SAE-H2E]"); if (os_snprintf_error(end - pos, ret)) @@ -3137,10 +3178,6 @@ static int wpa_supplicant_ctrl_iface_scan_result( return -1; pos += ret; } - osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); - if (osen_ie) - pos = wpa_supplicant_ie_txt(pos, end, "OSEN", - osen_ie, 2 + osen_ie[1]); owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (owe) { ret = os_snprintf(pos, end - pos, @@ -3150,7 +3187,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { + if (!ie && !ie2 && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) return -1; @@ -4248,6 +4285,9 @@ static int iftype_str_to_index(const char *iftype_str) if (os_strcmp(iftype_str, "NAN") == 0) return WPA_IF_NAN; + if (os_strcmp(iftype_str, "NAN_DATA") == 0) + return WPA_IF_NAN_DATA; + return WPA_IF_MAX; } @@ -4460,14 +4500,6 @@ static int ctrl_iface_get_capability_key_mgmt(int res, bool strict, pos += ret; } #endif /* CONFIG_SHA256 */ -#ifdef CONFIG_HS20 - if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) { - ret = os_snprintf(pos, end - pos, " OSEN"); - if (os_snprintf_error(end - pos, ret)) - return pos - buf; - pos += ret; - } -#endif /* CONFIG_HS20 */ return pos - buf; } @@ -4599,6 +4631,19 @@ static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, #endif /* CONFIG_PASN */ +#if defined(CONFIG_ENC_ASSOC) && defined(CONFIG_PASN) && defined(CONFIG_SAE) + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) && + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE) && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) { + ret = os_snprintf(pos, end - pos, "%sEPPKE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_ENC_ASSOC && CONFIG_PASN && CONFIG_SAE */ + return pos - buf; } @@ -4912,14 +4957,30 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD if (os_strcmp(field, "nan") == 0) { - res = os_snprintf(buf, buflen, "USD"); + char *pos = buf; + +#ifdef CONFIG_NAN_USD + res = os_snprintf(pos, buflen, "USD"); if (os_snprintf_error(buflen, res)) return -1; - return res; - } + + pos += res; + buflen -= res; #endif /* CONFIG_NAN_USD */ +#ifdef CONFIG_NAN + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SUPPORT_NAN)) { + res = os_snprintf(pos, buflen, "%sNAN", + pos == buf ? "" : " "); + if (os_snprintf_error(buflen, res)) + return -1; + + pos += res; + buflen -= res; + } +#endif /* CONFIG_NAN */ + return pos - buf; + } #ifdef CONFIG_SAE if (os_strcmp(field, "sae") == 0 && @@ -4960,6 +5021,28 @@ static int wpa_supplicant_ctrl_iface_get_capability( return res; } +#ifdef CONFIG_P2P + if (os_strcmp(field, "p2p2") == 0) { + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_V2) + res = os_snprintf(buf, buflen, "supported"); + else + res = os_snprintf(buf, buflen, "not supported"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } + + if (os_strcmp(field, "pcc_mode") == 0) { + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE) + res = os_snprintf(buf, buflen, "supported"); + else + res = os_snprintf(buf, buflen, "not supported"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_P2P */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -5074,17 +5157,13 @@ static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end) #endif /* CONFIG_FILS */ -static int print_rnr(struct wpa_bss *bss, char *pos, char *end) +static int print_rnr(const u8 *ie, char *pos, char *end) { char *start = pos; - const u8 *ie, *ie_end; + const u8 *ie_end; unsigned int n = 0; int ret; - ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT); - if (!ie) - return 0; - ie_end = ie + 2 + ie[1]; ie += 2; @@ -5093,93 +5172,102 @@ static int print_rnr(struct wpa_bss *bss, char *pos, char *end) (const struct ieee80211_neighbor_ap_info *) ie; const u8 *tbtt_start; size_t left = ie_end - ie; + unsigned int count; + unsigned int i; if (left < sizeof(struct ieee80211_neighbor_ap_info)) return 0; left -= sizeof(struct ieee80211_neighbor_ap_info); - if (left < info->tbtt_info_len) + count = RNR_TBTT_INFO_COUNT_VAL(info->tbtt_info_hdr) + 1; + if (left < count * info->tbtt_info_len) return 0; - ret = os_snprintf(pos, end - pos, - "ap_info[%u]: tbtt_info: hdr=0x%x, len=%u, op_c=%u, channel=%u, ", - n, *ie, info->tbtt_info_len, - info->op_class, info->channel); - if (os_snprintf_error(end - pos, ret)) - return 0; - pos += ret; - ie += sizeof(struct ieee80211_neighbor_ap_info); tbtt_start = ie; - if (info->tbtt_info_len >= 1) { + + for (i = 0; i < count; i++) { + ie = tbtt_start + i * info->tbtt_info_len; + ret = os_snprintf(pos, end - pos, - "tbtt_offset=%u, ", *ie); + "ap_info[%u]: tbtt_info[%u]: hdr=0x%x, len=%u, op_c=%u, channel=%u, ", + n, i, info->tbtt_info_hdr, + info->tbtt_info_len, + info->op_class, info->channel); if (os_snprintf_error(end - pos, ret)) return 0; + pos += ret; - ie++; + if (info->tbtt_info_len >= 1) { + ret = os_snprintf(pos, end - pos, + "tbtt_offset=%u, ", *ie); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie++; + pos += ret; + } + + if (info->tbtt_info_len >= 7) { + ret = os_snprintf(pos, end - pos, + "bssid=" MACSTR ", ", + MAC2STR(ie)); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie += ETH_ALEN; + pos += ret; + } + + if (info->tbtt_info_len >= 11) { + ret = os_snprintf(pos, end - pos, + "short SSID=0x%x, ", + WPA_GET_LE32(ie)); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie += 4; + pos += ret; + } + + if (info->tbtt_info_len >= 12) { + ret = os_snprintf(pos, end - pos, + "bss_params=0x%x, ", *ie); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie++; + pos += ret; + } + + if (info->tbtt_info_len >= 13) { + ret = os_snprintf(pos, end - pos, + "PSD=0x%x, ", *ie); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie++; + pos += ret; + } + + if (info->tbtt_info_len >= 16) { + ret = os_snprintf(pos, end - pos, + "mld ID=%u, link ID=%u", + *ie, *(ie + 1) & 0xF); + if (os_snprintf_error(end - pos, ret)) + return 0; + + ie += 3; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return 0; pos += ret; } - if (info->tbtt_info_len >= 7) { - ret = os_snprintf(pos, end - pos, - "bssid=" MACSTR ", ", - MAC2STR(ie)); - if (os_snprintf_error(end - pos, ret)) - return 0; - - ie += ETH_ALEN; - pos += ret; - } - - if (info->tbtt_info_len >= 11) { - ret = os_snprintf(pos, end - pos, - "short SSID=0x%x, ", - WPA_GET_LE32(ie)); - if (os_snprintf_error(end - pos, ret)) - return 0; - - ie += 4; - pos += ret; - } - - if (info->tbtt_info_len >= 12) { - ret = os_snprintf(pos, end - pos, - "bss_params=0x%x, ", *ie); - if (os_snprintf_error(end - pos, ret)) - return 0; - - ie++; - pos += ret; - } - - if (info->tbtt_info_len >= 13) { - ret = os_snprintf(pos, end - pos, - "PSD=0x%x, ", *ie); - if (os_snprintf_error(end - pos, ret)) - return 0; - - ie++; - pos += ret; - } - - if (info->tbtt_info_len >= 16) { - ret = os_snprintf(pos, end - pos, - "mld ID=%u, link ID=%u", - *ie, *(ie + 1) & 0xF); - if (os_snprintf_error(end - pos, ret)) - return 0; - - ie += 3; - pos += ret; - } - - ie = tbtt_start + info->tbtt_info_len; - - ret = os_snprintf(pos, end - pos, "\n"); - if (os_snprintf_error(end - pos, ret)) - return 0; - pos += ret; + ie = tbtt_start + count * info->tbtt_info_len; n++; } @@ -5329,7 +5417,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, size_t i; int ret; char *pos, *end; - const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe; + const u8 *ie, *ie2, *mesh, *owe, *rsnxe; pos = buf; end = buf + buflen; @@ -5444,12 +5532,12 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); - ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + ie2 = wpa_bss_get_rsne(wpa_s, bss, NULL, false); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); - rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) { ret = os_snprintf(pos, end - pos, "[SAE-H2E]"); if (os_snprintf_error(end - pos, ret)) @@ -5462,10 +5550,6 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, return 0; pos += ret; } - osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); - if (osen_ie) - pos = wpa_supplicant_ie_txt(pos, end, "OSEN", - osen_ie, 2 + osen_ie[1]); owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); if (owe) { ret = os_snprintf( @@ -5476,7 +5560,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); - if (!ie && !ie2 && !osen_ie && + if (!ie && !ie2 && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (os_snprintf_error(end - pos, ret)) @@ -5664,12 +5748,6 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, anqp->hs20_connection_capability); pos = anqp_add_hex(pos, end, "hs20_operating_class", anqp->hs20_operating_class); - pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", - anqp->hs20_osu_providers_list); - pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata", - anqp->hs20_operator_icon_metadata); - pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list", - anqp->hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ dl_list_for_each(elem, &anqp->anqp_elems, @@ -5772,8 +5850,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } - if (mask & WPA_BSS_MASK_RNR) - pos += print_rnr(bss, pos, end); + if (mask & WPA_BSS_MASK_RNR) { + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + const struct element *elem; + + for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, + wpa_bss_ie_ptr(bss), ies_len) + pos += print_rnr((const u8 *) elem, pos, end); + } if (mask & WPA_BSS_MASK_ML) pos += print_ml(bss, pos, end); @@ -6035,7 +6119,7 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, return -1; } - bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); + bss = wpa_bss_get_connection(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); @@ -6320,7 +6404,7 @@ static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd) return -1; } - return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + return wpas_p2p_prov_disc(wpa_s, addr, NULL, 0, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } @@ -6349,11 +6433,34 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) p2ps_prov->pd_seeker = 1; - return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + return wpas_p2p_prov_disc(wpa_s, addr, NULL, 0, WPAS_P2P_PD_FOR_ASP, p2ps_prov); } +static bool bootstrap_pwd_required(u16 bootstrap) +{ + switch (bootstrap) { + case P2P_PBMA_OPPORTUNISTIC: + return false; + case P2P_PBMA_PIN_CODE_DISPLAY: + case P2P_PBMA_PASSPHRASE_DISPLAY: + case P2P_PBMA_QR_DISPLAY: + case P2P_PBMA_NFC_TAG: + case P2P_PBMA_PIN_CODE_KEYPAD: + case P2P_PBMA_PASSPHRASE_KEYPAD: + case P2P_PBMA_QR_SCAN: + case P2P_PBMA_NFC_READER: + return true; + case P2P_PBMA_SERVICE_MANAGED: + case P2P_PBMA_HANDSHAKE_SKIP: + return false; + } + + return false; +} + + static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -6376,6 +6483,10 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, size_t group_ssid_len = 0; int he; bool allow_6ghz; + bool p2p2, skip_prov; + u16 bootstrap = 0; + const char *password = NULL; + char *token, *context = NULL; if (!wpa_s->global->p2p_init_wpa_s) return -1; @@ -6385,10 +6496,12 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, wpa_s = wpa_s->global->p2p_init_wpa_s; } - /* <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] + /* <"pbc" | "pin" | "pair" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=] * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] - * [ht40] [vht] [he] [edmg] [auto] [ssid=] */ + * [ht40] [vht] [he] [edmg] [auto] [ssid=] + * [p2p2] [skip_prov] [bstrapmethod=] [password=] + */ if (hwaddr_aton(cmd, addr)) return -1; @@ -6422,6 +6535,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, vht; he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he; edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg; + p2p2 = os_strstr(pos, " p2p2") != NULL; + skip_prov = os_strstr(pos, " skip_prov") != NULL; pos2 = os_strstr(pos, " go_intent="); if (pos2) { @@ -6477,6 +6592,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, wps_method = WPS_PBC; } else if (os_strstr(pos, "p2ps") != NULL) { wps_method = WPS_P2PS; + } else if (os_strncmp(pos, "pair", 4) == 0 && p2p2) { + wps_method = WPS_NOT_READY; } else { pin = pos; pos = os_strchr(pin, ' '); @@ -6492,11 +6609,31 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, } } + pos2 = os_strstr(pos, "bstrapmethod="); + if (pos2) { + pos2 += 13; + bootstrap = atoi(pos2); + } + + while ((token = str_token(pos, " ", &context))) { + if (os_strncmp(token, "password=", 9) == 0) { + password = token + 9; + continue; + } + } + + if (bootstrap_pwd_required(bootstrap) && !password) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: This P2P2 bootstrap method requires password"); + return -1; + } + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, auth, go_intent, freq, freq2, persistent_id, pd, ht40, vht, max_oper_chwidth, he, edmg, - group_ssid, group_ssid_len, allow_6ghz); + group_ssid, group_ssid_len, allow_6ghz, p2p2, + bootstrap, password, skip_prov); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -6534,10 +6671,11 @@ static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) { u8 addr[ETH_ALEN]; - char *pos; + char *pos, *pos2; enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG; + u16 bootstrap = 0; - /* [join|auto] */ + /* [join|auto] [bstrapmethod=] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -6552,7 +6690,13 @@ static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) else if (os_strstr(pos, " auto") != NULL) use = WPAS_P2P_PD_AUTO; - return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL); + pos2 = os_strstr(pos, "bstrapmethod="); + if (pos2) { + pos2 += 13; + bootstrap = atoi(pos2); + } + + return wpas_p2p_prov_disc(wpa_s, addr, pos, bootstrap, use, NULL); } @@ -6570,6 +6714,52 @@ static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, } +static int p2p_ctrl_validate_dira(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + char *pos, *pos2; + u8 addr[ETH_ALEN]; + u8 nonce[DEVICE_IDENTITY_NONCE_LEN]; + u8 tag[DEVICE_IDENTITY_TAG_LEN]; + int id; + int res; + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + + pos2 = os_strstr(pos, "nonce="); + if (pos2) { + pos2 += 6; + if (hexstr2bin(pos2, nonce, sizeof(nonce)) < 0) + return -1; + } else { + return -1; + } + + pos2 = os_strstr(pos, "tag="); + if (pos2) { + pos2 += 4; + if (hexstr2bin(pos2, tag, sizeof(tag)) < 0) + return -1; + } else { + return -1; + } + + id = wpas_p2p_validate_dira(wpa_s, addr, 0, nonce, tag); + if (id <= 0) + return -1; + + res = os_snprintf(buf, buflen, "%u", id); + if (os_snprintf_error(buflen, res)) + return -1; + return res; +} + + static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -7043,14 +7233,16 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; int id; - struct wpa_ssid *ssid; + struct wpa_ssid *ssid = NULL; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0; int edmg; bool allow_6ghz; + bool p2p2; + + p2p2 = os_strstr(cmd, " p2p2") != NULL; - id = atoi(cmd); pos = os_strstr(cmd, " peer="); if (pos) { pos += 6; @@ -7058,11 +7250,19 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) return -1; _peer = peer; } - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL || ssid->disabled != 2) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " - "for persistent P2P group", - id); + + if (os_strncmp(cmd, "persistent=", 11) == 0) { + id = atoi(cmd + 11); + ssid = wpa_config_get_network(wpa_s->conf, id); + if (!ssid || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find SSID id=%d for persistent P2P group", + id); + return -1; + } + } else if (p2p2 && !_peer) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find peer for persistent P2P group"); return -1; } @@ -7107,7 +7307,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, max_oper_chwidth, pref_freq, he, edmg, - allow_6ghz); + allow_6ghz, p2p2); } @@ -7147,8 +7347,8 @@ static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) { - if (os_strncmp(cmd, "persistent=", 11) == 0) - return p2p_ctrl_invite_persistent(wpa_s, cmd + 11); + if (os_strncmp(cmd, "persistent", 10) == 0) + return p2p_ctrl_invite_persistent(wpa_s, cmd); if (os_strncmp(cmd, "group=", 6) == 0) return p2p_ctrl_invite_group(wpa_s, cmd + 6); @@ -7160,9 +7360,11 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, int id, int freq, int vht_center_freq2, int ht40, int vht, int vht_chwidth, int he, int edmg, bool allow_6ghz, - const u8 *go_bssid) + const u8 *go_bssid, bool p2p2, + enum wpa_p2p_mode p2p_mode) { struct wpa_ssid *ssid; + bool join = false; ssid = wpa_config_get_network(wpa_s->conf, id); if (ssid == NULL || ssid->disabled != 2) { @@ -7172,17 +7374,26 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, + if (p2p2) { + wpa_s->p2p2 = p2p2; + wpa_s->p2p_mode = p2p_mode; + join = true; + } + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, freq, vht_center_freq2, ht40, vht, vht_chwidth, he, edmg, NULL, 0, 0, allow_6ghz, 0, - go_bssid); + go_bssid, NULL, NULL, NULL, 0, + join, false); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { int freq = 0, persistent = 0, group_id = -1; + bool p2p2 = false; + int p2pmode = WPA_P2P_MODE_WFD_R1; + enum wpa_p2p_mode p2p_mode; bool allow_6ghz = false; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; @@ -7198,7 +7409,8 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "freq2=%d", &freq2) == 1 || sscanf(token, "persistent=%d", &group_id) == 1 || - sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) { + sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1 || + sscanf(token, "p2pmode=%d", &p2pmode) == 1) { continue; #ifdef CONFIG_ACS } else if (os_strcmp(token, "freq=acs") == 0) { @@ -7219,6 +7431,8 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) persistent = 1; } else if (os_strcmp(token, "allow_6ghz") == 0) { allow_6ghz = true; + } else if (os_strcmp(token, "p2p2") == 0) { + p2p2 = true; } else if (os_strncmp(token, "go_bssid=", 9) == 0) { if (hwaddr_aton(token + 9, go_bssid_buf)) return -1; @@ -7233,7 +7447,18 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) #ifdef CONFIG_ACS if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && - (acs || freq == 2 || freq == 5)) { + (freq == 2 || freq == 5)) { + unsigned int res, size = P2P_MAX_PREF_CHANNELS; + struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS]; + + acs = 1; + res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, + &size, pref_freq_list); + if (!res && size > 0) + acs = 0; + } + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && acs) { if (freq == 2 && wpa_s->best_24_freq <= 0) { wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G; wpa_s->p2p_go_do_acs = 1; @@ -7262,15 +7487,21 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->p2p_go_allow_dfs = !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD); + if (p2pmode < WPA_P2P_MODE_WFD_R1 || p2pmode > WPA_P2P_MODE_WFD_PCC) + return -1; + p2p_mode = p2pmode; + if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, freq, freq2, ht40, vht, max_oper_chwidth, he, edmg, allow_6ghz, - go_bssid); + go_bssid, p2p2, + p2p_mode); return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht, - max_oper_chwidth, he, edmg, allow_6ghz); + max_oper_chwidth, he, edmg, allow_6ghz, p2p2, + p2p_mode); } @@ -7673,6 +7904,41 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) return 0; } + if (os_strcmp(cmd, "supported_bootstrapmethods") == 0) { + p2p_set_bootstrapmethods(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "pasn_type") == 0) { + p2p_set_pasn_type(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "comeback_after") == 0) { + p2p_set_comeback_after(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "reginfo") == 0) { + p2p_set_reg_info(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "twt_power_mgmt") == 0) { + p2p_set_twt_power_mgmt(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "chan_switch_req_enable") == 0) { + p2p_set_chan_switch_req_enable(wpa_s->global->p2p, atoi(param)); + return 0; + } + + if (os_strcmp(cmd, "inv_oper_freq") == 0) { + p2p_set_invitation_op_freq(wpa_s->global->p2p, atoi(param)); + return 0; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); @@ -7688,12 +7954,16 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TESTING_OPTIONS os_free(wpa_s->get_pref_freq_list_override); wpa_s->get_pref_freq_list_override = NULL; + if (wpa_s->global->p2p) + p2p_set_invitation_op_freq(wpa_s->global->p2p, -1); #endif /* CONFIG_TESTING_OPTIONS */ wpas_p2p_stop_find(wpa_s); wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); + + wpa_s->p2p2 = false; } @@ -7780,6 +8050,26 @@ static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd) return wpas_p2p_lo_start(wpa_s, freq, period, interval, count); } + +#ifdef CONFIG_TESTING_OPTIONS +static int p2p_ctrl_pmk_get(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + const u8 *pmk; + size_t pmk_len; + + /* Return the PMK from the first identity entry. This assumes test + * cases to remove all indentities at the beginning so that only one + * entry is available. */ + if (!wpa_s->conf->identity || !wpa_s->conf->identity->pmk) + return -1; + + pmk_len = wpabuf_len(wpa_s->conf->identity->pmk); + pmk = wpabuf_head(wpa_s->conf->identity->pmk); + + return wpa_snprintf_hex(buf, buflen, pmk, pmk_len); +} +#endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_P2P */ @@ -8120,7 +8410,7 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) if (subtypes == 0) return -1; - return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0); + return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); } @@ -8143,7 +8433,7 @@ static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len, 0); + buf, len); os_free(buf); @@ -8189,77 +8479,12 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len, 0); + buf, len); os_free(buf); return ret; } - -static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply, - int buflen) -{ - u8 dst_addr[ETH_ALEN]; - int used; - char *ctx = NULL, *icon, *poffset, *psize; - - used = hwaddr_aton2(cmd, dst_addr); - if (used < 0) - return -1; - cmd += used; - - icon = str_token(cmd, " ", &ctx); - poffset = str_token(cmd, " ", &ctx); - psize = str_token(cmd, " ", &ctx); - if (!icon || !poffset || !psize) - return -1; - - wpa_s->fetch_osu_icon_in_progress = 0; - return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize), - reply, buflen); -} - - -static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd) -{ - u8 dst_addr[ETH_ALEN]; - int used; - char *icon; - - if (!cmd[0]) - return hs20_del_icon(wpa_s, NULL, NULL); - - used = hwaddr_aton2(cmd, dst_addr); - if (used < 0) - return -1; - - while (cmd[used] == ' ') - used++; - icon = cmd[used] ? &cmd[used] : NULL; - - return hs20_del_icon(wpa_s, dst_addr, icon); -} - - -static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem) -{ - u8 dst_addr[ETH_ALEN]; - int used; - char *icon; - - used = hwaddr_aton2(cmd, dst_addr); - if (used < 0) - return -1; - - while (cmd[used] == ' ') - used++; - icon = &cmd[used]; - - wpa_s->fetch_osu_icon_in_progress = 0; - return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), - (u8 *) icon, os_strlen(icon), inmem); -} - #endif /* CONFIG_HS20 */ @@ -8651,6 +8876,7 @@ static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); if (ret == 0) { +#ifdef CONFIG_P2P if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { struct p2p_data *p2p = wpa_s->global->p2p; if (p2p) { @@ -8661,6 +8887,7 @@ static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, p2p_set_country(p2p, country); } } +#endif /* CONFIG_P2P */ ret = os_snprintf(buf, buflen, "%s\n", "OK"); if (os_snprintf_error(buflen, ret)) ret = -1; @@ -8882,13 +9109,6 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wnm_btm_reset(wpa_s); -#ifdef CONFIG_INTERWORKING -#ifdef CONFIG_HS20 - hs20_cancel_fetch_osu(wpa_s); - hs20_del_icon(wpa_s, NULL, NULL); -#endif /* CONFIG_HS20 */ -#endif /* CONFIG_INTERWORKING */ - wpa_s->ext_mgmt_frame_handling = 0; wpa_s->ext_eapol_frame_io = 0; #ifdef CONFIG_TESTING_OPTIONS @@ -8905,6 +9125,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL); wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, NULL); wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, NULL); + wpa_sm_set_test_rsnxe_data(wpa_s->wpa, NULL, NULL); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2, 0); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4, 0); os_free(wpa_s->get_pref_freq_list_override); @@ -8920,6 +9141,14 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->rsnxe_override_eapol); wpa_s->rsnxe_override_eapol = NULL; wpas_clear_driver_signal_override(wpa_s); + { + int i; + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + wpabuf_free(wpa_s->link_ies[i]); + wpa_s->link_ies[i] = NULL; + } + } #ifndef CONFIG_NO_ROBUST_AV wpa_s->disable_scs_support = 0; wpa_s->disable_mscs_support = 0; @@ -8933,6 +9162,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->oci_freq_override_fils_assoc = 0; wpa_s->oci_freq_override_wnm_sleep = 0; wpa_s->disable_eapol_g2_tx = 0; + wpa_s->eapol_2_key_info_set_mask = 0; wpa_s->test_assoc_comeback_type = -1; #ifdef CONFIG_DPP os_free(wpa_s->dpp_config_obj_override); @@ -8996,9 +9226,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->conf->ignore_old_scan_res = 0; -#ifdef CONFIG_NAN_USD - wpas_nan_usd_flush(wpa_s); -#endif /* CONFIG_NAN_USD */ + wpas_nan_de_flush(wpa_s); + + wpas_pr_flush(wpa_s); } @@ -9118,8 +9348,8 @@ static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, wpa_s->ext_work_id++; ework->id = wpa_s->ext_work_id; - if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb, - ework) < 0) { + if (!radio_add_work(wpa_s, freq, ework->type, 0, + wpas_ctrl_radio_work_cb, ework)) { os_free(ework); return -1; } @@ -10276,6 +10506,29 @@ static int wpas_ctrl_test_eapol_m4_elems(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_test_rsnxe_data(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *data = NULL, *mask = NULL; + char *pos; + + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + if (wpas_get_hex_buf(cmd, &data) < 0 || + wpas_get_hex_buf(pos, &mask) < 0 || + wpa_sm_set_test_rsnxe_data(wpa_s->wpa, data, mask) < 0) { + wpabuf_free(data); + wpabuf_free(mask); + return -1; + } + + return 0; +} + + static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s) { u8 zero[WPA_TK_MAX_LEN]; @@ -10339,6 +10592,7 @@ static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s) params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; params.mgmt_frame_protection = wpa_s->sme.mfp; + params.spp_amsdu = wpa_s->sme.spp_amsdu; params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; @@ -10450,6 +10704,24 @@ static int wpas_ctrl_iface_send_twt_teardown(struct wpa_supplicant *wpa_s, return wpas_twt_send_teardown(wpa_s, flags); } + +static int wpas_ctrl_iface_get_tk(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + u8 tk[WPA_TK_MAX_LEN]; + size_t tk_len = 0; + int ret; + + if (wpa_sm_get_cached_tk(wpa_s->wpa, tk, &tk_len) < 0) { + ret = os_snprintf(buf, buflen, "FAIL"); + } else { + ret = wpa_snprintf_hex(buf, buflen, tk, tk_len); + forced_memzero(tk, sizeof(tk)); + } + + return ret; +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -10600,7 +10872,7 @@ static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) const u8 *data; /* - * Neighbor Report element (IEEE P802.11-REVmc/D5.0) + * Neighbor Report element (IEEE Std 802.11-2024, 9.4.2.35) * BSSID[6] * BSSID Information[4] * Operating Class[1] @@ -10830,6 +11102,24 @@ static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) #endif /* CONFIG_AP */ } +#ifdef CONFIG_P2P +#ifdef CONFIG_PASN + +#ifdef CONFIG_TESTING_OPTIONS +static int p2p_ctrl_get_pasn_ptk(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + const u8 *ptk; + size_t ptk_len; + + if (wpas_p2p_get_pasn_ptk(wpa_s, &ptk, &ptk_len)) + return -1; + return wpa_snprintf_hex(buf, buflen, ptk, ptk_len); +} +#endif /* CONFIG_TESTING_OPTIONS */ + +#endif /* CONFIG_PASN */ +#endif /* CONFIG_P2P */ #ifdef CONFIG_PMKSA_CACHE_EXTERNAL @@ -11241,7 +11531,8 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd) } ret = wpas_pasn_auth_start(wpa_s, wpa_s->own_addr, bssid, akmp, cipher, - group, id, comeback, comeback_len); + group, id, comeback, comeback_len, + WLAN_AUTH_PASN, 0, 0, 0, NULL, false); out: os_free(comeback); return ret; @@ -11263,47 +11554,335 @@ static int wpas_ctrl_iface_pasn_deauthenticate(struct wpa_supplicant *wpa_s, } -#ifdef CONFIG_TESTING_OPTIONS -static int wpas_ctrl_iface_pasn_driver(struct wpa_supplicant *wpa_s, - const char *cmd) +#ifdef CONFIG_PR + +static int wpas_ctrl_iface_pr_pasn_start(struct wpa_supplicant *wpa_s, + char *cmd) { + char *token, *context = NULL; + struct pr_pasn_ranging_params params; + bool got_addr = false; + int interval_time = 0; + + os_memset(¶ms, 0, sizeof(params)); + params.action = PR_PASN_AND_RANGING; + + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "addr=", 5) == 0) { + if (hwaddr_aton(token + 5, params.peer_addr)) + return -1; + got_addr = true; + } else if (os_strcmp(token, "role=ISTA") == 0) { + params.ranging_role |= PR_ISTA_SUPPORT; + } else if (os_strcmp(token, "role=RSTA") == 0) { + params.ranging_role |= PR_RSTA_SUPPORT; + } else if (os_strcmp(token, "ranging_type=EDCA") == 0) { + params.ranging_type |= PR_EDCA_BASED_RANGING; + } else if (os_strcmp(token, "ranging_type=NTB-OPEN-PHY") == 0) { + params.ranging_type |= PR_NTB_OPEN_BASED_RANGING; + } else if (os_strcmp(token, "ranging_type=NTB-SEC-PHY") == 0) { + params.ranging_type |= PR_NTB_SECURE_LTF_BASED_RANGING; + } else if (os_strncmp(token, "freq=", 5) == 0) { + params.freq = atoi(token + 5); + } else if (os_strncmp(token, "auth=", 5) == 0) { + params.auth_mode = atoi(token + 5); + } else if (os_strncmp(token, "forced_pr_freq=", 15) == 0) { + params.forced_pr_freq = atoi(token + 15); + } else if (os_strncmp(token, "num_bursts_exp=", 15) == 0) { + params.num_bursts_exp = atoi(token + 15); + } else if (os_strncmp(token, "ftmr_retries=", 13) == 0) { + params.ftmr_retries = atoi(token + 13); + } else if (os_strncmp(token, "burst_duration=", 15) == 0) { + params.burst_duration = atoi(token + 15); + } else if (os_strncmp(token, "ftms_per_burst=", 15) == 0) { + params.ftms_per_burst = atoi(token + 15); + } else if (os_strncmp(token, "interval_time=", 14) == 0) { + interval_time = atoi(token + 14); + } else if (os_strncmp(token, "min_time_between_meas=", 22) == + 0) { + params.min_time_between_measurements = atoi(token + 22); + } else if (os_strncmp(token, "max_time_between_meas=", 22) == + 0) { + params.max_time_between_measurements = atoi(token + 22); + } else if (os_strncmp(token, "availability_window=", 20) == 0) { + params.availability_window = atoi(token + 20); + } else if (os_strcmp(token, "request_lci=1") == 0) { + params.request_lci = true; + } else if (os_strcmp(token, "request_civicloc=1") == 0) { + params.request_civicloc = true; + } else if (os_strncmp(token, "continuous_session_time=", 24) == + 0) { + params.continuous_ranging_session_time = + atoi(token + 24); + } else if (os_strncmp(token, "src_addr=", 9) == 0) { + if (hwaddr_aton(token + 9, params.src_addr)) + return -1; + } else if (os_strcmp(token, "pasn_role=INITIATOR") == 0) { + params.pasn_role = PR_ROLE_PASN_INITIATOR; + } else if (os_strcmp(token, "pasn_role=RESPONDER") == 0) { + params.pasn_role = PR_ROLE_PASN_RESPONDER; + } else if (os_strcmp(token, "lmr_feedback=1") == 0) { + params.lmr_feedback = true; + } else if (os_strncmp(token, "ingress_threshold=", 18) == 0) { + params.ingress_threshold = atoll(token + 18); + } else if (os_strncmp(token, "egress_threshold=", 17) == 0) { + params.egress_threshold = atoll(token + 17); + } else if (os_strcmp(token, "pd_suppress_results=1") == 0) { + params.pr_suppress_results = true; + } else if (os_strncmp(token, "password=", 9) == 0) { + size_t pwd_len = os_strlen(token + 9); + + if (pwd_len == 0 || + pwd_len >= sizeof(params.password)) { + wpa_printf(MSG_INFO, + "CTRL: PR_PASN_START invalid password length %zu", + pwd_len); + return -1; + } + os_strlcpy(params.password, token + 9, + sizeof(params.password)); + params.password_valid = true; + } else if (os_strncmp(token, "pmk=", 4) == 0) { + size_t pmk_len = os_strlen(token + 4) / 2; + + if (pmk_len != 32 && pmk_len != 48 && pmk_len != 64) { + wpa_printf(MSG_INFO, + "CTRL: PR_PASN_START invalid PMK length %zu", + pmk_len); + return -1; + } + if (hexstr2bin(token + 4, params.pmk, pmk_len)) { + wpa_printf(MSG_INFO, + "CTRL: PR_PASN_START invalid PMK"); + return -1; + } + params.pmk_len = pmk_len; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: PR_PASN_START invalid parameter: '%s'", + token); + return -1; + } + } + + /* + * Map interval_time to the appropriate protocol-specific field: + * - EDCA: burst_period + * - NTB: nominal_time (nominal time between measurements) + */ + if (interval_time > 0) { + if (params.ranging_type & PR_EDCA_BASED_RANGING) + params.burst_period = interval_time; + else if (params.ranging_type & + (PR_NTB_OPEN_BASED_RANGING | + PR_NTB_SECURE_LTF_BASED_RANGING)) + params.nominal_time = interval_time; + } + + if (!got_addr || params.ranging_type == 0 || params.ranging_role == 0 || + params.freq == 0) { + wpa_printf(MSG_DEBUG, + "CTRL: PR_PASN_START missing parameter"); + return -1; + } + + /* pmk and password are only valid for authenticated modes */ + if (params.auth_mode == PR_PASN_AUTH_MODE_PASN && + (params.pmk_len > 0 || params.password_valid)) { + wpa_printf(MSG_INFO, + "CTRL: PR_PASN_START pmk/password not applicable for unauthenticated mode"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "CTRL: PR_PASN_START params: ranging type=0x%x, role=0x%x," + " auth_mode=%d, freq=%d, addr=" MACSTR " src_addr=" MACSTR " pasn_role=%d", + params.ranging_type, params.ranging_role, params.auth_mode, + params.freq, MAC2STR(params.peer_addr), + MAC2STR(params.src_addr), params.pasn_role); + + return wpas_pr_pasn_trigger(wpa_s, ¶ms); +} + + +static int wpas_ctrl_iface_pr_set_dik_ctx(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int ret = -1; + bool own = false; + char *token, *context = NULL; + const char *password = NULL; + const u8 *pmk = NULL, *dik = NULL; + struct wpabuf *pmk_buf = NULL, *dik_buf = NULL; + size_t pmk_len; + + while ((token = str_token(cmd, " ", &context))) { + if (os_strcmp(token, "self") == 0) { + own = true; + continue; + } + + if (os_strncmp(token, "dik=", 4) == 0) { + dik_buf = wpabuf_parse_bin(token + 4); + if (!dik_buf) + goto fail; + dik = wpabuf_head_u8(dik_buf); + continue; + } + + if (os_strncmp(token, "password=", 9) == 0) { + password = token + 9; + continue; + } + + if (os_strncmp(token, "pmk=", 4) == 0) { + pmk_buf = wpabuf_parse_bin(token + 4); + if (!pmk_buf) + goto fail; + pmk = wpabuf_head_u8(pmk_buf); + pmk_len = wpabuf_len(pmk_buf); + continue; + } + } + + if (!dik) + goto fail; + + wpas_pr_set_dev_ik(wpa_s, dik, password, pmk, pmk_len, own); + ret = 0; +fail: + wpabuf_clear_free(dik_buf); + wpabuf_clear_free(pmk_buf); + return ret; +} + +#endif /* CONFIG_PR */ + + +#ifdef CONFIG_TESTING_OPTIONS +static int wpas_ctrl_iface_pasn_driver(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + u8 bssid[ETH_ALEN]; + u8 *comeback = NULL; + size_t comeback_len = 0; union wpa_event_data event; - const char *pos = cmd; - u8 addr[ETH_ALEN]; + unsigned int i; + struct pasn_peer *peer = NULL; + int ret = -1; os_memset(&event, 0, sizeof(event)); - if (os_strncmp(pos, "auth ", 5) == 0) + /* + * Entry format : + * + * .......... + */ + + if (os_strncmp(cmd, "auth ", 5) == 0) event.pasn_auth.action = PASN_ACTION_AUTH; - else if (os_strncmp(pos, "del ", 4) == 0) + else if (os_strncmp(cmd, "del ", 4) == 0) event.pasn_auth.action = PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT; else return -1; - pos = os_strchr(pos, ' '); - if (!pos) + cmd = os_strchr(cmd, ' '); + if (!cmd) return -1; - pos++; - while (hwaddr_aton(pos, addr) == 0) { - struct pasn_peer *peer; - if (event.pasn_auth.num_peers == WPAS_MAX_PASN_PEERS) - return -1; - peer = &event.pasn_auth.peer[event.pasn_auth.num_peers]; - os_memcpy(peer->own_addr, wpa_s->own_addr, ETH_ALEN); - os_memcpy(peer->peer_addr, addr, ETH_ALEN); - event.pasn_auth.num_peers++; + /* + * Entry format for peer details: + * bssid= akmp= cipher= group= + * nid= [comeback=] password= + */ + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "bssid=", 6) == 0) { + if (hwaddr_aton(token + 6, bssid)) + goto out; - pos = os_strchr(pos, ' '); - if (!pos) - break; - pos++; + if (event.pasn_auth.num_peers == WPAS_MAX_PASN_PEERS) + goto out; + peer = &event.pasn_auth.peer[event.pasn_auth.num_peers]; + os_memcpy(peer->own_addr, wpa_s->own_addr, ETH_ALEN); + os_memcpy(peer->peer_addr, bssid, ETH_ALEN); + event.pasn_auth.num_peers++; + continue; + } + + if (!peer) { + wpa_printf(MSG_INFO, + "CTRL: PASN: No peer (bssid) specified before parameter '%s'", + token); + goto out; + } + + if (os_strcmp(token, "akmp=PASN") == 0) { + peer->akmp = WPA_KEY_MGMT_PASN; +#ifdef CONFIG_IEEE80211R + } else if (os_strcmp(token, "akmp=FT-PSK") == 0) { + peer->akmp = WPA_KEY_MGMT_FT_PSK; + } else if (os_strcmp(token, "akmp=FT-EAP-SHA384") == 0) { + peer->akmp = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + } else if (os_strcmp(token, "akmp=FT-EAP") == 0) { + peer->akmp = WPA_KEY_MGMT_FT_IEEE8021X; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + } else if (os_strcmp(token, "akmp=SAE") == 0) { + peer->akmp = WPA_KEY_MGMT_SAE; + wpa_printf(MSG_ERROR, "PASN: SAE akmp picked"); + } else if (os_strcmp(token, "akmp=SAE-EXT-KEY") == 0) { + peer->akmp = WPA_KEY_MGMT_SAE_EXT_KEY; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + } else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) { + peer->akmp = WPA_KEY_MGMT_FILS_SHA256; + } else if (os_strcmp(token, "akmp=FILS-SHA384") == 0) { + peer->akmp = WPA_KEY_MGMT_FILS_SHA384; +#endif /* CONFIG_FILS */ + } else if (os_strncmp(token, "cipher=", 7) == 0) { + peer->cipher = wpa_parse_cipher(token + 7); + } else if (os_strncmp(token, "group=", 6) == 0) { + peer->group = atoi(token + 6); + } else if (os_strncmp(token, "nid=", 4) == 0) { + peer->network_id = atoi(token + 4); + } else if (os_strncmp(token, "comeback=", 9) == 0) { + comeback_len = os_strlen(token + 9); + if (comeback || !comeback_len || comeback_len % 2) + goto out; + + comeback_len /= 2; + peer->comeback = os_malloc(comeback_len); + if (!peer->comeback || + hexstr2bin(token + 9, peer->comeback, comeback_len)) + goto out; + peer->comeback_len = comeback_len; + } else if (os_strncmp(token, "password=", 9) == 0) { + peer->password = os_strdup(token + 9); + if (!peer->password) + goto out; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: PASN Invalid parameter: '%s'", + token); + goto out; + } } wpa_supplicant_event(wpa_s, EVENT_PASN_AUTH, &event); - return 0; + + ret = 0; + +out: + for (i = 0; i < event.pasn_auth.num_peers; i++) { + peer = &event.pasn_auth.peer[i]; + os_free(peer->password); + os_free(peer->comeback); + } + + return ret; } + #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ @@ -12087,6 +12666,85 @@ static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s, } +static int +wpa_supplicant_ctrl_iface_setup_link_reconfig(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + const char *pos, *add_pos, *dlt_pos; + struct wpa_mlo_reconfig_info info; + int link_id; + + add_pos = os_strstr(cmd, "add="); + dlt_pos = os_strstr(cmd, "delete="); + + if (!add_pos && !dlt_pos) { + wpa_printf(MSG_INFO, "No add or delete links info"); + return -1; + } + + if (!wpa_s->current_bss) { + wpa_printf(MSG_INFO, "%s: Not connected", __func__); + return -1; + } + + info.add_links = 0; + info.delete_links = 0; + + if (add_pos) { + pos = add_pos + 4; + + do { + link_id = atoi(pos); + if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS) + return -1; + + if (wpa_s->current_bss->valid_links & BIT(link_id)) { + info.add_links |= BIT(link_id); + os_memcpy(info.add_link_bssid[link_id], + wpa_s->current_bss->mld_links[link_id].bssid, + ETH_ALEN); + info.add_link_freq[link_id] = + wpa_s->current_bss->mld_links[link_id].freq; + } else { + wpa_printf(MSG_INFO, + "%s: add link info not present", + __func__); + return -1; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } while (pos && pos != dlt_pos); + } + + if (dlt_pos) { + pos = dlt_pos + 7; + + do { + link_id = atoi(pos); + if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS) + return -1; + + if (wpa_s->valid_links & BIT(link_id)) + info.delete_links |= BIT(link_id); + else { + wpa_printf(MSG_INFO, + "%s: not a valid delete link", + __func__); + return -1; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } while (pos && pos != add_pos); + } + + return wpa_drv_setup_link_reconfig(wpa_s, &info); +} + + static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -12109,6 +12767,21 @@ static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s, if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; + + if (wpa_s->connection_set) { + ret = os_snprintf( + pos, end - pos, + "max_nss_rx=%u\n" + "max_nss_tx=%u\n" + "channel_width=%u\n", + wpa_s->links[i].max_nss_rx, + wpa_s->links[i].max_nss_tx, + channel_width_to_int( + wpa_s->links[i].channel_bandwidth)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } } return pos - buf; @@ -12189,7 +12862,21 @@ static int wpas_ctrl_ml_probe(struct wpa_supplicant *wpa_s, char *cmd) #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_NAN_USD +#if defined(CONFIG_NAN) || defined(CONFIG_NAN_USD) + +static bool wpas_nan_gtk_cs_supported(const int *cipher_list) +{ + unsigned int i; + + for (i = 0; cipher_list && cipher_list[i]; i++) { + if (cipher_list[i] == NAN_CS_GTK_CCMP_128 || + cipher_list[i] == NAN_CS_GTK_GCMP_256) + return true; + } + + return false; +} + static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) @@ -12202,6 +12889,11 @@ static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd, int ret = -1; enum nan_service_protocol_type srv_proto_type = 0; int *freq_list = NULL; + int *cipher_list = NULL; + u8 nd_pmk[PMK_LEN]; + bool p2p = false; + u8 forced_addr[ETH_ALEN]; + bool security_required_set = false; os_memset(¶ms, 0, sizeof(params)); /* USD shall use both solicited and unsolicited transmissions */ @@ -12262,6 +12954,11 @@ static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd, continue; } + if (os_strcmp(token, "p2p=1") == 0) { + p2p = true; + continue; + } + if (os_strcmp(token, "solicited=0") == 0) { params.solicited = false; continue; @@ -12277,18 +12974,120 @@ static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd, continue; } + if (os_strcmp(token, "pr=1") == 0) { + params.proximity_ranging = true; + continue; + } + + if (os_strcmp(token, "sync=1") == 0) { + params.sync = true; + continue; + } + + if (os_strncmp(token, "match_filter_tx=", 16) == 0) { + params.match_filter_tx = token + 16; + continue; + } + + if (os_strncmp(token, "match_filter_rx=", 16) == 0) { + params.match_filter_rx = token + 16; + continue; + } + + if (os_strcmp(token, "close_proximity=1") == 0) { + params.close_proximity = true; + continue; + } + + if (os_strncmp(token, "pbm=", 4) == 0) { + params.pbm = strtol(token + 4, NULL, 0); + continue; + } + + if (os_strncmp(token, "cipher_suites=", 14) == 0) { + char *pos = token + 14; + + while (pos && pos[0]) { + int_array_add_unique(&cipher_list, atoi(pos)); + pos = os_strchr(pos, ','); + if (pos) + pos++; + } + + params.cipher_suites_list = cipher_list; + continue; + } + + if (os_strncmp(token, "nd_pmk=", 7) == 0) { + if (params.nd_pmk) { + wpa_printf(MSG_INFO, + "CTRL: Duplicate nd_pmk parameter"); + goto fail; + } + + if (hexstr2bin(token + 7, nd_pmk, PMK_LEN) < 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid nd_pmk value"); + goto fail; + } + + params.nd_pmk = nd_pmk; + continue; + } + + if (os_strcmp(token, "gtk_required=1") == 0) { + params.gtk_required = true; + continue; + } + + if (os_strcmp(token, "data_path=1") == 0) { + params.data_path = true; + continue; + } + + if (os_strncmp(token, "forced_addr=", 12) == 0) { + if (hwaddr_aton(token + 12, forced_addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid forced_addr"); + goto fail; + } + params.forced_addr = forced_addr; + continue; + } + + if (os_strcmp(token, "security_required=0") == 0) { + params.security_required = false; + security_required_set = true; + continue; + } + wpa_printf(MSG_INFO, "CTRL: Invalid NAN_PUBLISH parameter: %s", token); goto fail; } - publish_id = wpas_nan_usd_publish(wpa_s, service_name, srv_proto_type, - ssi, ¶ms); + /* Default security_required to true when cipher suites are specified */ + if (params.cipher_suites_list && !security_required_set) + params.security_required = true; + + if (params.gtk_required && + !wpas_nan_gtk_cs_supported(params.cipher_suites_list)) { + wpa_printf(MSG_INFO, + "CTRL: GTK required but no GTK cipher suite configured"); + goto fail; + } + + publish_id = wpas_nan_publish(wpa_s, service_name, srv_proto_type, + ssi, ¶ms, p2p); if (publish_id > 0) ret = os_snprintf(buf, buflen, "%d", publish_id); fail: + if (params.nd_pmk) + forced_memzero(nd_pmk, PMK_LEN); + wpabuf_free(ssi); os_free(freq_list); + os_free(cipher_list); return ret; } @@ -12314,7 +13113,7 @@ static int wpas_ctrl_nan_cancel_publish(struct wpa_supplicant *wpa_s, return -1; } - wpas_nan_usd_cancel_publish(wpa_s, publish_id); + wpas_nan_cancel_publish(wpa_s, publish_id); return 0; } @@ -12350,13 +13149,38 @@ static int wpas_ctrl_nan_update_publish(struct wpa_supplicant *wpa_s, goto fail; } - ret = wpas_nan_usd_update_publish(wpa_s, publish_id, ssi); + ret = wpas_nan_update_publish(wpa_s, publish_id, ssi); fail: wpabuf_free(ssi); return ret; } +static int wpas_ctrl_nan_publish_stop_listen(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *token, *context = NULL; + int publish_id = 0; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "id=%i", &publish_id) == 1) + continue; + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_PUBLISH_STOP_LISTEN parameter: %s", + token); + return -1; + } + + if (publish_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_PUBLISH_STOP_LISTEN publish_id"); + return -1; + } + + return wpas_nan_usd_publish_stop_listen(wpa_s, publish_id); +} + + static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -12367,6 +13191,9 @@ static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd, struct wpabuf *ssi = NULL; int ret = -1; enum nan_service_protocol_type srv_proto_type = 0; + int *freq_list = NULL; + bool p2p = false; + u8 forced_addr[ETH_ALEN]; os_memset(¶ms, 0, sizeof(params)); params.freq = NAN_USD_DEFAULT_FREQ; @@ -12392,6 +13219,27 @@ static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd, continue; } + if (os_strncmp(token, "freq_list=", 10) == 0) { + char *pos = token + 10; + + if (os_strcmp(pos, "all") == 0) { + os_free(freq_list); + freq_list = wpas_nan_usd_all_freqs(wpa_s); + params.freq_list = freq_list; + continue; + } + + while (pos && pos[0]) { + int_array_add_unique(&freq_list, atoi(pos)); + pos = os_strchr(pos, ','); + if (pos) + pos++; + } + + params.freq_list = freq_list; + continue; + } + if (os_strncmp(token, "srv_proto_type=", 15) == 0) { srv_proto_type = atoi(token + 15); continue; @@ -12406,19 +13254,85 @@ static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd, continue; } + if (os_strcmp(token, "p2p=1") == 0) { + p2p = true; + continue; + } + + if (os_strcmp(token, "pr=1") == 0) { + params.proximity_ranging = true; + continue; + } + + if (os_strcmp(token, "sync=1") == 0) { + params.sync = true; + continue; + } + + if (os_strncmp(token, "match_filter_tx=", 16) == 0) { + params.match_filter_tx = token + 16; + continue; + } + + if (os_strncmp(token, "match_filter_rx=", 16) == 0) { + params.match_filter_rx = token + 16; + continue; + } + + if (os_strncmp(token, "srf_include=", 12) == 0) { + params.srf_include = !!atoi(token + 12); + continue; + } + + if (os_strncmp(token, "srf_mac_list=", 13) == 0) { + params.srf_mac_list = token + 13; + continue; + } + + if (os_strncmp(token, "srf_bf_len=", 11) == 0) { + params.srf_bf_len = atoi(token + 11); + continue; + } + + if (os_strncmp(token, "srf_bf_idx=", 11) == 0) { + params.srf_bf_idx = atoi(token + 11); + continue; + } + + if (os_strcmp(token, "close_proximity=1") == 0) { + params.close_proximity = true; + continue; + } + + if (os_strncmp(token, "pbm=", 4) == 0) { + params.pbm = strtol(token + 4, NULL, 0); + continue; + } + + if (os_strncmp(token, "forced_addr=", 12) == 0) { + if (hwaddr_aton(token + 12, forced_addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid forced_addr"); + goto fail; + } + params.forced_addr = forced_addr; + continue; + } + wpa_printf(MSG_INFO, "CTRL: Invalid NAN_SUBSCRIBE parameter: %s", token); goto fail; } - subscribe_id = wpas_nan_usd_subscribe(wpa_s, service_name, - srv_proto_type, ssi, - ¶ms); + subscribe_id = wpas_nan_subscribe(wpa_s, service_name, + srv_proto_type, ssi, + ¶ms, p2p); if (subscribe_id > 0) ret = os_snprintf(buf, buflen, "%d", subscribe_id); fail: wpabuf_free(ssi); + os_free(freq_list); return ret; } @@ -12444,11 +13358,36 @@ static int wpas_ctrl_nan_cancel_subscribe(struct wpa_supplicant *wpa_s, return -1; } - wpas_nan_usd_cancel_subscribe(wpa_s, subscribe_id); + wpas_nan_cancel_subscribe(wpa_s, subscribe_id); return 0; } +static int wpas_ctrl_nan_subscribe_stop_listen(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *token, *context = NULL; + int subscribe_id = 0; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "id=%i", &subscribe_id) == 1) + continue; + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_SUBSCRIBE_STOP_LISTEN parameter: %s", + token); + return -1; + } + + if (subscribe_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_SUBSCRIBE_STOP_LISTEN subscribe_id"); + return -1; + } + + return wpas_nan_usd_subscribe_stop_listen(wpa_s, subscribe_id); +} + + static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd) { char *token, *context = NULL; @@ -12457,6 +13396,8 @@ static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd) struct wpabuf *ssi = NULL; u8 peer_addr[ETH_ALEN]; int ret = -1; + u32 cookie = 0; + u32 *cookie_ptr = NULL; os_memset(peer_addr, 0, ETH_ALEN); @@ -12482,6 +13423,19 @@ static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd) continue; } + if (os_strncmp(token, "cookie=", 7) == 0) { + cookie = strtoul(token + 7, NULL, 0); + if (!cookie) { + wpa_printf(MSG_INFO, + "CTRL: Invalid cookie value: %s", + token + 7); + goto fail; + } + + cookie_ptr = &cookie; + continue; + } + wpa_printf(MSG_INFO, "CTRL: Invalid NAN_TRANSMIT parameter: %s", token); @@ -12500,14 +13454,61 @@ static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd) goto fail; } - ret = wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr, - req_instance_id); + ret = wpas_nan_transmit(wpa_s, handle, ssi, NULL, peer_addr, + req_instance_id, cookie_ptr); fail: wpabuf_free(ssi); return ret; } -#endif /* CONFIG_NAN_USD */ + +static int wpas_ctrl_nan_unpause_publish(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *token, *context = NULL; + int publish_id = 0; + int peer_instance_id = 0; + u8 peer_addr[ETH_ALEN]; + + os_memset(peer_addr, 0, ETH_ALEN); + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "publish_id=%i", &publish_id) == 1) + continue; + + if (sscanf(token, "peer_instance_id=%i", + &peer_instance_id) == 1) + continue; + + if (os_strncmp(token, "peer=", 5) == 0) { + if (hwaddr_aton(token + 5, peer_addr) < 0) + return -1; + continue; + } + + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_UNPAUSE_PUBLISH parameter: %s", + token); + return -1; + } + + if (publish_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_UNPAUSE_PUBLIH publish_id"); + return -1; + } + + if (is_zero_ether_addr(peer_addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_UNPAUSE_PUBLISH address"); + return -1; + } + + return wpas_nan_usd_unpause_publish(wpa_s, publish_id, peer_instance_id, + peer_addr); +} + +#endif /* CONFIG_NAN || CONFIG_NAN_USD */ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, @@ -12561,6 +13562,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strncmp(buf, "NOTE ", 5) == 0) { wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + wpa_trace_set_context(buf + 5); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { @@ -12809,6 +13811,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) { reply_len = p2p_get_passphrase(wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "P2P_GET_DIRA") == 0) { + reply_len = wpas_p2p_get_dira(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "P2P_VALIDATE_DIRA ", 18) == 0) { + reply_len = p2p_ctrl_validate_dira(wpa_s, buf + 18, + reply, reply_size); +#ifdef CONFIG_PASN +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcmp(buf, "P2P_GET_PASNPTK") == 0) { + reply_len = p2p_ctrl_get_pasn_ptk(wpa_s, reply, reply_size); +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_PASN */ } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) { reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply, reply_size); @@ -12875,6 +13888,13 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "P2P_LO_STOP") == 0) { if (wpas_p2p_lo_stop(wpa_s)) reply_len = -1; + } else if (os_strcmp(buf, "P2P_REMOVE_IDENTITY") == 0) { + if (wpas_p2p_remove_all_identity(wpa_s)) + reply_len = -1; +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "P2P_PMK_GET", 12) == 0) { + reply_len = p2p_ctrl_pmk_get(wpa_s, reply, reply_size); +#endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { @@ -12927,25 +13947,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; - } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { - if (hs20_icon_request(wpa_s, buf + 18, 0) < 0) - reply_len = -1; - } else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) { - if (hs20_icon_request(wpa_s, buf + 14, 1) < 0) - reply_len = -1; - } else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) { - reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size); - } else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) { - if (del_hs20_icon(wpa_s, buf + 14) < 0) - reply_len = -1; - } else if (os_strcmp(buf, "FETCH_OSU") == 0) { - if (hs20_fetch_osu(wpa_s, 0) < 0) - reply_len = -1; - } else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) { - if (hs20_fetch_osu(wpa_s, 1) < 0) - reply_len = -1; - } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { - hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { @@ -13326,6 +14327,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "ML_PROBE_REQ ", 13) == 0) { if (wpas_ctrl_ml_probe(wpa_s, buf + 13)) reply_len = -1; + } else if (os_strncmp(buf, "TEST_RSNXE_DATA ", 16) == 0) { + if (wpas_ctrl_test_rsnxe_data(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_TK") == 0) { + reply_len = wpas_ctrl_iface_get_tk(wpa_s, reply, reply_size); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -13513,7 +14519,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; #endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD +#if defined(CONFIG_NAN) || defined(CONFIG_NAN_USD) } else if (os_strncmp(buf, "NAN_PUBLISH ", 12) == 0) { reply_len = wpas_ctrl_nan_publish(wpa_s, buf + 12, reply, reply_size); @@ -13523,16 +14529,27 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "NAN_UPDATE_PUBLISH ", 19) == 0) { if (wpas_ctrl_nan_update_publish(wpa_s, buf + 19) < 0) reply_len = -1; + } else if (os_strncmp(buf, "NAN_PUBLISH_STOP_LISTEN ", 24) == 0) { + if (wpas_ctrl_nan_publish_stop_listen(wpa_s, buf + 24) < 0) + reply_len = -1; } else if (os_strncmp(buf, "NAN_SUBSCRIBE ", 14) == 0) { reply_len = wpas_ctrl_nan_subscribe(wpa_s, buf + 14, reply, reply_size); } else if (os_strncmp(buf, "NAN_CANCEL_SUBSCRIBE ", 21) == 0) { if (wpas_ctrl_nan_cancel_subscribe(wpa_s, buf + 21) < 0) reply_len = -1; + } else if (os_strncmp(buf, "NAN_SUBSCRIBE_STOP_LISTEN ", 26) == 0) { + if (wpas_ctrl_nan_subscribe_stop_listen(wpa_s, buf + 26) < 0) + reply_len = -1; } else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) { if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0) reply_len = -1; -#endif /* CONFIG_NAN_USD */ + } else if (os_strncmp(buf, "NAN_UNPAUSE_PUBLISH ", 20) == 0) { + if (wpas_ctrl_nan_unpause_publish(wpa_s, buf + 20) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "NAN_FLUSH") == 0) { + wpas_nan_de_flush(wpa_s); +#endif /* CONFIG_NAN || CONFIG_NAN_USD */ #ifdef CONFIG_PASN } else if (os_strncmp(buf, "PASN_START ", 11) == 0) { if (wpas_ctrl_iface_pasn_start(wpa_s, buf + 11) < 0) @@ -13544,6 +14561,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "PASN_DEAUTH ", 12) == 0) { if (wpas_ctrl_iface_pasn_deauthenticate(wpa_s, buf + 12) < 0) reply_len = -1; +#ifdef CONFIG_PR + } else if (os_strncmp(buf, "PR_PASN_START ", 14) == 0) { + if (wpas_ctrl_iface_pr_pasn_start(wpa_s, buf + 14) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "PR_SET_DIK_CONTEXT ", 19) == 0) { + if (wpas_ctrl_iface_pr_set_dik_ctx(wpa_s, buf + 19) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "PR_CLEAR_DIK_CONTEXT") == 0) { + wpas_pr_clear_dev_iks(wpa_s); + } else if (os_strcmp(buf, "PR_RANGING_ABORT") == 0) { + wpas_pr_abort_ranging(wpa_s); +#endif /* CONFIG_PR */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strncmp(buf, "PASN_DRIVER ", 12) == 0) { if (wpas_ctrl_iface_pasn_driver(wpa_s, buf + 12) < 0) @@ -13564,12 +14593,69 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11)) reply_len = -1; #endif /* CONFIG_NO_ROBUST_AV */ + } else if (os_strncmp(buf, "SETUP_LINK_RECONFIG", 19) == 0) { + if (wpa_supplicant_ctrl_iface_setup_link_reconfig(wpa_s, + buf + 19) < 0) + reply_len = -1; } else if (os_strcmp(buf, "MLO_STATUS") == 0) { reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) { reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "NEW_RANDOM_MAC_ADDRESS") == 0) { + enum wpas_mac_addr_style mac_addr_style = + wpa_s->conf->preassoc_mac_addr; + + wpa_s->conf->preassoc_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM; + if (wpas_update_random_addr_disassoc(wpa_s) != 1) + reply_len = -1; + wpa_s->conf->preassoc_mac_addr = mac_addr_style; +#ifdef CONFIG_NAN + } else if (os_strncmp(buf, "NAN_START", 9) == 0) { + if (wpas_nan_start(wpa_s) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_STOP", 8) == 0) { + if (wpas_nan_stop(wpa_s) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_SET ", 8) == 0) { + if (wpas_nan_set(wpa_s, buf + 8) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_UPDATE_CONF", 15) == 0) { + if (wpas_nan_update_conf(wpa_s) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_SCHED_CONFIG_MAP ", 21) == 0) { + if (wpas_nan_sched_config_map(wpa_s, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_NDP_REQUEST ", 16) == 0) { + if (wpas_nan_ndp_request(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_NDP_RESPONSE ", 17) == 0) { + if (wpas_nan_ndp_response(wpa_s, buf + 17) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_NDP_TERMINATE ", 18) == 0) { + if (wpas_nan_ndp_terminate(wpa_s, buf + 18) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_PEER_INFO ", 14) == 0) { + reply_len = wpas_nan_peer_info(wpa_s, buf + 14, reply, + reply_size); + } else if (os_strcmp(buf, "NAN_STATUS") == 0) { + reply_len = wpas_nan_status(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "NAN_BOOTSTRAP ", 14) == 0) { + if (wpas_nan_bootstrap_request(wpa_s, buf + 14) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_BOOTSTRAP_RESET ", 20) == 0) { + if (wpas_nan_bootstrap_reset(wpa_s, buf + 20) < 0) + reply_len = -1; +#ifdef CONFIG_PASN + } else if (os_strncmp(buf, "NAN_PAIR ", 9) == 0) { + if (wpas_nan_pairing_start(wpa_s, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_PAIR_ABORT ", 15) == 0) { + if (wpas_nan_pairing_abort(wpa_s, buf + 15) < 0) + reply_len = -1; +#endif /* CONFIG_PASN */ +#endif /* CONFIG_NAN */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -13593,11 +14679,12 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, struct wpa_supplicant *wpa_s; unsigned int create_iface = 0; u8 mac_addr[ETH_ALEN]; + const u8 *addr_ptr = NULL; enum wpa_driver_if_type type = WPA_IF_STATION; /* * TABTABTABTAB - * TAB[TAB[TAB]] + * TAB[TAB[TAB][TAB]] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -13670,16 +14757,39 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, if (!pos) break; + extra = os_strchr(pos, '\t'); + if (extra) + *extra++ = '\0'; + if (os_strcmp(pos, "sta") == 0) { type = WPA_IF_STATION; } else if (os_strcmp(pos, "ap") == 0) { type = WPA_IF_AP_BSS; + } else if (os_strcmp(pos, "nan") == 0) { + type = WPA_IF_NAN; + iface.nan_mgmt = true; + } else if (os_strcmp(pos, "nan_data") == 0) { + type = WPA_IF_NAN_DATA; + iface.nan_data = true; } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported interface type: '%s'", pos); return -1; } + + pos = extra; + if (!pos || !pos[0]) + break; + + if (hwaddr_aton(pos, mac_addr)) { + wpa_printf(MSG_DEBUG, + "INTERFACE_ADD invalid MAC address: '%s'", + pos); + return -1; + } + + addr_ptr = mac_addr; } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported extra parameter: '%s'", @@ -13694,7 +14804,7 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, if (!global->ifaces) return -1; if (wpa_drv_if_add(global->ifaces, type, iface.ifname, - NULL, NULL, NULL, mac_addr, NULL) < 0) { + addr_ptr, NULL, NULL, mac_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "CTRL_IFACE interface creation failed"); return -1; @@ -13887,6 +14997,8 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, #ifdef CONFIG_AP "STA-FIRST", #endif /* CONFIG_AP */ + "P2P_GET_DIRA", + "P2P_VALIDATE_DIRA", NULL }; static const char * prefix[] = { @@ -13925,6 +15037,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "NFC_REPORT_HANDOVER ", "P2P_ASP_PROVISION ", "P2P_ASP_PROVISION_RESP ", + "NAN_", #ifdef CONFIG_AP "STA ", "STA-NEXT ", @@ -14283,6 +15396,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + wpa_trace_set_context(buf + 5); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c index 27003eb44e59..a3f32c34f6e6 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -1159,3 +1159,38 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry) os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); } + + +dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry) +{ + return entry->type == DBUS_TYPE_BYTE || + entry->type == DBUS_TYPE_INT16 || + entry->type == DBUS_TYPE_UINT16 || + entry->type == DBUS_TYPE_INT32 || + entry->type == DBUS_TYPE_UINT32 || + entry->type == DBUS_TYPE_INT64 || + entry->type == DBUS_TYPE_UINT64; +} + + +int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry) +{ + switch (entry->type) { + case DBUS_TYPE_BYTE: + return entry->byte_value; + case DBUS_TYPE_INT16: + return entry->int16_value; + case DBUS_TYPE_UINT16: + return entry->uint16_value; + case DBUS_TYPE_INT32: + return entry->int32_value; + case DBUS_TYPE_UINT32: + return entry->uint32_value; + case DBUS_TYPE_INT64: + return entry->int64_value; + case DBUS_TYPE_UINT64: + return entry->uint64_value; + } + + return -1; +} diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h index 1d33689a8e44..15c8aba0c6f7 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -158,4 +158,7 @@ dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict); void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry); +dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry); +int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry); + #endif /* DBUS_DICT_HELPERS_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index 76e42ffbcc77..2989002e4a3f 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -1159,6 +1159,29 @@ void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s) } +void wpas_dbus_signal_sae_password_mismatch(struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "SaePasswordMismatch"); + if (!msg) + return; + + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + /** * wpas_dbus_signal_sta - Send a station related event signal * @wpa_s: %wpa_supplicant network interface data @@ -2332,6 +2355,116 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, } +/** + * wpas_dbus_signal_p2p_bootstrap_req - Signals BootstrappingRequest event + * @wpa_s: %wpa_supplicant network interface data + * @src: Source address of the message triggering this notification + * @bootstrap_method: Peer's bootstrap method + * + * Sends a signal to notify that a peer P2P Device is requesting bootstrapping + * negotiation with us. + */ +void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 bootstrap_method) +{ + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface) + return; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(src)); + path = peer_obj_path; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "BootstrappingRequest"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, + &bootstrap_method)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_p2p_bootstrap_rsp - Signals BootstrappingResponse event + * @wpa_s: %wpa_supplicant network interface data + * @src: Source address of the peer with which bootstrapping is done + * @status: Status of Bootstrapping handshake + * @bootstrap_method: Local device requested bootstrap method + * + * Sends a signal to notify that a peer P2P Device is requesting bootstrapping + * negotiation with us. + */ +void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s, + const u8 *src, int status, + u16 bootstrap_method) +{ + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface) + return; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + if (!wpa_s->dbus_new_path) + return; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(src)); + path = peer_obj_path; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "BootstrappingCompleted"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, + &status) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, + &bootstrap_method)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + + dbus_message_unref(msg); +} + + #endif /* CONFIG_P2P */ @@ -2396,6 +2529,10 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_PROP_ROAM_COMPLETE: prop = "RoamComplete"; break; + case WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ: + prop = "ScanInProgress6GHz"; + flush = TRUE; + break; case WPAS_DBUS_PROP_SESSION_LENGTH: prop = "SessionLength"; break; @@ -2492,41 +2629,6 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, } -/** - * wpas_dbus_sta_signal_prop_changed - Signals change of STA property - * @wpa_s: %wpa_supplicant network interface data - * @property: indicates which property has changed - * @address: unique BSS identifier - * - * Sends PropertyChanged signals with path, interface, and arguments depending - * on which property has changed. - */ -void wpas_dbus_sta_signal_prop_changed(struct wpa_supplicant *wpa_s, - enum wpas_dbus_bss_prop property, - u8 address[ETH_ALEN]) -{ - char path[WPAS_DBUS_OBJECT_PATH_MAX]; - char *prop; - - switch (property) { - case WPAS_DBUS_STA_PROP_ADDRESS: - prop = "Address"; - break; - default: - wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", - __func__, property); - return; - } - - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_new_path, MAC2STR(address)); - - wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, - WPAS_DBUS_NEW_IFACE_STA, prop); -} - - /** * wpas_dbus_signal_debug_level_changed - Signals change of debug param * @global: wpa_global structure @@ -3767,6 +3869,66 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, }, #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_NAN_USD + { "NANPublish", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_publish, + { + { "args", "a{sv}", ARG_IN }, + { "publish_id", "u", ARG_OUT }, + END_ARGS + } + }, + { "NANCancelPublish", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_publish, + { + { "publish_id", "u", ARG_IN }, + END_ARGS + } + }, + { "NANUpdatePublish", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_update_publish, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "NANPublishStopListen", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_publish_stop_listen, + { + { "publish_id", "u", ARG_IN }, + END_ARGS + } + }, + { "NANSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_subscribe, + { + { "args", "a{sv}", ARG_IN }, + { "subscribe_id", "u", ARG_OUT }, + END_ARGS + } + }, + { "NANCancelSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_subscribe, + { + { "subscribe_id", "u", ARG_IN }, + END_ARGS + } + }, + { "NANSubscribeStopListen", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_subscribe_stop_listen, + { + { "subscribe_id", "u", ARG_IN }, + END_ARGS + } + }, + { "NANTransmit", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_nan_transmit, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_NAN_USD */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -3930,6 +4092,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { wpas_dbus_setter_p2p_device_config, NULL }, + { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ay", + wpas_dbus_getter_p2p_device_address, + NULL, + NULL + }, { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", wpas_dbus_getter_p2p_peers, NULL, @@ -3983,6 +4150,12 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL, NULL }, + { + "ScanInProgress6GHz", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_scan_in_progress_6ghz, + NULL, + NULL + }, { "SessionLength", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_session_length, @@ -4263,6 +4436,20 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "BootstrappingRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + { "bootstrap_method", "q", ARG_OUT }, + END_ARGS + } + }, + { "BootstrappingCompleted", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + { "status", "i", ARG_OUT }, + END_ARGS + } + }, #endif /* CONFIG_P2P */ #ifdef CONFIG_AP { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, @@ -4374,6 +4561,40 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { } }, #endif /* CONFIG_HS20 */ +#ifdef CONFIG_NAN_USD + { "NANDiscoveryResult", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "NANReplied", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "NANReceive", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "NANPublishTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "publish_id", "u", ARG_OUT }, + { "reason", "s", ARG_OUT }, + END_ARGS + } + }, + { "NANSubscribeTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "subscribe_id", "u", ARG_OUT }, + { "reason", "s", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_NAN_USD */ { NULL, NULL, { END_ARGS } } }; @@ -4940,6 +5161,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = { wpas_dbus_setter_p2p_group_vendor_ext, NULL }, + { "GODeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o", + wpas_dbus_getter_p2p_group_go_device_address, + NULL, + NULL + }, { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -5243,3 +5469,259 @@ void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s, dbus_message_unref(msg); } #endif /* CONFIG_HS20 */ + + +#ifdef CONFIG_NAN_USD + +/** + * wpas_dbus_signal_nan_discovery_result - Send NANDiscoveryResult signal + * @wpa_s: %wpa_supplicant network interface data + * @srv_proto_type: Service Protocol Type + * @subscribe_id: Subscribe ID of the session + * @peer_publish_id: Publish ID of the sender + * @peer_addr: MAC address of the peer device + * @ssi: Service specific information payload + * @ssi_len: Length of the SSI field + * + * This is used to indicate the NAN DE DiscoveryResult event. + */ +void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type + srv_proto_type, + int subscribe_id, + int peer_publish_id, + const u8 *peer_addr, + bool fsd, bool fsd_gas, + const u8 *ssi, size_t ssi_len) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + char addr_str[20]; + + iface = wpa_s->global->dbus; + /* Do nothing if the interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NANDiscoveryResult"); + if (!msg) + return; + + snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr)); + + dbus_message_iter_init_append(msg, &iter); + + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id", + subscribe_id) || + !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id", + peer_publish_id) || + !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) || + !wpa_dbus_dict_append_bool(&dict_iter, "fsd", fsd) || + !wpa_dbus_dict_append_bool(&dict_iter, "fsd_gas", fsd_gas) || + !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type", + srv_proto_type) || + (ssi && + !wpa_dbus_dict_append_byte_array(&dict_iter, + "ssi", + (const char *) ssi, + ssi_len)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_nan_replied - Send NANReplied signal + * @wpa_s: %wpa_supplicant network interface data + * @srv_proto_type: Service Protocol Type + * @publish_id: Publish id of the session + * @peer_subscribe_id: Subscribe id of the sender + * @peer_addr: MAC address of the peer device + * @ssi: Service specific information payload + * @ssi_len: Length of the SSI field + * + * This is used to indicate the NAN DE Replied event. + */ +void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type srv_proto_type, + int publish_id, + int peer_subscribe_id, + const u8 *peer_addr, + const u8 *ssi, size_t ssi_len) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + char addr_str[20]; + + iface = wpa_s->global->dbus; + /* Do nothing if the interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NANReplied"); + if (!msg) + return; + + snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr)); + + dbus_message_iter_init_append(msg, &iter); + + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id", + publish_id) || + !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id", + peer_subscribe_id) || + !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) || + !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type", + srv_proto_type) || + (ssi && + !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi", + (const char *) ssi, + ssi_len)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_nan_receive - Send NANReceive signal + * @wpa_s: %wpa_supplicant network interface data + * @id: The original publish_id or subscribe_id + * @peer_id: Peer instance identifier + * @peer_addr: Address of the sender + * @ssi: Service specific information payload + * @ssi_len: Length of the SSI + * + * This is used to indicate the NAN DE Receive event to notify reception of a + * follow-up frame. + */ +void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s, + int id, int peer_id, const u8 *peer_addr, + const u8 *ssi, size_t ssi_len) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + char addr_str[20]; + + iface = wpa_s->global->dbus; + /* Do nothing if the interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NANReceive"); + if (!msg) + return; + + snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr)); + + dbus_message_iter_init_append(msg, &iter); + + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint32(&dict_iter, "id", id) || + !wpa_dbus_dict_append_uint32(&dict_iter, "peer_id", peer_id) || + !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) || + (ssi && + !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi", + (const char *) ssi, + ssi_len)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_nan_publish_terminated - Send NANPublishTerminated signal + * @wpa_s: %wpa_supplicant network interface data + * @publish_id: The publish_id of the session + * @reason: The reason of the termination + * + * This is used to indicate the NAN DE PublishTerminated event to notify when + * the session has expired. + */ +void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s, + int publish_id, + const char *reason) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + dbus_uint32_t pub_id = publish_id; + + iface = wpa_s->global->dbus; + /* Do nothing if the interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NANPublishTerminated"); + if (!msg) + return; + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &pub_id, + DBUS_TYPE_INVALID) || + !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_nan_subscribe_terminated - Send NANSubscribeTerminated signal + * @wpa_s: %wpa_supplicant network interface data + * @subscribe_id: The subscribe_id of the session + * @reason: The reason of the termination + * + * This is used to indicate the NAN DE SubscribeTerminated event to notify when + * the session has expired. + */ +void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s, + int subscribe_id, + const char *reason) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + dbus_uint32_t sub_id = subscribe_id; + + iface = wpa_s->global->dbus; + /* Do nothing if the interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "NANSubscribeTerminated"); + if (!msg) + return; + + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &sub_id, + DBUS_TYPE_INVALID) || + !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + +#endif /* CONFIG_NAN_USD */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index 952bb422a6d6..92b0c70b4e53 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -21,6 +21,7 @@ struct wpa_bss; struct wps_event_m2d; struct wps_event_fail; struct wps_credential; +enum nan_service_protocol_type; enum wpas_dbus_prop { WPAS_DBUS_PROP_AP_SCAN, @@ -36,6 +37,7 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_ASSOC_STATUS_CODE, WPAS_DBUS_PROP_ROAM_TIME, WPAS_DBUS_PROP_ROAM_COMPLETE, + WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ, WPAS_DBUS_PROP_SESSION_LENGTH, WPAS_DBUS_PROP_BSS_TM_STATUS, WPAS_DBUS_PROP_MAC_ADDRESS, @@ -256,6 +258,7 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s); +void wpas_dbus_signal_sae_password_mismatch(struct wpa_supplicant *wpa_s); void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *sta); void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, @@ -264,6 +267,11 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *dev_addr, const u8 *bssid, int id, int op_freq); +void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 bootstrap_method); +void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s, + const u8 *src, int status, + u16 bootstrap_method); void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s, @@ -284,6 +292,28 @@ void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s, const u8 *dst, const char *result); void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url); +void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type + srv_proto_type, + int subscribe_id, + int peer_publish_id, + const u8 *peer_addr, + bool fsd, bool fsd_gas, + const u8 *ssi, size_t ssi_len); +void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type srv_proto_type, + int publish_id, int peer_subscribe_id, + const u8 *peer_addr, + const u8 *ssi, size_t ssi_len); +void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s, int id, + int peer_id, const u8 *peer_addr, + const u8 *ssi, size_t ssi_len); +void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s, + int publish_id, + const char *reason); +void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s, + int subscribe_id, + const char *reason); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -595,6 +625,11 @@ static inline void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s) { } +static inline void +wpas_dbus_signal_sae_password_mismatch(struct wpa_supplicant *wpa_s) +{ +} + static inline void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *sta) @@ -615,6 +650,19 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 bootstrap_method) +{ +} + +static inline +void wpas_dbus_signal_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s, + const u8 *src, int status, + u16 bootstrap_method) +{ +} + static inline void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) @@ -667,6 +715,45 @@ void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s, { } +static inline void +wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type + srv_proto_type, + int subscribe_id, + int peer_publish_id, const u8 *peer_addr, + bool fsd, bool fsd_gas, + const u8 *ssi, size_t ssi_len) +{ +} + +static inline void +wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type srv_proto_type, + int publish_id, int peer_subscribe_id, + const u8 *peer_addr, const u8 *ssi, size_t ssi_len) +{ +} + + +static inline void +wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s, + int id, int peer_id, const u8 *peer_addr, + const u8 *ssi, size_t ssi_len) +{ +} + +static inline void +wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s, + int publish_id, const char *reason) +{ +} + +static inline void +wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s, + int subscribe_id, const char *reason) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index db121319626e..2774a78386ae 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -12,6 +12,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/nan_de.h" #include "eap_peer/eap_methods.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" @@ -27,6 +28,7 @@ #include "../autoscan.h" #include "../ap.h" #include "../interworking.h" +#include "../nan_supplicant.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" @@ -939,7 +941,7 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, struct wpa_supplicant *wpa_s; char *path; DBusMessage *reply = NULL; - bool delete_iface; + char *delete_iface = NULL; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); @@ -949,7 +951,11 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, reply = wpas_dbus_error_iface_unknown(message); goto out; } - delete_iface = wpa_s->added_vif; + if (wpa_s->added_vif) { + /* Keep a local copy of wpa_s->ifname since wpa_s is going to be + * freed before we need the interface name. */ + delete_iface = os_strdup(wpa_s->ifname); + } if (wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_error_unknown_error( message, @@ -959,11 +965,11 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, if (delete_iface) { wpa_printf(MSG_DEBUG, "%s[dbus]: deleting the interface '%s'", - __func__, wpa_s->ifname); + __func__, delete_iface); /* wpa_supplicant does not create multi-BSS AP, so collapse to * WPA_IF_STATION to avoid unwanted clean up in the driver. */ if (wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, - wpa_s->ifname)) { + delete_iface)) { reply = wpas_dbus_error_unknown_error( message, "wpa_supplicant couldn't delete this interface."); @@ -971,6 +977,7 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, } out: + os_free(delete_iface); return reply; } @@ -3437,7 +3444,8 @@ dbus_bool_t wpas_dbus_getter_capabilities( #endif /* CONFIG_WPS */ #ifdef CONFIG_SAE - if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) && + if (((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) || + (capa.flags & WPA_DRIVER_FLAGS_SAE)) && !wpa_dbus_dict_string_array_add_element(&iter_array, "sae")) goto nomem; #endif /* CONFIG_SAE */ @@ -3835,6 +3843,29 @@ dbus_bool_t wpas_dbus_getter_roam_complete( } +/** + * wpas_dbus_getter_scan_in_progress_6ghz - Get whether a 6 GHz scan is in + * progress + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "ScanInProgress6GHz" property. + */ +dbus_bool_t wpas_dbus_getter_scan_in_progress_6ghz( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_bool_t scan_in_progress_6ghz = wpa_s->scan_in_progress_6ghz ? + TRUE : FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &scan_in_progress_6ghz, error); +} + + /** * wpas_dbus_getter_session_length - Get most recent BSS session length * @iter: Pointer to incoming dbus message iter @@ -4647,7 +4678,7 @@ dbus_bool_t wpas_dbus_setter_iface_global( return FALSE; } - ret = wpa_config_process_global(wpa_s->conf, buf, -1); + ret = wpa_config_process_global(wpa_s->conf, buf, -1, false); if (ret < 0) { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Failed to set interface property %s", @@ -5635,7 +5666,7 @@ dbus_bool_t wpas_dbus_getter_bss_rsn( return FALSE; os_memset(&wpa_data, 0, sizeof(wpa_data)); - ie = wpa_bss_get_ie(res, WLAN_EID_RSN); + ie = wpa_bss_get_rsne(args->wpa_s, res, NULL, false); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { dbus_set_error_const(error, DBUS_ERROR_FAILED, "failed to parse RSN IE"); @@ -5776,7 +5807,6 @@ dbus_bool_t wpas_dbus_getter_bss_anqp( struct bss_handler_args *args = user_data; struct wpa_bss *bss; struct wpa_bss_anqp *anqp; - struct wpa_bss_anqp_elem *elem; bss = get_bss_helper(args, error, __func__); if (!bss) @@ -5790,6 +5820,8 @@ dbus_bool_t wpas_dbus_getter_bss_anqp( anqp = bss->anqp; if (anqp) { #ifdef CONFIG_INTERWORKING + struct wpa_bss_anqp_elem *elem; + if (anqp->capability_list && !wpa_dbus_dict_append_byte_array( &iter_dict, "CapabilityList", @@ -5876,24 +5908,6 @@ dbus_bool_t wpas_dbus_getter_bss_anqp( wpabuf_head(anqp->hs20_operating_class), wpabuf_len(anqp->hs20_operating_class))) goto nomem; - if (anqp->hs20_osu_providers_list && - !wpa_dbus_dict_append_byte_array( - &iter_dict, "HS20OSUProvidersList", - wpabuf_head(anqp->hs20_osu_providers_list), - wpabuf_len(anqp->hs20_osu_providers_list))) - goto nomem; - if (anqp->hs20_operator_icon_metadata && - !wpa_dbus_dict_append_byte_array( - &iter_dict, "HS20OperatorIconMetadata", - wpabuf_head(anqp->hs20_operator_icon_metadata), - wpabuf_len(anqp->hs20_operator_icon_metadata))) - goto nomem; - if (anqp->hs20_osu_providers_nai_list && - !wpa_dbus_dict_append_byte_array( - &iter_dict, "HS20OSUProvidersNAIList", - wpabuf_head(anqp->hs20_osu_providers_nai_list), - wpabuf_len(anqp->hs20_osu_providers_nai_list))) - goto nomem; #endif /* CONFIG_HS20 */ dl_list_for_each(elem, &anqp->anqp_elems, @@ -6462,3 +6476,582 @@ dbus_bool_t wpas_dbus_getter_signal_change( } return TRUE; } + + +#ifdef CONFIG_NAN_USD + +/* + * wpas_dbus_handler_nan_publish - Send out NAN publish messages + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANPublish" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + int publish_id; + char *srv_name = NULL; + enum nan_service_protocol_type srv_proto_type = 0; + bool p2p = false; + struct nan_publish_params params; + int *freq_list = NULL; + struct wpabuf *ssi = NULL; + dbus_uint32_t id; + + wpa_printf(MSG_DEBUG, "dbus: NANPublish"); + if (!wpa_s->nan_de) + return NULL; + + os_memset(¶ms, 0, sizeof(params)); + /* USD shall use both solicited and unsolicited transmissions */ + params.unsolicited = true; + params.solicited = true; + /* USD shall require FSD without GAS */ + params.fsd = true; + params.freq = NAN_USD_DEFAULT_FREQ; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto fail; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto fail; + if (os_strcmp(entry.key, "srv_name") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(srv_name); + srv_name = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (!srv_name) + goto oom; + } else if (os_strcmp(entry.key, "srv_proto_type") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + srv_proto_type = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "solicited") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.solicited = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "unsolicited") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.unsolicited = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "solicited_multicast") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.solicited_multicast = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "ttl") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.ttl = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "disable_events") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.disable_events = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "fsd") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.fsd = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "fsd_gas") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.fsd_gas = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "p2p") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + p2p = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "freq") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.freq = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "announcement_period") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.announcement_period = + wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "ssi") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + wpabuf_free(ssi); + ssi = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + wpa_dbus_dict_entry_clear(&entry); + if (!ssi) + goto oom; + } else if (os_strcmp(entry.key, "freq_list") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_UINT16) { + unsigned int i; + + for (i = 0; i < entry.array_len; i++) + int_array_add_unique( + &freq_list, entry.uint16array_value[i]); + params.freq_list = freq_list; + } else { + wpa_printf(MSG_DEBUG, + "dbus: NANPublish - unsupported dict entry '%s'", + entry.key); + reply = wpas_dbus_error_invalid_args(message, + entry.key); + wpa_dbus_dict_entry_clear(&entry); + goto out; + } + } + + if (!srv_name) + goto fail; + + publish_id = wpas_nan_publish(wpa_s, srv_name, srv_proto_type, ssi, + ¶ms, p2p); + if (publish_id < 0) { + reply = wpas_dbus_error_unknown_error( + message, "error publishing NAN USD"); + goto out; + } + + id = publish_id; + reply = dbus_message_new_method_return(message); + if (!reply) { + reply = wpas_dbus_error_no_memory(message); + goto out; + } + + dbus_message_append_args(reply, DBUS_TYPE_UINT32, + &id, DBUS_TYPE_INVALID); + +out: + wpabuf_free(ssi); + os_free(freq_list); + os_free(srv_name); + return reply; +fail: + reply = wpas_dbus_error_invalid_args(message, + "failed to parse NANPublish"); + goto out; +oom: + reply = wpas_dbus_error_no_memory(message); + goto out; +} + + +/* + * wpas_dbus_handler_nan_cancel_publish - Cancel a NAN publish + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANCancelPublish" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_nan_cancel_publish(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t publish_id; + + if (!wpa_s->nan_de) + return NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_UINT32, &publish_id, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_DEBUG, + "dbus: NANCancelPublish failed to get args"); + return wpas_dbus_error_invalid_args(message, NULL); + } + + wpa_printf(MSG_DEBUG, "dbus: NANCancelPublish: id=%u", publish_id); + nan_de_cancel_publish(wpa_s->nan_de, publish_id); + return NULL; +} + + +/* + * wpas_dbus_handler_nan_update_publish - Update the SSI for a NAN publish + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANUpdatePublish" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_nan_update_publish(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + int publish_id = -1; + struct wpabuf *ssi = NULL; + + wpa_printf(MSG_DEBUG, "dbus: NANUpdatePublish"); + if (!wpa_s->nan_de) + return NULL; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto fail; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto fail; + if (os_strcmp(entry.key, "publish_id") == 0 && + entry.type == DBUS_TYPE_UINT32) { + publish_id = entry.uint32_value; + wpa_dbus_dict_entry_clear(&entry); + wpa_printf(MSG_DEBUG, "dbus: publish_id=%d", + publish_id); + } else if (os_strcmp(entry.key, "ssi") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + wpabuf_free(ssi); + ssi = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + wpa_dbus_dict_entry_clear(&entry); + if (!ssi) { + reply = wpas_dbus_error_no_memory(message); + goto out; + } + } else { + wpa_printf(MSG_DEBUG, + "dbus: NANTransmit - unsupported dict entry '%s'", + entry.key); + reply = wpas_dbus_error_invalid_args(message, + entry.key); + wpa_dbus_dict_entry_clear(&entry); + goto out; + } + } + + if (publish_id < 0) + goto fail; + + if (nan_de_update_publish(wpa_s->nan_de, publish_id, ssi) < 0) + reply = wpas_dbus_error_unknown_error( + message, "error updating NAN USD publish ssi"); + +out: + wpabuf_free(ssi); + return reply; +fail: + reply = wpas_dbus_error_invalid_args( + message, + "failed to parse NANUpdatePublish"); + goto out; +} + + +/* + * wpas_dbus_handler_nan_publish_stop_listen - Stop listen for a NAN publish + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANPublishStopListen" method call of network interface. + */ +DBusMessage * +wpas_dbus_handler_nan_publish_stop_listen(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t publish_id; + + if (!wpa_s->nan_de) + return NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_UINT32, &publish_id, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_DEBUG, + "dbus: NANPublishStopListen failed to get args"); + return wpas_dbus_error_invalid_args(message, NULL); + } + + wpa_printf(MSG_DEBUG, "dbus: NANPublishStopListen: id=%u", publish_id); + if (wpas_nan_usd_publish_stop_listen(wpa_s, publish_id) < 0) + return wpas_dbus_error_unknown_error( + message, "error stopping listen"); + return NULL; +} + + +/* + * wpas_dbus_handler_nan_subscribe - Send out NAN USD subscribe messages + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANSubscribe" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + int subscribe_id; + char *srv_name = NULL; + struct nan_subscribe_params params; + enum nan_service_protocol_type srv_proto_type = 0; + bool p2p = false; + struct wpabuf *ssi = NULL; + int *freq_list = NULL; + + wpa_printf(MSG_DEBUG, "dbus: NANSubscribe"); + if (!wpa_s->nan_de) + return NULL; + + os_memset(¶ms, 0, sizeof(params)); + params.freq = NAN_USD_DEFAULT_FREQ; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto fail; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto fail; + if (os_strcmp(entry.key, "srv_name") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(srv_name); + srv_name = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + if (!srv_name) + goto oom; + } else if (os_strcmp(entry.key, "srv_proto_type") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + srv_proto_type = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "active") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + params.active = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "p2p") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + p2p = entry.bool_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "ttl") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.ttl = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "freq") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.freq = wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "query_period") == 0 && + wpa_dbus_dict_entry_is_int(&entry)) { + params.query_period = + wpa_dbus_dict_entry_get_int(&entry); + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "ssi") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + wpabuf_free(ssi); + ssi = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + wpa_dbus_dict_entry_clear(&entry); + if (!ssi) + goto oom; + } else if (os_strcmp(entry.key, "freq_list") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_UINT16) { + unsigned int i; + + for (i = 0; i < entry.array_len; i++) + int_array_add_unique( + &freq_list, entry.uint16array_value[i]); + } else { + wpa_printf(MSG_DEBUG, + "dbus: NANSubscribe - unsupported dict entry '%s'", + entry.key); + reply = wpas_dbus_error_invalid_args(message, + entry.key); + wpa_dbus_dict_entry_clear(&entry); + goto out; + } + } + + if (!srv_name) + goto fail; + + subscribe_id = wpas_nan_subscribe(wpa_s, srv_name, srv_proto_type, + ssi, ¶ms, p2p); + if (subscribe_id < 0) { + reply = wpas_dbus_error_unknown_error( + message, "error subscribing NAN USD"); + goto out; + } + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT32, + &subscribe_id, DBUS_TYPE_INVALID); +out: + wpabuf_free(ssi); + os_free(freq_list); + os_free(srv_name); + return reply; +fail: + reply = wpas_dbus_error_invalid_args(message, + "failed to parse NANSubscribe"); + goto out; +oom: + reply = wpas_dbus_error_no_memory(message); + goto out; +} + + +/* + * wpas_dbus_handler_nan_cancel_subscribe - Cancel a NAN subscription + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANCancelSubscribe" method call of network interface. + */ +DBusMessage * +wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t subscribe_id; + + if (!wpa_s->nan_de) + return NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_UINT32, &subscribe_id, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_DEBUG, + "dbus: NANCancelSubscribe failed to get args"); + return wpas_dbus_error_invalid_args(message, NULL); + } + + wpa_printf(MSG_DEBUG, "dbus: NANCancelSubscribe: id=%u", subscribe_id); + nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id); + return NULL; +} + + +/* + * wpas_dbus_handler_nan_subscribe_stop_listen - Stop listen for a NAN subscription + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANSubscribeStopListen" method call of network + * interface. + */ +DBusMessage * +wpas_dbus_handler_nan_subscribe_stop_listen(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t subscribe_id; + + if (!wpa_s->nan_de) + return NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_UINT32, &subscribe_id, + DBUS_TYPE_INVALID)) { + wpa_printf(MSG_DEBUG, + "dbus: NANSubscribeStopListen failed to get args"); + return wpas_dbus_error_invalid_args(message, NULL); + } + + wpa_printf(MSG_DEBUG, "dbus: NANSubscribeStopListen: id=%u", + subscribe_id); + if (wpas_nan_usd_subscribe_stop_listen(wpa_s, subscribe_id) < 0) + return wpas_dbus_error_unknown_error( + message, "error stopping listen"); + return NULL; +} + + +/* + * wpas_dbus_handler_nan_transmit - Send out NAN followup frames + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "NANTransmit" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + int handle = -1; + int req_instance_id = -1; + u8 peer_addr[ETH_ALEN]; + bool peer_addr_set = false; + struct wpabuf *ssi = NULL; + + wpa_printf(MSG_DEBUG, "dbus: NANTransmit"); + if (!wpa_s->nan_de) + return NULL; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto fail; + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto fail; + if (os_strcmp(entry.key, "handle") == 0 && + entry.type == DBUS_TYPE_UINT32) { + handle = entry.uint32_value; + wpa_dbus_dict_entry_clear(&entry); + wpa_printf(MSG_DEBUG, "dbus: handle=%d", handle); + } else if (os_strcmp(entry.key, "req_instance_id") == 0 && + entry.type == DBUS_TYPE_UINT32) { + req_instance_id = entry.uint32_value; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "peer_addr") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (hwaddr_aton(entry.str_value, peer_addr) < 0) { + wpa_dbus_dict_entry_clear(&entry); + goto fail; + } + peer_addr_set = true; + wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "ssi") == 0 && + entry.type == DBUS_TYPE_ARRAY && + entry.array_type == DBUS_TYPE_BYTE) { + wpabuf_free(ssi); + ssi = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + wpa_dbus_dict_entry_clear(&entry); + if (!ssi) { + reply = wpas_dbus_error_no_memory(message); + goto out; + } + } else { + wpa_printf(MSG_DEBUG, + "dbus: NANTransmit - unsupported dict entry '%s'", + entry.key); + reply = wpas_dbus_error_invalid_args(message, + entry.key); + wpa_dbus_dict_entry_clear(&entry); + goto out; + } + } + + if (handle < 0 || req_instance_id < 0 || !peer_addr_set || !ssi) + goto fail; + + if (wpas_nan_transmit(wpa_s, handle, ssi, NULL, peer_addr, + req_instance_id, NULL) < 0) + reply = wpas_dbus_error_unknown_error( + message, "failed to transmit follow-up"); +out: + wpabuf_free(ssi); + return reply; + +fail: + reply = wpas_dbus_error_invalid_args(message, + "failed to parse NANTransmit"); + goto out; +} + +#endif /* CONFIG_NAN_USD */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index acd6af7ffa38..24ee678e1712 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -173,6 +173,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_auth_status_code); DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code); DECLARE_ACCESSOR(wpas_dbus_getter_roam_time); DECLARE_ACCESSOR(wpas_dbus_getter_roam_complete); +DECLARE_ACCESSOR(wpas_dbus_getter_scan_in_progress_6ghz); DECLARE_ACCESSOR(wpas_dbus_getter_session_length); DECLARE_ACCESSOR(wpas_dbus_getter_bss_tm_status); DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age); @@ -289,4 +290,23 @@ DBusMessage * wpas_dbus_handler_subscribe_preq( DBusMessage * wpas_dbus_handler_unsubscribe_preq( DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_cancel_publish( + DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_update_publish( + DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_publish_stop_listen( + DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * +wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * +wpas_dbus_handler_nan_subscribe_stop_listen(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message, + struct wpa_supplicant *wpa_s); + #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 418a8fd4292e..58053098c27e 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -473,11 +473,13 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto inv_args; - if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, + if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, freq, freq2, ht40, vht, max_oper_chwidth, he, edmg, NULL, 0, 0, allow_6ghz, - retry_limit, go_bssid)) { + retry_limit, go_bssid, NULL, + NULL, NULL, 0, false, + false)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -485,7 +487,7 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, } } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, freq2, ht40, vht, max_oper_chwidth, he, edmg, - allow_6ghz)) + allow_6ghz, wpa_s->p2p2, wpa_s->p2p_mode)) goto inv_args; out: @@ -623,6 +625,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, struct wpa_dbus_dict_entry entry; char *peer_object_path = NULL; int persistent_group = 0; + int auto_join = 0; int join = 0; int authorize_only = 0; int go_intent = -1; @@ -656,6 +659,9 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, } else if (os_strcmp(entry.key, "join") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { join = entry.bool_value; + } else if (os_strcmp(entry.key, "auto_join") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + auto_join = entry.bool_value; } else if (os_strcmp(entry.key, "authorize_only") == 0 && entry.type == DBUS_TYPE_BOOLEAN) { authorize_only = entry.bool_value; @@ -704,9 +710,10 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, wpa_s = wpa_s->global->p2p_init_wpa_s; new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, - persistent_group, 0, join, authorize_only, + persistent_group, auto_join, join, + authorize_only, go_intent, freq, 0, -1, 0, 0, 0, 0, 0, 0, - NULL, 0, false); + NULL, 0, false, 0, 0, NULL, false); if (new_pin >= 0) { char npin[9]; @@ -866,7 +873,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, goto err; if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0, - 0, 0, 0, false) < 0) { + 0, 0, 0, false, false) < 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -928,7 +935,7 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, if (!wpa_s) return wpas_dbus_error_no_p2p_mgmt_iface(message); - if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, + if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0, WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) return wpas_dbus_error_unknown_error(message, "Failed to send provision discovery request"); @@ -1454,6 +1461,21 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo( &path, error); } +dbus_bool_t wpas_dbus_getter_p2p_device_address( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) + return FALSE; + + wpa_s = wpa_s->global->p2p_init_wpa_s; + + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, (char *) wpa_s->own_addr, + ETH_ALEN, error); +} /* * Peer object properties accessor methods @@ -2574,6 +2596,17 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext( } +dbus_bool_t wpas_dbus_getter_p2p_group_go_device_address( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, wpa_s->go_dev_addr, + ETH_ALEN, error); +} + + dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index b3c45c11012c..6d37078c76f3 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -95,6 +95,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_address); /* * P2P Peer properties. @@ -127,6 +128,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext); DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_go_device_address); /* * P2P Persistent Groups and properties diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c index a8c0d28398d4..bfdc3e29e5d9 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c @@ -38,7 +38,7 @@ static struct interfaces * add_interface(struct dl_list *list, if (!iface) return NULL; iface->dbus_interface = os_strdup(dbus_interface); - iface->xml = wpabuf_alloc(16000); + iface->xml = wpabuf_alloc(18000); if (iface->dbus_interface == NULL || iface->xml == NULL) { os_free(iface->dbus_interface); wpabuf_free(iface->xml); diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 52befd8f15fc..389ae1879bc9 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -507,13 +507,9 @@ CONFIG_IEEE80211AX=y # IEEE 802.11be EHT support (mainly for AP mode) # CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE. -# Note: This is experimental and work in progress. The definitions are still -# subject to change and this should not be expected to interoperate with the -# final IEEE 802.11be version. -#CONFIG_IEEE80211BE=y +CONFIG_IEEE80211BE=y # Wireless Network Management (IEEE Std 802.11v-2011) -# Note: This is experimental and not complete implementation. #CONFIG_WNM=y # Interworking (IEEE 802.11u) @@ -638,15 +634,13 @@ CONFIG_BGSCAN_SIMPLE=y #CONFIG_BGSCAN_LEARN=y # Opportunistic Wireless Encryption (OWE) -# Experimental implementation of draft-harkins-owe-07.txt -#CONFIG_OWE=y +CONFIG_OWE=y # Device Provisioning Protocol (DPP) (also known as Wi-Fi Easy Connect) CONFIG_DPP=y # DPP version 2 support CONFIG_DPP2=y -# DPP version 3 support (experimental and still changing; do not enable for -# production use) +# DPP version 3 support #CONFIG_DPP3=y # Wired equivalent privacy (WEP) @@ -667,9 +661,6 @@ CONFIG_DPP2=y #CONFIG_NO_TKIP=y # Pre-Association Security Negotiation (PASN) -# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol -# design is still subject to change. As such, this should not yet be enabled in -# production use. #CONFIG_PASN=y # Disable support for Radio Measurement (IEEE 802.11k) and supported operating @@ -686,3 +677,32 @@ CONFIG_DPP2=y # Wi-Fi Aware unsynchronized service discovery (NAN USD) #CONFIG_NAN_USD=y + +# Wi-Fi Aware (NAN) support +#CONFIG_NAN=y + +# Proximity Ranging (PR) support +#CONFIG_PR=y + +# Enhanced Privacy Protection (IEEE P802.11bi) +# Note: These features are experimental and still changing; do not enable for +# production use. +# Encrypted association frame support (needed for EPPKE and EAP in +# Authentication frames) +#CONFIG_ENC_ASSOC=y +# +# PMKSA caching privacy support +#CONFIG_PMKSA_PRIVACY=y +# +# IEEE 802.1X (EAP) authentication in Authentication frames +# IEEE P802.11bi/D4.0, 12.16.5 (IEEE 802.1X authentication utilizing +# Authentication frames) +#CONFIG_IEEE8021X_AUTH=y + +# OpenSSL PKCS#11/OpenSC engine and module trusted-path validation +# Validate PKCS#11 engine, PKCS#11 module, and OpenSC engine shared library +# paths (root-owned, non-writable regular file under /usr/lib with root-owned +# ancestor directories) before dlopen()ing them. Disabled by default to avoid +# affecting deployments that run wpa_supplicant as a non-root user with shared +# libraries in a custom directory owned by that user. +#CONFIG_TLS_ENGINE_TRUSTED_PATH=y diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.c b/contrib/wpa/wpa_supplicant/dpp_supplicant.c index 4044b47dd7fe..6369bd26fff0 100644 --- a/contrib/wpa/wpa_supplicant/dpp_supplicant.c +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.c @@ -2,7 +2,7 @@ * wpa_supplicant - DPP * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -58,6 +58,9 @@ static int wpas_dpp_process_conf_obj(void *ctx, struct dpp_authentication *auth); static bool wpas_dpp_tcp_msg_sent(void *ctx, struct dpp_authentication *auth); #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 +static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_DPP3 */ static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -331,17 +334,17 @@ static char * wpas_dpp_scan_channel_list(struct wpa_supplicant *wpa_s) u8 last_op_class = 0; int res; - if (!wpa_s->last_scan_freqs || !wpa_s->num_last_scan_freqs) + len = int_array_len(wpa_s->last_scan_freqs); + if (!len) return NULL; - len = wpa_s->num_last_scan_freqs * 8; - str = os_zalloc(len); + str = os_zalloc(len * 8); if (!str) return NULL; end = str + len; pos = str; - for (i = 0; i < wpa_s->num_last_scan_freqs; i++) { + for (i = 0; wpa_s->last_scan_freqs[i]; i++) { enum hostapd_hw_mode mode; u8 op_class, channel; @@ -1031,7 +1034,7 @@ static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) wpa_s->dpp_pending_listen_freq = lwork->freq; if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, - wpa_s->max_remain_on_chan) < 0) { + wpa_s->max_remain_on_chan, NULL) < 0) { wpa_printf(MSG_DEBUG, "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", lwork->freq); @@ -1068,8 +1071,8 @@ static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, return -1; lwork->freq = freq; - if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb, - lwork) < 0) { + if (!radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb, + lwork)) { wpas_dpp_listen_work_free(lwork); return -1; } @@ -1332,6 +1335,16 @@ void wpas_dpp_tx_wait_expire(struct wpa_supplicant *wpa_s) struct dpp_authentication *auth = wpa_s->dpp_auth; int freq; +#ifdef CONFIG_DPP3 + if (wpa_s->dpp_pb_announcement && wpa_s->dpp_pb_discovery_done) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to send push button announcement"); + if (eloop_register_timeout(0, 0, wpas_dpp_pb_next, + wpa_s, NULL) < 0) + wpas_dpp_push_button_stop(wpa_s); + } +#endif /* CONFIG_DPP3 */ + if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) { wpa_printf(MSG_DEBUG, "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel", @@ -1393,7 +1406,8 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, if (res == 0 && !(capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_SAE) && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) && + !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_STA)) { wpa_printf(MSG_DEBUG, "DPP: SAE not supported by the driver"); return NULL; @@ -1410,6 +1424,21 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, return NULL; wpas_notify_network_added(wpa_s, ssid); wpa_config_set_network_defaults(ssid); + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP; + ssid->group_cipher |= WPA_CIPHER_GCMP; + } + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP_256; + ssid->group_cipher |= WPA_CIPHER_GCMP_256; + } + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) { + ssid->pairwise_cipher |= WPA_CIPHER_CCMP_256; + ssid->group_cipher |= WPA_CIPHER_CCMP_256; + } ssid->disabled = 1; ssid->ssid = os_malloc(conf->ssid_len); @@ -1418,6 +1447,20 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, os_memcpy(ssid->ssid, conf->ssid, conf->ssid_len); ssid->ssid_len = conf->ssid_len; + if (!wpa_bss_get(wpa_s, NULL, conf->ssid, conf->ssid_len)) + ssid->scan_ssid = 1; + +#ifdef CONFIG_DPP3 + if (conf->akm == DPP_AKM_SAE && conf->password_id[0]) { + size_t len = os_strlen(conf->password_id); + + ssid->sae_password_id = os_zalloc(len + 1); + if (!ssid->sae_password_id) + goto fail; + os_memcpy(ssid->sae_password_id, conf->password_id, len); + } +#endif /* CONFIG_DPP3 */ + if (conf->connector) { if (dpp_akm_dpp(conf->akm)) { ssid->key_mgmt = WPA_KEY_MGMT_DPP; @@ -1475,12 +1518,17 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; else ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; - if (conf->passphrase[0]) { + if (conf->passphrase[0] && dpp_akm_psk(conf->akm)) { if (wpa_config_set_quoted(ssid, "psk", conf->passphrase) < 0) goto fail; wpa_config_update_psk(ssid); ssid->export_keys = 1; + } else if (conf->passphrase[0] && dpp_akm_sae(conf->akm)) { + if (wpa_config_set_quoted(ssid, "sae_password", + conf->passphrase) < 0) + goto fail; + ssid->export_keys = 1; } else { ssid->psk_set = conf->psk_set; os_memcpy(ssid->psk, conf->psk, PMK_LEN); @@ -1691,6 +1739,12 @@ static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s", hex); } +#ifdef CONFIG_DPP3 + if (conf->password_id[0]) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_IDPASS "%s", + conf->password_id); + } +#endif /* CONFIG_DPP3 */ if (conf->c_sign_key) { char *hex; size_t hexlen; @@ -1772,6 +1826,7 @@ static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, if (!wpa_s->dpp_pb_result_indicated) { wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "success"); wpa_s->dpp_pb_result_indicated = true; + wpas_dpp_push_button_stop(wpa_s); } #endif /* CONFIG_DPP3 */ @@ -4500,7 +4555,7 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss) return 0; /* Not using DPP AKM - continue */ - rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && !(ied.key_mgmt & WPA_KEY_MGMT_DPP)) return 0; /* AP does not support DPP AKM - continue */ @@ -5485,7 +5540,6 @@ int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd) #define DPP_PB_ANNOUNCE_PER_CHAN 3 static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq); -static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx); static void wpas_dpp_pb_tx_status(struct wpa_supplicant *wpa_s, @@ -5733,6 +5787,8 @@ int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd) "DPP: Scan to create channel list for PB discovery"); wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_drv_dpp_listen(wpa_s, true); wpa_supplicant_req_scan(wpa_s, 0, 0); wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started"); return 0; @@ -5750,6 +5806,8 @@ void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s) if (wpa_s->dpp_pb_bi) { char id[20]; + wpa_drv_dpp_listen(wpa_s, false); + if (wpa_s->dpp_pb_bi == wpa_s->dpp_pkex_bi) wpa_s->dpp_pkex_bi = NULL; os_snprintf(id, sizeof(id), "%u", wpa_s->dpp_pb_bi->id); @@ -5790,6 +5848,8 @@ void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s) wpas_abort_ongoing_scan(wpa_s); wpa_s->scan_res_handler = NULL; } + + offchannel_send_action_done(wpa_s); } #endif /* CONFIG_DPP3 */ diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.h b/contrib/wpa/wpa_supplicant/dpp_supplicant.h index e2bdd9d40e5f..427032023fff 100644 --- a/contrib/wpa/wpa_supplicant/dpp_supplicant.h +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.h @@ -2,7 +2,7 @@ * wpa_supplicant - DPP * Copyright (c) 2017, Qualcomm Atheros, Inc. * Copyright (c) 2018-2020, The Linux Foundation - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index d01b52bb1f04..7d8b806a4547 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -9,15 +9,24 @@ #ifndef DRIVER_I_H #define DRIVER_I_H +#include "common/nan_de.h" #include "drivers/driver.h" /* driver_ops */ static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s, const char *ifname) { - if (wpa_s->driver->init2) + if (wpa_s->driver->init2) { + enum wpa_p2p_mode p2p_mode = WPA_P2P_MODE_WFD_R1; + +#ifdef CONFIG_P2P + p2p_mode = wpa_s->p2p_mode; +#endif /* CONFIG_P2P */ + return wpa_s->driver->init2(wpa_s, ifname, - wpa_s->global_drv_priv); + wpa_s->global_drv_priv, + p2p_mode); + } if (wpa_s->driver->init) { return wpa_s->driver->init(wpa_s, ifname); } @@ -103,6 +112,8 @@ static inline int wpa_drv_mesh_link_probe(struct wpa_supplicant *wpa_s, static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { + params->link_id = -1; + #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER) return -EBUSY; @@ -304,12 +315,14 @@ static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s, static inline struct hostapd_hw_modes * wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes, - u16 *flags, u8 *dfs_domain) + u16 *flags, u8 *dfs_domain, + char *alpha2, size_t alpha2_len) { if (wpa_s->driver->get_hw_feature_data) return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv, num_modes, flags, - dfs_domain); + dfs_domain, + alpha2, alpha2_len); return NULL; } @@ -449,11 +462,12 @@ static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s, static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s, unsigned int freq, - unsigned int duration) + unsigned int duration, + const u8 *filter_addr) { if (wpa_s->driver->remain_on_channel) return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq, - duration); + duration, filter_addr); return -1; } @@ -521,6 +535,16 @@ static inline int wpa_drv_mlo_signal_poll(struct wpa_supplicant *wpa_s, return -1; } +static inline int +wpa_drv_setup_link_reconfig(struct wpa_supplicant *wpa_s, + struct wpa_mlo_reconfig_info *info) +{ + if (wpa_s->driver->setup_link_reconfig) + return wpa_s->driver->setup_link_reconfig(wpa_s->drv_priv, + info); + return -1; +} + static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s, struct wpa_channel_info *ci) { @@ -716,6 +740,46 @@ static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers); } +#ifdef CONFIG_PR + +static inline int +wpa_drv_pd_start(struct wpa_supplicant *wpa_s, const u8 *addr, u8 *pd_addr) +{ + if (!wpa_s->driver->pd_start) + return -1; + return wpa_s->driver->pd_start(wpa_s->drv_priv, addr, pd_addr); +} + +static inline void +wpa_drv_pd_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->pd_stop) + return; + wpa_s->driver->pd_stop(wpa_s->drv_priv); +} + +static inline int +wpa_drv_start_peer_measurement(struct wpa_supplicant *wpa_s, const u8 *peer, + int freq, u8 channel, int bw, + struct pr_pasn_ranging_params *params) +{ + if (!wpa_s->driver->start_peer_measurement) + return -1; + return wpa_s->driver->start_peer_measurement(wpa_s->drv_priv, peer, + freq, channel, bw, + params); +} + +static inline void +wpa_drv_stop_peer_measurement(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->stop_peer_measurement) + return; + wpa_s->driver->stop_peer_measurement(wpa_s->drv_priv); +} + +#endif /* CONFIG_PR */ + static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s, int vendor_id, int subcmd, const u8 *data, size_t data_len, @@ -1020,6 +1084,16 @@ static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s, &wpa_s->extended_capa_len); } +static inline int wpa_drv_get_mld_capa(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type, + u16 *mld_eml_capa, u16 *mld_mld_capa) +{ + if (!wpa_s->driver->get_mld_capab) + return -1; + return wpa_s->driver->get_mld_capab(wpa_s->drv_priv, type, + mld_eml_capa, mld_mld_capa); +} + static inline int wpa_drv_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int channel, unsigned int period, @@ -1175,4 +1249,141 @@ wpas_drv_get_sta_mlo_info(struct wpa_supplicant *wpa_s, return wpa_s->driver->get_sta_mlo_info(wpa_s->drv_priv, mlo_info); } +static inline int +wpas_drv_nan_flush(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->nan_flush) + return 0; + + return wpa_s->driver->nan_flush(wpa_s->drv_priv); +} + +static inline int +wpas_drv_nan_publish(struct wpa_supplicant *wpa_s, const u8 *addr, + int publish_id, const char *service_name, + const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_publish_params *params, const u8 *network_id) +{ + if (!wpa_s->driver->nan_publish) + return 0; + + return wpa_s->driver->nan_publish(wpa_s->drv_priv, addr, publish_id, + service_name, service_id, + srv_proto_type, ssi, elems, params, + network_id); +} + +static inline int +wpas_drv_nan_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id) +{ + if (!wpa_s->driver->nan_cancel_publish) + return 0; + + return wpa_s->driver->nan_cancel_publish(wpa_s->drv_priv, publish_id); +} + +static inline int +wpas_drv_nan_update_publish(struct wpa_supplicant *wpa_s, int publish_id, + const struct wpabuf *ssi) +{ + if (!wpa_s->driver->nan_update_publish) + return 0; + + return wpa_s->driver->nan_update_publish(wpa_s->drv_priv, publish_id, + ssi); +} + +static inline int +wpas_drv_nan_subscribe(struct wpa_supplicant *wpa_s, const u8 *addr, + int subscribe_id, const char *service_name, + const u8 *service_id, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, const struct wpabuf *elems, + struct nan_subscribe_params *params, + const u8 *network_id) +{ + if (!wpa_s->driver->nan_subscribe) + return 0; + + return wpa_s->driver->nan_subscribe(wpa_s->drv_priv, addr, subscribe_id, + service_name, service_id, + srv_proto_type, ssi, elems, params, + network_id); +} + +static inline int +wpas_drv_nan_cancel_subscribe(struct wpa_supplicant *wpa_s, int subscribe_id) +{ + if (!wpa_s->driver->nan_cancel_subscribe) + return 0; + + return wpa_s->driver->nan_cancel_subscribe(wpa_s->drv_priv, + subscribe_id); +} + + +#ifdef CONFIG_NAN + +static inline int wpa_drv_nan_start(struct wpa_supplicant *wpa_s, + const struct nan_cluster_config *conf) +{ + if (!wpa_s->driver->nan_start) + return -1; + return wpa_s->driver->nan_start(wpa_s->drv_priv, conf); +} + +static inline void wpa_drv_nan_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->nan_stop) + return; + wpa_s->driver->nan_stop(wpa_s->drv_priv); +} + +static inline int +wpa_drv_nan_update_config(struct wpa_supplicant *wpa_s, + const struct nan_cluster_config *conf) +{ + if (!wpa_s->driver->nan_change_config) + return -1; + return wpa_s->driver->nan_change_config(wpa_s->drv_priv, conf); +} + +static inline int wpa_drv_nan_config_schedule(struct wpa_supplicant *wpa_s, + u8 map_id, + struct nan_schedule_config *conf) +{ + if (!wpa_s->driver->nan_config_schedule) + return -1; + return wpa_s->driver->nan_config_schedule(wpa_s->drv_priv, map_id, + conf); +} + +static inline int +wpa_drv_nan_config_peer_schedule(struct wpa_supplicant *wpa_s, const u8 *peer, + u16 cdw, u8 sequence_id, + u16 max_chan_switch_time, + const struct wpabuf *ulw, + struct nan_peer_schedule_config *sched) +{ + if (!wpa_s->driver->nan_config_peer_schedule) + return -1; + return wpa_s->driver->nan_config_peer_schedule(wpa_s->drv_priv, peer, + cdw, sequence_id, + max_chan_switch_time, + ulw, sched); +} + +static inline int wpa_drv_get_inact_sec(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->get_inact_sec) + return -1; + + return wpa_s->driver->get_inact_sec(wpa_s->drv_priv, addr); +} + +#endif /* CONFIG_NAN */ + #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/eap_register.c b/contrib/wpa/wpa_supplicant/eap_register.c index 3f018c4b3c32..794ec19ad890 100644 --- a/contrib/wpa/wpa_supplicant/eap_register.c +++ b/contrib/wpa/wpa_supplicant/eap_register.c @@ -40,13 +40,6 @@ int eap_register_methods(void) ret = eap_peer_unauth_tls_register(); #endif /* EAP_UNAUTH_TLS */ -#ifdef EAP_TLS -#ifdef CONFIG_HS20 - if (ret == 0) - ret = eap_peer_wfa_unauth_tls_register(); -#endif /* CONFIG_HS20 */ -#endif /* EAP_TLS */ - #ifdef EAP_MSCHAPv2 if (ret == 0) ret = eap_peer_mschapv2_register(); diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index 0c17aaea418c..aba4d7f10dbc 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -1529,8 +1529,11 @@ int main(int argc, char *argv[]) eapol_test.wpa_s = &wpa_s; dl_list_init(&wpa_s.bss); dl_list_init(&wpa_s.bss_id); +#ifndef CONFIG_NO_ROBUST_AV + dl_list_init(&wpa_s.active_scs_ids); +#endif /* CONFIG_NO_ROBUST_AV */ if (conf) - wpa_s.conf = wpa_config_read(conf, NULL, false); + wpa_s.conf = wpa_config_read(conf, NULL, false, true); else wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL); if (wpa_s.conf == NULL) { diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index 49917f7aaf72..4f85da193608 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/crc32.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "eloop.h" @@ -50,8 +51,9 @@ #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" -#include "nan_usd.h" #include "dpp_supplicant.h" +#include "pr_supplicant.h" +#include "nan_supplicant.h" #define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5 @@ -343,12 +345,16 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s) { + int i; + if (!wpa_s->valid_links) return; wpa_s->valid_links = 0; wpa_s->mlo_assoc_link_id = 0; os_memset(wpa_s->ap_mld_addr, 0, ETH_ALEN); + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) + wpabuf_free(wpa_s->links[i].ies); os_memset(wpa_s->links, 0, sizeof(wpa_s->links)); } @@ -384,12 +390,23 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) } wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + if (wpa_s->ptksa && + ptksa_cache_get(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); + } +#endif /* CONFIG_ENC_ASSOC */ + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); sme_clear_on_disassoc(wpa_s); wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; + wpa_s->assisted_dfs = false; if (bssid_changed) wpas_notify_bssid_changed(wpa_s); @@ -428,6 +445,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->wps_scan_done = false; wpas_reset_mlo_info(wpa_s); +#ifndef CONFIG_NO_ROBUST_AV + wpas_scs_deinit(wpa_s); +#endif /* CONFIG_NO_ROBUST_AV */ #ifdef CONFIG_SME wpa_s->sme.bss_max_idle_period = 0; @@ -435,10 +455,14 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->ssid_verified = false; wpa_s->bigtk_set = false; + + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + wpa_s->ext_auth_to_same_bss = false; } -static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s, bool authorized) +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) { struct wpa_ie_data ie; int pmksa_set = -1; @@ -463,8 +487,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s, bool authorized) true); if (pmksa_set == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); - if (authorized) - wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); break; } } @@ -636,9 +659,6 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, if (wpa_key_mgmt_wpa(ssid->key_mgmt)) privacy = 1; - if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) - privacy = 1; - if (bss->caps & IEEE80211_CAP_PRIVACY) return privacy; return !privacy; @@ -670,7 +690,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); #endif /* CONFIG_WEP */ - rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsn_ie = wpa_bss_get_rsne(wpa_s, bss, ssid, false); if (is_6ghz_bss && !rsn_ie) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, @@ -678,7 +698,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, return 0; } - while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) { + while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { @@ -713,8 +733,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WEP */ - if (!(ie.proto & ssid->proto) && - !(ssid->proto & WPA_PROTO_OSEN)) { + if (!(ie.proto & ssid->proto)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto mismatch"); @@ -916,13 +935,6 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, return 0; } - if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && - wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) { - if (debug_print) - wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN"); - return 1; - } - if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); @@ -1040,10 +1052,22 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, continue; } + if (flagged && ((rate_ie[j] & 0x7f) == + BSS_MEMBERSHIP_SELECTOR_EHT_PHY)) { + if (!eht_supported(mode, IEEE80211_MODE_INFRA)) + { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support EHT PHY"); + return 0; + } + continue; + } + #ifdef CONFIG_SAE if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) { - if (wpa_s->conf->sae_pwe == + if (wpas_get_ssid_sae_pwe(wpa_s, ssid) == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id && !is_6ghz_freq(bss->freq) && @@ -1139,30 +1163,20 @@ static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 **ret_ssid, size_t *ret_ssid_len) { #ifdef CONFIG_OWE - const u8 *owe, *pos, *end, *bssid; - u8 ssid_len; + const u8 *owe, *bssid; owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); - if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN)) + if (!owe || !wpa_bss_get_rsne(wpa_s, bss, NULL, false)) return; - pos = owe + 6; - end = owe + 2 + owe[1]; - - if (end - pos < ETH_ALEN + 1) - return; - bssid = pos; - pos += ETH_ALEN; - ssid_len = *pos++; - if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + if (wpas_get_owe_trans_network(owe, &bssid, ret_ssid, ret_ssid_len)) return; /* Match the profile SSID against the OWE transition mode SSID on the * open network. */ wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR - " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len)); - *ret_ssid = pos; - *ret_ssid_len = ssid_len; + " SSID: %s", MAC2STR(bssid), + wpa_ssid_txt(*ret_ssid, *ret_ssid_len)); if (!(bss->flags & WPA_BSS_OWE_TRANSITION)) { struct wpa_ssid *ssid; @@ -1170,8 +1184,9 @@ static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (wpas_network_disabled(wpa_s, ssid)) continue; - if (ssid->ssid_len == ssid_len && - os_memcmp(ssid->ssid, pos, ssid_len) == 0) { + if (ssid->ssid_len == *ret_ssid_len && + os_memcmp(ssid->ssid, *ret_ssid, *ret_ssid_len) == + 0) { /* OWE BSS in transition mode for a currently * enabled OWE network. */ wpa_dbg(wpa_s, MSG_DEBUG, @@ -1189,9 +1204,6 @@ static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { u16 removed_links; - if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL, NULL)) - return true; - if (!bss->valid_links) return true; @@ -1229,7 +1241,7 @@ int disabled_freq(struct wpa_supplicant *wpa_s, int freq) static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *match_ssid, size_t match_ssid_len, struct wpa_bss *bss, int bssid_ignore_count, - bool debug_print); + bool debug_print, bool link); #ifdef CONFIG_SAE_PK @@ -1247,7 +1259,7 @@ static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s, if (bss == orig_bss) continue; - ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + ie = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false); if (!(ieee802_11_rsnx_capab(ie, WLAN_RSNX_CAPAB_SAE_PK))) continue; @@ -1259,7 +1271,7 @@ static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s, count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid); if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len, - bss, count, 0)) + bss, count, false, false)) return true; } @@ -1271,32 +1283,29 @@ static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s, static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const u8 *match_ssid, size_t match_ssid_len, struct wpa_bss *bss, int bssid_ignore_count, - bool debug_print) + bool debug_print, bool link) { int res; - bool wpa, check_ssid, osen, rsn_osen = false; - struct wpa_ie_data data; + bool wpa, check_ssid = false; #ifdef CONFIG_MBO const u8 *assoc_disallow; #endif /* CONFIG_MBO */ #ifdef CONFIG_SAE u8 rsnxe_capa = 0; + enum sae_pwe sae_pwe; #endif /* CONFIG_SAE */ const u8 *ie; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa = ie && ie[1]; - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + ie = wpa_bss_get_rsne(wpa_s, bss, ssid, false); wpa |= ie && ie[1]; - if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 && - (data.key_mgmt & WPA_KEY_MGMT_OSEN)) - rsn_osen = true; - ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); - osen = ie != NULL; #ifdef CONFIG_SAE - ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX); - if (ie && ie[1] >= 1) + ie = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false); + if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4 + 1) + rsnxe_capa = ie[4 + 2]; + else if (ie && ie[1] >= 1) rsnxe_capa = ie[2]; #endif /* CONFIG_SAE */ @@ -1349,7 +1358,7 @@ static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, return false; } - if (ssid->bssid_set && + if (!link && ssid->bssid_set && !ether_addr_equal(bss->bssid, ssid->bssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); @@ -1379,7 +1388,7 @@ static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print)) return false; - if (!osen && !wpa && + if (!wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) && @@ -1399,13 +1408,6 @@ static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, } #endif /* CONFIG_WEP */ - if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) { - if (debug_print) - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - non-OSEN network not allowed"); - return false; - } - if (!wpa_supplicant_match_privacy(bss, ssid)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy mismatch"); @@ -1455,10 +1457,12 @@ static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, #ifdef CONFIG_SAE /* When using SAE Password Identifier and when operationg on the 6 GHz * band, only H2E is allowed. */ - if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || + sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); + if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT || + wpa_key_mgmt_only_sae_ext_key(ssid->key_mgmt) || is_6ghz_freq(bss->freq) || ssid->sae_password_id) && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && - wpa_key_mgmt_sae(ssid->key_mgmt) && + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && + wpa_key_mgmt_only_sae(ssid->key_mgmt) && !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, @@ -1622,7 +1626,7 @@ skip_assoc_disallow: return false; } - if (!wpas_valid_ml_bss(wpa_s, bss)) { + if (!link && !wpas_valid_ml_bss(wpa_s, bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - ML BSS going to be removed"); @@ -1637,12 +1641,12 @@ skip_assoc_disallow: struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, - int only_first_ssid, int debug_print) + int only_first_ssid, int debug_print, + bool link) { u8 wpa_ie_len, rsn_ie_len; const u8 *ie; struct wpa_ssid *ssid; - int osen; const u8 *match_ssid; size_t match_ssid_len; int bssid_ignore_count; @@ -1650,15 +1654,12 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + ie = wpa_bss_get_rsne(wpa_s, bss, NULL, false); rsn_ie_len = ie ? ie[1] : 0; - ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); - osen = ie != NULL; - if (debug_print) { wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR - " ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s", + " ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, @@ -1667,8 +1668,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, " wps" : "", (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) - ? " p2p" : "", - osen ? " osen=1" : ""); + ? " p2p" : ""); } bssid_ignore_count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid); @@ -1731,7 +1731,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) { if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len, - bss, bssid_ignore_count, debug_print)) + bss, bssid_ignore_count, debug_print, link)) return ssid; } @@ -1740,7 +1740,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, } -static struct wpa_bss * +struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, struct wpa_ssid **selected_ssid, @@ -1757,7 +1757,7 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss = wpa_s->last_scan_res[i]; ssid = wpa_scan_res_match(wpa_s, i, bss, group, - only_first_ssid, 0); + only_first_ssid, 0, false); if (ssid != wpa_s->current_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR @@ -1779,7 +1779,7 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, wpa_s->owe_transition_select = 1; *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group, - only_first_ssid, 1); + only_first_ssid, 1, false); wpa_s->owe_transition_select = 0; if (!*selected_ssid) continue; @@ -1796,7 +1796,8 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, - struct wpa_ssid **selected_ssid) + struct wpa_ssid **selected_ssid, + bool clear_ignorelist) { struct wpa_bss *selected = NULL; size_t prio; @@ -1835,7 +1836,7 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, if (!selected && (wpa_s->bssid_ignore || wnm_active_bss_trans_mgmt(wpa_s)) && - !wpa_s->countermeasures) { + !wpa_s->countermeasures && clear_ignorelist) { wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear BSSID ignore list and try again"); wnm_btm_reset(wpa_s); @@ -1914,17 +1915,16 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { int *freqs; - u16 missing_links = 0, removed_links; - u8 ap_mld_id; + u16 missing_links = 0, removed_links, usable_links; + int link_id; if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))) return 0; - if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL, - &missing_links, ssid, - &ap_mld_id) || - !missing_links) + usable_links = wpa_bss_get_usable_links(wpa_s, selected, ssid, + &missing_links); + if (!usable_links || !missing_links) return 0; removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected); @@ -1947,6 +1947,21 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, freqs[0] = selected->freq; freqs[1] = 0; + /* + * Also visit the channels of the missing links. The ML probe request + * on the association link's channel is sufficient when the AP MLD + * responds to it with complete per-STA profiles, but not all APs do + * so. Scanning the channels of the missing links allows the + * information to be updated from the Beacon or Probe Response frames + * of the affiliated APs themselves. + */ + for_each_link(missing_links, link_id) { + int freq = selected->mld_links[link_id].freq; + + if (freq) + int_array_add_unique(&freqs, freq); + } + wpa_s->manual_scan_passive = 0; wpa_s->manual_scan_use_id = 0; wpa_s->manual_scan_only_new = 0; @@ -1963,7 +1978,7 @@ static int wpa_supplicant_connect_ml_missing(struct wpa_supplicant *wpa_s, * In case the ML probe requested is intended to retrieve information * from a non-transmitted BSS, the AP MLD ID should not be included. */ - if (ap_mld_id) + if (selected->mld_bss_non_transmitted) wpa_s->ml_probe_mld_id = -1; else wpa_s->ml_probe_mld_id = 0; @@ -2117,7 +2132,7 @@ static void wpa_supplicant_rsn_preauth_scan_results( if (ssid == NULL) continue; - rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsn = wpa_bss_get_rsne(wpa_s, bss, NULL, false); if (rsn == NULL) continue; @@ -2170,7 +2185,8 @@ static int wpas_evaluate_band_score(int frequency) int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss, - struct wpa_bss *selected) + struct wpa_bss *selected, + bool poll_current) { int min_diff, diff; int cur_band_score, sel_band_score; @@ -2225,7 +2241,7 @@ int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, * scan results may be a bit old, since we can very quickly get fresh * information about our currently associated AP. */ - if (wpa_drv_signal_poll(wpa_s, &si) == 0 && + if (poll_current && wpa_drv_signal_poll(wpa_s, &si) == 0 && (si.data.avg_beacon_signal || si.data.avg_signal)) { /* * Normalize avg_signal to the RSSI over 20 MHz, as the @@ -2398,13 +2414,85 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, #ifndef CONFIG_NO_ROAMING return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, - selected); + selected, true); #else /* CONFIG_NO_ROAMING */ return 0; #endif /* CONFIG_NO_ROAMING */ } +static int wpas_trigger_6ghz_scan(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_driver_scan_params params; + unsigned int j; + + wpa_dbg(wpa_s, MSG_INFO, "Triggering 6GHz-only scan"); + os_memset(¶ms, 0, sizeof(params)); + params.non_coloc_6ghz = wpa_s->last_scan_non_coloc_6ghz; + for (j = 0; j < data->scan_info.num_ssids; j++) + params.ssids[j] = data->scan_info.ssids[j]; + params.num_ssids = data->scan_info.num_ssids; + wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, + true, false, false); + if (!wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, true)) { + wpa_s->scan_in_progress_6ghz = true; + wpas_notify_scan_in_progress_6ghz(wpa_s); + os_free(params.freqs); + return 1; + } + wpa_dbg(wpa_s, MSG_INFO, "Failed to trigger 6GHz-only scan"); + os_free(params.freqs); + return 0; +} + + +static bool wpas_short_ssid_match(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + struct wpa_ssid *ssid = wpa_s->current_ssid; + u32 current_ssid_short = ieee80211_crc32(ssid->ssid, ssid->ssid_len); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *res = scan_res->res[i]; + const u8 *rnr_ie, *ie_end; + const struct ieee80211_neighbor_ap_info *info; + size_t left; + + rnr_ie = wpa_scan_get_ie(res, WLAN_EID_REDUCED_NEIGHBOR_REPORT); + if (!rnr_ie) + continue; + + ie_end = rnr_ie + 2 + rnr_ie[1]; + rnr_ie += 2; + + left = ie_end - rnr_ie; + if (left < sizeof(struct ieee80211_neighbor_ap_info)) + continue; + + info = (const struct ieee80211_neighbor_ap_info *) rnr_ie; + if (info->tbtt_info_len < 11) + continue; /* short SSID not included */ + left -= sizeof(struct ieee80211_neighbor_ap_info); + rnr_ie += sizeof(struct ieee80211_neighbor_ap_info); + + while (left >= info->tbtt_info_len && rnr_ie + 11 <= ie_end) { + /* Skip TBTT offset and BSSID */ + u32 short_ssid = WPA_GET_LE32(rnr_ie + 1 + ETH_ALEN); + + if (short_ssid == current_ssid_short) + return true; + + left -= info->tbtt_info_len; + rnr_ie += info->tbtt_info_len; + } + } + + return false; +} + + /* * Return a negative value if no scan results could be fetched or if scan * results should not be shared with other virtual interfaces. @@ -2422,6 +2510,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, int ret = 0; int ap = 0; bool trigger_6ghz_scan; + bool short_ssid_match_found = false; #ifndef CONFIG_NO_RANDOM_POOL size_t i, num; #endif /* CONFIG_NO_RANDOM_POOL */ @@ -2441,6 +2530,12 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1, NULL); + + if (wpa_s->scan_in_progress_6ghz) { + wpa_s->scan_in_progress_6ghz = false; + wpas_notify_scan_in_progress_6ghz(wpa_s); + } + if (scan_res == NULL) { if (wpa_s->conf->ap_scan == 2 || ap || wpa_s->scan_res_handler == scan_only_handler) @@ -2481,6 +2576,28 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_NO_RANDOM_POOL */ + if (data) { + size_t idx; + + wpa_s->last_scan_external = data->scan_info.external_scan; + wpa_s->last_scan_num_ssids = data->scan_info.num_ssids; + for (idx = 0; idx < wpa_s->last_scan_num_ssids; idx++) { + /* Copy the SSID and its length */ + if (idx >= WPAS_MAX_SCAN_SSIDS || + data->scan_info.ssids[idx].ssid_len > SSID_MAX_LEN) + continue; + + os_memcpy(wpa_s->last_scan_ssids[idx].ssid, + data->scan_info.ssids[idx].ssid, + data->scan_info.ssids[idx].ssid_len); + wpa_s->last_scan_ssids[idx].ssid_len = + data->scan_info.ssids[idx].ssid_len; + } + } else { + wpa_s->last_scan_external = false; + wpa_s->last_scan_num_ssids = 0; + } + if (update_only) { ret = 1; goto scan_work_done; @@ -2529,8 +2646,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } - if (wnm_scan_process(wpa_s, false) > 0) + if (data && data->scan_info.aborted) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan was aborted - do not use the incomplete scan results to trigger new operations"); goto scan_work_done; + } if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; @@ -2561,10 +2681,19 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_wps_update_ap_info(wpa_s, scan_res); + if (wnm_scan_process(wpa_s, false) > 0) + goto scan_work_done; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING && wpa_s->wpa_state < WPA_COMPLETED) goto scan_work_done; + if (wpa_s->current_ssid && trigger_6ghz_scan && own_request && data && + wpas_short_ssid_match(wpa_s, scan_res)) { + wpa_dbg(wpa_s, MSG_INFO, "Short SSID match in scan results"); + short_ssid_match_found = true; + } + wpa_scan_results_free(scan_res); if (own_request && wpa_s->scan_work) { @@ -2575,22 +2704,25 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, os_free(wpa_s->last_scan_freqs); wpa_s->last_scan_freqs = NULL; - wpa_s->num_last_scan_freqs = 0; if (own_request && data && data->scan_info.freqs && data->scan_info.num_freqs) { - wpa_s->last_scan_freqs = os_malloc(sizeof(int) * - data->scan_info.num_freqs); + wpa_s->last_scan_freqs = + os_malloc(sizeof(int) * + (data->scan_info.num_freqs + 1)); if (wpa_s->last_scan_freqs) { os_memcpy(wpa_s->last_scan_freqs, data->scan_info.freqs, sizeof(int) * data->scan_info.num_freqs); - wpa_s->num_last_scan_freqs = data->scan_info.num_freqs; + wpa_s->last_scan_freqs[data->scan_info.num_freqs] = 0; } } if (wpa_s->supp_pbc_active && !wpas_wps_partner_link_scan_done(wpa_s)) return ret; + if (short_ssid_match_found && wpas_trigger_6ghz_scan(wpa_s, data) > 0) + return 1; + return wpas_select_network_from_last_scan(wpa_s, 1, own_request, trigger_6ghz_scan, data); @@ -2605,30 +2737,6 @@ scan_work_done: } -static int wpas_trigger_6ghz_scan(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) -{ - struct wpa_driver_scan_params params; - unsigned int j; - - wpa_dbg(wpa_s, MSG_INFO, "Triggering 6GHz-only scan"); - os_memset(¶ms, 0, sizeof(params)); - params.non_coloc_6ghz = wpa_s->last_scan_non_coloc_6ghz; - for (j = 0; j < data->scan_info.num_ssids; j++) - params.ssids[j] = data->scan_info.ssids[j]; - params.num_ssids = data->scan_info.num_ssids; - wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, ¶ms, - true, !wpa_s->last_scan_non_coloc_6ghz, false); - if (!wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, true)) { - os_free(params.freqs); - return 1; - } - wpa_dbg(wpa_s, MSG_INFO, "Failed to trigger 6GHz-only scan"); - os_free(params.freqs); - return 0; -} - - /** * Select a network from the last scan * @wpa_s: Pointer to wpa_supplicant data @@ -2666,7 +2774,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, os_free(wpa_s->owe_trans_scan_freq); wpa_s->owe_trans_scan_freq = NULL; #endif /* CONFIG_OWE */ - selected = wpa_supplicant_pick_network(wpa_s, &ssid); + selected = wpa_supplicant_pick_network(wpa_s, &ssid, new_scan); #ifdef CONFIG_MESH if (wpa_s->ifmsh) { @@ -2719,7 +2827,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, wpa_supplicant_rsn_preauth_scan_results(wpa_s); } else if (own_request) { if (wpa_s->support_6ghz && trigger_6ghz_scan && data && - wpas_trigger_6ghz_scan(wpa_s, data) < 0) + wpas_trigger_6ghz_scan(wpa_s, data) > 0) return 1; /* @@ -2809,6 +2917,20 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, } +static bool equal_scan_freq_list(struct wpa_supplicant *self, + struct wpa_supplicant *other) +{ + const int *list1, *list2; + + list1 = self->conf->freq_list ? self->conf->freq_list : + self->last_scan_freqs; + list2 = other->conf->freq_list ? other->conf->freq_list : + other->last_scan_freqs; + + return int_array_equal(list1, list2); +} + + static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -2836,12 +2958,19 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } /* - * Check other interfaces to see if they share the same radio. If - * so, they get updated with this same scan info. + * Manual scan requests are more specific to a use case than the + * normal scan requests; hence, skip updating sibling radios. + */ + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ) + return 0; + + /* + * Check other interfaces to see if they share the same radio and + * frequency list. If so, they get updated with this same scan info. */ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { - if (ifs != wpa_s) { + if (ifs != wpa_s && equal_scan_freq_list(wpa_s, ifs)) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); res = _wpa_supplicant_event_scan_results(ifs, data, 0, @@ -3021,7 +3150,7 @@ static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s) goto fail; } wpa_s->enabled_4addr_mode = 1; - wpa_msg(wpa_s, MSG_INFO, "Successfully set 4addr mode"); + wpa_dbg(wpa_s, MSG_DEBUG, "Successfully set 4addr mode"); return; fail: @@ -3182,8 +3311,7 @@ static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s, wpa_s->wpa_proto = ie.proto; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, wpa_s->wpa_proto); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, - !!(wpa_s->wpa_proto & - (WPA_PROTO_RSN | WPA_PROTO_OSEN))); + !!(wpa_s->wpa_proto & WPA_PROTO_RSN)); /* Update AKMP suite from (Re)Association Request frame info */ sel = ie.key_mgmt; @@ -3264,10 +3392,11 @@ static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s, /* Update GTK and IGTK from AP's RSNE */ found = false; - if (wpa_s->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) { + if (wpa_s->wpa_proto & WPA_PROTO_RSN) { const u8 *bss_rsn; - bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + bss_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, + wpa_s->valid_links); if (bss_rsn) { p = bss_rsn; len = 2 + bss_rsn[1]; @@ -3362,17 +3491,293 @@ static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s, } +static void wpas_parse_connection_info_link(struct wpa_supplicant *wpa_s, + int i, + struct ieee802_11_elems *req_elems, + struct ieee802_11_elems *resp_elems, + struct wpabuf *req_mlbuf, + struct wpabuf *resp_mlbuf) +{ + struct ieee802_11_elems req_persta_elems = *req_elems; + struct ieee802_11_elems resp_persta_elems = *resp_elems; + struct supported_chan_width sta_cw; + enum chan_width ap_cw; + + if (ieee802_11_parse_link_assoc_req(&req_persta_elems, req_mlbuf, i, + true) == ParseFailed || + ieee802_11_parse_link_assoc_resp(&resp_persta_elems, resp_mlbuf, i, + true) == ParseFailed) { + wpa_s->links[i].max_nss_rx = wpa_s->connection_max_nss_rx; + wpa_s->links[i].max_nss_tx = wpa_s->connection_max_nss_tx; + wpa_s->links[i].channel_bandwidth = + wpa_s->connection_channel_bandwidth; + return; + } + + sta_cw = get_supported_channel_width(&req_persta_elems, + wpa_s->links[i].freq); + ap_cw = get_operation_channel_width(&resp_persta_elems); + + if (wpa_s->connection_vht || wpa_s->connection_he || + wpa_s->connection_eht) { + wpa_s->links[i].channel_bandwidth = + get_sta_operation_chan_width(ap_cw, sta_cw); + } else if (wpa_s->connection_ht) { + wpa_s->links[i].channel_bandwidth = (ap_cw == CHAN_WIDTH_40) ? + CHAN_WIDTH_40 : CHAN_WIDTH_20; + } else { + wpa_s->links[i].channel_bandwidth = CHAN_WIDTH_20; + } + + wpa_s->links[i].max_nss_rx = + MIN(get_max_nss_capability(&req_persta_elems, true, + wpa_s->links[i].channel_bandwidth), + get_max_nss_capability(&resp_persta_elems, false, + wpa_s->links[i].channel_bandwidth)); + + wpa_s->links[i].max_nss_tx = + MIN(get_max_nss_capability(&req_persta_elems, false, + wpa_s->links[i].channel_bandwidth), + get_max_nss_capability(&resp_persta_elems, true, + wpa_s->links[i].channel_bandwidth)); + +} + + +static void wpas_parse_connection_info(struct wpa_supplicant *wpa_s, + unsigned int freq, + const u8 *req_ies, size_t req_ies_len, + const u8 *resp_ies, size_t resp_ies_len) +{ + struct ieee802_11_elems req_elems, resp_elems; + int max_nss_rx_req, max_nss_rx_resp, max_nss_tx_req, max_nss_tx_resp; + struct supported_chan_width sta_supported_chan_width; + enum chan_width ap_operation_chan_width; + struct wpabuf *req_mlbuf, *resp_mlbuf; + + wpa_s->connection_set = 0; + + if (!req_ies || !resp_ies || + ieee802_11_parse_elems(req_ies, req_ies_len, &req_elems, 0) == + ParseFailed || + ieee802_11_parse_elems(resp_ies, resp_ies_len, &resp_elems, 0) == + ParseFailed) + return; + + wpa_s->connection_set = 1; + wpa_s->connection_ht = req_elems.ht_capabilities && + resp_elems.ht_capabilities; + + /* Do not include subset of VHT on 2.4 GHz vendor extension in + * consideration for reporting VHT association. */ + wpa_s->connection_vht = req_elems.vht_capabilities && + resp_elems.vht_capabilities && + (!freq || wpas_freq_to_band(freq) != BAND_2_4_GHZ); + wpa_s->connection_he = req_elems.he_capabilities && + resp_elems.he_capabilities; + wpa_s->connection_eht = req_elems.eht_capabilities && + resp_elems.eht_capabilities; + if (req_elems.rrm_enabled) + wpa_s->rrm.rrm_used = 1; + +#ifdef CONFIG_PMKSA_PRIVACY + if (wpa_s->assoc_resp_encrypted && resp_elems.nonce) { + os_memcpy(wpa_s->pmkid_anonce, resp_elems.nonce, NONCE_LEN); + wpa_s->pmkid_anonce_set = true; + wpa_hexdump(MSG_DEBUG, + "PMKID privacy: ANonce in Assoc Response", + wpa_s->pmkid_anonce, NONCE_LEN); + } + if (wpa_s->assoc_resp_encrypted && req_elems.nonce) { + os_memcpy(wpa_s->pmkid_snonce, req_elems.nonce, NONCE_LEN); + wpa_s->pmkid_snonce_set = true; + wpa_hexdump(MSG_DEBUG, + "PMKID privacy: SNonce in Assoc Request", + wpa_s->pmkid_snonce, NONCE_LEN); + } +#endif /* CONFIG_PMKSA_PRIVACY */ + + sta_supported_chan_width = get_supported_channel_width(&req_elems, + freq); + ap_operation_chan_width = get_operation_channel_width(&resp_elems); + if (wpa_s->connection_vht || wpa_s->connection_he || + wpa_s->connection_eht) { + wpa_s->connection_channel_bandwidth = + get_sta_operation_chan_width(ap_operation_chan_width, + sta_supported_chan_width); + } else if (wpa_s->connection_ht) { + wpa_s->connection_channel_bandwidth = + ap_operation_chan_width == CHAN_WIDTH_40 ? + CHAN_WIDTH_40 : CHAN_WIDTH_20; + } else { + wpa_s->connection_channel_bandwidth = CHAN_WIDTH_20; + } + + max_nss_rx_req = get_max_nss_capability( + &req_elems, 1, wpa_s->connection_channel_bandwidth); + max_nss_rx_resp = get_max_nss_capability( + &resp_elems, 1, wpa_s->connection_channel_bandwidth); + max_nss_tx_req = get_max_nss_capability( + &req_elems, 0, wpa_s->connection_channel_bandwidth); + max_nss_tx_resp = get_max_nss_capability( + &resp_elems, 0, wpa_s->connection_channel_bandwidth); + + wpa_s->connection_max_nss_rx = MIN(max_nss_tx_resp, max_nss_rx_req); + wpa_s->connection_max_nss_tx = MIN(max_nss_rx_resp, max_nss_tx_req); + + req_mlbuf = ieee802_11_defrag(req_elems.basic_mle, + req_elems.basic_mle_len, true); + resp_mlbuf = ieee802_11_defrag(resp_elems.basic_mle, + resp_elems.basic_mle_len, true); + if (req_mlbuf && resp_mlbuf) { + int i; + + for_each_link(wpa_s->valid_links, i) { + struct wpabuf *req_copy, *resp_copy; + + /* + * ieee802_11_parse_link_profile() may defragment the + * Multi-Link element subelements in-place, modifying + * the buffer contents. Parse a fresh copy for each link + * so that one link's defragmentation does not corrupt + * the buffer used when parsing the remaining links. + */ + req_copy = wpabuf_dup(req_mlbuf); + resp_copy = wpabuf_dup(resp_mlbuf); + if (req_copy && resp_copy) + wpas_parse_connection_info_link( + wpa_s, i, &req_elems, &resp_elems, + req_copy, resp_copy); + wpabuf_free(req_copy); + wpabuf_free(resp_copy); + } + } + wpabuf_free(req_mlbuf); + wpabuf_free(resp_mlbuf); +} + + +#ifdef CONFIG_P2P + +static bool is_assisted_dfs_p2p_allowed_cc(const char *country) +{ + return country[0] == 'U' && country[1] == 'S'; +} + + +static bool is_assisted_p2p_dfs_allowed(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + const u8 *elem; + const char *country; + + if (wpa_s->hw_dfs_domain != HOSTAPD_DFS_REGION_FCC || + !wpa_s->device_country_set) + return false; + + /* Country IE (alpha2 at first two bytes of the payload) */ + elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!elem || elem[1] < 2) + return false; + + country = (const char *) (elem + 2); + + /* Require the BSS country to match the device country */ + if (country[0] != wpa_s->device_country[0] || + country[1] != wpa_s->device_country[1]) + return false; + + return is_assisted_dfs_p2p_allowed_cc(country); +} + + +static bool is_dfs_owner_ap(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + if (!bss) + return false; + + /* Must NOT have P2P IE (not a P2P GO) */ + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) + return false; + + /* Must be an infrastructure mode connection, not P2P group */ + if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) + return false; + + /* Beacon interval of the connected DFS AP needs to be short enough to + * have time to inform P2P clients about the need to vacate the + * operating channel within regulatory requirements. */ + if (bss->beacon_int == 0 || bss->beacon_int > 100) + return false; + + return true; +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_ENC_ASSOC +static int wpas_rx_enc_assoc_resp(struct wpa_supplicant *wpa_s, const u8 *aa, + const u8 *resp_ies, size_t resp_ies_len) +{ + struct ptksa_cache_entry *entry; + + entry = ptksa_cache_get(wpa_s->ptksa, aa, wpa_s->pairwise_cipher); + if (!entry || (entry->auth_alg != WLAN_AUTH_EPPKE && + entry->auth_alg != WLAN_AUTH_802_1X)) + return 0; + + wpa_sm_set_ptk_kck_kek(wpa_s->wpa, entry->ptk.hash_alg, + entry->ptk.kck, entry->ptk.kck_len, + entry->ptk.kek, entry->ptk.kek_len); + +#ifdef CONFIG_TESTING_OPTIONS + wpa_sm_set_ptk_tk(wpa_s->wpa, entry->ptk.tk, entry->ptk.tk_len); +#endif /* CONFIG_TESTING_OPTIONS */ + + return process_encrypted_assoc_resp(wpa_s->wpa, + ((wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_MLO) && + wpa_s->valid_links) ? + wpa_s->valid_links : -1, + resp_ies, resp_ies_len); +} +#endif /* CONFIG_ENC_ASSOC */ + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - int l, len, found = 0, found_x = 0, wpa_found, rsn_found; - const u8 *p; + int l, len, found = 0, wpa_found, rsn_found; +#ifndef CONFIG_NO_WPA + int found_x = 0; +#endif /* CONFIG_NO_WPA */ + const u8 *p, *ie; u8 bssid[ETH_ALEN]; bool bssid_known; + enum wpa_rsn_override rsn_override; wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); wpa_s->ssid_verified = false; wpa_s->bigtk_set = false; +#ifdef CONFIG_ENC_ASSOC + /* + * For SME-in-wpa_supplicant, check the Protected bit in the frame + * header. For SME-in-driver, the full frame is not available, so use + * the assoc_encrypted flag indicated by the driver instead. + */ + if ((data->assoc_info.resp_frame && + data->assoc_info.resp_frame_len >= 2 && + (WPA_GET_LE16(data->assoc_info.resp_frame) & WLAN_FC_PROTECTED)) || + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + data->assoc_info.assoc_encrypted)) { + wpa_printf(MSG_INFO, "Association Response frame is encrypted"); + wpa_s->assoc_resp_encrypted = true; + } else { + wpa_s->assoc_resp_encrypted = false; + } +#endif /* CONFIG_ENC_ASSOC */ #ifdef CONFIG_SAE #ifdef CONFIG_SME /* SAE H2E binds the SSID into PT and that verifies the SSID @@ -3398,7 +3803,7 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, #endif /* CONFIG_WNM */ interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); - if (wpa_s->hw_capab == CAPAB_VHT && + if ((wpa_s->hw_capab & BIT(CAPAB_VHT)) && get_ie(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) wpa_s->ieee80211ac = 1; @@ -3414,35 +3819,11 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); - wpa_s->connection_set = 0; - if (data->assoc_info.req_ies && data->assoc_info.resp_ies) { - struct ieee802_11_elems req_elems, resp_elems; - - if (ieee802_11_parse_elems(data->assoc_info.req_ies, - data->assoc_info.req_ies_len, - &req_elems, 0) != ParseFailed && - ieee802_11_parse_elems(data->assoc_info.resp_ies, - data->assoc_info.resp_ies_len, - &resp_elems, 0) != ParseFailed) { - wpa_s->connection_set = 1; - wpa_s->connection_ht = req_elems.ht_capabilities && - resp_elems.ht_capabilities; - /* Do not include subset of VHT on 2.4 GHz vendor - * extension in consideration for reporting VHT - * association. */ - wpa_s->connection_vht = req_elems.vht_capabilities && - resp_elems.vht_capabilities && - (!data->assoc_info.freq || - wpas_freq_to_band(data->assoc_info.freq) != - BAND_2_4_GHZ); - wpa_s->connection_he = req_elems.he_capabilities && - resp_elems.he_capabilities; - wpa_s->connection_eht = req_elems.eht_capabilities && - resp_elems.eht_capabilities; - if (req_elems.rrm_enabled) - wpa_s->rrm.rrm_used = 1; - } - } + wpas_parse_connection_info(wpa_s, data->assoc_info.freq, + data->assoc_info.req_ies, + data->assoc_info.req_ies_len, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; @@ -3464,21 +3845,43 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; found = 1; - wpa_find_assoc_pmkid(wpa_s, - data->assoc_info.authorized); + wpa_find_assoc_pmkid(wpa_s); } +#ifndef CONFIG_NO_WPA if (!found_x && p[0] == WLAN_EID_RSNX) { if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len)) break; found_x = 1; } +#endif /* CONFIG_NO_WPA */ l -= len; p += len; } if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifndef CONFIG_NO_WPA if (!found_x && data->assoc_info.req_ies) wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); +#endif /* CONFIG_NO_WPA */ + + rsn_override = RSN_OVERRIDE_NOT_USED; + ie = get_vendor_ie(data->assoc_info.req_ies, + data->assoc_info.req_ies_len, + RSN_SELECTION_IE_VENDOR_TYPE); + if (ie && ie[1] >= 4 + 1) { + switch (ie[2 + 4]) { + case RSN_SELECTION_RSNE: + rsn_override = RSN_OVERRIDE_RSNE; + break; + case RSN_SELECTION_RSNE_OVERRIDE: + rsn_override = RSN_OVERRIDE_RSNE_OVERRIDE; + break; + case RSN_SELECTION_RSNE_OVERRIDE_2: + rsn_override = RSN_OVERRIDE_RSNE_OVERRIDE_2; + break; + } + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, rsn_override); #ifdef CONFIG_FILS #ifdef CONFIG_SME @@ -3507,6 +3910,19 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1); #endif /* CONFIG_FILS */ +#ifdef CONFIG_ENC_ASSOC + if (wpa_s->assoc_resp_encrypted && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) && + wpas_rx_enc_assoc_resp(wpa_s, wpa_s->valid_links ? + wpa_s->ap_mld_addr : bssid, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len) < 0) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + return -1; + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_OWE if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA) && @@ -3665,9 +4081,21 @@ no_pfs: wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len); } + if (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + WPA_GET_BE32(&p[2]) == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) + wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, p, len); + + if (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && + WPA_GET_BE32(&p[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE) + wpa_sm_set_ap_rsne_override(wpa_s->wpa, p, len); + if (p[0] == WLAN_EID_RSNX && p[1] >= 1) wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len); + if (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 5 && + WPA_GET_BE32(&p[2]) == RSNXE_OVERRIDE_IE_VENDOR_TYPE) + wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, p, len); + l -= len; p += len; } @@ -3677,6 +4105,9 @@ no_pfs: if (!rsn_found && data->assoc_info.beacon_ies) { wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsne_override(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, NULL, 0); } if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; @@ -3700,6 +4131,70 @@ no_pfs: wpa_s->assoc_freq = data->assoc_info.freq; +#ifdef CONFIG_P2P + if (wpa_s->allow_p2p_assisted_dfs && + ieee80211_is_dfs(wpa_s->assoc_freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) { + struct wpa_bss *bss = NULL; + + if (bssid_known) + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + + if (bss && is_assisted_p2p_dfs_allowed(wpa_s, bss) && + is_dfs_owner_ap(wpa_s, bss)) { + enum chan_width ap_operation_chan_width = + CHAN_WIDTH_UNKNOWN; + struct ieee802_11_elems resp_elems_local; + + /* Parse resp_ies locally to extract AP operation + * channel width */ + if (data->assoc_info.resp_ies && + ieee802_11_parse_elems( + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &resp_elems_local, 0) != ParseFailed) + ap_operation_chan_width = + get_operation_channel_width( + &resp_elems_local); + + wpa_s->assisted_dfs = true; + wpas_update_dfs_ap_info(wpa_s, wpa_s->assoc_freq, + ap_operation_chan_width, false); + } + } else if (wpa_s->allow_p2p_assisted_dfs && wpa_s->valid_links) { + /* For MLO, check other links as well for DFS */ + int i; + + for_each_link(wpa_s->valid_links, i) { + struct wpa_bss *link_bss; + + if (wpa_s->links[i].freq == wpa_s->assoc_freq) + continue; + + if (ieee80211_is_dfs(wpa_s->links[i].freq, + wpa_s->hw.modes, + wpa_s->hw.num_modes)) { + link_bss = wpa_s->links[i].bss; + if (!link_bss) + link_bss = wpa_bss_get_bssid( + wpa_s, wpa_s->links[i].bssid); + + if (link_bss && + is_assisted_p2p_dfs_allowed(wpa_s, + link_bss) && + is_dfs_owner_ap(wpa_s, link_bss)) { + wpa_s->assisted_dfs = true; + wpas_update_dfs_ap_info( + wpa_s, wpa_s->links[i].freq, + wpa_s->links[i].channel_bandwidth, + false); + break; + } + } + } + } +#endif /* CONFIG_P2P */ + #ifndef CONFIG_NO_ROBUST_AV wpas_handle_assoc_resp_qos_mgmt(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); @@ -3712,6 +4207,7 @@ no_pfs: static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) { const u8 *bss_wpa = NULL, *bss_rsn = NULL, *bss_rsnx = NULL; + const u8 *rsnoe, *rsno2e, *rsnxoe; if (!wpa_s->current_bss || !wpa_s->current_ssid) return -1; @@ -3723,13 +4219,25 @@ static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN); bss_rsnx = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX); + rsnoe = wpa_bss_get_vendor_ie(wpa_s->current_bss, + RSNE_OVERRIDE_IE_VENDOR_TYPE); + rsno2e = wpa_bss_get_vendor_ie(wpa_s->current_bss, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + rsnxoe = wpa_bss_get_vendor_ie(wpa_s->current_bss, + RSNXE_OVERRIDE_IE_VENDOR_TYPE); if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, bss_rsn ? 2 + bss_rsn[1] : 0) || wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx, - bss_rsnx ? 2 + bss_rsnx[1] : 0)) + bss_rsnx ? 2 + bss_rsnx[1] : 0) || + wpa_sm_set_ap_rsne_override(wpa_s->wpa, rsnoe, + rsnoe ? 2 + rsnoe[1] : 0) || + wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, rsno2e, + rsno2e ? 2 + rsno2e[1] : 0) || + wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, rsnxoe, + rsnxoe ? 2 + rsnxoe[1] : 0)) return -1; return 0; @@ -3785,10 +4293,9 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, struct eht_ml_basic_common_info *common_info; const u8 *pos; u16 eml_capa = 0, mld_capa = 0; - const u16 control = - host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC | - BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | - BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT); + const u16 control = MULTI_LINK_CONTROL_TYPE_BASIC | + BASIC_MULTI_LINK_CTRL_PRES_LINK_ID | + BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT; u8 expected_common_info_len = 9; unsigned int i = 0; u16 ml_control; @@ -3845,13 +4352,20 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, } common_info = (struct eht_ml_basic_common_info *) ml->variable; - if (common_info->len != expected_common_info_len) { + if (common_info->len < expected_common_info_len) { wpa_printf(MSG_DEBUG, "MLD: Invalid common info len=%u. expected=%u", common_info->len, expected_common_info_len); goto out; } + if (sizeof(*ml) + common_info->len > ml_len) { + wpa_printf(MSG_DEBUG, + "MLD: Truncated common info (common_info->len=%u ml_len=%zu)", + common_info->len, ml_len); + goto out; + } + wpa_printf(MSG_DEBUG, "MLD: address: " MACSTR, MAC2STR(common_info->mld_addr)); @@ -3864,7 +4378,12 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, pos = common_info->variable; /* Store the information for the association link */ - ml_info[i].link_id = *pos; + ml_info[i].link_id = *pos & EHT_ML_LINK_ID_MSK; + if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid Link ID value for assoc link"); + goto out; + } pos++; /* Skip the BSS Parameters Change Count */ @@ -3893,28 +4412,41 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, pos = ((u8 *) common_info) + common_info->len; ml_len -= sizeof(*ml) + common_info->len; while (ml_len > 2 && i < MAX_NUM_MLD_LINKS) { - u8 sub_elem_len = pos[1]; - u8 sta_info_len; + size_t sub_elem_len, sta_info_len, sta_info_len_min; u8 nstr_bitmap_len = 0; u16 ctrl; const u8 *end; + int num_frag_subelems; - wpa_printf(MSG_DEBUG, "MLD: Subelement len=%u", sub_elem_len); + num_frag_subelems = + ieee802_11_defrag_mle_subelem(mlbuf, pos, + &sub_elem_len); + if (num_frag_subelems < 0) { + wpa_printf(MSG_DEBUG, + "MLD: Failed to parse MLE subelem"); + goto out; + } + + if ((size_t) num_frag_subelems * 2 > ml_len) + goto out; + ml_len -= num_frag_subelems * 2; + + wpa_printf(MSG_DEBUG, "MLD: Subelement len=%zu", sub_elem_len); if (sub_elem_len > ml_len - 2) { wpa_printf(MSG_DEBUG, - "MLD: Invalid link info len: %u > %zu", + "MLD: Invalid link info len: %zu > %zu", 2 + sub_elem_len, ml_len); goto out; } switch (*pos) { - case EHT_ML_SUB_ELEM_PER_STA_PROFILE: + case MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE: break; - case EHT_ML_SUB_ELEM_FRAGMENT: - case EHT_ML_SUB_ELEM_VENDOR: + case MULTI_LINK_SUB_ELEM_ID_FRAGMENT: + case MULTI_LINK_SUB_ELEM_ID_VENDOR: wpa_printf(MSG_DEBUG, - "MLD: Skip subelement id=%u, len=%u", + "MLD: Skip subelement id=%u, len=%zu", *pos, sub_elem_len); pos += 2 + sub_elem_len; ml_len -= 2 + sub_elem_len; @@ -3940,56 +4472,66 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, pos += 2; ml_len -= 2; - if (!(ctrl & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE)) { wpa_printf(MSG_DEBUG, "MLD: Per STA complete profile expected"); goto out; } - if (!(ctrl & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC)) { wpa_printf(MSG_DEBUG, "MLD: Per STA MAC address not present"); goto out; } - if (!(ctrl & EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET)) { wpa_printf(MSG_DEBUG, "MLD: Per STA TSF offset not present"); goto out; } - if (!(ctrl & EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_PRES_BEACON_INT)) { wpa_printf(MSG_DEBUG, "MLD: Beacon interval not present"); goto out; } - if (!(ctrl & EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_PRES_DTIM_INFO)) { wpa_printf(MSG_DEBUG, "MLD: DTIM information not present"); goto out; } - if (ctrl & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) { - if (ctrl & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK) + if (ctrl & BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR) { + if (ctrl & BASIC_MLE_STA_CTRL_NSTR_BITMAP) nstr_bitmap_len = 2; else nstr_bitmap_len = 1; } - if (!(ctrl & EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK)) { + if (!(ctrl & BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT)) { wpa_printf(MSG_DEBUG, "MLD: BSS params change count not present"); goto out; } - sta_info_len = 1 + ETH_ALEN + 8 + 2 + 2 + 1 + nstr_bitmap_len; - if (sta_info_len > ml_len || sta_info_len > end - pos || - sta_info_len + 2 > sub_elem_len || - sta_info_len != *pos) { + sta_info_len_min = 1 + ETH_ALEN + 8 + 2 + 2 + 1 + + nstr_bitmap_len; + if (sta_info_len_min > ml_len || + sta_info_len_min > (size_t) (end - pos) || + sta_info_len_min + 2 > sub_elem_len) { wpa_printf(MSG_DEBUG, - "MLD: Invalid STA info len=%u, len=%u", - sta_info_len, *pos); + "MLD: Invalid STA info min len=%zu, len=%u", + sta_info_len_min, *pos); + goto out; + } + sta_info_len = *pos; + if (sta_info_len > ml_len || + sta_info_len > sub_elem_len - 2 || + sta_info_len < sta_info_len_min) { + wpa_printf(MSG_DEBUG, + "MLD: Invalid STA info min len=%zu, len=%zu", + sta_info_len_min, sta_info_len); goto out; } @@ -3998,13 +4540,17 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, "MLD: link addr: " MACSTR " nstr BM len=%u", MAC2STR(pos + 1), nstr_bitmap_len); - ml_info[i].link_id = ctrl & EHT_PER_STA_CTRL_LINK_ID_MSK; + ml_info[i].link_id = ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK; + if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, "MLD: Invalid Link ID value"); + goto out; + } os_memcpy(ml_info[i].bssid, pos + 1, ETH_ALEN); pos += sta_info_len; ml_len -= sta_info_len; - wpa_printf(MSG_DEBUG, "MLD: sub_elem_len=%u, sta_info_len=%u", + wpa_printf(MSG_DEBUG, "MLD: sub_elem_len=%zu, sta_info_len=%zu", sub_elem_len, sta_info_len); sub_elem_len -= sta_info_len + 2; @@ -4016,6 +4562,8 @@ static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s, wpa_hexdump(MSG_MSGDUMP, "MLD: STA profile", pos, sub_elem_len); ml_info[i].status = WPA_GET_LE16(pos + 2); + if (sub_elem_len > ml_len) + goto out; pos += sub_elem_len; ml_len -= sub_elem_len; @@ -4082,7 +4630,6 @@ static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s) { struct driver_sta_mlo_info drv_mlo; struct wpa_sm_mlo wpa_mlo; - const u8 *bss_rsn = NULL, *bss_rsnx = NULL; int i; os_memset(&drv_mlo, 0, sizeof(drv_mlo)); @@ -4102,6 +4649,7 @@ static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s) for_each_link(drv_mlo.req_links, i) { struct wpa_bss *bss; + const u8 *rsne, *rsnxe, *rsnoe, *rsno2e, *rsnxoe; bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid); if (!bss) { @@ -4110,13 +4658,25 @@ static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s) return -1; } - bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); - bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsnoe = wpa_bss_get_vendor_ie(bss, + RSNE_OVERRIDE_IE_VENDOR_TYPE); + rsno2e = wpa_bss_get_vendor_ie(bss, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + rsnxoe = wpa_bss_get_vendor_ie(bss, + RSNXE_OVERRIDE_IE_VENDOR_TYPE); - wpa_mlo.links[i].ap_rsne = bss_rsn ? (u8 *) bss_rsn : NULL; - wpa_mlo.links[i].ap_rsne_len = bss_rsn ? 2 + bss_rsn[1] : 0; - wpa_mlo.links[i].ap_rsnxe = bss_rsnx ? (u8 *) bss_rsnx : NULL; - wpa_mlo.links[i].ap_rsnxe_len = bss_rsnx ? 2 + bss_rsnx[1] : 0; + wpa_mlo.links[i].ap_rsne = rsne ? (u8 *) rsne : NULL; + wpa_mlo.links[i].ap_rsne_len = rsne ? 2 + rsne[1] : 0; + wpa_mlo.links[i].ap_rsnxe = rsnxe ? (u8 *) rsnxe : NULL; + wpa_mlo.links[i].ap_rsnxe_len = rsnxe ? 2 + rsnxe[1] : 0; + wpa_mlo.links[i].ap_rsnoe = rsnoe ? (u8 *) rsnoe : NULL; + wpa_mlo.links[i].ap_rsnoe_len = rsnoe ? 2 + rsnoe[1] : 0; + wpa_mlo.links[i].ap_rsno2e = rsno2e ? (u8 *) rsno2e : NULL; + wpa_mlo.links[i].ap_rsno2e_len = rsno2e ? 2 + rsno2e[1] : 0; + wpa_mlo.links[i].ap_rsnxoe = rsnxoe ? (u8 *) rsnxoe : NULL; + wpa_mlo.links[i].ap_rsnxoe_len = rsnxoe ? 2 + rsnxoe[1] : 0; os_memcpy(wpa_mlo.links[i].bssid, drv_mlo.links[i].bssid, ETH_ALEN); @@ -4129,8 +4689,8 @@ out: } -static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static int wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { u8 bssid[ETH_ALEN]; int ft_completed, already_authorized; @@ -4142,13 +4702,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, #ifdef CONFIG_AP if (wpa_s->ap_iface) { if (!data) - return; + return 0; hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, NULL, 0, NULL, data->assoc_info.reassoc); - return; + return 0; } #endif /* CONFIG_AP */ @@ -4161,14 +4721,14 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); - return; + return -1; } if (wpa_drv_get_mlo_info(wpa_s) < 0) { wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO connection info"); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); - return; + return -1; } if (ft_completed && @@ -4178,13 +4738,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (!wpa_supplicant_update_current_bss(wpa_s, bssid)) { wpa_printf(MSG_ERROR, "Can't find target AP's information!"); - return; + return 0; } wpa_supplicant_assoc_update_ie(wpa_s); } if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) - return; + return -1; /* * FILS authentication can share the same mechanism to mark the * connection fully authenticated, so set ft_completed also based on @@ -4193,6 +4753,12 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (!ft_completed) ft_completed = wpa_fils_is_completed(wpa_s->wpa); + if (!ft_completed) + ft_completed = wpa_eppke_is_completed(wpa_s->wpa); + + if (!ft_completed) + ft_completed = wpa_eap_over_auth_frame_is_completed(wpa_s->wpa); + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); if (!ether_addr_equal(bssid, wpa_s->bssid)) { if (os_reltime_initialized(&wpa_s->session_start)) { @@ -4219,13 +4785,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_supplicant_select_config(wpa_s, data) < 0) { wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); - return; + return -1; } } if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && data && wpa_supplicant_use_own_rsne_params(wpa_s, data) < 0) - return; + return -1; multi_ap_set_4addr_mode(wpa_s); @@ -4260,12 +4826,42 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } wpa_sm_notify_assoc(wpa_s->wpa, bssid); +#ifdef CONFIG_PMKSA_PRIVACY + if (wpa_s->pmkid_anonce_set && wpa_s->pmkid_snonce_set && + wpa_s->assoc_resp_encrypted && + wpa_sm_pmksa_privacy_supported(wpa_s->wpa) && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) && + ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PMKSA_PRIVACY))) { + struct rsn_pmksa_cache *t = wpa_sm_get_pmksa_cache(wpa_s->wpa); + const u8 *addr = wpa_s->valid_links ? + wpa_s->ap_mld_addr : wpa_s->bssid; + struct rsn_pmksa_cache_entry *e; + + e = pmksa_cache_get(t, addr, NULL, NULL, 0, wpa_s->key_mgmt); + if (e && (e->auth_alg == WLAN_AUTH_EPPKE || + e->auth_alg == WLAN_AUTH_802_1X)) { + rsn_pmkid_privacy(wpa_s->pmkid_anonce, + wpa_s->pmkid_snonce, + wpa_s->key_mgmt, e->pmk_len, + e->pmkid); + } else { + wpa_printf(MSG_DEBUG, + "PMKID privacy: PMKSA cache entry not found for " + MACSTR, MAC2STR(addr)); + } + wpa_s->pmkid_anonce_set = false; + wpa_s->pmkid_snonce_set = false; + } +#endif /* CONFIG_PMKSA_PRIVACY */ + if (wpa_sm_set_ml_info(wpa_s)) { wpa_dbg(wpa_s, MSG_INFO, "Failed to set MLO connection info to wpa_sm"); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); - return; + return -1; } if (wpa_s->l2) @@ -4325,8 +4921,10 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, true); eapol_sm_notify_eap_success(wpa_s->eapol, true); - } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && - wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + } else if (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) || + ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OWE_OFFLOAD_STA) && + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE)) { if (already_authorized) { /* * We are done; the driver will take care of RSN 4-way @@ -4355,6 +4953,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } wpa_s->last_eapol_matches_bssid = 0; + wpa_s->ext_auth_to_same_bss = false; #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->rsne_override_eapol) { @@ -4415,7 +5014,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); - return; + return -1; } ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk); @@ -4457,6 +5056,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode) wpa_supplicant_set_4addr_mode(wpa_s); + + return 0; } @@ -4474,15 +5075,6 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, { const u8 *bssid; - if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { - /* - * At least Host AP driver and a Prism3 card seemed to be - * generating streams of disconnected events when configuring - * IBSS for WPA-None. Ignore them for now. - */ - return; - } - bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; @@ -4515,6 +5107,8 @@ static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code, if (locally_generated) { if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS) return 0; + if (reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY) + return 0; } return 1; @@ -4530,17 +5124,6 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, struct wpa_ssid *fast_reconnect_ssid = NULL; struct wpa_bss *curr = NULL; - if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { - /* - * At least Host AP driver and a Prism3 card seemed to be - * generating streams of disconnected events when configuring - * IBSS for WPA-None. Ignore them for now. - */ - wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in " - "IBSS/WPA-None mode"); - return; - } - if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING && reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY && locally_generated) @@ -4559,12 +5142,12 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, "pre-shared key may be incorrect"); if (wpas_p2p_4way_hs_failed(wpa_s) > 0) return; /* P2P group removed */ + bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpa_bssid_ignore_add(wpa_s, bssid); wpas_auth_failed(wpa_s, "WRONG_KEY", wpa_s->pending_bssid); wpas_notify_psk_mismatch(wpa_s); -#ifdef CONFIG_DPP2 - wpas_dpp_send_conn_status_result(wpa_s, - DPP_STATUS_AUTH_FAILURE); -#endif /* CONFIG_DPP2 */ } if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || @@ -4614,16 +5197,21 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, wpa_sm_notify_disassoc(wpa_s->wpa); ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); + wpas_update_dfs_ap_info(wpa_s, 0, 0, true); + if (locally_generated) wpa_s->disconnect_reason = -reason_code; else wpa_s->disconnect_reason = reason_code; wpas_notify_disconnect_reason(wpa_s); +#ifdef CONFIG_DPP2 + wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_AUTH_FAILURE); +#endif /* CONFIG_DPP2 */ + wpa_supplicant_mark_disassoc(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); wpa_clear_keys(wpa_s, wpa_s->bssid); } - wpa_supplicant_mark_disassoc(wpa_s); if (curr) wpa_bss_remove(wpa_s, curr, "Connection to AP lost"); @@ -4799,6 +5387,7 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, if (!wpa_s->global->p2p && !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && + !wpas_is_nan_iface(wpa_s) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && wpas_p2p_add_p2pdev_interface( @@ -4832,6 +5421,30 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_NAN + if (wpa_s->nan_data) { + wpa_printf(MSG_DEBUG, "%s: NAN data interface removed", + wpa_s->ifname); + + wpas_nan_data_interface_removed(wpa_s); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + break; + } + + if (wpa_s->nan_mgmt) { + wpa_printf(MSG_DEBUG, + "%s: NAN management interface removed", + wpa_s->ifname); + + /* + * Note: NAN is stopped when the interface is + * disabled. + */ + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + break; + } +#endif /* CONFIG_NAN */ + #ifdef CONFIG_MATCH_IFACE if (wpa_s->matched) { wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); @@ -4957,13 +5570,19 @@ static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211R -static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data, - size_t len) +static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len) { const u8 *sta_addr, *target_ap_addr; u16 status; wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len); + if (is_multicast_ether_addr(da)) { + wpa_printf(MSG_DEBUG, + "FT: Ignore group-addressed FT Action frame (A1=" MACSTR " A2=" MACSTR ")", + MAC2STR(da), MAC2STR(sa)); + return; + } if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) return; /* only SME case supported for now */ if (len < 1 + 2 * ETH_ALEN + 2) @@ -5163,6 +5782,9 @@ static void wpas_event_deauth(struct wpa_supplicant *wpa_s, wpa_reset_ft_completed(wpa_s->wpa); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_deauth(wpa_s, info); + wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, ie, ie_len, 1); } @@ -5254,12 +5876,40 @@ void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { bool was_6ghz_enabled; + char alpha2[3]; wpa_printf(MSG_DEBUG, "%s: Updating hw mode", ifs->ifname); + if (info && info->alpha2[0] && + info->type == REGDOM_TYPE_COUNTRY) { + ifs->device_country[0] = info->alpha2[0]; + ifs->device_country[1] = info->alpha2[1]; + ifs->device_country[2] = '\0'; + ifs->device_country_set = true; + } free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( - ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain); + ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain, + alpha2, sizeof(alpha2)); + + /* Keep hw_dfs_domain in sync with the latest driver data */ + ifs->hw_dfs_domain = dfs_domain; + + /* + * If device_country was not set via a REGDOM_TYPE_COUNTRY + * event (e.g., on Android where the country is pre-configured + * before wpa_supplicant starts and no regdom change event is + * sent), populate it from the alpha2 returned by the driver. + */ + if (!ifs->device_country_set && alpha2[0] && alpha2[1]) { + ifs->device_country[0] = alpha2[0]; + ifs->device_country[1] = alpha2[1]; + ifs->device_country[2] = '\0'; + ifs->device_country_set = true; + wpa_printf(MSG_DEBUG, + "%s: Device country code set to '%s' from hw feature data", + ifs->ifname, ifs->device_country); + } was_6ghz_enabled = ifs->is_6ghz_enabled; ifs->is_6ghz_enabled = wpas_is_6ghz_supported(ifs, true); @@ -5314,7 +5964,7 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211R if (category == WLAN_ACTION_FT) { - ft_rx_action(wpa_s, payload, plen); + ft_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen); return; } #endif /* CONFIG_IEEE80211R */ @@ -5372,6 +6022,15 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, payload[0] == QOS_QOS_MAP_CONFIG) { const u8 *pos = payload + 1; size_t qlen = plen - 1; + + if (is_multicast_ether_addr(mgmt->da)) { + wpa_printf(MSG_DEBUG, + "Interworking: Ignore group-addressed Qos Map Configure frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(mgmt->da), MAC2STR(mgmt->sa)); + return; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " MACSTR, MAC2STR(mgmt->sa)); if (ether_addr_equal(mgmt->sa, wpa_s->bssid) && @@ -5394,7 +6053,8 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { - wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); + wpas_rrm_process_neighbor_rep(wpa_s, mgmt->da, mgmt->sa, + payload + 1, plen - 1); return; } @@ -5414,16 +6074,28 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_FST */ -#ifdef CONFIG_NAN_USD - if (category == WLAN_ACTION_PUBLIC && plen >= 5 && - payload[0] == WLAN_PA_VENDOR_SPECIFIC && - WPA_GET_BE32(&payload[1]) == NAN_SDF_VENDOR_TYPE) { - payload += 5; - plen -= 5; - wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, freq, payload, plen); - return; + if ((category == WLAN_ACTION_PUBLIC || + category == WLAN_ACTION_PROTECTED_DUAL) && + plen >= 5 && payload[0] == WLAN_PA_VENDOR_SPECIFIC) { + /* Drop unprotected unicast NAN frames from paired peers */ + if (category == WLAN_ACTION_PUBLIC && + !is_multicast_ether_addr(mgmt->da) && + wpas_nan_is_peer_paired(wpa_s, mgmt->sa)) + return; + + if (WPA_GET_BE32(&payload[1]) == NAN_SDF_VENDOR_TYPE) { + payload += 5; + plen -= 5; + wpas_nan_de_rx_sdf(wpa_s, mgmt->sa, mgmt->bssid, freq, + payload, plen, rssi); + return; + } + + if (WPA_GET_BE32(&payload[1]) == NAN_NAF_VENDOR_TYPE) { + wpas_nan_rx_naf(wpa_s, mgmt, len); + return; + } } -#endif /* CONFIG_NAN_USD */ #ifdef CONFIG_DPP if (category == WLAN_ACTION_PUBLIC && plen >= 5 && @@ -5440,21 +6112,21 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, #ifndef CONFIG_NO_ROBUST_AV if (category == WLAN_ACTION_ROBUST_AV_STREAMING && payload[0] == ROBUST_AV_SCS_RESP) { - wpas_handle_robust_av_scs_recv_action(wpa_s, mgmt->sa, + wpas_handle_robust_av_scs_recv_action(wpa_s, mgmt->da, mgmt->sa, payload + 1, plen - 1); return; } if (category == WLAN_ACTION_ROBUST_AV_STREAMING && payload[0] == ROBUST_AV_MSCS_RESP) { - wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa, + wpas_handle_robust_av_recv_action(wpa_s, mgmt->da, mgmt->sa, payload + 1, plen - 1); return; } if (category == WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED && plen > 4 && WPA_GET_BE32(payload) == QM_ACTION_VENDOR_TYPE) { - wpas_handle_qos_mgmt_recv_action(wpa_s, mgmt->sa, + wpas_handle_qos_mgmt_recv_action(wpa_s, mgmt->da, mgmt->sa, payload + 4, plen - 4); return; } @@ -5599,7 +6271,8 @@ static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, wpa_s->last_eapol_matches_bssid = 1; wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr); - wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck, + wpa_sm_set_ptk_kck_kek(wpa_s->wpa, RSN_HASH_NOT_SPECIFIED, + data->assoc_info.ptk_kck, data->assoc_info.ptk_kck_len, data->assoc_info.ptk_kek, data->assoc_info.ptk_kek_len); @@ -5735,7 +6408,7 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, #ifdef CONFIG_DPP2 /* Try to follow AP's PFS policy. WLAN_STATUS_ASSOC_DENIED_UNSPEC is * the status code defined in the DPP R2 tech spec. - * WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an + * WLAN_STATUS_INVALID_AKMP is addressed in the same manner as an * interoperability workaround with older hostapd implementation. */ if (DPP_VERSION > 1 && wpa_s->current_ssid && (wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP || @@ -5744,7 +6417,7 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, wpa_s->current_ssid->dpp_pfs == 0 && (data->assoc_reject.status_code == WLAN_STATUS_ASSOC_DENIED_UNSPEC || - data->assoc_reject.status_code == WLAN_STATUS_AKMP_NOT_VALID)) { + data->assoc_reject.status_code == WLAN_STATUS_INVALID_AKMP)) { struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_bss *bss = wpa_s->current_bss; @@ -5941,6 +6614,76 @@ static void wpas_tid_link_map(struct wpa_supplicant *wpa_s, } +static void wpas_setup_link_reconfig(struct wpa_supplicant *wpa_s, + struct reconfig_info *info) +{ + const u8 *key_data = info->resp_ie; + size_t key_data_len = 0; + const u8 *ies, *end; + bool success = false; + int i; + + if (!info->added_links) { + wpa_printf(MSG_INFO, "No links to be added"); + return; + } + + if (wpa_drv_get_mlo_info(wpa_s) < 0) { + wpa_printf(MSG_INFO, + "SETUP_LINK_RECONFIG: Failed to set reconfig info to wpa_s"); + return; + } + + if (wpa_sm_set_ml_info(wpa_s)) { + wpa_printf(MSG_ERROR, + "SETUP_LINK_RECONFIG: Failed to set reconfig info to wpa_sm"); + return; + } + + wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration Status List", + info->status_list, info->count * 3); + for (i = 0; i < info->count; i++) { + if (WPA_GET_LE16(info->status_list + i * 3 + 1) == + WLAN_STATUS_SUCCESS) + success = true; + } + + if (!key_data || info->resp_ie_len == 0) + return; + + if (success) { + /* Starting with Group Key Data subfield, Key Data Length + * field */ + if (info->resp_ie_len < 1U + key_data[0]) { + wpa_printf(MSG_INFO, + "MLD: Invalid keys in the link setup response"); + return; + } + + key_data_len = key_data[0]; + key_data++; + wpa_hexdump_key(MSG_DEBUG, + "MLD: Link reconfig resp - Group Key Data", + key_data, key_data_len); + + if (wpa_sm_install_mlo_group_keys(wpa_s->wpa, key_data, + key_data_len, + info->added_links)) { + wpa_printf(MSG_ERROR, + "SETUP_LINK_RECONFIG: Failed to install group keys for added links"); + return; + } + } + + ies = key_data + key_data_len; + end = info->resp_ie + info->resp_ie_len; + wpa_hexdump(MSG_DEBUG, "MLD: Link reconfig resp - IEs", ies, end - ies); + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_LINK_RECONFIG "valid_links=0x%x", + wpa_s->valid_links); +} + + static void wpas_link_reconfig(struct wpa_supplicant *wpa_s) { u8 bssid[ETH_ALEN]; @@ -5979,6 +6722,54 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_PASN +static int wpas_pasn_auth(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct ieee802_11_elems elems; + size_t auth_length; + + auth_length = IEEE80211_HDRLEN + sizeof(mgmt->u.auth); + + if (len < auth_length) { + wpa_printf(MSG_DEBUG, "PASN: Too short Authentication frame"); + return -2; + } + + if (le_to_host16(mgmt->u.auth.auth_alg) != WLAN_AUTH_PASN) { + wpa_printf(MSG_DEBUG, + "PASN: Not a PASN Authentication frame, skip frame parsing"); + return -2; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return -2; + } + +#ifdef CONFIG_P2P + if (elems.p2p2_ie && elems.p2p2_ie_len) + return wpas_p2p_pasn_auth_rx(wpa_s, mgmt, len, freq); +#endif /* CONFIG_P2P */ +#ifdef CONFIG_PR + if (elems.proximity_ranging && elems.proximity_ranging_len) + return wpas_pr_pasn_auth_rx(wpa_s, mgmt, len, freq); +#endif /* CONFIG_PR */ +#ifdef CONFIG_NAN + if (wpa_s->nan_mgmt && elems.nan_ie && elems.nan_len) + return wpas_nan_pasn_auth_rx(wpa_s, mgmt, len); +#endif /* CONFIG_NAN */ + + return wpas_pasn_auth_rx(wpa_s, mgmt, len); +} +#endif /* CONFIG_PASN */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -6045,7 +6836,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, "Ignore unexpected EVENT_ASSOC in disconnected state"); break; } - wpa_supplicant_event_assoc(wpa_s, data); +#ifndef CONFIG_NO_ROBUST_AV + if (dl_list_len(&wpa_s->active_scs_ids) > 0) { + wpa_printf(MSG_DEBUG, + "SCS rules renegotiation required after roaming"); + wpa_s->scs_reconfigure = true; + } +#endif /* CONFIG_NO_ROBUST_AV */ + if (wpa_supplicant_event_assoc(wpa_s, data) < 0) { + wpa_printf(MSG_DEBUG, + "Skip assoc_auth handling - disconnected during assoc event processing"); + break; + } wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS; if (data && (data->assoc_info.authorized || @@ -6208,6 +7010,38 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* CONFIG_WNM */ #ifdef CONFIG_PASN +#ifdef CONFIG_P2P + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_AUTH && + !wpa_s->pasn_auth_work && + wpa_s->p2p_pasn_auth_work && + wpas_p2p_pasn_auth_tx_status(wpa_s, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_PR + if (data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_AUTH && + !wpa_s->pasn_auth_work && + wpa_s->pr_pasn_auth_work && + wpas_pr_pasn_auth_tx_status(wpa_s, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; +#endif /* CONFIG_PR */ +#ifdef CONFIG_NAN + if (wpa_s->nan_mgmt && + data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_AUTH && + !wpa_s->pasn_auth_work && + wpas_nan_pasn_auth_tx_status(wpa_s, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; +#endif /* CONFIG_NAN */ if (data->tx_status.type == WLAN_FC_TYPE_MGMT && data->tx_status.stype == WLAN_FC_STYPE_AUTH && wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data, @@ -6215,6 +7049,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->tx_status.ack) == 0) break; #endif /* CONFIG_PASN */ + + if (wpas_is_nan_iface(wpa_s) && + data->tx_status.type == WLAN_FC_TYPE_MGMT && + data->tx_status.stype == WLAN_FC_STYPE_ACTION && + wpas_nan_tx_status(wpa_s, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; + #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { #ifdef CONFIG_OFFCHANNEL @@ -6485,15 +7328,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #ifdef CONFIG_PASN if (stype == WLAN_FC_STYPE_AUTH && - wpas_pasn_auth_rx(wpa_s, mgmt, - data->rx_mgmt.frame_len) != -2) + wpas_pasn_auth(wpa_s, mgmt, data->rx_mgmt.frame_len, + data->rx_mgmt.freq) != -2) break; #endif /* CONFIG_PASN */ #ifdef CONFIG_SAE if (stype == WLAN_FC_STYPE_AUTH && !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && - (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { + ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) || + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE))) { sme_external_auth_mgmt_rx( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len); @@ -6561,11 +7405,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD wpas_nan_usd_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq, data->remain_on_channel.duration); -#endif /* CONFIG_NAN_USD */ break; case EVENT_CANCEL_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL @@ -6578,10 +7420,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_dpp_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD wpas_nan_usd_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); -#endif /* CONFIG_NAN_USD */ +#ifdef CONFIG_PR + wpas_pr_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_PR */ break; case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, @@ -6590,12 +7434,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->eapol_rx.encrypted); break; case EVENT_SIGNAL_CHANGE: - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE - "above=%d signal=%d noise=%d txrate=%lu", - data->signal_change.above_threshold, - data->signal_change.data.signal, - data->signal_change.current_noise, - data->signal_change.data.current_tx_rate); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE + "above=%d signal=%d noise=%d txrate=%lu", + data->signal_change.above_threshold, + data->signal_change.data.signal, + data->signal_change.current_noise, + data->signal_change.data.current_tx_rate); wpa_bss_update_level(wpa_s->current_bss, data->signal_change.data.signal); bgscan_notify_signal_change( @@ -6625,7 +7469,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, else wpa_sm_pmksa_cache_reconfig(wpa_s->wpa); wpa_supplicant_set_default_scan_ies(wpa_s); - if (wpa_s->p2p_mgmt) { + if (wpa_s->p2p_mgmt || wpa_s->nan_mgmt) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); break; @@ -6648,6 +7492,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_INTERFACE_DISABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); + if (wpa_s->nan_mgmt) { + wpas_nan_flush(wpa_s); + wpa_supplicant_set_state(wpa_s, + WPA_INTERFACE_DISABLED); + break; + } #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && @@ -6858,13 +7708,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, bgscan_notify_beacon_loss(wpa_s); break; case EVENT_EXTERNAL_AUTH: -#ifdef CONFIG_SAE if (!wpa_s->current_ssid) { - wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL"); + wpa_printf(MSG_DEBUG, + "EXTERNAL_AUTH: current_ssid is NULL"); break; } - sme_external_auth_trigger(wpa_s, data); -#endif /* CONFIG_SAE */ + sme_external_auth_trigger(wpa_s, &data->external_auth); break; #ifdef CONFIG_PASN case EVENT_PASN_AUTH: @@ -6917,14 +7766,46 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifdef CONFIG_DPP wpas_dpp_tx_wait_expire(wpa_s); #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD wpas_nan_usd_tx_wait_expire(wpa_s); -#endif /* CONFIG_NAN_USD */ break; case EVENT_TID_LINK_MAP: if (data) wpas_tid_link_map(wpa_s, &data->t2l_map_info); break; + case EVENT_SETUP_LINK_RECONFIG: + if (data) + wpas_setup_link_reconfig(wpa_s, &data->reconfig_info); + break; +#ifdef CONFIG_PR + case EVENT_PEER_MEASUREMENT_RESULT: + wpas_pr_measurement_result(wpa_s, + &data->peer_measurement_result); + break; + case EVENT_PEER_MEASUREMENT_COMPLETE: + wpas_pr_measurement_complete(wpa_s, + &data->peer_measurement_complete); + break; +#endif /* CONFIG_PR */ +#ifdef CONFIG_NAN + case EVENT_NAN_CLUSTER_JOIN: + wpas_nan_cluster_join(wpa_s, data->nan_cluster_join_info.bssid, + data->nan_cluster_join_info.new_cluster); + break; + case EVENT_NAN_NEXT_DW: + wpas_nan_next_dw(wpa_s, data->nan_next_dw_info.freq); + break; + case EVENT_NAN_SCHED_UPDATE_DONE: + wpas_nan_sched_update_done(wpa_s, data); + break; + case EVENT_NAN_ULW_UPDATE: + wpas_nan_ulw_update(wpa_s, data->nan_ulw_update_info.ulw, + data->nan_ulw_update_info.ulw_len); + break; + case EVENT_NAN_CHAN_EVACUATION: + wpas_nan_chan_evacuation(wpa_s, + &data->nan_chan_evacuation_info); + break; +#endif /* CONFIG_NAN */ default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c index 7d2993179660..73cc34e0abbd 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.c +++ b/contrib/wpa/wpa_supplicant/gas_query.c @@ -38,6 +38,7 @@ struct gas_query_pending { struct dl_list list; struct gas_query *gas; + struct wpa_radio_work *work; u8 addr[ETH_ALEN]; u8 dialog_token; u8 next_frag_id; @@ -46,6 +47,7 @@ struct gas_query_pending { unsigned int retry:1; unsigned int wildcard_bssid:1; unsigned int maintain_addr:1; + unsigned int sent:1; int freq; u16 status_code; struct wpabuf *req; @@ -67,7 +69,6 @@ struct gas_query { struct wpa_supplicant *wpa_s; struct dl_list pending; /* struct gas_query_pending */ struct gas_query_pending *current; - struct wpa_radio_work *work; struct os_reltime last_mac_addr_rand; int last_rand_sa_type; u8 rand_addr[ETH_ALEN]; @@ -142,10 +143,15 @@ static void gas_query_free(struct gas_query_pending *query, int del_list) if (del_list) dl_list_del(&query->list); - if (gas->work && gas->work->ctx == query) { - radio_work_done(gas->work); - gas->work = NULL; - } + if (gas->current == query) + gas->current = NULL; + + if (query->work) + radio_work_done(query->work); + + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); wpabuf_free(query->req); wpabuf_free(query->adv_proto); @@ -162,13 +168,8 @@ static void gas_query_done(struct gas_query *gas, " dialog_token=%u freq=%d status_code=%u result=%s", MAC2STR(query->addr), query->dialog_token, query->freq, query->status_code, gas_result_txt(result)); - if (gas->current == query) - gas->current = NULL; if (query->offchannel_tx_started) offchannel_send_action_done(gas->wpa_s); - eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); - eloop_cancel_timeout(gas_query_timeout, gas, query); - eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); dl_list_del(&query->list); query->cb(query->ctx, query->addr, query->dialog_token, result, query->adv_proto, query->resp, query->status_code); @@ -198,19 +199,30 @@ void gas_query_deinit(struct gas_query *gas) static struct gas_query_pending * gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token) { - struct gas_query_pending *q; + struct gas_query_pending *q, *alt = NULL; struct wpa_supplicant *wpa_s = gas->wpa_s; + /* Prefer a pending entry that matches with the current radio_work to + * avoid issues when freeing the pending entry in gas_query_free(). That + * case should not really happen, but due to the special case for + * AP MLD MAC address here, a same dialog token value might end up + * being pending and matching the same query. */ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { - if (ether_addr_equal(q->addr, addr) && - q->dialog_token == dialog_token) - return q; - if (wpa_s->valid_links && - ether_addr_equal(wpa_s->ap_mld_addr, addr) && - wpas_ap_link_address(wpa_s, q->addr)) - return q; + if (!q->sent) + continue; + if ((ether_addr_equal(q->addr, addr) && + q->dialog_token == dialog_token) || + (wpa_s->valid_links && + ether_addr_equal(wpa_s->ap_mld_addr, addr) && + wpas_ap_link_address(wpa_s, q->addr))) { + if (q == gas->current) + return q; + if (!alt) + alt = q; + } } - return NULL; + + return alt; } @@ -316,8 +328,10 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, wpabuf_len(req), wait_time, gas_query_tx_status, 0); - if (res == 0) + if (res == 0) { + query->sent = 1; query->offchannel_tx_started = 1; + } return res; } @@ -548,6 +562,14 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, return 0; } + if (pmf && is_multicast_ether_addr(da)) { + wpa_printf(MSG_DEBUG, + "GAS: Ignore group-addressed GAS frame (A1=" MACSTR + " A2=" MACSTR ") when PMF is enabled", + MAC2STR(da), MAC2STR(sa)); + return 0; + } + query = gas_query_get_pending(gas, sa, dialog_token); if (query == NULL) { wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR @@ -674,7 +696,7 @@ static int gas_query_dialog_token_available(struct gas_query *gas, { struct gas_query_pending *q; dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { - if (ether_addr_equal(dst, q->addr) && + if ((!dst || ether_addr_equal(dst, q->addr)) && dialog_token == q->dialog_token) return 0; } @@ -691,7 +713,6 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) if (deinit) { if (work->started) { - gas->work = NULL; gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT); return; } @@ -705,13 +726,11 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) wpa_msg(wpa_s, MSG_INFO, "Failed to assign random MAC address for GAS"); gas_query_free(query, 1); - radio_work_done(work); return; } os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN); } - gas->work = work; gas_query_tx_initial_req(gas, query); } @@ -745,6 +764,18 @@ static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst) * token by checking random values. Use a limit on the number of * iterations to handle the unexpected case of large number of pending * queries cleanly. */ + for (i = 0; i < 256; i++) { + /* Get a random number and check if it is not used in any + * pending entry for any peer. */ + if (os_get_random(&dialog_token, sizeof(dialog_token)) < 0) + break; + if (gas_query_dialog_token_available(gas, NULL, dialog_token)) + return dialog_token; + } + + /* In the highly unlikely case there were so many pending queries that + * no available unused dialog token was available. Try to find one that + * is unique for this specific peer. */ for (i = 0; i < 256; i++) { /* Get a random number and check if the slot is available */ if (os_get_random(&dialog_token, sizeof(dialog_token)) < 0) @@ -878,8 +909,9 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, " dialog_token=%u freq=%d", MAC2STR(query->addr), query->dialog_token, query->freq); - if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, - query) < 0) { + query->work = radio_add_work(gas->wpa_s, freq, "gas-query", 0, + gas_query_start_cb, query); + if (!query->work) { query->req = NULL; /* caller will free this in error case */ gas_query_free(query, 1); return -1; @@ -895,15 +927,6 @@ int gas_query_stop(struct gas_query *gas, u8 dialog_token) dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) { if (query->dialog_token == dialog_token) { - if (!gas->work) { - /* The pending radio work has not yet been - * started, but the pending entry has a - * reference to the soon to be freed query. - * Need to remove that radio work now to avoid - * leaving behind a reference to freed memory. - */ - radio_remove_pending_work(gas->wpa_s, query); - } gas_query_done(gas, query, GAS_QUERY_STOPPED); return 0; } diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c index 6a584651effe..ef53c4cf361f 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.c +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -29,79 +29,6 @@ #include "notify.h" -#define OSU_MAX_ITEMS 10 - -struct osu_lang_string { - char lang[4]; - char text[253]; -}; - -struct osu_icon { - u16 width; - u16 height; - char lang[4]; - char icon_type[256]; - char filename[256]; - unsigned int id; - unsigned int failed:1; -}; - -struct osu_provider { - u8 bssid[ETH_ALEN]; - u8 osu_ssid[SSID_MAX_LEN]; - u8 osu_ssid_len; - u8 osu_ssid2[SSID_MAX_LEN]; - u8 osu_ssid2_len; - char server_uri[256]; - u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ - char osu_nai[256]; - char osu_nai2[256]; - struct osu_lang_string friendly_name[OSU_MAX_ITEMS]; - size_t friendly_name_count; - struct osu_lang_string serv_desc[OSU_MAX_ITEMS]; - size_t serv_desc_count; - struct osu_icon icon[OSU_MAX_ITEMS]; - size_t icon_count; -}; - - -void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) -{ - struct wpa_bss *bss = wpa_s->current_bss; - const u8 *ie; - const u8 *ext_capa; - u32 filter = 0; - - if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) { - /* Not configuring frame filtering - BSS is not a Hotspot 2.0 - * network */ - return; - } - - ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); - - /* Check if DGAF disabled bit is zero (5th byte in the IE) */ - if (!ie || ie[1] < 5) - wpa_printf(MSG_DEBUG, - "Not configuring frame filtering - Can't extract DGAF bit"); - else if (!(ie[6] & HS20_DGAF_DISABLED)) - filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; - - ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); - if (!ext_capa || ext_capa[1] < 2) { - wpa_printf(MSG_DEBUG, - "Not configuring frame filtering - Can't extract Proxy ARP bit"); - return; - } - - if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP)) - filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | - WPA_DATA_FRAME_FILTER_FLAG_NA; - - wpa_drv_configure_frame_filters(wpa_s, filter); -} - - void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release) { int release; @@ -224,11 +151,6 @@ void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, wpabuf_put_u8(buf, 0); /* Reserved */ if (payload) wpabuf_put_data(buf, payload, payload_len); - } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) { - wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); - wpabuf_put_u8(buf, 0); /* Reserved */ - if (payload) - wpabuf_put_data(buf, payload, payload_len); } else { u8 i; wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); @@ -260,14 +182,13 @@ static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len, int inmem) + const u8 *payload, size_t payload_len) { struct wpabuf *buf; int ret = 0; int freq; struct wpa_bss *bss; int res; - struct icon_entry *icon_entry; bss = wpa_bss_get_bssid(wpa_s, dst); if (!bss) { @@ -297,295 +218,10 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - if (inmem) { - icon_entry = os_zalloc(sizeof(struct icon_entry)); - if (!icon_entry) - return -1; - os_memcpy(icon_entry->bssid, dst, ETH_ALEN); - icon_entry->file_name = os_malloc(payload_len + 1); - if (!icon_entry->file_name) { - os_free(icon_entry); - return -1; - } - os_memcpy(icon_entry->file_name, payload, payload_len); - icon_entry->file_name[payload_len] = '\0'; - icon_entry->dialog_token = res; - - dl_list_add(&wpa_s->icon_head, &icon_entry->list); - } - return ret; } -static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s, - const u8 *bssid, - const char *file_name) -{ - struct icon_entry *icon; - - dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { - if (ether_addr_equal(icon->bssid, bssid) && - os_strcmp(icon->file_name, file_name) == 0 && icon->image) - return icon; - } - - return NULL; -} - - -int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *file_name, size_t offset, size_t size, - char *reply, size_t buf_len) -{ - struct icon_entry *icon; - size_t out_size; - char *b64; - size_t b64_size; - int reply_size; - - wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)", - MAC2STR(bssid), file_name, (unsigned int) offset, - (unsigned int) size, (unsigned int) buf_len); - - icon = hs20_find_icon(wpa_s, bssid, file_name); - if (!icon || !icon->image || offset >= icon->image_len) - return -1; - if (size > icon->image_len - offset) - size = icon->image_len - offset; - out_size = buf_len - 3 /* max base64 padding */; - if (size * 4 > out_size * 3) - size = out_size * 3 / 4; - if (size == 0) - return -1; - - b64 = base64_encode(&icon->image[offset], size, &b64_size); - if (b64 && buf_len >= b64_size) { - os_memcpy(reply, b64, b64_size); - reply_size = b64_size; - } else { - reply_size = -1; - } - os_free(b64); - return reply_size; -} - - -static void hs20_free_icon_entry(struct icon_entry *icon) -{ - wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR - " dialog_token=%u file_name=%s image_len=%u", - MAC2STR(icon->bssid), icon->dialog_token, - icon->file_name ? icon->file_name : "N/A", - (unsigned int) icon->image_len); - os_free(icon->file_name); - os_free(icon->image); - os_free(icon); -} - - -int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *file_name) -{ - struct icon_entry *icon, *tmp; - int count = 0; - - if (!bssid) - wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons"); - else if (!file_name) - wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for " - MACSTR, MAC2STR(bssid)); - else - wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for " - MACSTR " file name %s", MAC2STR(bssid), file_name); - - dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, - list) { - if ((!bssid || ether_addr_equal(icon->bssid, bssid)) && - (!file_name || - os_strcmp(icon->file_name, file_name) == 0)) { - dl_list_del(&icon->list); - hs20_free_icon_entry(icon); - count++; - } - } - return count == 0 ? -1 : 0; -} - - -static void hs20_set_osu_access_permission(const char *osu_dir, - const char *fname) -{ - struct stat statbuf; - - /* Get OSU directory information */ - if (stat(osu_dir, &statbuf) < 0) { - wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s", - osu_dir); - return; - } - - if (chmod(fname, statbuf.st_mode) < 0) { - wpa_printf(MSG_WARNING, - "Cannot change the permissions for %s", fname); - return; - } - - if (lchown(fname, statbuf.st_uid, statbuf.st_gid) < 0) { - wpa_printf(MSG_WARNING, "Cannot change the ownership for %s", - fname); - } -} - - -static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s, - struct icon_entry *new_icon) -{ - struct icon_entry *icon, *tmp; - - dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, - list) { - if (icon == new_icon) - continue; - if (ether_addr_equal(icon->bssid, new_icon->bssid) && - os_strcmp(icon->file_name, new_icon->file_name) == 0) { - dl_list_del(&icon->list); - hs20_free_icon_entry(icon); - } - } -} - - -static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, - const u8 *sa, const u8 *pos, - size_t slen, u8 dialog_token) -{ - char fname[256]; - int png; - FILE *f; - u16 data_len; - struct icon_entry *icon; - - dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { - if (icon->dialog_token == dialog_token && !icon->image && - ether_addr_equal(icon->bssid, sa)) { - icon->image = os_memdup(pos, slen); - if (!icon->image) - return -1; - icon->image_len = slen; - hs20_remove_duplicate_icons(wpa_s, icon); - wpa_msg(wpa_s, MSG_INFO, - RX_HS20_ICON MACSTR " %s %u", - MAC2STR(sa), icon->file_name, - (unsigned int) icon->image_len); - return 0; - } - } - - wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File", - MAC2STR(sa)); - - if (slen < 4) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " - "value from " MACSTR, MAC2STR(sa)); - return -1; - } - - wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos); - if (*pos != 0) - return -1; - pos++; - slen--; - - if ((size_t) 1 + pos[0] > slen) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " - "value from " MACSTR, MAC2STR(sa)); - return -1; - } - wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]); - png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0; - slen -= 1 + pos[0]; - pos += 1 + pos[0]; - - if (slen < 2) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " - "value from " MACSTR, MAC2STR(sa)); - return -1; - } - data_len = WPA_GET_LE16(pos); - pos += 2; - slen -= 2; - - if (data_len > slen) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " - "value from " MACSTR, MAC2STR(sa)); - return -1; - } - - wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len); - if (wpa_s->conf->osu_dir == NULL) - return -1; - - wpa_s->osu_icon_id++; - if (wpa_s->osu_icon_id == 0) - wpa_s->osu_icon_id++; - snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s", - wpa_s->conf->osu_dir, wpa_s->osu_icon_id, - png ? "png" : "icon"); - f = fopen(fname, "wb"); - if (f == NULL) - return -1; - - hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); - - if (fwrite(pos, slen, 1, f) != 1) { - fclose(f); - unlink(fname); - return -1; - } - fclose(f); - - wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname); - return 0; -} - - -static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - if (wpa_s->fetch_osu_icon_in_progress) - hs20_next_osu_icon(wpa_s); -} - - -static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res) -{ - size_t i, j; - struct os_reltime now, tmp; - int dur; - - os_get_reltime(&now); - os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp); - dur = tmp.sec * 1000 + tmp.usec / 1000; - wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d", - dur, res); - - for (i = 0; i < wpa_s->osu_prov_count; i++) { - struct osu_provider *osu = &wpa_s->osu_prov[i]; - for (j = 0; j < osu->icon_count; j++) { - struct osu_icon *icon = &osu->icon[j]; - if (icon->id || icon->failed) - continue; - if (res < 0) - icon->failed = 1; - else - icon->id = wpa_s->osu_icon_id; - return; - } - } -} - - void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, const u8 *data, size_t slen, u8 dialog_token) @@ -593,7 +229,6 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, const u8 *pos = data; u8 subtype; struct wpa_bss_anqp *anqp = NULL; - int ret; if (slen < 2) return; @@ -664,46 +299,6 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, wpabuf_alloc_copy(pos, slen); } break; - case HS20_STYPE_OSU_PROVIDERS_LIST: - wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR - " OSU Providers list", MAC2STR(sa)); - wpa_s->num_prov_found++; - if (anqp) { - wpabuf_free(anqp->hs20_osu_providers_list); - anqp->hs20_osu_providers_list = - wpabuf_alloc_copy(pos, slen); - } - break; - case HS20_STYPE_ICON_BINARY_FILE: - ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen, - dialog_token); - if (wpa_s->fetch_osu_icon_in_progress) { - hs20_osu_icon_fetch_result(wpa_s, ret); - eloop_cancel_timeout(hs20_continue_icon_fetch, - wpa_s, NULL); - eloop_register_timeout(0, 0, hs20_continue_icon_fetch, - wpa_s, NULL); - } - break; - case HS20_STYPE_OPERATOR_ICON_METADATA: - wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR - " Operator Icon Metadata", MAC2STR(sa)); - wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen); - if (anqp) { - wpabuf_free(anqp->hs20_operator_icon_metadata); - anqp->hs20_operator_icon_metadata = - wpabuf_alloc_copy(pos, slen); - } - break; - case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: - wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR - " OSU Providers NAI List", MAC2STR(sa)); - if (anqp) { - wpabuf_free(anqp->hs20_osu_providers_nai_list); - anqp->hs20_osu_providers_nai_list = - wpabuf_alloc_copy(pos, slen); - } - break; default: wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); break; @@ -711,581 +306,6 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } -void hs20_notify_parse_done(struct wpa_supplicant *wpa_s) -{ - if (!wpa_s->fetch_osu_icon_in_progress) - return; - if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL)) - return; - /* - * We are going through icon fetch, but no icon response was received. - * Assume this means the current AP could not provide an answer to avoid - * getting stuck in fetch iteration. - */ - hs20_icon_fetch_failed(wpa_s); -} - - -static void hs20_free_osu_prov_entry(struct osu_provider *prov) -{ -} - - -void hs20_free_osu_prov(struct wpa_supplicant *wpa_s) -{ - size_t i; - for (i = 0; i < wpa_s->osu_prov_count; i++) - hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]); - os_free(wpa_s->osu_prov); - wpa_s->osu_prov = NULL; - wpa_s->osu_prov_count = 0; -} - - -static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) -{ - char fname[256]; - FILE *f; - size_t i, j; - - wpa_s->fetch_osu_info = 0; - wpa_s->fetch_osu_icon_in_progress = 0; - - if (wpa_s->conf->osu_dir == NULL) { - hs20_free_osu_prov(wpa_s); - wpa_s->fetch_anqp_in_progress = 0; - return; - } - - snprintf(fname, sizeof(fname), "%s/osu-providers.txt", - wpa_s->conf->osu_dir); - f = fopen(fname, "w"); - if (f == NULL) { - wpa_msg(wpa_s, MSG_INFO, - "Could not write OSU provider information"); - hs20_free_osu_prov(wpa_s); - wpa_s->fetch_anqp_in_progress = 0; - return; - } - - hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); - - for (i = 0; i < wpa_s->osu_prov_count; i++) { - struct osu_provider *osu = &wpa_s->osu_prov[i]; - if (i > 0) - fprintf(f, "\n"); - fprintf(f, "OSU-PROVIDER " MACSTR "\n" - "uri=%s\n" - "methods=%08x\n", - MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods); - if (osu->osu_ssid_len) { - fprintf(f, "osu_ssid=%s\n", - wpa_ssid_txt(osu->osu_ssid, - osu->osu_ssid_len)); - } - if (osu->osu_ssid2_len) { - fprintf(f, "osu_ssid2=%s\n", - wpa_ssid_txt(osu->osu_ssid2, - osu->osu_ssid2_len)); - } - if (osu->osu_nai[0]) - fprintf(f, "osu_nai=%s\n", osu->osu_nai); - if (osu->osu_nai2[0]) - fprintf(f, "osu_nai2=%s\n", osu->osu_nai2); - for (j = 0; j < osu->friendly_name_count; j++) { - fprintf(f, "friendly_name=%s:%s\n", - osu->friendly_name[j].lang, - osu->friendly_name[j].text); - } - for (j = 0; j < osu->serv_desc_count; j++) { - fprintf(f, "desc=%s:%s\n", - osu->serv_desc[j].lang, - osu->serv_desc[j].text); - } - for (j = 0; j < osu->icon_count; j++) { - struct osu_icon *icon = &osu->icon[j]; - if (icon->failed) - continue; /* could not fetch icon */ - fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n", - icon->id, icon->width, icon->height, icon->lang, - icon->icon_type, icon->filename); - } - } - fclose(f); - hs20_free_osu_prov(wpa_s); - - wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed"); - wpa_s->fetch_anqp_in_progress = 0; -} - - -void hs20_next_osu_icon(struct wpa_supplicant *wpa_s) -{ - size_t i, j; - - wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon"); - - for (i = 0; i < wpa_s->osu_prov_count; i++) { - struct osu_provider *osu = &wpa_s->osu_prov[i]; - for (j = 0; j < osu->icon_count; j++) { - struct osu_icon *icon = &osu->icon[j]; - if (icon->id || icon->failed) - continue; - - wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' " - "from " MACSTR, icon->filename, - MAC2STR(osu->bssid)); - os_get_reltime(&wpa_s->osu_icon_fetch_start); - if (hs20_anqp_send_req(wpa_s, osu->bssid, - BIT(HS20_STYPE_ICON_REQUEST), - (u8 *) icon->filename, - os_strlen(icon->filename), - 0) < 0) { - icon->failed = 1; - continue; - } - return; - } - } - - wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch"); - hs20_osu_fetch_done(wpa_s); -} - - -static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - const u8 *osu_ssid, u8 osu_ssid_len, - const u8 *osu_ssid2, u8 osu_ssid2_len, - const u8 *pos, size_t len) -{ - struct osu_provider *prov; - const u8 *end = pos + len; - u16 len2; - const u8 *pos2; - u8 uri_len, osu_method_len, osu_nai_len; - - wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len); - prov = os_realloc_array(wpa_s->osu_prov, - wpa_s->osu_prov_count + 1, - sizeof(*prov)); - if (prov == NULL) - return; - wpa_s->osu_prov = prov; - prov = &prov[wpa_s->osu_prov_count]; - os_memset(prov, 0, sizeof(*prov)); - - os_memcpy(prov->bssid, bss->bssid, ETH_ALEN); - os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len); - prov->osu_ssid_len = osu_ssid_len; - if (osu_ssid2) - os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len); - prov->osu_ssid2_len = osu_ssid2_len; - - /* OSU Friendly Name Length */ - if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " - "Friendly Name Length"); - return; - } - len2 = WPA_GET_LE16(pos); - pos += 2; - if (len2 > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " - "Friendly Name Duples"); - return; - } - pos2 = pos; - pos += len2; - - /* OSU Friendly Name Duples */ - while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) { - struct osu_lang_string *f; - u8 slen; - - slen = pos2[0]; - if (1 + slen > pos - pos2) { - wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name"); - break; - } - if (slen < 3) { - wpa_printf(MSG_DEBUG, - "Invalid OSU Friendly Name (no room for language)"); - break; - } - f = &prov->friendly_name[prov->friendly_name_count++]; - pos2++; - os_memcpy(f->lang, pos2, 3); - pos2 += 3; - slen -= 3; - os_memcpy(f->text, pos2, slen); - pos2 += slen; - } - - /* OSU Server URI */ - if (end - pos < 1) { - wpa_printf(MSG_DEBUG, - "HS 2.0: Not enough room for OSU Server URI length"); - return; - } - uri_len = *pos++; - if (uri_len > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server " - "URI"); - return; - } - os_memcpy(prov->server_uri, pos, uri_len); - pos += uri_len; - - /* OSU Method list */ - if (end - pos < 1) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " - "list length"); - return; - } - osu_method_len = pos[0]; - if (osu_method_len > end - pos - 1) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " - "list"); - return; - } - pos2 = pos + 1; - pos += 1 + osu_method_len; - while (pos2 < pos) { - if (*pos2 < 32) - prov->osu_methods |= BIT(*pos2); - pos2++; - } - - /* Icons Available Length */ - if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " - "Available Length"); - return; - } - len2 = WPA_GET_LE16(pos); - pos += 2; - if (len2 > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " - "Available"); - return; - } - pos2 = pos; - pos += len2; - - /* Icons Available */ - while (pos2 < pos) { - struct osu_icon *icon = &prov->icon[prov->icon_count]; - u8 flen; - - if (2 + 2 + 3 + 1 + 1 > pos - pos2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); - break; - } - - icon->width = WPA_GET_LE16(pos2); - pos2 += 2; - icon->height = WPA_GET_LE16(pos2); - pos2 += 2; - os_memcpy(icon->lang, pos2, 3); - pos2 += 3; - - flen = *pos2++; - if (flen > pos - pos2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); - break; - } - os_memcpy(icon->icon_type, pos2, flen); - pos2 += flen; - - if (pos - pos2 < 1) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " - "Filename length"); - break; - } - flen = *pos2++; - if (flen > pos - pos2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " - "Filename"); - break; - } - os_memcpy(icon->filename, pos2, flen); - pos2 += flen; - - prov->icon_count++; - } - - /* OSU_NAI */ - if (end - pos < 1) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); - return; - } - osu_nai_len = *pos++; - if (osu_nai_len > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); - return; - } - os_memcpy(prov->osu_nai, pos, osu_nai_len); - pos += osu_nai_len; - - /* OSU Service Description Length */ - if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " - "Service Description Length"); - return; - } - len2 = WPA_GET_LE16(pos); - pos += 2; - if (len2 > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " - "Service Description Duples"); - return; - } - pos2 = pos; - pos += len2; - - /* OSU Service Description Duples */ - while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) { - struct osu_lang_string *f; - u8 descr_len; - - descr_len = *pos2++; - if (descr_len > pos - pos2 || descr_len < 3) { - wpa_printf(MSG_DEBUG, "Invalid OSU Service " - "Description"); - break; - } - f = &prov->serv_desc[prov->serv_desc_count++]; - os_memcpy(f->lang, pos2, 3); - os_memcpy(f->text, pos2 + 3, descr_len - 3); - pos2 += descr_len; - } - - wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, - MAC2STR(bss->bssid)); - wpa_s->osu_prov_count++; -} - - -void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) -{ - struct wpa_bss *bss; - struct wpabuf *prov_anqp; - const u8 *pos, *end; - u16 len; - const u8 *osu_ssid, *osu_ssid2; - u8 osu_ssid_len, osu_ssid2_len; - u8 num_providers; - - hs20_free_osu_prov(wpa_s); - - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - struct wpa_ie_data data; - const u8 *ie; - - if (bss->anqp == NULL) - continue; - prov_anqp = bss->anqp->hs20_osu_providers_list; - if (prov_anqp == NULL) - continue; - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 && - (data.key_mgmt & WPA_KEY_MGMT_OSEN)) { - osu_ssid2 = bss->ssid; - osu_ssid2_len = bss->ssid_len; - } else { - osu_ssid2 = NULL; - osu_ssid2_len = 0; - } - wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from " - MACSTR, MAC2STR(bss->bssid)); - wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list", - prov_anqp); - pos = wpabuf_head(prov_anqp); - end = pos + wpabuf_len(prov_anqp); - - /* OSU SSID */ - if (end - pos < 1) - continue; - if (1 + pos[0] > end - pos) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " - "OSU SSID"); - continue; - } - osu_ssid_len = *pos++; - if (osu_ssid_len > SSID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID " - "Length %u", osu_ssid_len); - continue; - } - osu_ssid = pos; - pos += osu_ssid_len; - - if (end - pos < 1) { - wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " - "Number of OSU Providers"); - continue; - } - num_providers = *pos++; - wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u", - num_providers); - - /* OSU Providers */ - while (end - pos > 2 && num_providers > 0) { - num_providers--; - len = WPA_GET_LE16(pos); - pos += 2; - if (len > (unsigned int) (end - pos)) - break; - hs20_osu_add_prov(wpa_s, bss, osu_ssid, - osu_ssid_len, osu_ssid2, - osu_ssid2_len, pos, len); - pos += len; - } - - if (pos != end) { - wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of " - "extra data after OSU Providers", - (int) (end - pos)); - } - - prov_anqp = bss->anqp->hs20_osu_providers_nai_list; - if (!prov_anqp) - continue; - wpa_printf(MSG_DEBUG, - "HS 2.0: Parsing OSU Providers NAI List from " - MACSTR, MAC2STR(bss->bssid)); - wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List", - prov_anqp); - pos = wpabuf_head(prov_anqp); - end = pos + wpabuf_len(prov_anqp); - num_providers = 0; - while (end - pos > 0) { - len = *pos++; - if (end - pos < len) { - wpa_printf(MSG_DEBUG, - "HS 2.0: Not enough room for OSU_NAI"); - break; - } - if (num_providers >= wpa_s->osu_prov_count) { - wpa_printf(MSG_DEBUG, - "HS 2.0: Ignore unexpected OSU Provider NAI List entries"); - break; - } - os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2, - pos, len); - pos += len; - num_providers++; - } - } - - wpa_s->fetch_osu_icon_in_progress = 1; - hs20_next_osu_icon(wpa_s); -} - - -static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res) -{ - wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed"); - if (!wpa_s->fetch_osu_waiting_scan) { - wpa_printf(MSG_DEBUG, "OSU fetch have been canceled"); - return; - } - wpa_s->network_select = 0; - wpa_s->fetch_all_anqp = 1; - wpa_s->fetch_osu_info = 1; - wpa_s->fetch_osu_icon_in_progress = 0; - - interworking_start_fetch_anqp(wpa_s); -} - - -int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan) -{ - if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { - wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " - "interface disabled"); - return -1; - } - - if (wpa_s->scanning) { - wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " - "scanning"); - return -1; - } - - if (wpa_s->conf->osu_dir == NULL) { - wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " - "osu_dir not configured"); - return -1; - } - - if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { - wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " - "fetch in progress (%d, %d)", - wpa_s->fetch_anqp_in_progress, - wpa_s->network_select); - return -1; - } - - wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch"); - wpa_s->num_osu_scans = 0; - wpa_s->num_prov_found = 0; - if (skip_scan) { - wpa_s->network_select = 0; - wpa_s->fetch_all_anqp = 1; - wpa_s->fetch_osu_info = 1; - wpa_s->fetch_osu_icon_in_progress = 0; - - interworking_start_fetch_anqp(wpa_s); - } else { - hs20_start_osu_scan(wpa_s); - } - - return 0; -} - - -void hs20_start_osu_scan(struct wpa_supplicant *wpa_s) -{ - wpa_s->fetch_osu_waiting_scan = 1; - wpa_s->num_osu_scans++; - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_s->scan_res_handler = hs20_osu_scan_res_handler; - wpa_supplicant_req_scan(wpa_s, 0, 0); -} - - -void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s) -{ - wpa_printf(MSG_DEBUG, "Cancel OSU fetch"); - interworking_stop_fetch_anqp(wpa_s); - wpa_s->fetch_osu_waiting_scan = 0; - wpa_s->network_select = 0; - wpa_s->fetch_osu_info = 0; - wpa_s->fetch_osu_icon_in_progress = 0; -} - - -void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s) -{ - hs20_osu_icon_fetch_result(wpa_s, -1); - eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); - eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL); -} - - -void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, - const char *url, u8 osu_method) -{ - if (url) - wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s", - osu_method, url); - else - wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION); -} - - void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url) { @@ -1339,18 +359,3 @@ void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url) wpas_notify_hs20_t_c_acceptance(wpa_s, url); } - - -void hs20_init(struct wpa_supplicant *wpa_s) -{ - dl_list_init(&wpa_s->icon_head); -} - - -void hs20_deinit(struct wpa_supplicant *wpa_s) -{ - eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); - hs20_free_osu_prov(wpa_s); - if (wpa_s->icon_head.next) - hs20_del_icon(wpa_s, NULL, NULL); -} diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/contrib/wpa/wpa_supplicant/hs20_supplicant.h index e43414bc65c5..0bfdd847e7e5 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.h +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.h @@ -8,14 +8,13 @@ #ifndef HS20_SUPPLICANT_H #define HS20_SUPPLICANT_H -void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s); void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release); void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, const struct wpa_ssid *ssid); int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len, int inmem); + const u8 *payload, size_t payload_len); void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, struct wpabuf *buf); void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, @@ -25,27 +24,10 @@ int get_hs20_version(struct wpa_bss *bss); int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); -void hs20_notify_parse_done(struct wpa_supplicant *wpa_s); -void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, - const char *url, u8 osu_method); void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url); void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url); -void hs20_free_osu_prov(struct wpa_supplicant *wpa_s); -void hs20_next_osu_icon(struct wpa_supplicant *wpa_s); -void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s); -int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan); -void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s); -void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s); -void hs20_start_osu_scan(struct wpa_supplicant *wpa_s); -void hs20_init(struct wpa_supplicant *wpa_s); -void hs20_deinit(struct wpa_supplicant *wpa_s); -int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *file_name, size_t offset, size_t size, - char *reply, size_t buf_len); -int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *file_name); #endif /* HS20_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index 25039a0f98c5..06228d0ef6c9 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -150,6 +150,12 @@ static int supp_set_key(void *ctx, int link_id, enum wpa_alg alg, { struct ibss_rsn_peer *peer = ctx; + if (key_flag & KEY_FLAG_NEXT) { + wpa_printf(MSG_DEBUG, + "SUPP: Ignore set_key with KEY_FLAG_NEXT"); + return 0; + } + wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " "set_tx=%d)", __func__, alg, MAC2STR(addr), key_idx, set_tx); @@ -320,6 +326,12 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, struct ibss_rsn *ibss_rsn = ctx; u8 seq[6]; + if (key_flag & KEY_FLAG_NEXT) { + wpa_printf(MSG_DEBUG, + "AUTH: Ignore set_key with KEY_FLAG_NEXT"); + return 0; + } + os_memset(seq, 0, sizeof(seq)); if (addr) { @@ -485,7 +497,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, "\x01\x00\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x02" "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, - NULL) != WPA_IE_OK) { + NULL, false) != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); return -1; } diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index 6773a243cf54..f77880409ad7 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -303,10 +303,6 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); if (all) wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); - if (all) { - wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST); - wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); - } gas_anqp_set_element_len(extra, len_pos); } #endif /* CONFIG_HS20 */ @@ -903,7 +899,7 @@ static int already_connected(struct wpa_supplicant *wpa_s, return 0; sel_ssid = NULL; - selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid); + selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid, true); if (selected && sel_ssid && sel_ssid->priority > ssid->priority) return 0; /* higher priority network in scan results */ @@ -1496,18 +1492,18 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid, char *anon; /* Use anonymous NAI in Phase 1 */ pos = os_strchr(cred->username, '@'); - if (pos) { - size_t buflen = 9 + os_strlen(pos) + 1; - anon = os_malloc(buflen); - if (anon == NULL) - return -1; - os_snprintf(anon, buflen, "anonymous%s", pos); - } else if (cred->realm) { + if (cred->realm) { size_t buflen = 10 + os_strlen(cred->realm) + 1; anon = os_malloc(buflen); if (anon == NULL) return -1; os_snprintf(anon, buflen, "anonymous@%s", cred->realm); + } else if (pos) { + size_t buflen = 9 + os_strlen(pos) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous%s", pos); } else { anon = os_strdup("anonymous"); if (anon == NULL) @@ -1726,7 +1722,7 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, " for connection", MAC2STR(bss->bssid)); - if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + if (!wpa_bss_get_rsne(wpa_s, bss, NULL, false)) { /* * We currently support only HS 2.0 networks and those are * required to use WPA2-Enterprise. @@ -2459,7 +2455,7 @@ static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s, cred2 = interworking_credentials_available(wpa_s, bss, NULL); if (!cred2) continue; - if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) + if (!wpa_bss_get_rsne(wpa_s, bss, NULL, false)) continue; prio = roaming_prio(wpa_s, cred2, bss); wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS " @@ -2511,7 +2507,7 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) if (!cred) continue; - if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + if (!wpa_bss_get_rsne(wpa_s, bss, NULL, false)) { /* * We currently support only HS 2.0 networks and those * are required to use WPA2-Enterprise. @@ -2690,23 +2686,14 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) int found = 0; wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - " - "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d", - wpa_s->fetch_anqp_in_progress, - wpa_s->fetch_osu_icon_in_progress); + "fetch_anqp_in_progress=%d", + wpa_s->fetch_anqp_in_progress); if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) { wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch"); return; } -#ifdef CONFIG_HS20 - if (wpa_s->fetch_osu_icon_in_progress) { - wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)"); - hs20_next_osu_icon(wpa_s); - return; - } -#endif /* CONFIG_HS20 */ - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!(bss->caps & IEEE80211_CAP_ESS)) continue; @@ -2739,20 +2726,6 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) } if (found == 0) { -#ifdef CONFIG_HS20 - if (wpa_s->fetch_osu_info) { - if (wpa_s->num_prov_found == 0 && - wpa_s->fetch_osu_waiting_scan && - wpa_s->num_osu_scans < 3) { - wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again"); - hs20_start_osu_scan(wpa_s); - return; - } - wpa_printf(MSG_DEBUG, "Interworking: Next icon"); - hs20_osu_icon_fetch(wpa_s); - return; - } -#endif /* CONFIG_HS20 */ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); wpa_s->fetch_anqp_in_progress = 0; if (wpa_s->network_select) @@ -2785,7 +2758,6 @@ int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) wpa_s->network_select = 0; wpa_s->fetch_all_anqp = 1; - wpa_s->fetch_osu_info = 0; interworking_start_fetch_anqp(wpa_s); @@ -3133,10 +3105,6 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, " dialog_token=%u result=%d status_code=%u", MAC2STR(dst), dialog_token, result, status_code); if (result != GAS_QUERY_SUCCESS) { -#ifdef CONFIG_HS20 - if (wpa_s->fetch_osu_icon_in_progress) - hs20_icon_fetch_failed(wpa_s); -#endif /* CONFIG_HS20 */ anqp_result = "FAILURE"; goto out; } @@ -3146,10 +3114,6 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Unexpected Advertisement Protocol in response"); -#ifdef CONFIG_HS20 - if (wpa_s->fetch_osu_icon_in_progress) - hs20_icon_fetch_failed(wpa_s); -#endif /* CONFIG_HS20 */ anqp_result = "INVALID_FRAME"; goto out; } @@ -3200,9 +3164,6 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, out_parse_done: if (bss) wpas_notify_bss_anqp_changed(wpa_s, bss->id); -#ifdef CONFIG_HS20 - hs20_notify_parse_done(wpa_s); -#endif /* CONFIG_HS20 */ out: wpas_notify_anqp_query_done(wpa_s, dst, anqp_result); } @@ -3225,7 +3186,6 @@ int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, wpa_s->auto_network_select = 0; wpa_s->auto_select = !!auto_select; wpa_s->fetch_all_anqp = 0; - wpa_s->fetch_osu_info = 0; wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Start scan for network selection"); wpa_s->scan_res_handler = interworking_scan_res_handler; diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c index e13abc407a93..3f55446e754e 100644 --- a/contrib/wpa/wpa_supplicant/main.c +++ b/contrib/wpa/wpa_supplicant/main.c @@ -32,16 +32,19 @@ static void usage(void) #ifdef CONFIG_CTRL_IFACE_DBUS_NEW "u" #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ - "vW] [-P] " + "vWy] [-P] " "[-g] \\\n" " [-G] \\\n" - " -i -c [-C] [-D] " + " [-i] -c [-C] [-D] " "[-p] \\\n" " [-b] [-e]" #ifdef CONFIG_DEBUG_FILE " [-f]" #endif /* CONFIG_DEBUG_FILE */ " \\\n" +#ifdef CONFIG_PROCESS_COORDINATION + " [-z] \\\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " [-o] [-O] \\\n" " [-N -i -c [-C] " "[-D] \\\n" @@ -103,10 +106,19 @@ static void usage(void) " -u = enable DBus control interface\n" #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ " -v = show version\n" - " -W = wait for a control interface monitor before starting\n"); + " -W = wait for a control interface monitor before starting\n" + " -y = show configuration parsing details in debug log\n" +#ifdef CONFIG_PROCESS_COORDINATION + " -z = process coordination directory\n" +#endif /* CONFIG_PROCESS_COORDINATION */ + ); printf("example:\n" - " wpa_supplicant -Dbsd -iwlan0 -c/etc/wpa_supplicant.conf\n"); + " wpa_supplicant -Dbsd -iwlan0 -c/etc/wpa_supplicant.conf\n", + wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211"); + printf("\nIf run without specifying a network interface or control interface, a\n" + "configuration file is parsed without starting any operation.\n" + "This can be used to check whether a configuration file has valid contents.\n"); #endif /* CONFIG_NO_STDOUT_DEBUG */ } @@ -201,7 +213,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW"); + "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvWyz:"); if (c < 0) break; switch (c) { @@ -263,7 +275,8 @@ int main(int argc, char *argv[]) goto out; #ifdef CONFIG_P2P case 'm': - params.conf_p2p_dev = optarg; + os_free(params.conf_p2p_dev); + params.conf_p2p_dev = os_rel2abs_path(optarg); break; #endif /* CONFIG_P2P */ case 'o': @@ -331,6 +344,14 @@ int main(int argc, char *argv[]) iface = &ifaces[iface_count - 1]; os_memset(iface, 0, sizeof(*iface)); break; + case 'y': + params.show_details = true; + break; +#ifdef CONFIG_PROCESS_COORDINATION + case 'z': + params.proc_coord_dir = optarg; + break; +#endif /* CONFIG_PROCESS_COORDINATION */ default: usage(); exitcode = 0; @@ -344,13 +365,23 @@ int main(int argc, char *argv[]) wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); exitcode = -1; goto out; - } else { - wpa_printf(MSG_INFO, "Successfully initialized " - "wpa_supplicant"); } + if (iface_count == 1 && ifaces[0].confname && !ifaces[0].ifname && +#ifdef CONFIG_MATCH_IFACE + !params.match_iface_count && +#endif /* CONFIG_MATCH_IFACE */ + !ifaces[0].ctrl_interface && !params.dbus_ctrl_interface) { + exitcode = wpa_supplicant_parse_config(ifaces[0].confname); + wpa_supplicant_deinit(global); + goto out; + } + + wpa_printf(MSG_INFO, "Successfully initialized wpa_supplicant"); + if (fst_global_init()) { wpa_printf(MSG_ERROR, "Failed to initialize FST"); + wpa_supplicant_deinit(global); exitcode = -1; goto out; } @@ -402,6 +433,9 @@ out: os_free(params.match_ifaces); #endif /* CONFIG_MATCH_IFACE */ os_free(params.pid_file); +#ifdef CONFIG_P2P + os_free(params.conf_p2p_dev); +#endif /* CONFIG_P2P */ crypto_unload(); os_program_deinit(); diff --git a/contrib/wpa/wpa_supplicant/mbo.c b/contrib/wpa/wpa_supplicant/mbo.c index 59b15daf6dfd..dff75415df6e 100644 --- a/contrib/wpa/wpa_supplicant/mbo.c +++ b/contrib/wpa/wpa_supplicant/mbo.c @@ -115,7 +115,7 @@ void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, return; if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON)) return; /* STA-CFON is not required to enable PMF */ - rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsne = wpa_bss_get_rsne(wpa_s, bss, ssid, false); if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0) return; /* AP is not using RSN */ @@ -460,6 +460,10 @@ void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) { u8 *len; + if (wpa_s->drv_max_probe_req_ie_len < + 9 + ((wpa_s->enable_oce & OCE_STA) ? 3 : 0)) + return; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(ie, 1); diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 85c1ea8ba270..8f7143f6ea63 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -150,30 +150,12 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, } -static void wpas_mesh_copy_groups(struct hostapd_data *bss, - struct wpa_supplicant *wpa_s) -{ - int num_groups; - size_t groups_size; - - for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0; - num_groups++) - ; - - groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]); - bss->conf->sae_groups = os_malloc(groups_size); - if (bss->conf->sae_groups) - os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups, - groups_size); -} - - static int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s) { struct hostapd_iface *ifmsh = wpa_s->ifmsh; struct wpa_ssid *ssid = wpa_s->current_ssid; struct hostapd_data *bss = ifmsh->bss[0]; - static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; + static int default_groups[] = { 19, 20, 21, 25, 26, 0 }; const char *password; size_t len; @@ -189,14 +171,12 @@ static int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s) bss->conf->wpa = ssid->proto; bss->conf->wpa_key_mgmt = ssid->key_mgmt; - if (wpa_s->conf->sae_groups && wpa_s->conf->sae_groups[0] > 0) { - wpas_mesh_copy_groups(bss, wpa_s); - } else { - bss->conf->sae_groups = os_memdup(default_groups, - sizeof(default_groups)); - if (!bss->conf->sae_groups) - return -1; - } + if (wpa_s->conf->sae_groups && wpa_s->conf->sae_groups[0] > 0) + bss->conf->sae_groups = int_array_dup(wpa_s->conf->sae_groups); + else + bss->conf->sae_groups = int_array_dup(default_groups); + if (!bss->conf->sae_groups) + return -1; len = os_strlen(password); bss->conf->ssid.wpa_passphrase = dup_binstr(password, len); @@ -281,7 +261,7 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s) params->ies = ifmsh->mconf->rsn_ie; params->ie_len = ifmsh->mconf->rsn_ie_len; - params->basic_rates = ifmsh->basic_rates; + params->basic_rates = ifmsh->bss[0]->basic_rates; params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode; @@ -386,9 +366,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, struct hostapd_data *bss; struct hostapd_config *conf; struct mesh_conf *mconf; - int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; - int rate_len; + int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, 0 }; int frequency; + bool is_dfs; + u8 chan; if (!wpa_s->conf->user_mpm) { /* not much for us to do here */ @@ -464,6 +445,9 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, case 160: conf->op_class = 134; break; + case 320: + conf->op_class = 137; + break; default: conf->op_class = 131; break; @@ -476,8 +460,35 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity; bss->conf->mesh_fwding = wpa_s->conf->mesh_fwding; - if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, - wpa_s->hw.num_modes) && wpa_s->conf->country[0]) { + ieee80211_freq_to_chan(freq->center_freq1, &chan); + if (wpa_s->mesh_vht_enabled) { + if (freq->bandwidth == 80) + conf->vht_oper_chwidth = CONF_OPER_CHWIDTH_80MHZ; + else if (freq->bandwidth == 160) + conf->vht_oper_chwidth = CONF_OPER_CHWIDTH_160MHZ; + conf->vht_oper_centr_freq_seg0_idx = chan; + } + +#ifdef CONFIG_IEEE80211AX + if (wpa_s->mesh_he_enabled) { + if (freq->bandwidth == 80) + conf->he_oper_chwidth = CONF_OPER_CHWIDTH_80MHZ; + else if (freq->bandwidth == 160) + conf->he_oper_chwidth = CONF_OPER_CHWIDTH_160MHZ; + conf->he_oper_centr_freq_seg0_idx = chan; + } +#endif /* CONFIG_IEEE80211AX */ + + is_dfs = ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, + wpa_s->hw.num_modes); + + /* Check if secondary 80 MHz of 160 MHz has DFS channels */ + if (!is_dfs && freq->bandwidth == 160) + is_dfs = ieee80211_is_dfs(ssid->frequency + 80, + wpa_s->hw.modes, + wpa_s->hw.num_modes); + + if (is_dfs && wpa_s->conf->country[0]) { conf->ieee80211h = 1; conf->ieee80211d = 1; conf->country[0] = wpa_s->conf->country[0]; @@ -516,24 +527,16 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, * advertised in beacons match the one in peering frames, sigh. */ if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { - conf->basic_rates = os_memdup(basic_rates_erp, - sizeof(basic_rates_erp)); - if (!conf->basic_rates) + bss->conf->basic_rates = os_memdup( + basic_rates_erp, + sizeof(basic_rates_erp)); + if (!bss->conf->basic_rates) goto out_free; } } else { - rate_len = 0; - while (1) { - if (ssid->mesh_basic_rates[rate_len] < 1) - break; - rate_len++; - } - conf->basic_rates = os_calloc(rate_len + 1, sizeof(int)); - if (conf->basic_rates == NULL) + bss->conf->basic_rates = int_array_dup(ssid->mesh_basic_rates); + if (bss->conf->basic_rates == NULL) goto out_free; - os_memcpy(conf->basic_rates, ssid->mesh_basic_rates, - rate_len * sizeof(int)); - conf->basic_rates[rate_len] = -1; } /* While it can enhance performance to switch the primary channel, which @@ -602,7 +605,8 @@ void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, /* EID + 0-length (wildcard) mesh-id */ size_t ielen = 2; - if (wpabuf_resize(extra_ie, ielen) == 0) { + if (ielen <= wpa_s->drv_max_probe_req_ie_len && + wpabuf_resize(extra_ie, ielen) == 0) { wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID); wpabuf_put_u8(*extra_ie, 0); } diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index b2fc1271ea5a..66684c99c67d 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -1203,7 +1203,9 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, } /* check for mesh peering, mesh id and mesh config IEs */ - if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { + os_memset(&elems, 0, sizeof(elems)); + elems.stop_at_mic = true; + if (ieee802_11_parse_elems_ctrl(ies, ie_len, &elems) == ParseFailed) { wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs"); return; } diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.c b/contrib/wpa/wpa_supplicant/mesh_rsn.c index ada53c7966a2..67b27ddb2746 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.c +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.c @@ -356,9 +356,10 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, } if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) { - sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id); + sta->sae->tmp->pw_id = (u8 *) os_strdup(ssid->sae_password_id); if (!sta->sae->tmp->pw_id) return -1; + sta->sae->tmp->pw_id_len = os_strlen(ssid->sae_password_id); } return sae_prepare_commit(wpa_s->own_addr, sta->addr, (u8 *) password, os_strlen(password), @@ -653,7 +654,9 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, u8 null_nonce[WPA_NONCE_LEN] = {}; u8 ampe_eid; u8 ampe_ie_len; - u8 *ampe_buf, *crypt = NULL, *pos, *end; + u8 *ampe_buf, *plain, *pos, *end; + size_t plain_len; + const u8 *crypt; size_t crypt_len; const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, @@ -679,30 +682,22 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, return -1; } - if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { + if (!elems->mic || elems->mic_len != AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); return -1; } - ampe_buf = (u8 *) elems->mic + elems->mic_len; - if ((int) elems_len < ampe_buf - start) - return -1; - - crypt_len = elems_len - (elems->mic - start); + crypt = elems->mic; + crypt_len = elems_len - (crypt - start); if (crypt_len < 2 + AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); return -1; } - /* crypt is modified by siv_decrypt */ - crypt = os_zalloc(crypt_len); - if (!crypt) { - wpa_printf(MSG_ERROR, "Mesh RSN: out of memory"); - ret = -ENOMEM; - goto free; - } - - os_memcpy(crypt, elems->mic, crypt_len); + plain_len = crypt_len - AES_BLOCK_SIZE; + ampe_buf = plain = os_malloc(plain_len); + if (!plain) + return -ENOMEM; if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3, aad, aad_len, ampe_buf)) { @@ -809,6 +804,6 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, } free: - os_free(crypt); + bin_clear_free(plain, plain_len); return ret; } diff --git a/contrib/wpa/wpa_supplicant/nan_supplicant.c b/contrib/wpa/wpa_supplicant/nan_supplicant.c new file mode 100644 index 000000000000..f44a7a6459fe --- /dev/null +++ b/contrib/wpa/wpa_supplicant/nan_supplicant.c @@ -0,0 +1,5003 @@ +/* + * wpa_supplicant - NAN + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "utils/bitfield.h" +#include "common/nan_de.h" +#include "common/ieee802_11_common.h" +#include "ap/hostapd.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "nan/nan.h" +#include "config.h" +#include "offchannel.h" +#include "notify.h" +#include "p2p_supplicant.h" +#include "pr_supplicant.h" +#include "nan_supplicant.h" + +#define DEFAULT_NAN_MASTER_PREF 2 +#define DEFAULT_NAN_DUAL_BAND 0 +#define DEFAULT_NAN_SCAN_PERIOD 60 +#define DEFAULT_NAN_SCAN_DWELL_TIME 150 +#define DEFAULT_NAN_DISCOVERY_BEACON_INTERVAL 100 +#define DEFAULT_NAN_LOW_BAND_FREQUENCY 2437 +#define DEFAULT_NAN_HIGH_BAND_FREQUENCY 5745 +#define DEFAULT_NAN_RSSI_CLOSE -50 +#define DEFAULT_NAN_RSSI_MIDDLE -65 + +#define NAN_MIN_RSSI_CLOSE -60 +#define NAN_MIN_RSSI_MIDDLE -75 + +#define NAN_AVAIL_ATTR_MAX_LEN 100 + +#define DEFAULT_NAN_SUPP_PBM (NAN_PBA_METHOD_OPPORTUNISTIC | \ + NAN_PBA_METHOD_PIN_DISPLAY | \ + NAN_PBA_METHOD_PASSPHRASE_DISPLAY | \ + NAN_PBA_METHOD_QR_DISPLAY | \ + NAN_PBA_METHOD_NFC_TAG | \ + NAN_PBA_METHOD_PIN_KEYPAD | \ + NAN_PBA_METHOD_PASSPHRASE_KEYPAD | \ + NAN_PBA_METHOD_QR_SCAN | \ + NAN_PBA_METHOD_NFC_READER) + +#define DEFAULT_NAN_AUTO_ACCEPT_PBM NAN_PBA_METHOD_OPPORTUNISTIC + +#define DEFAULT_NAN_BOOTSTRAP_COMEBACK_TIMEOUT 1024 + +/* Default NAN NIK lifetime in seconds - 12 hours */ +#define NAN_NIK_LIFETIME_DEFAULT 43200 + +/* Default NAN idle period in seconds */ +#define DEFAULT_NAN_MAX_NDL_IDLE_PERIOD 25 + +#ifdef CONFIG_NAN + +static int get_center(u8 channel, const u8 *center_channels, + unsigned int num_chan, int width) +{ + int span = (width - 20) / 10; + unsigned int i; + + for (i = 0; i < num_chan; i++) { + if (channel >= center_channels[i] - span && + channel <= center_channels[i] + span) + return center_channels[i]; + } + + return 0; +} + + +static u8 get_center_and_width(int bw, u8 channel, int *width) +{ + static const u8 nan_160mhz_5ghz_chans[] = { 50, 114, 163 }; + static const u8 nan_80mhz_5ghz_chans[] = + { 42, 58, 106, 122, 138, 155, 171 }; + + switch (bw) { + case BW20: + *width = 20; + return channel; + case BW40PLUS: + case BW40MINUS: + *width = 40; + return bw == BW40PLUS ? channel + 2 : channel - 2; + case BW80: + *width = 80; + return get_center(channel, nan_80mhz_5ghz_chans, + ARRAY_SIZE(nan_80mhz_5ghz_chans), *width); + case BW160: + *width = 160; + return get_center(channel, nan_160mhz_5ghz_chans, + ARRAY_SIZE(nan_160mhz_5ghz_chans), *width); + default: + return 0; + } + + return 0; +} + + +static bool wpas_nan_valid_chan(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode mode, + u8 channel, int bw, u8 op_class, u8 *cf1) +{ + struct hostapd_hw_modes *hw_mode; + int width, span; + u8 c, center; + + hw_mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, mode, false); + if (!hw_mode) + return false; + + center = get_center_and_width(bw, channel, &width); + if (!center) + return false; + + if (wpa_s->nan_max_bw && width > wpa_s->nan_max_bw) + return false; + + span = (width - 20) / 10; + for (c = center - span; c <= center + span; c += 4) { + int freq = ieee80211_chan_to_freq(NULL, op_class, c); + + if (freq < 0) + return false; + + if (freq_range_list_includes(&wpa_s->nan_disallowed_freqs, + freq)) + return false; + + if (ieee80211_is_dfs(freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) + return false; + } + + /* Wide channels use center */ + if (width > 40) + channel = center; + + *cf1 = center; + return verify_channel(hw_mode, op_class, channel, bw) == ALLOWED; +} + + +static int wpas_nan_start_cb(void *ctx, const struct nan_cluster_config *config) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_nan_start(wpa_s, config); +} + + +static int wpas_nan_update_config_cb(void *ctx, + const struct nan_cluster_config *config) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_nan_update_config(wpa_s, config); +} + + +static void clear_sched_config(struct nan_schedule_config *sched_cfg) +{ + int i; + + for (i = 0; i < sched_cfg->num_channels; i++) + wpabuf_free(sched_cfg->channels[i].time_bitmap); + + wpabuf_free(sched_cfg->avail_attr); + os_memset(sched_cfg, 0, sizeof(*sched_cfg)); +} + + +static void wpas_nan_stop_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + int i; + + for (i = 0; i < MAX_NAN_RADIOS; i++) { + if (wpa_s->nan_sched[i].num_channels) { + wpa_drv_nan_config_schedule(wpa_s, i + 1, NULL); + clear_sched_config(&wpa_s->nan_sched[i]); + } + } + + wpa_drv_nan_stop(wpa_s); + nan_de_set_cluster_id(wpa_s->nan_de, NULL); + wpas_notify_nan_stopped(wpa_s); +} + + +static int wpas_nan_set_peer_sched_chan(struct wpa_supplicant *wpa_s, + const struct nan_peer_schedule *sched, + int i, int j, + struct nan_schedule_config *sched_cfg) +{ + const struct nan_map_chan *src_chan = &sched->maps[i].chans[j]; + struct nan_chan_entry *chan_entry; + int ch_idx; + + if (!src_chan->committed) + return 0; + + wpa_printf(MSG_DEBUG, " Channel freq=%u, rx_nss=%u", + src_chan->chan.freq, src_chan->rx_nss); + wpa_hexdump(MSG_DEBUG, " committed_bitmap", + src_chan->tbm.bitmap, src_chan->tbm.len); + + ch_idx = sched_cfg->num_channels; + sched_cfg->channels[ch_idx].freq = src_chan->chan.freq; + sched_cfg->channels[ch_idx].center_freq1 = src_chan->chan.center_freq1; + sched_cfg->channels[ch_idx].center_freq2 = src_chan->chan.center_freq2; + sched_cfg->channels[ch_idx].bandwidth = src_chan->chan.bandwidth; + sched_cfg->channels[ch_idx].rx_nss = src_chan->rx_nss; + chan_entry = (struct nan_chan_entry *) + sched_cfg->channels[ch_idx].chan_entry; + + if (nan_get_chan_entry(wpa_s->nan, &src_chan->chan, chan_entry)) { + wpa_printf(MSG_INFO, + "NAN: Failed to get chan entry for freq %d", + src_chan->chan.freq); + return -1; + } + + /* Copy time bitmap */ + if (src_chan->tbm.len > 0) + sched_cfg->channels[ch_idx].time_bitmap = + wpabuf_alloc_copy(src_chan->tbm.bitmap, + src_chan->tbm.len); + + sched_cfg->num_channels++; + + return 0; +} + + +static int wpas_nan_set_peer_schedule_cb(void *ctx, const u8 *nmi_addr, + bool new_sta, u16 cdw, u8 sequence_id, + u16 max_channel_switch_time, + const struct nan_peer_schedule *sched, + const struct wpabuf *ulw_elems) +{ + struct wpa_supplicant *wpa_s = ctx; + struct nan_peer_schedule_config peer_sched; + int i, j, ret; + + wpa_printf(MSG_DEBUG, "NAN: Set peer schedule - nmi_addr=" MACSTR + " new_sta=%d cdw=%u seq_id=%u max_chan_switch_time=%u", + MAC2STR(nmi_addr), new_sta, cdw, sequence_id, + max_channel_switch_time); + + if (new_sta) { + struct hostapd_sta_add_params sta_params; + + wpa_printf(MSG_DEBUG, "NAN: New NMI station"); + os_memset(&sta_params, 0, sizeof(sta_params)); + sta_params.addr = nmi_addr; + sta_params.flags = WPA_STA_AUTHENTICATED | WPA_STA_ASSOCIATED; + sta_params.flags_mask = sta_params.flags; + ret = wpa_drv_sta_add(wpa_s, &sta_params); + if (ret) { + wpa_printf(MSG_INFO, "NAN: Failed to add NMI station"); + return ret; + } + } + + os_memset(&peer_sched, 0, sizeof(peer_sched)); + if (sched) { + wpa_printf(MSG_DEBUG, "NAN: Peer schedule info:"); + wpa_printf(MSG_DEBUG, " n_maps=%u", sched->n_maps); + + for (i = 0; i < sched->n_maps && i < MAX_NUM_NAN_MAPS; i++) { + struct nan_schedule_config *sched_cfg = + &peer_sched.maps[peer_sched.n_maps].sched; + + wpa_printf(MSG_DEBUG, " Map %d: map_id=%u", + i, sched->maps[i].map_id); + + sched_cfg->num_channels = 0; + + for (j = 0; j < sched->maps[i].n_chans && + sched_cfg->num_channels < + MAX_NUM_NAN_SCHEDULE_CHANNELS; j++) { + if (wpas_nan_set_peer_sched_chan(wpa_s, sched, + i, j, + sched_cfg) < 0) + goto out; + } + + /* Only add map if it has channels after filtering */ + if (sched_cfg->num_channels > 0) { + peer_sched.maps[peer_sched.n_maps].map_id = + sched->maps[i].map_id; + peer_sched.n_maps++; + } + } + } + + ret = wpa_drv_nan_config_peer_schedule(wpa_s, nmi_addr, + cdw, sequence_id, + max_channel_switch_time, + ulw_elems, &peer_sched); + + /* Only print an error without returning, so we attempt to remove + * the STA if needed (sched == NULL) + */ + if (ret) + wpa_printf(MSG_INFO, "NAN: Failed to configure peer schedule"); + + if (!sched && !new_sta) { + /* TODO: Should we maybe keep that NMI station? */ + wpa_printf(MSG_DEBUG, "NAN: Unpair NMI station before removal"); + nan_pairing_unpair_peer(wpa_s->nan, nmi_addr); + + wpa_printf(MSG_DEBUG, "NAN: Remove NMI station"); + ret = wpa_drv_sta_remove(wpa_s, nmi_addr); + if (ret) + wpa_printf(MSG_INFO, + "NAN: Failed to remove NMI station"); + } + +out: + /* Free allocated time bitmaps */ + for (i = 0; i < peer_sched.n_maps; i++) { + struct nan_schedule_config *sched_cfg = + &peer_sched.maps[i].sched; + + for (j = 0; j < sched_cfg->num_channels; j++) { + wpabuf_free(sched_cfg->channels[j].time_bitmap); + sched_cfg->channels[j].time_bitmap = NULL; + } + } + + return ret; +} + + +static void +wpas_nan_ndp_action_notif_cb(void *ctx, + struct nan_ndp_action_notif_params *params) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (params->is_request) { + wpas_notify_nan_ndp_request(wpa_s, params->ndp_id.peer_nmi, + params->ndp_id.init_ndi, + params->ndp_id.id, + params->publish_inst_id, + params->ssi, params->ssi_len, + params->csid); + } else { + wpas_notify_nan_ndp_counter_request(wpa_s, + params->ndp_id.peer_nmi, + params->ndp_id.init_ndi, + params->ndp_id.id, + params->ssi, + params->ssi_len); + } +} + + +static int wpas_nan_set_ndi_keys(struct wpa_supplicant *wpa_s, + const u8 *ndi_addr, + enum nan_cipher_suite_id csid, + const u8 *tk, size_t tk_len) +{ + enum wpa_alg alg; + u8 rsc[6]; + + os_memset(rsc, 0, sizeof(rsc)); + switch (csid) { + case NAN_CS_SK_CCM_128: + case NAN_CS_PK_PASN_128: + alg = WPA_ALG_CCMP; + break; + case NAN_CS_SK_GCM_256: + case NAN_CS_PK_PASN_256: + alg = WPA_ALG_GCMP_256; + break; + default: + wpa_printf(MSG_INFO, "NAN: Unsupported CSID %d for NDI keys", + csid); + return -1; + } + + return wpa_drv_set_key(wpa_s, -1, alg, ndi_addr, 0, 1, rsc, sizeof(rsc), + tk, tk_len, KEY_FLAG_PAIRWISE); +} + + +static int wpas_nan_remove_ndi_keys(struct wpa_supplicant *wpa_s, + const u8 *ndi_addr) +{ + return wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, ndi_addr, 0, 0, + NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE); +} + + +static int wpas_nan_remove_ndi_gtk(struct wpa_supplicant *wpa_s, int key_id, + const u8 *ndi_addr) +{ + return wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, ndi_addr, key_id, 0, + NULL, 0, NULL, 0, KEY_FLAG_GROUP); +} + + +static int wpas_nan_remove_ndi_local_gtk(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->ndi_gtk.gtk.gtk_len) + return 0; + + if (wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, broadcast_ether_addr, + wpa_s->ndi_gtk.id, 0, NULL, 0, NULL, 0, + KEY_FLAG_GROUP_TX_DEFAULT)) { + wpa_printf(MSG_INFO, "NAN: Failed to remove NDI group TX key"); + return -1; + } + + wpa_s->ndi_gtk.id = 0; + os_memset(&wpa_s->ndi_gtk, 0, sizeof(wpa_s->ndi_gtk)); + return 0; +} + + +static struct wpa_supplicant * +wpas_nan_get_ndi_iface(struct wpa_supplicant *wpa_s, const u8 *ndi_addr) +{ + struct wpa_supplicant *ndi_wpa_s; + + for (ndi_wpa_s = wpa_s->global->ifaces; ndi_wpa_s; + ndi_wpa_s = ndi_wpa_s->next) { + if (ndi_wpa_s->nan_data && + ether_addr_equal(ndi_wpa_s->own_addr, ndi_addr)) + return ndi_wpa_s; + } + + return NULL; +} + + +static int wpas_nan_configure_nmi_sta_capa(struct wpa_supplicant *wpa_s, + const u8 *nmi_addr) +{ + struct hostapd_sta_add_params sta_params; + const u8 *ie; + u8 *elems; + int elems_len; + + elems_len = nan_get_peer_elems(wpa_s->nan, nmi_addr, &elems); + if (elems_len < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to get peer elems for NMI station"); + return -1; + } + + os_memset(&sta_params, 0, sizeof(sta_params)); + sta_params.addr = nmi_addr; + sta_params.set = 1; + sta_params.flags = WPA_STA_AUTHORIZED; + sta_params.flags_mask = sta_params.flags; + + ie = get_ie(elems, elems_len, WLAN_EID_HT_CAP); + if (!ie) { + wpa_printf(MSG_INFO, + "NAN: No HT capabilities in peer elems for NMI station"); + return -1; + } + + sta_params.ht_capabilities = (const void *) (ie + 2); + ie = get_ie(elems, elems_len, WLAN_EID_VHT_CAP); + sta_params.vht_capabilities = ie ? (const void *) (ie + 2) : NULL; + + return wpa_drv_sta_add(wpa_s, &sta_params); +} + + +static int wpas_nan_csid_to_wpa_alg(enum nan_cipher_suite_id csid, + enum wpa_alg *alg) +{ + switch (csid) { + case NAN_CS_NONE: + *alg = WPA_ALG_NONE; + break; + case NAN_CS_SK_CCM_128: + case NAN_CS_GTK_CCMP_128: + *alg = WPA_ALG_CCMP; + break; + case NAN_CS_SK_GCM_256: + case NAN_CS_GTK_GCMP_256: + *alg = WPA_ALG_GCMP_256; + break; + default: + wpa_printf(MSG_INFO, "NAN: Unsupported CSID %d", csid); + return -1; + } + + return 0; +} + + +static int wpas_nan_set_ndi_group_keys(struct wpa_supplicant *wpa_s, + struct nan_ndp_connection_params *params) +{ + enum wpa_alg alg; + + /* Install the local GTK only if not already installed */ + if (!wpa_s->ndi_gtk.id && params->local_gtk && params->local_gtk->id) { + u8 rsc[RSN_PN_LEN]; + + if (wpas_nan_csid_to_wpa_alg(params->local_gtk->csid, &alg)) { + wpa_printf(MSG_INFO, + "NAN: Unsupported CSID %u for local GTK", + params->local_gtk->csid); + return -1; + } + + os_memset(rsc, 0, sizeof(rsc)); + if (wpa_drv_set_key(wpa_s, -1, alg, broadcast_ether_addr, + params->local_gtk->id, 0, rsc, sizeof(rsc), + params->local_gtk->gtk.gtk, + params->local_gtk->gtk.gtk_len, + KEY_FLAG_GROUP_TX_DEFAULT)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set local GTK for NDI"); + return -1; + } + + os_memcpy(&wpa_s->ndi_gtk, params->local_gtk, + sizeof(wpa_s->ndi_gtk)); + } + + if (params->peer_gtk && params->peer_gtk->id) { + if (wpas_nan_csid_to_wpa_alg(params->peer_gtk->csid, &alg)) { + wpa_printf(MSG_INFO, + "NAN: Unsupported CSID %u for peer GTK", + params->peer_gtk->csid); + return -1; + } + + if (wpa_drv_set_key(wpa_s, -1, alg, params->peer_ndi, + params->peer_gtk->id, 0, + params->peer_gtk_rsc, RSN_PN_LEN, + params->peer_gtk->gtk.gtk, + params->peer_gtk->gtk.gtk_len, + KEY_FLAG_GROUP_RX)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set peer GTK for NDI"); + return -1; + } + } + + return 0; +} + + +static int wpas_nan_add_ndi_sta(struct wpa_supplicant *wpa_s, + struct nan_ndp_connection_params *params) +{ + u8 tk[WPA_TK_MAX_LEN]; + size_t tk_len; + enum nan_cipher_suite_id csid; + struct wpa_supplicant *ndi_wpa_s; + struct hostapd_sta_add_params sta_params; + const u8 *peer_nmi = params->ndp_id.peer_nmi; + const u8 *peer_ndi = params->peer_ndi; + + ndi_wpa_s = wpas_nan_get_ndi_iface(wpa_s, params->local_ndi); + if (!ndi_wpa_s) { + wpa_printf(MSG_INFO, + "NAN: No NDI interface found for " MACSTR, + MAC2STR(params->local_ndi)); + return -1; + } + + /* HT/VHT capabilities are configured per NMI station only for + * the first NDP. After that it is assumed that capablities are not + * changing. + */ + if (params->first_ndp && + wpas_nan_configure_nmi_sta_capa(wpa_s, peer_nmi)) { + wpa_printf(MSG_INFO, + "NAN: Failed to configure NMI station capabilities"); + return -1; + } + + if (params->new_ndi_sta) { + os_memset(&sta_params, 0, sizeof(sta_params)); + sta_params.addr = peer_ndi; + sta_params.nmi_addr = peer_nmi; + sta_params.flags = WPA_STA_AUTHENTICATED | WPA_STA_ASSOCIATED; + + /* Set MFP flag early, to prevent races until keys are installed + */ + if (params->install_keys) + sta_params.flags |= WPA_STA_MFP; + else + sta_params.flags |= WPA_STA_AUTHORIZED; + + if (wpa_drv_sta_add(ndi_wpa_s, &sta_params)) { + wpa_printf(MSG_INFO, + "NAN: Failed to add NDI station for peer " + MACSTR, MAC2STR(peer_ndi)); + return -1; + } + } else { + wpa_printf(MSG_DEBUG, + "NAN: NDI station already exists for peer " MACSTR, + MAC2STR(peer_ndi)); + /* Set MFP flag if keys will be installed (security upgrade) */ + if (params->install_keys && + wpa_drv_sta_set_flags(ndi_wpa_s, peer_ndi, WPA_STA_MFP, + WPA_STA_MFP, ~0)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set MFP flag for peer " + MACSTR, MAC2STR(peer_ndi)); + return -1; + } + } + + wpa_printf(MSG_DEBUG, "NAN: NDI station for peer " MACSTR " %s", + MAC2STR(peer_ndi), + params->new_ndi_sta ? "added" : "already exists"); + + if (!params->install_keys) { + wpa_printf(MSG_DEBUG, + "NAN: NDI station %s without keys for peer " MACSTR, + params->new_ndi_sta ? "added" : "ready", + MAC2STR(peer_ndi)); + goto out_success; + } + + if (nan_peer_get_tk(wpa_s->nan, peer_nmi, peer_ndi, params->local_ndi, + tk, &tk_len, &csid)) { + wpa_printf(MSG_INFO, "NAN: Failed to get TK for NDI station"); + goto remove_sta; + } + + if (wpas_nan_set_ndi_keys(ndi_wpa_s, peer_ndi, csid, tk, tk_len)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set NDI keys for peer " MACSTR, + MAC2STR(peer_ndi)); + forced_memzero(tk, tk_len); + goto remove_sta; + } + forced_memzero(tk, tk_len); + + if (wpas_nan_set_ndi_group_keys(ndi_wpa_s, params)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set NDI group keys for peer " + MACSTR, MAC2STR(peer_ndi)); + wpas_nan_remove_ndi_keys(ndi_wpa_s, peer_ndi); + goto remove_sta; + } + + if (wpa_drv_sta_set_flags(ndi_wpa_s, peer_ndi, WPA_STA_AUTHORIZED, + WPA_STA_AUTHORIZED, ~0)) { + wpa_printf(MSG_INFO, + "NAN: Failed to set authorize for NDI station"); + wpas_nan_remove_ndi_keys(ndi_wpa_s, peer_ndi); + wpas_nan_remove_ndi_gtk(ndi_wpa_s, params->peer_gtk->id, + peer_ndi); + goto remove_sta; + } + +out_success: + ndi_wpa_s->nan_ndi_ndp_refcount++; + wpa_printf(MSG_DEBUG, + "NAN: NDP refcount incremented to %u (peer_ndi=" MACSTR + " peer_nmi=" MACSTR ")", + ndi_wpa_s->nan_ndi_ndp_refcount, + MAC2STR(peer_ndi), MAC2STR(peer_nmi)); + + /* Set operstate UP only when the first NDP is established on this NDI + */ + if (ndi_wpa_s->nan_ndi_ndp_refcount == 1) + wpa_drv_set_operstate(ndi_wpa_s, 1); + + return 0; + +remove_sta: + /* + * Clean up the NDI station if it was newly added for this NDP. For + * existing stations, we assume that the caller will tear down other + * NDPs with this station on failure as it may be now in some + * inconsistent state that is too hard to rollback here. + */ + if (params->new_ndi_sta) + wpa_drv_sta_remove(ndi_wpa_s, params->peer_ndi); + return -2; +} + + +static void wpas_nan_remove_ndi_sta(struct wpa_supplicant *wpa_s, + const u8 *local_ndi, + const u8 *peer_ndi, + bool remove_sta, int gtk_id) +{ + struct wpa_supplicant *ndi_wpa_s; + + ndi_wpa_s = wpas_nan_get_ndi_iface(wpa_s, local_ndi); + if (!ndi_wpa_s) { + wpa_printf(MSG_INFO, + "NAN: No NDI interface found for " MACSTR, + MAC2STR(local_ndi)); + return; + } + + if (!ndi_wpa_s->nan_ndi_ndp_refcount) + return; + + ndi_wpa_s->nan_ndi_ndp_refcount--; + wpa_printf(MSG_DEBUG, "NAN: NDP refcount decremented to %u (peer_ndi=" + MACSTR ")", ndi_wpa_s->nan_ndi_ndp_refcount, + MAC2STR(peer_ndi)); + + /* Only remove the NDI station if no other NDP is using the same + * peer NDI address + */ + if (remove_sta) { + if (wpa_drv_sta_set_flags(ndi_wpa_s, peer_ndi, + WPA_STA_AUTHORIZED, 0, + ~WPA_STA_AUTHORIZED)) + wpa_printf(MSG_DEBUG, + "NAN: Failed to clear authorized flag for NDI station"); + + wpas_nan_remove_ndi_keys(ndi_wpa_s, peer_ndi); + if (gtk_id) + wpas_nan_remove_ndi_gtk(ndi_wpa_s, gtk_id, peer_ndi); + wpa_drv_sta_remove(ndi_wpa_s, peer_ndi); + } + + /* Remove the local GTK and set operstate DORMANT only when the last NDP + * is removed from this NDI + */ + if (!ndi_wpa_s->nan_ndi_ndp_refcount) { + wpas_nan_remove_ndi_local_gtk(ndi_wpa_s); + wpa_drv_set_operstate(ndi_wpa_s, 0); + } +} + + +static int wpas_nan_ndp_connected_cb(void *ctx, + struct nan_ndp_connection_params *params) +{ + struct wpa_supplicant *wpa_s = ctx; + int ret; + + ret = wpas_nan_add_ndi_sta(wpa_s, params); + if (ret) { + wpa_printf(MSG_INFO, + "NAN: Failed to add NDI station for NDP connection"); + return ret; + } + + wpas_notify_nan_ndp_connected(wpa_s, params->ndp_id.peer_nmi, + params->ndp_id.id, + params->local_ndi, params->peer_ndi, + params->ssi, params->ssi_len, + params->interface_id); + + return 0; +} + + +static void wpas_nan_ndp_disconnected_cb(void *ctx, struct nan_ndp_id *ndp_id, + const u8 *local_ndi, + const u8 *peer_ndi, + enum nan_reason reason, + bool locally_generated, + bool remove_sta, + bool failure, u8 gtk_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_nan_remove_ndi_sta(wpa_s, local_ndi, peer_ndi, remove_sta, gtk_id); + wpas_notify_nan_ndp_disconnected(wpa_s, ndp_id->peer_nmi, + ndp_id->id, local_ndi, peer_ndi, + reason, locally_generated, failure); +} + + +static int wpas_nan_send_naf_cb(void *ctx, const u8 *dst, const u8 *src, + const u8 *cluster_id, struct wpabuf *buf) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *a2; + int ret; + + a2 = src ? src : wpa_s->own_addr; + + if (src && !ether_addr_equal(src, wpa_s->own_addr)) { + wpa_printf(MSG_DEBUG, "NAN: Use NDI interface for sending NAF"); + + wpa_s = wpas_nan_get_ndi_iface(wpa_s, src); + if (!wpa_s) { + wpa_printf(MSG_DEBUG, + "NAN: No NDI interface found for address " + MACSTR, MAC2STR(src)); + wpa_s = ctx; + } + } + + wpa_printf(MSG_DEBUG, "NAN: Send NAF - dst=" MACSTR " src=" MACSTR + " cluster_id=" MACSTR, MAC2STR(dst), MAC2STR(a2), + MAC2STR(cluster_id)); + + ret = wpa_drv_send_action(wpa_s, 0, 0, dst, a2, cluster_id, + wpabuf_head(buf), wpabuf_len(buf), 1); + if (ret) + wpa_printf(MSG_DEBUG, + "NAN: Failed to send sync Action frame (%d)", ret); + return ret; +} + + +static int nan_chan_info_cmp(const void *a, const void *b) +{ + const struct nan_channel_info *chan_a = a; + const struct nan_channel_info *chan_b = b; + + return chan_b->pref - chan_a->pref; +} + + +static int wpas_nan_get_chans_cb(void *ctx, u8 map_id, + struct nan_channels *chans) +{ + struct wpa_supplicant *wpa_s = ctx; + int *shared_freqs = NULL; + struct nan_channel_info *chan_list = NULL; + unsigned int chan_count = 0; + unsigned int chan_capacity = 0; + int op; + + wpa_printf(MSG_DEBUG, "NAN: Get channels - map_id=%u", map_id); + + /* Check if override is configured */ + if (wpa_s->nan_override_potential_avail.n_chans > 0) { + wpa_printf(MSG_DEBUG, + "NAN: Using override potential availability (%u channels)", + wpa_s->nan_override_potential_avail.n_chans); + + chans->n_chans = wpa_s->nan_override_potential_avail.n_chans; + chans->chans = os_memdup( + wpa_s->nan_override_potential_avail.chans, + wpa_s->nan_override_potential_avail.n_chans * + sizeof(struct nan_channel_info)); + if (!chans->chans) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate memory for override channels"); + chans->n_chans = 0; + return -1; + } + + return 0; + } + + /* Allocate one extra element so it will be 0 terminated int_array */ + shared_freqs = os_calloc(wpa_s->num_multichan_concurrent + 1, + sizeof(int)); + if (!shared_freqs) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate memory for shared freqs"); + goto fail; + } + + if (get_shared_radio_freqs(wpa_s, shared_freqs, + wpa_s->num_multichan_concurrent, + false) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Failed to get shared radio freqs"); + goto fail; + } + + /* Iterate through global operating classes */ + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; + int c; + + /* Don't support 80+, 6 GHz, etc. yet */ + if (o->op_class > 129) + continue; + + /* Iterate through channels in this operating class */ + for (c = o->min_chan; c <= o->max_chan; c += o->inc) { + int freq; + u8 center; + u8 pref; + + /* Don't support 40 MHz channels on 2.4 GHz band */ + if (o->mode == HOSTAPD_MODE_IEEE80211G && o->bw != BW20) + continue; + + if (!wpas_nan_valid_chan(wpa_s, o->mode, c, o->bw, + o->op_class, ¢er)) + continue; + + freq = ieee80211_chan_to_freq(NULL, o->op_class, c); + if (freq < 0) + continue; + + /* Determine preference based on shared frequencies */ + if (int_array_includes(shared_freqs, freq)) + pref = 3; + else + pref = 1; + + /* Expand channel list if needed */ + if (chan_count >= chan_capacity) { + unsigned int new_capacity = chan_capacity ? + chan_capacity * 2 : 16; + struct nan_channel_info *new_list; + + new_list = os_realloc_array( + chan_list, new_capacity, + sizeof(struct nan_channel_info)); + if (!new_list) { + wpa_printf(MSG_INFO, + "NAN: Failed to expand channel list"); + goto fail; + } + chan_list = new_list; + chan_capacity = new_capacity; + } + + /* Add channel to list */ + chan_list[chan_count].op_class = o->op_class; + chan_list[chan_count].channel = + o->bw == BW80 || o->bw == BW160 ? center : c; + chan_list[chan_count].pref = pref; + chan_count++; + } + } + + /* Sort channels by preference (higher preference first) */ + if (chan_count > 1) + qsort(chan_list, chan_count, sizeof(struct nan_channel_info), + nan_chan_info_cmp); + + chans->n_chans = chan_count; + chans->chans = chan_list; + + os_free(shared_freqs); + + wpa_printf(MSG_DEBUG, "NAN: Get channels completed - found %u channels", + chan_count); + return 0; + +fail: + os_free(shared_freqs); + os_free(chan_list); + chans->n_chans = 0; + chans->chans = NULL; + return -1; +} + + +static bool wpas_nan_is_valid_publish_id_cb(void *ctx, u8 instance_id, + u8 *service_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_printf(MSG_DEBUG, "NAN: Check valid publish ID - instance_id=%u", + instance_id); + + return nan_de_is_valid_instance_id(wpa_s->nan_de, instance_id, true, + service_id); +} + + +static void wpas_nan_bootstrap_request_cb(void *ctx, const u8 *peer_nmi, + u16 pbm, int handle, + u8 requestor_instance_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_bootstrap_request(wpa_s, peer_nmi, pbm, handle, + requestor_instance_id); +} + + +static void wpas_nan_bootstrap_completed_cb(void *ctx, const u8 *peer_nmi, + u16 pbm, bool success, + u8 reason_code, int handle, + u8 requestor_instance_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (success) + wpas_notify_nan_bootstrap_success(wpa_s, peer_nmi, pbm, handle, + requestor_instance_id); + else + wpas_notify_nan_bootstrap_failure(wpa_s, peer_nmi, pbm, + reason_code, handle, + requestor_instance_id); +} + + +static void wpas_nan_schedule_changed_cb(void *ctx, const u8 *peer_nmi) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_schedule_changed(wpa_s, peer_nmi); +} + + +static int wpas_nan_transmit_followup_cb(void *ctx, const u8 *peer_nmi, + const struct wpabuf *attrs, int handle, + u8 req_instance_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (!wpa_s->nan_de) + return -1; + + return nan_de_transmit(wpa_s->nan_de, handle, NULL, NULL, + peer_nmi, req_instance_id, attrs, NULL); +} + + +static u16 wpas_nan_get_service_bootstrap_methods(void *ctx, int handle) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (!wpa_s->nan_de) + return 0; + + return nan_de_get_service_bootstrap_methods(wpa_s->nan_de, handle); +} + + +#ifdef CONFIG_PASN + +static int wpas_nan_pasn_send_cb(void *ctx, const u8 *data, size_t data_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_send_mlme(wpa_s, data, data_len, 0, 0, 0); +} + + +static int wpas_nan_pasn_auth_status_cb(void *ctx, const u8 *peer_addr, + int akmp, int cipher, u16 status, + struct wpa_ptk *ptk, const u8 *nd_pmk) +{ + struct wpa_supplicant *wpa_s = ctx; + enum wpa_alg alg; + u8 seq[6]; + + wpas_notify_nan_pairing_status(wpa_s, peer_addr, akmp, cipher, + status, nd_pmk); + + if (status != WLAN_STATUS_SUCCESS) + return 0; + + if (!ptk) { + wpa_printf(MSG_DEBUG, + "NAN: No PTK provided after pairing with peer " + MACSTR, MAC2STR(peer_addr)); + return -1; + } + + alg = cipher == WPA_CIPHER_CCMP ? WPA_ALG_CCMP : WPA_ALG_GCMP_256; + os_memset(seq, 0, sizeof(seq)); + if (wpa_drv_set_key(wpa_s, -1, alg, peer_addr, 0, 1, seq, sizeof(seq), + ptk->tk, ptk->tk_len, KEY_FLAG_PAIRWISE_RX_TX)) { + wpa_printf(MSG_INFO, + "NAN: Failed to install NM-TK for peer " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + return 0; +} + + +static int wpas_nan_update_pairing_credentials_cb(void *ctx, const u8 *nik, + size_t nik_len, + int cipher_ver, + int nik_lifetime, int akmp, + const u8 *npk, size_t npk_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_dev_ik *ik; + + if (!nik || cipher_ver != NAN_NIRA_CIPHER_VER_128 || + nik_len != NAN_NIK_LEN || !npk || !npk_len) { + wpa_printf(MSG_DEBUG, "NAN: Invalid NIK/NPK parameters"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "NAN: Received NIK", nik, nik_len); + wpa_printf(MSG_DEBUG, "NAN: NIK lifetime=%d cipher_ver=%d", + nik_lifetime, cipher_ver); + + /* Check if an identity with the same NIK already exists */ + for (ik = wpa_s->conf->identity; ik; ik = ik->next) { + if (nik_len == wpabuf_len(ik->dik) && + os_memcmp(nik, wpabuf_head(ik->dik), nik_len) == 0) { + wpa_printf(MSG_DEBUG, + "NAN: Remove previous device identity entry for matching NIK"); + wpa_config_remove_identity(wpa_s->conf, ik->id); + break; + } + } + + /* Create a new device identity entry */ + wpa_printf(MSG_DEBUG, + "NAN: Create a new device identity entry for NIK"); + ik = wpa_config_add_identity(wpa_s->conf); + if (!ik) { + wpa_printf(MSG_INFO, "NAN: Failed to allocate identity"); + return -1; + } + + /* Store the NIK as the DIK */ + ik->dik = wpabuf_alloc_copy(nik, nik_len); + if (!ik->dik) + goto fail; + + /* Store the NPK as the PMK */ + ik->pmk = wpabuf_alloc_copy(npk, npk_len); + if (!ik->pmk) + goto fail; + + /* Store cipher version and AKMP */ + ik->dik_cipher = cipher_ver; + ik->akmp = akmp; + + wpa_printf(MSG_INFO, "NAN: Stored NIK as device identity (id=%d)", + ik->id); + + /* Notify control interface about received NIK */ + wpas_notify_nan_nik_received(wpa_s, nik, nik_len, cipher_ver, akmp, + npk, npk_len, nik_lifetime, ik->id); + + return ik->id; + +fail: + wpa_printf(MSG_INFO, "NAN: Failed to store NIK as device identity"); + wpa_config_remove_identity(wpa_s->conf, ik->id); + return -1; +} + + +static const struct wpa_dev_ik * +wpas_nan_find_ik_by_nonce_tag(struct wpa_supplicant *wpa_s, const u8 *peer_nmi, + const u8 *nonce, const u8 *tag) +{ + struct wpa_dev_ik *ik; + struct wpabuf *derived_tag; + + if (!nonce || !tag) { + wpa_printf(MSG_DEBUG, "NAN: Invalid nonce or tag"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "NAN: Looking up device identity"); + wpa_hexdump(MSG_DEBUG, "NAN: NIRA nonce", nonce, NAN_NIRA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "NAN: NIRA tag", tag, NAN_NIRA_TAG_LEN); + + /* Iterate over all saved NIKs (stored as device identities) */ + for (ik = wpa_s->conf->identity; ik; ik = ik->next) { + /* The device identities saved in the interface configuration + * are not checked to match NIK length and to have a PMK. + * Although other identities are not expected since this is the + * NAN management interface, verify that the DIK matches NIK + * length, that a PMK is stored, and the stored AKMP is valid + * for NAN pairing. + */ + if (!ik->dik || wpabuf_len(ik->dik) != NAN_NIK_LEN || + !ik->pmk || + (ik->akmp != WPA_KEY_MGMT_SAE && + ik->akmp != WPA_KEY_MGMT_PASN)) + continue; + + /* Derive tag from this NIK */ + derived_tag = + nan_crypto_derive_nira_tag(wpabuf_head_u8(ik->dik), + NAN_NIK_LEN, peer_nmi, + nonce); + if (!derived_tag) + continue; + + /* Compare derived tag with received tag */ + if (os_memcmp(wpabuf_head(derived_tag), tag, + NAN_NIRA_TAG_LEN) != 0) { + wpabuf_free(derived_tag); + continue; + } + + wpa_printf(MSG_DEBUG, + "NAN: NIRA validation succeeded with NIK id=%d", + ik->id); + wpabuf_free(derived_tag); + return ik; + } + + return NULL; +} + + +static const struct wpabuf * wpas_nan_get_npk_akmp_cb(void *ctx, + const u8 *peer_nmi, + const u8 *nonce, + const u8 *tag, int *akmp) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct wpa_dev_ik *ik; + + if (!akmp) { + wpa_printf(MSG_DEBUG, "NAN: Invalid akmp pointer"); + return NULL; + } + + ik = wpas_nan_find_ik_by_nonce_tag(wpa_s, peer_nmi, nonce, tag); + if (ik) { + *akmp = ik->akmp; + wpa_printf(MSG_DEBUG, "NAN: Found NPK for NIK id=%d, akmp=%d", + ik->id, *akmp); + return ik->pmk; + } + + wpa_printf(MSG_DEBUG, "NAN: No matching NIK found"); + return NULL; +} + + +static void +wpas_nan_pasn_pairing_request_cb(void *ctx, const u8 *peer_nmi, u8 csid, + u8 instance_id, + const struct wpa_ie_data *rsn_data) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_pairing_request(wpa_s, peer_nmi, csid, instance_id, + rsn_data->key_mgmt, + !!rsn_data->num_pmkid); +} + +#endif /* CONFIG_PASN */ + + +static int wpas_nan_set_group_key_cb(void *ctx, enum wpa_alg alg, + const u8 *addr, int key_idx, const u8 *seq, + const u8 *key, size_t key_len, + enum key_flag key_flags) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_set_key(wpa_s, -1, alg, addr, key_idx, 0, + seq, RSN_PN_LEN, key, key_len, key_flags); +} + + +static int wpas_nan_get_seqnum_cb(void *ctx, int key_idx, u8 *seq, + const u8 *ndi_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (ndi_addr) { + wpa_s = wpas_nan_get_ndi_iface(wpa_s, ndi_addr); + if (!wpa_s) { + wpa_printf(MSG_DEBUG, + "NAN: No NDI interface found for address " + MACSTR, MAC2STR(ndi_addr)); + return -1; + } + + /* If the NDI GTK is not installed yet, RSC is 0 */ + if (!wpa_s->ndi_gtk.id) { + os_memset(seq, 0, WPA_KEY_RSC_LEN); + return 0; + } + } + + return wpa_drv_get_seqnum(wpa_s, NULL, key_idx, seq); +} + + +static int wpas_nan_get_peer_inactivity(void *ctx, const u8 *local_ndi, + const u8 *peer_ndi) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_s = wpas_nan_get_ndi_iface(wpa_s, local_ndi); + if (!wpa_s) { + wpa_printf(MSG_DEBUG, + "NAN: No NDI interface found for address " MACSTR, + MAC2STR(local_ndi)); + return -1; + } + + return wpa_drv_get_inact_sec(wpa_s, peer_ndi); +} + + +int wpas_nan_init(struct wpa_supplicant *wpa_s) +{ + struct nan_config nan; + + if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SUPPORT_NAN) || + !(wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG)) { + wpa_printf(MSG_INFO, "NAN: Driver does not support NAN"); + return -1; + } + + os_memset(&nan, 0, sizeof(nan)); + nan.cb_ctx = wpa_s; + os_memcpy(nan.nmi_addr, wpa_s->own_addr, ETH_ALEN); + + nan.start = wpas_nan_start_cb; + nan.stop = wpas_nan_stop_cb; + nan.update_config = wpas_nan_update_config_cb; + + /* NDP and bootstrapping enabled */ + if (wpa_s->nan_capa.drv_flags & WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP) { +#ifdef CONFIG_PASN + wpa_printf(MSG_DEBUG, "NAN: Pairing support enabled"); + nan.send_pasn = wpas_nan_pasn_send_cb; + nan.pairing_result_cb = wpas_nan_pasn_auth_status_cb; + nan.update_pairing_credentials = + wpas_nan_update_pairing_credentials_cb; + nan.get_npk_akmp = wpas_nan_get_npk_akmp_cb; + nan.pairing_request = wpas_nan_pasn_pairing_request_cb; + nan.pairing_cfg.pairing_setup = true; + nan.pairing_cfg.npk_caching = true; + nan.pairing_cfg.pairing_verification = true; + nan.pairing_cfg.cipher_suites = NAN_PAIRING_PASN_128 | + NAN_PAIRING_PASN_256; +#endif /* CONFIG_PASN */ + + wpa_printf(MSG_DEBUG, "NAN: NDP support enabled"); + + nan.ndp_action_notif = wpas_nan_ndp_action_notif_cb; + nan.ndp_connected = wpas_nan_ndp_connected_cb; + nan.ndp_disconnected = wpas_nan_ndp_disconnected_cb; + nan.send_naf = wpas_nan_send_naf_cb; + nan.get_chans = wpas_nan_get_chans_cb; + nan.is_valid_publish_id = wpas_nan_is_valid_publish_id_cb; + nan.set_peer_schedule = wpas_nan_set_peer_schedule_cb; + nan.set_group_key = wpas_nan_set_group_key_cb; + nan.get_seqnum = wpas_nan_get_seqnum_cb; + nan.schedule_changed = wpas_nan_schedule_changed_cb; + + wpa_printf(MSG_DEBUG, "NAN: Bootstrap support enabled"); + nan.bootstrap_request = wpas_nan_bootstrap_request_cb; + nan.bootstrap_completed = wpas_nan_bootstrap_completed_cb; + nan.transmit_followup = wpas_nan_transmit_followup_cb; + nan.get_supported_bootstrap_methods = + wpas_nan_get_service_bootstrap_methods; + + if (wpa_s->driver->get_inact_sec) + nan.get_peer_inactivity = wpas_nan_get_peer_inactivity; + else + wpa_printf(MSG_DEBUG, + "NAN: Driver does not support getting peer inactivity"); + + /* + * Set the group security capabilities based on driver support + */ + if ((wpa_s->drv_enc & (WPA_DRIVER_CAPA_ENC_CCMP | + WPA_DRIVER_CAPA_ENC_GCMP_256)) && + (wpa_s->drv_enc & (WPA_DRIVER_CAPA_ENC_BIP | + WPA_DRIVER_CAPA_ENC_BIP_GMAC_256))) { + /* + * By default, use BIP-CMAC-128 cipher suite for + * group keys for maximum compatibility. + */ + if (!(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) + nan.security_capab |= + NAN_CS_INFO_CAPA_IGTK_USE_NCS_BIP_GMAC_256; + + /* + * By default enable only GTK/IGTK support. Beacon + * protection support can be enabled separately + */ + nan.security_capab |= + NAN_CS_INFO_CAPA_GTK_SUPP_NO_BIGTK << + NAN_CS_INFO_CAPA_GTK_SUPP_POS; + } + + wpa_printf(MSG_DEBUG, "NAN: security capabilities=0x%02x", + nan.security_capab); + } + + nan.dev_capa.cdw_info = + ((1 << NAN_CDW_INFO_2G_POS) & NAN_CDW_INFO_2G_MASK) | + ((1 << NAN_CDW_INFO_5G_POS) & NAN_CDW_INFO_5G_MASK); + + /* + * Wi-Fi Aware spec v4.0, Table 80 defines the 2.4 GHz and 5 GHz CDW + * Override Map ID fields as mandatory without any option to indicate + * "applies for all" as in other places in the specification that use + * map_ids. At this stage we don't have a local schedule yet, so we will + * be referencing non-existent map_id. + * + * In case of a single radio devices all maps will be using map_id 1, + * so we can already configure it. Otherwise, we have no choice but to + * leave it unassigned and let this field be updated when the schedule + * is configured. + * + * TODO: Dual radio devices may want to properly query this information + * from the driver/device. + */ + if (wpa_s->nan_capa.num_radios == 1) { + nan.dev_capa.cdw_info |= 0x1 << NAN_CDW_INFO_2G_OVERRIDE_POS; + nan.dev_capa.cdw_info |= 0x1 << NAN_CDW_INFO_5G_OVERRIDE_POS; + } + + nan.dev_capa.supported_bands = NAN_DEV_CAPA_SBAND_2G; + if (wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND) + nan.dev_capa.supported_bands |= NAN_DEV_CAPA_SBAND_5G; + + nan.dev_capa.op_mode = wpa_s->nan_capa.op_modes; + nan.dev_capa.n_antennas = wpa_s->nan_capa.num_antennas; + nan.dev_capa.channel_switch_time = + wpa_s->nan_capa.max_channel_switch_time; + nan.dev_capa.capa = wpa_s->nan_capa.dev_capa; + nan.dev_capa.capa |= NAN_DEV_CAPA_NDPE_ATTR_SUPP; + + nan.supported_bootstrap_methods = DEFAULT_NAN_SUPP_PBM; + nan.auto_accept_bootstrap_methods = DEFAULT_NAN_AUTO_ACCEPT_PBM; + nan.bootstrap_comeback_timeout = DEFAULT_NAN_BOOTSTRAP_COMEBACK_TIMEOUT; + + if (os_get_random(nan.nik, NAN_NIK_LEN) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to get random data for NIK"); + return -1; + } + + nan.nik_lifetime = NAN_NIK_LIFETIME_DEFAULT; + nan.max_ndl_idle_period = DEFAULT_NAN_MAX_NDL_IDLE_PERIOD; + + wpa_s->nan = nan_init(&nan); + if (!wpa_s->nan) { + wpa_printf(MSG_INFO, "NAN: Failed to init"); + return -1; + } + + /* Set the default configuration */ + os_memset(&wpa_s->nan_cluster_config, 0, + sizeof(wpa_s->nan_cluster_config)); + + wpa_s->nan_cluster_config.master_pref = DEFAULT_NAN_MASTER_PREF; + wpa_s->nan_cluster_config.dual_band = DEFAULT_NAN_DUAL_BAND; + os_memset(wpa_s->nan_cluster_config.cluster_id, 0, ETH_ALEN); + wpa_s->nan_cluster_config.scan_period = DEFAULT_NAN_SCAN_PERIOD; + wpa_s->nan_cluster_config.scan_dwell_time = DEFAULT_NAN_SCAN_DWELL_TIME; + wpa_s->nan_cluster_config.discovery_beacon_interval = + DEFAULT_NAN_DISCOVERY_BEACON_INTERVAL; + + wpa_s->nan_cluster_config.low_band_cfg.frequency = + DEFAULT_NAN_LOW_BAND_FREQUENCY; + wpa_s->nan_cluster_config.low_band_cfg.rssi_close = + DEFAULT_NAN_RSSI_CLOSE; + wpa_s->nan_cluster_config.low_band_cfg.rssi_middle = + DEFAULT_NAN_RSSI_MIDDLE; + wpa_s->nan_cluster_config.low_band_cfg.awake_dw_interval = true; + + wpa_s->nan_cluster_config.high_band_cfg.frequency = + DEFAULT_NAN_HIGH_BAND_FREQUENCY; + wpa_s->nan_cluster_config.high_band_cfg.rssi_close = + DEFAULT_NAN_RSSI_CLOSE; + wpa_s->nan_cluster_config.high_band_cfg.rssi_middle = + DEFAULT_NAN_RSSI_MIDDLE; + wpa_s->nan_cluster_config.high_band_cfg.awake_dw_interval = true; + + /* TODO: Optimize this, so that the notification are enabled only when + * needed, i.e., when the DE is configured with unsolicited publish or + * active subscribe + */ + wpa_s->nan_cluster_config.enable_dw_notif = + !!(wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE); + + wpa_s->nan_supported_csids = BIT(NAN_CS_SK_CCM_128) | + BIT(NAN_CS_SK_GCM_256); +#ifdef CONFIG_PASN + wpa_s->nan_supported_csids |= BIT(NAN_CS_PK_PASN_128) | + BIT(NAN_CS_PK_PASN_256); +#endif /* CONFIG_PASN */ + + return 0; +} + + +void wpas_nan_deinit(struct wpa_supplicant *wpa_s) +{ + int i; + + if (!wpa_s || !wpa_s->nan) + return; + + for (i = 0; i < MAX_NAN_RADIOS; i++) + clear_sched_config(&wpa_s->nan_sched[i]); + + nan_deinit(wpa_s->nan); + os_free(wpa_s->nan_disallowed_freqs.range); + os_memset(&wpa_s->nan_disallowed_freqs, 0, + sizeof(wpa_s->nan_disallowed_freqs)); + clear_sched_config(&wpa_s->nan_sched_update.sched); + wpabuf_free(wpa_s->nan_ulw_attr); + wpa_s->nan_ulw_attr = NULL; + + os_free(wpa_s->nan_override_potential_avail.chans); + wpa_s->nan_override_potential_avail.chans = NULL; + wpa_s->nan_override_potential_avail.n_chans = 0; + + wpa_s->nan = NULL; +} + + +static bool wpas_nan_ready(struct wpa_supplicant *wpa_s) +{ + return wpa_s->nan_mgmt && wpa_s->nan && wpa_s->nan_de && + wpa_s->wpa_state != WPA_INTERFACE_DISABLED; +} + + +static bool wpas_nan_ndp_allowed(struct wpa_supplicant *wpa_s) +{ + return wpas_nan_ready(wpa_s) && + (wpa_s->nan_capa.drv_flags & WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP); +} + + +/* Join a cluster using current configuration */ +int wpas_nan_start(struct wpa_supplicant *wpa_s) +{ + if (!wpas_nan_ready(wpa_s)) + return -1; + + return nan_start(wpa_s->nan, &wpa_s->nan_cluster_config); +} + + +int wpas_nan_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpas_nan_ready(wpa_s)) + return -1; + + nan_stop(wpa_s->nan); + + return 0; +} + + +void wpas_nan_flush(struct wpa_supplicant *wpa_s) +{ + if (!wpas_nan_ready(wpa_s)) + return; + + nan_flush(wpa_s->nan); +} + + +static int wpas_nan_parse_override_potential_avail(struct wpa_supplicant *wpa_s, + char *param) +{ + struct nan_channel_info *chans = NULL; + unsigned int n_chans = 0, capacity = 0; + char *pos, *end; + + /* Empty string clears the override */ + if (*param == '\0') { + wpa_printf(MSG_DEBUG, + "NAN: Clearing override potential availability"); + goto out; + } + + /* Parse format: ,... */ + pos = param; + while (pos && *pos) { + u8 op_class, pref; + u16 bitmap; + const struct oper_class_map *o = NULL; + int op, idx; + + if (sscanf(pos, "%hhu:0x%hx:%hhu", &op_class, &bitmap, &pref) != + 3) { + wpa_printf(MSG_INFO, + "NAN: Invalid override_potential_availability format at '%s'", + pos); + os_free(chans); + return -1; + } + + if (!op_class || op_class > 129 || pref > 3) { + wpa_printf(MSG_INFO, + "NAN: Invalid values in override_potential_availability"); + os_free(chans); + return -1; + } + + /* Find the operating class in global_op_class */ + for (op = 0; global_op_class[op].op_class; op++) { + if (global_op_class[op].op_class == op_class) { + o = &global_op_class[op]; + break; + } + } + + if (!o) { + wpa_printf(MSG_INFO, + "NAN: Unknown operating class %d in override_potential_availability", + op_class); + os_free(chans); + return -1; + } + + /* Iterate through bitmap bits */ + for (idx = 0; idx < 16 && bitmap; idx++) { + u8 chan, center; + + if (!(bitmap & BIT(idx))) + continue; + + chan = op_class_idx_to_chan(o, idx); + if (!chan) { + wpa_printf(MSG_INFO, + "NAN: Invalid channel index %d for op_class %d", + idx, op_class); + os_free(chans); + return -1; + } + + /* + * Validate the channel. For zero preference only + * check the very basic validity, but accept + * "NOT ALLOWED" channels, as the user might want + * to explicitly mark them as unavailable. + */ + if (pref && !wpas_nan_valid_chan(wpa_s, o->mode, chan, + o->bw, o->op_class, + ¢er)) { + wpa_printf(MSG_INFO, + "NAN: Channel %d (op_class %d) is not a valid NAN channel", + chan, op_class); + os_free(chans); + return -1; + } + + if (!pref) { + int width; + + center = get_center_and_width(o->bw, chan, + &width); + if (!center) { + wpa_printf(MSG_INFO, + "NAN: Invalid channel %d for op_class %d", + chan, op_class); + os_free(chans); + return -1; + } + } + + /* Expand array if needed */ + if (n_chans >= capacity) { + struct nan_channel_info *new_chans; + + capacity = capacity ? capacity * 2 : 4; + new_chans = os_realloc_array(chans, capacity, + sizeof(*chans)); + if (!new_chans) { + wpa_printf(MSG_INFO, + "NAN: Memory allocation failed"); + os_free(chans); + return -1; + } + chans = new_chans; + } + + /* Use center for wide channels */ + chans[n_chans].op_class = op_class; + chans[n_chans].channel = (o->bw == BW80 || + o->bw == BW160) ? + center : chan; + chans[n_chans].pref = pref; + n_chans++; + } + + /* Move to next entry */ + end = os_strchr(pos, ','); + if (end) + pos = end + 1; + else + break; + } + + /* Sort channels by preference (higher preference first) */ + if (n_chans > 1) + qsort(chans, n_chans, sizeof(*chans), nan_chan_info_cmp); + +out: + /* Free previous configuration */ + os_free(wpa_s->nan_override_potential_avail.chans); + wpa_s->nan_override_potential_avail.chans = chans; + wpa_s->nan_override_potential_avail.n_chans = n_chans; + wpa_s->schedule_sequence_id++; + + wpa_printf(MSG_DEBUG, + "NAN: Configured %u override potential availability channels", + n_chans); + return 0; +} + + +int wpas_nan_set(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct nan_cluster_config *config = &wpa_s->nan_cluster_config; + char *param = os_strchr(cmd, ' '); + + if (!param) + return -1; + + *param++ = '\0'; + +#define NAN_PARSE_INT(_str, _min, _max) \ + if (os_strcmp(#_str, cmd) == 0) { \ + int val = atoi(param); \ + \ + if (val < (_min) || val > (_max)) { \ + wpa_printf(MSG_INFO, \ + "NAN: Invalid value for " #_str); \ + return -1; \ + } \ + config->_str = val; \ + return 0; \ + } + +#define NAN_PARSE_BAND(_str) \ + if (os_strcmp(#_str, cmd) == 0) { \ + int a, b, c, d; \ + \ + if (sscanf(param, "%d,%d,%d,%d", &a, &b, &c, &d) != \ + 4) { \ + wpa_printf(MSG_DEBUG, \ + "NAN: Invalid value for " #_str); \ + return -1; \ + } \ + \ + if (a < NAN_MIN_RSSI_CLOSE || \ + b < NAN_MIN_RSSI_MIDDLE || \ + a <= b) { \ + wpa_printf(MSG_DEBUG, \ + "NAN: Invalid value for " #_str); \ + return -1; \ + } \ + config->_str.rssi_close = a; \ + config->_str.rssi_middle = b; \ + config->_str.awake_dw_interval = c; \ + config->_str.disable_scan = !!d; \ + return 0; \ + } + + /* 0 and 255 are reserved */ + NAN_PARSE_INT(master_pref, 1, 254); + NAN_PARSE_INT(dual_band, 0, 1); + NAN_PARSE_INT(scan_period, 0, 0xffff); + NAN_PARSE_INT(scan_dwell_time, 10, 150); + NAN_PARSE_INT(discovery_beacon_interval, 50, 200); + + NAN_PARSE_BAND(low_band_cfg); + NAN_PARSE_BAND(high_band_cfg); + + if (os_strcmp("cluster_id", cmd) == 0) { + u8 cluster_id[ETH_ALEN]; + + if (hwaddr_aton(param, cluster_id) < 0) { + wpa_printf(MSG_INFO, "NAN: Invalid cluster ID"); + return -1; + } + + if (cluster_id[0] != 0x50 || cluster_id[1] != 0x6f || + cluster_id[2] != 0x9a || cluster_id[3] != 0x01) { + wpa_printf(MSG_DEBUG, "NAN: Invalid cluster ID format"); + return -1; + } + + os_memcpy(config->cluster_id, cluster_id, ETH_ALEN); + return 0; + } + + if (os_strcmp("max_bw", cmd) == 0) { + wpa_s->nan_max_bw = atoi(param); + return 0; + } + + if (os_strcmp("disallowed_freqs", cmd) == 0) { + if (freq_range_list_parse(&wpa_s->nan_disallowed_freqs, + param)) { + wpa_printf(MSG_INFO, + "NAN: Invalid disallowed_freqs value"); + return -1; + } + + return 0; + } + + if (os_strcmp("override_potential_availability", cmd) == 0) + return wpas_nan_parse_override_potential_avail(wpa_s, param); + + if (os_strcmp("bootstrap_config", cmd) == 0) { + u16 supported_methods, auto_accept_methods, comeback_timeout; + + if (sscanf(param, "%hx,%hx,%hu", &supported_methods, + &auto_accept_methods, &comeback_timeout) != 3) { + wpa_printf(MSG_INFO, + "NAN: Invalid value for boostrap_config"); + return -1; + } + + return nan_set_bootstrap_configuration(wpa_s->nan, + supported_methods, + auto_accept_methods, + comeback_timeout); + } + +#undef NAN_PARSE_INT +#undef NAN_PARSE_BAND + +#ifdef CONFIG_PASN +#define NAN_PARSE_PAIRING_BOOL(_str) \ + if (os_strcmp(#_str, cmd) == 0) { \ + int val = atoi(param); \ + \ + if (val != 0 && val != 1) { \ + wpa_printf(MSG_INFO, \ + "NAN: Invalid value for " #_str); \ + return -1; \ + } \ + return nan_pairing_set_##_str(wpa_s->nan, val); \ + } + +#define NAN_PARSE_PAIRING_INT(_str, _mask) \ + if (os_strcmp(#_str, cmd) == 0) { \ + unsigned int val = atoi(param); \ + \ + if ((val & (_mask)) != val) { \ + wpa_printf(MSG_INFO, \ + "NAN: Invalid value for " #_str); \ + return -1; \ + } \ + return nan_pairing_set_##_str(wpa_s->nan, val); \ + } + + NAN_PARSE_PAIRING_BOOL(pairing_setup); + NAN_PARSE_PAIRING_BOOL(npk_caching); + NAN_PARSE_PAIRING_BOOL(pairing_verification); + NAN_PARSE_PAIRING_INT(cipher_suites, + NAN_PAIRING_PASN_128 | NAN_PAIRING_PASN_256); +#undef NAN_PARSE_PAIRING_BOOL +#undef NAN_PARSE_PAIRING_INT + + if (os_strcmp("nik", cmd) == 0) { + u8 nik[NAN_NIK_LEN]; + int res; + + /* Parse NIK value (hex string) */ + if (hexstr2bin(param, nik, NAN_NIK_LEN) < 0) { + wpa_printf(MSG_INFO, "NAN: Invalid NIK format"); + return -1; + } + + res = nan_pairing_set_nik(wpa_s->nan, nik, NAN_NIK_LEN); + forced_memzero(nik, NAN_NIK_LEN); + return res; + } + + if (os_strcmp("nik_lifetime", cmd) == 0) { + u32 lifetime = atoi(param); + + if (lifetime == 0) { + wpa_printf(MSG_INFO, "NAN: Invalid NIK lifetime"); + return -1; + } + + return nan_pairing_set_nik_lifetime(wpa_s->nan, lifetime); + } +#endif /* CONFIG_PASN */ + + if (os_strcmp("mgmt_group_cipher", cmd) == 0) { + int cipher; + + if (os_strcmp(param, "BIP-CMAC-128") == 0) { + if (!(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) { + wpa_printf(MSG_INFO, + "NAN: BIP-CMAC-128 not supported by the driver"); + return -1; + } + + cipher = WPA_CIPHER_AES_128_CMAC; + } else if (os_strcmp(param, "BIP-GMAC-256") == 0) { + if (!(wpa_s->drv_enc & + WPA_DRIVER_CAPA_ENC_BIP_GMAC_256)) { + wpa_printf(MSG_INFO, + "NAN: BIP-CMAC-256 not supported by the driver"); + return -1; + } + + cipher = WPA_CIPHER_BIP_GMAC_256; + } else { + wpa_printf(MSG_INFO, + "NAN: Unsupported mgmt_group_cipher value"); + return -1; + } + + return nan_set_mgmt_group_cipher(wpa_s->nan, cipher); + } + + if (os_strcmp("beacon_prot", cmd) == 0) { + bool val = !!atoi(param); + + if (val && !(wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_BEACON_PROT)) { + wpa_printf(MSG_INFO, + "NAN: Beacon protection not supported by the driver"); + return -1; + } + + if (nan_set_beacon_prot(wpa_s->nan, val) < 0) + return -1; + + return 0; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (os_strcmp("tx_mcast_follow_up_prot", cmd) == 0) { + bool val = !!atoi(param); + + nan_de_set_tx_mcast_follow_up_prot(wpa_s->nan_de, val); + return 0; + } + + if (os_strcmp("force_conditional_sched", cmd) == 0) { + wpa_s->nan_force_conditional_sched = !!atoi(param); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (os_strcmp("max_ndl_idle_period", cmd) == 0) { + u16 max_ndl_idle_period = atoi(param) & 0xffff; + + return nan_set_max_ndl_idle_period(wpa_s->nan, + max_ndl_idle_period); + } + + wpa_printf(MSG_INFO, "NAN: Unknown NAN_SET cmd='%s'", cmd); + return -1; +} + + +int wpas_nan_update_conf(struct wpa_supplicant *wpa_s) +{ + if (!wpas_nan_ready(wpa_s)) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: Update NAN configuration"); + return nan_update_config(wpa_s->nan, &wpa_s->nan_cluster_config); +} + + +static u8 nan_select_40mhz_channel(u8 chan, u8 *op_class, int *bw) +{ + int op; + + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; + int c; + + /* No support for 40 MHz on 2.4 GHz */ + if (o->mode != HOSTAPD_MODE_IEEE80211A) + continue; + + /* Currently don't support NAN for 80+, 6 GHz, etc. */ + if (o->op_class > 129) + continue; + + if (o->bw != BW40MINUS && o->bw != BW40PLUS) + continue; + + for (c = o->min_chan; c <= o->max_chan; c += o->inc) { + if (c != chan) + continue; + + *op_class = o->op_class; + *bw = o->bw; + if (o->bw == BW40MINUS) + return chan - 2; + else + return chan + 2; + } + } + + return 0; +} + + +static int wpas_nan_select_channel_params(struct wpa_supplicant *wpa_s, + int freq, int *center_freq1, + int *center_freq2, int *bandwidth) +{ + u8 chan, op_class, center; + enum hostapd_hw_mode mode; + int bw; + + mode = ieee80211_freq_to_channel_ext(freq, 0, CONF_OPER_CHWIDTH_USE_HT, + &op_class, &chan); + if (mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, "NAN: Invalid frequency %d", freq); + return -1; + } + + if (!wpas_nan_valid_chan(wpa_s, mode, chan, BW20, op_class, ¢er)) { + wpa_printf(MSG_DEBUG, + "NAN: Channel not valid for NAN (freq = %d)", + freq); + return -1; + } + + /* On 2.4 GHz use 20 MHz channels */ + if (freq >= 2412 && freq <= 2484) + goto out; + + /* TODO: Add support for NAN on other bands */ + if (freq < 5180 || freq > 5885) { + wpa_printf(MSG_DEBUG, "NAN: Unsupported frequency %d", freq); + return -1; + } + + if (wpas_nan_valid_chan(wpa_s, mode, chan, BW160, 129, ¢er)) { + *center_freq1 = ieee80211_chan_to_freq(NULL, op_class, center); + *center_freq2 = 0; + *bandwidth = 160; + return 0; + } + + if (wpas_nan_valid_chan(wpa_s, mode, chan, BW80, 128, ¢er)) { + *center_freq1 = ieee80211_chan_to_freq(NULL, op_class, center); + *center_freq2 = 0; + *bandwidth = 80; + return 0; + } + + if (nan_select_40mhz_channel(chan, &op_class, &bw) && + wpas_nan_valid_chan(wpa_s, mode, center, bw, op_class, + ¢er)) { + *center_freq1 = ieee80211_chan_to_freq(NULL, op_class, + center); + *center_freq2 = 0; + *bandwidth = 40; + return 0; + } + +out: + /* Fallback to 20 MHz */ + *center_freq1 = freq; + *center_freq2 = 0; + *bandwidth = 20; + return 0; +} + + +static void nan_dump_sched_config(const char *title, + struct nan_schedule_config *sched_cfg) +{ + int i; + + wpa_printf(MSG_DEBUG, "%s: num_channels=%d", title, + sched_cfg->num_channels); + for (i = 0; i < sched_cfg->num_channels; i++) { + wpa_printf(MSG_DEBUG, + " Channel %d: freq=%d center_freq1=%d center_freq2=%d bandwidth=%d time_bitmap_len=%zu", + i + 1, + sched_cfg->channels[i].freq, + sched_cfg->channels[i].center_freq1, + sched_cfg->channels[i].center_freq2, + sched_cfg->channels[i].bandwidth, + wpabuf_len(sched_cfg->channels[i].time_bitmap)); + } +} + + +static void wpas_nan_fill_ndp_schedule(struct wpa_supplicant *wpa_s, + struct nan_schedule *sched); + +static void wpas_nan_update_local_schedule(struct wpa_supplicant *wpa_s) +{ + struct nan_schedule sched; + + wpas_nan_fill_ndp_schedule(wpa_s, &sched); + nan_local_sched_update(wpa_s->nan, &sched); +} + + +/* Parse format NAN_SCHED_CONFIG_MAP map_id= [freq:bitmap_hex].. + * If no bitmaps provided - clear the map */ +int wpas_nan_sched_config_map(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct nan_schedule_config *sched_cfg = &wpa_s->nan_sched_update.sched; + struct nan_schedule_config old_sched_cfg; + struct nan_schedule sched; + char *token, *context = NULL; + u8 map_id; + char *pos; + int *shared_freqs; + int shared_freqs_count, unused_freqs_count, ret = -1; + struct bitfield *bf_total; + unsigned int expected_bitmap_len; + bool cdw_overwrite_2g = false, cdw_overwrite_5g = false; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + if (sched_cfg->deferred) { + wpa_printf(MSG_DEBUG, + "NAN: Previous schedule update is still pending"); + return -1; + } + + if (os_strncmp(cmd, "map_id=", 7) != 0) { + wpa_printf(MSG_INFO, "NAN: Invalid schedule map format"); + return -1; + } + + map_id = atoi(cmd + 7); + + if (!map_id || map_id >= MAX_NAN_RADIOS) { + wpa_printf(MSG_INFO, "NAN: Invalid map_id %d", map_id); + return -1; + } + + if (map_id > wpa_s->nan_capa.num_radios) { + wpa_printf(MSG_INFO, + "NAN: map_id %d exceeds number of supported NAN radios %d", + map_id, wpa_s->nan_capa.num_radios); + return -1; + } + + if (!wpa_s->nan_capa.schedule_period || + !wpa_s->nan_capa.slot_duration) { + wpa_printf(MSG_INFO, + "NAN: Driver doesn't advertise support for NAN scheduling"); + return -1; + } + + expected_bitmap_len = (wpa_s->nan_capa.schedule_period / + wpa_s->nan_capa.slot_duration + 7) / 8; + + os_memset(sched_cfg, 0, sizeof(*sched_cfg)); + + pos = os_strchr(cmd + 7, ' '); + if (!pos) { + wpa_printf(MSG_INFO, + "NAN: Missing freq:timebitmap pairs - cleanup schedule"); + ret = wpa_drv_nan_config_schedule(wpa_s, map_id, sched_cfg); + if (!ret) { + clear_sched_config(&wpa_s->nan_sched[map_id - 1]); + wpas_nan_update_local_schedule(wpa_s); + } + + return ret; + } + + shared_freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(int)); + if (!shared_freqs) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate memory for shared freqs"); + return -1; + } + + shared_freqs_count = + get_shared_radio_freqs(wpa_s, shared_freqs, + wpa_s->num_multichan_concurrent, + false); + + unused_freqs_count = wpa_s->nan_capa.sched_chans - shared_freqs_count; + + bf_total = bitfield_alloc(wpa_s->nan_capa.schedule_period / + wpa_s->nan_capa.slot_duration); + if (!bf_total) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate bitfield for total schedule"); + goto out; + } + + /* Parse freq:timebitmap pairs and optional CDW overwrite flags */ + pos++; + while ((token = str_token(pos, " ", &context))) { + if (os_strcmp(token, "cdw_overwrite_low_band") == 0) { + cdw_overwrite_2g = true; + continue; + } + if (os_strcmp(token, "cdw_overwrite_high_band") == 0) { + cdw_overwrite_5g = true; + continue; + } + int j, i = sched_cfg->num_channels; + struct bitfield *bf_chan = NULL; + char *colon = os_strchr(token, ':'); + struct nan_sched_chan chan; + struct nan_chan_entry *chan_entry; + + if (i >= wpa_s->nan_capa.sched_chans) { + wpa_printf(MSG_INFO, + "NAN: Exceeded max channels per radio %u", + wpa_s->nan_capa.sched_chans); + goto out; + } + + if (!colon) { + wpa_printf(MSG_INFO, + "NAN: Invalid freq:timebitmap format"); + goto out; + } + + sched_cfg->channels[i].freq = atoi(token); + if (sched_cfg->channels[i].freq <= 0) { + wpa_printf(MSG_INFO, "NAN: Invalid frequency %d", + sched_cfg->channels[i].freq); + goto out; + } + + for (j = 0; j < i; j++) { + if (sched_cfg->channels[j].freq == + sched_cfg->channels[i].freq) { + wpa_printf(MSG_INFO, + "NAN: Duplicate frequency %d", + sched_cfg->channels[i].freq); + goto out; + } + } + + if (wpas_nan_select_channel_params( + wpa_s, sched_cfg->channels[i].freq, + &sched_cfg->channels[i].center_freq1, + &sched_cfg->channels[i].center_freq2, + &sched_cfg->channels[i].bandwidth)) { + wpa_printf(MSG_INFO, + "NAN: Failed to select channel params for freq %d", + sched_cfg->channels[i].freq); + goto out; + } + + if (!int_array_includes(shared_freqs, + sched_cfg->channels[i].freq)) { + if (!unused_freqs_count) { + wpa_printf(MSG_INFO, + "NAN: No unused radio frequency available for freq %d", + sched_cfg->channels[i].freq); + goto out; + } + + unused_freqs_count--; + } + + sched_cfg->channels[i].time_bitmap = + wpabuf_parse_bin(colon + 1); + if (!sched_cfg->channels[i].time_bitmap) { + wpa_printf(MSG_INFO, "NAN: Invalid time bitmap"); + goto out; + } + + sched_cfg->num_channels++; + + if (wpabuf_len(sched_cfg->channels[i].time_bitmap) != + expected_bitmap_len) { + wpa_printf(MSG_INFO, + "NAN: Invalid bitmap length (%zu) for period=%d, slot length=%d", + wpabuf_len(sched_cfg->channels[i].time_bitmap), + wpa_s->nan_capa.schedule_period, + wpa_s->nan_capa.slot_duration); + goto out; + } + + bf_chan = bitfield_alloc_data( + wpabuf_head(sched_cfg->channels[i].time_bitmap), + wpabuf_len(sched_cfg->channels[i].time_bitmap)); + if (!bf_chan) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate bitfield for channel schedule"); + goto out; + } + + if (bitfield_intersects(bf_total, bf_chan)) { + wpa_printf(MSG_INFO, + "NAN: Overlapping time bitmap detected for freq %d", + sched_cfg->channels[i].freq); + bitfield_free(bf_chan); + goto out; + } + + /* Extract RX NSS from upper nibble of num_antennas */ + sched_cfg->channels[i].rx_nss = + (wpa_s->nan_capa.num_antennas >> 4) & 0x0f; + + bitfield_union_in_place(bf_total, bf_chan); + bitfield_free(bf_chan); + + chan.freq = sched_cfg->channels[i].freq; + chan.center_freq1 = sched_cfg->channels[i].center_freq1; + chan.center_freq2 = sched_cfg->channels[i].center_freq2; + chan.bandwidth = sched_cfg->channels[i].bandwidth; + chan_entry = (struct nan_chan_entry *) + &sched_cfg->channels[i].chan_entry; + if (nan_get_chan_entry(wpa_s->nan, &chan, chan_entry)) { + wpa_printf(MSG_INFO, + "NAN: Failed to get channel entry for freq %d", + sched_cfg->channels[i].freq); + goto out; + } + } + + sched_cfg->avail_attr = wpabuf_alloc(NAN_AVAIL_ATTR_MAX_LEN); + if (!sched_cfg->avail_attr) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate memory for Availability attribute"); + ret = -1; + goto out; + } + + /* Keep previous schedule configuration as we may need to restore it */ + os_memcpy(&old_sched_cfg, &wpa_s->nan_sched[map_id - 1], + sizeof(old_sched_cfg)); + + os_memcpy(&wpa_s->nan_sched[map_id - 1], sched_cfg, sizeof(*sched_cfg)); + wpas_nan_fill_ndp_schedule(wpa_s, &sched); + + ret = nan_convert_sched_to_avail_attrs(wpa_s->nan, + wpa_s->schedule_sequence_id + 1, + BIT(map_id), + sched.n_chans, sched.chans, + sched_cfg->avail_attr, + false); + + /* Restore previous schedule configuration */ + os_memcpy(&wpa_s->nan_sched[map_id - 1], &old_sched_cfg, + sizeof(old_sched_cfg)); + if (ret < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to convert schedule to Availability Attributes for map_id %d", + map_id); + goto out; + } + + if (nan_has_active_ndp(wpa_s->nan)) { + wpa_printf(MSG_DEBUG, "NAN: Set schedule config as deferred"); + sched_cfg->deferred = true; + wpa_s->nan_sched_update.map_id = map_id; + nan_set_sched_update_pending(wpa_s->nan, true); + } + + nan_dump_sched_config("NAN: Set schedule config", sched_cfg); + ret = wpa_drv_nan_config_schedule(wpa_s, map_id, sched_cfg); + if (ret < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to configure NAN schedule map_id %d", + map_id); + os_memcpy(&wpa_s->nan_sched[map_id - 1], &old_sched_cfg, + sizeof(old_sched_cfg)); + nan_set_sched_update_pending(wpa_s->nan, false); + goto out; + } + + if (!sched_cfg->deferred) { + /* Store the configured schedule */ + wpa_s->schedule_sequence_id++; + clear_sched_config(&wpa_s->nan_sched[map_id - 1]); + os_memcpy(&wpa_s->nan_sched[map_id - 1], sched_cfg, + sizeof(*sched_cfg)); + os_memset(sched_cfg, 0, sizeof(*sched_cfg)); + wpas_nan_update_local_schedule(wpa_s); + } + + /* Update CDW overwrite map_id for the specified band */ + if (cdw_overwrite_2g || cdw_overwrite_5g) + nan_set_cdw_overwrite(wpa_s->nan, + cdw_overwrite_2g ? map_id : -1, + cdw_overwrite_5g ? map_id : -1); + +out: + os_free(bf_total); + os_free(shared_freqs); + if (ret) + clear_sched_config(sched_cfg); + + return ret; +} + + +static struct wpabuf * wpas_nan_build_ndp_elems(struct wpa_supplicant *wpa_s) +{ + struct ieee80211_ht_capabilities *ht_cap; + struct ieee80211_vht_capabilities *vht_cap; + size_t len; + struct wpabuf *buf; + + /* Include HT and VHT Capability elements */ + len = 2 + sizeof(struct ieee80211_ht_capabilities); + if (wpa_s->nan_capa.vht_valid) + len += 2 + sizeof(struct ieee80211_vht_capabilities); + + buf = wpabuf_alloc(len); + if (!buf) + return NULL; + + wpabuf_put_u8(buf, WLAN_EID_HT_CAP); + wpabuf_put_u8(buf, sizeof(*ht_cap)); + ht_cap = wpabuf_put(buf, sizeof(*ht_cap)); + ht_cap->ht_capabilities_info = host_to_le16(wpa_s->nan_capa.ht_capab); + ht_cap->a_mpdu_params = wpa_s->nan_capa.ht_ampdu_params; + os_memcpy(ht_cap->supported_mcs_set, wpa_s->nan_capa.ht_mcs_set, + sizeof(ht_cap->supported_mcs_set)); + + if (!wpa_s->nan_capa.vht_valid) + return buf; + + wpabuf_put_u8(buf, WLAN_EID_VHT_CAP); + wpabuf_put_u8(buf, sizeof(*vht_cap)); + vht_cap = wpabuf_put(buf, sizeof(*vht_cap)); + vht_cap->vht_capabilities_info = + host_to_le32(wpa_s->nan_capa.vht_capab); + os_memcpy(&vht_cap->vht_supported_mcs_set, + wpa_s->nan_capa.vht_mcs_set, + sizeof(vht_cap->vht_supported_mcs_set)); + + /* TODO: Add HE capabilities */ + return buf; +} + + +static int +wpas_nan_fill_ndp_schedule_chan(struct wpa_supplicant *wpa_s, + struct nan_schedule *sched, int map_id, + const struct nan_schedule_channel *chan) +{ + struct nan_chan_schedule *chan_sched; + struct nan_time_bitmap *tbm; + const u8 *bitmap_data; + size_t bitmap_len; + + /* None of these should happen */ + if (!chan->time_bitmap) { + wpa_printf(MSG_INFO, + "NAN: Missing time bitmap for map_id %d freq %d", + map_id + 1, chan->freq); + return -1; + } + + bitmap_len = wpabuf_len(chan->time_bitmap); + bitmap_data = wpabuf_head(chan->time_bitmap); + if (bitmap_len > NAN_TIME_BITMAP_MAX_LEN) { + wpa_printf(MSG_INFO, + "NAN: Time bitmap length %zu exceeds maximum %d", + bitmap_len, NAN_TIME_BITMAP_MAX_LEN); + return -1; + } + + chan_sched = &sched->chans[sched->n_chans++]; + chan_sched->map_id = map_id + 1; + chan_sched->chan.freq = chan->freq; + chan_sched->chan.center_freq1 = chan->center_freq1; + chan_sched->chan.center_freq2 = chan->center_freq2; + chan_sched->chan.bandwidth = chan->bandwidth; + + tbm = &chan_sched->committed; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->nan_force_conditional_sched) { + wpa_printf(MSG_DEBUG, + "NAN: Using conditional TBM for schedule channel"); + tbm = &chan_sched->conditional; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + tbm->duration = wpa_s->nan_capa.slot_duration >> 5; + tbm->period = ffs(wpa_s->nan_capa.schedule_period) - 7; + tbm->offset = 0; + tbm->len = bitmap_len; + os_memcpy(tbm->bitmap, bitmap_data, bitmap_len); + + wpa_printf(MSG_DEBUG, + "NAN: NDP schedule channel added: map_id=%d freq=%d center_freq1=%d center_freq2=%d bandwidth=%d", + chan_sched->map_id, + chan_sched->chan.freq, + chan_sched->chan.center_freq1, + chan_sched->chan.center_freq2, + chan_sched->chan.bandwidth); + + return 0; +} + + +static void wpas_nan_fill_ndp_schedule(struct wpa_supplicant *wpa_s, + struct nan_schedule *sched) +{ + int map_id; + + os_memset(sched, 0, sizeof(*sched)); + + /* Fill the NAN schedule structure from the schedule config */ + for (map_id = 0; map_id < MAX_NAN_RADIOS; map_id++) { + int i; + struct nan_schedule_config *sched_cfg = + &wpa_s->nan_sched[map_id]; + + for (i = 0; i < wpa_s->nan_sched[map_id].num_channels; i++) { + struct nan_schedule_channel *chan; + + chan = &sched_cfg->channels[i]; + if (wpas_nan_fill_ndp_schedule_chan(wpa_s, sched, + map_id, chan) + < 0) + return; + } + } + + /* Mark all supported radios - for potential availability */ + sched->map_ids_bitmap = (BIT(wpa_s->nan_capa.num_radios) - 1) << 1; +} + + +static int wpas_nan_get_ndc_map_id(struct wpa_supplicant *wpa_s, + const struct nan_peer_schedule *peer_sched, + u8 peer_map_id) +{ + int i; + int freq = nan_get_peer_ndc_freq(wpa_s->nan, peer_sched, peer_map_id); + + if (freq < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to get NDC frequency from peer schedule"); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: Peer NDC frequency is %d MHz", freq); + + for (i = 0; i < MAX_NAN_RADIOS; i++) { + struct nan_schedule_config *sched_cfg = &wpa_s->nan_sched[i]; + int j; + + for (j = 0; j < sched_cfg->num_channels; j++) { + if (sched_cfg->channels[j].freq == freq) { + wpa_printf(MSG_DEBUG, + "NAN: Found local NDC map_id %d for peer NDC freq %d", + i + 1, freq); + return i + 1; + } + } + } + + return -1; +} + + + +static int wpas_nan_select_ndc_copy_peers(struct wpa_supplicant *wpa_s, + struct nan_ndp_params *ndp) +{ + struct nan_peer_schedule peer_sched; + int ret; + u8 map_id; + + wpa_printf(MSG_DEBUG, "NAN: NDP CONF - use the NDC from peer"); + ret = nan_peer_get_schedule_info(wpa_s->nan, ndp->ndp_id.peer_nmi, + &peer_sched); + if (ret) { + wpa_printf(MSG_DEBUG, "NAN: Failed to get peer schedule info"); + return -1; + } + + for (map_id = 0; map_id < peer_sched.n_maps; map_id++) { + if (peer_sched.maps[map_id].ndc.len) { + ret = wpas_nan_get_ndc_map_id(wpa_s, &peer_sched, + map_id); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: No local NDC map_id found for peer NDC"); + return -1; + } + + ndp->sched.ndc_map_id = ret; + os_memcpy(&ndp->sched.ndc, &peer_sched.maps[map_id].ndc, + sizeof(ndp->sched.ndc)); + return 0; + } + } + + wpa_printf(MSG_DEBUG, "NAN: No NDC found in peer schedule"); + return -1; +} + + +static int wpas_nan_select_ndc(struct wpa_supplicant *wpa_s, + struct nan_ndp_params *ndp) +{ + struct nan_time_bitmap *tbm; + int i; + + /* NDC attribute in request is optional, let the peer decide */ + if (ndp->type == NAN_NDP_ACTION_REQ) + return 0; + + /* For successfull confirm, copy peer's NDC */ + if (ndp->type == NAN_NDP_ACTION_CONF && + ndp->u.resp.status == NAN_NDP_STATUS_ACCEPTED) + return wpas_nan_select_ndc_copy_peers(wpa_s, ndp); + + tbm = &ndp->sched.chans[0].committed; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->nan_force_conditional_sched) { + wpa_printf(MSG_DEBUG, + "NAN: Using conditional TBM for NDC selection"); + tbm = &ndp->sched.chans[0].conditional; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(&ndp->sched.ndc, tbm, sizeof(ndp->sched.ndc)); + os_memset(ndp->sched.ndc.bitmap, 0, sizeof(ndp->sched.ndc.bitmap)); + ndp->sched.ndc_map_id = ndp->sched.chans[0].map_id; + + /* + * For default NDC channels (6, 149, 44) take the first slot after DW. + * Note that if the slot duration is 16 TUs we need to select the next + * slot after DW. If the first channel is not one of default NDC + * channels, select the first available slot. + */ + if (ndp->sched.chans[0].chan.freq == 5745 || + ndp->sched.chans[0].chan.freq == 5220) { + int dw_bit, byte_idx, bit_in_byte; + + dw_bit = 128 / wpa_s->nan_capa.slot_duration; + dw_bit += !!(wpa_s->nan_capa.slot_duration == 16); + byte_idx = dw_bit / 8; + bit_in_byte = dw_bit % 8; + + if (tbm->bitmap[byte_idx] & BIT(bit_in_byte)) { + ndp->sched.ndc.bitmap[byte_idx] = BIT(bit_in_byte); + return 0; + } + } else if (ndp->sched.chans[0].chan.freq == 2437 && + wpa_s->nan_capa.slot_duration == 16) { + if (tbm->bitmap[0] & 0x02) { + ndp->sched.ndc.bitmap[0] = 0x02; + return 0; + } + } + + /* For other cases, select the first available slot */ + for (i = 0; i < NAN_TIME_BITMAP_MAX_LEN; i++) { + if (tbm->bitmap[i]) { + ndp->sched.ndc.bitmap[i] = + tbm->bitmap[i] & (~tbm->bitmap[i] + 1); + break; + } + } + + return 0; +} + + +static int wpas_nan_set_ndp_schedule(struct wpa_supplicant *wpa_s, + struct nan_ndp_params *ndp) +{ + /* Set schedule for request or successful response */ + if (ndp->type != NAN_NDP_ACTION_REQ && + ndp->u.resp.status == NAN_NDP_STATUS_REJECTED) + return 0; + + wpas_nan_fill_ndp_schedule(wpa_s, &ndp->sched); + + if (!ndp->sched.n_chans) { + wpa_printf(MSG_DEBUG, + "NAN: No channels configured for NDP schedule"); + return -1; + } + + /* Set sequence ID */ + ndp->sched.sequence_id = wpa_s->schedule_sequence_id; + + /* Add additional elements */ + ndp->sched.elems = wpas_nan_build_ndp_elems(wpa_s); + + /* Mark schedule as valid */ + ndp->sched_valid = true; + + return wpas_nan_select_ndc(wpa_s, ndp); +} + + +static char * wpas_nan_parse_password_hex(const char *hexstr) +{ + size_t len = os_strlen(hexstr); + size_t pwd_len; + char *pwd; + size_t i; + + if (!len || len % 2 != 0) { + wpa_printf(MSG_INFO, "NAN: Invalid password hex length: %zu", + len); + return NULL; + } + + pwd_len = len / 2; + pwd = os_malloc(pwd_len + 1); + if (!pwd) + return NULL; + + if (hexstr2bin(hexstr, (u8 *) pwd, pwd_len) < 0) { + wpa_printf(MSG_INFO, "NAN: Invalid password hex data"); + os_free(pwd); + return NULL; + } + + /* Reject passwords containing NULL bytes (except the terminator) */ + for (i = 0; i < pwd_len; i++) { + if (pwd[i] == '\0') { + wpa_printf(MSG_DEBUG, + "NAN: Decoded password contains embedded NUL byte at offset %zu", + i); + os_free(pwd); + return NULL; + } + } + + pwd[pwd_len] = '\0'; + return pwd; +} + + +static int wpas_nan_fill_nd_pmk(struct wpa_supplicant *wpa_s, + struct nan_ndp_params *ndp, + int handle, + const u8 *publisher_nmi, + const char *pwd, const char *pmk) +{ + u8 service_id[NAN_SERVICE_ID_LEN]; + + if (ndp->sec.csid < NAN_CS_NONE || ndp->sec.csid >= NAN_CS_MAX) { + wpa_printf(MSG_INFO, "NAN: Invalid CSID value: %d", + ndp->sec.csid); + return -1; + } + + /* + * Get service ID from the local handle (subscribe on + * requester and publish on responder) + */ + if (!nan_de_is_valid_instance_id(wpa_s->nan_de, handle, + ndp->type == NAN_NDP_ACTION_RESP, + service_id)) { + wpa_printf(MSG_INFO, + "NAN: Invalid service instance handle: %d", + handle); + return -1; + } + + /* + * For NDP response (publisher side), check if the requested CSID + * is supported by the service (including open/NAN_CS_NONE). + */ + if (ndp->type == NAN_NDP_ACTION_RESP && + !nan_de_service_supports_csid(wpa_s->nan_de, handle, + ndp->sec.csid)) { + wpa_printf(MSG_DEBUG, + "NAN: CSID %d not supported by service", + ndp->sec.csid); + return -1; + } + + if (ndp->sec.csid == NAN_CS_NONE) + return 0; + + /* Security parameters are not needed in confirmation */ + if (ndp->type == NAN_NDP_ACTION_CONF) + return 0; + + if (!(wpa_s->nan_supported_csids & BIT(ndp->sec.csid))) { + wpa_printf(MSG_INFO, + "NAN: Requested CSID %d not supported", + ndp->sec.csid); + return -1; + } + + if ((!pwd || os_strlen(pwd) == 0) && (!pmk || os_strlen(pmk) == 0)) { + wpa_printf(MSG_INFO, + "NAN: Password/PMK required for CSID %d", + ndp->sec.csid); + return -1; + } + + if (pmk) { + if (os_strlen(pmk) != PMK_LEN * 2) { + wpa_printf(MSG_INFO, "NAN: Invalid PMK length: %zu", + os_strlen(pmk)); + return -1; + } + + if (hexstr2bin(pmk, ndp->sec.pmk, PMK_LEN) < 0) { + wpa_printf(MSG_INFO, "NAN: Invalid PMK hex data"); + return -1; + } + + return 0; + } + + /* Derive PMK from password */ + return nan_crypto_derive_nd_pmk(pwd, service_id, ndp->sec.csid, + publisher_nmi, ndp->sec.pmk); +} + + +static int wpas_nan_set_gtk(struct wpa_supplicant *ndi_wpa_s, + struct nan_ndp_params *ndp, int gtk_csid) +{ + if (ndi_wpa_s->ndi_gtk.gtk.gtk_len) { + if (ndi_wpa_s->ndi_gtk.csid != gtk_csid) { + wpa_printf(MSG_INFO, + "NAN: NDI GTK CSID mismatch (expected %d, got %d)", + gtk_csid, ndi_wpa_s->ndi_gtk.csid); + return -1; + } + + os_memcpy(&ndp->sec.gtk, &ndi_wpa_s->ndi_gtk, + sizeof(ndp->sec.gtk)); + return 0; + } + + ndp->sec.gtk.csid = gtk_csid; + if (gtk_csid == NAN_CS_GTK_GCMP_256 && + (ndi_wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) { + ndp->sec.gtk.gtk.gtk_len = 32; + } else if (gtk_csid == NAN_CS_GTK_CCMP_128 && + (ndi_wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP)) { + ndp->sec.gtk.gtk.gtk_len = 16; + } else { + wpa_printf(MSG_INFO, + "NAN: NDI does not support GTK cipher suites"); + return -1; + } + + if (os_get_random(ndp->sec.gtk.gtk.gtk, ndp->sec.gtk.gtk.gtk_len) < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to generate GTK"); + return -1; + } + + ndp->sec.gtk.id = 1; + + wpa_hexdump_key(MSG_DEBUG, "NAN: Generated new GTK", + ndp->sec.gtk.gtk.gtk, ndp->sec.gtk.gtk.gtk_len); + return 0; +} + + +/* Command format NAN_NDP_REQUEST handle= ndi= peer_nmi= + peer_id= ssi= qos= + [csid = |pwd_hex=|pmk=> + [gtk_csid=]] [interface_id=] */ +int wpas_nan_ndp_request(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct nan_ndp_params ndp; + struct wpabuf *ssi_buf = NULL; + char *token, *context = NULL; + char *pos; + const char *pwd = NULL, *pmk = NULL, *pwd_hex = NULL; + char *pwd_decoded = NULL; + int handle = -1; + int ret = -1; + u8 *interface_id = NULL; + struct wpa_supplicant *ndi_wpa_s = NULL; + int gtk_csid = 0; + + os_memset(&ndp, 0, sizeof(ndp)); + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + ndp.type = NAN_NDP_ACTION_REQ; + ndp.qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF; + ndp.qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + + /* Parse command parameters */ + while ((token = str_token(cmd, " ", &context))) { + pos = os_strchr(token, '='); + if (!pos) { + wpa_printf(MSG_INFO, + "NAN: Invalid parameter format: %s", + token); + goto fail; + } + *pos++ = '\0'; + + if (os_strcmp(token, "handle") == 0) { + handle = atoi(pos); + + /* Get service ID from the local handle */ + if (!nan_de_is_valid_instance_id(wpa_s->nan_de, + handle, false, + ndp.u.req.service_id)) + { + wpa_printf(MSG_INFO, + "NAN: Invalid subscribe handle: %d", + handle); + goto fail; + } + } else if (os_strcmp(token, "ndi") == 0) { + ndi_wpa_s = wpa_supplicant_get_iface(wpa_s->global, + pos); + if (!ndi_wpa_s) { + wpa_printf(MSG_INFO, + "NAN: NDI interface not found: %s", + pos); + goto fail; + } + + if (!ndi_wpa_s->nan_data) { + wpa_printf(MSG_INFO, + "NAN: Interface %s is not a NAN data interface", + pos); + goto fail; + } + + os_memcpy(ndp.ndp_id.init_ndi, ndi_wpa_s->own_addr, + ETH_ALEN); + } else if (os_strcmp(token, "peer_nmi") == 0) { + if (hwaddr_aton(pos, ndp.ndp_id.peer_nmi) < 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid peer NMI address: %s", + pos); + goto fail; + } + + } else if (os_strcmp(token, "peer_id") == 0) { + ndp.u.req.publish_inst_id = atoi(pos); + } else if (os_strcmp(token, "ssi") == 0) { + ssi_buf = wpabuf_parse_bin(pos); + if (!ssi_buf) { + wpa_printf(MSG_INFO, + "NAN: Invalid SSI data: %s", pos); + goto fail; + } + + ndp.ssi_len = wpabuf_len(ssi_buf); + ndp.ssi = wpabuf_head(ssi_buf); + } else if (os_strcmp(token, "qos") == 0) { + if (sscanf(pos, "%hhu:%hu", + &ndp.qos.min_slots, + &ndp.qos.max_latency) != 2) { + wpa_printf(MSG_INFO, + "NAN: Invalid QoS parameter: %s", + pos); + goto fail; + } + } else if (os_strcmp(token, "csid") == 0) { + ndp.sec.csid = atoi(pos); + } else if (os_strcmp(token, "password") == 0) { + pwd = pos; + } else if (os_strcmp(token, "pwd_hex") == 0) { + pwd_hex = pos; + } else if (os_strcmp(token, "pmk") == 0) { + pmk = pos; + } else if (os_strcmp(token, "interface_id") == 0) { + os_free(interface_id); + interface_id = + os_malloc(NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + if (!interface_id) + goto fail; + + if (hexstr2bin(pos, interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid interface_id hex data: %s", + pos); + goto fail; + } + + ndp.interface_id = interface_id; + } else if (os_strcmp(token, "gtk_csid") == 0) { + gtk_csid = atoi(pos); + if (gtk_csid != NAN_CS_GTK_CCMP_128 && + gtk_csid != NAN_CS_GTK_GCMP_256) { + wpa_printf(MSG_INFO, + "NAN: Invalid GTK CSID value: %d", + gtk_csid); + goto fail; + } + } else { + wpa_printf(MSG_INFO, "NAN: Unknown parameter: %s", + token); + goto fail; + } + } + + /* Validate required parameters */ + if (handle < 0) { + wpa_printf(MSG_INFO, "NAN: Missing required parameter: handle"); + goto fail; + } + + if (!ndp.u.req.publish_inst_id) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: peer_id"); + goto fail; + } + + if (is_zero_ether_addr(ndp.ndp_id.init_ndi)) { + wpa_printf(MSG_INFO, "NAN: Missing required parameter: ndi"); + goto fail; + } + + if (is_zero_ether_addr(ndp.ndp_id.peer_nmi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: peer_nmi"); + goto fail; + } + + if ((pmk && pwd) || (pmk && pwd_hex) || (pwd && pwd_hex)) { + wpa_printf(MSG_INFO, + "NAN: Specify only one of password, pwd_hex, or pmk"); + goto fail; + } + + if (pwd_hex) { + pwd_decoded = wpas_nan_parse_password_hex(pwd_hex); + if (!pwd_decoded) + goto fail; + } + + if (wpas_nan_fill_nd_pmk(wpa_s, &ndp, handle, ndp.ndp_id.peer_nmi, + pwd_decoded ? pwd_decoded : pwd, pmk) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to derive NDP PMK"); + goto fail; + } + + if (wpas_nan_set_ndp_schedule(wpa_s, &ndp)) { + wpa_printf(MSG_INFO, "NAN: Failed to set NDP schedule"); + goto fail; + } + + if (gtk_csid) { + if (ndp.sec.csid == NAN_CS_NONE || !ndi_wpa_s) { + wpa_printf(MSG_INFO, + "NAN: GTK CSID specified without a valid NDP CSID"); + goto fail; + } + + if (wpas_nan_set_gtk(ndi_wpa_s, &ndp, gtk_csid) < 0) { + wpa_printf(MSG_DEBUG, "NAN: Failed to set NDP GTK"); + goto fail; + } + } + + wpa_printf(MSG_DEBUG, "NAN: Requesting NDP with peer " MACSTR + " using handle %d", MAC2STR(ndp.ndp_id.peer_nmi), + ndp.u.req.publish_inst_id); + ret = nan_handle_ndp_setup(wpa_s->nan, &ndp); +fail: + wpabuf_free(ndp.sched.elems); + wpabuf_free(ssi_buf); + os_free(interface_id); + str_clear_free(pwd_decoded); + + return ret; +} + + +int wpas_nan_ndp_response_set_gtk(struct wpa_supplicant *wpa_s, + struct wpa_supplicant *ndi_wpa_s, + int handle, struct nan_ndp_params *ndp) +{ + int gtk_csid; + + gtk_csid = nan_ndp_requested_gtk_csid(wpa_s->nan, &ndp->ndp_id); + if (!gtk_csid) { + wpa_printf(MSG_DEBUG, "NAN: No GTK requested by peer for NDP"); + return 0; + } + + if (!nan_de_service_supports_csid(wpa_s->nan_de, handle, gtk_csid)) { + wpa_printf(MSG_DEBUG, + "NAN: Cannot set GTK - CSID %d not supported by service", + gtk_csid); + return -1; + } + + return wpas_nan_set_gtk(ndi_wpa_s, ndp, gtk_csid); +} + + +/* Command format NAN_NDP_RESPONSE accept|reject peer_nmi= + [reason_code=] + [ndi= handle= init_ndi= + ndp_id= [ssi=] [qos=] + [csid= |pwd_hex=|pmk=>]] + [interface_id=] */ +int wpas_nan_ndp_response(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct nan_ndp_params ndp; + struct wpabuf *ssi_buf = NULL; + char *token, *context = NULL; + char *pos; + const char *pwd = NULL, *pmk = NULL, *pwd_hex = NULL; + char *pwd_decoded = NULL; + int handle = -1; + int ret = -1; + u8 *interface_id = NULL; + struct wpa_supplicant *ndi_wpa_s = NULL; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + os_memset(&ndp, 0, sizeof(ndp)); + + ndp.type = NAN_NDP_ACTION_RESP; + ndp.qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF; + ndp.qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF; + + /* Parse accept/reject status - the first parameter is mandatory */ + token = str_token(cmd, " ", &context); + if (!token) { + wpa_printf(MSG_INFO, "NAN: Missing accept/reject parameter"); + return -1; + } + + if (os_strcmp(token, "accept") == 0) { + ndp.u.resp.status = NAN_NDP_STATUS_ACCEPTED; + } else if (os_strcmp(token, "reject") == 0) { + ndp.u.resp.status = NAN_NDP_STATUS_REJECTED; + } else { + wpa_printf(MSG_INFO, "NAN: Invalid accept/reject parameter: %s", + token); + return -1; + } + + /* Parse optional parameters */ + while ((token = str_token(cmd, " ", &context))) { + pos = os_strchr(token, '='); + if (!pos) { + wpa_printf(MSG_INFO, + "NAN: Invalid parameter format: %s", token); + goto fail; + } + *pos++ = '\0'; + + if (os_strcmp(token, "reason_code") == 0) { + ndp.u.resp.reason_code = atoi(pos); + } else if (os_strcmp(token, "ndi") == 0) { + ndi_wpa_s = wpa_supplicant_get_iface(wpa_s->global, + pos); + if (!ndi_wpa_s) { + wpa_printf(MSG_INFO, + "NAN: NDI interface not found: %s", + pos); + goto fail; + } + + if (!ndi_wpa_s->nan_data) { + wpa_printf(MSG_INFO, + "NAN: Interface %s is not a NAN data interface", + pos); + goto fail; + } + + os_memcpy(ndp.u.resp.resp_ndi, ndi_wpa_s->own_addr, + ETH_ALEN); + } else if (os_strcmp(token, "peer_nmi") == 0) { + if (hwaddr_aton(pos, ndp.ndp_id.peer_nmi) < 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid peer NMI address: %s", + pos); + goto fail; + } + } else if (os_strcmp(token, "ndp_id") == 0) { + ndp.ndp_id.id = atoi(pos); + } else if (os_strcmp(token, "init_ndi") == 0) { + if (hwaddr_aton(pos, ndp.ndp_id.init_ndi) < 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid initiator NDI address: %s", + pos); + goto fail; + } + } else if (os_strcmp(token, "ssi") == 0) { + ssi_buf = wpabuf_parse_bin(pos); + if (!ssi_buf) { + wpa_printf(MSG_INFO, + "NAN: Invalid SSI data: %s", pos); + goto fail; + } + + ndp.ssi_len = wpabuf_len(ssi_buf); + ndp.ssi = wpabuf_head(ssi_buf); + } else if (os_strcmp(token, "qos") == 0) { + if (sscanf(pos, "%hhu:%hu", + &ndp.qos.min_slots, + &ndp.qos.max_latency) != 2) { + wpa_printf(MSG_INFO, + "NAN: Invalid QoS parameter: %s", + pos); + goto fail; + } + } else if (os_strcmp(token, "handle") == 0) { + handle = atoi(pos); + } else if (os_strcmp(token, "csid") == 0) { + ndp.sec.csid = atoi(pos); + } else if (os_strcmp(token, "password") == 0) { + pwd = pos; + } else if (os_strcmp(token, "pwd_hex") == 0) { + pwd_hex = pos; + } else if (os_strcmp(token, "pmk") == 0) { + pmk = pos; + } else if (os_strcmp(token, "interface_id") == 0) { + os_free(interface_id); + interface_id = + os_malloc(NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + if (!interface_id) + goto fail; + + if (hexstr2bin(pos, interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Invalid interface_id hex data: %s", + pos); + goto fail; + } + + ndp.interface_id = interface_id; + } else { + wpa_printf(MSG_DEBUG, "NAN: Unknown parameter: %s", + token); + } + } + + /* If we initiated the NDP setup, we are the subscriber */ + if (ether_addr_equal(ndp.u.resp.resp_ndi, ndp.ndp_id.init_ndi)) + ndp.type = NAN_NDP_ACTION_CONF; + + /* Validate required parameters for accept case */ + if (ndp.u.resp.status == NAN_NDP_STATUS_ACCEPTED) { + const u8 *publisher_nmi; + + if (is_zero_ether_addr(ndp.u.resp.resp_ndi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter for accept: ndi"); + goto fail; + } + + if (ndp.type == NAN_NDP_ACTION_CONF) + publisher_nmi = ndp.ndp_id.peer_nmi; + else + publisher_nmi = wpa_s->own_addr; + + if (handle < 1) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter for accept: handle"); + goto fail; + } + + if ((pmk && pwd) || (pmk && pwd_hex) || (pwd && pwd_hex)) { + wpa_printf(MSG_INFO, + "NAN: Specify only one of password, pwd_hex, or pmk"); + goto fail; + } + + if (pwd_hex) { + pwd_decoded = wpas_nan_parse_password_hex(pwd_hex); + if (!pwd_decoded) + goto fail; + } + + if (wpas_nan_fill_nd_pmk(wpa_s, &ndp, handle, publisher_nmi, + pwd_decoded ? pwd_decoded : pwd, pmk) + < 0) { + wpa_printf(MSG_INFO, "NAN: Failed to derive NDP PMK"); + goto fail; + } + } + + /* Validate common required parameters */ + if (is_zero_ether_addr(ndp.ndp_id.peer_nmi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: peer_nmi"); + goto fail; + } + + if (is_zero_ether_addr(ndp.ndp_id.init_ndi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: init_ndi"); + goto fail; + } + + if (!ndp.ndp_id.id) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: ndp_id"); + goto fail; + } + + if (ndp.u.resp.status == NAN_NDP_STATUS_ACCEPTED && ndi_wpa_s && + wpas_nan_ndp_response_set_gtk(wpa_s, ndi_wpa_s, handle, &ndp) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to set GTK for NDP response"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NAN: %s NDP response for peer " MACSTR + " ndp_id=%u", + ndp.u.resp.status == NAN_NDP_STATUS_ACCEPTED ? + "Accepting" : "Rejecting", + MAC2STR(ndp.ndp_id.peer_nmi), ndp.ndp_id.id); + + if (wpas_nan_set_ndp_schedule(wpa_s, &ndp) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to set NDP schedule"); + goto fail; + } + + ret = nan_handle_ndp_setup(wpa_s->nan, &ndp); + if (ret < 0) + wpa_printf(MSG_INFO, "NAN: Failed to handle NDP response"); + +fail: + wpabuf_free(ndp.sched.elems); + wpabuf_free(ssi_buf); + os_free(interface_id); + str_clear_free(pwd_decoded); + + return ret; +} + + +/* Format: NAN_NDP_TERMINATE peer_nmi= init_ndi= ndp_id= */ +int wpas_nan_ndp_terminate(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct nan_ndp_params ndp; + char *token, *context = NULL; + char *pos; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + os_memset(&ndp, 0, sizeof(ndp)); + + ndp.type = NAN_NDP_ACTION_TERM; + + /* Parse command parameters */ + while ((token = str_token(cmd, " ", &context))) { + pos = os_strchr(token, '='); + if (!pos) { + wpa_printf(MSG_INFO, + "NAN: Invalid parameter format: %s", + token); + return -1; + } + *pos++ = '\0'; + + if (os_strcmp(token, "peer_nmi") == 0) { + if (hwaddr_aton(pos, ndp.ndp_id.peer_nmi) < 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid peer NMI address: %s", + pos); + return -1; + } + } else if (os_strcmp(token, "init_ndi") == 0) { + if (hwaddr_aton(pos, ndp.ndp_id.init_ndi) < 0) { + wpa_printf(MSG_INFO, + "NAN: Invalid initiator NDI address: %s", + pos); + return -1; + } + } else if (os_strcmp(token, "ndp_id") == 0) { + ndp.ndp_id.id = atoi(pos); + } else { + wpa_printf(MSG_DEBUG, "NAN: Unknown parameter: %s", + token); + } + } + + /* Validate required parameters */ + if (is_zero_ether_addr(ndp.ndp_id.peer_nmi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: peer_nmi"); + return -1; + } + + if (is_zero_ether_addr(ndp.ndp_id.init_ndi)) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: init_ndi"); + return -1; + } + + if (!ndp.ndp_id.id) { + wpa_printf(MSG_INFO, + "NAN: Missing required parameter: ndp_id"); + return -1; + } + + wpa_printf(MSG_DEBUG, "NAN: Terminating NDP with peer " MACSTR + " init_ndi=" MACSTR " ndp_id=%u", + MAC2STR(ndp.ndp_id.peer_nmi), + MAC2STR(ndp.ndp_id.init_ndi), ndp.ndp_id.id); + + return nan_handle_ndp_setup(wpa_s->nan, &ndp); +} + + +int wpas_nan_status(struct wpa_supplicant *wpa_s, char *reply, + size_t reply_size) +{ + char *pos = reply; + char *end = reply + reply_size; + int ret; + + if (!wpas_nan_ready(wpa_s)) + return -1; + + ret = nan_get_status(wpa_s->nan, pos, end - pos); + if (ret > 0) + pos += ret; + + ret = nan_de_get_status(wpa_s->nan_de, pos, end - pos); + if (ret > 0) + pos += ret; + return pos - reply; +} + + +#ifdef CONFIG_PASN +static int wpas_nan_append_ik_info(char *reply, size_t reply_size, + const struct wpa_dev_ik *ik) +{ + char *pos = reply; + char *end = reply + reply_size; + + pos += wpa_scnprintf(pos, end - pos, "nik_cipher=%d\n", ik->dik_cipher); + pos += wpa_scnprintf(pos, end - pos, "nik="); + pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(ik->dik), + wpabuf_len(ik->dik)); + pos += wpa_scnprintf(pos, end - pos, "\n"); + + if (ik->pmk) { + pos += wpa_scnprintf(pos, end - pos, "akmp=%s\n", + wpa_key_mgmt_txt(ik->akmp, WPA_PROTO_RSN)); + pos += wpa_scnprintf(pos, end - pos, "npk="); + pos += wpa_snprintf_hex(pos, end - pos, wpabuf_head(ik->pmk), + wpabuf_len(ik->pmk)); + pos += wpa_scnprintf(pos, end - pos, "\n"); + } + + return pos - reply; +} +#endif /* CONFIG_PASN */ + + +/* Format: NAN_PEER_INFO + * [map_id] */ +int wpas_nan_peer_info(struct wpa_supplicant *wpa_s, const char *cmd, + char *reply, size_t reply_size) +{ + u8 addr[ETH_ALEN]; + char *pos; + int ret = 0; + + if (!wpas_nan_ready(wpa_s)) + return -1; + + if (hwaddr_aton(cmd, addr) < 0) { + wpa_printf(MSG_INFO, "NAN: Invalid peer address: %s", cmd); + return -1; + } + + pos = os_strchr(cmd, ' '); + if (!pos) { + wpa_printf(MSG_INFO, "NAN: Missing info type parameter"); + return -1; + } + + if (os_strncmp(pos + 1, "schedule", 8) == 0) { + struct nan_peer_schedule sched; + + if (nan_peer_get_schedule_info(wpa_s->nan, addr, &sched) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to get schedule info for peer " + MACSTR, MAC2STR(addr)); + return -1; + } + + ret = nan_peer_dump_sched_to_buf(&sched, reply, reply_size); + } else if (os_strncmp(pos + 1, "potential", 9) == 0) { + struct nan_peer_potential_avail pot_avail; + + if (nan_peer_get_pot_avail(wpa_s->nan, addr, &pot_avail) < 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to get potential availability for peer " + MACSTR, MAC2STR(addr)); + return -1; + } + + ret = nan_peer_dump_pot_avail_to_buf(&pot_avail, reply, + reply_size); + } else if (os_strncmp(pos + 1, "capa", 4) == 0) { + int map_id = 0; + const struct nan_device_capabilities *capa; + int written = 0; + char *m; + + m = os_strchr(pos + 1, ' '); + if (m) + map_id = atoi(m + 1); + + capa = nan_peer_get_device_capabilities(wpa_s->nan, addr, + map_id); + if (!capa) { + wpa_printf(MSG_INFO, + "NAN: Failed to get capabilities for peer " + MACSTR, MAC2STR(addr)); + return -1; + } + + written += wpa_scnprintf(reply + written, reply_size - written, + "supported_bands=0x%02x\n", + capa->supported_bands); + written += wpa_scnprintf(reply + written, reply_size - written, + "op_modes=0x%04x\n", capa->op_mode); + written += wpa_scnprintf(reply + written, reply_size - written, + "cdw_info=0x%04x\n", capa->cdw_info); + written += wpa_scnprintf(reply + written, reply_size - written, + "n_antennas=%d\n", capa->n_antennas); + written += wpa_scnprintf(reply + written, reply_size - written, + "channel_switch_time=%d\n", + capa->channel_switch_time); + written += wpa_scnprintf(reply + written, reply_size - written, + "capabilities=0x%02x\n", capa->capa); + + ret = written; + } else if (os_strncmp(pos + 1, "bootstrap", 9) == 0) { + u16 supported_methods; + + if (nan_bootstrap_get_supported_methods(wpa_s->nan, addr, + &supported_methods) < + 0) { + wpa_printf(MSG_INFO, + "NAN: Failed to get bootstrap methods for peer " + MACSTR, MAC2STR(addr)); + return -1; + } + + ret = wpa_scnprintf(reply, reply_size, + "supported_methods=0x%04x\n", + supported_methods); +#ifdef CONFIG_PASN + } else if (os_strncmp(pos + 1, "pairing", 7) == 0) { + const struct nan_pairing_cfg *pairing_cfg; + const struct wpa_dev_ik *ik = NULL; + const u8 *nonce = NULL; + const u8 *tag = NULL; + + pairing_cfg = nan_peer_get_pairing_cfg(wpa_s->nan, addr, + &nonce, &tag); + if (!pairing_cfg) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to get pairing config for peer " + MACSTR, MAC2STR(addr)); + return -1; + } + + ret = wpa_scnprintf(reply, reply_size, + "pairing_setup=%d\n" + "npk_caching=%d\n" + "pairing_verification=%d\n" + "cipher_suites=0x%08x\n", + pairing_cfg->pairing_setup, + pairing_cfg->npk_caching, + pairing_cfg->pairing_verification, + pairing_cfg->cipher_suites); + + /* Try to find matching NIK if nonce and tag are available */ + if (nonce && tag) + ik = wpas_nan_find_ik_by_nonce_tag(wpa_s, addr, nonce, + tag); + + if (ik) + ret += wpas_nan_append_ik_info(reply + ret, + reply_size - ret, ik); +#endif /* CONFIG_PASN */ + } else if (os_strncmp(pos + 1, "ndps", 4) == 0) { + ret = nan_peer_dump_ndps_to_buf(wpa_s->nan, addr, reply, + reply_size); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to get NDPs for peer " MACSTR, + MAC2STR(addr)); + return -1; + } + } else { + wpa_printf(MSG_INFO, "NAN: Unknown info type: %s", pos + 1); + ret = -1; + } + + return ret; +} + + +/* + * Format: NAN_BOOTSTRAP > + * > method= [auth] + */ +int wpas_nan_bootstrap_request(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *token, *context = NULL; + int handle = 0; + int req_instance_id = 0; + u8 peer_nmi[ETH_ALEN]; + u16 bootstrap_method = 0; + bool auth = false; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + /* Parse peer address first */ + if (hwaddr_aton(cmd, peer_nmi) < 0) + return -1; + + /* Move past the peer_mac address */ + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + while ((token = str_token(pos, " ", &context))) { + if (sscanf(token, "handle=%i", &handle) == 1) + continue; + + if (sscanf(token, "req_instance_id=%i", &req_instance_id) == 1) + continue; + + if (os_strncmp(token, "method=", 7) == 0) { + bootstrap_method = atoi(token + 7); + continue; + } + + if (os_strcmp(token, "auth") == 0) { + auth = true; + continue; + } + + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_BOOTSTRAP parameter: %s", + token); + return -1; + } + + if (!bootstrap_method) { + wpa_printf(MSG_INFO, "CTRL: Missing NAN_BOOTSTRAP method"); + return -1; + } + + if (handle <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_BOOTSTRAP handle"); + return -1; + } + + if (is_zero_ether_addr(peer_nmi)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_BOOTSTRAP address"); + return -1; + } + + return nan_bootstrap_request(wpa_s->nan, handle, peer_nmi, + req_instance_id, bootstrap_method, auth); +} + + +/* Format: NAN_BOOTSTRAP_RESET */ +int wpas_nan_bootstrap_reset(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 peer_nmi[ETH_ALEN]; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + if (hwaddr_aton(cmd, peer_nmi) < 0) + return -1; + + return nan_bootstrap_peer_reset(wpa_s->nan, peer_nmi); +} + + +static void wpas_nan_de_add_extra_attrs(void *ctx, struct wpabuf *buf) +{ + struct wpa_supplicant *wpa_s = ctx; + struct nan_schedule sched; + u32 map_ids = (BIT(wpa_s->nan_capa.num_radios) - 1) << 1; + int i; + + if (!wpas_nan_ndp_allowed(wpa_s) || !map_ids) + return; + + wpas_nan_fill_ndp_schedule(wpa_s, &sched); + nan_add_dev_capa_attr(wpa_s->nan, buf); + nan_convert_sched_to_avail_attrs(wpa_s->nan, + wpa_s->schedule_sequence_id, + map_ids, sched.n_chans, + sched.chans, buf, true); + nan_pairing_add_attrs(wpa_s->nan, buf); + + if (!wpa_s->nan_ulw_attr) + return; + + /* Add ULW attribute only if there are committed availability entries */ + for (i = 0; i < sched.n_chans; i++) { + if (sched.chans[i].committed.len) { + wpabuf_put_buf(buf, wpa_s->nan_ulw_attr); + break; + } + } +} + + +void wpas_nan_cluster_join(struct wpa_supplicant *wpa_s, + const u8 *cluster_id, + bool new_cluster) +{ + if (!wpas_nan_ready(wpa_s)) + return; + + wpas_notify_nan_cluster_join(wpa_s, cluster_id, new_cluster); + + nan_de_set_cluster_id(wpa_s->nan_de, cluster_id); + nan_set_cluster_id(wpa_s->nan, cluster_id); +} + + +void wpas_nan_next_dw(struct wpa_supplicant *wpa_s, u32 freq) +{ + if (!wpas_nan_ready(wpa_s)) + return; + + wpa_printf(MSG_DEBUG, "NAN: Next DW notification freq=%d", freq); + nan_de_dw_trigger(wpa_s->nan_de, freq); +} + + +void wpas_nan_sched_update_done(struct wpa_supplicant *wpa_s, + const union wpa_event_data *data) +{ + u8 map_id = wpa_s->nan_sched_update.map_id; + bool success = data->nan_sched_update_done_info.success; + + if (!wpas_nan_ready(wpa_s)) + return; + + if (!wpa_s->nan_sched_update.sched.deferred) { + wpa_printf(MSG_DEBUG, "NAN: Schedule update not in progress"); + return; + } + + nan_set_sched_update_pending(wpa_s->nan, false); + wpas_notify_nan_sched_update_done(wpa_s, success); + + if (!success) { + clear_sched_config(&wpa_s->nan_sched_update.sched); + wpa_printf(MSG_DEBUG, "NAN: Schedule update failed"); + return; + } + + clear_sched_config(&wpa_s->nan_sched[map_id - 1]); + os_memcpy(&wpa_s->nan_sched[map_id - 1], + &wpa_s->nan_sched_update.sched, + sizeof(wpa_s->nan_sched_update.sched)); + os_memset(&wpa_s->nan_sched_update.sched, 0, + sizeof(wpa_s->nan_sched_update.sched)); + wpa_s->schedule_sequence_id++; + + wpas_nan_update_local_schedule(wpa_s); +} + + +void wpas_nan_ulw_update(struct wpa_supplicant *wpa_s, + const u8 *ulw, size_t ulw_len) +{ + if (!wpas_nan_ready(wpa_s)) + return; + + wpabuf_free(wpa_s->nan_ulw_attr); + if (ulw && ulw_len) { + wpa_s->nan_ulw_attr = wpabuf_alloc_copy(ulw, ulw_len); + if (!wpa_s->nan_ulw_attr) { + wpa_printf(MSG_INFO, + "NAN: Failed to allocate ULW attribute buffer"); + return; + } + + wpa_hexdump(MSG_DEBUG, "NAN: ULW update", ulw, ulw_len); + } else { + wpa_printf(MSG_DEBUG, "NAN: ULW update cleared"); + wpa_s->nan_ulw_attr = NULL; + } +} + + +void wpas_nan_chan_evacuation(struct wpa_supplicant *wpa_s, + const struct nan_chan_evacuation_info *info) +{ + size_t map_id, i; + int freq = info->freq; + + if (!wpas_nan_ready(wpa_s)) + return; + + wpa_printf(MSG_DEBUG, "NAN: Channel evacuation notification freq=%d", + freq); + + for (map_id = 0; map_id < MAX_NAN_RADIOS; map_id++) { + struct nan_schedule_config *sched = + &wpa_s->nan_sched[map_id]; + + for (i = 0; i < sched->num_channels; i++) { + if (sched->channels[i].freq != freq) + continue; + + wpas_notify_nan_chan_evacuation(wpa_s, map_id, freq); + break; + } + } +} + + +#ifdef CONFIG_PASN + +static int wpas_nan_pasn_update_station(struct wpa_supplicant *wpa_s, + const u8 *nmi_addr) +{ + struct hostapd_sta_add_params params; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = nmi_addr; + params.flags = WPA_STA_MFP; + params.set = 1; + + if (wpa_drv_sta_add(wpa_s, ¶ms) < 0) { + wpa_printf(MSG_INFO, "NAN PASN: Failed to update PASN station " + MACSTR, MAC2STR(nmi_addr)); + return -1; + } + + return 0; +} + + +/** + * wpas_nan_pair - Initiate NAN pairing with a peer device + * @wpa_s: Pointer to wpa_supplicant data structure + * @peer_addr: MAC address of the peer device to pair with + * @auth_mode: Authentication mode to use for pairing + * @cipher: Cipher suite to use for the pairing session + * @handle: Handle of the service for which pairing is requested + * @peer_instance_id: Instance ID of the peer service + * @responder: True if the local device is the responder, false if initiator + * @password: Password for PASN authentication + * Returns: 0 on success, -1 on failure + */ +int wpas_nan_pair(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + u8 auth_mode, int cipher, int handle, u8 peer_instance_id, + bool responder, const char *password) +{ + int ret; + struct nan_schedule sched; + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + wpas_nan_fill_ndp_schedule(wpa_s, &sched); + ret = nan_pairing_initiate_pasn_auth(wpa_s->nan, peer_addr, auth_mode, + cipher, handle, peer_instance_id, + responder, password, &sched); + if (!ret) + ret = wpas_nan_pasn_update_station(wpa_s, peer_addr); + else + wpa_printf(MSG_INFO, + "NAN PASN: Failed to start PASN authentication"); + + return ret; +} + + +/* + * Format: NAN_PAIR > + * > > > + * [responder] [password=|pwd_hex=] + */ +int wpas_nan_pairing_start(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + u8 addr[ETH_ALEN]; + u8 auth_mode = 0; + u8 peer_instance_id = 0; + int handle = 0; + int cipher = WPA_CIPHER_NONE; + char *password = NULL, *password_hex = NULL; + char *password_decoded = NULL; + bool responder = false; + char *pos; + + /* Parse peer address first */ + if (hwaddr_aton(cmd, addr) < 0) + return -1; + + /* Move past the peer_mac address */ + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + while ((token = str_token(pos, " ", &context))) { + if (os_strncmp(token, "auth=", 5) == 0) { + auth_mode = atoi(token + 5); + if (auth_mode > 2) { + wpa_printf(MSG_INFO, + "NAN_PAIR: Invalid auth mode: %u", + auth_mode); + return -1; + } + } else if (os_strncmp(token, "handle=", 7) == 0) { + handle = atoi(token + 7); + } else if (os_strncmp(token, "peer_instance_id=", 17) == 0) { + peer_instance_id = atoi(token + 17); + } else if (os_strncmp(token, "cipher=", 7) == 0) { + if (os_strcmp(token + 7, "CCMP") == 0) { + cipher = WPA_CIPHER_CCMP; + } else if (os_strcmp(token + 7, "GCMP-256") == 0) { + cipher = WPA_CIPHER_GCMP_256; + } else { + wpa_printf(MSG_INFO, + "NAN_PAIR: Invalid cipher: '%s'", + token + 7); + return -1; + } + } else if (os_strncmp(token, "responder", 9) == 0) { + responder = true; + } else if (os_strncmp(token, "password=", 9) == 0) { + password = token + 9; + } else if (os_strncmp(token, "pwd_hex=", 8) == 0) { + password_hex = token + 8; + } else { + wpa_printf(MSG_INFO, + "NAN_PAIR: Invalid parameter: '%s'", + token); + return -1; + } + } + + if (handle <= 0) { + wpa_printf(MSG_INFO, "NAN_PAIR: missing or invalid handle"); + return -1; + } + + if (!peer_instance_id) { + wpa_printf(MSG_INFO, + "NAN_PAIR: missing or invalid peer_instance_id"); + return -1; + } + + if (cipher == WPA_CIPHER_NONE) { + wpa_printf(MSG_INFO, "NAN_PAIR: missing cipher"); + return -1; + } + + if (password && password_hex) { + wpa_printf(MSG_DEBUG, + "NAN_PAIR: Specify only one of password or pwd_hex"); + return -1; + } + + if (password_hex) { + password_decoded = wpas_nan_parse_password_hex(password_hex); + if (!password_decoded) + return -1; + } + + if (wpas_nan_pair(wpa_s, addr, auth_mode, cipher, handle, + peer_instance_id, responder, + password_decoded ? password_decoded : password) < 0) { + str_clear_free(password_decoded); + wpa_printf(MSG_INFO, "NAN_PAIR: Pairing initiation failed"); + return -1; + } + + str_clear_free(password_decoded); + + return 0; +} + + +int wpas_nan_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked) +{ + struct nan_data *nan = wpa_s->nan; + + return nan_pairing_pasn_auth_tx_status(nan, data, data_len, acked); +} + + +int wpas_nan_pairing_abort(struct wpa_supplicant *wpa_s, const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct nan_data *nan = wpa_s->nan; + + if (!nan) { + wpa_printf(MSG_INFO, "NAN_PAIR_ABORT: NAN not initialized"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_INFO, + "NAN_PAIR_ABORT: Invalid peer address: '%s'", cmd); + return -1; + } + + if (nan_pairing_abort(nan, addr) < 0) { + wpa_printf(MSG_INFO, + "NAN_PAIR_ABORT: Abort failed for peer " MACSTR, + MAC2STR(addr)); + return -1; + } + + return 0; +} + + +int wpas_nan_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct nan_data *nan = wpa_s->nan; + + if (!nan || !wpas_nan_ndp_allowed(wpa_s)) + return -1; + + return nan_pairing_auth_rx(nan, mgmt, len); +} + +#endif /* CONFIG_PASN */ + + +bool wpas_nan_is_peer_paired(struct wpa_supplicant *wpa_s, const u8 *peer_addr) +{ + if (!wpa_s->nan) + return false; + + return nan_pairing_is_peer_paired(wpa_s->nan, peer_addr); +} + +#endif /* CONFIG_NAN */ + + +static const char * +tx_status_result_txt(enum offchannel_send_action_result result) +{ + switch (result) { + case OFFCHANNEL_SEND_ACTION_SUCCESS: + return "success"; + case OFFCHANNEL_SEND_ACTION_NO_ACK: + return "no-ack"; + case OFFCHANNEL_SEND_ACTION_FAILED: + return "failed"; + } + + return "?"; +} + + +static void wpas_nan_de_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + if (!wpa_s->nan_de) + return; + + wpa_printf(MSG_DEBUG, "NAN: TX status A1=" MACSTR " A2=" MACSTR + " A3=" MACSTR " freq=%d len=%zu result=%s", + MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq, + data_len, tx_status_result_txt(result)); + + nan_de_tx_status(wpa_s->nan_de, freq, dst, data, data_len, + result == OFFCHANNEL_SEND_ACTION_SUCCESS); +} + + +struct wpas_nan_usd_tx_work { + unsigned int freq; + unsigned int wait_time; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + struct wpabuf *buf; +}; + + +static void wpas_nan_usd_tx_work_free(struct wpas_nan_usd_tx_work *twork) +{ + if (!twork) + return; + wpabuf_free(twork->buf); + os_free(twork); +} + + +static void wpas_nan_usd_tx_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_nan_usd_tx_work *twork; + + if (!wpa_s->nan_usd_tx_work) + return; + + twork = wpa_s->nan_usd_tx_work->ctx; + wpas_nan_usd_tx_work_free(twork); + radio_work_done(wpa_s->nan_usd_tx_work); + wpa_s->nan_usd_tx_work = NULL; +} + + +static int wpas_nan_de_tx_send(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int wait_time, const u8 *dst, + const u8 *src, const u8 *bssid, + const struct wpabuf *buf) +{ + wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR + " A3=" MACSTR " freq=%d len=%zu", + MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq, + wpabuf_len(buf)); + + return offchannel_send_action(wpa_s, freq, dst, src, bssid, + wpabuf_head(buf), wpabuf_len(buf), + wait_time, wpas_nan_de_tx_status, 1); +} + + +static void wpas_nan_usd_start_tx_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_nan_usd_tx_work *twork = work->ctx; + + if (deinit) { + if (work->started) { + wpa_s->nan_usd_tx_work = NULL; + offchannel_send_action_done(wpa_s); + } + wpas_nan_usd_tx_work_free(twork); + return; + } + + wpa_s->nan_usd_tx_work = work; + + if (wpas_nan_de_tx_send(wpa_s, twork->freq, twork->wait_time, + twork->dst, twork->src, twork->bssid, + twork->buf) < 0) + wpas_nan_usd_tx_work_done(wpa_s); +} + + +static int wpas_nan_de_tx(void *ctx, unsigned int freq, unsigned int wait_time, + const u8 *dst, const u8 *src, const u8 *bssid, + const struct wpabuf *buf) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpas_nan_usd_tx_work *twork; + + if (!freq && !wait_time) { + int ret; + + wpa_printf(MSG_DEBUG, "NAN: SYNC TX NAN SDF A1=" MACSTR " A2=" + MACSTR " A3=" MACSTR " len=%zu", + MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + wpabuf_len(buf)); + ret = wpa_drv_send_action(wpa_s, 0, 0, dst, src, bssid, + wpabuf_head(buf), wpabuf_len(buf), + 1); + if (ret) + wpa_printf(MSG_DEBUG, + "NAN: Failed to send sync action frame (%d)", + ret); + return ret; + } + + if (wpa_s->nan_usd_tx_work || wpa_s->nan_usd_listen_work) { + /* Reuse ongoing radio work */ + return wpas_nan_de_tx_send(wpa_s, freq, wait_time, dst, src, + bssid, buf); + } + + twork = os_zalloc(sizeof(*twork)); + if (!twork) + return -1; + twork->freq = freq; + twork->wait_time = wait_time; + os_memcpy(twork->dst, dst, ETH_ALEN); + os_memcpy(twork->src, src, ETH_ALEN); + os_memcpy(twork->bssid, bssid, ETH_ALEN); + twork->buf = wpabuf_dup(buf); + if (!twork->buf) { + wpas_nan_usd_tx_work_free(twork); + return -1; + } + + if (!radio_add_work(wpa_s, freq, "nan-usd-tx", 0, + wpas_nan_usd_start_tx_cb, twork)) { + wpas_nan_usd_tx_work_free(twork); + return -1; + } + + return 0; +} + + +struct wpas_nan_usd_listen_work { + unsigned int freq; + unsigned int duration; + u8 forced_addr[ETH_ALEN]; + bool forced_addr_set; +}; + + +static void wpas_nan_usd_listen_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_nan_usd_listen_work *lwork; + + if (!wpa_s->nan_usd_listen_work) + return; + + lwork = wpa_s->nan_usd_listen_work->ctx; + os_free(lwork); + radio_work_done(wpa_s->nan_usd_listen_work); + wpa_s->nan_usd_listen_work = NULL; +} + + +static void wpas_nan_usd_remain_on_channel_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpas_nan_usd_listen_work *lwork = timeout_ctx; + + wpas_nan_usd_cancel_remain_on_channel_cb(wpa_s, lwork->freq); +} + + +static void wpas_nan_usd_start_listen_cb(struct wpa_radio_work *work, + int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_nan_usd_listen_work *lwork = work->ctx; + unsigned int duration; + + if (deinit) { + if (work->started) { + wpa_s->nan_usd_listen_work = NULL; + wpa_drv_cancel_remain_on_channel(wpa_s); + } + os_free(lwork); + return; + } + + wpa_s->nan_usd_listen_work = work; + + duration = lwork->duration; + if (duration > wpa_s->max_remain_on_chan) + duration = wpa_s->max_remain_on_chan; + wpa_printf(MSG_DEBUG, "NAN: Start listen on %u MHz for %u ms", + lwork->freq, duration); + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration, + (lwork->forced_addr_set && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER)) ? + lwork->forced_addr : NULL) < 0) { + wpa_printf(MSG_DEBUG, + "NAN: Failed to request the driver to remain on channel (%u MHz) for listen", + lwork->freq); + eloop_cancel_timeout(wpas_nan_usd_remain_on_channel_timeout, + wpa_s, ELOOP_ALL_CTX); + /* Restart the listen state after a delay */ + eloop_register_timeout(0, 500, + wpas_nan_usd_remain_on_channel_timeout, + wpa_s, lwork); + wpas_nan_usd_listen_work_done(wpa_s); + return; + } +} + + +static int wpas_nan_de_listen(void *ctx, unsigned int freq, + unsigned int duration, const u8 *forced_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpas_nan_usd_listen_work *lwork; + + lwork = os_zalloc(sizeof(*lwork)); + if (!lwork) + return -1; + lwork->freq = freq; + lwork->duration = duration; + if (forced_addr) { + os_memcpy(lwork->forced_addr, forced_addr, ETH_ALEN); + lwork->forced_addr_set = true; + } + + if (!radio_add_work(wpa_s, freq, "nan-usd-listen", 0, + wpas_nan_usd_start_listen_cb, lwork)) { + os_free(lwork); + return -1; + } + + return 0; +} + + +static void +wpas_nan_de_discovery_result(void *ctx, struct nan_discovery_result *res) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_discovery_result(wpa_s, res); +} + + +static void wpas_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr, + int peer_subscribe_id, + enum nan_service_protocol_type srv_proto_type, + const u8 *ssi, size_t ssi_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_replied(wpa_s, srv_proto_type, publish_id, + peer_subscribe_id, peer_addr, ssi, ssi_len); +} + + +static void wpas_nan_de_publish_terminated(void *ctx, int publish_id, + enum nan_de_reason reason) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_publish_terminated(wpa_s, publish_id, reason); +} + + +static void wpas_nan_usd_offload_cancel_publish(void *ctx, int publish_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + wpas_drv_nan_cancel_publish(wpa_s, publish_id); +} + + +static void wpas_nan_de_subscribe_terminated(void *ctx, int subscribe_id, + enum nan_de_reason reason) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_subscribe_terminated(wpa_s, subscribe_id, reason); +} + + +static void wpas_nan_usd_offload_cancel_subscribe(void *ctx, int subscribe_id) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + wpas_drv_nan_cancel_subscribe(wpa_s, subscribe_id); +} + + +static void wpas_nan_de_receive(void *ctx, int id, int peer_instance_id, + const u8 *ssi, size_t ssi_len, + const u8 *peer_addr, + const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + +#ifdef CONFIG_NAN + if (nan_process_followup(wpa_s->nan, peer_addr, buf, len, + peer_instance_id, id)) + return; +#endif /* CONFIG_NAN */ + + wpas_notify_nan_receive(wpa_s, id, peer_instance_id, peer_addr, + ssi, ssi_len); +} + + +static void wpas_nan_de_transmit_req_status(void *ctx, u32 cookie, bool ack) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_nan_transmit_req_status(wpa_s, cookie, ack); +} + + +#ifdef CONFIG_P2P +static void wpas_nan_process_p2p_usd_elems(void *ctx, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_p2p_process_usd_elems(wpa_s, buf, buf_len, peer_addr, freq); +} +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_PR +static void wpas_nan_process_pr_usd_elems(void *ctx, const u8 *buf, u16 buf_len, + const u8 *peer_addr, + unsigned int freq) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_pr_process_usd_elems(wpa_s, buf, buf_len, peer_addr, freq); +} +#endif /* CONFIG_PR */ + + +#if defined(CONFIG_NAN) && defined(CONFIG_PASN) +static bool wpas_nan_is_peer_paired_cb(void *ctx, const u8 *peer_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpas_nan_is_peer_paired(wpa_s, peer_addr); +} +#endif /* CONFIG_NAN && CONFIG_PASN */ + + +int wpas_nan_de_init(struct wpa_supplicant *wpa_s) +{ + struct nan_callbacks cb; + bool offload = !!(wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD); + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = wpa_s; + cb.tx = wpas_nan_de_tx; + cb.listen = wpas_nan_de_listen; + cb.discovery_result = wpas_nan_de_discovery_result; + cb.replied = wpas_nan_de_replied; + cb.publish_terminated = wpas_nan_de_publish_terminated; + cb.subscribe_terminated = wpas_nan_de_subscribe_terminated; + cb.offload_cancel_publish = wpas_nan_usd_offload_cancel_publish; + cb.offload_cancel_subscribe = wpas_nan_usd_offload_cancel_subscribe; + cb.receive = wpas_nan_de_receive; + cb.transmit_req_status = wpas_nan_de_transmit_req_status; +#ifdef CONFIG_P2P + cb.process_p2p_usd_elems = wpas_nan_process_p2p_usd_elems; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_PR + cb.process_pr_usd_elems = wpas_nan_process_pr_usd_elems; +#endif /* CONFIG_PR */ +#ifdef CONFIG_NAN + cb.add_extra_attrs = wpas_nan_de_add_extra_attrs; +#ifdef CONFIG_PASN + cb.is_peer_paired = wpas_nan_is_peer_paired_cb; +#endif /* CONFIG_PASN */ +#endif /* CONFIG_NAN */ + + wpa_s->nan_de = nan_de_init(wpa_s->own_addr, offload, false, + wpa_s->max_remain_on_chan, &cb); + if (!wpa_s->nan_de) + return -1; + return 0; +} + + +void wpas_nan_de_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_nan_usd_remain_on_channel_timeout, + wpa_s, ELOOP_ALL_CTX); + nan_de_deinit(wpa_s->nan_de); + wpa_s->nan_de = NULL; +} + + +void wpas_nan_de_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *a3, unsigned int freq, + const u8 *buf, size_t len, int rssi) +{ + bool store_peer; + + if (!wpa_s->nan_de) + return; + + store_peer = nan_de_rx_sdf(wpa_s->nan_de, src, a3, freq, buf, + len, rssi); + + if (!store_peer) + return; + +#ifdef CONFIG_NAN + if (!wpas_nan_ready(wpa_s)) + return; + + nan_add_peer(wpa_s->nan, src, buf, len); +#endif /* CONFIG_NAN */ +} + + +void wpas_nan_de_flush(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->nan_de) + return; + nan_de_flush(wpa_s->nan_de); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + wpas_drv_nan_flush(wpa_s); +} + + +int wpas_nan_publish(struct wpa_supplicant *wpa_s, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + struct nan_publish_params *params, bool p2p) +{ + int publish_id; + struct wpabuf *elems = NULL; + const u8 *addr; + + if (!wpa_s->nan_de) + return -1; + + if (params->proximity_ranging && !params->solicited) { + wpa_printf(MSG_INFO, + "PR unsolicited publish service discovery not allowed"); + return -1; + } + + addr = wpa_s->own_addr; + +#ifdef CONFIG_NAN + if (params->sync) { + if (!(wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) { + wpa_printf(MSG_INFO, + "NAN: Cannot advertise sync service, driver does not support user space DE"); + return -1; + } + + if (!wpas_nan_ready(wpa_s)) { + wpa_printf(MSG_INFO, + "NAN: Synchronized support is not enabled"); + return -1; + } + + if (p2p) { + wpa_printf(MSG_INFO, + "NAN: Sync discovery is not supported for P2P"); + return -1; + } + + if (params->proximity_ranging) { + wpa_printf(MSG_INFO, + "NAN: Sync discovery is not supported for PR"); + return -1; + } + } +#endif /* CONFIG_NAN */ + + if (p2p) { + elems = wpas_p2p_usd_elems(wpa_s, service_name); + addr = wpa_s->global->p2p_dev_addr; + } else if (params->proximity_ranging) { + const u8 *src = params->forced_addr ? + params->forced_addr : wpa_s->own_addr; + + elems = wpas_pr_usd_elems(wpa_s, src); + } + + if (params->forced_addr) { + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA)) { + wpa_printf(MSG_INFO, "NAN: Random TA not allowed"); + return -1; + } + addr = params->forced_addr; + } + + publish_id = nan_de_publish(wpa_s->nan_de, service_name, srv_proto_type, + ssi, elems, params, p2p, addr); + if (publish_id >= 1 && !params->sync && + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) && + wpas_drv_nan_publish(wpa_s, addr, publish_id, service_name, + nan_de_get_service_id(wpa_s->nan_de, + publish_id), + srv_proto_type, ssi, elems, params, + p2p ? p2p_network_id : nan_network_id) < 0) { + nan_de_cancel_publish(wpa_s->nan_de, publish_id); + publish_id = -1; + } +#ifdef CONFIG_AP + if (publish_id >= 1 && wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) { + wpa_printf(MSG_DEBUG, "NAN: Linking nan_de for AP interface"); + wpa_s->ap_iface->bss[0]->nan_de = wpa_s->nan_de; + } +#endif /* CONFIG_AP */ + + wpabuf_free(elems); + return publish_id; +} + + +void wpas_nan_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id) +{ + if (!wpa_s->nan_de) + return; + nan_de_cancel_publish(wpa_s->nan_de, publish_id); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + wpas_drv_nan_cancel_publish(wpa_s, publish_id); +} + + +int wpas_nan_update_publish(struct wpa_supplicant *wpa_s, int publish_id, + const struct wpabuf *ssi) +{ + int ret; + + if (!wpa_s->nan_de) + return -1; + ret = nan_de_update_publish(wpa_s->nan_de, publish_id, ssi); + if (ret == 0 && (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) && + wpas_drv_nan_update_publish(wpa_s, publish_id, ssi) < 0) + return -1; + return ret; +} + + +int wpas_nan_usd_unpause_publish(struct wpa_supplicant *wpa_s, int publish_id, + u8 peer_instance_id, const u8 *peer_addr) +{ + if (!wpa_s->nan_de) + return -1; + return nan_de_unpause_publish(wpa_s->nan_de, publish_id, + peer_instance_id, peer_addr); +} + + +static int wpas_nan_stop_listen(struct wpa_supplicant *wpa_s, int id) +{ + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + return 0; + + if (nan_de_stop_listen(wpa_s->nan_de, id) < 0) + return -1; + + if (wpa_s->nan_usd_listen_work) { + wpa_printf(MSG_DEBUG, "NAN: Stop listen operation"); + wpa_drv_cancel_remain_on_channel(wpa_s); + wpas_nan_usd_listen_work_done(wpa_s); + } + + if (wpa_s->nan_usd_tx_work) { + wpa_printf(MSG_DEBUG, "NAN: Stop TX wait operation"); + offchannel_send_action_done(wpa_s); + wpas_nan_usd_tx_work_done(wpa_s); + } + + return 0; +} + + +int wpas_nan_usd_publish_stop_listen(struct wpa_supplicant *wpa_s, + int publish_id) +{ + if (!wpa_s->nan_de) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: Request to stop listen for publish_id=%d", + publish_id); + return wpas_nan_stop_listen(wpa_s, publish_id); +} + + +int wpas_nan_subscribe(struct wpa_supplicant *wpa_s, + const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + struct nan_subscribe_params *params, bool p2p) +{ + int subscribe_id; + struct wpabuf *elems = NULL; + const u8 *addr; + + if (!wpa_s->nan_de) + return -1; + + if (params->proximity_ranging && !params->active) { + wpa_printf(MSG_INFO, + "PR passive subscriber service discovery not allowed"); + return -1; + } + + addr = wpa_s->own_addr; + +#ifdef CONFIG_NAN + if (params->sync) { + if (!(wpa_s->nan_capa.drv_flags & + WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE)) { + wpa_printf(MSG_INFO, + "NAN: Cannot subscribe sync, user space DE is not supported"); + return -1; + } + + if (!wpas_nan_ready(wpa_s)) { + wpa_printf(MSG_INFO, "NAN: Not ready (subscribe)"); + return -1; + } + + if (p2p) { + wpa_printf(MSG_INFO, + "NAN: Sync discovery is not supported for P2P (subscribe)"); + return -1; + } + + if (params->proximity_ranging) { + wpa_printf(MSG_INFO, + "NAN: Sync discovery is not supported for PR (subscribe)"); + return -1; + } + } +#endif /* CONFIG_NAN */ + + if (p2p) { + elems = wpas_p2p_usd_elems(wpa_s, service_name); + addr = wpa_s->global->p2p_dev_addr; + } else if (params->proximity_ranging) { + const u8 *src = params->forced_addr ? + params->forced_addr : wpa_s->own_addr; + + elems = wpas_pr_usd_elems(wpa_s, src); + } + + if (params->forced_addr) { + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA)) { + wpa_printf(MSG_INFO, "NAN: Random TA not allowed"); + return -1; + } + addr = params->forced_addr; + } + + subscribe_id = nan_de_subscribe(wpa_s->nan_de, service_name, + srv_proto_type, ssi, elems, params, + p2p, addr); + if (subscribe_id >= 1 && !params->sync && + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) && + wpas_drv_nan_subscribe(wpa_s, addr, subscribe_id, service_name, + nan_de_get_service_id(wpa_s->nan_de, + subscribe_id), + srv_proto_type, ssi, elems, params, + p2p ? p2p_network_id : nan_network_id) < 0) { + nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id); + subscribe_id = -1; + } +#ifdef CONFIG_AP + if (subscribe_id >= 1 && wpa_s->ap_iface && wpa_s->ap_iface->bss[0]) { + wpa_printf(MSG_DEBUG, "NAN: Linking nan_de for AP interface"); + wpa_s->ap_iface->bss[0]->nan_de = wpa_s->nan_de; + } +#endif /* CONFIG_AP */ + + wpabuf_free(elems); + return subscribe_id; +} + + +void wpas_nan_cancel_subscribe(struct wpa_supplicant *wpa_s, + int subscribe_id) +{ + if (!wpa_s->nan_de) + return; + nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_USD_OFFLOAD) + wpas_drv_nan_cancel_subscribe(wpa_s, subscribe_id); +} + + +int wpas_nan_usd_subscribe_stop_listen(struct wpa_supplicant *wpa_s, + int subscribe_id) +{ + if (!wpa_s->nan_de) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: Request to stop listen for subscribe_id=%d", + subscribe_id); + return wpas_nan_stop_listen(wpa_s, subscribe_id); +} + + +int wpas_nan_transmit(struct wpa_supplicant *wpa_s, int handle, + const struct wpabuf *ssi, const struct wpabuf *elems, + const u8 *peer_addr, u8 req_instance_id, u32 *cookie) +{ + if (!wpa_s->nan_de) + return -1; + return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr, + req_instance_id, NULL, cookie); +} + + +void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + wpas_nan_usd_listen_work_done(wpa_s); + + if (wpa_s->nan_de) + nan_de_listen_started(wpa_s->nan_de, freq, duration); +} + + +void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + if (wpa_s->nan_de) + nan_de_listen_ended(wpa_s->nan_de, freq); +} + + +void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s) +{ + wpas_nan_usd_tx_work_done(wpa_s); + + if (wpa_s->nan_de) + nan_de_tx_wait_ended(wpa_s->nan_de); +} + + +int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s) +{ + int i, j; + int *freqs = NULL; + + if (!wpa_s->hw.modes) + return NULL; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &mode->channels[j]; + + /* All 20 MHz channels on 2.4 and 5 GHz band */ + if (chan->freq < 2412 || chan->freq > 5900) + continue; + + /* that allow frames to be transmitted */ + if (chan->flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + + int_array_add_unique(&freqs, chan->freq); + } + } + + return freqs; +} + + +void wpas_nan_usd_state_change_notif(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *ifs; + unsigned int n_active = 0; + struct nan_de_cfg cfg; + + if (!wpa_s->radio) + return; + + os_memset(&cfg, 0, sizeof(cfg)); + + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs->wpa_state >= WPA_AUTHENTICATING) + n_active++; + } + + wpa_printf(MSG_DEBUG, + "NAN: state change notif: n_active=%u, p2p_in_progress=%u", + n_active, wpas_p2p_in_progress(wpa_s)); + + if (n_active) { + cfg.n_max = 3; + + if (!wpas_p2p_in_progress(wpa_s)) { + /* Limit the USD operation on channel to 100 - 300 TUs + * to allow more time for other interfaces. + */ + cfg.n_min = 1; + } else { + /* Limit the USD operation on channel to 200 - 300 TUs + * to allow P2P operation to complete. + */ + cfg.n_min = 2; + } + + /* Each 500 ms suspend USD operation for 300 ms */ + cfg.cycle = 500; + cfg.suspend = 300; + } + + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs->nan_de) + nan_de_config(ifs->nan_de, &cfg); + } +} + + +#ifdef CONFIG_NAN +static struct wpa_supplicant * +wpas_nan_get_mgmt_iface(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *nmi_wpa_s; + + for (nmi_wpa_s = wpa_s->global->ifaces; nmi_wpa_s; + nmi_wpa_s = nmi_wpa_s->next) { + if (nmi_wpa_s->nan_mgmt) + return nmi_wpa_s; + } + + return wpa_s; +} +#endif /* CONFIG_NAN */ + + +int wpas_nan_tx_status(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len, int acked) +{ +#ifdef CONFIG_NAN + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + + wpa_s = wpas_nan_get_mgmt_iface(wpa_s); + + if (wpa_s->nan_de) + nan_de_tx_status(wpa_s->nan_de, 0, mgmt->da, data, data_len, + acked); + + if (!wpas_nan_ndp_allowed(wpa_s)) + return -1; + + wpa_printf(MSG_DEBUG, "NAN: TX status for frame len=%zu acked=%u", + data_len, acked); + + if (!nan_tx_status(wpa_s->nan, mgmt->da, data, data_len, acked)) { + wpa_printf(MSG_DEBUG, "NAN: Processed NAF TX status"); + return 0; + } +#endif /* CONFIG_NAN */ + + return -1; +} + + +#ifdef CONFIG_NAN +void wpas_nan_rx_naf(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + if (mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) { + wpa_printf(MSG_DEBUG, "NAN: RX NAF: ifname=%s: protected", + wpa_s->ifname); + + wpa_s = wpas_nan_get_mgmt_iface(wpa_s); + + wpa_printf(MSG_DEBUG, "NAN: RX NAF: Continue processing on %s", + wpa_s->ifname); + } + + if (!wpas_nan_ndp_allowed(wpa_s)) + return; + + nan_action_rx(wpa_s->nan, mgmt, len); +} + + +void wpas_nan_data_interface_removed(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *nan_dev_wpas = wpas_nan_get_mgmt_iface(wpa_s); + + wpa_printf(MSG_DEBUG, + "NAN: Data interface removed (%s) - terminate NDPs on " + MACSTR, wpa_s->ifname, MAC2STR(wpa_s->own_addr)); + + if (nan_dev_wpas) + nan_terminate_ndi_ndps(nan_dev_wpas->nan, wpa_s->own_addr); +} +#endif /* CONFIG_NAN */ diff --git a/contrib/wpa/wpa_supplicant/nan_supplicant.h b/contrib/wpa/wpa_supplicant/nan_supplicant.h new file mode 100644 index 000000000000..b257db085ee2 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/nan_supplicant.h @@ -0,0 +1,252 @@ +/* + * wpa_supplicant - NAN + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * Copyright (C) 2025 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NAN_SUPPLICANT_H +#define NAN_SUPPLICANT_H + +/* NAN synchronization only */ +#ifdef CONFIG_NAN + +int wpas_nan_init(struct wpa_supplicant *wpa_s); +void wpas_nan_deinit(struct wpa_supplicant *wpa_s); +int wpas_nan_start(struct wpa_supplicant *wpa_s); +int wpas_nan_set(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_nan_update_conf(struct wpa_supplicant *wpa_s); +int wpas_nan_stop(struct wpa_supplicant *wpa_s); +void wpas_nan_flush(struct wpa_supplicant *wpa_s); +void wpas_nan_cluster_join(struct wpa_supplicant *wpa_s, + const u8 *cluster_id, + bool new_cluster); +void wpas_nan_next_dw(struct wpa_supplicant *wpa_s, u32 freq); +void wpas_nan_sched_update_done(struct wpa_supplicant *wpa_s, + const union wpa_event_data *data); +void wpas_nan_ulw_update(struct wpa_supplicant *wpa_s, + const u8 *ulw, size_t ulw_len); +void wpas_nan_chan_evacuation(struct wpa_supplicant *wpa_s, + const struct nan_chan_evacuation_info *info); +int wpas_nan_sched_config_map(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_nan_ndp_request(struct wpa_supplicant *wpa_s, char *cmd); +void wpas_nan_rx_naf(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); +int wpas_nan_ndp_response(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_nan_ndp_terminate(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_nan_peer_info(struct wpa_supplicant *wpa_s, const char *cmd, + char *reply, size_t reply_size); +int wpas_nan_status(struct wpa_supplicant *wpa_s, char *reply, + size_t reply_size); +int wpas_nan_bootstrap_request(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_nan_bootstrap_reset(struct wpa_supplicant *wpa_s, char *cmd); +bool wpas_nan_is_peer_paired(struct wpa_supplicant *wpa_s, const u8 *peer_addr); +void wpas_nan_data_interface_removed(struct wpa_supplicant *wpa_s); + +int wpas_nan_pair(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + u8 auth_mode, int cipher, int handle, u8 peer_instance_id, + bool responder, const char *password); +int wpas_nan_pairing_start(struct wpa_supplicant *wpa_s, char *cmd); +int wpas_nan_pairing_abort(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_nan_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked); +int wpas_nan_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); + +#else /* CONFIG_NAN */ + +static inline int wpas_nan_init(struct wpa_supplicant *wpa_s) +{ + return -1; +} + +static inline void wpas_nan_deinit(struct wpa_supplicant *wpa_s) +{} + +static inline int wpas_nan_start(struct wpa_supplicant *wpa_s) +{ + return -1; +} + +static inline int wpas_nan_set(struct wpa_supplicant *wpa_s, char *cmd) +{ + return -1; +} + +static inline int wpas_nan_update_conf(struct wpa_supplicant *wpa_s) +{ + return -1; +} + +static inline int wpas_nan_stop(struct wpa_supplicant *wpa_s) +{ + return -1; +} + +static inline void wpas_nan_flush(struct wpa_supplicant *wpa_s) +{} + +static inline void wpas_nan_cluster_join(struct wpa_supplicant *wpa_s, + const u8 *cluster_id, + bool new_cluster) +{} + +static inline void wpas_nan_next_dw(struct wpa_supplicant *wpa_s, u32 freq) +{} + +static inline void wpas_nan_sched_update_done(struct wpa_supplicant *wpa_s, + const union wpa_event_data *data) +{} + +static inline void wpas_nan_ulw_update(struct wpa_supplicant *wpa_s, + const u8 *ulw, size_t ulw_len) +{} + +static inline void wpas_nan_rx_naf(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, + size_t len) +{} + +static inline bool wpas_nan_is_peer_paired(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + return false; +} + +static inline void +wpas_nan_chan_evacuation(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{} + +#endif /* CONFIG_NAN */ + +struct nan_subscribe_params; +struct nan_publish_params; +enum nan_service_protocol_type; + +/* NAN sync and USD common */ +#if defined(CONFIG_NAN_USD) || defined(CONFIG_NAN) + +int wpas_nan_de_init(struct wpa_supplicant *wpa_s); +void wpas_nan_de_deinit(struct wpa_supplicant *wpa_s); +void wpas_nan_de_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *a3, unsigned int freq, + const u8 *buf, size_t len, int rssi); +void wpas_nan_de_flush(struct wpa_supplicant *wpa_s); +int wpas_nan_publish(struct wpa_supplicant *wpa_s, const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + struct nan_publish_params *params, bool p2p); +void wpas_nan_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id); +int wpas_nan_update_publish(struct wpa_supplicant *wpa_s, int publish_id, + const struct wpabuf *ssi); +int wpas_nan_subscribe(struct wpa_supplicant *wpa_s, + const char *service_name, + enum nan_service_protocol_type srv_proto_type, + const struct wpabuf *ssi, + struct nan_subscribe_params *params, bool p2p); +void wpas_nan_cancel_subscribe(struct wpa_supplicant *wpa_s, + int subscribe_id); +int wpas_nan_transmit(struct wpa_supplicant *wpa_s, int handle, + const struct wpabuf *ssi, const struct wpabuf *elems, + const u8 *peer_addr, u8 req_instance_id, u32 *cookie); +int wpas_nan_tx_status(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len, int acked); + +#else /* CONFIG_NAN_USD || CONFIG_NAN */ + +static inline int wpas_nan_de_init(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_nan_de_deinit(struct wpa_supplicant *wpa_s) +{} + +static inline +void wpas_nan_de_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *a3, unsigned int freq, + const u8 *buf, size_t len, int rssi) +{} + +static inline void wpas_nan_de_flush(struct wpa_supplicant *wpa_s) +{} + +static inline int wpas_nan_tx_status(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len, + u8 acked) +{ + return -1; +} + +#endif /* CONFIG_NAN_USD || CONFIG_NAN */ + +/* NAN USD only */ +#ifdef CONFIG_NAN_USD + +void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, + unsigned int duration); +void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s); +int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s); +int wpas_nan_usd_unpause_publish(struct wpa_supplicant *wpa_s, int publish_id, + u8 peer_instance_id, const u8 *peer_addr); +int wpas_nan_usd_publish_stop_listen(struct wpa_supplicant *wpa_s, + int publish_id); +int wpas_nan_usd_subscribe_stop_listen(struct wpa_supplicant *wpa_s, + int subscribe_id); +void wpas_nan_usd_state_change_notif(struct wpa_supplicant *wpa_s); + +#else /* CONFIG_NAN_USD */ + +static inline +void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, + unsigned int duration) +{} + +static inline +void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{} + +static inline void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s) +{} + +static inline +int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s) +{ + return NULL; +} + +static inline +int wpas_nan_usd_unpause_publish(struct wpa_supplicant *wpa_s, int publish_id, + u8 peer_instance_id, const u8 *peer_addr) +{ + return -1; +} + +static inline +int wpas_nan_usd_publish_stop_listen(struct wpa_supplicant *wpa_s, + int publish_id) +{ + return -1; +} + +static inline +int wpas_nan_usd_subscribe_stop_listen(struct wpa_supplicant *wpa_s, + int subscribe_id) +{ + return -1; +} + +static inline void wpas_nan_usd_state_change_notif(struct wpa_supplicant *wpa_s) +{} + +#endif /* CONFIG_NAN_USD */ + +#endif /* NAN_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/nan_usd.c b/contrib/wpa/wpa_supplicant/nan_usd.c deleted file mode 100644 index 657b302c11e2..000000000000 --- a/contrib/wpa/wpa_supplicant/nan_usd.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * NAN unsynchronized service discovery (USD) - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. - * - * 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/nan_de.h" -#include "wpa_supplicant_i.h" -#include "offchannel.h" -#include "driver_i.h" -#include "nan_usd.h" - - -static const char * -tx_status_result_txt(enum offchannel_send_action_result result) -{ - switch (result) { - case OFFCHANNEL_SEND_ACTION_SUCCESS: - return "success"; - case OFFCHANNEL_SEND_ACTION_NO_ACK: - return "no-ack"; - case OFFCHANNEL_SEND_ACTION_FAILED: - return "failed"; - } - - return "?"; -} - - -static void wpas_nan_de_tx_status(struct wpa_supplicant *wpa_s, - unsigned int freq, const u8 *dst, - const u8 *src, const u8 *bssid, - const u8 *data, size_t data_len, - enum offchannel_send_action_result result) -{ - if (!wpa_s->nan_de) - return; - - wpa_printf(MSG_DEBUG, "NAN: TX status A1=" MACSTR " A2=" MACSTR - " A3=" MACSTR " freq=%d len=%zu result=%s", - MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq, - data_len, tx_status_result_txt(result)); - - nan_de_tx_status(wpa_s->nan_de, freq, dst); -} - - -struct wpas_nan_usd_tx_work { - unsigned int freq; - unsigned int wait_time; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - struct wpabuf *buf; -}; - - -static void wpas_nan_usd_tx_work_free(struct wpas_nan_usd_tx_work *twork) -{ - if (!twork) - return; - wpabuf_free(twork->buf); - os_free(twork); -} - - -static void wpas_nan_usd_tx_work_done(struct wpa_supplicant *wpa_s) -{ - struct wpas_nan_usd_tx_work *twork; - - if (!wpa_s->nan_usd_tx_work) - return; - - twork = wpa_s->nan_usd_tx_work->ctx; - wpas_nan_usd_tx_work_free(twork); - radio_work_done(wpa_s->nan_usd_tx_work); - wpa_s->nan_usd_tx_work = NULL; -} - - -static int wpas_nan_de_tx_send(struct wpa_supplicant *wpa_s, unsigned int freq, - unsigned int wait_time, const u8 *dst, - const u8 *src, const u8 *bssid, - const struct wpabuf *buf) -{ - wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR - " A3=" MACSTR " freq=%d len=%zu", - MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), freq, - wpabuf_len(buf)); - - return offchannel_send_action(wpa_s, freq, dst, src, bssid, - wpabuf_head(buf), wpabuf_len(buf), - wait_time, wpas_nan_de_tx_status, 1); -} - - -static void wpas_nan_usd_start_tx_cb(struct wpa_radio_work *work, int deinit) -{ - struct wpa_supplicant *wpa_s = work->wpa_s; - struct wpas_nan_usd_tx_work *twork = work->ctx; - - if (deinit) { - if (work->started) { - wpa_s->nan_usd_tx_work = NULL; - offchannel_send_action_done(wpa_s); - } - wpas_nan_usd_tx_work_free(twork); - return; - } - - wpa_s->nan_usd_tx_work = work; - - if (wpas_nan_de_tx_send(wpa_s, twork->freq, twork->wait_time, - twork->dst, twork->src, twork->bssid, - twork->buf) < 0) - wpas_nan_usd_tx_work_done(wpa_s); -} - - -static int wpas_nan_de_tx(void *ctx, unsigned int freq, unsigned int wait_time, - const u8 *dst, const u8 *src, const u8 *bssid, - const struct wpabuf *buf) -{ - struct wpa_supplicant *wpa_s = ctx; - struct wpas_nan_usd_tx_work *twork; - - if (wpa_s->nan_usd_tx_work || wpa_s->nan_usd_listen_work) { - /* Reuse ongoing radio work */ - return wpas_nan_de_tx_send(wpa_s, freq, wait_time, dst, src, - bssid, buf); - } - - twork = os_zalloc(sizeof(*twork)); - if (!twork) - return -1; - twork->freq = freq; - twork->wait_time = wait_time; - os_memcpy(twork->dst, dst, ETH_ALEN); - os_memcpy(twork->src, src, ETH_ALEN); - os_memcpy(twork->bssid, bssid, ETH_ALEN); - twork->buf = wpabuf_dup(buf); - if (!twork->buf) { - wpas_nan_usd_tx_work_free(twork); - return -1; - } - - if (radio_add_work(wpa_s, freq, "nan-usd-tx", 0, - wpas_nan_usd_start_tx_cb, twork) < 0) { - wpas_nan_usd_tx_work_free(twork); - return -1; - } - - return 0; -} - - -struct wpas_nan_usd_listen_work { - unsigned int freq; - unsigned int duration; -}; - - -static void wpas_nan_usd_listen_work_done(struct wpa_supplicant *wpa_s) -{ - struct wpas_nan_usd_listen_work *lwork; - - if (!wpa_s->nan_usd_listen_work) - return; - - lwork = wpa_s->nan_usd_listen_work->ctx; - os_free(lwork); - radio_work_done(wpa_s->nan_usd_listen_work); - wpa_s->nan_usd_listen_work = NULL; -} - - -static void wpas_nan_usd_start_listen_cb(struct wpa_radio_work *work, - int deinit) -{ - struct wpa_supplicant *wpa_s = work->wpa_s; - struct wpas_nan_usd_listen_work *lwork = work->ctx; - unsigned int duration; - - if (deinit) { - if (work->started) { - wpa_s->nan_usd_listen_work = NULL; - wpa_drv_cancel_remain_on_channel(wpa_s); - } - os_free(lwork); - return; - } - - wpa_s->nan_usd_listen_work = work; - - duration = lwork->duration; - if (duration > wpa_s->max_remain_on_chan) - duration = wpa_s->max_remain_on_chan; - wpa_printf(MSG_DEBUG, "NAN: Start listen on %u MHz for %u ms", - lwork->freq, duration); - if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) { - wpa_printf(MSG_DEBUG, - "NAN: Failed to request the driver to remain on channel (%u MHz) for listen", - lwork->freq); - wpas_nan_usd_listen_work_done(wpa_s); - return; - } -} - - -static int wpas_nan_de_listen(void *ctx, unsigned int freq, - unsigned int duration) -{ - struct wpa_supplicant *wpa_s = ctx; - struct wpas_nan_usd_listen_work *lwork; - - lwork = os_zalloc(sizeof(*lwork)); - if (!lwork) - return -1; - lwork->freq = freq; - lwork->duration = duration; - - if (radio_add_work(wpa_s, freq, "nan-usd-listen", 0, - wpas_nan_usd_start_listen_cb, lwork) < 0) { - os_free(lwork); - return -1; - } - - return 0; -} - - -static void -wpas_nan_de_discovery_result(void *ctx, int subscribe_id, - enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len, int peer_publish_id, - const u8 *peer_addr, bool fsd, bool fsd_gas) -{ - struct wpa_supplicant *wpa_s = ctx; - char *ssi_hex; - - ssi_hex = os_zalloc(2 * ssi_len + 1); - if (!ssi_hex) - return; - if (ssi) - wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); - wpa_msg(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT - "subscribe_id=%d publish_id=%d address=" MACSTR - " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s", - subscribe_id, peer_publish_id, MAC2STR(peer_addr), - fsd, fsd_gas, srv_proto_type, ssi_hex); - os_free(ssi_hex); -} - - -static void wpas_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr, - int peer_subscribe_id, - enum nan_service_protocol_type srv_proto_type, - const u8 *ssi, size_t ssi_len) -{ - struct wpa_supplicant *wpa_s = ctx; - char *ssi_hex; - - ssi_hex = os_zalloc(2 * ssi_len + 1); - if (!ssi_hex) - return; - if (ssi) - wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); - wpa_msg(wpa_s, MSG_INFO, NAN_REPLIED - "publish_id=%d address=" MACSTR - " subscribe_id=%d srv_proto_type=%u ssi=%s", - publish_id, MAC2STR(peer_addr), peer_subscribe_id, - srv_proto_type, ssi_hex); - os_free(ssi_hex); -} - - -static const char * nan_reason_txt(enum nan_de_reason reason) -{ - switch (reason) { - case NAN_DE_REASON_TIMEOUT: - return "timeout"; - case NAN_DE_REASON_USER_REQUEST: - return "user-request"; - case NAN_DE_REASON_FAILURE: - return "failure"; - } - - return "unknown"; -} - - -static void wpas_nan_de_publish_terminated(void *ctx, int publish_id, - enum nan_de_reason reason) -{ - struct wpa_supplicant *wpa_s = ctx; - - wpa_msg(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED - "publish_id=%d reason=%s", - publish_id, nan_reason_txt(reason)); -} - - -static void wpas_nan_de_subscribe_terminated(void *ctx, int subscribe_id, - enum nan_de_reason reason) -{ - struct wpa_supplicant *wpa_s = ctx; - - wpa_msg(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED - "subscribe_id=%d reason=%s", - subscribe_id, nan_reason_txt(reason)); -} - - -static void wpas_nan_de_receive(void *ctx, int id, int peer_instance_id, - const u8 *ssi, size_t ssi_len, - const u8 *peer_addr) -{ - struct wpa_supplicant *wpa_s = ctx; - char *ssi_hex; - - ssi_hex = os_zalloc(2 * ssi_len + 1); - if (!ssi_hex) - return; - if (ssi) - wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); - wpa_msg(wpa_s, MSG_INFO, NAN_RECEIVE - "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s", - id, peer_instance_id, MAC2STR(peer_addr), ssi_hex); - os_free(ssi_hex); -} - - -int wpas_nan_usd_init(struct wpa_supplicant *wpa_s) -{ - struct nan_callbacks cb; - - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = wpa_s; - cb.tx = wpas_nan_de_tx; - cb.listen = wpas_nan_de_listen; - cb.discovery_result = wpas_nan_de_discovery_result; - cb.replied = wpas_nan_de_replied; - cb.publish_terminated = wpas_nan_de_publish_terminated; - cb.subscribe_terminated = wpas_nan_de_subscribe_terminated; - cb.receive = wpas_nan_de_receive; - - wpa_s->nan_de = nan_de_init(wpa_s->own_addr, false, &cb); - if (!wpa_s->nan_de) - return -1; - return 0; -} - - -void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s) -{ - nan_de_deinit(wpa_s->nan_de); - wpa_s->nan_de = NULL; -} - - -void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src, - unsigned int freq, const u8 *buf, size_t len) -{ - if (!wpa_s->nan_de) - return; - nan_de_rx_sdf(wpa_s->nan_de, src, freq, buf, len); -} - - -void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s) -{ - if (!wpa_s->nan_de) - return; - nan_de_flush(wpa_s->nan_de); -} - - -int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, const char *service_name, - enum nan_service_protocol_type srv_proto_type, - const struct wpabuf *ssi, - struct nan_publish_params *params) -{ - int publish_id; - struct wpabuf *elems = NULL; - - if (!wpa_s->nan_de) - return -1; - - publish_id = nan_de_publish(wpa_s->nan_de, service_name, srv_proto_type, - ssi, elems, params); - wpabuf_free(elems); - return publish_id; -} - - -void wpas_nan_usd_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id) -{ - if (!wpa_s->nan_de) - return; - nan_de_cancel_publish(wpa_s->nan_de, publish_id); -} - - -int wpas_nan_usd_update_publish(struct wpa_supplicant *wpa_s, int publish_id, - const struct wpabuf *ssi) -{ - if (!wpa_s->nan_de) - return -1; - return nan_de_update_publish(wpa_s->nan_de, publish_id, ssi); -} - - -int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s, - const char *service_name, - enum nan_service_protocol_type srv_proto_type, - const struct wpabuf *ssi, - struct nan_subscribe_params *params) -{ - int subscribe_id; - struct wpabuf *elems = NULL; - - if (!wpa_s->nan_de) - return -1; - - subscribe_id = nan_de_subscribe(wpa_s->nan_de, service_name, - srv_proto_type, ssi, elems, params); - wpabuf_free(elems); - return subscribe_id; -} - - -void wpas_nan_usd_cancel_subscribe(struct wpa_supplicant *wpa_s, - int subscribe_id) -{ - if (!wpa_s->nan_de) - return; - nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id); -} - - -int wpas_nan_usd_transmit(struct wpa_supplicant *wpa_s, int handle, - const struct wpabuf *ssi, const struct wpabuf *elems, - const u8 *peer_addr, u8 req_instance_id) -{ - if (!wpa_s->nan_de) - return -1; - return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr, - req_instance_id); -} - - -void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s, - unsigned int freq, unsigned int duration) -{ - wpas_nan_usd_listen_work_done(wpa_s); - - if (wpa_s->nan_de) - nan_de_listen_started(wpa_s->nan_de, freq, duration); -} - - -void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, - unsigned int freq) -{ - if (wpa_s->nan_de) - nan_de_listen_ended(wpa_s->nan_de, freq); -} - - -void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s) -{ - wpas_nan_usd_tx_work_done(wpa_s); - - if (wpa_s->nan_de) - nan_de_tx_wait_ended(wpa_s->nan_de); -} - - -int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s) -{ - int i, j; - int *freqs = NULL; - - if (!wpa_s->hw.modes) - return NULL; - - for (i = 0; i < wpa_s->hw.num_modes; i++) { - struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; - - for (j = 0; j < mode->num_channels; j++) { - struct hostapd_channel_data *chan = &mode->channels[j]; - - /* All 20 MHz channels on 2.4 and 5 GHz band */ - if (chan->freq < 2412 || chan->freq > 5900) - continue; - - /* that allow frames to be transmitted */ - if (chan->flag & (HOSTAPD_CHAN_DISABLED | - HOSTAPD_CHAN_NO_IR | - HOSTAPD_CHAN_RADAR)) - continue; - - int_array_add_unique(&freqs, chan->freq); - } - } - - return freqs; -} diff --git a/contrib/wpa/wpa_supplicant/nan_usd.h b/contrib/wpa/wpa_supplicant/nan_usd.h deleted file mode 100644 index 149ac9e60adf..000000000000 --- a/contrib/wpa/wpa_supplicant/nan_usd.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * NAN unsynchronized service discovery (USD) - * Copyright (c) 2024, Qualcomm Innovation Center, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef NAN_USD_H -#define NAN_USD_H - -struct nan_subscribe_params; -struct nan_publish_params; -enum nan_service_protocol_type; - -int wpas_nan_usd_init(struct wpa_supplicant *wpa_s); -void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s); -void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src, - unsigned int freq, const u8 *buf, size_t len); -void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s); -int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, const char *service_name, - enum nan_service_protocol_type srv_proto_type, - const struct wpabuf *ssi, - struct nan_publish_params *params); -void wpas_nan_usd_cancel_publish(struct wpa_supplicant *wpa_s, int publish_id); -int wpas_nan_usd_update_publish(struct wpa_supplicant *wpa_s, int publish_id, - const struct wpabuf *ssi); -int wpas_nan_usd_subscribe(struct wpa_supplicant *wpa_s, - const char *service_name, - enum nan_service_protocol_type srv_proto_type, - const struct wpabuf *ssi, - struct nan_subscribe_params *params); -void wpas_nan_usd_cancel_subscribe(struct wpa_supplicant *wpa_s, - int subscribe_id); -int wpas_nan_usd_transmit(struct wpa_supplicant *wpa_s, int handle, - const struct wpabuf *ssi, const struct wpabuf *elems, - const u8 *peer_addr, u8 req_instance_id); -void wpas_nan_usd_remain_on_channel_cb(struct wpa_supplicant *wpa_s, - unsigned int freq, - unsigned int duration); -void wpas_nan_usd_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, - unsigned int freq); -void wpas_nan_usd_tx_wait_expire(struct wpa_supplicant *wpa_s); -int * wpas_nan_usd_all_freqs(struct wpa_supplicant *wpa_s); - -#endif /* NAN_USD_H */ diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index e967dc8ff13a..e0a6d8262a60 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -10,6 +10,8 @@ #include "utils/common.h" #include "common/wpa_ctrl.h" +#include "common/nan_de.h" +#include "common/proximity_ranging.h" #include "config.h" #include "wpa_supplicant_i.h" #include "wps_supplicant.h" @@ -87,6 +89,8 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + if (wpa_s->p2p_mgmt) return; @@ -103,10 +107,14 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_FST */ - if (new_state == WPA_COMPLETED) + if (new_state == WPA_COMPLETED) { wpas_p2p_notif_connected(wpa_s); - else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) + if (ssid && !wpa_s->sta_roaming_disabled) + wpa_drv_roaming(wpa_s, !ssid->bssid_set, + ssid->bssid_set ? ssid->bssid : NULL); + } else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) { wpas_p2p_notif_disconnected(wpa_s); + } sme_state_changed(wpa_s); @@ -168,6 +176,16 @@ void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s) } +void wpas_notify_scan_in_progress_6ghz(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, + WPAS_DBUS_PROP_SCAN_IN_PROGRESS_6GHZ); +} + + void wpas_notify_session_length(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_mgmt) @@ -782,6 +800,20 @@ void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, id, op_freq); } +void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 bootstrap_method) +{ + wpas_dbus_signal_p2p_bootstrap_req(wpa_s, src, bootstrap_method); +} + +void wpas_notify_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s, + const u8 *src, int status, + u16 bootstrap_method) +{ + wpas_dbus_signal_p2p_bootstrap_rsp(wpa_s, src, status, + bootstrap_method); +} + #endif /* CONFIG_P2P */ @@ -912,6 +944,12 @@ void wpas_notify_psk_mismatch(struct wpa_supplicant *wpa_s) } +void wpas_notify_sae_password_mismatch(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_sae_password_mismatch(wpa_s); +} + + void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -1058,3 +1096,552 @@ void wpas_notify_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s, wpas_dbus_signal_hs20_t_c_acceptance(wpa_s, url); } #endif /* CONFIG_HS20 */ + + +#if defined(CONFIG_NAN) || defined(CONFIG_NAN_USD) + +void wpas_notify_nan_discovery_result(struct wpa_supplicant *wpa_s, + struct nan_discovery_result *res) +{ + char *ssi_hex, *pmkid_hex = NULL; + char *cipher_suites_str = NULL; + + ssi_hex = os_zalloc(2 * res->ssi_len + 1); + if (!ssi_hex) + return; + if (res->ssi) + wpa_snprintf_hex(ssi_hex, 2 * res->ssi_len + 1, + res->ssi, res->ssi_len); + + if (res->pmkid_list && res->pmkid_count > 0) { + const size_t pmkid_hex_len = 2 * PMKID_LEN + 1; + unsigned int i; + + pmkid_hex = os_zalloc(res->pmkid_count * pmkid_hex_len); + if (pmkid_hex) { + for (i = 0; i < res->pmkid_count; i++) { + char *pos = &pmkid_hex[i * pmkid_hex_len]; + + wpa_snprintf_hex( + pos, pmkid_hex_len, + &res->pmkid_list[i * PMKID_LEN], + PMKID_LEN); + if (i < res->pmkid_count - 1) + pos[2 * PMKID_LEN] = ','; + } + } + } + + if (res->cipher_suites && res->n_cipher_suites > 0) { + char *pos; + unsigned int i; + + /* Allocate enough space for trailing space after each cipher */ + cipher_suites_str = os_zalloc(res->n_cipher_suites * 2); + if (!cipher_suites_str) + goto err; + + pos = cipher_suites_str; + + for (i = 0; i < res->n_cipher_suites; i++) { + int ret; + size_t left = res->n_cipher_suites * 2 - + (pos - cipher_suites_str); + + ret = os_snprintf(pos, left, "%s%u", i > 0 ? "," : "", + res->cipher_suites[i]); + if (os_snprintf_error(left, ret)) + break; + pos += ret; + } + } +err: + + wpa_msg_global(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT + "subscribe_id=%d publish_id=%d address=" MACSTR + " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s%s%s%s%s pairing_setup_supp=%d npk_nik_caching_supp=%d pbm=0x%04x data_path=%d", + res->subscribe_id, res->peer_publish_id, + MAC2STR(res->peer_addr), + res->fsd, res->fsd_gas, res->srv_proto_type, ssi_hex, + pmkid_hex ? " pmkid=" : "", + pmkid_hex ? pmkid_hex : "", + cipher_suites_str ? " cipher_suites=" : "", + cipher_suites_str ? cipher_suites_str : "", + res->pairing_setup_supp, res->npk_nik_caching_supp, + res->pbm, res->data_path); + os_free(ssi_hex); + os_free(pmkid_hex); + os_free(cipher_suites_str); + + wpas_dbus_signal_nan_discovery_result(wpa_s, + res->srv_proto_type, + res->subscribe_id, + res->peer_publish_id, + res->peer_addr, + res->fsd, res->fsd_gas, + res->ssi, res->ssi_len); +} + + +void wpas_notify_nan_replied(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type srv_proto_type, + int publish_id, int peer_subscribe_id, + const u8 *peer_addr, + const u8 *ssi, size_t ssi_len) +{ + char *ssi_hex; + + ssi_hex = os_zalloc(2 * ssi_len + 1); + if (!ssi_hex) + return; + if (ssi) + wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + wpa_msg_global(wpa_s, MSG_INFO, NAN_REPLIED + "publish_id=%d address=" MACSTR + " subscribe_id=%d srv_proto_type=%u ssi=%s", + publish_id, MAC2STR(peer_addr), peer_subscribe_id, + srv_proto_type, ssi_hex); + os_free(ssi_hex); + + wpas_dbus_signal_nan_replied(wpa_s, srv_proto_type, publish_id, + peer_subscribe_id, peer_addr, + ssi, ssi_len); +} + + +void wpas_notify_nan_receive(struct wpa_supplicant *wpa_s, int id, + int peer_instance_id, const u8 *peer_addr, + const u8 *ssi, size_t ssi_len) +{ + char *ssi_hex; + + ssi_hex = os_zalloc(2 * ssi_len + 1); + if (!ssi_hex) + return; + if (ssi) + wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + wpa_msg_global(wpa_s, MSG_INFO, NAN_RECEIVE + "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s", + id, peer_instance_id, MAC2STR(peer_addr), ssi_hex); + os_free(ssi_hex); + + wpas_dbus_signal_nan_receive(wpa_s, id, peer_instance_id, peer_addr, + ssi, ssi_len); +} + + +static const char * nan_reason_txt(enum nan_de_reason reason) +{ + switch (reason) { + case NAN_DE_REASON_TIMEOUT: + return "timeout"; + case NAN_DE_REASON_USER_REQUEST: + return "user-request"; + case NAN_DE_REASON_FAILURE: + return "failure"; + } + + return "unknown"; +} + + +void wpas_notify_nan_publish_terminated(struct wpa_supplicant *wpa_s, + int publish_id, + enum nan_de_reason reason) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED + "publish_id=%d reason=%s", + publish_id, nan_reason_txt(reason)); + wpas_dbus_signal_nan_publish_terminated(wpa_s, publish_id, + nan_reason_txt(reason)); +} + + +void wpas_notify_nan_subscribe_terminated(struct wpa_supplicant *wpa_s, + int subscribe_id, + enum nan_de_reason reason) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED + "subscribe_id=%d reason=%s", + subscribe_id, nan_reason_txt(reason)); + wpas_dbus_signal_nan_subscribe_terminated(wpa_s, subscribe_id, + nan_reason_txt(reason)); +} + + +void wpas_notify_nan_transmit_req_status(struct wpa_supplicant *wpa_s, + u32 cookie, bool acked) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_TRANSMIT_STATUS + "cookie=%u acked=%u", cookie, acked); +} + + +void wpas_notify_nan_bootstrap_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u16 pbm, + int handle, u8 requestor_instance_id) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_BOOTSTRAP_REQUEST + "peer_nmi=" MACSTR + " pbm=0x%04x handle=%d requestor_instance_id=%u", + MAC2STR(peer_nmi), pbm, handle, requestor_instance_id); +} + + +void wpas_notify_nan_bootstrap_success(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u16 pbm, + int handle, u8 requestor_instance_id) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_BOOTSTRAP_SUCCESS + "peer_nmi=" MACSTR + " pbm=0x%04x handle=%d requestor_instance_id=%u", + MAC2STR(peer_nmi), pbm, handle, requestor_instance_id); +} + + +void wpas_notify_nan_bootstrap_failure(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u16 pbm, + u8 reason, int handle, + u8 requestor_instance_id) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_BOOTSTRAP_FAILURE + "peer_nmi=" MACSTR + " pbm=0x%04x reason=%u handle=%d requestor_instance_id=%u", + MAC2STR(peer_nmi), pbm, reason, handle, + requestor_instance_id); +} + + +#ifdef CONFIG_PASN + +void wpas_notify_nan_nik_received(struct wpa_supplicant *wpa_s, + const u8 *nik, size_t nik_len, + int cipher_ver, int akmp, + const u8 *npk, size_t npk_len, + int nik_lifetime, int identity_id) +{ + char *nik_hex, *npk_hex; + size_t nik_hex_len = 2 * nik_len + 1; + size_t npk_hex_len = 2 * npk_len + 1; + + nik_hex = os_malloc(nik_hex_len); + npk_hex = os_malloc(npk_hex_len); + if (!nik_hex || !npk_hex) { + wpa_printf(MSG_INFO, + "NAN NIK received: Failed to allocate memory"); + goto out; + } + + wpa_snprintf_hex(nik_hex, nik_hex_len, nik, nik_len); + wpa_snprintf_hex(npk_hex, npk_hex_len, npk, npk_len); + + wpa_msg_global(wpa_s, MSG_INFO, NAN_NIK_RECEIVED + "nik=%s cipher_version=%d akmp=%s pmk=%s lifetime=%d identity_id=%d", + nik_hex, cipher_ver, + wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN), + npk_hex, nik_lifetime, identity_id); + +out: + bin_clear_free(nik_hex, nik_hex_len); + bin_clear_free(npk_hex, npk_hex_len); +} + + +void wpas_notify_nan_pairing_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u8 csid, + u8 instance_id, int key_mgmt, + bool verify) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_PAIRING_REQUEST + "peer_nmi=" MACSTR + " csid=%u instance_id=%u key_mgmt=%s verify=%d", + MAC2STR(peer_nmi), csid, instance_id, + wpa_key_mgmt_txt(key_mgmt, WPA_PROTO_RSN), + verify); +} + +#endif /* CONFIG_PASN */ + + +void wpas_notify_nan_ndp_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, const u8 *init_ndi, + u32 ndp_id, u8 publish_inst_id, + const u8 *ssi, size_t ssi_len, u32 csid) +{ + char *ssi_hex = NULL; + + if (ssi) { + ssi_hex = os_zalloc(2 * ssi_len + 1); + if (!ssi_hex) + return; + + wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + } + + wpa_msg_global(wpa_s, MSG_INFO, NAN_NDP_REQUEST "peer_nmi=" MACSTR + " init_ndi=" MACSTR + " ndp_id=%u publish_inst_id=%u ssi=%s csid=%u", + MAC2STR(peer_nmi), MAC2STR(init_ndi), + ndp_id, publish_inst_id, + ssi_hex ? ssi_hex : "", csid); + + os_free(ssi_hex); +} + + +void wpas_notify_nan_ndp_counter_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, + const u8 *init_ndi, u32 ndp_id, + const u8 *ssi, size_t ssi_len) +{ + char *ssi_hex = NULL; + + if (ssi) { + ssi_hex = os_zalloc(2 * ssi_len + 1); + if (!ssi_hex) + return; + + wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + } + + wpa_msg_global(wpa_s, MSG_INFO, NAN_NDP_COUNTER_REQUEST + "peer_nmi=" MACSTR " init_ndi=" MACSTR + " ndp_id=%u ssi=%s", + MAC2STR(peer_nmi), MAC2STR(init_ndi), + ndp_id, ssi_hex ? ssi_hex : ""); + + os_free(ssi_hex); +} + + +void wpas_notify_nan_ndp_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u32 ndp_id, + const u8 *local_ndi, + const u8 *peer_ndi, + const u8 *ssi, size_t ssi_len, + const u8 *interface_id) +{ + char *ssi_hex = NULL, *interface_id_hex = NULL; + + if (ssi) { + ssi_hex = os_zalloc(2 * ssi_len + 1); + if (!ssi_hex) + return; + + wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len); + } + + if (interface_id) { + interface_id_hex = + os_zalloc(2 * NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN + 1); + if (!interface_id_hex) + return; + + wpa_snprintf_hex(interface_id_hex, + 2 * NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN + 1, + interface_id, + NAN_NDPE_TLV_IPV6_LINK_LOCAL_LEN); + } + + wpa_msg_global(wpa_s, MSG_INFO, NAN_NDP_CONNECTED "peer=" MACSTR + " ndp_id=%u local_ndi=" MACSTR + " peer_ndi=" MACSTR " ssi=%s interface_id=%s", + MAC2STR(peer_nmi), ndp_id, + MAC2STR(local_ndi), MAC2STR(peer_ndi), + ssi_hex ? ssi_hex : "", + interface_id ? interface_id_hex : ""); + + os_free(ssi_hex); + os_free(interface_id_hex); +} + + +void wpas_notify_nan_ndp_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u32 ndp_id, + const u8 *local_ndi, + const u8 *peer_ndi, + enum nan_reason reason, + bool locally_generated, + bool failure) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_NDP_DISCONNECTED + "peer=" MACSTR " ndp_id=%u local_ndi=" MACSTR + " peer_ndi=" MACSTR + " reason=%u locally_generated=%d, failure=%d", + MAC2STR(peer_nmi), ndp_id, + MAC2STR(local_ndi), MAC2STR(peer_ndi), reason, + locally_generated, failure); +} + + +void wpas_notify_nan_cluster_join(struct wpa_supplicant *wpa_s, + const u8 *cluster_id, bool new_cluster) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_CLUSTER_JOIN "cluster_id=" MACSTR + " new=%d", MAC2STR(cluster_id), new_cluster); +} + + +void wpas_notify_nan_schedule_changed(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi) +{ + wpa_msg_global(wpa_s, MSG_INFO, + NAN_PEER_SCHEDULE_CHANGED "peer_nmi=" MACSTR, + MAC2STR(peer_nmi)); +} + + +void wpas_notify_nan_sched_update_done(struct wpa_supplicant *wpa_s, + bool success) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_SCHEDULE_UPDATE_DONE "status=%s", + success ? "success" : "failure"); +} + + +void wpas_notify_nan_pairing_status(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int akmp, + int cipher, u16 status, + const u8 *nd_pmk) +{ + char nd_pmk_hex[2 * PMK_LEN + 1]; + + if (nd_pmk) + wpa_snprintf_hex(nd_pmk_hex, sizeof(nd_pmk_hex), nd_pmk, + PMK_LEN); + else + nd_pmk_hex[0] = '\0'; + + wpa_msg_global(wpa_s, MSG_INFO, NAN_PAIRING_STATUS "addr=" MACSTR + " akmp=%s cipher=%s status=%s%s%s", + MAC2STR(peer_addr), + wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN), + wpa_cipher_txt(cipher), + status == WLAN_STATUS_SUCCESS ? "success" : "failure", + nd_pmk ? " nd_pmk=" : "", + nd_pmk ? nd_pmk_hex : ""); + forced_memzero(nd_pmk_hex, sizeof(nd_pmk_hex)); +} + + +void wpas_notify_nan_chan_evacuation(struct wpa_supplicant *wpa_s, + u8 map_id, int freq) +{ + wpa_msg_global(wpa_s, MSG_INFO, + NAN_CHAN_EVACUATION "map_id=%u freq=%d", + map_id, freq); +} + + +void wpas_notify_nan_stopped(struct wpa_supplicant *wpa_s) +{ + wpa_msg_global(wpa_s, MSG_INFO, NAN_STOPPED "ifname=%s", + wpa_s->ifname); +} + +#endif /* CONFIG_NAN || CONFIG_NAN_USD */ + + +#ifdef CONFIG_PR + +void wpas_notify_pr_pasn_result(struct wpa_supplicant *wpa_s, u8 role, + u8 protocol_type, u8 op_class, u8 op_channel, + const char *country) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_PASN_RESULT + "SUCCESS role=%u protocol=%u opclass=%u channel=%u cc=%c%c", + role, protocol_type, op_class, op_channel, + country[0], country[1]); +} + + +void wpas_notify_pr_negotiation_started(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, u8 role, + u8 protocol_type) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_PASN_NEGOTIATION_STARTED + "peer_addr=" MACSTR " role=%u protocol=%u", + MAC2STR(peer_addr), role, protocol_type); +} + + +void wpas_notify_pr_ranging_params(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const u8 *peer_addr, + u8 ranging_role, u8 protocol_type, int freq, + int channel, int bw, int format_bw) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_RANGING_PARAMS + "dev_addr=" MACSTR " peer_addr=" MACSTR + " role=%u protocol=%u freq=%d channel=%d bw=%d format_bw=%d", + MAC2STR(dev_addr), MAC2STR(peer_addr), ranging_role, + protocol_type, freq, channel, bw, format_bw); +} + + +void wpas_notify_pr_measurement_result( + struct wpa_supplicant *wpa_s, + const struct peer_measurement_result *result) +{ + char rtt[32] = ""; + char dist[32] = ""; + + if (result->ftm.fail) { + wpa_msg_global(wpa_s, MSG_INFO, PR_EVENT_PEER_MEASUREMENT + "addr=" MACSTR + " status=%u burst_index=%u fail=1 fail_reason=%u", + MAC2STR(result->addr), result->status, + result->ftm.burst_index, + result->ftm.fail_reason); + return; + } + + if (result->ftm.rtt_avg || result->ftm.has_data) + os_snprintf(rtt, sizeof(rtt), " rtt_avg=%lld", + (long long) result->ftm.rtt_avg); + if (result->ftm.dist_avg || result->ftm.has_data) + os_snprintf(dist, sizeof(dist), " dist_avg=%lld", + (long long) result->ftm.dist_avg); + + wpa_msg_global(wpa_s, MSG_INFO, PR_EVENT_PEER_MEASUREMENT + "addr=" MACSTR " status=%u burst_index=%u%s%s", + MAC2STR(result->addr), result->status, + result->ftm.burst_index, rtt, dist); +} + + +void wpas_notify_pr_ranging_complete(struct wpa_supplicant *wpa_s, u64 cookie) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_EVENT_RANGING_COMPLETE + "cookie=%llu", (unsigned long long) cookie); +} + + +void wpas_notify_pr_device_found(struct wpa_supplicant *wpa_s, + const struct pr_device *dev) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_PEER_FOUND + "peer_addr=" MACSTR + " pasn_type=0x%02x name=%s edca=%d edca_ista=%d edca_rsta=%d ntb=%d ntb_ista=%d ntb_rsta=%d secure_ltf=%d 6ghz=%d freq=%d dik_valid=%d", + MAC2STR(dev->pr_device_addr), + dev->pr_caps.pasn_type, + dev->pr_caps.device_name, + dev->pr_caps.edca_support, + dev->edca_caps.ista_support, + dev->edca_caps.rsta_support, + dev->pr_caps.ntb_support, + dev->ntb_caps.ista_support, + dev->ntb_caps.rsta_support, + dev->pr_caps.secure_he_ltf, + dev->pr_caps.support_6ghz, + dev->listen_freq, + dev->dik_valid); +} + + +void wpas_notify_pr_ranging_terminated(struct wpa_supplicant *wpa_s, int reason) +{ + wpa_msg_global(wpa_s, MSG_INFO, PR_RANGING_TERMINATED "reason=%d", + reason); +} + +#endif /* CONFIG_PR */ diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index f6c7ac4e2d3b..3a0fe3e3861c 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -17,6 +17,10 @@ struct wps_event_fail; struct tls_cert_data; struct wpa_cred; struct rsn_pmksa_cache_entry; +enum nan_de_reason; +enum nan_service_protocol_type; +struct nan_discovery_result; +struct pr_device; int wpas_notify_supplicant_initialized(struct wpa_global *global); void wpas_notify_supplicant_deinitialized(struct wpa_global *global); @@ -30,6 +34,7 @@ void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_roam_time(struct wpa_supplicant *wpa_s); void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s); +void wpas_notify_scan_in_progress_6ghz(struct wpa_supplicant *wpa_s); void wpas_notify_session_length(struct wpa_supplicant *wpa_s); void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); @@ -146,6 +151,7 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code); void wpas_notify_psk_mismatch(struct wpa_supplicant *wpa_s); +void wpas_notify_sae_password_mismatch(struct wpa_supplicant *wpa_s); void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, @@ -153,6 +159,11 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr, const u8 *bssid, int id, int op_freq); +void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 bootstrap_method); +void wpas_notify_p2p_bootstrap_rsp(struct wpa_supplicant *wpa_s, + const u8 *src, int status, + u16 bootstrap_method); void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s, @@ -175,5 +186,92 @@ void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s, void wpas_notify_signal_change(struct wpa_supplicant *wpa_s); void wpas_notify_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url); +void wpas_notify_nan_discovery_result(struct wpa_supplicant *wpa_s, + struct nan_discovery_result *res); +void wpas_notify_nan_replied(struct wpa_supplicant *wpa_s, + enum nan_service_protocol_type srv_proto_type, + int publish_id, int peer_subscribe_id, + const u8 *peer_addr, + const u8 *ssi, size_t ssi_len); +void wpas_notify_nan_receive(struct wpa_supplicant *wpa_s, int id, + int peer_instance_id, const u8 *peer_addr, + const u8 *ssi, size_t ssi_len); +void wpas_notify_nan_publish_terminated(struct wpa_supplicant *wpa_s, + int publish_id, + enum nan_de_reason reason); +void wpas_notify_nan_subscribe_terminated(struct wpa_supplicant *wpa_s, + int subscribe_id, + enum nan_de_reason reason); +void wpas_notify_nan_transmit_req_status(struct wpa_supplicant *wpa_s, + u32 cookie, bool acked); +void wpas_notify_nan_nik_received(struct wpa_supplicant *wpa_s, + const u8 *nik, size_t nik_len, + int cipher_ver, int akmp, + const u8 *npk, size_t npk_len, + int nik_lifetime, int identity_id); +void wpas_notify_pr_pasn_result(struct wpa_supplicant *wpa_s, u8 role, + u8 protocol_type, u8 op_class, u8 op_channel, + const char *country); +void wpas_notify_pr_negotiation_started(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, u8 role, + u8 protocol_type); +void wpas_notify_pr_ranging_params(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const u8 *peer_addr, + u8 role, u8 protocol, int freq, int channel, + int bw, int format_bw); +void wpas_notify_pr_measurement_result( + struct wpa_supplicant *wpa_s, + const struct peer_measurement_result *result); +void wpas_notify_pr_ranging_complete(struct wpa_supplicant *wpa_s, + u64 cookie); +void wpas_notify_pr_device_found(struct wpa_supplicant *wpa_s, + const struct pr_device *dev); +void wpas_notify_nan_bootstrap_request(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, u16 pbm, + int handle, u8 requestor_instance_id); +void wpas_notify_nan_bootstrap_success(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, u16 pbm, + int handle, u8 requestor_instance_id); +void wpas_notify_nan_bootstrap_failure(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, u16 pbm, + u8 reason, int handle, + u8 requestor_instance_id); +void wpas_notify_nan_pairing_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u8 csid, + u8 instance_id, int key_mgmt, + bool verify); +void wpas_notify_nan_ndp_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, const u8 *init_ndi, + u32 ndp_id, u8 publish_inst_id, + const u8 *ssi, size_t ssi_len, u32 csid); +void wpas_notify_nan_ndp_counter_request(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, + const u8 *init_ndi, u32 ndp_id, + const u8 *ssi, size_t ssi_len); +void wpas_notify_nan_ndp_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u32 ndp_id, + const u8 *local_ndi, const u8 *peer_ndi, + const u8 *ssi, size_t ssi_len, + const u8 *interface_id); +void wpas_notify_nan_ndp_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi, u32 ndp_id, + const u8 *local_ndi, const u8 *peer_ndi, + enum nan_reason reason, + bool locally_generated, + bool failure); +void wpas_notify_nan_cluster_join(struct wpa_supplicant *wpa_s, + const u8 *cluster_id, bool new_cluster); +void wpas_notify_nan_schedule_changed(struct wpa_supplicant *wpa_s, + const u8 *peer_nmi); +void wpas_notify_nan_sched_update_done(struct wpa_supplicant *wpa_s, + bool success); +void wpas_notify_nan_pairing_status(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int akmp, int cipher, + u16 status, const u8 *nd_pmk); +void wpas_notify_nan_chan_evacuation(struct wpa_supplicant *wpa_s, + u8 map_id, int freq); +void wpas_notify_nan_stopped(struct wpa_supplicant *wpa_s); +void wpas_notify_pr_ranging_terminated(struct wpa_supplicant *wpa_s, + int reason); #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/offchannel.c b/contrib/wpa/wpa_supplicant/offchannel.c index 9e591d7d6df1..688718cd7148 100644 --- a/contrib/wpa/wpa_supplicant/offchannel.c +++ b/contrib/wpa/wpa_supplicant/offchannel.c @@ -124,7 +124,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_drv_remain_on_channel( wpa_s, wpa_s->pending_action_freq, - duration) < 0) { + duration, NULL) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to " "request driver to remain on " "channel (%u MHz) for Action Frame " @@ -369,7 +369,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, wait_time += wpa_s->extra_roc_dur; } #endif /* CONFIG_TESTING_OPTIONS */ - if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { + if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time, NULL) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " "to remain on channel (%u MHz) for Action " "Frame TX", freq); diff --git a/contrib/wpa/wpa_supplicant/op_classes.c b/contrib/wpa/wpa_supplicant/op_classes.c index ff11d20b9903..72ac3e91ba35 100644 --- a/contrib/wpa/wpa_supplicant/op_classes.c +++ b/contrib/wpa/wpa_supplicant/op_classes.c @@ -15,6 +15,7 @@ #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" #include "bss.h" +#include "config.h" static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, @@ -172,7 +173,8 @@ static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, return NOT_ALLOWED; if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL) || - !(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL)) + (!(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL) && + !(flags & HOSTAPD_CHAN_AUTO_BW))) return NOT_ALLOWED; if (flags & HOSTAPD_CHAN_NO_IR) @@ -529,6 +531,8 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, u8 *ie_len; size_t res; bool op128 = false, op130 = false, op133 = false, op135 = false; + bool disable_op_classes_80_80_mhz = wpa_s->conf ? + wpa_s->conf->disable_op_classes_80_80_mhz : false; /* * Determine the current operating class correct mode based on @@ -585,7 +589,8 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, } /* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */ - if ((op128 && op130) || (op133 && op135)) { + if (!disable_op_classes_80_80_mhz && + ((op128 && op130) || (op133 && op135))) { /* Operating Class Duple Sequence field */ /* Zero Delimiter */ diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index 70025f1e4a56..e5d4afe3ca27 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -2,6 +2,7 @@ * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications * Copyright (c) 2010-2014, Jouni Malinen + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,8 +24,10 @@ #include "ap/wps_hostapd.h" #include "ap/p2p_hostapd.h" #include "ap/dfs.h" +#include "ap/wpa_auth.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "ap.h" @@ -37,6 +40,7 @@ #include "wps_supplicant.h" #include "p2p_supplicant.h" #include "wifi_display.h" +#include "nan_supplicant.h" /* @@ -151,7 +155,8 @@ static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type); static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, - int already_deleted); + int already_deleted, + const char *reason); static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, struct wpa_used_freq_data *freqs, unsigned int num); @@ -532,8 +537,8 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, wpabuf_free(ies); radio_remove_works(wpa_s, "p2p-scan", 0); - if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, - params) < 0) + if (!radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, + params)) goto fail; return 0; @@ -1011,7 +1016,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; - wpas_p2p_group_formation_failed(wpa_s, 1); + wpas_p2p_group_formation_failed(wpa_s, 1, reason); } wpa_s->p2p_in_invitation = 0; @@ -1063,6 +1068,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_s->p2p_group_common_freqs_num = 0; wpa_s->p2p_go_do_acs = 0; wpa_s->p2p_go_allow_dfs = 0; + wpa_s->p2p_neg_go_setup = false; wpa_s->waiting_presence_resp = 0; @@ -1177,7 +1183,7 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - const u8 *go_dev_addr) + const u8 *go_dev_addr, int dik_id) { struct wpa_ssid *s; int changed = 0; @@ -1190,6 +1196,9 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, s->ssid_len == ssid->ssid_len && os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0) break; + + if (dik_id && s->go_dik_id == dik_id) + break; } if (s) { @@ -1226,12 +1235,19 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, s->bssid_set = 1; os_memcpy(s->bssid, go_dev_addr, ETH_ALEN); s->mode = ssid->mode; - s->auth_alg = WPA_AUTH_ALG_OPEN; - s->key_mgmt = WPA_KEY_MGMT_PSK; - s->proto = WPA_PROTO_RSN; + s->auth_alg = ssid->auth_alg; + s->key_mgmt = ssid->key_mgmt; + s->proto = ssid->proto; s->pbss = ssid->pbss; + s->pmk_valid = ssid->pmk_valid; s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; s->export_keys = 1; + s->go_dik_id = dik_id; + + if (ssid->sae_password) { + os_free(s->sae_password); + s->sae_password = os_strdup(ssid->sae_password); + } if (ssid->passphrase) { os_free(s->passphrase); s->passphrase = os_strdup(ssid->passphrase); @@ -1265,8 +1281,43 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, } +static void wpas_p2p2_add_group_client_dik_id(struct wpa_ssid *s, int dik_id) +{ + size_t i; + bool found = false; + size_t num = int_array_len(s->p2p2_client_list); + + for (i = 0; i < num; i++) { + if (s->p2p2_client_list[i] != dik_id) + continue; + + if (i == num - 1) + return; /* already the most recent entry */ + + /* Move the entry to mark it most recent */ + os_memmove(s->p2p2_client_list + i, + s->p2p2_client_list + i + 1, + (num - i - 1) * sizeof(int)); + s->p2p2_client_list[num - 1] = dik_id; + found = true; + break; + } + + if (!found && num < P2P_MAX_STORED_CLIENTS) { + int_array_add_unique(&s->p2p2_client_list, dik_id); + } else if (!found && s->p2p2_client_list) { + /* Not enough room for an additional entry - drop the oldest + * entry + */ + os_memmove(s->p2p2_client_list, s->p2p2_client_list + 1, + (num - 1) * sizeof(int)); + s->p2p2_client_list[num - 1] = dik_id; + } +} + + static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, - const u8 *addr) + const u8 *addr, int dik_id) { struct wpa_ssid *ssid, *s; u8 *n; @@ -1291,6 +1342,11 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, if (s == NULL) return; + if (dik_id) { + wpas_p2p2_add_group_client_dik_id(s, dik_id); + goto done; + } + for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) { if (!ether_addr_equal(s->p2p_client_list + i * 2 * ETH_ALEN, addr)) @@ -1338,6 +1394,7 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, 0xff, ETH_ALEN); } +done: if (p2p_wpa_s->conf->update_config && wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); @@ -1386,11 +1443,118 @@ static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq, MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "", extra); + + if (go && is_zero_ether_addr(wpa_s->go_dev_addr)) + os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN); +} + + +int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s) +{ + struct wpa_dev_ik *ik; + + for (ik = wpa_s->conf->identity; ik; ik = ik->next) + wpa_config_remove_identity(wpa_s->conf, ik->id); + + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); + return -1; + } + return 0; +} + + +static int wpas_p2p_store_identity(struct wpa_supplicant *wpa_s, u8 cipher, + const u8 *dik_data, size_t dik_len, + const u8 *pmk, size_t pmk_len, + const u8 *pmkid) +{ + struct wpa_dev_ik *ik; + + for (ik = wpa_s->conf->identity; ik; ik = ik->next) { + if (dik_len == wpabuf_len(ik->dik) && + os_memcmp(dik_data, wpabuf_head(ik->dik), dik_len) == 0) { + wpa_printf(MSG_DEBUG, + "P2P: Remove previous device identity entry for matching DIK"); + wpa_config_remove_identity(wpa_s->conf, ik->id); + break; + } + } + + wpa_printf(MSG_DEBUG, "P2P: Create a new device identity entry"); + ik = wpa_config_add_identity(wpa_s->conf); + if (!ik) + return 0; + + ik->dik = wpabuf_alloc_copy(dik_data, dik_len); + if (!ik->dik) + goto fail; + ik->pmk = wpabuf_alloc_copy(pmk, pmk_len); + if (!ik->pmk) + goto fail; + ik->pmkid = wpabuf_alloc_copy(pmkid, PMKID_LEN); + if (!ik->pmkid) + goto fail; + + ik->dik_cipher = cipher; + + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); + return 0; + } + return ik->id; + +fail: + wpa_config_remove_identity(wpa_s->conf, ik->id); + return 0; +} + + +static int wpas_p2p_store_go_identity(struct wpa_supplicant *wpa_s, + const u8 *go_dev_addr, const u8 *bssid) +{ + int ret; + u8 cipher; + const u8 *dik_data, *pmk, *pmkid; + size_t dik_len, pmk_len; + u8 iface_addr[ETH_ALEN]; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; + + if (!wpa_s->p2p2) + return 0; + + ret = p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, go_dev_addr, + &dik_data, &dik_len, &cipher); + if (ret) + return 0; + + ret = p2p_get_interface_addr(p2p_wpa_s->global->p2p, go_dev_addr, + iface_addr); + if (ret) { + wpa_printf(MSG_DEBUG, + "P2P: Fetch PMK for GO BSSID " MACSTR, + MAC2STR(bssid)); + os_memcpy(iface_addr, bssid, ETH_ALEN); + } + ret = wpa_sm_pmksa_get_pmk(wpa_s->wpa, iface_addr, &pmk, &pmk_len, + &pmkid); + if (ret) + return 0; + + wpa_printf(MSG_DEBUG, + "P2P: Storing Device identity of GO (Interface Addr " MACSTR + ")", + MAC2STR(iface_addr)); + return wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len, + pmk, pmk_len, pmkid); } static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, - int success, int already_deleted) + int already_deleted, + const char *failure_reason) { struct wpa_ssid *ssid; int client; @@ -1407,7 +1571,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; - } else if (wpa_s->p2p_in_provisioning && !success) { + } else if (wpa_s->p2p_in_provisioning && failure_reason) { wpa_msg(wpa_s, MSG_DEBUG, "P2P: Stop provisioning state due to failure"); wpa_s->p2p_in_provisioning = 0; @@ -1416,10 +1580,10 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, wpa_s->p2p_retry_limit = 0; wpa_s->group_formation_reported = 1; - if (!success) { + if (failure_reason) { wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); - wpas_notify_p2p_group_formation_failure(wpa_s, ""); + wpas_notify_p2p_group_formation_failure(wpa_s, failure_reason); if (already_deleted) return; wpas_p2p_group_delete(wpa_s, @@ -1444,11 +1608,13 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, persistent = ssid->p2p_persistent_group; os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); - } else + } else { + os_memset(go_dev_addr, 0, ETH_ALEN); persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, ssid->ssid_len); + } } else { client = wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT; @@ -1476,7 +1642,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (persistent) wpas_p2p_store_persistent_group(wpa_s->p2pdev, - ssid, go_dev_addr); + ssid, go_dev_addr, 0); else { os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; @@ -1659,8 +1825,8 @@ static int wpas_send_action_work(struct wpa_supplicant *wpa_s, awork->wait_time = wait_time; os_memcpy(awork->buf, buf, len); - if (radio_add_work(wpa_s, freq, "p2p-send-action", 1, - wpas_send_action_cb, awork) < 0) { + if (!radio_add_work(wpa_s, freq, "p2p-send-action", 1, + wpas_send_action_cb, awork)) { os_free(awork); return -1; } @@ -1685,8 +1851,11 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, if (listen_freq != (int) freq && send_freq != (int) freq) { int res; - wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)", - listen_freq, send_freq, freq); + wpa_printf(MSG_DEBUG, + "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u dst=" + MACSTR " src=" MACSTR " bssid=" MACSTR, + listen_freq, send_freq, freq, MAC2STR(dst), + MAC2STR(src), MAC2STR(bssid)); res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, len, wait_time); if (res == 0 && scheduled) @@ -1717,6 +1886,125 @@ static void wpas_send_action_done(void *ctx) } +#ifdef CONFIG_PASN + +struct wpa_p2p_pasn_auth_work { + u8 peer_addr[ETH_ALEN]; + int freq; + bool verify; + int force_freq; + int pref_freq; + enum p2p_invite_role role; + u8 *ssid; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + u8 go_dev_addr[ETH_ALEN]; +}; + + +static void wpas_p2p_pasn_free_auth_work(struct wpa_p2p_pasn_auth_work *awork) +{ + if (!awork) + return; + os_free(awork->ssid); + os_free(awork); +} + + +static void wpas_p2p_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "P2P PASN: Cancel p2p-pasn-start-auth work"); + + /* Remove pending/started work */ + radio_remove_works(wpa_s, "p2p-pasn-start-auth", 0); +} + + +static void wpas_p2p_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + int ret; + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_p2p_pasn_auth_work *awork = work->ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + const u8 *peer_addr = NULL; + const u8 *bssid = NULL; + const u8 *go_dev_addr = NULL; + + if (deinit) { + if (!work->started) { + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + } + wpas_p2p_pasn_free_auth_work(awork); + return; + } + + if (!is_zero_ether_addr(awork->peer_addr)) + peer_addr = awork->peer_addr; + if (!is_zero_ether_addr(awork->bssid)) + bssid = awork->bssid; + if (!is_zero_ether_addr(awork->go_dev_addr)) + go_dev_addr = awork->go_dev_addr; + + + if (awork->verify) + ret = p2p_initiate_pasn_verify(p2p, peer_addr, awork->freq, + awork->role, bssid, awork->ssid, + awork->ssid_len, + awork->force_freq, go_dev_addr, + awork->pref_freq); + else + ret = p2p_initiate_pasn_auth(p2p, peer_addr, awork->freq); + + if (ret) { + wpa_printf(MSG_DEBUG, + "P2P PASN: Failed to start PASN authentication"); + goto fail; + } + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + wpa_s->p2p_pasn_auth_work = work; + return; + +fail: + wpas_p2p_pasn_free_auth_work(awork); + work->ctx = NULL; + radio_work_done(work); +} + + +static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq) +{ + struct wpa_p2p_pasn_auth_work *awork; + + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + + awork = os_zalloc(sizeof(*awork)); + if (!awork) + return -1; + + awork->freq = freq; + os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN); + + if (!radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1, + wpas_p2p_pasn_auth_start_cb, awork)) { + wpas_p2p_pasn_free_auth_work(awork); + return -1; + } + + wpa_printf(MSG_DEBUG, + "P2P PASN: Authentication work successfully added"); + return 0; +} + +#endif /* CONFIG_PASN */ + + static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) { @@ -1730,6 +2018,109 @@ static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, } +static void wpas_start_gc(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *res) +{ + struct os_reltime now; + struct wpa_ssid *ssid; + struct rsn_pmksa_cache_entry *entry; + + if (!res->ssid_len) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: SSID info not present"); + return; + } + + wpa_s->group_formation_reported = 0; + wpa_printf(MSG_DEBUG, "P2P: Start connect for peer " MACSTR + " dev_addr " MACSTR, + MAC2STR(res->peer_interface_addr), + MAC2STR(res->peer_device_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start connect for SSID", + res->ssid, res->ssid_len); + wpa_supplicant_ap_deinit(wpa_s); + wpas_copy_go_neg_results(wpa_s, res); + + ssid = wpa_config_add_network(wpa_s->conf); + if (!ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Could not add network for client"); + return; + } + os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->p2p_group = 1; + + ssid->ssid = os_memdup(res->ssid, res->ssid_len); + if (!ssid->ssid) + return; + ssid->ssid_len = res->ssid_len; + + os_memcpy(ssid->bssid, res->peer_interface_addr, ETH_ALEN); + + if (res->akmp == WPA_KEY_MGMT_PASN && res->sae_password[0]) { + ssid->auth_alg = WPA_AUTH_ALG_SAE; + ssid->sae_password = os_strdup(res->sae_password); + if (!ssid->sae_password) + return; + } else if (res->akmp == WPA_KEY_MGMT_SAE && res->pmk_len) { + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + entry = os_zalloc(sizeof(*entry)); + if (!entry) + return; + os_memcpy(entry->aa, res->peer_interface_addr, ETH_ALEN); + os_memcpy(entry->pmkid, res->pmkid, PMKID_LEN); + entry->pmk_len = res->pmk_len; + os_memcpy(entry->pmk, res->pmk, res->pmk_len); + entry->akmp = res->akmp; + os_get_reltime(&now); + entry->expiration = now.sec + 43200; + entry->reauth_time = now.sec + 43200 * 70 / 100; + entry->network_ctx = ssid; + os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN); + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + } else { + os_free(wpa_s->p2p_pmksa_entry); + wpa_s->p2p_pmksa_entry = entry; + } + ssid->pmk_valid = true; + } else if ((res->akmp & WPA_KEY_MGMT_SAE) && res->sae_password[0]) { + ssid->auth_alg = WPA_AUTH_ALG_SAE; + ssid->sae_password = os_strdup(res->sae_password); + if (!ssid->sae_password) + return; + } + + if (res->psk_set) { + os_memcpy(ssid->psk, res->psk, 32); + ssid->psk_set = 1; + } + ssid->proto = WPA_PROTO_RSN; + if (wpa_s->p2p_mode == WPA_P2P_MODE_WFD_PCC) + ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PSK; + else + ssid->key_mgmt = WPA_KEY_MGMT_SAE; + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->group_cipher = WPA_CIPHER_CCMP; + if (res->cipher) + ssid->pairwise_cipher |= res->cipher; + ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT; + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + ssid->disabled = 0; + wpa_s->show_group_started = 1; + wpa_s->p2p_in_invitation = 1; + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->global->p2p_group_formation = wpa_s; + ssid->rsn_overriding = RSN_OVERRIDING_ENABLED; + + wpa_s->current_ssid = ssid; + wpa_supplicant_update_scan_results(wpa_s, res->peer_interface_addr); + wpa_supplicant_select_network(wpa_s, ssid); +} + + static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) { @@ -1874,6 +2265,20 @@ static void p2p_go_configured(void *ctx, void *data) return; } + if (wpa_s->ap_iface && params->p2p2 && + params->akmp == WPA_KEY_MGMT_SAE) { + struct hostapd_data *hapd = wpa_s->ap_iface->bss[0]; + + wpa_auth_pmksa_add_sae(hapd->wpa_auth, + params->peer_device_addr, + params->pmk, params->pmk_len, + params->pmkid, WPA_KEY_MGMT_SAE, + false, 0); + hostapd_add_pmkid(hapd, params->peer_device_addr, + params->pmk, params->pmk_len, + params->pmkid, WPA_KEY_MGMT_SAE); + } + p2p_go_save_group_common_freqs(wpa_s, params); p2p_go_dump_common_freqs(wpa_s); @@ -1912,7 +2317,7 @@ static void p2p_go_configured(void *ctx, void *data) if (params->persistent_group) { wpas_p2p_store_persistent_group( wpa_s->p2pdev, ssid, - wpa_s->global->p2p_dev_addr); + wpa_s->global->p2p_dev_addr, 0); wpas_p2p_add_psk_list(wpa_s, ssid); } @@ -1939,13 +2344,21 @@ static void p2p_go_configured(void *ctx, void *data) return; } - wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning"); if (wpa_supplicant_ap_mac_addr_filter(wpa_s, params->peer_interface_addr)) { wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address " "filtering"); return; } + + if (params->p2p2) { + wpas_group_formation_completed(wpa_s, 0, NULL); + wpa_printf(MSG_DEBUG, + "P2P2: Group formation completed - first connection in progress"); + goto out; + } + + wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning"); if (params->wps_method == WPS_PBC) { wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr, params->peer_device_addr); @@ -1966,6 +2379,7 @@ static void p2p_go_configured(void *ctx, void *data) } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, wpa_s->p2p_pin, NULL, 0, 0); +out: os_free(wpa_s->go_params); wpa_s->go_params = NULL; } @@ -2048,9 +2462,9 @@ int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s, } -static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, - struct p2p_go_neg_results *params, - int group_formation) +static void wpas_start_go(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int group_formation, enum wpa_p2p_mode p2p_mode) { struct wpa_ssid *ssid; @@ -2145,6 +2559,24 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_config_update_psk(ssid); ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity; + ssid->p2p_mode = p2p_mode; + if (params->p2p2) { + if (params->akmp == WPA_KEY_MGMT_SAE) + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + else + ssid->auth_alg |= WPA_AUTH_ALG_SAE; + + ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN; + ssid->sae_password = os_strdup(params->sae_password); + /* In PCC, RSNE indicates PMF to be disabled while RSNOE/RSNO2E + * requires PMF for SAE. */ + if (ssid->p2p_mode != WPA_P2P_MODE_WFD_PCC) + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT; + if (params->cipher) + ssid->pairwise_cipher |= params->cipher; + } + wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; wpa_s->ap_configured_cb_data = wpa_s->go_params; @@ -2152,6 +2584,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_s->connect_without_scan = ssid; wpa_s->reassociate = 1; wpa_s->disconnected = 0; + wpa_s->p2p_neg_go_setup = group_formation; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to " "start GO)"); wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -2217,6 +2650,7 @@ do { \ d->go_venue_group = s->go_venue_group; d->go_venue_type = s->go_venue_type; d->p2p_add_cli_chan = s->p2p_add_cli_chan; + d->disable_op_classes_80_80_mhz = s->disable_op_classes_80_80_mhz; } @@ -2359,6 +2793,8 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) wpa_s->global->pending_group_iface_for_p2ps = 0; wpas_p2p_clone_config(group_wpa_s, wpa_s); + group_wpa_s->p2p2 = wpa_s->p2p2; + group_wpa_s->assisted_dfs = wpa_s->assisted_dfs; if (wpa_s->conf->p2p_interface_random_mac_addr) { if (wpa_drv_set_mac_addr(group_wpa_s, @@ -2391,19 +2827,34 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; + +#ifdef CONFIG_PASN + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } +#endif /* CONFIG_PASN */ + wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); - wpas_p2p_group_formation_failed(wpa_s, 0); + wpas_p2p_group_formation_failed(wpa_s, 0, "Group formation timed out"); } static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, - int already_deleted) + int already_deleted, + const char *reason) { + /* reason == NULL would indicate success in + * wpas_group_formation_completed(), so make sure that is not the case + * here. */ + if (!reason) + reason = ""; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); - wpas_group_formation_completed(wpa_s, 0, already_deleted); + wpas_group_formation_completed(wpa_s, already_deleted, reason); } @@ -2445,6 +2896,56 @@ bool wpas_p2p_retry_limit_exceeded(struct wpa_supplicant *wpa_s) } +static void wpas_set_go_security_config(void *ctx, + struct p2p_go_neg_results *params) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *tmp, *ifs = NULL; + struct hostapd_data *hapd; + + wpa_printf(MSG_DEBUG, "P2P: GO security config callback"); + +#ifdef CONFIG_NAN_USD + wpas_nan_usd_state_change_notif(wpa_s); +#endif /* CONFIG_NAN_USD */ + + if (!params->p2p2) + return; + + dl_list_for_each(tmp, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + struct wpa_ssid *ssid = tmp->current_ssid; + + if (ssid && ssid->mode == WPAS_MODE_P2P_GO && + ssid->ssid && ssid->ssid_len == params->ssid_len && + os_memcmp(ssid->ssid, params->ssid, params->ssid_len) == 0) + { + ifs = tmp; + break; + } + } + + if (!ifs || !ifs->ap_iface) + return; + + hapd = ifs->ap_iface->bss[0]; + hapd->conf->wps_state = 0; + + if (params->akmp == WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_DEBUG, "P2P: Adding PMK for peer: " MACSTR, + MAC2STR(params->peer_device_addr)); + wpa_auth_pmksa_add_sae(hapd->wpa_auth, + params->peer_device_addr, + params->pmk, params->pmk_len, + params->pmkid, WPA_KEY_MGMT_SAE, + false, 0); + hostapd_add_pmkid(hapd, params->peer_device_addr, + params->pmk, params->pmk_len, + params->pmkid, WPA_KEY_MGMT_SAE); + } +} + + static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) { struct wpa_supplicant *wpa_s = ctx; @@ -2456,6 +2957,17 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpa_s->roc_waiting_drv_freq = 0; } +#ifdef CONFIG_PASN + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } +#endif /* CONFIG_PASN */ + +#ifdef CONFIG_NAN_USD + wpas_nan_usd_state_change_notif(wpa_s); +#endif /* CONFIG_NAN_USD */ + if (res->status) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", @@ -2511,7 +3023,8 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_remove_pending_group_interface(wpa_s); eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); - wpas_p2p_group_formation_failed(wpa_s, 1); + wpas_p2p_group_formation_failed(wpa_s, 1, + "Could not initialize group interface"); return; } os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); @@ -2529,12 +3042,18 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, sizeof(group_wpa_s->p2p_pin)); group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; + group_wpa_s->p2p2 = res->p2p2; + group_wpa_s->p2p_bootstrap = wpa_s->p2p_bootstrap; } + if (res->role_go) { - wpas_start_wps_go(group_wpa_s, res, 1); + wpas_start_go(group_wpa_s, res, 1, group_wpa_s->p2p_mode); } else { os_get_reltime(&group_wpa_s->scan_min_time); - wpas_start_wps_enrollee(group_wpa_s, res); + if (res->p2p2) + wpas_start_gc(group_wpa_s, res); + else + wpas_start_wps_enrollee(group_wpa_s, res); } wpa_s->global->p2p_long_listen = 0; @@ -2621,8 +3140,7 @@ static void wpas_dev_found(void *ctx, const u8 *addr, wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR " p2p_dev_addr=" MACSTR - " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d", + " pri_dev_type=%s name='%s' config_methods=0x%x dev_capab=0x%x group_capab=0x%x%s%s%s new=%d pcea_cap_info=0x%x bootstrap_methods=0x%x pasn_type=0x%x", MAC2STR(addr), MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str(info->pri_dev_type, devtype, sizeof(devtype)), @@ -2631,7 +3149,9 @@ static void wpas_dev_found(void *ctx, const u8 *addr, wfd_dev_info_hex ? " wfd_dev_info=0x" : "", wfd_dev_info_hex ? wfd_dev_info_hex : "", info->vendor_elems ? " vendor_elems=1" : "", - new_device); + new_device, info->pcea_cap_info, + info->pairing_config.bootstrap_methods, + info->pairing_config.pasn_type); done: os_free(wfd_dev_info_hex); @@ -2694,6 +3214,20 @@ static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s) } +static void wpas_stop_started_listen_work(struct wpa_radio_work *work) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + + wpa_s->p2p_listen_work = NULL; + work->ctx = NULL; + + /* Prevent recursive p2p-listen work removal while handling deinit. */ + wpa_s->p2p_removing_listen_work = true; + wpas_stop_listen(wpa_s); + wpa_s->p2p_removing_listen_work = false; +} + + static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; @@ -2701,10 +3235,8 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) unsigned int duration; if (deinit) { - if (work->started) { - wpa_s->p2p_listen_work = NULL; - wpas_stop_listen(wpa_s); - } + if (work->started && !wpa_s->p2p_removing_listen_work) + wpas_stop_started_listen_work(work); wpas_p2p_listen_work_free(lwork); return; } @@ -2716,6 +3248,7 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) if (wpa_drv_probe_req_report(wpa_s, 1) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to " "report received Probe Request frames"); + p2p_listen_failed(wpa_s->global->p2p, lwork->freq); wpas_p2p_listen_work_done(wpa_s); return; } @@ -2732,10 +3265,11 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) } #endif /* CONFIG_TESTING_OPTIONS */ - if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) { + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration, NULL) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " "to remain on channel (%u MHz) for Listen " "state", lwork->freq); + p2p_listen_failed(wpa_s->global->p2p, lwork->freq); wpas_p2p_listen_work_done(wpa_s); wpa_s->pending_listen_freq = 0; return; @@ -2770,8 +3304,8 @@ static int wpas_start_listen(void *ctx, unsigned int freq, } } - if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb, - lwork) < 0) { + if (!radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb, + lwork)) { wpas_p2p_listen_work_free(lwork); return -1; } @@ -2799,10 +3333,13 @@ static void wpas_stop_listen(void *ctx) wpas_p2p_listen_work_done(wpa_s); - if (radio_work_pending(wpa_s, "p2p-listen")) { + if (!wpa_s->p2p_removing_listen_work && + radio_work_pending(wpa_s, "p2p-listen")) { + wpa_s->p2p_removing_listen_work = true; wpa_printf(MSG_DEBUG, "P2P: p2p-listen is still pending - remove it"); radio_remove_works(wpa_s, "p2p-listen", 0); + wpa_s->p2p_removing_listen_work = false; } } @@ -3183,7 +3720,8 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, size_t ssid_len, int *go, u8 *group_bssid, int *force_freq, int persistent_group, const struct p2p_channels *channels, - int dev_pw_id) + int dev_pw_id, bool p2p2, const u8 **new_ssid, + size_t *new_ssid_len) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; @@ -3247,13 +3785,47 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, for (s = wpa_s->conf->ssid; s; s = s->next) { if (s->disabled == 2 && - ether_addr_equal(s->bssid, go_dev_addr) && + (p2p2 || ether_addr_equal(s->bssid, go_dev_addr)) && s->ssid_len == ssid_len && os_memcmp(ssid, s->ssid, ssid_len) == 0) break; } - if (!s) { + if (p2p2) { + int dik_id; + u8 go_ssid[SSID_MAX_LEN]; + + dik_id = p2p_get_dik_id(wpa_s->global->p2p, sa); + s = wpa_config_get_network_with_dik_id(wpa_s->conf, dik_id); + if (!s) { + wpa_printf(MSG_DEBUG, "P2P2: Invitation from " MACSTR + " requested reinvocation of an unknown group", + MAC2STR(sa)); + return P2P_SC_FAIL_UNKNOWN_GROUP; + } + os_free(s->ssid); + if (s->mode == WPAS_MODE_P2P_GO) { + p2p_build_ssid(wpa_s->global->p2p, go_ssid, + &s->ssid_len); + s->ssid = os_memdup(go_ssid, s->ssid_len); + if (!s->ssid) { + s->ssid_len = 0; + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + wpa_printf(MSG_DEBUG, + "P2P: New random SSID for the group: %s", + wpa_ssid_txt(s->ssid, s->ssid_len)); + *new_ssid = s->ssid; + *new_ssid_len = s->ssid_len; + } else { + s->ssid_len = ssid_len; + s->ssid = os_memdup(ssid, ssid_len); + if (!s->ssid) { + s->ssid_len = 0; + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + } + } else if (!s) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR " requested reinvocation of an unknown group", MAC2STR(sa)); @@ -3342,7 +3914,8 @@ accept_inv: static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, const u8 *ssid, size_t ssid_len, const u8 *go_dev_addr, u8 status, - int op_freq) + int op_freq, const u8 *pmkid, + const u8 *pmk, size_t pmk_len) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; @@ -3359,19 +3932,25 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, " was accepted; op_freq=%d MHz, SSID=%s", MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { + const char *ssid_txt; + + ssid_txt = wpa_ssid_txt(s->ssid, s->ssid_len); int go = s->mode == WPAS_MODE_P2P_GO; if (go) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_ACCEPTED "sa=" MACSTR - " persistent=%d freq=%d", - MAC2STR(sa), s->id, op_freq); + " persistent=%d freq=%d ssid=\"%s\" go_dev_addr=" + MACSTR, MAC2STR(sa), s->id, + op_freq, ssid_txt, + MAC2STR(go_dev_addr)); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_ACCEPTED "sa=" MACSTR - " persistent=%d", - MAC2STR(sa), s->id); + " persistent=%d ssid=\"%s\" go_dev_addr=" MACSTR, + MAC2STR(sa), s->id, ssid_txt, + MAC2STR(go_dev_addr)); } wpas_p2p_group_add_persistent( wpa_s, s, go, 0, op_freq, 0, @@ -3382,7 +3961,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, wpa_s->conf->p2p_go_edmg, NULL, go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 1, is_p2p_allow_6ghz(wpa_s->global->p2p), 0, - NULL); + bssid, sa, pmkid, pmk, pmk_len, false, true); } else if (bssid) { wpa_s->user_initiated_pd = 0; wpa_msg_global(wpa_s, MSG_INFO, @@ -3497,23 +4076,87 @@ static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, } -static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, +static void wpas_msg_p2p_invitation_result(struct wpa_supplicant *wpa_s, + int status, const u8 *new_ssid, + size_t new_ssid_len, const u8 *bssid, + const u8 *go_dev_addr) +{ + int res; + char buf[500]; + char *pos, *end; + const char *ssid_txt = NULL; + + pos = buf; + end = buf + sizeof(buf); + + if (go_dev_addr && new_ssid && new_ssid_len) { + ssid_txt = wpa_ssid_txt(new_ssid, new_ssid_len); + } else if (go_dev_addr) { + struct wpa_ssid *ssid; + + ssid = wpa_config_get_network(wpa_s->conf, + wpa_s->pending_invite_ssid_id); + if (ssid) + ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len); + } + + res = os_snprintf(pos, end - pos, "status=%d", status); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + + if (bssid) { + res = os_snprintf(pos, end - pos, " " MACSTR, MAC2STR(bssid)); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + if (ssid_txt) { + res = os_snprintf(pos, end - pos, " ssid=\"%s\"", ssid_txt); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + if (go_dev_addr) { + res = os_snprintf(pos, end - pos, " go_dev_addr=" MACSTR, + MAC2STR(go_dev_addr)); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT "%s", buf); + return; + +fail: + wpa_printf(MSG_DEBUG, + "P2P: Failed to send P2P-INVITATION-RESULT message"); +} + + +static void wpas_invitation_result(void *ctx, int status, const u8 *new_ssid, + size_t new_ssid_len, const u8 *bssid, const struct p2p_channels *channels, const u8 *peer, int neg_freq, - int peer_oper_freq) + int peer_oper_freq, const u8 *pmkid, + const u8 *pmk, size_t pmk_len, + const u8 *go_dev_addr) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid; int freq; - if (bssid) { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT - "status=%d " MACSTR, - status, MAC2STR(bssid)); - } else { - wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT - "status=%d ", status); +#ifdef CONFIG_PASN + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; } +#endif /* CONFIG_PASN */ + + wpas_msg_p2p_invitation_result(wpa_s, status, new_ssid, new_ssid_len, + bssid, go_dev_addr); wpas_notify_p2p_invitation_result(wpa_s, status, bssid); wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, @@ -3576,6 +4219,16 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, return; } + if (new_ssid && new_ssid_len) { + os_free(ssid->ssid); + ssid->ssid = os_memdup(new_ssid, new_ssid_len); + if (!ssid->ssid) { + ssid->ssid_len = 0; + return; + } + ssid->ssid_len = new_ssid_len; + } + /* * The peer could have missed our ctrl::ack frame for Invitation * Response and continue retransmitting the frame. To reduce the @@ -3613,7 +4266,8 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 1, is_p2p_allow_6ghz(wpa_s->global->p2p), 0, - NULL); + bssid, peer, pmkid, pmk, pmk_len, false, + true); } @@ -3775,7 +4429,7 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, chans, num_chans); if (!center_chan) return NOT_ALLOWED; - if (!wpa_s->p2p_go_allow_dfs && + if (!wpa_s->p2p_go_allow_dfs && !wpa_s->allow_p2p_assisted_dfs && !is_6ghz && center_chan >= 58 && center_chan <= 138) return NOT_ALLOWED; /* Do not allow DFS channels for P2P */ @@ -3871,7 +4525,8 @@ static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s, if (!is_6ghz_op_class(op_class)) { if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL) || - !(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL)) + (!(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL) && + !(flags & HOSTAPD_CHAN_AUTO_BW))) return NOT_ALLOWED; } else if (is_6ghz_op_class(op_class) && (!(wpas_get_6ghz_he_chwidth_capab(mode) & @@ -3949,6 +4604,23 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, } +static bool is_dfs_chan_supported(struct wpa_supplicant *wpa_s, + u8 op_class, u8 chan) +{ + if (!wpa_s->allow_p2p_assisted_dfs) + return false; + + if (op_class >= 128 && op_class <= 130) { + int freq = ieee80211_chan_to_freq(NULL, op_class, chan); + + if (ieee80211_is_dfs(freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) + return true; + } + return false; +} + + static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, struct p2p_channels *chan, struct p2p_channels *cli_chan, @@ -3970,8 +4642,10 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, const struct oper_class_map *o = &global_op_class[op]; unsigned int ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; + bool is_dfs_allowed = is_dfs_global_op_class(o->op_class) && + wpa_s->allow_p2p_assisted_dfs; - if (o->p2p == NO_P2P_SUPP || + if ((!is_dfs_allowed && o->p2p == NO_P2P_SUPP) || (is_6ghz_op_class(o->op_class) && p2p_disable_6ghz)) continue; @@ -3983,6 +4657,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_24ghz_social_channels = 1; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { enum chan_allowed res; + bool dfs_chan_supported = is_dfs_allowed || + is_dfs_chan_supported(wpa_s, o->op_class, ch); /* Check for non-continuous jump in channel index * incrementation */ @@ -3992,7 +4668,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, res = wpas_p2p_verify_channel(wpa_s, mode, o->op_class, ch, o->bw); - if (res == ALLOWED) { + if (res == ALLOWED || + (res == RADAR && dfs_chan_supported)) { if (reg == NULL) { if (cla == P2P_MAX_REG_CLASSES) continue; @@ -4056,7 +4733,8 @@ int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s, * driver offloaded DFS. */ if ((o->p2p == NO_P2P_SUPP && (!is_dfs_global_op_class(o->op_class) || - !wpa_s->p2p_go_allow_dfs)) || + (!wpa_s->p2p_go_allow_dfs && + !wpa_s->allow_p2p_assisted_dfs))) || (is_6ghz_op_class(o->op_class) && wpa_s->conf->p2p_6ghz_disable)) continue; @@ -4086,7 +4764,8 @@ int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s, return get_6ghz_sec_channel(channel); return (o->bw == BW40MINUS) ? -1 : 1; } - if (ret == RADAR && wpa_s->p2p_go_allow_dfs) { + if (ret == RADAR && (wpa_s->p2p_go_allow_dfs || + wpa_s->allow_p2p_assisted_dfs)) { /* Allow RADAR channels used for driver * offloaded DFS */ return (o->bw == BW40MINUS) ? -1 : 1; @@ -4106,7 +4785,9 @@ int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, enum chan_allowed ret; ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80); - if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs))) + if (!(ret == ALLOWED || + (ret == RADAR && (wpa_s->p2p_go_allow_dfs || + wpa_s->allow_p2p_assisted_dfs)))) return 0; if (is_6ghz_op_class(op_class)) { @@ -4130,8 +4811,27 @@ int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, enum chan_allowed ret; ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160); - if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs))) + + /* + * For 160 MHz operation on radar (DFS) channels: + * - Check if DFS operation is allowed. + * - Additionally, when coming up as part of GO negotiation with a peer + * (i.e., negotiated GO), do not allow upgrading to 160 MHz on radar + * channels unless assisted_dfs is set. For autonomous GO bring-up, + * allow as long as p2p_go_allow_dfs permits it. + */ + if (ret == RADAR) { + if (!wpa_s->p2p_go_allow_dfs && !wpa_s->allow_p2p_assisted_dfs) + return 0; + + /* For negotiated GO and P2P invitation, require assisted DFS */ + if ((wpa_s->p2p_neg_go_setup || wpa_s->p2p_in_invitation) && + !wpa_s->assisted_dfs) + return 0; + } else if (ret != ALLOWED) { return 0; + } + if (is_6ghz_op_class(op_class)) { chans = center_channels_6ghz_160mhz; num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz); @@ -4465,7 +5165,8 @@ static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len, } -static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, +static void wpas_p2ps_prov_complete(void *ctx, enum p2p_status_code status, + const u8 *dev, const u8 *adv_mac, const u8 *ses_mac, const u8 *grp_mac, u32 adv_id, u32 ses_id, u8 conncap, int passwd_id, @@ -4679,10 +5380,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : - 0, 0, false, 0, NULL); + 0, 0, false, 0, NULL, NULL, NULL, NULL, + 0, false, false); } else if (response_done) { wpas_p2p_group_add(wpa_s, 1, freq, - 0, 0, 0, 0, 0, 0, false); + 0, 0, 0, 0, 0, 0, false, + wpa_s->p2p2, + WPA_P2P_MODE_WFD_R1); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { @@ -4802,10 +5506,12 @@ static int wpas_prov_disc_resp_cb(void *ctx) NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0, - is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL); + is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL, NULL, + NULL, NULL, 0, false, false); } else { wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0, - is_p2p_allow_6ghz(wpa_s->global->p2p)); + is_p2p_allow_6ghz(wpa_s->global->p2p), + wpa_s->p2p2, WPA_P2P_MODE_WFD_R1); } return 1; @@ -4823,6 +5529,250 @@ static int wpas_p2p_get_pref_freq_list(void *ctx, int go, } +static void wpas_p2p_send_bootstrap_comeback(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_printf(MSG_DEBUG, "P2P2: Send bootstrapping comeback PD Request"); + + if (wpa_s->p2p_pd_before_go_neg) { + wpas_p2p_connect(wpa_s, wpa_s->p2p_bootstrap_dev_addr, + wpa_s->p2p_pin, wpa_s->p2p_wps_method, + wpa_s->p2p_persistent_group, 0, 0, 0, + wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_go_vht_center_freq2, + wpa_s->p2p_persistent_id, 1, + wpa_s->p2p_go_ht40, + wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, + wpa_s->p2p_go_he, + wpa_s->p2p_go_edmg, + NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p), + wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL, + false); + } else { + p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->p2p_bootstrap_dev_addr, NULL, + 0, 0, 0, 1); + } +} + + +static void wpas_p2p_register_bootstrap_comeback(void *ctx, const u8 *addr, + u16 comeback_after) +{ + unsigned int timeout_us; + struct wpa_supplicant *wpa_s = ctx; + + timeout_us = comeback_after * 1024; + os_memcpy(wpa_s->p2p_bootstrap_dev_addr, addr, ETH_ALEN); + + eloop_cancel_timeout(wpas_p2p_send_bootstrap_comeback, wpa_s, NULL); + eloop_register_timeout(0, timeout_us, wpas_p2p_send_bootstrap_comeback, + wpa_s, NULL); +} + + +static void wpas_bootstrap_req_rx(void *ctx, const u8 *addr, + u16 bootstrap_method) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_BOOTSTRAP_REQUEST MACSTR + " bootstrap_method=%u", MAC2STR(addr), bootstrap_method); + + wpas_notify_p2p_bootstrap_req(wpa_s, addr, bootstrap_method); +} + + +static void wpas_bootstrap_rsp_rx(void *ctx, const u8 *addr, + enum p2p_status_code status, int freq, + u16 bootstrap_method) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_p2p_bootstrap_rsp(wpa_s, addr, status, bootstrap_method); + + if (status) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_BOOTSTRAP_FAILURE MACSTR " status=%d", + MAC2STR(addr), status); + return; + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_BOOTSTRAP_SUCCESS MACSTR " status=%d", + MAC2STR(addr), status); + +#ifdef CONFIG_PASN + if (wpa_s->p2p_pd_before_go_neg) + wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq); +#endif /* CONFIG_PASN */ +} + + +static int wpas_set_pmksa(void *ctx, const u8 *peer_addr, int dik_id) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_dev_ik *ik; + + for (ik = wpa_s->conf->identity; ik; ik = ik->next) { + if (ik->id == dik_id) + break; + } + if (!ik) + return -1; +#ifdef CONFIG_PASN + p2p_pasn_pmksa_set_pmk(wpa_s->global->p2p, wpa_s->global->p2p_dev_addr, + peer_addr, + wpabuf_head(ik->pmk), wpabuf_len(ik->pmk), + wpabuf_head(ik->pmkid)); +#endif /* CONFIG_PASN */ + return 0; +} + + +static int wpas_validate_dira(void *ctx, const u8 *peer_addr, + const u8 *dira_nonce, const u8 *dira_tag) +{ + struct wpa_supplicant *wpa_s = ctx; + int ret; + u8 tag[DEVICE_MAX_HASH_LEN]; + struct wpa_dev_ik *ik; + const u8 *addr[3]; + size_t len[3]; + const char *label = "DIR"; + + addr[0] = (const u8 *) label; + len[0] = DIR_STR_LEN; + addr[1] = peer_addr; + len[1] = ETH_ALEN; + addr[2] = dira_nonce; + len[2] = DEVICE_IDENTITY_NONCE_LEN; + + for (ik = wpa_s->conf->identity; ik; ik = ik->next) { + if (wpabuf_len(ik->dik) != DEVICE_IDENTITY_KEY_LEN || + ik->dik_cipher != DIRA_CIPHER_VERSION_128) + continue; + + ret = hmac_sha256_vector(wpabuf_head(ik->dik), + DEVICE_IDENTITY_KEY_LEN, + 3, addr, len, tag); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "P2P2: Failed to derive DIRA Tag"); + return 0; + } + + if (os_memcmp(tag, dira_tag, DEVICE_IDENTITY_TAG_LEN) == 0) { + wpa_printf(MSG_DEBUG, "P2P2: DIRA Tag matched"); + break; + } + } + + if (!ik) + return 0; + + return ik->id; +} + + +#ifdef CONFIG_PASN + +static int wpas_p2p_initiate_pasn_verify(struct wpa_supplicant *wpa_s, + const u8 *peer, + enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, + size_t ssid_len, + unsigned int force_freq, + const u8 *go_dev_addr, + unsigned int pref_freq) +{ + int freq; + struct wpa_p2p_pasn_auth_work *awork; + + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + + freq = p2p_get_listen_freq(wpa_s->global->p2p, peer); + if (freq == -1) + return -1; + + awork = os_zalloc(sizeof(*awork)); + if (!awork) + return -1; + + awork->verify = 1; + awork->role = role; + awork->freq = freq; + awork->force_freq = force_freq; + awork->pref_freq = pref_freq; + os_memcpy(awork->peer_addr, peer, ETH_ALEN); + if (go_dev_addr) + os_memcpy(awork->go_dev_addr, go_dev_addr, ETH_ALEN); + if (bssid) + os_memcpy(awork->bssid, bssid, ETH_ALEN); + if (ssid_len) { + awork->ssid = os_zalloc(ssid_len); + if (!awork->ssid) { + os_free(awork); + return -1; + } + os_memcpy(awork->ssid, ssid, ssid_len); + awork->ssid_len = ssid_len; + } + + if (!radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1, + wpas_p2p_pasn_auth_start_cb, awork)) { + wpas_p2p_pasn_free_auth_work(awork); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P PASN: Auth work successfully added"); + return 0; +} + + +static int wpas_p2p_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, + unsigned int wait) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait); +} + + +static int wpas_p2p_prepare_data_element(void *ctx, const u8 *peer_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_prepare_data_element(p2p, peer_addr); +} + + +static int wpas_p2p_parse_data_element(void *ctx, const u8 *data, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_parse_data_element(p2p, data, len); +} + + +static int wpas_p2p_pasn_validate_pmkid(void *ctx, const u8 *addr, + const u8 *rsn_pmkid) +{ + struct wpa_supplicant *wpa_s = ctx; + + return p2p_pasn_validate_and_update_pmkid(wpa_s->global->p2p, addr, + rsn_pmkid); +} + +#endif /* CONFIG_PASN */ + + int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) { u8 addr[ETH_ALEN] = {0}; @@ -4881,6 +5831,20 @@ int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) } +static bool wpas_p2p_dfs_chan(void *ctx, int freq, u8 op_class, u8 chan) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (freq == 0) { + freq = ieee80211_chan_to_freq(NULL, op_class, chan); + if (freq < 0) + return false; + } + + return ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes); +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -4890,6 +5854,7 @@ int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) { struct p2p_config p2p; + struct wpa_supplicant *t_wpa_s; int i; if (wpa_s->conf->p2p_disabled) @@ -4914,6 +5879,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.send_action = wpas_send_action; p2p.send_action_done = wpas_send_action_done; p2p.go_neg_completed = wpas_go_neg_completed; + p2p.set_go_security_config = wpas_set_go_security_config; p2p.go_neg_req_rx = wpas_go_neg_req_rx; p2p.dev_found = wpas_dev_found; p2p.dev_lost = wpas_dev_lost; @@ -4942,6 +5908,18 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.p2ps_group_capability = p2ps_group_capability; p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list; p2p.p2p_6ghz_disable = wpa_s->conf->p2p_6ghz_disable; + p2p.register_bootstrap_comeback = wpas_p2p_register_bootstrap_comeback; + p2p.bootstrap_req_rx = wpas_bootstrap_req_rx; + p2p.bootstrap_rsp_rx = wpas_bootstrap_rsp_rx; + p2p.validate_dira = wpas_validate_dira; + p2p.set_pmksa = wpas_set_pmksa; +#ifdef CONFIG_PASN + p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mgmt; + p2p.prepare_data_element = wpas_p2p_prepare_data_element; + p2p.parse_data_element = wpas_p2p_parse_data_element; + p2p.pasn_validate_pmkid = wpas_p2p_pasn_validate_pmkid; +#endif /* CONFIG_PASN */ + p2p.is_p2p_dfs_chan = wpas_p2p_dfs_chan; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -5057,11 +6035,60 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) else p2p.passphrase_len = 8; + if (wpa_s->conf->dik && + wpabuf_len(wpa_s->conf->dik) <= DEVICE_IDENTITY_KEY_MAX_LEN) { + p2p.pairing_config.dik_cipher = wpa_s->conf->dik_cipher; + p2p.pairing_config.dik_len = wpabuf_len(wpa_s->conf->dik); + os_memcpy(p2p.pairing_config.dik_data, + wpabuf_head(wpa_s->conf->dik), + p2p.pairing_config.dik_len); + } else { + p2p.pairing_config.dik_cipher = DIRA_CIPHER_VERSION_128; + p2p.pairing_config.dik_len = DEVICE_IDENTITY_KEY_LEN; + if (os_get_random(p2p.pairing_config.dik_data, + p2p.pairing_config.dik_len) < 0) + return -1; + + wpa_s->conf->dik = + wpabuf_alloc_copy(p2p.pairing_config.dik_data, + p2p.pairing_config.dik_len); + if (!wpa_s->conf->dik) + return -1; + + wpa_s->conf->dik_cipher = p2p.pairing_config.dik_cipher; + + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, + "P2P: Failed to update configuration"); + } + + p2p.pairing_config.enable_pairing_setup = wpa_s->p2p_pairing_setup; + p2p.pairing_config.pairing_capable = wpa_s->p2p_pairing_setup; + p2p.pairing_config.enable_pairing_cache = wpa_s->p2p_pairing_cache; + p2p.pairing_config.bootstrap_methods = + wpa_s->conf->p2p_bootstrap_methods; + p2p.pairing_config.pasn_type = wpa_s->conf->p2p_pasn_type; + p2p.comeback_after = wpa_s->conf->p2p_comeback_after; + p2p.reg_info = wpa_s->conf->p2p_reg_info; + p2p.twt_power_mgmt = wpa_s->conf->p2p_twt_power_mgmt; + p2p.chan_switch_req_enable = wpa_s->conf->p2p_chan_switch_req_enable; + global->p2p = p2p_init(&p2p); if (global->p2p == NULL) return -1; global->p2p_init_wpa_s = wpa_s; + for (t_wpa_s = global->ifaces; t_wpa_s; t_wpa_s = t_wpa_s->next) { + if (global->p2p && t_wpa_s->dfs_ap_connected) { + p2p_update_dfs_ap_info( + global->p2p, t_wpa_s->sta_connected_freq, + t_wpa_s->sta_connected_chan_width, + false); + break; + } + } + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) continue; @@ -5111,6 +6138,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) wpa_s->p2p_send_action_work = NULL; } eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_send_bootstrap_comeback, wpa_s, NULL); wpabuf_free(wpa_s->p2p_oob_dev_pw); wpa_s->p2p_oob_dev_pw = NULL; @@ -5188,12 +6216,27 @@ static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_PASN +static int wpas_p2p_config_sae_password(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p || !ssid->sae_password) + return -2; + + return p2p_config_sae_password(p2p, ssid->sae_password); +} +#endif /* CONFIG_PASN */ + + static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, - struct wpa_ssid *ssid, unsigned int pref_freq) + struct wpa_ssid *ssid, unsigned int pref_freq, + bool p2p2, u16 bootstrap, const char *password) { if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; @@ -5211,7 +6254,7 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, ssid ? ssid->ssid_len : 0, wpa_s->p2p_pd_before_go_neg, pref_freq, wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : - 0); + 0, p2p2, bootstrap, password); } @@ -5220,17 +6263,27 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, - struct wpa_ssid *ssid, unsigned int pref_freq) + struct wpa_ssid *ssid, unsigned int pref_freq, + u16 bootstrap, const char *password) { + int ret; + if (persistent_group && wpa_s->conf->persistent_reconnect) persistent_group = 2; - return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, - go_intent, own_interface_addr, force_freq, - persistent_group, ssid ? ssid->ssid : NULL, - ssid ? ssid->ssid_len : 0, pref_freq, - wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : - 0); + ret = p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0, bootstrap, password); + if (!ret) { + wpa_printf(MSG_DEBUG, "P2P: Peer authorized"); +#ifdef CONFIG_NAN_USD + wpas_nan_usd_state_change_notif(wpa_s); +#endif /* CONFIG_NAN_USD */ + } + return ret; } @@ -5350,7 +6403,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (scan_res) wpas_p2p_scan_res_handler(wpa_s, scan_res); - if (wpa_s->p2p_auto_pd) { + if (!wpa_s->p2p2 && wpa_s->p2p_auto_pd) { int join = wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr); if (join == 0 && @@ -5391,15 +6444,22 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, return; } - if (wpa_s->p2p_auto_join) { + if (wpa_s->p2p2 || wpa_s->p2p_auto_join) { int join = wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr); - if (join < 0) { - wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " - "running a GO -> use GO Negotiation"); - wpa_msg_global(wpa_s->p2pdev, MSG_INFO, - P2P_EVENT_FALLBACK_TO_GO_NEG - "reason=peer-not-running-GO"); + + if (wpa_s->p2p2 || join < 0) { + if (join < 0) { + wpa_printf(MSG_DEBUG, + "P2P: Peer was not found to be running a GO -> use GO Negotiation"); + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, + P2P_EVENT_FALLBACK_TO_GO_NEG + "reason=peer-not-running-GO"); + } + + if (wpa_s->p2p2) + wpa_printf(MSG_DEBUG, + "P2P2: Initiate GO negotiation and provisioning using PASN Authentication"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, @@ -5414,7 +6474,11 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, NULL, 0, - is_p2p_allow_6ghz(wpa_s->global->p2p)); + is_p2p_allow_6ghz(wpa_s->global->p2p), + wpa_s->p2p2, wpa_s->p2p_bootstrap, + wpa_s->pending_join_password[0] ? + wpa_s->pending_join_password : NULL, + false); return; } @@ -5568,7 +6632,7 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, { int ret; struct wpa_driver_scan_params params; - struct wpabuf *wps_ie, *ies; + struct wpabuf *wps_ie = NULL, *ies; size_t ielen; int freqs[2] = { 0, 0 }; unsigned int bands; @@ -5588,13 +6652,16 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, wpa_s->p2p_join_ssid_len = 0; } - wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev, - wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0, - NULL); - if (wps_ie == NULL) { - wpas_p2p_scan_res_join(wpa_s, NULL); - return; + if (!wpa_s->p2p2) { + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, + &wpa_s->wps->dev, + wpa_s->wps->uuid, + WPS_REQ_ENROLLEE, 0, NULL); + if (!wps_ie) { + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } } if (!freq) { @@ -5616,14 +6683,21 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, } ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { + + if (wps_ie) + ielen += wpabuf_len(wps_ie); + + ies = wpabuf_alloc(ielen); + if (!ies) { wpabuf_free(wps_ie); wpas_p2p_scan_res_join(wpa_s, NULL); return; } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); + + if (wps_ie) { + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + } bands = wpas_get_bands(wpa_s, freqs); p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); @@ -5706,6 +6780,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, struct wpa_supplicant *group; struct p2p_go_neg_results res; struct wpa_bss *bss; + const u8 *iface_addr = NULL; group = wpas_p2p_get_group_iface(wpa_s, 0, 0); if (group == NULL) @@ -5733,15 +6808,30 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, ETH_ALEN); + if (!is_zero_ether_addr(wpa_s->pending_join_iface_addr)) + iface_addr = wpa_s->pending_join_iface_addr; + + if (wpa_s->pending_join_password[0]) { + if (wpa_s->p2p_mode == WPA_P2P_MODE_WFD_PCC) + res.akmp = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PSK; + else + res.akmp = WPA_KEY_MGMT_SAE; + os_strlcpy(res.sae_password, wpa_s->pending_join_password, + sizeof(res.sae_password)); + os_memset(wpa_s->pending_join_password, 0, + sizeof(wpa_s->pending_join_password)); + } res.wps_method = wpa_s->pending_join_wps_method; + res.p2p2 = wpa_s->p2p2; + res.cipher = WPA_CIPHER_CCMP; + if (freq && ssid && ssid_len) { res.freq = freq; res.ssid_len = ssid_len; os_memcpy(res.ssid, ssid, ssid_len); } else { if (ssid && ssid_len) { - bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, - ssid, ssid_len); + bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len); } else { bss = wpa_bss_get_bssid_latest( wpa_s, wpa_s->pending_join_iface_addr); @@ -5750,6 +6840,8 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, res.freq = bss->freq; res.ssid_len = bss->ssid_len; os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + os_memcpy(res.peer_interface_addr, bss->bssid, + ETH_ALEN); wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", bss->freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); @@ -5768,7 +6860,10 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } - wpas_start_wps_enrollee(group, &res); + if (res.p2p2) + wpas_start_gc(group, &res); + else + wpas_start_wps_enrollee(group, &res); /* * Allow a longer timeout for join-a-running-group than normal 15 @@ -5821,12 +6916,15 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, else ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq); if (!ret) { - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + if (((wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DFS_OFFLOAD) || + wpa_s->assisted_dfs) && ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes)) { /* - * If freq is a DFS channel and DFS is offloaded - * to the driver, allow P2P GO to use it. + * If freq is a DFS channel and either DFS is + * offloaded to the driver or P2P assisted DFS + * case is present, allow P2P GO to use it. */ wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver", @@ -5908,6 +7006,9 @@ static bool is_p2p_6ghz_supported(struct wpa_supplicant *wpa_s, HOSTAPD_MODE_IEEE80211A, true)) return false; + if (wpa_s->p2p2) + return true; + if (!p2p_wfd_enabled(wpa_s->global->p2p)) return false; if (peer_addr && !p2p_peer_wfd_enabled(wpa_s->global->p2p, peer_addr)) @@ -5959,6 +7060,11 @@ static int wpas_p2p_check_6ghz(struct wpa_supplicant *wpa_s, * @group_ssid: Specific Group SSID for join or %NULL if not set * @group_ssid_len: Length of @group_ssid in octets * @allow_6ghz: Allow P2P connection on 6 GHz channels + * @p2p2: Whether device is in P2P R2 mode + * @bootstrap: Requested bootstrap method for pairing in P2P2 + * @password: Password for pairing setup or NULL for opportunistic method + * in P2P2 + * @skip_prov: Connect without provisioning * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified * failure, -2 on failure due to channel not currently available, * -3 if forced channel is not supported @@ -5970,7 +7076,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int persistent_id, int pd, int ht40, int vht, unsigned int vht_chwidth, int he, int edmg, const u8 *group_ssid, size_t group_ssid_len, - bool allow_6ghz) + bool allow_6ghz, bool p2p2, u16 bootstrap, + const char *password, bool skip_prov) { int force_freq = 0, pref_freq = 0; int ret = 0, res; @@ -5990,12 +7097,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return -1; } + wpa_s->p2p2 = p2p2; + wpa_s->p2p_mode = p2p2 ? WPA_P2P_MODE_WFD_R2 : WPA_P2P_MODE_WFD_R1; + if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq)) return -2; os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; + p2p_set_go_role(wpa_s->global->p2p, false); wpa_s->global->p2p_fail_on_wps_complete = 0; wpa_s->global->pending_p2ps_group = 0; wpa_s->global->pending_p2ps_group_freq = 0; @@ -6020,6 +7131,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_go_max_oper_chwidth = vht_chwidth; wpa_s->p2p_go_he = !!he; wpa_s->p2p_go_edmg = !!edmg; + wpa_s->p2p_bootstrap = bootstrap; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); @@ -6038,14 +7150,81 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } else wpa_s->p2p_pin[0] = '\0'; + if (!password) + os_memset(wpa_s->pending_join_password, 0, + sizeof(wpa_s->pending_join_password)); + if (join || auto_join) { u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; if (auth) { +#ifdef CONFIG_PASN + struct wpa_supplicant *ifs; +#endif /* CONFIG_PASN */ + wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " "connect a running group from " MACSTR, MAC2STR(peer_addr)); os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + +#ifdef CONFIG_PASN + if (!wpa_s->p2p2) + return ret; + + /* When P2P connect request is configured with 'p2p2', + * 'join' and 'auth', the local device is expected to + * allow a peer device to join an existing group. Thus, + * having 'persistent_id' set is not allowed. + */ + if (ssid) { + wpa_printf(MSG_DEBUG, + "P2P: No persistent group SSID provided for P2P2 join"); + return -1; + } + + /* Iterate over existing interface and try to find an + * interface that is already hosting a P2P GO. + */ + dl_list_for_each(ifs, &wpa_s->radio->ifaces, + struct wpa_supplicant, radio_list) { + if (!ifs->current_ssid || + ifs->current_ssid->mode != + WPAS_MODE_P2P_GO || + ifs->wpa_state != WPA_COMPLETED) + continue; + + ssid = ifs->current_ssid; + + if (bootstrap == P2P_PBMA_OPPORTUNISTIC && + wpas_p2p_config_sae_password(wpa_s, ssid)) { + ssid = NULL; + continue; + } + + force_freq = ifs->ap_iface->freq; + break; + } + + if (ssid) { + wpa_printf(MSG_DEBUG, + "P2P: Reusing existing GO interface=%s", + ifs->ifname); + if_addr = ifs->own_addr; + } else { + wpa_printf(MSG_DEBUG, + "P2P: No existing GO interface found for P2P2 join"); + return -1; + } + + p2p_set_go_role(wpa_s->global->p2p, true); + return wpas_p2p_auth_go_neg(wpa_s, peer_addr, + wps_method, 15, if_addr, + force_freq, + persistent_group, ssid, + pref_freq, bootstrap, + password); +#else /* CONFIG_PASN */ return ret; +#endif /* CONFIG_PASN */ } os_memcpy(dev_addr, peer_addr, ETH_ALEN); if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr, @@ -6062,6 +7241,20 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_auto_started.usec); } wpa_s->user_initiated_pd = 1; + if (password) + os_strlcpy(wpa_s->pending_join_password, password, + sizeof(wpa_s->pending_join_password)); + + if (skip_prov) { + if (!wpa_s->p2p2) { + wpa_printf(MSG_DEBUG, + "P2P: Join without provisioning not supported"); + return -1; + } + /* Start join operation immediately */ + return wpas_p2p_join_start(wpa_s, 0, group_ssid, + group_ssid_len); + } if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, auto_join, freq, group_ssid, group_ssid_len) < 0) @@ -6105,18 +7298,29 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, go_intent, if_addr, force_freq, persistent_group, ssid, - pref_freq) < 0) + pref_freq, bootstrap, password) < 0) return -1; return ret; } if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent, if_addr, force_freq, - persistent_group, ssid, pref_freq) < 0) { + persistent_group, ssid, pref_freq, p2p2, + bootstrap, password) < 0) { if (wpa_s->create_p2p_iface) wpas_p2p_remove_pending_group_interface(wpa_s); return -1; } + +#ifdef CONFIG_PASN + if (wpa_s->p2p2 && !wpa_s->p2p_pd_before_go_neg) { + int listen_freq = p2p_get_listen_freq(wpa_s->global->p2p, + peer_addr); + + wpas_p2p_initiate_pasn_auth(wpa_s, peer_addr, listen_freq); + } +#endif /* CONFIG_PASN */ + return ret; } @@ -6259,13 +7463,11 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) { - unsigned int r; + unsigned int r, i, size = P2P_MAX_PREF_CHANNELS; + struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS]; + int res = -1; if (!wpa_s->conf->num_p2p_pref_chan && !freq) { - unsigned int i, size = P2P_MAX_PREF_CHANNELS; - struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS]; - int res; - res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, &size, pref_freq_list); if (!res && size > 0 && !is_p2p_allow_6ghz(wpa_s->global->p2p)) @@ -6301,6 +7503,13 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } } + if (freq == 2 || freq == 5 || freq == 6) { + res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, + &size, pref_freq_list); + if (!res && size > 0 && !is_p2p_allow_6ghz(wpa_s->global->p2p)) + size = p2p_remove_6ghz_channels(pref_freq_list, size); + } + if (freq == 2) { wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " "band"); @@ -6310,6 +7519,28 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " "channel: %d MHz", freq); + } else if (!res && size > 0) { + for (i = 0; i < size; i++) { + freq = pref_freq_list[i].freq; + if (is_24ghz_freq(freq) && + p2p_supported_freq(wpa_s->global->p2p, + freq) && + !wpas_p2p_disallowed_freq(wpa_s->global, + freq) && + p2p_pref_freq_allowed(&pref_freq_list[i], + true)) + break; + } + + if (i >= size) { + wpa_printf(MSG_DEBUG, + "P2P: Could not select 2.4 GHz channel for P2P group"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "P2P: Use preferred 2.4 GHz band channel: %d MHz", + freq); } else { if (os_get_random((u8 *) &r, sizeof(r)) < 0) return -1; @@ -6328,6 +7559,27 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " "channel: %d MHz", freq); + } else if (!res && size > 0) { + for (i = 0; i < size; i++) { + freq = pref_freq_list[i].freq; + if (is_5ghz_freq(freq) && + p2p_supported_freq(wpa_s->global->p2p, + freq) && + !wpas_p2p_disallowed_freq(wpa_s->global, + freq) && + p2p_pref_freq_allowed(&pref_freq_list[i], + true)) + break; + } + + if (i >= size) { + wpa_printf(MSG_DEBUG, + "P2P: Could not select 5 GHz channel for P2P group"); + return -1; + } + wpa_printf(MSG_DEBUG, + "P2P: Use preferred 5 GHz band channel: %d MHz", + freq); } else { const int freqs[] = { /* operating class 115 */ @@ -6335,7 +7587,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) /* operating class 124 */ 5745, 5765, 5785, 5805, }; - unsigned int i, num_freqs = ARRAY_SIZE(freqs); + unsigned int num_freqs = ARRAY_SIZE(freqs); if (os_get_random((u8 *) &r, sizeof(r)) < 0) return -1; @@ -6364,6 +7616,36 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } } + if (freq == 6) { + wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 6 GHz band"); + if (!res && size > 0) { + for (i = 0; i < size; i++) { + freq = pref_freq_list[i].freq; + if (is_6ghz_freq(freq) && + p2p_supported_freq(wpa_s->global->p2p, + freq) && + !wpas_p2p_disallowed_freq(wpa_s->global, + freq) && + p2p_pref_freq_allowed(&pref_freq_list[i], + true)) + break; + } + + if (i >= size) { + wpa_printf(MSG_DEBUG, + "P2P: Could not select 6 GHz channel for P2P group"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "P2P: Use preferred 6 GHz band channel: %d MHz", + freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: No preferred 6 GHz channel available"); + } + } + if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) { if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && ieee80211_is_dfs(freq, wpa_s->hw.modes, @@ -6391,6 +7673,10 @@ static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels, int freq) { + if (is_6ghz_freq(freq) && + !is_p2p_6ghz_capable(wpa_s->global->p2p)) + return 0; + if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) && p2p_supported_freq_go(wpa_s->global->p2p, freq) && freq_included(wpa_s, channels, freq)) @@ -6498,6 +7784,7 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, params->max_oper_chwidth = max_oper_chwidth; params->vht_center_freq2 = vht_center_freq2; params->edmg = edmg; + params->p2p2 = wpa_s->p2p2; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); @@ -6886,6 +8173,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, * @vht_chwidth: channel bandwidth for GO operating with VHT support * @edmg: Start GO with EDMG support * @allow_6ghz: Allow P2P group creation on a 6 GHz channel + * @p2p_mode: Operation mode for GO (R1/R2/PCC) * Returns: 0 on success, -1 on failure * * This function creates a new P2P group with the local end as the Group Owner, @@ -6894,7 +8182,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, int max_oper_chwidth, int he, int edmg, - bool allow_6ghz) + bool allow_6ghz, bool p2p2, enum wpa_p2p_mode p2p_mode) { struct p2p_go_neg_results params; int selected_freq = 0; @@ -6906,6 +8194,8 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; + wpa_s->p2p2 = p2p2; + wpa_s->p2p_mode = p2p_mode; /* Make sure we are not running find during connection establishment */ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); @@ -6931,7 +8221,8 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, return -1; if (freq > 0) wpa_s->p2p_go_no_pri_sec_switch = 1; - wpas_start_wps_go(wpa_s, ¶ms, 0); + params.p2p2 = wpa_s->p2p2; + wpas_start_go(wpa_s, ¶ms, 0, p2p_mode); return 0; } @@ -6940,11 +8231,14 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, struct wpa_ssid *params, int addr_allocated, int freq, int force_scan, int retry_limit, - const u8 *go_bssid) + const u8 *go_bssid, bool p2p2, const u8 *pmkid, + const u8 *pmk, size_t pmk_len) { + struct os_reltime now; struct wpa_ssid *ssid; int other_iface_found = 0; struct wpa_supplicant *ifs; + struct rsn_pmksa_cache_entry *entry; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) @@ -6998,6 +8292,39 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, os_memcpy(ssid->bssid, go_bssid, ETH_ALEN); } + if (p2p2) { + ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN; + ssid->auth_alg = WPA_AUTH_ALG_OPEN; + ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT; + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + ssid->disabled = 0; + + if (pmk && pmk_len && pmkid) { + entry = os_zalloc(sizeof(*entry)); + if (!entry) + return -1; + os_memcpy(entry->aa, ssid->bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + entry->pmk_len = pmk_len; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->akmp = WPA_KEY_MGMT_SAE; + os_get_reltime(&now); + entry->expiration = now.sec + 43200; + entry->reauth_time = now.sec + 43200 * 70 / 100; + entry->network_ctx = ssid; + os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN); + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + } else { + os_free(wpa_s->p2p_pmksa_entry); + wpa_s->p2p_pmksa_entry = entry; + } + ssid->pmk_valid = true; + } + wpa_s->current_ssid = ssid; + } + wpa_s->show_group_started = 1; wpa_s->p2p_in_invitation = 1; wpa_s->p2p_retry_limit = retry_limit; @@ -7045,7 +8372,10 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels, int connection_timeout, int force_scan, bool allow_6ghz, int retry_limit, - const u8 *go_bssid) + const u8 *go_bssid, const u8 *dev_addr, + const u8 *pmkid, const u8 *pmk, + size_t pmk_len, bool join, + bool is_invitation) { struct p2p_go_neg_results params; int go = 0, freq; @@ -7099,6 +8429,16 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, } } else if (ssid->mode == WPAS_MODE_INFRA) { freq = neg_freq; + + if (wpa_s->p2p2 && join) { + if (ssid->passphrase) + os_strlcpy(wpa_s->pending_join_password, + ssid->passphrase, + sizeof(wpa_s->pending_join_password)); + return wpas_p2p_join_start(wpa_s, 0, ssid->ssid, + ssid->ssid_len); + } + if (freq <= 0 || !freq_included(wpa_s, channels, freq)) { struct os_reltime now; struct wpa_bss *bss = @@ -7114,7 +8454,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, } return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq, - force_scan, retry_limit, go_bssid); + force_scan, retry_limit, go_bssid, + wpa_s->p2p2, pmkid, pmk, pmk_len); } else { return -1; } @@ -7141,6 +8482,15 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, params.ssid_len = ssid->ssid_len; params.persistent_group = 1; + if (wpa_s->p2p2 && pmk_len && pmk && pmkid) { + os_memcpy(params.peer_device_addr, dev_addr, ETH_ALEN); + os_memcpy(params.pmkid, pmkid, PMKID_LEN); + os_memcpy(params.pmk, pmk, pmk_len); + params.pmk_len = pmk_len; + params.akmp = WPA_KEY_MGMT_SAE; + params.p2p2 = true; + } + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1); if (wpa_s == NULL) return -1; @@ -7148,7 +8498,9 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS); wpa_s->p2p_first_connection_timeout = connection_timeout; - wpas_start_wps_go(wpa_s, ¶ms, 0); + wpa_s->p2p_in_invitation = is_invitation; + params.p2p2 = wpa_s->p2p2; + wpas_start_go(wpa_s, ¶ms, 0, wpa_s->p2p_mode); return 0; } @@ -7171,6 +8523,12 @@ static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies, } wpabuf_free(hapd->p2p_probe_resp_ie); hapd->p2p_probe_resp_ie = proberesp_ies; + + if (wpa_s->p2p2) { + hapd->iconf->peer_to_peer_twt = true; + hapd->iconf->channel_usage = true; + } + } else { wpabuf_free(beacon_ies); wpabuf_free(proberesp_ies); @@ -7229,6 +8587,7 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, cfg->idle_update = wpas_p2p_idle_update; cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start) != 0; + cfg->p2p2 = wpa_s->p2p2; group = p2p_group_init(wpa_s->global->p2p, cfg); if (group == NULL) @@ -7297,7 +8656,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, peer_addr); - wpas_group_formation_completed(wpa_s, 1, 0); + wpas_group_formation_completed(wpa_s, 0, NULL); } @@ -7346,7 +8705,7 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - const char *config_method, + const char *config_method, u16 bootstrap, enum wpas_p2p_prov_disc_use use, struct p2ps_provision *p2ps_prov) { @@ -7368,6 +8727,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, p2ps_prov->status, p2ps_prov->info); config_methods = 0; + } else if (bootstrap) { + wpa_s->p2p2 = true; + config_methods = 0; + wpa_s->p2p_bootstrap = bootstrap; + p2p_set_req_bootstrap_method(wpa_s->global->p2p, peer_addr, + bootstrap); } else if (os_strncmp(config_method, "display", 7) == 0) config_methods = WPS_CONFIG_DISPLAY; else if (os_strncmp(config_method, "keypad", 6) == 0) @@ -7678,7 +9043,7 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, int max_chwidth, - int pref_freq, int he, int edmg, bool allow_6ghz) + int pref_freq, int he, int edmg, bool allow_6ghz, bool p2p2) { enum p2p_invite_role role; u8 *bssid = NULL; @@ -7697,6 +9062,22 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, else os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + if (wpa_s->global->p2p && p2p2 && !ssid && peer_addr) { + int dik_id; + + dik_id = p2p_get_dik_id(wpa_s->global->p2p, peer_addr); + ssid = wpa_config_get_network_with_dik_id(wpa_s->conf, dik_id); + if (!ssid) { + wpa_printf(MSG_DEBUG, + "P2P: Could not find SSID for P2P2 peer " + MACSTR, MAC2STR(peer_addr)); + return -1; + } + } + + if (!ssid) + return -1; + wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; @@ -7704,6 +9085,7 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_go_max_oper_chwidth = max_chwidth; wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; wpa_s->p2p_go_edmg = !!edmg; + wpa_s->p2p2 = p2p2; if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_GO; if (peer_addr == NULL) { @@ -7726,7 +9108,8 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, bssid = wpa_s->own_addr; } else { role = P2P_INVITE_ROLE_CLIENT; - peer_addr = ssid->bssid; + if (!wpa_s->p2p2) + peer_addr = ssid->bssid; } wpa_s->pending_invite_ssid_id = ssid->id; @@ -7757,9 +9140,24 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, */ wpas_p2p_stop_find_oper(wpa_s); +#ifdef CONFIG_PASN + if (p2p2) { + if (peer_addr && + wpas_p2p_initiate_pasn_verify(wpa_s, peer_addr, role, bssid, + ssid->ssid, ssid->ssid_len, + force_freq, go_dev_addr, + pref_freq) < 0) { + if (wpa_s->create_p2p_iface) + wpas_p2p_remove_pending_group_interface(wpa_s); + return -1; + } + return 0; + } +#endif /* CONFIG_PASN */ + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, - 1, pref_freq, -1); + 1, pref_freq, -1, false); } @@ -7843,15 +9241,16 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, ssid->ssid, ssid->ssid_len, force_freq, - go_dev_addr, persistent, pref_freq, -1); + go_dev_addr, persistent, pref_freq, -1, false); } void wpas_p2p_completed(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *bssid; u8 go_dev_addr[ETH_ALEN]; - int persistent; + int persistent, dik_id; int freq; u8 ip[3 * 4], *ip_ptr = NULL; char ip_addr[100]; @@ -7864,6 +9263,11 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) if (!wpa_s->show_group_started || !ssid) return; + if (wpa_s->go_params) + bssid = wpa_s->go_params->peer_interface_addr; + else + bssid = wpa_s->bssid; + wpa_s->show_group_started = 0; if (!wpa_s->p2p_go_group_formation_completed && wpa_s->global->p2p_group_formation == wpa_s) { @@ -7910,9 +9314,11 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ssid->passphrase, go_dev_addr, persistent, ip_addr); - if (persistent) + if (persistent) { + dik_id = wpas_p2p_store_go_identity(wpa_s, go_dev_addr, bssid); wpas_p2p_store_persistent_group(wpa_s->p2pdev, - ssid, go_dev_addr); + ssid, go_dev_addr, dik_id); + } wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr); } @@ -8378,7 +9784,7 @@ static int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) "session overlap"); if (wpa_s != wpa_s->p2pdev) wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP); - wpas_p2p_group_formation_failed(wpa_s, 0); + wpas_p2p_group_formation_failed(wpa_s, 0, "WPS PBC session overlap"); return 1; } @@ -8396,6 +9802,7 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, struct p2p_channels chan, cli_chan; struct wpa_used_freq_data *freqs = NULL; unsigned int num = wpa_s->num_multichan_concurrent; + struct wpa_supplicant *p2p_wpa_s; if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) return; @@ -8406,9 +9813,12 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s, num = get_shared_radio_freqs_data(wpa_s, freqs, num, false); + p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ? + wpa_s->global->p2p_init_wpa_s : wpa_s; + os_memset(&chan, 0, sizeof(chan)); os_memset(&cli_chan, 0, sizeof(cli_chan)); - if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan, + if (wpas_p2p_setup_channels(p2p_wpa_s, &chan, &cli_chan, is_p2p_6ghz_disabled(wpa_s->global->p2p))) { wpa_printf(MSG_ERROR, "P2P: Failed to update supported " "channel list"); @@ -8487,7 +9897,8 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); if (wpa_s->p2p_in_provisioning) { - wpas_group_formation_completed(wpa_s, 0, 0); + wpas_group_formation_completed(wpa_s, 0, + "Canceled"); break; } wpas_p2p_group_delete(wpa_s, @@ -8497,7 +9908,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling", wpa_s->ifname); found = 1; - wpas_p2p_group_formation_failed(wpa_s, 0); + wpas_p2p_group_formation_failed(wpa_s, 0, "Canceled"); break; } } @@ -8671,9 +10082,57 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, } +static int wpas_p2p_store_client_identity(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + u8 cipher; + size_t dik_len; + const u8 *dik_data; + const u8 *pmk, *pmkid; + size_t pmk_len; + u8 iface_addr[ETH_ALEN]; + struct hostapd_data *hapd; + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s; + + if (!wpa_s->p2p2 || !wpa_s->ap_iface) + return 0; + + hapd = wpa_s->ap_iface->bss[0]; + if (!hapd) + return 0; + + if (p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, addr, + &dik_data, &dik_len, &cipher)) + return 0; + + wpa_printf(MSG_DEBUG, "P2P: Fetch PMK from client (Device Addr " MACSTR + ")", MAC2STR(addr)); + if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, addr, &pmk, &pmk_len, + &pmkid)) { + if (p2p_get_interface_addr(p2p_wpa_s->global->p2p, addr, + iface_addr)) + return 0; + wpa_printf(MSG_DEBUG, + "P2P: Fetch PMK from client (Interface Addr " MACSTR + ")", MAC2STR(iface_addr)); + if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, iface_addr, &pmk, + &pmk_len, &pmkid)) + return 0; + } + + wpa_printf(MSG_DEBUG, + "P2P: Storing device identity of client (Device Addr " + MACSTR ")", MAC2STR(addr)); + return wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len, + pmk, pmk_len, pmkid); +} + + void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { + int dik_id; + if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL) > 0) { /* @@ -8698,7 +10157,7 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, */ if (wpa_s->global->p2p) p2p_wps_success_cb(wpa_s->global->p2p, addr); - wpas_group_formation_completed(wpa_s, 1, 0); + wpas_group_formation_completed(wpa_s, 0, NULL); } } if (!wpa_s->p2p_go_group_formation_completed) { @@ -8712,7 +10171,9 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_go_wait_client.sec = 0; if (addr == NULL) return; - wpas_p2p_add_persistent_group_client(wpa_s, addr); + + dik_id = wpas_p2p_store_client_identity(wpa_s, addr); + wpas_p2p_add_persistent_group_client(wpa_s, addr, dik_id); } @@ -8740,7 +10201,8 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p)); + NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p), + wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL, false); return ret; } @@ -9208,6 +10670,8 @@ struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, return NULL; } + wpa_s->p2p2 = false; + if (cli_freq == 0) { wsc = wps_build_nfc_handover_req_p2p( wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey); @@ -9237,6 +10701,8 @@ struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return NULL; + wpa_s->p2p2 = false; + if (cli_freq == 0) { wsc = wps_build_nfc_handover_sel_p2p( wpa_s->parent->wps, @@ -9278,7 +10744,8 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, params->go_ssid_len ? params->go_ssid : NULL, - params->go_ssid_len, false); + params->go_ssid_len, false, wpa_s->p2p2, + wpa_s->p2p_bootstrap, NULL, false); } @@ -9342,7 +10809,7 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, ssid->ssid, ssid->ssid_len, ssid->frequency, wpa_s->global->p2p_dev_addr, persistent, 0, - wpa_s->p2pdev->p2p_oob_dev_pw_id); + wpa_s->p2pdev->p2p_oob_dev_pw_id, false); } @@ -9357,7 +10824,8 @@ static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0, false); + NULL, 0, false, wpa_s->p2p2, + wpa_s->p2p_bootstrap, NULL, false); } @@ -9374,7 +10842,8 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, wpa_s->p2p_go_he, wpa_s->p2p_go_edmg, - NULL, 0, false); + NULL, 0, false, wpa_s->p2p2, + wpa_s->p2p_bootstrap, NULL, false); if (res) return res; @@ -9502,6 +10971,8 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); wpa_s->p2p_peer_oob_pk_hash_known = 1; + wpa_s->p2p2 = false; + if (tag) { if (id < 0x10) { wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid " @@ -10242,3 +11713,158 @@ int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s) wpa_s->p2p_lo_started = 0; return ret; } + + +struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s, + const char *service_name) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return NULL; + p2p_usd_service_hash(p2p, service_name); + return p2p_usd_elems(p2p); +} + + +void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return; + p2p_process_usd_elems(p2p, buf, buf_len, peer_addr, freq); +} + + +#ifdef CONFIG_PASN + +int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return -2; + + wpa_s->p2p2 = true; + if (wpa_s->p2p_mode == WPA_P2P_MODE_WFD_R1) + wpa_s->p2p_mode = WPA_P2P_MODE_WFD_R2; + return p2p_pasn_auth_rx(p2p, mgmt, len, freq); +} + + +int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + struct wpa_p2p_pasn_auth_work *awork; + + if (!wpa_s->p2p_pasn_auth_work) + return -1; + awork = wpa_s->p2p_pasn_auth_work->ctx; + + return p2p_pasn_auth_tx_status(p2p, data, data_len, acked, + awork->verify); +} + + +#ifdef CONFIG_TESTING_OPTIONS +int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk, + size_t *ptk_len) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return -2; + return p2p_pasn_get_ptk(p2p, ptk, ptk_len); +} +#endif /* CONFIG_TESTING_OPTIONS */ + +#endif /* CONFIG_PASN */ + + +int wpas_p2p_get_dira(struct wpa_supplicant *wpa_s, char *buf, size_t buf_len) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return 0; + return p2p_get_dira_info(p2p, buf, buf_len); +} + + +int wpas_p2p_validate_dira(struct wpa_supplicant *wpa_s, const u8 *addr, + u8 cipher, const u8 *nonce, const u8 *tag) +{ + if (cipher != DIRA_CIPHER_VERSION_128) { + wpa_printf(MSG_INFO, "P2P2: Unsupported DIRA cipher version %d", + cipher); + return 0; + } + + return wpas_validate_dira(wpa_s, addr, nonce, tag); +} + + +void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s) +{ + os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); + p2p_set_dev_addr(wpa_s->global->p2p, wpa_s->own_addr); +} + + +static void wpas_p2p_set_disabled(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "P2P: Disabled"); + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || + wpa_s->p2p_mgmt) { + wpa_printf(MSG_DEBUG, + "P2P: Disable only implemented on parent interface"); + } else { + if (wpa_s->global->p2p_init_wpa_s && + wpa_s->global->p2p && + wpa_s->global->p2p_init_wpa_s->parent == wpa_s) { + wpa_supplicant_remove_iface( + wpa_s->global, wpa_s->global->p2p_init_wpa_s, + 0); + } else { + wpa_printf(MSG_DEBUG, "P2P: Not global P2P owner"); + } + } +} + + +static void wpas_p2p_set_enabled(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "P2P: Enabled"); + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || + wpa_s->p2p_mgmt) { + wpa_printf(MSG_INFO, + "P2P: Enabling only implemented on parent interface"); + } else if (!wpa_s->global->p2p && + !wpa_s->global->p2p_disabled) { + if (wpas_p2p_add_p2pdev_interface( + wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) { + wpa_printf(MSG_DEBUG, + "P2P: Failed to enable P2P Device interface"); + } + } else { + wpa_printf(MSG_DEBUG, + "P2P: Not enabling, global P2P configured or globally disabled"); + } +} + + +void wpas_p2p_disabled_changed(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->conf->p2p_disabled) + wpas_p2p_set_disabled(wpa_s); + else + wpas_p2p_set_enabled(wpa_s); +} diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h index d71f7701c159..bf145de89a9b 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -39,12 +39,14 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int persistent_id, int pd, int ht40, int vht, unsigned int vht_chwidth, int he, int edmg, const u8 *group_ssid, size_t group_ssid_len, - bool allow_6ghz); + bool allow_6ghz, bool p2p2, u16 bootstrap, + const char *password, bool skip_prov); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth, int he, int edmg, bool allow_6ghz); + int max_oper_chwidth, int he, int edmg, bool allow_6ghz, + bool p2p2, enum wpa_p2p_mode p2p_mode); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, @@ -53,7 +55,10 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, const struct p2p_channels *channels, int connection_timeout, int force_scan, bool allow_6ghz, int retry_limit, - const u8 *go_bssid); + const u8 *go_bssid, const u8 *dev_addr, + const u8 *pmkid, const u8 *pmk, + size_t pmk_len, bool join, + bool is_invitation); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); enum wpas_p2p_prov_disc_use { @@ -63,7 +68,7 @@ enum wpas_p2p_prov_disc_use { WPAS_P2P_PD_FOR_ASP }; int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - const char *config_method, + const char *config_method, u16 bootstrap, enum wpas_p2p_prov_disc_use use, struct p2ps_provision *p2ps_prov); void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, @@ -121,7 +126,8 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, int max_chwidth, - int pref_freq, int he, int edmg, bool allow_6ghz); + int pref_freq, int he, int edmg, bool allow_6ghz, + bool p2p2); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, const u8 *peer_addr, const u8 *go_dev_addr, bool allow_6ghz); @@ -178,6 +184,12 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled); void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx); int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params); +void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq); +int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked); +int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s); #ifdef CONFIG_P2P @@ -225,6 +237,18 @@ int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int count); int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s); +struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s, + const char *service_name); +void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s); +int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq); +int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk, + size_t *ptk_len); +int wpas_p2p_get_dira(struct wpa_supplicant *wpa_s, char *buf, size_t buf_len); +int wpas_p2p_validate_dira(struct wpa_supplicant *wpa_s, const u8 *addr, + u8 cipher, const u8 *nonce, const u8 *tag); +void wpas_p2p_disabled_changed(struct wpa_supplicant *wpa_s); #else /* CONFIG_P2P */ @@ -351,6 +375,24 @@ static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, return 0; } +static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s, + const char *service_name) +{ + return NULL; +} + +static inline void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s) +{ +} + +#ifdef CONFIG_TESTING_OPTIONS +static inline int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, + const u8 **ptk, size_t *ptk_len) +{ + return 0; +} +#endif /* CONFIG_TESTING_OPTIONS */ + #endif /* CONFIG_P2P */ #endif /* P2P_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/pasn_supplicant.c b/contrib/wpa/wpa_supplicant/pasn_supplicant.c index 1bb38f73dcf6..d9295a348843 100644 --- a/contrib/wpa/wpa_supplicant/pasn_supplicant.c +++ b/contrib/wpa/wpa_supplicant/pasn_supplicant.c @@ -2,6 +2,7 @@ * wpa_supplicant - PASN processing * * Copyright (C) 2019 Intel Corporation + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,12 +20,14 @@ #include "crypto/random.h" #include "eap_common/eap_defs.h" #include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" #include "rsn_supp/pmksa_cache.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "bss.h" #include "scan.h" #include "config.h" +#include "sme.h" static const int dot11RSNAConfigPMKLifetime = 43200; @@ -36,9 +39,38 @@ struct wpa_pasn_auth_work { u16 group; int network_id; struct wpabuf *comeback; + unsigned int auth_alg; + int group_cipher; + int group_mgmt_cipher; +#ifdef CONFIG_ENC_ASSOC + u16 rsn_capab; + u8 *rsnxe_data; + bool is_ml_peer; +#endif /* CONFIG_ENC_ASSOC */ }; +static void wpas_pasn_free_peer_password(struct pasn_peer *peer) +{ + str_clear_free(peer->password); + peer->password = NULL; +} + + +static void wpas_pasn_free_peer_comeback(struct pasn_peer *peer) +{ + os_free(peer->comeback); + peer->comeback = NULL; +} + + +static void wpas_pasn_free_peer(struct pasn_peer *peer) +{ + wpas_pasn_free_peer_password(peer); + wpas_pasn_free_peer_comeback(peer); +} + + static int wpas_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len, int noack, unsigned int freq, unsigned int wait) { @@ -52,6 +84,10 @@ static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork) { wpabuf_free(awork->comeback); awork->comeback = NULL; +#ifdef CONFIG_ENC_ASSOC + os_free(awork->rsnxe_data); + awork->rsnxe_data = NULL; +#endif /* CONFIG_ENC_ASSOC */ os_free(awork); } @@ -127,11 +163,55 @@ wpas_pasn_sae_derive_pt(struct wpa_ssid *ssid, int group) } return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, - (const u8 *) password, os_strlen(password), - ssid->sae_password_id); + (const u8 *) password, os_strlen(password), + (const u8 *) ssid->sae_password_id, + ssid->sae_password_id ? + os_strlen(ssid->sae_password_id) : 0); } +#ifdef CONFIG_ENC_ASSOC +struct sae_pt * +wpas_pasn_sae_derive_pt_for_eppke(struct wpa_ssid *ssid, int group) +{ + const char *password = ssid->sae_password; + int groups[2] = { group, 0 }; + const u8 *password_id = NULL; + size_t password_id_len = 0; + + if (!password) + password = ssid->passphrase; + + if (!password) { + wpa_printf(MSG_DEBUG, "EPPKE: SAE without a password"); + return NULL; + } + + /* Prefer an alternative (changing) password identifier if available */ + if (ssid->alt_sae_password_ids && ssid->alt_sae_password_ids->num) { + unsigned int idx = + os_random() % ssid->alt_sae_password_ids->num; + struct wpabuf *id = ssid->alt_sae_password_ids->buf[idx]; + + password_id = wpabuf_head(id); + password_id_len = wpabuf_len(id); + wpa_hexdump(MSG_DEBUG, + "EPPKE: Prepare PT for alternative password ID", + password_id, password_id_len); + ssid->alt_sae_passwords_ids_idx = idx; + ssid->alt_sae_passwords_ids_used = true; + } else if (ssid->sae_password_id) { + password_id = (const u8 *) ssid->sae_password_id; + password_id_len = os_strlen(ssid->sae_password_id); + } + + return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, + (const u8 *) password, os_strlen(password), + password_id, password_id_len); +} +#endif /* CONFIG_ENC_ASSOC */ + + static int wpas_pasn_sae_setup_pt(struct wpa_ssid *ssid, int group) { if (!ssid->sae_password && !ssid->passphrase) { @@ -150,31 +230,81 @@ static int wpas_pasn_sae_setup_pt(struct wpa_ssid *ssid, int group) #endif /* CONFIG_SAE */ +int wpas_pasn_get_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct pasn_data *pasn) +{ + static const int default_groups[] = { 19, 20, 21, 0 }; + const int *groups; + unsigned int i, j; + + if (ssid && ssid->pasn_groups) + groups = ssid->pasn_groups; + else if (wpa_s->conf->pasn_groups) + groups = wpa_s->conf->pasn_groups; + else + groups = default_groups; + + for (i = 0; groups[i]; i++) { + bool rejected = false; + bool ap_supported = true; + + if (!dragonfly_suitable_group(groups[i], 1)) + continue; + + if (!pasn) + return groups[i]; + + /* Skip groups already rejected in this session */ + for (j = 0; j < pasn->rejected_group_idx; j++) { + if (groups[i] == pasn->rejected_groups[j]) { + rejected = true; + break; + } + } + if (rejected) + continue; + + /* Take intersection with AP's supported groups */ + if (pasn->ap_supported_group_idx > 0) { + ap_supported = false; + for (j = 0; j < pasn->ap_supported_group_idx; j++) { + if (groups[i] == pasn->ap_supported_groups[j]) { + ap_supported = true; + break; + } + } + } + if (ap_supported) + return groups[i]; + } + + /* pasn_groups configured but no suitable group found - failure */ + return 0; +} + + static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s, - struct pasn_peer *peer) + struct pasn_peer *peer, + struct wpa_bss *bss, + struct wpa_ssid *ssid) { int ret; const u8 *rsne, *rsnxe; - struct wpa_bss *bss; struct wpa_ie_data rsne_data; int sel, key_mgmt, pairwise_cipher; - int network_id = 0, group = 19; - struct wpa_ssid *ssid = NULL; - size_t ssid_str_len = 0; - const u8 *ssid_str = NULL; - const u8 *peer_addr = peer->peer_addr; + int group; - bss = wpa_bss_get_bssid(wpa_s, peer_addr); - if (!bss) { - wpa_supplicant_update_scan_results(wpa_s, peer_addr); - bss = wpa_bss_get_bssid(wpa_s, peer_addr); - if (!bss) { - wpa_printf(MSG_DEBUG, "PASN: BSS not found"); - return -1; - } + group = wpas_pasn_get_group(wpa_s, ssid, NULL); + + if (!group) { + wpa_printf(MSG_INFO, + "PASN: No suitable group found; cannot start authentication"); + return -1; } - rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + wpa_printf(MSG_DEBUG, "PASN: Selected group %d", group); + + rsne = wpa_bss_get_rsne(wpa_s, bss, NULL, false); if (!rsne) { wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE"); return -1; @@ -186,24 +316,13 @@ static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s, return -1; } - rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); - ssid_str_len = bss->ssid_len; - ssid_str = bss->ssid; - - /* Get the network configuration based on the obtained SSID */ - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { - if (!wpas_network_disabled(wpa_s, ssid) && - ssid_str_len == ssid->ssid_len && - os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0) - break; - } - - if (ssid) - network_id = ssid->id; sel = rsne_data.pairwise_cipher; - if (ssid && ssid->pairwise_cipher) + if (peer->cipher && peer->cipher != WPA_CIPHER_NONE) + sel &= peer->cipher; + else if (ssid && !ssid->temporary && ssid->pairwise_cipher) sel &= ssid->pairwise_cipher; wpa_printf(MSG_DEBUG, "PASN: peer pairwise 0x%x, select 0x%x", @@ -217,7 +336,9 @@ static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s, } sel = rsne_data.key_mgmt; - if (ssid && ssid->key_mgmt) + if (peer->akmp && peer->akmp != WPA_KEY_MGMT_NONE) + sel &= peer->akmp; + else if (ssid && !ssid->temporary && ssid->key_mgmt) sel &= ssid->key_mgmt; wpa_printf(MSG_DEBUG, "PASN: peer AKMP 0x%x, select 0x%x", @@ -251,13 +372,13 @@ static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s, #endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE - } else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) && + } else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) && ssid && (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) && (wpas_pasn_sae_setup_pt(ssid, group) == 0)) { key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY; wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)"); - } else if ((sel & WPA_KEY_MGMT_SAE) && + } else if ((sel & WPA_KEY_MGMT_SAE) && ssid && (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) && (wpas_pasn_sae_setup_pt(ssid, group) == 0)) { @@ -300,7 +421,8 @@ static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s, peer->akmp = key_mgmt; peer->cipher = pairwise_cipher; - peer->network_id = network_id; + if (ssid) + peer->network_id = ssid->id; peer->group = group; return 0; } @@ -339,17 +461,87 @@ static int wpas_pasn_set_keys_from_cache(struct wpa_supplicant *wpa_s, } +static struct wpa_ssid * +wpas_pasn_add_temporary_network(struct wpa_supplicant *wpa_s, + const struct wpa_bss *bss, const char *password) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (!ssid) { + wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SSID block"); + return NULL; + } + + ssid->ssid = os_memdup(bss->ssid, bss->ssid_len); + if (!ssid->ssid) + return NULL; + + ssid->ssid_len = bss->ssid_len; + ssid->passphrase = os_strdup(password); + if (!ssid->passphrase) { + wpa_config_free_ssid(ssid); + wpa_printf(MSG_DEBUG, "PASN: Failed to copy password"); + return NULL; + } + + ssid->temporary = true; + wpa_printf(MSG_DEBUG, "PASN: Created temporary network block for " + MACSTR, MAC2STR(bss->bssid)); + + return ssid; +} + + +static struct wpa_bss * wpas_pasn_get_bss(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + struct wpa_bss *bss; + + bss = wpa_bss_get_bssid(wpa_s, peer_addr); + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s, peer_addr); + bss = wpa_bss_get_bssid(wpa_s, peer_addr); + } + + return bss; +} + + +static struct wpa_ssid * wpas_pasn_get_network(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + size_t ssid_str_len; + const u8 *ssid_str; + struct wpa_ssid *ssid; + + ssid_str_len = bss->ssid_len; + ssid_str = bss->ssid; + + /* Get the network configuration based on the obtained SSID */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid_str_len == ssid->ssid_len && + os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0) + break; + } + + return ssid; +} + + static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s, struct pasn_auth *pasn_params) { struct pasn_peer *peer; - u8 comeback_len = 0; - const u8 *comeback = NULL; + struct wpa_ssid *ssid; if (!pasn_params) return; while (wpa_s->pasn_count < pasn_params->num_peers) { + struct wpa_bss *bss; + bool check_cache = true; + peer = &pasn_params->peer[wpa_s->pasn_count]; if (ether_addr_equal(wpa_s->bssid, peer->peer_addr)) { @@ -360,7 +552,39 @@ static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s, continue; } - if (wpas_pasn_set_keys_from_cache(wpa_s, peer->own_addr, + bss = wpas_pasn_get_bss(wpa_s, peer->peer_addr); + if (!bss) { + wpa_printf(MSG_DEBUG, "PASN: BSS not found"); + peer->status = PASN_STATUS_FAILURE; + wpa_s->pasn_count++; + continue; + } + + ssid = wpas_pasn_get_network(wpa_s, bss); + if (peer->password && peer->akmp && + peer->akmp != WPA_KEY_MGMT_NONE) { + ssid = wpas_pasn_add_temporary_network(wpa_s, bss, + peer->password); + + if (!ssid) { + wpa_printf(MSG_DEBUG, + "PASN: Failed to create temporary network"); + return; + } + peer->temporary_network = true; + } + + if (ssid && ssid->temporary) + check_cache = false; + + if (wpas_pasn_get_params_from_bss(wpa_s, peer, bss, ssid)) { + peer->status = PASN_STATUS_FAILURE; + wpa_s->pasn_count++; + continue; + } + + if (check_cache && + wpas_pasn_set_keys_from_cache(wpa_s, peer->own_addr, peer->peer_addr, peer->cipher, peer->akmp) == 0) { @@ -369,19 +593,21 @@ static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s, continue; } - if (wpas_pasn_get_params_from_bss(wpa_s, peer)) { - peer->status = PASN_STATUS_FAILURE; - wpa_s->pasn_count++; - continue; - } - if (wpas_pasn_auth_start(wpa_s, peer->own_addr, peer->peer_addr, peer->akmp, peer->cipher, peer->group, peer->network_id, - comeback, comeback_len)) { + peer->comeback, peer->comeback_len, + WLAN_AUTH_PASN, 0, 0, 0, NULL, + false)) { peer->status = PASN_STATUS_FAILURE; + wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR + " akmp=%s, status=%u", + MAC2STR(peer->peer_addr), + wpa_key_mgmt_txt(peer->akmp, WPA_PROTO_RSN), + peer->status); wpa_s->pasn_count++; + wpas_pasn_free_peer(peer); continue; } wpa_printf(MSG_DEBUG, "PASN: Sent PASN auth start for " MACSTR, @@ -390,8 +616,27 @@ static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s, } if (wpa_s->pasn_count == pasn_params->num_peers) { + unsigned int i; + wpa_drv_send_pasn_resp(wpa_s, pasn_params); wpa_printf(MSG_DEBUG, "PASN: Response sent"); + for (i = 0; i < pasn_params->num_peers; i++) { + peer = &pasn_params->peer[i]; + wpas_pasn_free_peer(peer); + + if (peer->temporary_network) { + ssid = wpa_config_get_network(wpa_s->conf, + peer->network_id); + + if (ssid && ssid->temporary) { + wpa_config_remove_network( + wpa_s->conf, peer->network_id); + wpa_printf(MSG_DEBUG, + "PASN: Remove temporary network block of " + MACSTR, MAC2STR(peer->peer_addr)); + } + } + } os_free(wpa_s->pasn_params); wpa_s->pasn_params = NULL; } @@ -422,6 +667,7 @@ static void wpas_pasn_delete_peers(struct wpa_supplicant *wpa_s, peer = &pasn_params->peer[i]; ptksa_cache_flush(wpa_s->ptksa, peer->peer_addr, WPA_CIPHER_NONE); + wpas_pasn_free_peer_password(peer); } } @@ -461,26 +707,48 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s) static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int akmp, - int cipher) + int cipher, int auth_alg, + int group_cipher, + int group_mgmt_cipher) { struct wpa_bss *bss; const u8 *rsne; struct wpa_ie_data rsne_data; int ret; - if (ether_addr_equal(wpa_s->bssid, peer_addr)) { + if (auth_alg != WLAN_AUTH_EPPKE && + ether_addr_equal(wpa_s->bssid, peer_addr)) { wpa_printf(MSG_DEBUG, "PASN: Not doing authentication with current BSS"); return NULL; } - bss = wpa_bss_get_bssid_latest(wpa_s, peer_addr); + if (auth_alg == WLAN_AUTH_EPPKE) { +#if defined(CONFIG_SME) && defined(CONFIG_SAE) + /* EPPKE processing can reach here only when external + * authentication is used. + * + * In this flow, peer_addr is the peer MLD address for MLO. + * However, wpa_bss_get_bssid_latest() matches a link BSSID + * entry in the BSS table. Use the link BSSID saved by SME + * in ext_auth_bssid for BSS lookup. + */ + bss = wpa_bss_get_bssid_latest(wpa_s, + wpa_s->sme.ext_auth_bssid); +#else /* CONFIG_SME && CONFIG_SAE */ + wpa_printf(MSG_ERROR, + "EPPKE ext-auth requires CONFIG_SME and CONFIG_SAE"); + return NULL; +#endif /* CONFIG_SME && CONFIG_SAE */ + } else { + bss = wpa_bss_get_bssid_latest(wpa_s, peer_addr); + } if (!bss) { wpa_printf(MSG_DEBUG, "PASN: BSS not found"); return NULL; } - rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsne = wpa_bss_get_rsne(wpa_s, bss, NULL, false); if (!rsne) { wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE"); return NULL; @@ -499,10 +767,62 @@ static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s, return NULL; } +#ifdef CONFIG_ENC_ASSOC + if (auth_alg == WLAN_AUTH_EPPKE) { + if (group_cipher & + !(rsne_data.group_cipher & group_cipher)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support requested group cipher"); + return NULL; + } + if (group_mgmt_cipher && + !(rsne_data.mgmt_group_cipher & group_mgmt_cipher)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support requested group mgmt cipher"); + return NULL; + } + } +#endif /* CONFIG_ENC_ASSOC */ + return bss; } +#ifdef CONFIG_ENC_ASSOC +/* + * Build RSNE for EPPKE in SME-in-driver mode. + */ +static int wpas_eppke_set_rsne(struct wpa_supplicant *wpa_s, + struct pasn_data *pasn, + struct wpa_pasn_auth_work *awork) +{ + u8 rsne[257]; + int rsne_len; + + rsne_len = wpa_external_auth_add_rsne(rsne, sizeof(rsne), + awork->akmp, awork->cipher, + awork->group_cipher, + awork->group_mgmt_cipher, + awork->rsn_capab, + sme_get_ext_auth_pmkid(wpa_s)); + if (rsne_len < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to build RSNE"); + return -1; + } + + pasn_set_rsne(pasn, rsne); + if (!pasn->rsn_ie) + return -1; + + wpa_printf(MSG_DEBUG, + "EPPKE: RSNE for ext-auth (group=0x%x mgmt=0x%x capab=0x%x)", + awork->group_cipher, awork->group_mgmt_cipher, + awork->rsn_capab); + return 0; +} +#endif /* CONFIG_ENC_ASSOC */ + + static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; @@ -515,7 +835,7 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) const u8 *indic; u16 fils_info; #endif /* CONFIG_FILS */ - u16 capab = 0; + u64 capab = 0; bool derive_kdk; int ret; @@ -538,7 +858,9 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) * established. */ bss = wpas_pasn_allowed(wpa_s, awork->peer_addr, awork->akmp, - awork->cipher); + awork->cipher, awork->auth_alg, + awork->group_cipher, + awork->group_mgmt_cipher); if (!bss) { wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed"); goto fail; @@ -550,7 +872,8 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) goto fail; } - rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + /* Use the RSNXOE, if it was included, for actual AP capability check */ + rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) && ieee802_11_rsnx_capab(rsnxe, @@ -576,25 +899,118 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) pasn->corrupt_mic = wpa_s->conf->pasn_corrupt_mic; #endif /* CONFIG_TESTING_OPTIONS */ - capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + if (wpa_key_mgmt_sae(awork->akmp)) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA) capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); - if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA) - capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); - pasn_set_rsnxe_caps(pasn, capab); - pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA) { + /* + * URNM_MFPR_X20 is a subset of URNM_MFPR which excludes 20 MHz + * bandwidth from mandating protected Management frames. Set + * URNM_MFPR only when URNM_MFPR_X20 is not set. + */ + if (wpa_s->disable_urnm_mfpr) { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR, 0); + } else { + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR, 1); + } + if (wpa_s->urnm_mfpr_x20) { + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR_X20, + 1); + } else { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR_X20, + 0); + } + } + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) && + ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU)) + capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); ssid = wpa_config_get_network(wpa_s->conf, awork->network_id); +#ifdef CONFIG_ENC_ASSOC + if (awork->auth_alg == WLAN_AUTH_EPPKE) { + if (!ssid) { + wpa_printf(MSG_DEBUG, + "EPPKE: No network profile found"); + goto fail; + } + if (!ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_KEK_IN_PASN)) + { + wpa_printf(MSG_INFO, + "EPPKE: KEK_IN_PASN not set in AP RSNXE"); + goto fail; + } + if (!ieee802_11_rsnx_capab(rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) { + wpa_printf(MSG_INFO, + "EPPKE: ASSOC_FRAME_ENCRYPTION not set in AP RSNXE"); + goto fail; + } + if (awork->akmp == WPA_KEY_MGMT_EPPKE && + !ieee802_11_rsnx_capab(rsnxe, + WLAN_RSNX_CAPAB_UNAUTH_EPPKE)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support unauthenticated EPPKE"); + goto fail; + } + if (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) { + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); + pasn->derive_kek = true; +#ifdef CONFIG_SAE + /* + * Advertise support for changing SAE password + * identifiers if configured per network profile. + */ + if (ssid && ssid->sae_password_id && + ssid->sae_password_id_change && + wpa_key_mgmt_sae_ext_key(awork->akmp)) { + capab |= BIT_ULL( + WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE); + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_SAE_PW_ID_CHANGE, 1); + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_PMKSA_PRIVACY + if ((wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_PMKSA_PRIVACY) && + ssid->pmksa_privacy && + ieee802_11_rsnx_capab( + rsnxe, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) + capab |= BIT( + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); +#endif /* CONFIG_PMKSA_PRIVACY */ + } + } +#endif /* CONFIG_ENC_ASSOC */ + + pasn_set_rsnxe_caps(pasn, capab); + pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL, NULL, + NULL); #ifdef CONFIG_SAE - if (awork->akmp == WPA_KEY_MGMT_SAE) { + if (awork->akmp == WPA_KEY_MGMT_SAE || + awork->akmp == WPA_KEY_MGMT_SAE_EXT_KEY) { + struct sae_pt *pt = NULL; + if (!ssid) { wpa_printf(MSG_DEBUG, "PASN: No network profile found for SAE"); goto fail; } - pasn_set_pt(pasn, wpas_pasn_sae_derive_pt(ssid, awork->group)); +#ifdef CONFIG_ENC_ASSOC + if (awork->auth_alg == WLAN_AUTH_EPPKE) + pt = wpas_pasn_sae_derive_pt_for_eppke(ssid, + awork->group); +#endif /* CONFIG_ENC_ASSOC */ + if (awork->auth_alg != WLAN_AUTH_EPPKE) + pt = wpas_pasn_sae_derive_pt(ssid, awork->group); + pasn_set_pt(pasn, pt); if (!pasn->pt) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT"); goto fail; @@ -648,6 +1064,32 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_IEEE80211R */ } +#ifdef CONFIG_ENC_ASSOC + pasn->auth_alg = awork->auth_alg; + pasn->group_cipher = awork->group_cipher; + pasn->group_mgmt_cipher = awork->group_mgmt_cipher; + pasn->rsn_capab = awork->rsn_capab; + pasn_set_rsnxe_ie(pasn, awork->rsnxe_data); + pasn->is_ml_peer = awork->is_ml_peer; + /* + * Set network_ctx so the PMKSA entry is stored with the correct + * network context for lookup on reconnection. + */ + if (awork->auth_alg == WLAN_AUTH_EPPKE && ssid) + pasn->network_ctx = ssid; + + /* Build RSNE for EPPKE Authentication in SME-in-driver mode */ + if (awork->auth_alg == WLAN_AUTH_EPPKE && + wpas_eppke_set_rsne(wpa_s, pasn, awork) < 0) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to configure RSNE"); + goto fail; + } +#endif /* CONFIG_ENC_ASSOC */ + + /* PASN has not been defined to be modified for RSN overriding, so use + * the RSNE and RSNXE from the AP for PASN MIC calculation instead of + * the RSNO elements, if any. */ + rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); ret = wpas_pasn_start(pasn, awork->own_addr, awork->peer_addr, awork->peer_addr, awork->akmp, awork->cipher, @@ -677,7 +1119,10 @@ fail: int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *own_addr, const u8 *peer_addr, int akmp, int cipher, u16 group, int network_id, - const u8 *comeback, size_t comeback_len) + const u8 *comeback, size_t comeback_len, + unsigned int auth_alg, int group_cipher, + int group_mgmt_cipher, u16 rsn_capab, + const u8 *rsnxe_data, bool is_ml_peer) { struct wpa_pasn_auth_work *awork; struct wpa_bss *bss; @@ -707,7 +1152,8 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, return -1; } - bss = wpas_pasn_allowed(wpa_s, peer_addr, akmp, cipher); + bss = wpas_pasn_allowed(wpa_s, peer_addr, akmp, cipher, auth_alg, + group_cipher, group_mgmt_cipher); if (!bss) return -1; @@ -723,6 +1169,21 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, awork->cipher = cipher; awork->group = group; awork->network_id = network_id; + awork->auth_alg = auth_alg; + awork->group_cipher = group_cipher; + awork->group_mgmt_cipher = group_mgmt_cipher; +#ifdef CONFIG_ENC_ASSOC + awork->rsn_capab = rsn_capab; + awork->is_ml_peer = is_ml_peer; + + if (rsnxe_data) { + awork->rsnxe_data = os_memdup(rsnxe_data, 2 + rsnxe_data[1]); + if (!awork->rsnxe_data) { + wpas_pasn_free_auth_work(awork); + return -1; + } + } +#endif /* CONFIG_ENC_ASSOC */ if (comeback && comeback_len) { awork->comeback = wpabuf_alloc_copy(comeback, comeback_len); @@ -732,8 +1193,8 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, } } - if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1, - wpas_pasn_auth_start_cb, awork) < 0) { + if (!radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1, + wpas_pasn_auth_start_cb, awork)) { wpas_pasn_free_auth_work(awork); return -1; } @@ -758,6 +1219,25 @@ void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s) pasn->comeback_after); wpas_pasn_reset(wpa_s); + + /* Reset rejected group state when authentication ends */ + pasn->rejected_group_idx = 0; + os_memset(pasn->rejected_groups, 0, sizeof(pasn->rejected_groups)); +} + + +void wpas_pasn_free_params(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + if (!wpa_s->pasn_params) + return; + + for (i = 0; i < wpa_s->pasn_params->num_peers; i++) + wpas_pasn_free_peer(&wpa_s->pasn_params->peer[i]); + + os_free(wpa_s->pasn_params); + wpa_s->pasn_params = NULL; } @@ -770,15 +1250,61 @@ static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s, u16 group = pasn->group; u8 own_addr[ETH_ALEN]; u8 peer_addr[ETH_ALEN]; + int network_id; + unsigned int auth_alg; wpa_printf(MSG_DEBUG, "PASN: Immediate retry"); os_memcpy(own_addr, pasn->own_addr, ETH_ALEN); os_memcpy(peer_addr, pasn->peer_addr, ETH_ALEN); + + /* Hold network ID to avoid losing it in wpas_pasn_reset(). */ + network_id = pasn->network_id; + + /* + * Cache auth_alg before reset as wpas_pasn_reset() clears the pasn + * struct. This path is shared with EPPKE, so without preserving it, + * a group rejection retry would incorrectly restart with PASN instead + * of EPPKE. + */ + auth_alg = pasn->auth_alg; + wpas_pasn_reset(wpa_s); return wpas_pasn_auth_start(wpa_s, own_addr, peer_addr, akmp, cipher, - group, pasn->network_id, - params->comeback, params->comeback_len); + group, network_id, params->comeback, + params->comeback_len, auth_alg, + pasn->group_cipher, + pasn->group_mgmt_cipher, pasn->rsn_capab, + pasn->rsnxe_ie, pasn->is_ml_peer); +} + + +static int wpas_pasn_retry_with_next_group(struct wpa_supplicant *wpa_s, + struct pasn_data *pasn) +{ + struct wpa_pasn_params_data params; + u16 next_group; + struct wpa_ssid *ssid = NULL; + +#ifdef CONFIG_ENC_ASSOC + if (pasn->auth_alg == WLAN_AUTH_EPPKE) + ssid = wpa_s->current_ssid; +#endif /* CONFIG_ENC_ASSOC */ + + next_group = (u16) wpas_pasn_get_group(wpa_s, ssid, pasn); + if (!next_group) { + wpa_printf(MSG_DEBUG, + "PASN: No more groups to try after rejection"); + return -1; + } + + wpa_printf(MSG_DEBUG, "PASN: Retrying with group %u after rejection", + next_group); + + pasn->group = next_group; + + os_memset(¶ms, 0, sizeof(params)); + return wpas_pasn_immediate_retry(wpa_s, pasn, ¶ms); } @@ -796,6 +1322,32 @@ static void wpas_pasn_deauth_cb(struct ptksa_cache_entry *entry) } +static void wpas_pasn_store_comeback_data(struct wpa_supplicant *wpa_s, + const struct wpabuf *comeback, + u16 comeback_after) +{ + struct pasn_peer *peer; + + if (!wpa_s->pasn_params) + return; + + peer = &wpa_s->pasn_params->peer[wpa_s->pasn_count]; + if (!peer) + return; + + wpas_pasn_free_peer_comeback(peer); + peer->comeback = os_memdup(wpabuf_head(comeback), wpabuf_len(comeback)); + if (!peer->comeback) { + wpa_printf(MSG_ERROR, + "PASN: Mem alloc failed for comeback data"); + return; + } + + peer->comeback_len = wpabuf_len(comeback); + peer->comeback_after = comeback_after; +} + + int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -806,7 +1358,11 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, if (!wpa_s->pasn_auth_work) return -2; - pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL); + wpabuf_free(pasn->frame); + pasn->frame = NULL; + + pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL, NULL, + NULL); ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data); if (ret == 0) { ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr, @@ -815,15 +1371,28 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, pasn_get_ptk(pasn), wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL, wpa_s->pasn_params ? wpa_s : NULL, - pasn_get_akmp(pasn)); + pasn_get_akmp(pasn), pasn->auth_alg); if (pasn->pmksa_entry) wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry); + + if (pasn->auth_alg == WLAN_AUTH_EPPKE) { +#ifdef CONFIG_SME + os_memcpy(wpa_s->sme.sae.pmkid, pasn->sae.pmkid, + PMKID_LEN); +#endif /* CONFIG_SME */ + wpa_sm_set_pmk(wpa_s->wpa, pasn->pmk, pasn->pmk_len, + pasn->sae.pmkid, NULL); + } } forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); if (ret == -1) { + if (pasn->status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && + pasn->comeback && wpabuf_len(pasn->comeback)) + wpas_pasn_store_comeback_data(wpa_s, pasn->comeback, + pasn->comeback_after); wpas_pasn_auth_stop(wpa_s); wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE); } @@ -831,6 +1400,16 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, if (ret == 1) ret = wpas_pasn_immediate_retry(wpa_s, pasn, &pasn_data); + if (ret == 2) { + ret = wpas_pasn_retry_with_next_group(wpa_s, pasn); + if (ret) { + wpa_printf(MSG_INFO, + "PASN: Group rejection retry failed"); + wpas_pasn_auth_stop(wpa_s); + wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE); + } + } + return ret; } @@ -870,6 +1449,26 @@ void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s, os_memcpy(dst->peer_addr, src->peer_addr, ETH_ALEN); dst->ltf_keyseed_required = src->ltf_keyseed_required; dst->status = PASN_STATUS_SUCCESS; + dst->akmp = src->akmp; + dst->cipher = src->cipher; + if (src->password) { + dst->password = os_strdup(src->password); + if (!dst->password) { + wpa_printf(MSG_DEBUG, + "PASN: Mem alloc failed for password"); + goto fail; + } + } + if (src->comeback_len && src->comeback) { + dst->comeback = os_memdup(src->comeback, + src->comeback_len); + if (!dst->comeback) { + wpa_printf(MSG_DEBUG, + "PASN: Mem alloc failed for comeback cookie"); + goto fail; + } + dst->comeback_len = src->comeback_len; + } if (!is_zero_ether_addr(src->own_addr)) { os_memcpy(dst->own_addr, src->own_addr, ETH_ALEN); @@ -885,15 +1484,72 @@ void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s, } else if (pasn_auth->action == PASN_ACTION_AUTH) { wpas_pasn_configure_next_peer(wpa_s, wpa_s->pasn_params); } + + return; + +fail: + wpas_pasn_free_params(wpa_s); } +#ifdef CONFIG_SME +#ifdef CONFIG_ENC_ASSOC +static u16 wpas_eppke_external_auth_set_keys(struct wpa_supplicant *wpa_s, + struct pasn_data *pasn, bool acked) +{ + static const u8 zero[WPA_TK_MAX_LEN] = { 0 }; + enum wpa_alg alg; + struct ptksa_cache_entry *entry; + + if (!acked) { + wpa_printf(MSG_DEBUG, + "EPPKE: Authentication frame 3 TX was not ACKed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + alg = wpa_cipher_to_alg(pasn_get_cipher(pasn)); + entry = ptksa_cache_get(wpa_s->ptksa, pasn->peer_addr, + pasn_get_cipher(pasn)); + if (!entry) { + wpa_printf(MSG_INFO, + "EPPKE: No PTKSA found to configure keys"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* Install TK to driver. */ + if (wpa_drv_set_key(wpa_s, -1, alg, pasn->peer_addr, 0, 1, zero, 6, + entry->ptk.tk, entry->ptk.tk_len, + KEY_FLAG_PAIRWISE_RX_TX)) { + wpa_printf(MSG_DEBUG, + "EPPKE: Failed to install TK to driver"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* Install LTF Keyseed to driver. */ + if (pasn->secure_ltf && + wpa_drv_set_secure_ranging_ctx(wpa_s, pasn->own_addr, + pasn->peer_addr, + pasn_get_cipher(pasn), 0, NULL, + entry->ptk.ltf_keyseed_len, + entry->ptk.ltf_keyseed, 0)) { + wpa_printf(MSG_DEBUG, + "EPPKE: Failed to install LTF Keyseed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; +} +#endif /* CONFIG_ENC_ASSOC */ +#endif /* CONFIG_SME */ + + int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, size_t data_len, u8 acked) { struct pasn_data *pasn = &wpa_s->pasn; int ret; + enum pasn_status auth_status = PASN_STATUS_SUCCESS; if (!wpa_s->pasn_auth_work) { wpa_printf(MSG_DEBUG, @@ -905,6 +1561,22 @@ int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, if (ret != 1) return ret; + if (pasn->auth_alg == WLAN_AUTH_EPPKE) { +#ifdef CONFIG_SME +#ifdef CONFIG_ENC_ASSOC + u16 status; + + status = wpas_eppke_external_auth_set_keys(wpa_s, pasn, acked); + if (status != WLAN_STATUS_SUCCESS) + auth_status = PASN_STATUS_FAILURE; +#ifdef CONFIG_SAE + sme_send_external_auth_status(wpa_s, status); +#endif /* CONFIG_SAE */ +#endif /* CONFIG_ENC_ASSOC */ +#endif /* CONFIG_SME */ + goto auth_done; + } + if (!wpa_s->pasn_params) { wpas_pasn_auth_stop(wpa_s); return 0; @@ -913,8 +1585,9 @@ int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr, pasn_get_cipher(pasn), pasn_get_akmp(pasn)); +auth_done: wpas_pasn_auth_stop(wpa_s); - wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS); + wpas_pasn_auth_work_done(wpa_s, auth_status); return 0; } diff --git a/contrib/wpa/wpa_supplicant/pr_supplicant.c b/contrib/wpa/wpa_supplicant/pr_supplicant.c new file mode 100644 index 000000000000..dac2626fa117 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/pr_supplicant.c @@ -0,0 +1,1807 @@ +/* + * Proxmity Ranging + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/proximity_ranging.h" +#include "p2p/p2p.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "notify.h" +#include "driver_i.h" +#include "pr_supplicant.h" + +#ifdef CONFIG_PASN +static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_pr_pasn_roc_total_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_pr_pasn_auth_work_done(struct wpa_supplicant *wpa_s); +static void wpas_pr_pasn_auth_retry_timeout(void *eloop_ctx, void *timeout_ctx); + +/* Total listen window (ms) for the PASN responder ROC */ +#define PR_PASN_RESPONDER_ROC_DURATION 10000 +/* Initiator PASN authentication timeout (s) */ +#define PR_PASN_AUTH_TIMEOUT 10 +/* Retry interval (ms) for unacked PASN Authentication frame 1 */ +#define PR_PASN_AUTH1_RETRY_INTERVAL_MS 100 +#endif /* CONFIG_PASN */ + + +static u8 wpas_pr_best_edca_format_bw(u32 bw_bitmap, u32 preamble_bitmap) +{ + /* Prefer highest bandwidth first */ + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_160)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT))) + return EDCA_FORMAT_AND_BW_VHT160_DUAL_LO; + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80P80)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT))) + return EDCA_FORMAT_AND_BW_VHT80P80; + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT))) + return EDCA_FORMAT_AND_BW_VHT80; + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_40)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT))) + return EDCA_FORMAT_AND_BW_VHT40; + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_40)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_HT))) + return EDCA_FORMAT_AND_BW_HT40; + if ((bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_20)) && + (preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT))) + return EDCA_FORMAT_AND_BW_VHT20; + return EDCA_FORMAT_AND_BW_INVALID; +} + + +static u8 wpas_pr_best_ntb_format_bw(u32 bw_bitmap, u32 preamble_bitmap) +{ + if (!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_HE))) + return NTB_FORMAT_AND_BW_INVALID; + + if (bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_160)) + return NTB_FORMAT_AND_BW_HE160_SINGLE_LO; + if (bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80P80)) + return NTB_FORMAT_AND_BW_HE80P80; + if (bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80)) + return NTB_FORMAT_AND_BW_HE80; + if (bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_40)) + return NTB_FORMAT_AND_BW_HE40; + if (bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_20)) + return NTB_FORMAT_AND_BW_HE20; + return NTB_FORMAT_AND_BW_INVALID; +} + + +static bool +wpas_pr_edca_is_valid_op_class(u32 bw_bitmap, u32 preamble_bitmap, + const struct oper_class_map *op_class_map) +{ + if (!op_class_map) + return false; + + switch (op_class_map->bw) { + case BW20: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_20)) && + !!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT)); + case BW40PLUS: + case BW40MINUS: + case BW40: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_40)) && + !!(preamble_bitmap & (BIT(WPA_PR_PREAMBLE_VHT) | + BIT(WPA_PR_PREAMBLE_HT))); + case BW80: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80)) && + !!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT)); + case BW80P80: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80P80)) && + !!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT)); + case BW160: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_160)) && + !!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_VHT)); + default: + return false; + } +} + + +static bool +wpas_pr_ntb_is_valid_op_class(u32 bw_bitmap, u32 preamble_bitmap, + const struct oper_class_map *op_class_map) +{ + if (!op_class_map) + return false; + + /* NTB ranging requires HE preamble */ + if (!(preamble_bitmap & BIT(WPA_PR_PREAMBLE_HE))) + return false; + + switch (op_class_map->bw) { + case BW20: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_20)); + case BW40PLUS: + case BW40MINUS: + case BW40: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_40)); + case BW80: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80)); + case BW80P80: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_80P80)); + case BW160: + return !!(bw_bitmap & BIT(WPA_PR_CHAN_WIDTH_160)); + default: + return false; + } +} + + +/** + * wpas_pr_op_class_to_chan_params - Derive center freq and width from op_class + */ +static int wpas_pr_op_class_to_chan_params(u8 op_class, u8 op_channel, + u32 *center_freq1, + u32 *center_freq2, + u16 *channel_width) +{ + int control_freq, bw, offset = 0; + int half_bw, block_pos; + + if (!center_freq1 || !center_freq2 || !channel_width) + return -1; + + *center_freq1 = 0; + *center_freq2 = 0; + *channel_width = 0; + + /* 80+80 MHz: center_freq2 is unknown from primary channel alone */ + if (op_class == 130 || op_class == 135) { + wpa_printf(MSG_DEBUG, + "PR: op_class %u (80+80 MHz) not supported, center_freq2 cannot be determined", + op_class); + return -1; + } + + control_freq = ieee80211_chan_to_freq(NULL, op_class, op_channel); + if (control_freq < 0) { + wpa_printf(MSG_DEBUG, "PR: Invalid op_class=%u channel=%u", + op_class, op_channel); + return -1; + } + + bw = op_class_to_bandwidth(op_class); + + /* + * 2.4 GHz 40 MHz: channels are 5 MHz apart so the generic offset + * formula does not apply. Handle upper (op_class 83) and lower + * (op_class 84) secondary channel directions explicitly. + */ + if (op_class == 83) { + *center_freq1 = control_freq + 10; + *channel_width = 40; + return 0; + } + if (op_class == 84) { + *center_freq1 = control_freq - 10; + *channel_width = 40; + return 0; + } + + if (bw == 20) { + *center_freq1 = control_freq; + *channel_width = 20; + return 0; + } + + /* + * For 5 GHz and 6 GHz wider bandwidths, compute the primary channel's + * position (in 20 MHz steps) within its band segment, then derive + * center_freq1 using the formula: + * center_freq1 = control_freq + (bw/2 - 10) - + * (offset & (bw/20 - 1)) * 20 + */ + if (control_freq >= 5955) + offset = (control_freq - 5955) / 20; + else if (control_freq >= 5745) + offset = (control_freq - 5745) / 20; + else if (control_freq >= 5180) + offset = (control_freq - 5180) / 20; + + /* Distance from the primary channel to the center of the BW block */ + half_bw = bw / 2 - 10; + + /* Position of the primary channel within its BW block (in 20 MHz steps) + */ + block_pos = offset & (bw / 20 - 1); + + /* Center = primary channel + half-BW offset - position within block */ + *center_freq1 = control_freq + half_bw - block_pos * 20; + *channel_width = bw; + + wpa_printf(MSG_DEBUG, "PR: op_class=%u ch=%u -> freq=%d cf1=%u bw=%d", + op_class, op_channel, control_freq, *center_freq1, bw); + + return 0; +} + + +static void +wpas_pr_setup_edca_channels(struct wpa_supplicant *wpa_s, + struct pr_channels *chan, + u32 bw_bitmap, u32 preamble_bitmap, + bool allow_6ghz) +{ + struct hostapd_hw_modes *mode; + int cla = 0, i; + + for (i = 0; global_op_class[i].op_class; i++) { + unsigned int ch; + struct pr_op_class *op = NULL; + const struct oper_class_map *o = &global_op_class[i]; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode, + is_6ghz_op_class(o->op_class)); + if (!mode || (!allow_6ghz && is_6ghz_op_class(o->op_class)) || + !wpas_pr_edca_is_valid_op_class(bw_bitmap, preamble_bitmap, + o)) + continue; + + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + enum chan_allowed res; + + /* Check for non-continuous jump in channel index + * increment. + */ + if (o->op_class >= 128 && o->op_class <= 130 && + ch < 149 && ch + o->inc > 149) + ch = 149; + + res = verify_channel(mode, o->op_class, ch, o->bw); + + if (res == ALLOWED) { + if (!op) { + if (cla == PR_MAX_OP_CLASSES) + continue; + + wpa_printf(MSG_DEBUG, + "PR: Add operating class: %u (EDCA)", + o->op_class); + op = &chan->op_class[cla]; + cla++; + op->op_class = o->op_class; + } + if (op->channels == PR_MAX_OP_CLASS_CHANNELS) + continue; + op->channel[op->channels] = ch; + op->channels++; + } + } + + if (op) + wpa_hexdump(MSG_DEBUG, "PR: Channels (EDCA)", + op->channel, op->channels); + } + + chan->op_classes = cla; +} + + +static void +wpas_pr_setup_ntb_channels(struct wpa_supplicant *wpa_s, + struct pr_channels *chan, + u32 bw_bitmap, u32 preamble_bitmap, + bool allow_6ghz) +{ + int cla = 0, i; + struct hostapd_hw_modes *mode; + + for (i = 0; global_op_class[i].op_class; i++) { + unsigned int ch; + struct pr_op_class *op = NULL; + const struct oper_class_map *o = &global_op_class[i]; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode, + is_6ghz_op_class(o->op_class)); + if (!mode || (!allow_6ghz && is_6ghz_op_class(o->op_class)) || + !wpas_pr_ntb_is_valid_op_class(bw_bitmap, preamble_bitmap, + o)) + continue; + + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + enum chan_allowed res; + + /* Check for non-continuous jump in channel index + * increment. + */ + if (o->op_class >= 128 && o->op_class <= 130 && + ch < 149 && ch + o->inc > 149) + ch = 149; + + res = verify_channel(mode, o->op_class, ch, o->bw); + + if (res == ALLOWED) { + if (!op) { + if (cla == PR_MAX_OP_CLASSES) + continue; + wpa_printf(MSG_DEBUG, + "PR: Add operating class: %u (NTB)", + o->op_class); + op = &chan->op_class[cla]; + cla++; + op->op_class = o->op_class; + } + if (op->channels == PR_MAX_OP_CLASS_CHANNELS) + continue; + op->channel[op->channels] = ch; + op->channels++; + } + } + if (op) { + wpa_hexdump(MSG_DEBUG, "PR: Channels (NTB)", + op->channel, op->channels); + } + } + + chan->op_classes = cla; +} + + +static int wpas_pr_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, + unsigned int wait) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait); +} + + +static void wpas_pr_device_found(void *ctx, const struct pr_device *dev) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_pr_device_found(wpa_s, dev); +} + + +static void wpas_pr_pasn_negotiation_started(void *ctx, const u8 *peer_addr, + u8 role, u8 protocol_type) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_pr_negotiation_started(wpa_s, peer_addr, role, + protocol_type); +} + + +static void wpas_pr_pasn_result(void *ctx, u8 role, u8 protocol_type, + u8 op_class, u8 op_channel, const char *country) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_pr_pasn_result(wpa_s, role, protocol_type, op_class, + op_channel, country); +} + + +static void wpas_pr_clear_ranging_params(struct pr_data *pr) +{ + if (pr) { + os_free(pr->pr_pasn_params); + pr->pr_pasn_params = NULL; + pr->ranging_final_received = false; + } +} + + +static void wpas_pr_ranging_session_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct pr_data *pr = wpa_s->global->pr; + + wpa_printf(MSG_DEBUG, "PR: Ranging session timeout - cleaning up"); + + /* Stop peer measurement and cleanup ranging socket */ + wpa_drv_stop_peer_measurement(wpa_s); + + /* Free ranging params */ + wpas_pr_clear_ranging_params(pr); + + wpas_pr_pd_stop(wpa_s); + wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_TIMEOUT); +} + + +/** + * wpas_pr_trigger_ranging - Trigger FTM ranging after successful PASN auth + */ +static int wpas_pr_trigger_ranging(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq, u8 op_class, + u8 op_channel, u8 format_bw, + u8 protocol_type) +{ + struct pr_data *pr = wpa_s->global->pr; + struct pr_pasn_ranging_params *params; + u16 channel_width = 0; + u32 center_freq1 = 0, center_freq2 = 0; + int ret; + + if (!pr || !pr->pr_pasn_params) { + wpa_printf(MSG_DEBUG, + "PR: No ranging params available for " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + params = pr->pr_pasn_params; + + wpa_printf(MSG_DEBUG, + "PR: Triggering ranging for " MACSTR " freq=%d ch=%u", + MAC2STR(peer_addr), freq, op_channel); + + /* Derive center frequencies and channel width (MHz) from op_class */ + ret = wpas_pr_op_class_to_chan_params(op_class, op_channel, + ¢er_freq1, ¢er_freq2, + &channel_width); + if (ret < 0) { + wpa_printf(MSG_INFO, + "PR: Failed to derive channel params for op_class=%u ch=%u", + op_class, op_channel); + goto fail; + } + + /* Populate ranging params */ + params->ranging_op_class = op_class; + params->channel_width = channel_width; + params->format_bw = format_bw; + params->center_freq1 = center_freq1; + params->center_freq2 = center_freq2; + + /* Validate format_bw is within enum limits before setting preamble */ + if (protocol_type & PR_EDCA_BASED_RANGING) { + if (format_bw < EDCA_FORMAT_AND_BW_VHT20 || + format_bw > EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO) { + wpa_printf(MSG_INFO, + "PR: Invalid EDCA format_bw %u (valid range: %u-%u)", + format_bw, EDCA_FORMAT_AND_BW_VHT20, + EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO); + goto fail; + } + } else if (protocol_type & (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING)) { + if (format_bw > NTB_FORMAT_AND_BW_HE160_SINGLE_LO) { + wpa_printf(MSG_INFO, + "PR: Invalid NTB format_bw %u (valid range: 0-%u)", + format_bw, + NTB_FORMAT_AND_BW_HE160_SINGLE_LO); + goto fail; + } + } else { + wpa_printf(MSG_INFO, "PR: Unknown protocol_type %u", + protocol_type); + goto fail; + } + + wpa_printf(MSG_DEBUG, + "PR: Ranging params - op_class=%u, ch_width=%u, format_bw=%u, cf1=%u, cf2=%u", + params->ranging_op_class, params->channel_width, + params->format_bw, params->center_freq1, + params->center_freq2); + + /* Call driver operation to start peer measurement */ + if (wpa_drv_start_peer_measurement(wpa_s, peer_addr, freq, op_channel, + channel_width, params) < 0) { + wpa_printf(MSG_INFO, "PR: Failed to start peer measurement"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "PR: Successfully triggered ranging measurement"); + + /* Start session timeout timer if continuous ranging session time is set + */ + if (params->continuous_ranging_session_time > 0) { + unsigned int timeout_sec, timeout_usec; + + wpa_printf(MSG_DEBUG, + "PR: Starting ranging session timeout timer for %u ms", + params->continuous_ranging_session_time); + + timeout_sec = params->continuous_ranging_session_time / 1000; + timeout_usec = (params->continuous_ranging_session_time % + 1000) * 1000; + eloop_cancel_timeout(wpas_pr_ranging_session_timeout, + wpa_s, NULL); + eloop_register_timeout(timeout_sec, timeout_usec, + wpas_pr_ranging_session_timeout, + wpa_s, NULL); + } + + return 0; + +fail: + wpas_pr_clear_ranging_params(pr); + return -1; +} + + +static void wpas_pr_ranging_params(void *ctx, const u8 *dev_addr, + const u8 *peer_addr, u8 ranging_role, + u8 protocol_type, u8 op_class, u8 op_channel, + u8 self_format_bw, u8 peer_format_bw) +{ + struct wpa_supplicant *wpa_s = ctx; + int bw, format_bw, freq; + + bw = op_class_to_bandwidth(op_class); + format_bw = self_format_bw < peer_format_bw ? + self_format_bw : peer_format_bw; + freq = ieee80211_chan_to_freq(NULL, op_class, op_channel); + + wpas_notify_pr_ranging_params(wpa_s, dev_addr, peer_addr, ranging_role, + protocol_type, freq, op_channel, bw, + format_bw); + + /* + * PASN succeeded - cancel the PASN timeout so it does not fire and + * prematurely stop the PD wdev while ranging is in progress. + * Cleanup happens on COMPLETE event or session timeout. + */ + eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL); + wpas_pr_pasn_auth_work_done(wpa_s); + + /* Trigger ranging measurement after successful PASN authentication */ + if (wpas_pr_trigger_ranging(wpa_s, peer_addr, freq, op_class, + op_channel, format_bw, protocol_type) < 0) { + wpa_printf(MSG_INFO, + "PR: Failed to trigger ranging, stopping PD wdev"); + wpas_pr_pd_stop(wpa_s); + } +} + + +static int wpas_pr_pasn_set_keys(void *ctx, const u8 *own_addr, + const u8 *peer_addr, int cipher, int akmp, + struct wpa_ptk *ptk) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_driver_set_key_params params; + + wpa_printf(MSG_DEBUG, "PR PASN: Set secure ranging context for " MACSTR, + MAC2STR(peer_addr)); + + if (!wpa_s->driver->set_key) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.ifname = wpa_s->ifname; + params.own_addr = own_addr; + params.alg = wpa_cipher_to_alg(cipher); + params.addr = peer_addr; + params.key_idx = 0; + params.set_tx = 1; + params.key = ptk->tk; + params.key_len = ptk->tk_len; + params.key_flag = KEY_FLAG_PAIRWISE_RX_TX; + params.link_id = -1; + params.ltf_keyseed = ptk->ltf_keyseed; + params.ltf_keyseed_len = ptk->ltf_keyseed_len; + + if (wpa_s->driver->set_key(wpa_s->drv_priv, ¶ms) < 0) { + wpa_printf(MSG_INFO, "PR PASN: Failed to set TK"); + return -1; + } + + return 0; +} + + +static void wpas_pr_pasn_clear_keys(void *ctx, const u8 *own_addr, + const u8 *peer_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_driver_set_key_params params; + + wpa_printf(MSG_DEBUG, "PR PASN: Clear secure ranging context for " + MACSTR, MAC2STR(peer_addr)); + + if (!wpa_s->driver->set_key) + return; + + os_memset(¶ms, 0, sizeof(params)); + params.ifname = wpa_s->ifname; + params.own_addr = own_addr; + params.alg = WPA_ALG_NONE; + params.addr = peer_addr; + params.key_idx = 0; + params.link_id = -1; + + if (wpa_s->driver->set_key(wpa_s->drv_priv, ¶ms) < 0) + wpa_printf(MSG_INFO, "PR PASN: Failed to clear TK"); +} + + +struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s, + const u8 *src_addr) +{ + if (!wpa_s->global->pr) + return NULL; + + return pr_prepare_usd_elems(wpa_s->global->pr, src_addr); +} + + +void wpas_pr_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!pr) + return; + pr_process_usd_elems(pr, buf, buf_len, peer_addr, freq); +} + + +int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s, + const struct wpa_driver_capa *capa) +{ + struct pr_config pr; + + if (global->pr) + return 0; + + os_memset(&pr, 0, sizeof(pr)); + + os_memcpy(pr.dev_addr, wpa_s->own_addr, ETH_ALEN); + pr.cb_ctx = wpa_s; + pr.dev_name = wpa_s->conf->device_name; + pr.pasn_type = wpa_s->conf->pr_pasn_type ? + wpa_s->conf->pr_pasn_type : + (int) (PR_PASN_DH19_UNAUTH | PR_PASN_DH19_AUTH); + pr.preferred_ranging_role = wpa_s->conf->pr_preferred_role; + + pr.edca_format_and_bw = + wpas_pr_best_edca_format_bw(capa->pd_bandwidths, + capa->pd_preambles); + pr.edca_ista_support = capa->ista.support_edca && + capa->asap_support && + pr.edca_format_and_bw != EDCA_FORMAT_AND_BW_INVALID; + pr.edca_rsta_support = capa->rsta.support_edca && + capa->asap_support && + pr.edca_format_and_bw != EDCA_FORMAT_AND_BW_INVALID; + pr.pd_format_bw_bitmap = capa->pd_bandwidths; + pr.pd_preamble_bitmap = capa->pd_preambles; + pr.max_rx_antenna = capa->max_rx_antenna; + pr.max_tx_antenna = capa->max_tx_antenna; + + wpas_pr_setup_edca_channels(wpa_s, &pr.edca_channels, + capa->pd_bandwidths, + capa->pd_preambles, + pr.support_6ghz); + pr.ntb_format_and_bw = + wpas_pr_best_ntb_format_bw(capa->pd_bandwidths, + capa->pd_preambles); + pr.ntb_ista_support = capa->ista.support_ntb && + pr.ntb_format_and_bw != NTB_FORMAT_AND_BW_INVALID; + pr.ntb_rsta_support = capa->rsta.support_ntb && + pr.ntb_format_and_bw != NTB_FORMAT_AND_BW_INVALID; + pr.max_tx_ltf_repetations = capa->max_tx_ltf_repetations; + pr.max_rx_ltf_repetations = capa->max_rx_ltf_repetations; + pr.max_tx_ltf_total = capa->max_tx_ltf_total; + pr.max_rx_ltf_total = capa->max_rx_ltf_total; + pr.max_rx_sts_le_80 = capa->max_rx_sts_le_80; + pr.max_rx_sts_gt_80 = capa->max_rx_sts_gt_80; + pr.max_tx_sts_le_80 = capa->max_tx_sts_le_80; + pr.max_tx_sts_gt_80 = capa->max_tx_sts_gt_80; + + pr.edca_min_ranging_interval = capa->edca_min_ranging_interval; + pr.ntb_min_ranging_interval = capa->ntb_min_ranging_interval; + pr.concurrent_ista_rsta = capa->concurrent_ista_rsta; + pr.pmsr_max_peers = capa->pmsr_max_peers; + pr.pr_max_peer_ista_role = capa->ista.max_peers; + pr.pr_max_peer_rsta_role = capa->rsta.max_peers; + pr.max_ftms_per_burst = capa->max_ftms_per_burst; + + pr.support_6ghz = capa->support_6ghz; + + pr.pasn_send_mgmt = wpas_pr_pasn_send_mgmt; + pr.negotiation_started = wpas_pr_pasn_negotiation_started; + pr.pasn_result = wpas_pr_pasn_result; + pr.get_ranging_params = wpas_pr_ranging_params; + pr.set_keys = wpas_pr_pasn_set_keys; + pr.device_found = wpas_pr_device_found; + pr.clear_keys = wpas_pr_pasn_clear_keys; + + pr.secure_he_ltf = wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA; + + wpas_pr_setup_ntb_channels(wpa_s, &pr.ntb_channels, + capa->pd_bandwidths, capa->pd_preambles, + pr.support_6ghz); + + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + os_memcpy(pr.country, wpa_s->conf->country, 2); + pr.country[2] = 0x04; + } else { + os_memcpy(pr.country, "XX\x04", 3); + } + + if (wpa_s->conf->dik && + wpabuf_len(wpa_s->conf->dik) <= DEVICE_IDENTITY_KEY_LEN) { + pr.dik_cipher = wpa_s->conf->dik_cipher; + pr.dik_len = wpabuf_len(wpa_s->conf->dik); + os_memcpy(pr.dik_data, wpabuf_head(wpa_s->conf->dik), + pr.dik_len); + pr.expiration = 24; /* hours */ + } else { + pr.dik_cipher = DIRA_CIPHER_VERSION_128; + pr.dik_len = DEVICE_IDENTITY_KEY_LEN; + pr.expiration = 24; /* hours */ + if (os_get_random(pr.dik_data, pr.dik_len) < 0) + return -1; + + wpa_s->conf->dik = + wpabuf_alloc_copy(pr.dik_data, pr.dik_len); + if (!wpa_s->conf->dik) + return -1; + + wpa_s->conf->dik_cipher = pr.dik_cipher; + + wpa_printf(MSG_DEBUG, "PR: PR init new DIRA set"); + + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, + "PR: Failed to update configuration"); + } + + global->pr = pr_init(&pr); + if (!global->pr) { + wpa_printf(MSG_DEBUG, "PR: Failed to init PR"); + return -1; + } + global->pr_init_wpa_s = wpa_s; + + return 0; +} + + +void wpas_pr_flush(struct wpa_supplicant *wpa_s) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (pr) + pr_flush(pr); +} + +void wpas_pr_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s == wpa_s->global->pr_init_wpa_s) { + pr_deinit(wpa_s->global->pr); + wpa_s->global->pr = NULL; + wpa_s->global->pr_init_wpa_s = NULL; + } + +#ifdef CONFIG_PASN + eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL); +#endif /* CONFIG_PASN */ + eloop_cancel_timeout(wpas_pr_ranging_session_timeout, wpa_s, NULL); +} + + +void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s) +{ + struct pr_data *pr = wpa_s->global->pr; + + /* Cancel ranging session timeout and stop peer measurement */ + eloop_cancel_timeout(wpas_pr_ranging_session_timeout, wpa_s, NULL); + wpa_drv_stop_peer_measurement(wpa_s); + + if (is_zero_ether_addr(wpa_s->pd_addr)) { + wpa_printf(MSG_DEBUG, "PR: pd_stop: no active PD wdev"); + return; + } + + wpa_printf(MSG_DEBUG, "PR: Stopping PD wdev addr=" MACSTR, + MAC2STR(wpa_s->pd_addr)); + + wpa_drv_pd_stop(wpa_s); + os_memset(wpa_s->pd_addr, 0, ETH_ALEN); + + /* Restore dev_addr to station MAC now that PD wdev is gone */ + if (pr) + pr_set_dev_addr(pr, wpa_s->own_addr); + + wpa_printf(MSG_DEBUG, "PR: PD wdev stopped, dev_addr restored to " + MACSTR, MAC2STR(wpa_s->own_addr)); +} + + +void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s) +{ + pr_set_dev_addr(wpa_s->global->pr, wpa_s->own_addr); +} + + +void wpas_pr_clear_dev_iks(struct wpa_supplicant *wpa_s) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!pr) + return; + + pr_clear_dev_iks(pr); +} + + +void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, const u8 *dik, + const char *password, const u8 *pmk, size_t pmk_len, + bool own) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!pr || !dik) + return; + + pr_add_dev_ik(pr, dik, password, pmk, pmk_len, own); +} + + +void wpas_pr_measurement_complete(struct wpa_supplicant *wpa_s, + struct peer_measurement_complete *complete) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!complete) { + wpa_printf(MSG_INFO, + "PR: Invalid measurement complete event"); + return; + } + + wpa_printf(MSG_DEBUG, + "PR: Peer measurement complete cookie=%llu", + (unsigned long long) complete->cookie); + + /* Validate cookie if we have a pending ranging request */ + if (pr && pr->pr_pasn_params && complete->cookie != 0 && + pr->pr_pasn_params->cookie != complete->cookie) { + wpa_printf(MSG_INFO, + "PR: Complete cookie mismatch - expected %llu, got %llu. Ignoring.", + (unsigned long long) pr->pr_pasn_params->cookie, + (unsigned long long) complete->cookie); + return; + } + + wpas_notify_pr_ranging_complete(wpa_s, complete->cookie); + wpas_pr_clear_ranging_params(pr); + wpas_pr_pd_stop(wpa_s); + wpas_notify_pr_ranging_terminated(wpa_s, + PR_SESSION_END_PEER_COMPLETE); +} + + +void wpas_pr_measurement_result(struct wpa_supplicant *wpa_s, + struct peer_measurement_result *result) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!result) { + wpa_printf(MSG_INFO, "PR: Invalid measurement result"); + return; + } + + /* Drop results after final has been received */ + if (pr && pr->ranging_final_received) { + wpa_printf(MSG_DEBUG, + "PR: Ignoring result after final for " MACSTR, + MAC2STR(result->addr)); + return; + } + + /* Validate cookie if we have a pending ranging request */ + if (pr && pr->pr_pasn_params && result->cookie != 0 && + pr->pr_pasn_params->cookie != result->cookie) { + wpa_printf(MSG_INFO, + "PR: Cookie mismatch - expected %llu, got %llu. Ignoring result.", + (unsigned long long) pr->pr_pasn_params->cookie, + (unsigned long long) result->cookie); + return; + } + + /* Forward result to upper layer - includes failures and final */ + if (result->ftm.has_data || result->ftm.fail) + wpas_notify_pr_measurement_result(wpa_s, result); + + /* After final result, mark session done - no more results accepted */ + if (result->final) { + wpa_printf(MSG_DEBUG, + "PR: Final result received for " MACSTR + " - no further results will be processed", + MAC2STR(result->addr)); + if (pr) + pr->ranging_final_received = true; + } +} + + +#ifdef CONFIG_PASN + +static int wpas_pr_start_pd(struct wpa_supplicant *wpa_s, const u8 *src_addr) +{ + u8 pd_addr[ETH_ALEN]; + + if (!src_addr || is_zero_ether_addr(src_addr)) { + wpa_printf(MSG_INFO, "PR: Invalid MAC address for PD wdev"); + return -1; + } + + if (!is_zero_ether_addr(wpa_s->pd_addr)) { + wpa_printf(MSG_INFO, "PR: PD wdev already active addr=" MACSTR, + MAC2STR(wpa_s->pd_addr)); + return -1; + } + + wpa_printf(MSG_DEBUG, "PR: Creating PD wdev with MAC address " MACSTR, + MAC2STR(src_addr)); + + os_memset(pd_addr, 0, ETH_ALEN); + if (wpa_drv_pd_start(wpa_s, src_addr, pd_addr) < 0) { + wpa_printf(MSG_ERROR, "PR: Failed to create PD wdev"); + return -1; + } + + os_memcpy(wpa_s->pd_addr, pd_addr, ETH_ALEN); + pr_set_dev_addr(wpa_s->global->pr, pd_addr); + + wpa_printf(MSG_DEBUG, "PR: PD wdev created addr=" MACSTR, + MAC2STR(pd_addr)); + return 0; +} + + +struct wpa_pr_pasn_auth_work { + u8 peer_addr[ETH_ALEN]; + u8 auth_mode; + int freq; + enum pr_pasn_role role; + u8 ranging_role; + u8 ranging_type; + u8 *ssid; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + int forced_pr_freq; +}; + + +struct wpa_pr_pasn_roc_work { + unsigned int freq; + u8 src_addr[ETH_ALEN]; +}; + + +static void wpas_pr_pasn_free_auth_work(struct wpa_pr_pasn_auth_work *awork) +{ + if (!awork) + return; + os_free(awork->ssid); + os_free(awork); +} + + +static void wpas_pr_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "PR PASN: Cancel pr-pasn-start-auth work"); + + /* Remove pending/started work */ + radio_remove_works(wpa_s, "pr-pasn-start-auth", 0); +} + + +/** + * wpas_pr_pasn_auth_work_done - Release PASN auth radio work + */ +static void wpas_pr_pasn_auth_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpa_pr_pasn_auth_work *awork; + + if (!wpa_s->pr_pasn_auth_work) + return; + + awork = wpa_s->pr_pasn_auth_work->ctx; + wpas_pr_pasn_free_auth_work(awork); + wpa_s->pr_pasn_auth_work->ctx = NULL; + radio_work_done(wpa_s->pr_pasn_auth_work); + wpa_s->pr_pasn_auth_work = NULL; + eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL); +} + + +/** + * wpas_pr_pasn_roc_work_done - Idempotent helper to complete ROC radio work + */ +static void wpas_pr_pasn_roc_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpa_pr_pasn_roc_work *rwork; + + if (!wpa_s->pr_roc_work) + return; + + rwork = wpa_s->pr_roc_work->ctx; + os_free(rwork); + wpa_s->pr_roc_work->ctx = NULL; + radio_work_done(wpa_s->pr_roc_work); + wpa_s->pr_roc_work = NULL; +} + + +/** + * wpas_pr_cancel_roc - Stop all responder ROC activity, both active and queued + * + * Cancels any in-progress driver ROC, releases the active radio work item, + * and removes any pending pr-pasn-roc work items that have not yet started. + * Safe to call when no ROC is active; all sub-operations are idempotent. + * Must NOT be called from within a pr-pasn-roc radio work callback (deinit + * path) as that would cause re-entrant radio_remove_works() -> + * radio_work_free(). + */ +static void wpas_pr_cancel_roc(struct wpa_supplicant *wpa_s) +{ + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + wpas_pr_pasn_roc_work_done(wpa_s); + radio_remove_works(wpa_s, "pr-pasn-roc", 0); +} + + +/** + * wpas_pr_pasn_abort_responder - Cancel the responder PASN session and clean + * up all associated state. Safe to call from any responder error path; calling + * eloop_cancel_timeout() on an already-expired timer is a no-op. + */ +static void wpas_pr_pasn_abort_responder(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, wpa_s, NULL); + wpa_s->pr_responder_mode = false; + os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN); + wpas_pr_clear_ranging_params(wpa_s->global->pr); +} + + +/** + * wpas_pr_pasn_roc_total_timeout - Total ROC budget expiry; stop responder + */ +static void wpas_pr_pasn_roc_total_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_printf(MSG_DEBUG, + "PR PASN: Total ROC budget expired, stopping responder listen"); + + wpas_pr_cancel_roc(wpa_s); + wpas_pr_pasn_abort_responder(wpa_s); + wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_NEG_FAILED); +} + + +/** + * wpas_pr_pasn_roc_start_cb - Radio work callback to start the responder ROC + */ +static void wpas_pr_pasn_roc_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_pr_pasn_roc_work *rwork = work->ctx; + unsigned int chunk_ms; + + if (deinit) { + if (work->started) { + /* + * ROC was already started but the work is being + * cancelled (e.g., interface removal). Cancel the + * driver ROC and clear the channel state. + */ + wpa_s->pr_roc_work = NULL; + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = 0; + } + /* + * Clear responder state and cancel the total-budget timer + * regardless of whether the work was started or not - the + * ROC will never fire now. + */ + wpas_pr_pasn_abort_responder(wpa_s); + os_free(rwork); + work->ctx = NULL; + return; + } + + wpa_s->pr_roc_work = work; + + /* Use max_remain_on_chan as per-chunk duration, matching DPP/P2P */ + chunk_ms = wpa_s->max_remain_on_chan; + + wpa_printf(MSG_DEBUG, + "PR PASN: Starting ROC chunk at freq %u MHz duration %u ms%s", + rwork->freq, chunk_ms, + is_zero_ether_addr(rwork->src_addr) ? "" : + " with MAC filter"); + + if (wpa_drv_remain_on_channel(wpa_s, rwork->freq, chunk_ms, + is_zero_ether_addr(rwork->src_addr) ? + NULL : rwork->src_addr) < 0) { + wpa_printf(MSG_ERROR, + "PR PASN: Failed to start ROC for responder"); + wpas_pr_pasn_roc_work_done(wpa_s); + wpas_pr_pasn_abort_responder(wpa_s); + wpas_notify_pr_ranging_terminated(wpa_s, + PR_SESSION_END_NEG_FAILED); + return; + } + + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = rwork->freq; +} + + +/** + * wpas_pr_schedule_responder_roc - Queue next ROC chunk for the responder + */ +static void wpas_pr_schedule_responder_roc(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + struct wpa_pr_pasn_roc_work *rwork; + + rwork = os_zalloc(sizeof(*rwork)); + if (!rwork) { + wpa_printf(MSG_INFO, "PR PASN: OOM restarting ROC"); + goto fail; + } + rwork->freq = freq; + + if (wpa_s->pr_responder_mode && + !is_zero_ether_addr(wpa_s->pr_responder_src_addr)) + os_memcpy(rwork->src_addr, wpa_s->pr_responder_src_addr, + ETH_ALEN); + + if (!radio_add_work(wpa_s, freq, "pr-pasn-roc", 0, + wpas_pr_pasn_roc_start_cb, rwork)) { + wpa_printf(MSG_INFO, "PR PASN: Failed to reschedule ROC"); + os_free(rwork); + goto fail; + } + return; + +fail: + wpas_pr_pasn_abort_responder(wpa_s); +} + + +/** + * wpas_pr_cancel_remain_on_channel_cb - ROC cancel/expiry callback for PR + */ +void wpas_pr_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_printf(MSG_DEBUG, "PR PASN: Remain on channel cancel for %u MHz", + freq); + + if (!wpa_s->pr_roc_work) + return; + + wpas_pr_pasn_roc_work_done(wpa_s); + + if (wpa_s->pr_responder_mode) { + /* Total-budget timer still live — restart another chunk */ + wpa_printf(MSG_DEBUG, + "PR PASN: ROC chunk expired, restarting for next chunk"); + wpas_pr_schedule_responder_roc(wpa_s, freq); + return; + } + + wpa_printf(MSG_DEBUG, + "PR PASN: ROC total timeout reached, responder done"); +} + + +static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL); + + if (wpa_s->pr_pasn_auth_work) { + wpas_pr_pasn_cancel_auth_work(wpa_s); + wpa_s->pr_pasn_auth_work = NULL; + } + + /* + * Stop the PD wdev only after radio_work_done() has fully returned. + * Calling wpas_pr_pd_stop() from inside the radio-work deinit callback + * would trigger a re-entrant radio_remove_works() -> radio_work_free() + * on the same work item, causing a use-after-free / SIGSEGV. + */ + wpas_pr_pd_stop(wpa_s); + + wpas_pr_clear_ranging_params(wpa_s->global->pr); + + wpa_printf(MSG_DEBUG, "PR: PASN timed out"); + wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_NEG_FAILED); +} + + +static void wpas_pr_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + int ret; + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_pr_pasn_auth_work *awork = work->ctx; + struct pr_data *pr = wpa_s->global->pr; + const u8 *peer_addr = NULL; + + if (deinit) { + if (!work->started) + eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL); + + wpas_pr_pasn_free_auth_work(awork); + work->ctx = NULL; + return; + } + + if (!is_zero_ether_addr(awork->peer_addr)) + peer_addr = awork->peer_addr; + + ret = pr_initiate_pasn_auth(pr, peer_addr, awork->freq, + awork->auth_mode, awork->ranging_role, + awork->ranging_type, awork->forced_pr_freq); + if (ret) { + wpa_printf(MSG_DEBUG, + "PR PASN: Failed to start PASN authentication"); + goto fail; + } + + eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL); + eloop_register_timeout(PR_PASN_AUTH_TIMEOUT, 0, wpas_pr_pasn_timeout, + wpa_s, NULL); + wpa_s->pr_pasn_auth_work = work; + return; + +fail: + wpas_pr_pasn_free_auth_work(awork); + work->ctx = NULL; + radio_work_done(work); + /* Stop PD wdev after radio_work_done() to avoid use-after-free */ + wpas_pr_pd_stop(wpa_s); + wpas_pr_clear_ranging_params(wpa_s->global->pr); +} + + +int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq, u8 auth_mode, + u8 ranging_role, u8 ranging_type, + int forced_pr_freq, const u8 *src_addr, + enum pr_pasn_role pasn_role) +{ + struct wpa_pr_pasn_auth_work *awork; + struct pr_data *pr = wpa_s->global->pr; + + /* Add OOB peer if not already in the discovery list */ + if (pr && pr_ensure_oob_peer(pr, peer_addr, freq) < 0) + return -1; + + if (pasn_role == PR_ROLE_PASN_RESPONDER) { + struct wpa_pr_pasn_roc_work *rwork; + unsigned int roc_time_ms = PR_PASN_RESPONDER_ROC_DURATION; + bool has_src_addr = src_addr && !is_zero_ether_addr(src_addr); + + wpa_printf(MSG_DEBUG, + "PR PASN: Scheduling ROC at freq %d for responder role%s", + freq, has_src_addr ? " with custom MAC" : ""); + + rwork = os_zalloc(sizeof(*rwork)); + if (!rwork) + return -1; + + rwork->freq = freq; + if (has_src_addr) + os_memcpy(rwork->src_addr, src_addr, ETH_ALEN); + /* else rwork->src_addr stays all-zeros (no MAC filter on ROC) + */ + + /* + * Store state so wpas_pr_pasn_auth_rx() can create the PD + * interface when M1 arrives. When no custom MAC address is + * given the PD wdev is skipped and the existing interface is + * used. + */ + wpa_s->pr_responder_mode = true; + if (has_src_addr) + os_memcpy(wpa_s->pr_responder_src_addr, src_addr, + ETH_ALEN); + /* else pr_responder_src_addr stays all-zeros */ + + if (!radio_add_work(wpa_s, freq, "pr-pasn-roc", 0, + wpas_pr_pasn_roc_start_cb, rwork)) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to schedule ROC for responder"); + os_free(rwork); + wpa_s->pr_responder_mode = false; + os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN); + return -1; + } + + /* + * Register the total-budget timer. When it fires it clears + * pr_responder_mode so the cancel callback stops restarting + * chunks. Defaults to PR_PASN_RESPONDER_ROC_DURATION; + * overridden by continuous_ranging_session_time when non-zero. + */ + if (pr && pr->pr_pasn_params && + pr->pr_pasn_params->continuous_ranging_session_time > 0) + roc_time_ms = pr->pr_pasn_params->continuous_ranging_session_time; + + eloop_register_timeout(roc_time_ms / 1000, + (roc_time_ms % 1000) * 1000, + wpas_pr_pasn_roc_total_timeout, + wpa_s, NULL); + return 0; + } + + /* + * PASN initiator role: create the PD wdev if src_addr is provided, + * then queue the radio work to send M1. + */ + if (src_addr && !is_zero_ether_addr(src_addr)) { + if (wpas_pr_start_pd(wpa_s, src_addr) < 0) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to create PD wdev"); + return -1; + } + } + + wpas_pr_pasn_cancel_auth_work(wpa_s); + wpa_s->pr_pasn_auth_work = NULL; + + awork = os_zalloc(sizeof(*awork)); + if (!awork) { + wpas_pr_pd_stop(wpa_s); + return -1; + } + + awork->freq = freq; + os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN); + awork->ranging_role = ranging_role; + awork->ranging_type = ranging_type; + awork->auth_mode = auth_mode; + awork->forced_pr_freq = forced_pr_freq; + + if (!radio_add_work(wpa_s, freq, "pr-pasn-start-auth", 1, + wpas_pr_pasn_auth_start_cb, awork)) { + wpas_pr_pasn_free_auth_work(awork); + wpas_pr_pd_stop(wpa_s); + return -1; + } + + wpa_printf(MSG_DEBUG, + "PR PASN: Authentication work successfully added"); + return 0; +} + + +/** + * wpas_pr_validate_ranging_request - Validate PR ranging request parameters + */ +static int +wpas_pr_validate_ranging_request(struct wpa_supplicant *wpa_s, + struct pr_pasn_ranging_params *pr_pasn_params) +{ + struct pr_data *pr = wpa_s->global->pr; + struct pr_config *cfg; + bool is_edca, is_ntb, is_ista; + + if (!pr || !pr->cfg) { + wpa_printf(MSG_INFO, "PR: PR not initialized"); + return -1; + } + + cfg = pr->cfg; + + /* Peer address must not be all-zeros */ + if (is_zero_ether_addr(pr_pasn_params->peer_addr)) { + wpa_printf(MSG_INFO, "PR: Invalid peer address (all zeros)"); + return -1; + } + + /* Frequency must be set */ + if (!pr_pasn_params->freq) { + wpa_printf(MSG_INFO, "PR: Invalid frequency (zero)"); + return -1; + } + + /* ranging_type must have at least one valid bit and no unknown bits */ + if (!pr_pasn_params->ranging_type || + (pr_pasn_params->ranging_type & + ~(PR_EDCA_BASED_RANGING | PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING))) { + wpa_printf(MSG_INFO, "PR: Invalid ranging_type=0x%x", + pr_pasn_params->ranging_type); + return -1; + } + + /* ranging_role must have at least one valid bit and no unknown bits */ + if (!pr_pasn_params->ranging_role || + (pr_pasn_params->ranging_role & + ~(PR_ISTA_SUPPORT | PR_RSTA_SUPPORT))) { + wpa_printf(MSG_INFO, "PR: Invalid ranging_role=0x%x", + pr_pasn_params->ranging_role); + return -1; + } + + is_edca = pr_pasn_params->ranging_type & PR_EDCA_BASED_RANGING; + is_ntb = pr_pasn_params->ranging_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING); + is_ista = pr_pasn_params->ranging_role & PR_ISTA_SUPPORT; + + /* Validate ranging type against device capabilities */ + if (is_edca) { + if (is_ista && !cfg->edca_ista_support) { + wpa_printf(MSG_INFO, + "PR: EDCA ISTA ranging not supported by device"); + return -1; + } + if (!is_ista && !cfg->edca_rsta_support) { + wpa_printf(MSG_INFO, + "PR: EDCA RSTA ranging not supported by device"); + return -1; + } + } + + if (is_ntb) { + if (is_ista && !cfg->ntb_ista_support) { + wpa_printf(MSG_INFO, + "PR: NTB ISTA ranging not supported by device"); + return -1; + } + if (!is_ista && !cfg->ntb_rsta_support) { + wpa_printf(MSG_INFO, + "PR: NTB RSTA ranging not supported by device"); + return -1; + } + + /* Secure LTF requires explicit device support */ + if ((pr_pasn_params->ranging_type & + PR_NTB_SECURE_LTF_BASED_RANGING) && + !cfg->secure_he_ltf) { + wpa_printf(MSG_INFO, + "PR: Secure HE-LTF NTB ranging not supported by device"); + return -1; + } + + /* min must not exceed max time between measurements */ + if (pr_pasn_params->min_time_between_measurements && + pr_pasn_params->max_time_between_measurements && + pr_pasn_params->min_time_between_measurements > + pr_pasn_params->max_time_between_measurements * 100) { + wpa_printf(MSG_INFO, + "PR: min_time_between_measurements=%u > max=%u (units: 100us vs 10ms)", + pr_pasn_params->min_time_between_measurements, + pr_pasn_params->max_time_between_measurements); + return -1; + } + } + + /* lmr_feedback is only valid for NTB ranging */ + if (pr_pasn_params->lmr_feedback && !is_ntb) { + wpa_printf(MSG_INFO, + "PR: lmr_feedback is only valid for NTB ranging"); + return -1; + } + + wpa_printf(MSG_DEBUG, "PR: Ranging request validation successful"); + return 0; +} + + +/** + * wpas_pr_pasn_trigger - Entry point to trigger PASN authentication for PR + */ +int wpas_pr_pasn_trigger(struct wpa_supplicant *wpa_s, + struct pr_pasn_ranging_params *pr_pasn_params) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!pr_pasn_params) { + wpa_printf(MSG_DEBUG, "PR PASN: trigger: NULL params"); + return -1; + } + + if (!pr) { + wpa_printf(MSG_DEBUG, "PR PASN: trigger: PR not initialized"); + return -1; + } + + if (pr->pr_pasn_params) { + wpa_printf(MSG_DEBUG, + "PR PASN: auth_trigger: Already in progress"); + pr_pasn_params->pr_pasn_status = PASN_STATUS_FAILURE; + return -1; + } + + /* Validate request before proceeding */ + if (wpas_pr_validate_ranging_request(wpa_s, pr_pasn_params) < 0) { + wpa_printf(MSG_DEBUG, "PR PASN: Request validation failed"); + pr_pasn_params->pr_pasn_status = PASN_STATUS_FAILURE; + return -1; + } + + if (pr_pasn_params->action == PR_PASN_AND_RANGING) { + wpa_printf(MSG_DEBUG, + "PR PASN: Triggering PASN authentication for " MACSTR + " type=%u role=%u mode=%u freq=%d", + MAC2STR(pr_pasn_params->peer_addr), + pr_pasn_params->ranging_type, + pr_pasn_params->ranging_role, + pr_pasn_params->auth_mode, + pr_pasn_params->freq); + + /* Allocate and store the params to track the request */ + pr->pr_pasn_params = os_zalloc(sizeof(*pr->pr_pasn_params)); + if (!pr->pr_pasn_params) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to allocate params"); + pr_pasn_params->pr_pasn_status = PASN_STATUS_FAILURE; + return -1; + } + + os_memcpy(pr->pr_pasn_params, pr_pasn_params, + sizeof(*pr->pr_pasn_params)); + + /* Ensure peer device entry exists before setting credentials */ + if (pr_pasn_params->pmk_len > 0 || + pr_pasn_params->password_valid) { + if (pr_ensure_oob_peer(pr, pr_pasn_params->peer_addr, + pr_pasn_params->freq) < 0 || + pr_set_peer_credentials( + pr, pr_pasn_params->peer_addr, + pr_pasn_params->pmk_len > 0 ? + pr_pasn_params->pmk : NULL, + pr_pasn_params->pmk_len, + pr_pasn_params->password_valid ? + pr_pasn_params->password : NULL) < 0) { + pr_pasn_params->pr_pasn_status = + PASN_STATUS_FAILURE; + wpas_pr_clear_ranging_params(pr); + return -1; + } + } + + /* Log EDCA parameters if applicable */ + if (pr_pasn_params->ranging_type & PR_EDCA_BASED_RANGING) { + wpa_printf(MSG_DEBUG, + "PR PASN: EDCA params - burst_period=%u num_bursts_exp=%u ftms_per_burst=%u ftmr_retries=%u burst_duration=%u", + pr_pasn_params->burst_period, + pr_pasn_params->num_bursts_exp, + pr_pasn_params->ftms_per_burst, + pr_pasn_params->ftmr_retries, + pr_pasn_params->burst_duration); + } + + /* Log NTB parameters if applicable */ + if (pr_pasn_params->ranging_type & + (PR_NTB_SECURE_LTF_BASED_RANGING | + PR_NTB_OPEN_BASED_RANGING)) { + wpa_printf(MSG_DEBUG, + "PR PASN: NTB params - min_time=%u max_time=%u aw=%u nominal_time=%u", + pr_pasn_params->min_time_between_measurements, + pr_pasn_params->max_time_between_measurements, + pr_pasn_params->availability_window, + pr_pasn_params->nominal_time); + } + + /* Log location request parameters */ + if (pr_pasn_params->request_lci || + pr_pasn_params->request_civicloc) { + wpa_printf(MSG_DEBUG, + "PR PASN: Location requests - LCI=%d CivicLoc=%d", + pr_pasn_params->request_lci, + pr_pasn_params->request_civicloc); + } + + /* Initiate PASN authentication for the peer */ + if (wpas_pr_initiate_pasn_auth(wpa_s, pr_pasn_params->peer_addr, + pr_pasn_params->freq, + pr_pasn_params->auth_mode, + pr_pasn_params->ranging_role, + pr_pasn_params->ranging_type, + pr_pasn_params->forced_pr_freq, + pr_pasn_params->src_addr, + pr_pasn_params->pasn_role)) { + wpa_printf(MSG_DEBUG, + "PR PASN: Failed to initiate PASN for " + MACSTR, + MAC2STR(pr_pasn_params->peer_addr)); + pr_pasn_params->pr_pasn_status = PASN_STATUS_FAILURE; + wpas_pr_clear_ranging_params(pr); + return -1; + } + return 0; + } else { + wpa_printf(MSG_INFO, + "PR PASN: Unsupported action %u, ignoring request", + pr_pasn_params->action); + pr_pasn_params->pr_pasn_status = PASN_STATUS_FAILURE; + return -1; + } +} + + +int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked) +{ + struct pr_data *pr = wpa_s->global->pr; + int ret; + + if (!wpa_s->pr_pasn_auth_work && is_zero_ether_addr(wpa_s->pd_addr)) + return -1; + + ret = pr_pasn_auth_tx_status(pr, data, data_len, acked); + if (ret == 2) { + eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, + NULL); + eloop_register_timeout(0, + PR_PASN_AUTH1_RETRY_INTERVAL_MS * 1000, + wpas_pr_pasn_auth_retry_timeout, + wpa_s, NULL); + return 0; + } + + return ret; +} + + +static void wpas_pr_pasn_auth_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct pr_data *pr = wpa_s->global->pr; + + if (!pr || !pr->pr_pasn_params) + return; + + pr_pasn_auth_retransmit(pr, pr->pr_pasn_params->peer_addr); +} + + +static int wpas_pr_check_pd_wdev_create(struct wpa_supplicant *wpa_s) +{ + if (is_zero_ether_addr(wpa_s->pr_responder_src_addr)) { + wpa_printf(MSG_DEBUG, + "PR PASN: M1 received in responder mode, no custom MAC - using existing interface"); + return 0; + } + + wpa_printf(MSG_DEBUG, + "PR PASN: M1 received in responder mode, creating PD wdev"); + + if (wpas_pr_start_pd(wpa_s, wpa_s->pr_responder_src_addr) < 0) { + wpa_printf(MSG_INFO, + "PR PASN: Failed to create PD wdev for responder"); + return -1; + } + + return 0; +} + + +int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct pr_data *pr = wpa_s->global->pr; + int ret; + + if (!pr) + return -2; + + /* + * Responder path: when we are waiting for PASN M1 on the parent + * interface ROC, create the PD wdev on first receipt of Auth1 before + * handing the frame to the common layer. + */ + if (wpa_s->pr_responder_mode && + len >= offsetof(struct ieee80211_mgmt, u.auth.variable)) { + u16 auth_transaction; + + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + if (auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH1) { + if (wpas_pr_check_pd_wdev_create(wpa_s) < 0) + return -1; + + /* + * Cancel the total-budget timer first so it does not + * fire after we have already handed off to the PASN + * layer. + */ + eloop_cancel_timeout(wpas_pr_pasn_roc_total_timeout, + wpa_s, NULL); + + /* + * Cancel ROC on the listening interface; the dedicated + * PR interface (if created) will handle all subsequent + * frames. wpas_pr_cancel_roc() also flushes any pending + * pr-pasn-roc chunks that were queued but not yet + * started (M1 can arrive between two ROC chunks when + * pr_roc_work is already NULL but a next chunk is + * already queued). + */ + wpas_pr_cancel_roc(wpa_s); + + /* Clear responder mode */ + wpa_s->pr_responder_mode = false; + os_memset(wpa_s->pr_responder_src_addr, 0, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "PR PASN: M1 processed, proceeding with PASN"); + } + } + + ret = pr_pasn_auth_rx(pr, mgmt, len, freq); + + if (ret < 0) { + eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL); + wpas_pr_pasn_auth_work_done(wpa_s); + wpas_pr_pd_stop(wpa_s); + wpas_pr_clear_ranging_params(wpa_s->global->pr); + wpas_notify_pr_ranging_terminated(wpa_s, + PR_SESSION_END_NEG_FAILED); + } + + return ret; +} + + +void wpas_pr_abort_ranging(struct wpa_supplicant *wpa_s) +{ + struct pr_data *pr = wpa_s->global->pr; + + if (!pr) { + wpa_printf(MSG_DEBUG, "PR: abort_ranging: PR not initialized"); + return; + } + + /* Check if there's an active ranging session */ + if (is_zero_ether_addr(wpa_s->pd_addr) && !pr->pr_pasn_params) { + wpa_printf(MSG_DEBUG, + "PR: abort_ranging: no active ranging session"); + return; + } + + wpa_printf(MSG_DEBUG, "PR: Aborting ranging session"); + + /* + * Cancel PASN and ROC in case PD wdev is not yet created + * (PASN still in progress or responder ROC active). + */ + wpas_pr_pasn_cancel_auth_work(wpa_s); + wpa_s->pr_pasn_auth_work = NULL; + if (wpa_s->pr_responder_mode) { + wpas_pr_cancel_roc(wpa_s); + wpas_pr_pasn_abort_responder(wpa_s); + } + + /* Stop PD wdev and cleanup all ranging resources */ + wpas_pr_pd_stop(wpa_s); + + eloop_cancel_timeout(wpas_pr_pasn_auth_retry_timeout, wpa_s, NULL); + /* Free ranging params so a new session can be started */ + wpas_pr_clear_ranging_params(pr); + wpas_notify_pr_ranging_terminated(wpa_s, PR_SESSION_END_USER_ABORT); +} + +#endif /* CONFIG_PASN */ diff --git a/contrib/wpa/wpa_supplicant/pr_supplicant.h b/contrib/wpa/wpa_supplicant/pr_supplicant.h new file mode 100644 index 000000000000..e2f05316a931 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/pr_supplicant.h @@ -0,0 +1,148 @@ +/* + * Proxmity Ranging + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PR_SUPPLICANT_H +#define PR_SUPPLICANT_H + +#include "common/proximity_ranging.h" + +#ifdef CONFIG_PR + +int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s, + const struct wpa_driver_capa *capa); +void wpas_pr_flush(struct wpa_supplicant *wpa_s); +void wpas_pr_deinit(struct wpa_supplicant *wpa_s); +void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s); +void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s); +void wpas_pr_clear_dev_iks(struct wpa_supplicant *wpa_s); +void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, const u8 *dik, + const char *password, const u8 *pmk, size_t pmk_len, + bool own); +struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s, + const u8 *src_addr); +void wpas_pr_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, + u16 buf_len, const u8 *peer_addr, + unsigned int freq); +int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq, u8 auth_mode, + u8 ranging_role, u8 ranging_type, + int forced_pr_freq, const u8 *src_addr, + enum pr_pasn_role pasn_role); +int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, bool acked); +int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq); +void wpas_pr_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +int wpas_pr_pasn_trigger(struct wpa_supplicant *wpa_s, + struct pr_pasn_ranging_params *pr_pasn_params); +void wpas_pr_abort_ranging(struct wpa_supplicant *wpa_s); +void wpas_pr_measurement_complete(struct wpa_supplicant *wpa_s, + struct peer_measurement_complete *complete); +void wpas_pr_measurement_result(struct wpa_supplicant *wpa_s, + struct peer_measurement_result *result); + +#else /* CONFIG_PR */ + +static inline int wpas_pr_init(struct wpa_global *global, + struct wpa_supplicant *wpa_s, + const struct wpa_driver_capa *capa) +{ + return 0; +} + +static inline void wpas_pr_flush(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_pr_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_pr_pd_stop(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_pr_update_dev_addr(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_pr_clear_dev_iks(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, + const u8 *dik, const char *password, + const u8 *pmk, size_t pmk_len, bool own) +{ +} + +static inline struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s, + const u8 *src_addr) +{ + return NULL; +} + +static inline int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq, + u8 auth_mode, u8 ranging_role, + u8 ranging_type, + int forced_pr_freq, + const u8 *src_addr, + enum pr_pasn_role pasn_role) +{ + return 0; +} + +static inline int wpas_pr_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, + const u8 *data, size_t data_len, + bool acked) +{ + return 0; +} + +static inline int wpas_pr_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, + size_t len, int freq) +{ + return 0; +} + +static inline void +wpas_pr_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ +} + +static inline int +wpas_pr_pasn_trigger(struct wpa_supplicant *wpa_s, + struct pr_pasn_ranging_params *pr_pasn_params) +{ + return -1; +} + +static inline void wpas_pr_abort_ranging(struct wpa_supplicant *wpa_s) +{ +} + +static inline void +wpas_pr_measurement_complete(struct wpa_supplicant *wpa_s, + struct peer_measurement_complete *complete) +{ +} + +static inline void +wpas_pr_measurement_result(struct wpa_supplicant *wpa_s, + struct peer_measurement_result *result) +{ +} + +#endif /* CONFIG_PR */ + +#endif /* PR_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index f266ce4f409a..67c716f93830 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -318,7 +318,7 @@ int main(int argc, char *argv[]) } os_memset(&wpa_s, 0, sizeof(wpa_s)); - wpa_s.conf = wpa_config_read(argv[1], NULL, false); + wpa_s.conf = wpa_config_read(argv[1], NULL, false, true); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", argv[1]); return -1; diff --git a/contrib/wpa/wpa_supplicant/robust_av.c b/contrib/wpa/wpa_supplicant/robust_av.c index a3edf3bee80e..a1196a33b8b1 100644 --- a/contrib/wpa/wpa_supplicant/robust_av.c +++ b/contrib/wpa/wpa_supplicant/robust_av.c @@ -1,6 +1,7 @@ /* * wpa_supplicant - Robust AV procedures * Copyright (c) 2020, The Linux Foundation + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -205,8 +206,8 @@ skip_tclas_elem: qos_elem->mask &= ~SCS_QOS_BIT_SERVICE_START_TIME_LINKID; - /* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, - * Figure 9-1001av (Control Info field format) + /* IEEE Std 802.11be-2024, 9.4.2.326 QoS Characteristics + * element, Figure 9-1074bd (Control Info field format) */ control_info = ((u32) qos_elem->direction << EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET); @@ -263,6 +264,51 @@ end: } +static int populate_type10_classifier_data(const struct tclas_element *src, + struct tclas_element *dst, + unsigned int num_tclas_elem) +{ + struct type10_params *t10_param; + unsigned int i; + size_t filter_len; + + if (!src) + return 0; + + if (!dst) + return -1; + + for (i = 0; i < num_tclas_elem; i++, src++, dst++) { + if (src->classifier_type != 10) + continue; + + filter_len = src->frame_classifier.type10_param.filter_len; + if (!filter_len) + continue; + + t10_param = &dst->frame_classifier.type10_param; + t10_param->filter_value = os_memdup( + src->frame_classifier.type10_param.filter_value, + filter_len); + if (!t10_param->filter_value) + return -1; + + t10_param->filter_mask = os_memdup( + src->frame_classifier.type10_param.filter_mask, + filter_len); + if (!t10_param->filter_mask) { + os_free(t10_param->filter_value); + t10_param->filter_value = NULL; + return -1; + } + + t10_param->filter_len = filter_len; + } + + return 0; +} + + int wpas_send_mscs_req(struct wpa_supplicant *wpa_s) { struct wpabuf *buf; @@ -278,6 +324,13 @@ int wpas_send_mscs_req(struct wpa_supplicant *wpa_s) return -1; } + if (wpa_s->mscs_setup_done && + wpa_s->robust_av.request_type == SCS_REQ_ADD) { + wpa_msg(wpa_s, MSG_INFO, + "MSCS: Failed to send MSCS ADD request: MSCS session already active"); + return -1; + } + if (!wpa_s->mscs_setup_done && wpa_s->robust_av.request_type != SCS_REQ_ADD) { wpa_msg(wpa_s, MSG_INFO, @@ -471,6 +524,16 @@ static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem, } +static void scs_cleanup_descriptors(struct active_scs_elem *scs_elem) +{ + if (!scs_elem) + return; + dl_list_del(&scs_elem->list); + free_up_tclas_elem(&scs_elem->desc_elem); + os_free(scs_elem); +} + + static void scs_request_timer(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -497,10 +560,9 @@ static void scs_request_timer(void *eloop_ctx, void *timeout_ctx) " SCSID=%u status_code=timedout", MAC2STR(src), scs_desc->scs_id); - dl_list_del(&scs_desc->list); wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout", __func__, scs_desc->scs_id); - os_free(scs_desc); + scs_cleanup_descriptors(scs_desc); } eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); @@ -508,17 +570,19 @@ static void scs_request_timer(void *eloop_ctx, void *timeout_ctx) } -int wpas_send_scs_req(struct wpa_supplicant *wpa_s) +static int _wpa_send_scs_req(struct wpa_supplicant *wpa_s, + struct scs_desc_elem *desc_elem, + unsigned int num_scs_desc) { struct wpabuf *buf = NULL; - struct scs_desc_elem *desc_elem = NULL; const struct ieee80211_eht_capabilities *eht; const u8 *eht_ie; int ret = -1; unsigned int i; bool allow_scs_traffic_desc = false; - if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid || + !desc_elem) return -1; if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) { @@ -527,10 +591,6 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) return -1; } - desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; - if (!desc_elem) - return -1; - if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss)) allow_scs_traffic_desc = true; @@ -540,7 +600,7 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) if (wpa_s->connection_eht && eht_ie && eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) { eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3]; - if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC) + if (le_to_host16(eht->mac_cap) & EHT_MACCAP_SCS_TRAFFIC_DESC) allow_scs_traffic_desc = true; } @@ -550,9 +610,7 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) return -1; } - buf = allocate_scs_buf(desc_elem, - wpa_s->scs_robust_av_req.num_scs_desc, - allow_scs_traffic_desc); + buf = allocate_scs_buf(desc_elem, num_scs_desc, allow_scs_traffic_desc); if (!buf) return -1; @@ -563,8 +621,7 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) wpa_s->scs_dialog_token++; wpabuf_put_u8(buf, wpa_s->scs_dialog_token); - for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; - i++, desc_elem++) { + for (i = 0; i < num_scs_desc; i++, desc_elem++) { /* SCS Descriptor element */ if (wpas_populate_scs_descriptor_ie(desc_elem, buf, allow_scs_traffic_desc) < 0) @@ -581,22 +638,6 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) goto end; } - desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; - for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; - i++, desc_elem++) { - struct active_scs_elem *active_scs_elem; - - if (desc_elem->request_type != SCS_REQ_ADD) - continue; - - active_scs_elem = os_malloc(sizeof(struct active_scs_elem)); - if (!active_scs_elem) - break; - active_scs_elem->scs_id = desc_elem->scs_id; - active_scs_elem->status = SCS_DESC_SENT; - dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list); - } - /* * Register a timeout after which this request will be removed from * the cache. @@ -607,8 +648,141 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) end: wpabuf_free(buf); - free_up_scs_desc(&wpa_s->scs_robust_av_req); + return ret; +} + +int wpas_scs_reconfigure(struct wpa_supplicant *wpa_s) +{ + struct scs_desc_elem *desc_elems, *desc_data, desc_elem; + struct active_scs_elem *scs_desc; + unsigned int num_scs_desc; + unsigned int i; + unsigned int num_tclas_elem; + int ret = -1; + + if (!wpa_s->scs_reconfigure) + return 0; + + num_scs_desc = dl_list_len(&wpa_s->active_scs_ids); + if (!num_scs_desc) + return 0; + + desc_elems = os_calloc(num_scs_desc, sizeof(struct scs_desc_elem)); + if (!desc_elems) { + wpa_printf(MSG_ERROR, "SCS: Failed to allocate memory"); + return ret; + } + + num_scs_desc = 0; + dl_list_for_each(scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + os_memcpy(&desc_elem, &scs_desc->desc_elem, sizeof(desc_elem)); + + num_tclas_elem = scs_desc->desc_elem.num_tclas_elem; + if (num_tclas_elem) { + desc_elem.tclas_elems = + os_memdup(scs_desc->desc_elem.tclas_elems, + num_tclas_elem * + sizeof(struct tclas_element)); + if (!desc_elem.tclas_elems) + goto end; + + if (populate_type10_classifier_data( + scs_desc->desc_elem.tclas_elems, + desc_elem.tclas_elems, + num_tclas_elem) < 0) { + free_up_tclas_elem(&desc_elem); + goto end; + } + } + + os_memcpy(&desc_elems[num_scs_desc], &desc_elem, + sizeof(desc_elem)); + num_scs_desc++; + } + + if (_wpa_send_scs_req(wpa_s, desc_elems, num_scs_desc) < 0) { + wpa_printf(MSG_DEBUG, + "SCS: Failed to reconfigure SCS requests - retain for next roaming"); + } else { + dl_list_for_each(scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) + scs_desc->status = SCS_DESC_SENT; + } + ret = 0; +end: + wpa_s->scs_reconfigure = false; + + if (desc_elems) { + desc_data = desc_elems; + for (i = 0; i < num_scs_desc; i++, desc_data++) { + if (desc_data->tclas_elems) + free_up_tclas_elem(desc_data); + } + os_free(desc_elems); + } + + return ret; +} + + +int wpas_send_scs_req(struct wpa_supplicant *wpa_s) +{ + struct scs_desc_elem *desc_elem = NULL; + int ret = -1; + unsigned int i; + + if (_wpa_send_scs_req(wpa_s, wpa_s->scs_robust_av_req.scs_desc_elems, + wpa_s->scs_robust_av_req.num_scs_desc) < 0) + goto end; + + desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; + + for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; + i++, desc_elem++) { + struct active_scs_elem *active_scs_elem; + struct tclas_element *tclas_elem; + unsigned int num_tclas_elem = desc_elem->num_tclas_elem; + + if (desc_elem->request_type != SCS_REQ_ADD) + continue; + + active_scs_elem = os_zalloc(sizeof(struct active_scs_elem)); + if (!active_scs_elem) + break; + + os_memcpy(&active_scs_elem->desc_elem, desc_elem, + sizeof(struct scs_desc_elem)); + + if (num_tclas_elem) { + tclas_elem = os_memdup(desc_elem->tclas_elems, + num_tclas_elem * + sizeof(struct tclas_element)); + if (!tclas_elem) { + os_free(active_scs_elem); + goto end; + } + + active_scs_elem->desc_elem.tclas_elems = tclas_elem; + + if (populate_type10_classifier_data( + desc_elem->tclas_elems, tclas_elem, + num_tclas_elem) < 0) { + free_up_tclas_elem(&active_scs_elem->desc_elem); + os_free(active_scs_elem); + goto end; + } + } + + active_scs_elem->scs_id = desc_elem->scs_id; + active_scs_elem->status = SCS_DESC_SENT; + dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list); + } + ret = 0; /* Success */ + +end: + free_up_scs_desc(&wpa_s->scs_robust_av_req); return ret; } @@ -729,7 +903,8 @@ event_mscs_result: void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, const u8 *buf, size_t len) + const u8 *dst, const u8 *src, + const u8 *buf, size_t len) { u8 dialog_token; u16 status_code; @@ -755,6 +930,14 @@ void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, return; } + if (is_multicast_ether_addr(dst)) { + wpa_printf(MSG_DEBUG, + "MSCS: Ignore group-addressed MSCS Response frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(dst), MAC2STR(src)); + return; + } + status_code = WPA_GET_LE16(buf); buf += 2; len -= 2; @@ -835,8 +1018,8 @@ void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s, void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, const u8 *buf, - size_t len) + const u8 *dst, const u8 *src, + const u8 *buf, size_t len) { u8 dialog_token; unsigned int i, count; @@ -850,6 +1033,14 @@ void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, return; } + if (is_multicast_ether_addr(dst)) { + wpa_printf(MSG_DEBUG, + "SCS: Ignore group-addressed SCS Response frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(dst), MAC2STR(src)); + return; + } + dialog_token = *buf++; len--; if (dialog_token != wpa_s->scs_dialog_token) { @@ -895,12 +1086,10 @@ void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, continue; } - if (status != WLAN_STATUS_SUCCESS) { - dl_list_del(&scs_desc->list); - os_free(scs_desc); - } else if (status == WLAN_STATUS_SUCCESS) { + if (status == WLAN_STATUS_SUCCESS) scs_desc->status = SCS_DESC_SUCCESS; - } + else + scs_cleanup_descriptors(scs_desc); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR " SCSID=%u status_code=%u", MAC2STR(src), id, status); @@ -916,8 +1105,7 @@ void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, WPA_EVENT_SCS_RESULT "bssid=" MACSTR " SCSID=%u status_code=response_not_received", MAC2STR(src), scs_desc->scs_id); - dl_list_del(&scs_desc->list); - os_free(scs_desc); + scs_cleanup_descriptors(scs_desc); } } } @@ -929,8 +1117,7 @@ static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s) while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids, struct active_scs_elem, list))) { - dl_list_del(&scs_elem->list); - os_free(scs_elem); + scs_cleanup_descriptors(scs_elem); } } @@ -942,6 +1129,7 @@ void wpas_scs_deinit(struct wpa_supplicant *wpa_s) wpas_clear_active_scs_ids(wpa_s); eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); wpa_s->ongoing_scs_req = false; + wpa_s->scs_reconfigure = false; } @@ -1469,7 +1657,7 @@ static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id, void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, + const u8 *dst, const u8 *src, const u8 *buf, size_t len) { int rem_len; @@ -1504,6 +1692,14 @@ void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s, return; } + if (is_multicast_ether_addr(dst)) { + wpa_printf(MSG_DEBUG, + "QM: Ignore group-addressed DSCP Policy Request frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(dst), MAC2STR(src)); + return; + } + if (len < 3) { wpa_printf(MSG_ERROR, "Received QoS Management DSCP Policy Request frame with invalid length %zu", diff --git a/contrib/wpa/wpa_supplicant/rrm.c b/contrib/wpa/wpa_supplicant/rrm.c index 2ec43105ceec..2430aeee7bcf 100644 --- a/contrib/wpa/wpa_supplicant/rrm.c +++ b/contrib/wpa/wpa_supplicant/rrm.c @@ -56,14 +56,25 @@ void wpas_rrm_reset(struct wpa_supplicant *wpa_s) /* * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report * @wpa_s: Pointer to wpa_supplicant + * @da: DA of the received frame + * @sa: SA of the received frame * @report: Neighbor report buffer, prefixed by a 1-byte dialog token * @report_len: Length of neighbor report buffer */ void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *da, const u8 *sa, const u8 *report, size_t report_len) { struct wpabuf *neighbor_rep; + if (is_multicast_ether_addr(da)) { + wpa_printf(MSG_DEBUG, + "RRM: Ignore group-addressed Neighbor Report Response frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(da), MAC2STR(sa)); + return; + } + wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); if (report_len < 1) return; @@ -160,6 +171,7 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RRM_ENABLED_CAPABILITIES); if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || + rrm_ie[1] < 1 || !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { wpa_dbg(wpa_s, MSG_DEBUG, "RRM: No network support for Neighbor Report."); @@ -188,6 +200,24 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), wpa_s->rrm.next_neighbor_rep_token); + /* + * According to IEEE Std 802.11-2024, 11.10.10.2 (Requesting a neighbor + * report) LCI and civic requests depend on FTM responder support. + */ + if (lci || civic) { + const u8 *ext_capab; + + ext_capab = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_EXT_CAPAB); + if (!ieee802_11_ext_capab(ext_capab, + WLAN_EXT_CAPAB_FTM_RESPONDER)) { + wpa_printf(MSG_DEBUG, + "AP doesn't support FTM responder, can't request LCI and civic"); + lci = 0; + civic = 0; + } + } + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); @@ -197,8 +227,10 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); } - if (lci) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + if (lci && rrm_ie[1] >= 2 && + (rrm_ie[3] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + /* IEEE Std 802.11-2024, 9.4.2.19 (Measurement Request element) + */ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); @@ -215,13 +247,14 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* IEEE Std 802.11-2024, 9.4.2.19.10 (LCI request) */ /* Location Subject */ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); /* Optional Subelements */ /* - * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * IEEE Std 802.11-2024, Figure 9-265 (Maximum Age subelement + * format) * The Maximum Age subelement is required, otherwise the AP can * send only data that was determined after receiving the * request. Setting it here to unlimited age. @@ -229,10 +262,14 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); wpabuf_put_u8(buf, 2); wpabuf_put_le16(buf, 0xffff); + } else if (lci) { + wpa_printf(MSG_DEBUG, "RRM: LCI request isn't supported by AP"); } - if (civic) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + if (civic && rrm_ie[1] >= 5 && + (rrm_ie[6] & WLAN_RRM_CAPS_CIVIC_LOCATION_MEASUREMENT)) { + /* IEEE Std 802.11-2024, 9.4.2.19 (Measurement Request element) + */ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); @@ -250,8 +287,7 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, /* Measurement Type */ wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: - * Location Civic request */ + /* IEEE Std 802.11-2024, 9.4.2.19.14 (Location Civic request) */ /* Location Subject */ wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ @@ -261,6 +297,9 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, */ wpabuf_put_le16(buf, 0); /* No optional subelements */ + } else if (civic) { + wpa_printf(MSG_DEBUG, + "RRM: Civic request isn't supported by AP"); } wpa_s->rrm.next_neighbor_rep_token++; @@ -712,18 +751,22 @@ static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s, int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, u8 *op_class, u8 *chan, u8 *phy_type) { - const u8 *ie; - int sec_chan = 0, vht = 0; - struct ieee80211_ht_operation *ht_oper = NULL; - struct ieee80211_vht_operation *vht_oper = NULL; - u8 seg0, seg1; + int sec_chan = 0, chanwidth = 0; + struct ieee802_11_elems elems; + struct ieee80211_ht_operation *ht_oper; - ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); - if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) { - u8 sec_chan_offset; + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return -1; - ht_oper = (struct ieee80211_ht_operation *) (ie + 2); - sec_chan_offset = ht_oper->ht_param & + chanwidth = get_operation_channel_width(&elems); + if (chanwidth == CHAN_WIDTH_UNKNOWN) { + wpa_printf(MSG_DEBUG, "Cannot determine channel width"); + return -1; + } + + ht_oper = (struct ieee80211_ht_operation *) elems.ht_operation; + if (ht_oper) { + u8 sec_chan_offset = ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) sec_chan = 1; @@ -732,42 +775,16 @@ int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, sec_chan = -1; } - ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); - if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) { - vht_oper = (struct ieee80211_vht_operation *) (ie + 2); - - switch (vht_oper->vht_op_info_chwidth) { - case CHANWIDTH_80MHZ: - seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; - seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; - if (seg1 && abs(seg1 - seg0) == 8) - vht = CONF_OPER_CHWIDTH_160MHZ; - else if (seg1) - vht = CONF_OPER_CHWIDTH_80P80MHZ; - else - vht = CONF_OPER_CHWIDTH_80MHZ; - break; - case CHANWIDTH_160MHZ: - vht = CONF_OPER_CHWIDTH_160MHZ; - break; - case CHANWIDTH_80P80MHZ: - vht = CONF_OPER_CHWIDTH_80P80MHZ; - break; - default: - vht = CONF_OPER_CHWIDTH_USE_HT; - break; - } - } - - if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class, + if (ieee80211_chaninfo_to_channel(freq, chanwidth, sec_chan, op_class, chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, "Cannot determine operating class and channel"); return -1; } - *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, - vht_oper != NULL); + *phy_type = ieee80211_get_phy_type(freq, elems.ht_operation != NULL, + elems.vht_operation != NULL, + elems.he_operation != NULL); if (*phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "Cannot determine phy type"); return -1; @@ -1553,7 +1570,7 @@ int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, /* If the measurement was aborted, don't report partial results */ if (info->aborted) - goto out; + goto out_refuse; wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR, MAC2STR(info->scan_start_tsf_bssid), @@ -1562,7 +1579,7 @@ int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, !wpas_beacon_rep_scan_match(wpa_s, info->scan_start_tsf_bssid)) { wpa_printf(MSG_DEBUG, "RRM: Ignore scan results due to mismatching TSF BSSID"); - goto out; + goto out_refuse; } for (i = 0; i < scan_res->num; i++) { @@ -1632,6 +1649,10 @@ int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, wpas_rrm_send_msr_report(wpa_s, buf); wpabuf_free(buf); + goto out; + +out_refuse: + wpas_rrm_refuse_request(wpa_s); out: wpas_clear_beacon_rep_data(wpa_s); return 1; diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index 8b59e409b81d..c143cd2cfff2 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -81,11 +81,22 @@ static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, #endif /* CONFIG_WPS */ -static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params, +static int wpa_setup_mac_addr_rand_params(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, const u8 *mac_addr) { u8 *tmp; + /* If we are connected, there is no point in randomization */ + if (wpa_s && wpa_s->wpa_state > WPA_SCANNING) + return 0; + +#ifdef CONFIG_P2P + /* The P2P GO might ignore our probe requests otherwise */ + if (wpa_s && wpa_s->p2p_in_provisioning) + return 0; +#endif /* CONFIG_P2P */ + if (params->mac_addr) { params->mac_addr_mask = NULL; os_free(params->mac_addr); @@ -198,9 +209,9 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) return; } - if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && - wpa_s->wpa_state <= WPA_SCANNING) - wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan); + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) + wpa_setup_mac_addr_rand_params(wpa_s, params, + wpa_s->mac_addr_scan); if (wpas_update_random_addr_disassoc(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, @@ -329,8 +340,8 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } if (!ctx || - radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb, - ctx) < 0) { + !radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb, + ctx)) { wpa_scan_free_params(ctx); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); return -1; @@ -705,12 +716,12 @@ static struct wpabuf * wpa_supplicant_ml_probe_ie(int mld_id, u16 links) wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links); for_each_link(links, link_id) { - wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE); + wpabuf_put_u8(extra_ie, MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE); /* Subelement length includes only the control */ wpabuf_put_u8(extra_ie, 2); - control = link_id | EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK; + control = link_id | BASIC_MLE_STA_CTRL_COMPLETE_PROFILE; wpabuf_put_le16(extra_ie, control); } @@ -750,17 +761,20 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab), NULL); if (ext_capab_len > 0 && + (size_t) ext_capab_len < wpa_s->drv_max_probe_req_ie_len && wpabuf_resize(&extra_ie, ext_capab_len) == 0) wpabuf_put_data(extra_ie, ext_capab, ext_capab_len); #ifdef CONFIG_INTERWORKING if (wpa_s->conf->interworking && + wpa_s->drv_max_probe_req_ie_len >= 2 && wpabuf_resize(&extra_ie, 100) == 0) wpas_add_interworking_elements(wpa_s, extra_ie); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_MBO - if (wpa_s->enable_oce & OCE_STA) + if ((wpa_s->enable_oce & OCE_STA) && + wpa_s->drv_max_probe_req_ie_len >= 5) wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie); #endif /* CONFIG_MBO */ @@ -774,17 +788,19 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) &wpa_s->wps->dev, wpa_s->wps->uuid, req_type, 0, NULL); - if (wps_ie) { - if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) - wpabuf_put_buf(extra_ie, wps_ie); - wpabuf_free(wps_ie); - } + if (wps_ie && + wpabuf_len(wps_ie) <= wpa_s->drv_max_probe_req_ie_len && + wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) + wpabuf_put_buf(extra_ie, wps_ie); + wpabuf_free(wps_ie); } #ifdef CONFIG_P2P if (wps) { size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - if (wpabuf_resize(&extra_ie, ielen) == 0) + + if (ielen <= wpa_s->drv_max_probe_req_ie_len && + wpabuf_resize(&extra_ie, ielen) == 0) wpas_p2p_scan_ie(wpa_s, extra_ie); } #endif /* CONFIG_P2P */ @@ -794,12 +810,14 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS */ #ifdef CONFIG_HS20 - if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0) + if (wpa_s->conf->hs20 && wpa_s->drv_max_probe_req_ie_len >= 9 && + wpabuf_resize(&extra_ie, 9) == 0) wpas_hs20_add_indication(extra_ie, -1, 0); #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST if (wpa_s->fst_ies && + wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(wpa_s->fst_ies) && wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0) wpabuf_put_buf(extra_ie, wpa_s->fst_ies); #endif /* CONFIG_FST */ @@ -813,7 +831,8 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]; - if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0) + if (wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(buf) && + wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0) wpabuf_put_buf(extra_ie, buf); } @@ -940,9 +959,9 @@ static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s, } -static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - struct wpa_ssid *ssid, size_t max_ssids) +void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + const struct wpa_ssid *ssid, size_t max_ssids) { #ifdef CONFIG_OWE struct wpa_bss *bss; @@ -954,8 +973,7 @@ static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - const u8 *owe, *pos, *end; - const u8 *owe_ssid; + const u8 *owe, *owe_bssid, *owe_ssid; size_t owe_ssid_len; if (bss->ssid_len != ssid->ssid_len || @@ -966,21 +984,9 @@ static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, if (!owe || owe[1] < 4) continue; - pos = owe + 6; - end = owe + 2 + owe[1]; - - /* Must include BSSID and ssid_len */ - if (end - pos < ETH_ALEN + 1) - return; - - /* Skip BSSID */ - pos += ETH_ALEN; - owe_ssid_len = *pos++; - owe_ssid = pos; - - if ((size_t) (end - pos) < owe_ssid_len || - owe_ssid_len > SSID_MAX_LEN) - return; + if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid, + &owe_ssid_len)) + continue; wpa_printf(MSG_DEBUG, "OWE: scan_ssids: transition mode OWE ssid=%s", @@ -1464,9 +1470,9 @@ ssid_list_set: } #endif /* CONFIG_P2P */ - if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && - wpa_s->wpa_state <= WPA_SCANNING) - wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_scan); + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) + wpa_setup_mac_addr_rand_params(wpa_s, ¶ms, + wpa_s->mac_addr_scan); if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) { struct wpa_bss *bss; @@ -1490,7 +1496,14 @@ ssid_list_set: } } else if (!is_zero_ether_addr(wpa_s->ml_probe_bssid)) { wpa_printf(MSG_DEBUG, "Scanning for ML probe request"); - params.bssid = wpa_s->ml_probe_bssid; + /* + * When the scan visits the channels of the missing links in + * addition to the association link's channel, use the wildcard + * BSSID so that the affiliated APs on those channels respond + * to the probe requests, too. + */ + if (!params.freqs || !params.freqs[0] || !params.freqs[1]) + params.bssid = wpa_s->ml_probe_bssid; params.min_probe_req_content = true; } @@ -1940,9 +1953,8 @@ scan: wpa_setband_scan_freqs(wpa_s, scan_params); - if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) && - wpa_s->wpa_state <= WPA_SCANNING) - wpa_setup_mac_addr_rand_params(¶ms, + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) + wpa_setup_mac_addr_rand_params(wpa_s, ¶ms, wpa_s->mac_addr_sched_scan); wpa_scan_set_relative_rssi_params(wpa_s, scan_params); @@ -2381,7 +2393,9 @@ static int wpa_scan_result_compar(const void *a, const void *b) int wpa_a, wpa_b; int snr_a, snr_b, snr_a_full, snr_b_full; size_t ies_len; +#ifndef CONFIG_NO_WPA const u8 *rsne_a, *rsne_b; +#endif /* CONFIG_NO_WPA */ /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || @@ -2425,6 +2439,7 @@ static int wpa_scan_result_compar(const void *a, const void *b) snr_b = snr_b_full = wb->level; } +#ifndef CONFIG_NO_WPA /* If SNR of a SAE BSS is good or at least as high as the PSK BSS, * prefer SAE over PSK for mixed WPA3-Personal transition mode and * WPA2-Personal deployments */ @@ -2450,6 +2465,7 @@ static int wpa_scan_result_compar(const void *a, const void *b) (snr_b >= GREAT_SNR || snr_b >= snr_a)) return 1; } +#endif /* CONFIG_NO_WPA */ /* If SNR is close, decide by max rate or frequency band. For cases * involving the 6 GHz band, use the throughput estimate irrespective @@ -2803,7 +2819,140 @@ static const struct minsnr_bitrate_entry he160_table[] = { { -1, 1441200 } /* SNR > 51 */ }; -/* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1 +/* See IEEE Std 802.11be-2024, Table 36-78 - EHT-MCSs for 484+242-tone MRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht60_table[] = { + { 0, 0 }, + { 8, 25800 }, /* EHT80 with 20 MHz punctured MCS0 */ + { 11, 51600 }, /* EHT80 with 20 MHz punctured MCS1 */ + { 15, 77400 }, /* EHT80 with 20 MHz punctured MCS2 */ + { 17, 103200 }, /* EHT80 with 20 MHz punctured MCS3 */ + { 21, 154900 }, /* EHT80 with 20 MHz punctured MCS4 */ + { 24, 206500 }, /* EHT80 with 20 MHz punctured MCS5 */ + { 26, 232300 }, /* EHT80 with 20 MHz punctured MCS6 */ + { 31, 258100 }, /* EHT80 with 20 MHz punctured MCS7 */ + { 35, 309700 }, /* EHT80 with 20 MHz punctured MCS8 */ + { 37, 344100 }, /* EHT80 with 20 MHz punctured MCS9 */ + { 40, 387100 }, /* EHT80 with 20 MHz punctured MCS10 */ + { 42, 430100 }, /* EHT80 with 20 MHz punctured MCS11 */ + { 45, 464600 }, /* EHT80 with 20 MHz punctured MCS12 */ + { 48, 516200 }, /* EHT80 with 20 MHz punctured MCS13 */ + { -1, 516200 } /* SNR > 48 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-80 - EHT-MCSs for 996+484-tone MRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht120_table[] = { + { 0, 0 }, + { 11, 53200 }, /* EHT160 with 40 MHz punctured MCS0 */ + { 14, 106500 }, /* EHT160 with 40 MHz punctured MCS1 */ + { 18, 159700 }, /* EHT160 with 40 MHz punctured MCS2 */ + { 20, 212900 }, /* EHT160 with 40 MHz punctured MCS3 */ + { 24, 319400 }, /* EHT160 with 40 MHz punctured MCS4 */ + { 27, 425900 }, /* EHT160 with 40 MHz punctured MCS5 */ + { 29, 479100 }, /* EHT160 with 40 MHz punctured MCS6 */ + { 34, 532400 }, /* EHT160 with 40 MHz punctured MCS7 */ + { 38, 638800 }, /* EHT160 with 40 MHz punctured MCS8 */ + { 40, 709800 }, /* EHT160 with 40 MHz punctured MCS9 */ + { 43, 798500 }, /* EHT160 with 40 MHz punctured MCS10 */ + { 45, 887200 }, /* EHT160 with 40 MHz punctured MCS11 */ + { 48, 958200 }, /* EHT160 with 40 MHz punctured MCS12 */ + { 51, 1064700 }, /* EHT160 with 40 MHz punctured MCS13 */ + { -1, 1064700 } /* SNR > 51 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-81 - EHT-MCSs for 996+484+242-tone MRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht140_table[] = { + { 0, 0 }, + { 11, 61800 }, /* EHT160 with 20 MHz punctured MCS0 */ + { 14, 123700 }, /* EHT160 with 20 MHz punctured MCS1 */ + { 18, 185500 }, /* EHT160 with 20 MHz punctured MCS2 */ + { 20, 247400 }, /* EHT160 with 20 MHz punctured MCS3 */ + { 24, 371000 }, /* EHT160 with 20 MHz punctured MCS4 */ + { 27, 494700 }, /* EHT160 with 20 MHz punctured MCS5 */ + { 29, 556500 }, /* EHT160 with 20 MHz punctured MCS6 */ + { 34, 618400 }, /* EHT160 with 20 MHz punctured MCS7 */ + { 38, 742100 }, /* EHT160 with 20 MHz punctured MCS8 */ + { 40, 824500 }, /* EHT160 with 20 MHz punctured MCS9 */ + { 43, 927600 }, /* EHT160 with 20 MHz punctured MCS10 */ + { 45, 1030600 }, /* EHT160 with 20 MHz punctured MCS11 */ + { 48, 1113100 }, /* EHT160 with 20 MHz punctured MCS12 */ + { 51, 1236800 }, /* EHT160 with 20 MHz punctured MCS13 */ + { -1, 1236800 } /* SNR > 51 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-83 - EHT-MCSs for 2x996+484-tone NRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht200_table[] = { + { 0, 0 }, + { 14, 89300 }, /* EHT320 with 120 MHz punctured MCS0 */ + { 17, 178500 }, /* EHT320 with 120 MHz punctured MCS1 */ + { 21, 267800 }, /* EHT320 with 120 MHz punctured MCS2 */ + { 23, 357100 }, /* EHT320 with 120 MHz punctured MCS3 */ + { 27, 535600 }, /* EHT320 with 120 MHz punctured MCS4 */ + { 30, 714100 }, /* EHT320 with 120 MHz punctured MCS5 */ + { 32, 803400 }, /* EHT320 with 120 MHz punctured MCS6 */ + { 37, 892600 }, /* EHT320 with 120 MHz punctured MCS7 */ + { 41, 1071200 }, /* EHT320 with 120 MHz punctured MCS8 */ + { 43, 1190100 }, /* EHT320 with 120 MHz punctured MCS9 */ + { 46, 1339000 }, /* EHT320 with 120 MHz punctured MCS10 */ + { 48, 1487700 }, /* EHT320 with 120 MHz punctured MCS11 */ + { 51, 1606800 }, /* EHT320 with 120 MHz punctured MCS12 */ + { 54, 1785300 }, /* EHT320 with 120 MHz punctured MCS13 */ + { -1, 1785300 } /* SNR > 54 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-84 - EHT-MCSs for 3x996-tone MRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht240_table[] = { + { 0, 0 }, + { 14, 108100 }, /* EHT320 with 80 MHz punctured MCS0 */ + { 17, 216200 }, /* EHT320 with 80 MHz punctured MCS1 */ + { 21, 324300 }, /* EHT320 with 80 MHz punctured MCS2 */ + { 23, 432400 }, /* EHT320 with 80 MHz punctured MCS3 */ + { 27, 648500 }, /* EHT320 with 80 MHz punctured MCS4 */ + { 30, 864700 }, /* EHT320 with 80 MHz punctured MCS5 */ + { 32, 972800 }, /* EHT320 with 80 MHz punctured MCS6 */ + { 37, 1080900 }, /* EHT320 with 80 MHz punctured MCS7 */ + { 41, 1297100 }, /* EHT320 with 80 MHz punctured MCS8 */ + { 43, 1441200 }, /* EHT320 with 80 MHz punctured MCS9 */ + { 46, 1621300 }, /* EHT320 with 80 MHz punctured MCS10 */ + { 48, 1801500 }, /* EHT320 with 80 MHz punctured MCS11 */ + { 51, 1945600 }, /* EHT320 with 80 MHz punctured MCS12 */ + { 54, 2161800 }, /* EHT320 with 80 MHz punctured MCS13 */ + { -1, 2161800 } /* SNR > 54 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-85: EHT-MCSs for 3x996+484-tone MRU, + * NSS,u = 1 + */ +static const struct minsnr_bitrate_entry eht280_table[] = { + { 0, 0 }, + { 14, 125300 }, /* EHT320 with 40 MHz punctured MCS0 */ + { 17, 250600 }, /* EHT320 with 40 MHz punctured MCS1 */ + { 21, 375900 }, /* EHT320 with 40 MHz punctured MCS2 */ + { 23, 501200 }, /* EHT320 with 40 MHz punctured MCS3 */ + { 27, 751800 }, /* EHT320 with 40 MHz punctured MCS4 */ + { 30, 1002400 }, /* EHT320 with 40 MHz punctured MCS5 */ + { 32, 1127600 }, /* EHT320 with 40 MHz punctured MCS6 */ + { 37, 1252900 }, /* EHT320 with 40 MHz punctured MCS7 */ + { 41, 1503500 }, /* EHT320 with 40 MHz punctured MCS8 */ + { 43, 1670600 }, /* EHT320 with 40 MHz punctured MCS9 */ + { 46, 1879400 }, /* EHT320 with 40 MHz punctured MCS10 */ + { 48, 2088200 }, /* EHT320 with 40 MHz punctured MCS11 */ + { 51, 2255300 }, /* EHT320 with 40 MHz punctured MCS12 */ + { 54, 2505900 }, /* EHT320 with 40 MHz punctured MCS13 */ + { -1, 2505900 } /* SNR > 54 */ +}; + +/* See IEEE Std 802.11be-2024, Table 36-86: EHT-MCSs for 4x996-tone RU, + * NSS,u = 1 */ static const struct minsnr_bitrate_entry eht320_table[] = { { 0, 0 }, @@ -2892,6 +3041,96 @@ static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[], } +static unsigned int get_eht_punctured_rate(enum chan_width cw, + u8 num_punct_bits, int adjusted_snr, + u8 boost) +{ + const struct minsnr_bitrate_entry *eht_table; + + switch (cw) { + case CHAN_WIDTH_80: + switch (num_punct_bits) { + case 1: + /* EHT80 with 20 MHz punctured */ + eht_table = eht60_table; + break; + default: + eht_table = he80_table; + break; + } + break; + case CHAN_WIDTH_160: + switch (num_punct_bits) { + case 2: + /* EHT160 with 40 MHz punctured */ + eht_table = eht120_table; + break; + case 1: + /* EHT160 with 20 MHz punctured */ + eht_table = eht140_table; + break; + default: + eht_table = he160_table; + break; + } + break; + case CHAN_WIDTH_320: + switch (num_punct_bits) { + case 6: + /* EHT320 with 120 MHz punctured */ + eht_table = eht200_table; + break; + case 4: + /* EHT320 with 80 MHz punctured */ + eht_table = eht240_table; + break; + case 2: + /* EHT320 with 40 MHz punctured */ + eht_table = eht280_table; + break; + default: + eht_table = eht320_table; + break; + } + break; + default: + /* Puncturing is not supported for the channel width */ + return 0; + } + + return max_he_eht_rate(eht_table, adjusted_snr, true) + boost; +} + + +static u8 get_eht_num_punct_bits(const u8 *ies, size_t ies_len) +{ + const u8 *eht_ie; + + eht_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_EHT_OPERATION); + if (eht_ie && eht_ie[1] >= 1 + sizeof(struct ieee80211_eht_operation)) { + struct ieee80211_eht_operation *eht_op; + + eht_op = (struct ieee80211_eht_operation *) &eht_ie[3]; + + if (eht_op->oper_params & + EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT) { + u16 punct_bitmap; + u8 count = 0; + + punct_bitmap = le_to_host16( + eht_op->oper_info.disabled_chan_bitmap); + while (punct_bitmap) { + count += punct_bitmap & 1; + punct_bitmap >>= 1; + } + return count; + } + } + + return 0; +} + + unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, const u8 *ies, size_t ies_len, int rate, int snr, int freq, enum chan_width *max_cw) @@ -3049,8 +3288,9 @@ unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, struct ieee80211_eht_capabilities *eht; struct he_capabilities *own_he; u8 cw, boost = 2; - const u8 *eht_ie; + const u8 *eht_ie = NULL; bool is_eht = false; + u8 num_punct_bits; ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES); if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN)) @@ -3100,8 +3340,17 @@ unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, *max_cw = CHAN_WIDTH_80; adjusted_snr = snr + wpas_channel_width_rssi_bump( ies, ies_len, CHAN_WIDTH_80); - tmp = max_he_eht_rate(he80_table, adjusted_snr, - is_eht) + boost; + + num_punct_bits = get_eht_num_punct_bits(ies, ies_len); + if (is_eht && num_punct_bits) + tmp = get_eht_punctured_rate(CHAN_WIDTH_80, + num_punct_bits, + adjusted_snr, + boost); + else + tmp = max_he_eht_rate(he80_table, adjusted_snr, + is_eht) + boost; + if (tmp > est) est = tmp; } @@ -3115,13 +3364,21 @@ unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, *max_cw = CHAN_WIDTH_160; adjusted_snr = snr + wpas_channel_width_rssi_bump( ies, ies_len, CHAN_WIDTH_160); - tmp = max_he_eht_rate(he160_table, adjusted_snr, - is_eht) + boost; + + num_punct_bits = get_eht_num_punct_bits(ies, ies_len); + if (is_eht && num_punct_bits) + tmp = get_eht_punctured_rate(CHAN_WIDTH_160, + num_punct_bits, + adjusted_snr, + boost); + else + tmp = max_he_eht_rate(he160_table, adjusted_snr, + is_eht) + boost; if (tmp > est) est = tmp; } - if (!is_eht) + if (!is_eht || !eht_ie) return est; eht = (struct ieee80211_eht_capabilities *) &eht_ie[3]; @@ -3134,7 +3391,16 @@ unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s, *max_cw = CHAN_WIDTH_320; adjusted_snr = snr + wpas_channel_width_rssi_bump( ies, ies_len, CHAN_WIDTH_320); - tmp = max_he_eht_rate(eht320_table, adjusted_snr, true); + + num_punct_bits = get_eht_num_punct_bits(ies, ies_len); + if (num_punct_bits) + tmp = get_eht_punctured_rate(CHAN_WIDTH_320, + num_punct_bits, + adjusted_snr, + 0); + else + tmp = max_he_eht_rate(eht320_table, adjusted_snr, + true); if (tmp > est) est = tmp; } @@ -3167,6 +3433,231 @@ void scan_est_throughput(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_IEEE80211BE + +static struct wpa_scan_res * +wpa_scan_get_bssid(struct wpa_scan_results *scan_res, const u8 *bssid) +{ + size_t i; + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *scan_res_item = scan_res->res[i]; + + if (ether_addr_equal(scan_res_item->bssid, bssid)) + return scan_res_item; + } + + return NULL; +} + + +static unsigned int +get_partner_est_tput(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct wpa_scan_res *res, u8 mbssid_idx, + const struct ieee80211_neighbor_ap_info *ap_info, + size_t len, u16 *seen) +{ + const u8 *pos, *end; + const u8 *mld_params; + u8 count, mld_params_offset; + u8 i, type, link_id; + unsigned int total_partner_tput = 0; + + if (len < sizeof(*ap_info)) + return 0; + + count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1; + type = ap_info->tbtt_info_hdr & RNR_TBTT_INFO_HDR_TYPE_MSK; + + if (type != 0 || ap_info->tbtt_info_len < RNR_TBTT_INFO_MLD_LEN) + return 0; + + mld_params_offset = RNR_TBTT_INFO_LEN; + + pos = (const u8 *) ap_info; + end = pos + len; + pos += sizeof(*ap_info); + for (i = 0; i < count; i++) { + if (end - pos < ap_info->tbtt_info_len) + break; + + mld_params = pos + mld_params_offset; + if (mld_params_offset + 2 > ap_info->tbtt_info_len) + break; + + link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK; + if (link_id < MAX_NUM_MLD_LINKS && + *mld_params == mbssid_idx && + ((*seen & BIT(link_id)) == 0)) { + struct wpa_scan_res *neigh_scan_res; + + neigh_scan_res = wpa_scan_get_bssid(scan_res, pos + 1); + if (!neigh_scan_res) + continue; + + *seen |= BIT(link_id); + if (neigh_scan_res->mlo_tput_accumulated) { + res->mlo_tput_accumulated = true; + return neigh_scan_res->est_throughput; + } + + total_partner_tput += neigh_scan_res->est_throughput; + } + pos += ap_info->tbtt_info_len; + } + + return total_partner_tput; +} + + +static void mlo_scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct wpa_scan_res *res, + bool sta_emlsr_support) +{ + const u8 *ies = (const u8 *) (res + 1); + size_t ie_len = res->ie_len; + u8 mbssid_idx = 0; + const struct element *elem; + const u8 *mle; + u8 mle_len; + const u8 *ap_eml_capa; + bool ap_emlsr_support = false; + u16 seen; + unsigned int mlo_est_throughput = res->est_throughput; + unsigned int partner_est_throughput = 0; + int link_id; + + if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) + return; + + if (!ie_len) + ie_len = res->beacon_ie_len; + + mle = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mle) + return; + mle_len = mle[1]; + if (mle_len < 2) + return; + mle += 3; + mle_len--; + + ap_eml_capa = get_basic_mle_eml_capa(mle, mle_len); + if (ap_eml_capa) { + u16 eml_capa = WPA_GET_LE16(ap_eml_capa); + + if (eml_capa & EHT_ML_EML_CAPA_EMLSR_SUPP) + ap_emlsr_support = true; + } + + link_id = get_basic_mle_link_id(mle, mle_len); + if (link_id < 0) + return; + + seen = BIT(link_id); + elem = (const struct element *) + wpa_scan_get_ie(res, WLAN_EID_MULTIPLE_BSSID_INDEX); + if (elem && elem->datalen >= 1) + mbssid_idx = elem->data[0]; + + /* + * The following three scenarios need to be considered when estimating + * throughput for ML cases: + * 1. Non-AP MLD is MLSR and finds an AP MLD that does not support MLSR + * 2. Non-AP MLD is MLSR and finds an AP MLD that supports MLSR + * 3. Non-AP MLD is MLMR and finds an AP MLD that may or may not + * support MLSR + * + * In the first case, since the non-AP MLD is in MLSR mode and detects + * an AP MLD that doesn't support MLSR, keep the AP MLD's original + * throughput unchanged. + * + * In the second case, since the non-AP MLD is in MLSR mode, select the + * partner link with the highest throughput and update the scan result + * accordingly. + * + * In the third case, since the non-AP MLD is in MLMR mode, sum the + * throughput of all links, including partners, regardless of AP MLD + * support. + */ + + for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, + ies, ie_len) { + const struct ieee80211_neighbor_ap_info *ap_info; + const u8 *pos = elem->data; + size_t len = elem->datalen; + + /* RNR element may contain more than one Neighbor AP Info */ + while (sizeof(*ap_info) <= len) { + size_t ap_info_len = sizeof(*ap_info); + u8 count; + + ap_info = + (const struct ieee80211_neighbor_ap_info *) pos; + count = + RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + + 1; + ap_info_len += count * ap_info->tbtt_info_len; + + if (ap_info_len > len) + return; + + partner_est_throughput = + get_partner_est_tput(wpa_s, scan_res, res, + mbssid_idx, ap_info, + len, &seen); + + if (sta_emlsr_support && !ap_emlsr_support) + return; + + if (sta_emlsr_support && ap_emlsr_support) { + if (partner_est_throughput > + mlo_est_throughput) + mlo_est_throughput = + partner_est_throughput; + } else { + /* + * The following additional scenarios need to + * be handled here: + * + * 1. The current scan result is the first scan + * result with its partners unseen: + * Loop through all partners, accumulate + * their estimated throughput and update + * its own bss->est_throughput to the new + * accumulated throughput. + * + * 2. Scan result with one of its partners + * already seen: + * Iterator returns the partner's + * est_throughput. Once it sees the partner + * who has an accumulated throughput, it + * marks self as seen and overrides its + * bss->est_throughput with the same. + */ + if (!res->mlo_tput_accumulated) { + mlo_est_throughput += + partner_est_throughput; + } else { + res->est_throughput = + partner_est_throughput; + return; + } + } + + pos += ap_info_len; + len -= ap_info_len; + } + } + res->mlo_tput_accumulated = true; + res->est_throughput = mlo_est_throughput; +} + +#endif /* CONFIG_IEEE80211BE */ + + /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data @@ -3217,6 +3708,14 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211BE + for (i = 0; i < scan_res->num; i++) { + mlo_scan_est_throughput(wpa_s, scan_res, scan_res->res[i], + wpa_s->eml_capa & + EHT_ML_EML_CAPA_EMLSR_SUPP); + } +#endif /* CONFIG_IEEE80211BE */ + if (scan_res->res) { qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), compar); @@ -3371,7 +3870,7 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) } if (src->mac_addr_rand && - wpa_setup_mac_addr_rand_params(params, src->mac_addr)) + wpa_setup_mac_addr_rand_params(NULL, params, src->mac_addr)) goto failed; if (src->bssid) { @@ -3561,9 +4060,9 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) params.freqs = wpa_s->manual_sched_scan_freqs; } - if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) && - wpa_s->wpa_state <= WPA_SCANNING) - wpa_setup_mac_addr_rand_params(¶ms, wpa_s->mac_addr_pno); + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) + wpa_setup_mac_addr_rand_params(wpa_s, ¶ms, + wpa_s->mac_addr_pno); wpa_scan_set_relative_rssi_params(wpa_s, ¶ms); diff --git a/contrib/wpa/wpa_supplicant/scan.h b/contrib/wpa/wpa_supplicant/scan.h index d4c06c1aed40..7ea99928ca27 100644 --- a/contrib/wpa/wpa_supplicant/scan.h +++ b/contrib/wpa/wpa_supplicant/scan.h @@ -104,5 +104,8 @@ int wpas_channel_width_rssi_bump(const u8 *ies, size_t ies_len, enum chan_width cw); int wpas_adjust_snr_by_chanwidth(const u8 *ies, size_t ies_len, enum chan_width max_cw, int snr); +void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + const struct wpa_ssid *ssid, size_t max_ssids); #endif /* SCAN_H */ diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index e6538e871e3a..459b0a8b3710 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -11,15 +11,22 @@ #include "common.h" #include "utils/eloop.h" #include "utils/ext_password.h" +#include "common/dragonfly.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/ocv.h" +#include "common/ptksa_cache.h" +#include "crypto/crypto.h" #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" #include "common/sae.h" #include "common/dpp.h" +#include "crypto/random.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" +#include "rsn_supp/wpa_ie.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -35,11 +42,78 @@ #define SME_AUTH_TIMEOUT 5 #define SME_ASSOC_TIMEOUT 5 +#ifdef CONFIG_ENC_ASSOC +static const int dot11RSNAConfigPMKLifetime = 43200; +#endif /* CONFIG_ENC_ASSOC */ static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +static int sme_validate_basic_mle(const struct ieee802_11_elems *elems, + const u8 *addr); +#ifdef CONFIG_IEEE8021X_AUTH +static void sme_process_802_1x_auth_response(struct wpa_supplicant *wpa_s, + struct auth_info *auth, + bool external); +#endif /* CONFIG_IEEE8021X_AUTH */ + +#if defined(CONFIG_IEEE8021X_AUTH) || defined(CONFIG_ENC_ASSOC) + +static const u8 * sme_get_peer_addr(struct wpa_supplicant *wpa_s, bool external) +{ + if (external) + return wpa_s->sme.ext_ml_auth ? + wpa_s->sme.ext_auth_ap_mld_addr : + wpa_s->sme.ext_auth_bssid; + return wpa_s->valid_links ? wpa_s->ap_mld_addr : wpa_s->pending_bssid; +} + + +const u8 * sme_get_ext_auth_pmkid(struct wpa_supplicant *wpa_s) +{ + struct rsn_pmksa_cache *cache; + struct rsn_pmksa_cache_entry *entry; + const u8 *peer; + + peer = sme_get_peer_addr(wpa_s, true); + if (!peer) + return NULL; + + cache = wpa_sm_get_pmksa_cache(wpa_s->wpa); + if (!cache) + return NULL; + + entry = pmksa_cache_get(cache, peer, wpa_s->own_addr, NULL, + wpa_s->sme.ext_auth_wpa_ssid, + wpa_s->sme.ext_auth_key_mgmt); + return entry ? entry->pmkid : NULL; +} + +#endif /* CONFIG_IEEE8021X_AUTH || CONFIG_ENC_ASSOC */ + + +#ifdef CONFIG_IEEE8021X_AUTH + +static int sme_get_key_mgmt(struct wpa_supplicant *wpa_s, bool external) +{ + return external ? wpa_s->sme.ext_auth_key_mgmt : wpa_s->key_mgmt; +} + + +static int sme_get_pairwise_cipher(struct wpa_supplicant *wpa_s, bool external) +{ + return external ? wpa_s->sme.ext_pairwise_cipher : + wpa_s->pairwise_cipher; +} + + +static bool sme_is_ml_auth(struct wpa_supplicant *wpa_s, bool external) +{ + return external ? wpa_s->sme.ext_ml_auth : wpa_s->valid_links; +} + +#endif /* CONFIG_IEEE8021X_AUTH */ #ifdef CONFIG_SAE @@ -105,6 +179,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt : wpa_s->key_mgmt; const u8 *addr = mld_addr ? mld_addr : bssid; + enum sae_pwe sae_pwe; + const u8 *password_id = (const u8 *) ssid->sae_password_id; + size_t password_id_len = ssid->sae_password_id ? + os_strlen(ssid->sae_password_id) : 0; if (ret_use_pt) *ret_use_pt = 0; @@ -190,19 +268,24 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, if (bss) { const u8 *rsnxe; - rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); - if (rsnxe && rsnxe[1] >= 1) + rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false); + if (rsnxe && rsnxe[0] == WLAN_EID_VENDOR_SPECIFIC && + rsnxe[1] >= 1 + 4) + rsnxe_capa = rsnxe[2 + 4]; + else if (rsnxe && rsnxe[1] >= 1) rsnxe_capa = rsnxe[2]; } + sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); + if (ssid->sae_password_id && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) use_pt = 1; if (wpa_key_mgmt_sae_ext_key(key_mgmt) && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) use_pt = 1; if (bss && is_6ghz_freq(bss->freq) && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) use_pt = 1; #ifdef CONFIG_SAE_PK if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) && @@ -222,14 +305,14 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_SAE_PK */ - if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || - wpa_s->conf->sae_pwe == SAE_PWE_BOTH) { + if (use_pt || sae_pwe == SAE_PWE_HASH_TO_ELEMENT || + sae_pwe == SAE_PWE_BOTH) { use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E)); - if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT || + if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT || ssid->sae_password_id || wpa_key_mgmt_sae_ext_key(key_mgmt)) && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK && !use_pt) { wpa_printf(MSG_DEBUG, "SAE: Cannot use H2E with the selected AP"); @@ -238,7 +321,7 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, } if (use_pt && !ssid->pt) - wpa_s_setup_sae_pt(wpa_s->conf, ssid, true); + wpa_s_setup_sae_pt(wpa_s, ssid, true); if (use_pt && sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt, wpa_s->own_addr, addr, @@ -279,8 +362,13 @@ reuse_data: else wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS); } + + if (use_pt && ssid->pt && ssid->pt->password_id) { + password_id = wpabuf_head(ssid->pt->password_id); + password_id_len = wpabuf_len(ssid->pt->password_id); + } if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token, - ssid->sae_password_id) < 0) { + password_id, password_id_len) < 0) { wpabuf_free(buf); goto fail; } @@ -319,6 +407,296 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s, #endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE8021X_AUTH + +static void sme_802_1x_auth_data_free(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s || !wpa_s->auth_1x) + return; + + if (wpa_s->auth_1x->ecdh) { + crypto_ecdh_deinit(wpa_s->auth_1x->ecdh); + wpa_s->auth_1x->ecdh = NULL; + } + + wpabuf_clear_free(wpa_s->auth_1x->dhss); + wpa_s->auth_1x->dhss = NULL; + + os_free(wpa_s->auth_1x); + wpa_s->auth_1x = NULL; + + if (wpa_s->eapol) + eapol_sm_set_eap_over_auth_frame(wpa_s->eapol, false); +} + + +static struct wpabuf * +sme_build_802_1x_for_ptk(struct wpa_supplicant *wpa_s, bool external) +{ + struct wpabuf *pubkey_buf = NULL; + struct wpabuf *buf = NULL; + int rsne_len, rsnxe_len; + size_t total_len = 0; + + /* Generate SNonce */ + if (random_get_bytes(wpa_s->auth_1x->snonce, WPA_NONCE_LEN) < 0) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to generate SNonce"); + goto fail; + } + + /* Initialize ECDH for Diffie-Hellman Parameter element */ + /* TODO: Add support for other groups */ + wpa_s->auth_1x->dh_group = 19; + wpa_s->auth_1x->ecdh = crypto_ecdh_init(wpa_s->auth_1x->dh_group); + if (!wpa_s->auth_1x->ecdh) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to init ECDH group %d", + wpa_s->auth_1x->dh_group); + goto fail; + } + + pubkey_buf = crypto_ecdh_get_pubkey(wpa_s->auth_1x->ecdh, 0); + if (!pubkey_buf) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to get ECDH pubkey"); + goto fail; + } + + /* Generate RSNE */ + if (external) + rsne_len = wpa_external_auth_add_rsne( + wpa_s->auth_1x->rsne, sizeof(wpa_s->auth_1x->rsne), + wpa_s->sme.ext_auth_key_mgmt, + wpa_s->sme.ext_pairwise_cipher, + wpa_s->sme.ext_group_cipher, + wpa_s->sme.ext_mgmt_group_cipher, + wpa_s->sme.ext_rsn_capab, + sme_get_ext_auth_pmkid(wpa_s)); + else + rsne_len = wpa_gen_wpa_ie_rsn( + wpa_s->auth_1x->rsne, sizeof(wpa_s->auth_1x->rsne), + wpa_s->pairwise_cipher, wpa_s->group_cipher, + wpa_s->key_mgmt, wpa_s->mgmt_group_cipher, wpa_s->wpa); + if (rsne_len < 0) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to generate RSNE"); + goto fail; + } + wpa_s->auth_1x->rsne_len = rsne_len; + + /* Generate RSNXE */ + if (external) { + /* Store driver-provided RSNXE directly */ + os_memcpy(wpa_s->auth_1x->rsnxe, wpa_s->sme.ext_rsnxe, + wpa_s->sme.ext_rsnxe_len); + rsnxe_len = wpa_s->sme.ext_rsnxe_len; + } else { + rsnxe_len = wpa_gen_rsnxe(wpa_s->wpa, wpa_s->auth_1x->rsnxe, + sizeof(wpa_s->auth_1x->rsnxe)); + } + if (rsnxe_len < 0) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to generate RSNXE"); + goto fail; + } + wpa_s->auth_1x->rsnxe_len = rsnxe_len; + + total_len = 3 + WPA_NONCE_LEN + rsne_len + rsnxe_len + + 3 + 2 + wpabuf_len(pubkey_buf); + + buf = wpabuf_alloc(total_len); + if (!buf) { + wpa_dbg(wpa_s, MSG_INFO, "Failed to allocate buf"); + goto fail; + } + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + WPA_NONCE_LEN); + wpabuf_put_u8(buf, WLAN_EID_EXT_NONCE); + wpabuf_put_data(buf, wpa_s->auth_1x->snonce, WPA_NONCE_LEN); + + wpabuf_put_data(buf, wpa_s->auth_1x->rsne, rsne_len); + wpabuf_put_data(buf, wpa_s->auth_1x->rsnxe, rsnxe_len); + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + 2 + wpabuf_len(pubkey_buf)); + wpabuf_put_u8(buf, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(buf, wpa_s->auth_1x->dh_group); + wpabuf_put_buf(buf, pubkey_buf); + + wpabuf_free(pubkey_buf); + + return buf; + +fail: + wpabuf_free(pubkey_buf); + sme_802_1x_auth_data_free(wpa_s); + return NULL; +} + + +static struct wpabuf * sme_build_802_1x_auth_start(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + bool external) +{ + struct wpabuf *buf, *eapol_pdu; + size_t buf_len; + u32 suite = 0; + struct wpabuf *buf_for_ptk = NULL; + int key_mgmt = sme_get_key_mgmt(wpa_s, external); + + if (wpa_key_mgmt_wpa_ieee8021x(key_mgmt & ~WPA_KEY_MGMT_IEEE8021X)) + suite = wpa_akm_to_suite(key_mgmt); + + if (suite == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "No matching IEEE 802.1X AKM found"); + return NULL; + } + + eapol_pdu = eapol_sm_get_eapol_pdu(wpa_s->eapol, + IEEE802_1X_TYPE_EAPOL_START); + if (!eapol_pdu) + return NULL; + + buf_len = 2 + 2 + 2 + wpabuf_len(eapol_pdu); + + if (wpa_s->auth_1x->derive_ptk) { + buf_for_ptk = sme_build_802_1x_for_ptk(wpa_s, external); + if (!buf_for_ptk) { + wpabuf_free(eapol_pdu); + return NULL; + } + buf_len += wpabuf_len(buf_for_ptk); + } else { + buf_len += 7; /* AKM Suite Selector element */ + } + + buf = wpabuf_alloc(buf_len); + if (!buf) { + wpabuf_free(eapol_pdu); + wpabuf_free(buf_for_ptk); + return NULL; + } + + wpabuf_put_le16(buf, wpa_s->auth_1x->auth_trans); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, wpabuf_len(eapol_pdu)); + wpabuf_put_buf(buf, eapol_pdu); + + if (wpa_s->auth_1x->derive_ptk) { + wpabuf_put_buf(buf, buf_for_ptk); + wpabuf_free(buf_for_ptk); + } else { + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + 4); + wpabuf_put_u8(buf, WLAN_EID_EXT_AKM_SUITE_SELECTOR); + wpabuf_put_be32(buf, suite); + } + + wpabuf_free(eapol_pdu); + return buf; +} + + +static struct wpabuf * +sme_build_802_1x_auth_continue(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf, *eapol_pdu; + + if (wpa_s->auth_1x->status != WLAN_STATUS_SUCCESS) { + buf = wpabuf_alloc(2 + 2 + 2); + if (!buf) + return NULL; + + wpabuf_put_le16(buf, wpa_s->auth_1x->auth_trans); + wpabuf_put_le16(buf, wpa_s->auth_1x->status); + wpabuf_put_le16(buf, 0); + return buf; + } + + eapol_pdu = eapol_sm_get_eapol_pdu(wpa_s->eapol, + IEEE802_1X_TYPE_EAP_PACKET); + if (!eapol_pdu) + return NULL; + + buf = wpabuf_alloc(2 + 2 + 2 + wpabuf_len(eapol_pdu)); + if (!buf) { + wpabuf_free(eapol_pdu); + return NULL; + } + + wpa_s->auth_1x->auth_trans++; + wpabuf_put_le16(buf, wpa_s->auth_1x->auth_trans); + wpabuf_put_le16(buf, wpa_s->auth_1x->status); + wpabuf_put_le16(buf, wpabuf_len(eapol_pdu)); + wpabuf_put_buf(buf, eapol_pdu); + wpabuf_free(eapol_pdu); + + return buf; +} + + +static void sme_check_802_1x_pmksa_caching(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid, + bool external) +{ + struct rsn_pmksa_cache_entry *pmksa; + const u8 *rsnxe, *peer_addr; + int key_mgmt; + + peer_addr = sme_get_peer_addr(wpa_s, external); + key_mgmt = sme_get_key_mgmt(wpa_s, external); + + rsnxe = bss ? wpa_bss_get_ie(bss, WLAN_EID_RSNX) : NULL; + if (ssid->eap_over_auth_frame && + ieee802_11_rsnx_capab(rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION) && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) + wpa_s->auth_1x->derive_ptk = true; + + if (wpa_s->auth_1x->derive_ptk && + pmksa_cache_set_current(wpa_s->wpa, NULL, peer_addr, ssid, 0, NULL, + key_mgmt, false) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "IEEE 802.1X: PMKSA cache entry found, using PMKSA caching"); + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); + wpa_s->auth_1x->pmksa_caching = true; + + pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (pmksa) + os_memcpy(wpa_s->auth_1x->pmkid, pmksa->pmkid, + PMKID_LEN); + } +} + + +static struct wpabuf * +sme_build_802_1x_auth_request(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid, bool start, + bool external) +{ + if (start) { + sme_802_1x_auth_data_free(wpa_s); + wpa_s->auth_1x = os_zalloc(sizeof(struct auth_802_1x_data)); + if (!wpa_s->auth_1x) + return NULL; + + sme_check_802_1x_pmksa_caching(wpa_s, bss, ssid, external); + + if (!wpa_s->auth_1x->pmksa_caching) { + eapol_sm_set_eap_over_auth_frame(wpa_s->eapol, true); + eapol_sm_notify_portEnabled(wpa_s->eapol, true); + } + + wpa_s->auth_1x->auth_trans = 1; + return sme_build_802_1x_auth_start(wpa_s, ssid, external); + } + + return sme_build_802_1x_auth_continue(wpa_s); +} + +#endif /* CONFIG_IEEE8021X_AUTH */ + + /** * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt * @wpa_s: Pointer to wpa_supplicant data @@ -476,7 +854,6 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s, int ie_offset) { struct ieee802_11_elems elems; - const u8 *mld_addr; u16 status_code = data->auth.status_code; if (!wpa_s->valid_links) @@ -501,37 +878,42 @@ static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s, return 0; } - mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); - if (!mld_addr) - return -1; - - wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr)); - - if (!ether_addr_equal(wpa_s->ap_mld_addr, mld_addr)) { - wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected " - MACSTR ")", MAC2STR(wpa_s->ap_mld_addr)); - return -1; - } - - return 0; + return sme_validate_basic_mle(&elems, wpa_s->ap_mld_addr); } static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { + u16 usable_links; u8 i; + wpas_reset_mlo_info(wpa_s); + + if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) + return; + + usable_links = wpa_bss_get_usable_links(wpa_s, bss, ssid, NULL); + if (!usable_links) + return; + + os_memcpy(wpa_s->ap_mld_addr, bss->mld_addr, ETH_ALEN); wpa_s->valid_links = 0; wpa_s->mlo_assoc_link_id = bss->mld_link_id; - for_each_link(bss->valid_links, i) { + for_each_link(usable_links, i) { const u8 *bssid = bss->mld_links[i].bssid; wpa_s->valid_links |= BIT(i); os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN); wpa_s->links[i].freq = bss->mld_links[i].freq; wpa_s->links[i].disabled = bss->mld_links[i].disabled; + wpabuf_free(wpa_s->links[i].ies); + wpa_s->links[i].ies = NULL; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->link_ies[i]) + wpa_s->links[i].ies = wpabuf_dup(wpa_s->link_ies[i]); +#endif /* CONFIG_TESTING_OPTIONS */ if (bss->mld_link_id == i) wpa_s->links[i].bss = bss; @@ -545,6 +927,273 @@ static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s, } +static void sme_add_assoc_req_ie(struct wpa_supplicant *wpa_s, + const struct wpabuf *buf) +{ + size_t len; + u8 *pos, *end; + + if (!buf) + return; + + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + end = wpa_s->sme.assoc_req_ie + sizeof(wpa_s->sme.assoc_req_ie); + if (pos >= end) + return; + + len = wpabuf_len(buf); + if (len < (size_t) (end - pos)) { + os_memcpy(pos, wpabuf_head(buf), len); + wpa_s->sme.assoc_req_ie_len += len; + } +} + + +#ifdef CONFIG_ENC_ASSOC + +static int wpas_eppke_initialize(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid) +{ + struct pasn_data *pasn; + const u8 *ap_rsne, *ap_rsnxe; + u8 ap_rsne_len, ap_rsnxe_len; + u64 capab = 0; + int group; + bool derive_kdk; + u8 rsne[80]; + size_t rsne_len = sizeof(rsne); + int len; + + pasn = &wpa_s->pasn; + + group = wpas_pasn_get_group(wpa_s, ssid, pasn); + if (!group) { + wpa_printf(MSG_DEBUG, "EPPKE: No suitable group available"); + return -1; + } + + /* Reset PASN data before initialization to clear state from any + * previous attempt, e.g., on group rejection retries. + */ + wpa_pasn_reset(pasn); + + /* As per IEEE P802.11-REVmf/D2.1, 12.13.5, when SAE is wrapped within + * PASN authentication, both shall use the same finite cyclic group. + * SAE-level group rejection validation is not required. + */ + if (sae_set_group(&wpa_s->sme.sae, group) < 0) { + wpa_printf(MSG_INFO, "EPPKE: Failed to set SAE group %u", + group); + return -1; + } + + /* EPPKE has not been defined to be modified for RSN overriding, so use + * the RSNE and RSNXE from the AP for PASN MIC calculation instead of + * the RSNO elements, if any. */ + ap_rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (!ap_rsne) { + wpa_printf(MSG_DEBUG, "EPPKE: Can't connect without AP RSNE"); + return -1; + } + + ap_rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + + ap_rsne_len = *(ap_rsne + 1) + 2; + ap_rsnxe_len = ap_rsnxe ? *(ap_rsnxe + 1) + 2 : 0; + if (ap_rsne && ap_rsne_len) { + wpabuf_free(pasn->beacon_rsne_rsnxe); + pasn->beacon_rsne_rsnxe = wpabuf_alloc(ap_rsne_len + + ap_rsnxe_len); + if (!pasn->beacon_rsne_rsnxe) { + wpa_printf(MSG_INFO, + "EPPKE: Failed storing AP's RSNE/RSNXE"); + return -1; + } + + wpabuf_put_data(pasn->beacon_rsne_rsnxe, ap_rsne, ap_rsne_len); + if (ap_rsnxe && ap_rsnxe_len) + wpabuf_put_data(pasn->beacon_rsne_rsnxe, + ap_rsnxe, ap_rsnxe_len); + } + + /* Use the RSNXOE, if it was included, for actual AP capability check */ + ap_rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); + if (!ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_KEK_IN_PASN)) { + wpa_printf(MSG_DEBUG, "EPPKE: AP does not support KEK_IN_PASN"); + goto fail; + } + + if (!ieee802_11_rsnx_capab(ap_rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support association frame encryption"); + goto fail; + } + + if (!(wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) { + wpa_printf(MSG_INFO, + "EPPKE: Cannot use EPPKE without support for association frame encryption"); + goto fail; + } + + capab |= BIT(WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + capab |= BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN); +#ifdef CONFIG_PMKSA_PRIVACY + if (ssid->pmksa_privacy) + capab |= BIT(WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY); +#endif /* CONFIG_PMKSA_PRIVACY */ + pasn->derive_kek = true; + + if (0) { +#ifdef CONFIG_SAE + } else if (wpa_key_mgmt_sae_ext_key(ssid->key_mgmt)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + if (!ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SAE_H2E)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support SAE H2E"); + goto fail; + } + if (pasn->pt) + sae_deinit_pt(pasn->pt); + pasn_set_pt(pasn, wpas_pasn_sae_derive_pt_for_eppke(ssid, + group)); + if (!pasn->pt) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to derive PT"); + goto fail; + } + pasn->sae.state = SAE_NOTHING; + pasn->sae.send_confirm = 0; + + /* + * Advertise support for changing password identifiers if + * configured. + */ + if (ssid->sae_password_id && ssid->sae_password_id_change) { + capab |= BIT_ULL(WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE); + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_SAE_PW_ID_CHANGE, 1); + } +#endif /* CONFIG_SAE */ + } else if (wpa_key_mgmt_eppke(ssid->key_mgmt) && + wpa_key_mgmt_only_enhanced_open(ssid->key_mgmt)) { + /* EPPKE without base AKM: verify AP supports unauthenticated + * EPPKE per IEEE P802.11bi/D4.0, 12.16.9.1 */ + if (!ieee802_11_rsnx_capab(ap_rsnxe, + WLAN_RSNX_CAPAB_UNAUTH_EPPKE)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support unauthenticated EPPKE"); + goto fail; + } + } else { + wpa_msg(wpa_s, MSG_INFO, + "EPPKE: Suitable base AKM not enabled in local configuration"); + goto fail; + } + + derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) && + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF); +#ifdef CONFIG_TESTING_OPTIONS + if (!derive_kdk) + derive_kdk = wpa_s->conf->force_kdk_derivation; +#endif /* CONFIG_TESTING_OPTIONS */ + if (derive_kdk) + pasn_enable_kdk_derivation(pasn); + else + pasn_disable_kdk_derivation(pasn); + + wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len); + + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) && + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) + pasn->secure_ltf = true; + else + pasn->secure_ltf = false; + +#ifdef CONFIG_TESTING_OPTIONS + pasn->corrupt_mic = wpa_s->conf->pasn_corrupt_mic; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA) + capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT); + if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA) { + /* + * URNM_MFPR_X20 is a subset of URNM_MFPR which excludes 20 MHz + * bandwidth from mandating protected Management frames. Set + * URNM_MFPR only when URNM_MFPR_X20 is not set. + */ + if (wpa_s->disable_urnm_mfpr) { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR, 0); + } else { + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR, 1); + } + if (wpa_s->urnm_mfpr_x20) { + capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR_X20); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR_X20, + 1); + } else { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_URNM_MFPR_X20, + 0); + } + } + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) && + ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU)) + capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU); + + pasn_set_rsnxe_caps(pasn, capab); + pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa)); + + if (pasn->ecdh) + crypto_ecdh_deinit(pasn->ecdh); + pasn->ecdh = crypto_ecdh_init(group); + if (!pasn->ecdh) { + wpa_printf(MSG_INFO, "EPPKE: Failed to init ECDH"); + goto fail; + } + + len = wpa_gen_wpa_ie(wpa_s->wpa, rsne, rsne_len); + if (len < 0) { + wpa_printf(MSG_INFO, "EPPKE: Failed to generate RSNE"); + goto fail; + } + pasn_set_rsne(pasn, rsne); + wpa_hexdump(MSG_DEBUG, "EPPKE: Set own RSNE default", + pasn->rsn_ie, pasn->rsn_ie_len); + pasn->akmp = wpa_s->key_mgmt; + pasn->cipher = wpa_s->pairwise_cipher; + pasn->group = group; + pasn->freq = bss->freq; + pasn->auth_alg = WLAN_AUTH_EPPKE; + + os_memcpy(pasn->own_addr, wpa_s->own_addr, ETH_ALEN); + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) && + wpa_s->valid_links) { + pasn->is_ml_peer = true; + os_memcpy(pasn->peer_addr, wpa_s->ap_mld_addr, ETH_ALEN); + } else { + os_memcpy(pasn->peer_addr, bss->bssid, ETH_ALEN); + } + os_memcpy(pasn->bssid, bss->bssid, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u", + MAC2STR(pasn->peer_addr), pasn->akmp, + pasn->cipher, pasn->group); + + return 0; + +fail: + wpa_pasn_reset(pasn); + return -1; +} + +#endif /* CONFIG_ENC_ASSOC */ + + static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int start) @@ -568,6 +1217,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, const u8 *mbo_ie; #endif /* CONFIG_MBO */ int omit_rsnxe = 0; + unsigned int keys_to_clear = 0; if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " @@ -578,21 +1228,19 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_memset(¶ms, 0, sizeof(params)); - if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) && - !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr, - NULL, ssid, NULL) && - bss->valid_links) { + wpas_sme_set_mlo_links(wpa_s, bss, ssid); + + if (wpa_s->valid_links) { wpa_printf(MSG_DEBUG, "MLD: In authentication"); - wpas_sme_set_mlo_links(wpa_s, bss, ssid); #ifdef CONFIG_TESTING_OPTIONS bss = wpas_ml_connect_pref(wpa_s, bss, ssid); +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf->mld_force_single_link) { wpa_printf(MSG_DEBUG, "MLD: Force single link"); wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id); } -#endif /* CONFIG_TESTING_OPTIONS */ params.mld = true; params.mld_link_id = wpa_s->mlo_assoc_link_id; params.ap_mld_addr = wpa_s->ap_mld_addr; @@ -643,18 +1291,31 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, const u8 *rsn; struct wpa_ie_data ied; - rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); if (!rsn) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise RSN"); + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied)) { + wpa_printf(MSG_DEBUG, "SME: Failed parsing RSNE data"); + return; #ifdef CONFIG_DPP - } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && - (ssid->key_mgmt & WPA_KEY_MGMT_DPP) && + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) && (ied.key_mgmt & WPA_KEY_MGMT_DPP)) { wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled"); #endif /* CONFIG_DPP */ - } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && - wpa_key_mgmt_sae(ied.key_mgmt)) { +#ifdef CONFIG_ENC_ASSOC + } else if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE) && + (ssid->key_mgmt & WPA_KEY_MGMT_EPPKE) && + (ied.key_mgmt & WPA_KEY_MGMT_EPPKE) && + wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) && + wpa_key_mgmt_sae_ext_key(ied.key_mgmt) && + !wpas_is_sae_avoided(wpa_s, ssid, &ied) && + wpas_eppke_ap_capable(wpa_s, bss, false)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Prefer EPPKE over SAE when both are enabled"); + params.auth_alg = WPA_AUTH_ALG_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ + } else if (wpa_key_mgmt_sae(ied.key_mgmt)) { if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but disallowing SAE auth_alg without PMF"); @@ -669,21 +1330,85 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_ENC_ASSOC + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE) && + wpa_key_mgmt_only_enhanced_open(ssid->key_mgmt) && + wpa_key_mgmt_eppke(ssid->key_mgmt)) { + const u8 *rsn; + struct wpa_ie_data ied; + + rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); + if (!rsn) { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Target BSS does not advertise RSN"); + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied)) { + wpa_printf(MSG_DEBUG, "SME: Failed parsing RSNE data"); + return; + } else if (ied.key_mgmt & WPA_KEY_MGMT_EPPKE && + wpas_eppke_ap_capable(wpa_s, bss, true)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using EPPKE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_EPPKE; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Target BSS does not advertise EPPKE AKM"); + } + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_WEP { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { - if (ssid->wep_key_len[i]) + if (ssid->wep_key_len[i]) { params.wep_key[i] = ssid->wep_key[i]; + keys_to_clear |= BIT(i); + } params.wep_key_len[i] = ssid->wep_key_len[i]; } params.wep_tx_keyidx = ssid->wep_tx_keyidx; } #endif /* CONFIG_WEP */ +#ifdef CONFIG_IEEE8021X_AUTH + if (ssid->eap_over_auth_frame && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt & + ~WPA_KEY_MGMT_IEEE8021X)) { + const u8 *rsne, *rsnxe; + struct wpa_ie_data ied; + + rsne = wpa_bss_get_rsne(wpa_s, bss, ssid, false); + if (!rsne) { + wpa_dbg(wpa_s, MSG_DEBUG, + "IEEE 802.1X enabled, but target BSS does not advertise RSNE"); + } else if (rsne[1] && + wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ied) == 0 && + wpa_key_mgmt_wpa_ieee8021x( + ied.key_mgmt & ~WPA_KEY_MGMT_IEEE8021X)) { + rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + if (ieee802_11_rsnx_capab( + rsnxe, + WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES) && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_802_1X_AUTH)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Using IEEE 802.1X authentication using Authentication frames"); + params.auth_alg = WPA_AUTH_ALG_802_1X; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "IEEE 802.1X in Authentication frames enabled, but AP doesn't support it"); + } + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "IEEE 802.1X in Authentication frames enabled, but the target BSS does not advertise a suitable AKMP for it"); + } + } +#endif /* CONFIG_IEEE8021X_AUTH */ + + if (!start) + goto skip_setup; if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || - wpa_bss_get_ie(bss, WLAN_EID_RSN)) && + wpa_bss_get_rsne(wpa_s, bss, ssid, false)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; const u8 *cache_id = NULL; @@ -713,21 +1438,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpas_connect_work_done(wpa_s); return; } -#ifdef CONFIG_HS20 - } else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && - (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { - /* No PMKSA caching, but otherwise similar to RSN/WPA */ - wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); - if (wpa_supplicant_set_suites(wpa_s, bss, ssid, - wpa_s->sme.assoc_req_ie, - &wpa_s->sme.assoc_req_ie_len, - false)) { - wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " - "key management and encryption suites"); - wpas_connect_work_done(wpa_s); - return; - } -#endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* @@ -752,14 +1462,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #ifdef CONFIG_WPS } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { struct wpabuf *wps_ie; + + wpa_s->sme.assoc_req_ie_len = 0; wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); - if (wps_ie && wpabuf_len(wps_ie) <= - sizeof(wpa_s->sme.assoc_req_ie)) { - wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie); - os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie), - wpa_s->sme.assoc_req_ie_len); - } else - wpa_s->sme.assoc_req_ie_len = 0; + sme_add_assoc_req_ie(wpa_s, wps_ie); wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); #endif /* CONFIG_WPS */ @@ -778,6 +1484,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_proto == WPA_PROTO_WPA) { wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); + omit_rsnxe = 1; if (wpa_ie) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE"); @@ -788,6 +1495,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpas_connect_work_done(wpa_s); return; } + } else if (wpa_s->wpa_proto == WPA_PROTO_RSN && + wpa_key_mgmt_wpa_psk_no_sae(wpa_s->key_mgmt) && + !wpa_bss_get_rsnxe(wpa_s, bss, ssid, false)) { + /* Omit RSNXE for WPA2-Personal connections to avoid AP + * compatibility issues. */ + omit_rsnxe = 1; } #ifdef CONFIG_IEEE80211R @@ -807,7 +1520,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x", md[0], md[1]); - omit_rsnxe = !wpa_bss_get_ie(bss, WLAN_EID_RSNX); if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; @@ -827,6 +1539,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_sm_has_ft_keys(wpa_s->wpa, md)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " "over-the-air"); + omit_rsnxe |= !wpa_bss_get_rsnxe(wpa_s, bss, ssid, + false); params.auth_alg = WPA_AUTH_ALG_FT; params.ie = wpa_s->sme.ft_ies; params.ie_len = wpa_s->sme.ft_ies_len; @@ -836,7 +1550,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid); if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { - const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + const u8 *rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); struct wpa_ie_data _ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && _ie.capabilities & @@ -847,6 +1561,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } } + wpa_s->sme.spp_amsdu = wpa_sm_uses_spp_amsdu(wpa_s->wpa); + #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; @@ -863,18 +1579,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ #ifdef CONFIG_FST - if (wpa_s->fst_ies) { - int fst_ies_len = wpabuf_len(wpa_s->fst_ies); - - if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <= - sizeof(wpa_s->sme.assoc_req_ie)) { - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(wpa_s->fst_ies), - fst_ies_len); - wpa_s->sme.assoc_req_ie_len += fst_ies_len; - } - } + sme_add_assoc_req_ie(wpa_s, wpa_s->fst_ies); #endif /* CONFIG_FST */ sme_auth_handle_rrm(wpa_s, bss); @@ -917,15 +1622,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_TESTING_OPTIONS - if (wpa_s->rsnxe_override_assoc && - wpabuf_len(wpa_s->rsnxe_override_assoc) <= - sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len) { + if (wpa_s->rsnxe_override_assoc) { wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override"); - os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, - wpabuf_head(wpa_s->rsnxe_override_assoc), - wpabuf_len(wpa_s->rsnxe_override_assoc)); - wpa_s->sme.assoc_req_ie_len += - wpabuf_len(wpa_s->rsnxe_override_assoc); + sme_add_assoc_req_ie(wpa_s, wpa_s->rsnxe_override_assoc); } else #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->rsnxe_len > 0 && @@ -937,6 +1636,22 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_s->sme.assoc_req_ie_len += wpa_s->rsnxe_len; } + if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION && + wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_KNOWN_STA_IDENTIFICATION)) { + struct wpabuf *e; + + e = wpa_sm_known_sta_identification( + wpa_s->wpa, + params.mld ? params.ap_mld_addr : bss->bssid, + bss->tsf); + if (e) { + wpa_printf(MSG_DEBUG, + "SME: Add Known STA Identification element"); + sme_add_assoc_req_ie(wpa_s, e); + wpabuf_free(e); + } + } + #ifdef CONFIG_HS20 if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; @@ -944,19 +1659,11 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); - size_t len; wpas_hs20_add_indication(hs20, pps_mo_id, get_hs20_version(bss)); wpas_hs20_add_roam_cons_sel(hs20, ssid); - len = sizeof(wpa_s->sme.assoc_req_ie) - - wpa_s->sme.assoc_req_ie_len; - if (wpabuf_len(hs20) <= len) { - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(hs20), wpabuf_len(hs20)); - wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); - } + sme_add_assoc_req_ie(wpa_s, hs20); wpabuf_free(hs20); } } @@ -982,19 +1689,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_free(wpa_ie); } - if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { - struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; - size_t len; - - len = sizeof(wpa_s->sme.assoc_req_ie) - - wpa_s->sme.assoc_req_ie_len; - if (wpabuf_len(buf) <= len) { - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(buf), wpabuf_len(buf)); - wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf); - } - } + sme_add_assoc_req_ie(wpa_s, wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]); #ifdef CONFIG_MBO mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); @@ -1012,6 +1707,31 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_MBO */ +skip_setup: +#ifdef CONFIG_ENC_ASSOC + if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_EPPKE) { + if (start) { + /* Initialize EPPKE just for the first Authentication + * frame */ + if (wpas_eppke_initialize(wpa_s, bss, ssid) < 0) { + wpas_connection_failed(wpa_s, bss->bssid, NULL); + return; + } + + resp = wpas_pasn_build_auth_1(&wpa_s->pasn, NULL, + false, 0); + } else { + resp = wpas_pasn_build_auth_3(&wpa_s->pasn, 0); + } + if (!resp) { + wpas_connection_failed(wpa_s, bss->bssid, NULL); + return; + } + params.auth_data = wpabuf_head(resp); + params.auth_data_len = wpabuf_len(resp); + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && pmksa_cache_set_current(wpa_s->wpa, NULL, @@ -1060,7 +1780,27 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_s->current_ssid = ssid; wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_sm_set_ssid(wpa_s->wpa, bss->ssid, bss->ssid_len); - wpa_supplicant_initiate_eapol(wpa_s); + + /* + * Skip EAPOL SM re-init for IEEE 802.1X Authentication algorithm on + * subsequent Authentication frames since it was already configured + * for the first Authentication frame (start=1). + */ + if (params.auth_alg != WPA_AUTH_ALG_802_1X || start) + wpa_supplicant_initiate_eapol(wpa_s); + +#ifdef CONFIG_IEEE8021X_AUTH + if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_802_1X) { + resp = sme_build_802_1x_auth_request(wpa_s, bss, ssid, start, + false); + if (!resp) { + wpas_connection_failed(wpa_s, bss->bssid, NULL); + return; + } + params.auth_data = wpabuf_head(resp); + params.auth_data_len = wpabuf_len(resp); + } +#endif /* CONFIG_IEEE8021X_AUTH */ #ifdef CONFIG_FILS /* TODO: FILS operations can in some cases be done between different @@ -1158,9 +1898,7 @@ no_fils: if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); -#ifdef CONFIG_HS20 - hs20_configure_frame_filters(wpa_s); -#endif /* CONFIG_HS20 */ + wpas_configure_frame_filters(wpa_s); #ifdef CONFIG_P2P /* @@ -1198,6 +1936,7 @@ no_fils: wpa_s->sme.auth_alg = params.auth_alg; + wpa_s->keys_cleared &= ~keys_to_clear; if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the " "driver failed"); @@ -1309,13 +2048,13 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_s->sme.sae_group_index = 0; #endif /* CONFIG_SAE */ - if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1, - sme_auth_start_cb, cwork) < 0) + if (!radio_add_work(wpa_s, bss->freq, "sme-connect", 1, + sme_auth_start_cb, cwork)) wpas_connect_work_free(cwork); } -#ifdef CONFIG_SAE +#if defined(CONFIG_SAE) || defined(CONFIG_IEEE8021X_AUTH) #define WPA_AUTH_FRAME_ML_IE_LEN (6 + ETH_ALEN) @@ -1335,6 +2074,10 @@ static void wpa_auth_ml_ie(struct wpabuf *buf, const u8 *mld_addr) wpabuf_put_data(buf, mld_addr, ETH_ALEN); } +#endif /* CONFIG_SAE || CONFIG_IEEE8021X_AUTH */ + + +#ifdef CONFIG_SAE static int sme_external_auth_build_buf(struct wpabuf *buf, struct wpabuf *params, @@ -1406,16 +2149,18 @@ static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, wpa_s->sme.seq_num, status, wpa_s->sme.ext_ml_auth ? wpa_s->own_addr : NULL); - wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); + wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, + wpa_s->sme.ext_auth_freq, 0); wpabuf_free(resp); wpabuf_free(buf); return 0; } +#endif /* CONFIG_SAE */ -static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, - u16 status) + +void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, u16 status) { struct external_auth params; @@ -1425,18 +2170,54 @@ static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, params.ssid = wpa_s->sme.ext_auth_ssid; params.ssid_len = wpa_s->sme.ext_auth_ssid_len; params.bssid = wpa_s->sme.ext_auth_bssid; +#ifdef CONFIG_SAE if (wpa_s->conf->sae_pmkid_in_assoc && status == WLAN_STATUS_SUCCESS) params.pmkid = wpa_s->sme.sae.pmkid; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_ENC_ASSOC + if (status == WLAN_STATUS_SUCCESS && + wpa_s->sme.ext_auth_alg == WLAN_AUTH_EPPKE && + wpa_s->pasn.using_pmksa) { + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (pmksa) + params.pmkid = pmksa->pmkid; + } +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + if (status == WLAN_STATUS_SUCCESS && + wpa_s->sme.ext_auth_alg == WLAN_AUTH_802_1X) { + struct ptksa_cache_entry *entry; + struct rsn_pmksa_cache_entry *pmksa; + + entry = ptksa_cache_get(wpa_s->ptksa, + sme_get_peer_addr(wpa_s, true), + sme_get_pairwise_cipher(wpa_s, true)); + if (entry && entry->ptk.kck_len) { + params.kck = entry->ptk.kck; + params.kck_len = entry->ptk.kck_len; + } + if (wpa_s->auth_1x && wpa_s->auth_1x->pmkid_found) { + pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (pmksa) + params.pmkid = pmksa->pmkid; + } + } +#endif /* CONFIG_IEEE8021X_AUTH */ + wpa_drv_send_external_auth_status(wpa_s, ¶ms); } +#ifdef CONFIG_SAE + static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct external_auth *ext_auth) { struct wpa_ssid *ssid; - size_t ssid_str_len = data->external_auth.ssid_len; - const u8 *ssid_str = data->external_auth.ssid; + size_t ssid_str_len = ext_auth->ssid_len; + const u8 *ssid_str = ext_auth->ssid; wpa_s->sme.ext_auth_wpa_ssid = NULL; /* Get the SSID conf from the ssid string obtained */ @@ -1446,13 +2227,13 @@ static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && wpa_key_mgmt_sae(ssid->key_mgmt)) { /* Make sure PT is derived */ - wpa_s_setup_sae_pt(wpa_s->conf, ssid, false); + wpa_s_setup_sae_pt(wpa_s, ssid, false); wpa_s->sme.ext_auth_wpa_ssid = ssid; break; } } if (!ssid || - sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid, + sme_external_auth_send_sae_commit(wpa_s, ext_auth->bssid, ssid) < 0) return -1; @@ -1487,7 +2268,8 @@ static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s, wpa_s->sme.ext_ml_auth ? wpa_s->own_addr : NULL); - wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0); + wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, + wpa_s->sme.ext_auth_freq, 0); wpabuf_free(resp); wpabuf_free(buf); } @@ -1502,10 +2284,15 @@ static bool is_sae_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite) * match that initial implementation so that already deployed use cases * remain functional. */ if (RSN_SELECTOR_GET(&suite) == RSN_AUTH_KEY_MGMT_SAE) { - /* Old drivers which follow initial implementation send SAE AKM - * for both SAE and FT-SAE connections. In that case, determine - * the actual AKM from wpa_s->key_mgmt. */ - wpa_s->sme.ext_auth_key_mgmt = wpa_s->key_mgmt; + /* This will be true in following cases + * 1. Old drivers which follow the initial implementation send + * RSN_AUTH_KEY_MGMT_SAE with swapped byte order for both SAE + * and FT-SAE connections. + * 2. The driver sending RSN_AUTH_KEY_MGMT_SAE in host byte + * order but kernel swapped that byte order to follow initial + * implementation. + * In these cases, update the AKM as WPA_KEY_MGMT_SAE. */ + wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE; return true; } @@ -1523,36 +2310,340 @@ static bool is_sae_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite) return true; } +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_ENC_ASSOC + +static bool is_eppke_auth_key_mgmt_suite(struct wpa_supplicant *wpa_s, + u32 suite) +{ + /* Supported AKM suites for EPPKE */ + if (suite == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY) + wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY; + else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY) + wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY; + else if (suite == RSN_AUTH_KEY_MGMT_EPPKE) + wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_EPPKE; + else + return false; + + return true; +} + + +static int sme_handle_eppke_external_auth_start(struct wpa_supplicant *wpa_s, + struct external_auth *ext_auth) +{ + struct wpa_ssid *ssid; + size_t ssid_str_len = ext_auth->ssid_len; + const u8 *ssid_str = ext_auth->ssid; + u8 peer_addr[ETH_ALEN]; + bool is_ml_peer; + int group; + + wpa_s->sme.ext_auth_wpa_ssid = NULL; + /* Find the network profil based on the selected SSID */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && + wpa_key_mgmt_eppke(ssid->key_mgmt) && + ssid_str_len == ssid->ssid_len && + os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0) { + wpa_s->sme.ext_auth_wpa_ssid = ssid; + break; + } + } + if (!ssid) { + wpa_printf(MSG_DEBUG, + "EPPKE: No matching network block found"); + return -1; + } + + wpa_s->sme.ext_auth_alg = ext_auth->auth_alg; + + if (ext_auth->mld_addr) { + is_ml_peer = true; + os_memcpy(peer_addr, ext_auth->mld_addr, ETH_ALEN); + } else { + is_ml_peer = false; + os_memcpy(peer_addr, ext_auth->bssid, ETH_ALEN); + } + + group = wpas_pasn_get_group(wpa_s, ssid, NULL); + if (!group) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed to select PASN group"); + return -1; + } + + /* As per IEEE P802.11-REVmf/D2.1, 12.13.5, when SAE is wrapped within + * PASN authentication, both shall use the same finite cyclic group. + */ + if (sae_set_group(&wpa_s->sme.sae, group) < 0) { + wpa_printf(MSG_INFO, "EPPKE: Failed to set SAE group %u", + group); + return -1; + } + + return wpas_pasn_auth_start(wpa_s, wpa_s->own_addr, peer_addr, + rsn_key_mgmt_to_wpa_akm( + ext_auth->key_mgmt_suite), + ext_auth->pairwise_cipher, + group, ssid->id, NULL, 0, + ext_auth->auth_alg, + ext_auth->group_cipher, + ext_auth->group_mgmt_cipher, + ext_auth->rsn_capab, + ext_auth->rsnxe_data, + is_ml_peer); +} + +#endif /* CONFIG_ENC_ASSOC */ + + +static void sme_ext_auth_get_freq(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss *bss; + + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (!bss) { + wpa_printf(MSG_DEBUG, + "SAE: BSS not available, update scan result to get BSS"); + wpa_supplicant_update_scan_results(wpa_s, bssid); + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + } + wpa_s->sme.ext_auth_freq = bss ? bss->freq : 0; +} + + +#ifdef CONFIG_IEEE8021X_AUTH + +static void sme_external_auth_build_802_1x_buf(struct wpabuf *buf, + struct wpabuf *params, + const u8 *sa, const u8 *da, + const u8 *mld_addr) +{ + struct ieee80211_hdr *hdr; + + hdr = wpabuf_put(buf, sizeof(*hdr)); + hdr->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + os_memcpy(hdr->addr1, da, ETH_ALEN); + os_memcpy(hdr->addr2, sa, ETH_ALEN); + os_memcpy(hdr->addr3, da, ETH_ALEN); + + wpabuf_put_le16(buf, WLAN_AUTH_802_1X); + + if (params) + wpabuf_put_buf(buf, params); + + if (mld_addr) + wpa_auth_ml_ie(buf, mld_addr); +} + + +static int sme_external_auth_send_802_1x(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, bool start) +{ + struct wpabuf *resp, *buf; + struct wpa_bss *bss = NULL; + size_t buf_len; + int ret = -1; + + if (start) + bss = wpa_bss_get_bssid_latest(wpa_s, + wpa_s->sme.ext_auth_bssid); + + resp = sme_build_802_1x_auth_request(wpa_s, bss, ssid, start, true); + if (!resp) + return -1; + + buf_len = offsetof(struct ieee80211_mgmt, u.auth.variable) + + wpabuf_len(resp); + if (wpa_s->sme.ext_ml_auth) + buf_len += WPA_AUTH_FRAME_ML_IE_LEN; + + buf = wpabuf_alloc(buf_len); + if (!buf) + goto fail; + + sme_external_auth_build_802_1x_buf(buf, resp, wpa_s->own_addr, + wpa_s->sme.ext_auth_bssid, + wpa_s->sme.ext_ml_auth ? + wpa_s->own_addr : NULL); + + if (wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), + 0, 0, 0) < 0) { + wpa_printf(MSG_INFO, + "IEEE 802.1X: Failed to send Authentication frame"); + goto fail; + } + + ret = 0; +fail: + wpabuf_free(resp); + wpabuf_free(buf); + return ret; +} + + +static int sme_external_auth_start_802_1x(struct wpa_supplicant *wpa_s, + struct external_auth *ext_auth) +{ + struct wpa_ssid *ssid; + const u8 *ssid_str = ext_auth->ssid; + size_t ssid_str_len = ext_auth->ssid_len; + + wpa_s->sme.ext_auth_wpa_ssid = NULL; + /* Match the driver-provided SSID against configured network blocks */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && + ssid_str_len == ssid->ssid_len && + os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + wpa_s->sme.ext_auth_wpa_ssid = ssid; + break; + } + } + if (!ssid) + return -1; + + wpa_s->sme.ext_auth_alg = ext_auth->auth_alg; + wpa_s->sme.ext_pairwise_cipher = ext_auth->pairwise_cipher; + wpa_s->sme.ext_group_cipher = ext_auth->group_cipher; + wpa_s->sme.ext_mgmt_group_cipher = ext_auth->group_mgmt_cipher; + wpa_s->sme.ext_rsn_capab = ext_auth->rsn_capab; + if (ext_auth->rsnxe_data && + ext_auth->rsnxe_data_len <= sizeof(wpa_s->sme.ext_rsnxe)) { + os_memcpy(wpa_s->sme.ext_rsnxe, ext_auth->rsnxe_data, + ext_auth->rsnxe_data_len); + wpa_s->sme.ext_rsnxe_len = ext_auth->rsnxe_data_len; + } + + if (sme_external_auth_send_802_1x(wpa_s, ssid, 1) < 0) + return -1; + + return 0; +} + + +static bool is_ieee8021x_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite) +{ + /* + * If an originator chooses to initiate IEEE 802.1X authentication + * utilizing Authentication frames, it shall select an IEEE 802.1X AKM, + * other than IEEE 802.1X AKM 00-0F-AC:1. + */ + static const struct { + u32 selector; + int key_mgmt; + } suite_map[] = { + { RSN_AUTH_KEY_MGMT_FT_802_1X, WPA_KEY_MGMT_FT_IEEE8021X }, + { RSN_AUTH_KEY_MGMT_802_1X_SHA256, + WPA_KEY_MGMT_IEEE8021X_SHA256 }, + { RSN_AUTH_KEY_MGMT_802_1X_SUITE_B, + WPA_KEY_MGMT_IEEE8021X_SUITE_B }, + { RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192, + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 }, + { RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384, + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 }, + { RSN_AUTH_KEY_MGMT_FILS_SHA256, + WPA_KEY_MGMT_FILS_SHA256 }, + { RSN_AUTH_KEY_MGMT_FILS_SHA384, + WPA_KEY_MGMT_FILS_SHA384 }, + { RSN_AUTH_KEY_MGMT_FT_FILS_SHA256, + WPA_KEY_MGMT_FT_FILS_SHA256 }, + { RSN_AUTH_KEY_MGMT_FT_FILS_SHA384, + WPA_KEY_MGMT_FT_FILS_SHA384 }, + { RSN_AUTH_KEY_MGMT_802_1X_SHA384, + WPA_KEY_MGMT_IEEE8021X_SHA384 }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(suite_map); i++) { + if (suite == suite_map[i].selector) { + wpa_s->sme.ext_auth_key_mgmt = suite_map[i].key_mgmt; + return true; + } + } + + return false; +} + +#endif /* CONFIG_IEEE8021X_AUTH */ + void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct external_auth *ext_auth) { - if (!is_sae_key_mgmt_suite(wpa_s, data->external_auth.key_mgmt_suite)) - return; - - if (data->external_auth.action == EXT_AUTH_START) { - if (!data->external_auth.bssid || !data->external_auth.ssid) + switch (ext_auth->auth_alg) { +#ifdef CONFIG_ENC_ASSOC + case WLAN_AUTH_EPPKE: + if (!is_eppke_auth_key_mgmt_suite(wpa_s, + ext_auth->key_mgmt_suite)) return; - os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid, + break; +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + case WLAN_AUTH_802_1X: + if (!is_ieee8021x_key_mgmt_suite(wpa_s, + ext_auth->key_mgmt_suite)) + return; + break; +#endif /* CONFIG_IEEE8021X_AUTH */ + default: +#ifdef CONFIG_SAE + if (!is_sae_key_mgmt_suite(wpa_s, ext_auth->key_mgmt_suite)) + return; + break; +#else /* CONFIG_SAE */ + return; +#endif /* CONFIG_SAE */ + } + + if (ext_auth->action == EXT_AUTH_START) { + if (!ext_auth->bssid || !ext_auth->ssid) + return; + os_memcpy(wpa_s->sme.ext_auth_bssid, ext_auth->bssid, ETH_ALEN); - os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, - data->external_auth.ssid_len); - wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len; - if (data->external_auth.mld_addr) { + sme_ext_auth_get_freq(wpa_s, ext_auth->bssid); + os_memcpy(wpa_s->sme.ext_auth_ssid, ext_auth->ssid, + ext_auth->ssid_len); + wpa_s->sme.ext_auth_ssid_len = ext_auth->ssid_len; + if (ext_auth->mld_addr) { wpa_s->sme.ext_ml_auth = true; os_memcpy(wpa_s->sme.ext_auth_ap_mld_addr, - data->external_auth.mld_addr, ETH_ALEN); + ext_auth->mld_addr, ETH_ALEN); } else { wpa_s->sme.ext_ml_auth = false; } +#ifdef CONFIG_ENC_ASSOC + if (ext_auth->auth_alg == WLAN_AUTH_EPPKE) { + if (sme_handle_eppke_external_auth_start(wpa_s, + ext_auth) < 0) + sme_send_external_auth_status( + wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); + return; + } +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + if (ext_auth->auth_alg == WLAN_AUTH_802_1X) { + if (sme_external_auth_start_802_1x(wpa_s, ext_auth) < 0) + sme_send_external_auth_status( + wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); + return; + } +#endif /* CONFIG_IEEE8021X_AUTH */ +#ifdef CONFIG_SAE wpa_s->sme.seq_num = 0; wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae_group_index = 0; - if (sme_handle_external_auth_start(wpa_s, data) < 0) + if (sme_handle_external_auth_start(wpa_s, ext_auth) < 0) sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); - } else if (data->external_auth.action == EXT_AUTH_ABORT) { +#endif /* CONFIG_SAE */ + } else if (ext_auth->action == EXT_AUTH_ABORT) { /* Report failure to driver for the wrong trigger */ sme_send_external_auth_status(wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); @@ -1560,6 +2651,8 @@ void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_SAE + static int sme_sae_is_group_enabled(struct wpa_supplicant *wpa_s, int group) { int *groups = wpa_s->conf->sae_groups; @@ -1618,7 +2711,6 @@ static int sme_external_ml_auth(struct wpa_supplicant *wpa_s, u16 status_code) { struct ieee802_11_elems elems; - const u8 *mld_addr; if (ieee802_11_parse_elems(data + ie_offset, len - ie_offset, &elems, 0) == ParseFailed) { @@ -1638,22 +2730,7 @@ static int sme_external_ml_auth(struct wpa_supplicant *wpa_s, return 0; } - mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); - if (!mld_addr) { - wpa_printf(MSG_DEBUG, "MLD: No MLD address in ML element"); - return -1; - } - - wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr)); - - if (!ether_addr_equal(wpa_s->sme.ext_auth_ap_mld_addr, mld_addr)) { - wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected " - MACSTR ")", - MAC2STR(wpa_s->sme.ext_auth_ap_mld_addr)); - return -1; - } - - return 0; + return sme_validate_basic_mle(&elems, wpa_s->sme.ext_auth_ap_mld_addr); } @@ -1666,7 +2743,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " "status code %u", auth_transaction, status_code); - if (auth_transaction == 1 && + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && wpa_s->sme.sae.state == SAE_COMMITTED && ((external && wpa_s->sme.ext_auth_wpa_ssid) || @@ -1722,6 +2799,30 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; } token_len = elen - 1; +#ifdef CONFIG_IEEE80211BE + } else if ((wpa_s->valid_links || + (external && wpa_s->sme.ext_ml_auth)) && + token_len > 12 && + token_pos[token_len - 12] == WLAN_EID_EXTENSION && + token_pos[token_len - 11] == 10 && + token_pos[token_len - 10] == + WLAN_EID_EXT_MULTI_LINK) { + /* IEEE P802.11be requires H2E to be used whenever SAE + * is used for ML association. However, some early + * Wi-Fi 7 APs enable MLO without H2E. Recognize this + * special case based on the fixed length Basic + * Multi-Link element being at the end of the data that + * would contain the unknown variable length + * Anti-Clogging Token field. The Basic Multi-Link + * element in Authentication frames include the MLD MAC + * addreess in the Common Info field and all subfields + * of the Presence Bitmap subfield of the Multi-Link + * Control field of the element zero and consequently, + * has a fixed length of 12 octets. */ + wpa_printf(MSG_DEBUG, + "SME: Detected Basic Multi-Link element at the end of Anti-Clogging Token field"); + token_len -= 12; +#endif /* CONFIG_IEEE80211BE */ } *ie_offset = token_pos + token_len - data; @@ -1751,7 +2852,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return 0; } - if (auth_transaction == 1 && + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && wpa_s->sme.sae.state == SAE_COMMITTED && ((external && wpa_s->sme.ext_auth_wpa_ssid) || @@ -1779,9 +2880,26 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return 0; } - if (auth_transaction == 1 && + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT && status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { const u8 *bssid = sa ? sa : wpa_s->pending_bssid; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid && ssid->alt_sae_password_ids && + ssid->alt_sae_passwords_ids_used) { + wpa_printf(MSG_DEBUG, + "SAE: Remove alternative password identifier (idx=%u) due to rejection", + ssid->alt_sae_passwords_ids_idx); + wpabuf_array_remove(ssid->alt_sae_password_ids, + ssid->alt_sae_passwords_ids_idx); + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, + "SAE: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ + } wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR, @@ -1801,7 +2919,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -2; } - if (auth_transaction == 1) { + if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_COMMIT) { u16 res; groups = wpa_s->conf->sae_groups; @@ -1875,7 +2993,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, sme_external_auth_send_sae_confirm(wpa_s, sa); } return 0; - } else if (auth_transaction == 2) { + } else if (auth_transaction == WLAN_AUTH_TR_SEQ_SAE_CONFIRM) { if (status_code != WLAN_STATUS_SUCCESS) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); @@ -1894,6 +3012,16 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, wpa_s_clear_sae_rejected(wpa_s); if (external) { + const u8 *connected_addr = wpa_s->valid_links ? + wpa_s->ap_mld_addr : wpa_s->bssid; + const u8 *src = wpa_s->sme.ext_ml_auth ? + wpa_s->sme.ext_auth_ap_mld_addr : + wpa_s->sme.ext_auth_bssid; + + wpa_s->ext_auth_to_same_bss = + wpa_s->wpa_state > WPA_ASSOCIATED && + ether_addr_equal(src, connected_addr); + /* Report success to driver */ sme_send_external_auth_status(wpa_s, WLAN_STATUS_SUCCESS); @@ -1936,6 +3064,49 @@ static int sme_sae_set_pmk(struct wpa_supplicant *wpa_s, const u8 *bssid) return 0; } +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_ENC_ASSOC +static void sme_handle_eppke_unknown_password_id(struct wpa_supplicant *wpa_s, + bool external) +{ + struct wpa_ssid *ssid; + const u8 *bssid; + + ssid = external ? wpa_s->sme.ext_auth_wpa_ssid : wpa_s->current_ssid; + bssid = external ? wpa_s->sme.ext_auth_bssid : wpa_s->pending_bssid; + + if (ssid && ssid->alt_sae_password_ids && + ssid->alt_sae_passwords_ids_used) { + wpa_printf(MSG_DEBUG, + "EPPKE: Remove alternative password identifier (idx=%u) due to rejection", + ssid->alt_sae_passwords_ids_idx); + wpabuf_array_remove(ssid->alt_sae_password_ids, + ssid->alt_sae_passwords_ids_idx); + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, + "EPPKE: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ + } + + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR, + MAC2STR(bssid)); + + if (external) { + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_UNSPECIFIED_FAILURE); + } else { + wpas_connection_failed(wpa_s, bssid, NULL); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } +} +#endif /* CONFIG_ENC_ASSOC */ + void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, const u8 *auth_frame, size_t len) @@ -1953,6 +3124,7 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_SAE if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) { int res; int ie_offset = 0; @@ -1979,10 +3151,660 @@ void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, wpa_s->sme.ext_auth_ap_mld_addr : wpa_s->sme.ext_auth_bssid) < 0) return; +#ifdef CONFIG_ENC_ASSOC + } else if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_EPPKE) { + int res; + u16 status_code = le_to_host16(header->u.auth.status_code); + + /* + * Handle unknown password identifier rejection for EPPKE: + * remove the rejected alternative password identifier and + * report the event. + */ + if (status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + sme_handle_eppke_unknown_password_id(wpa_s, true); + return; + } + + res = wpas_pasn_auth_rx(wpa_s, header, len); + if (res < 0) { + /* Notify failure to the driver */ + sme_send_external_auth_status( + wpa_s, + res == -2 ? + status_code : + WLAN_STATUS_UNSPECIFIED_FAILURE); + return; + } +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_IEEE8021X_AUTH + } else if (le_to_host16(header->u.auth.auth_alg) == + WLAN_AUTH_802_1X) { + struct auth_info auth; + + os_memset(&auth, 0, sizeof(auth)); + os_memcpy(auth.peer, header->sa, ETH_ALEN); + auth.auth_type = le_to_host16(header->u.auth.auth_alg); + auth.auth_transaction = + le_to_host16(header->u.auth.auth_transaction); + auth.status_code = le_to_host16(header->u.auth.status_code); + auth.ies = header->u.auth.variable; + auth.ies_len = len - auth_length; + auth.frame_body = (const u8 *) &header->u.auth; + auth.frame_body_len = len - IEEE80211_HDRLEN; + + sme_process_802_1x_auth_response(wpa_s, &auth, true); + return; +#endif /* CONFIG_IEEE8021X_AUTH */ + } +#endif /* CONFIG_SAE */ +} + + +static int sme_validate_basic_mle(const struct ieee802_11_elems *elems, + const u8 *addr) +{ + const u8 *mld_addr; + + if (!addr) { + wpa_printf(MSG_DEBUG, "MLD: No peer address to check against"); + return -1; + } + + if (!elems->basic_mle || !elems->basic_mle_len) { + wpa_printf(MSG_DEBUG, "MLD: No MLE in authentication"); + return -1; + } + + mld_addr = get_basic_mle_mld_addr(elems->basic_mle, + elems->basic_mle_len); + if (!mld_addr) { + wpa_printf(MSG_DEBUG, "MLD: No MLD address in MLE"); + return -1; + } + + wpa_printf(MSG_DEBUG, "MLD: MLD MAC address=" MACSTR, + MAC2STR(mld_addr)); + + if (!ether_addr_equal(addr, mld_addr)) { + wpa_printf(MSG_DEBUG, + "MLD: Unexpected MLD MAC address (expected " MACSTR + ")", MAC2STR(addr)); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE8021X_AUTH + +static int sme_validate_802_1x_auth_mic(struct wpa_supplicant *wpa_s, + const u8 *frame_data, + size_t frame_data_len, + const struct wpa_ptk *ptk, + bool external) +{ + const u8 *aa = sme_get_peer_addr(wpa_s, external); + const u8 *spa = wpa_s->own_addr; + const u8 *ap_rsne, *ap_rsnxe, *mic_elem; + size_t ap_rsne_len, ap_rsnxe_len, mic_len, data_len; + int key_mgmt = sme_get_key_mgmt(wpa_s, external); + u8 calc_mic[SHA384_MAC_LEN]; + u8 *data; + u8 *pos; + struct wpa_bss *bss; + int ret = -1; + size_t mic_field_off; + + if (!ptk || !ptk->kck_len || !frame_data || !frame_data_len) + return -1; + + /* Get AP RSNE and RSNXE from Beacon/Probe Response frame */ + if (external) + bss = wpa_bss_get_bssid(wpa_s, wpa_s->sme.ext_auth_bssid); + else + bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_bssid); + if (!bss) + return -1; + + ap_rsne = wpa_bss_get_rsne(wpa_s, bss, wpa_s->current_ssid, false); + ap_rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, wpa_s->current_ssid, false); + if (!ap_rsne || !ap_rsnxe) + return -1; + + ap_rsne_len = 2 + ap_rsne[1]; + ap_rsnxe_len = 2 + ap_rsnxe[1]; + + mic_elem = get_ie(frame_data, frame_data_len, WLAN_EID_MIC); + if (!mic_elem) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: No MIC element in Authentication frame"); + return -1; + } + + /* Determine MIC length based on key management */ + mic_len = wpa_key_mgmt_sha384(key_mgmt) ? 24 : 16; + if (mic_elem[1] != mic_len) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: MIC length mismatch (%u != %zu)", + mic_elem[1], mic_len); + return -1; + } + + /* Build data for MIC calculation: + AA || SPA || AP RSNE || AP RSNXE || Frame Data */ + data_len = ETH_ALEN + ETH_ALEN + ap_rsne_len + ap_rsnxe_len + + frame_data_len; + data = os_malloc(data_len); + if (!data) + return -1; + + pos = data; + os_memcpy(pos, aa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, spa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ap_rsne, ap_rsne_len); + pos += ap_rsne_len; + os_memcpy(pos, ap_rsnxe, ap_rsnxe_len); + pos += ap_rsnxe_len; + os_memcpy(pos, frame_data, frame_data_len); + + /* Clear the MIC field */ + mic_field_off = (size_t) (mic_elem - frame_data) + 2; + if (mic_field_off + mic_len > frame_data_len) + goto out; + os_memset(pos + mic_field_off, 0, mic_len); + + /* Calculate MIC */ + if (wpa_key_mgmt_sha384(key_mgmt)) { + if (hmac_sha384(ptk->kck, ptk->kck_len, data, data_len, + calc_mic) < 0) + goto out; + } else { + if (hmac_sha256(ptk->kck, ptk->kck_len, data, data_len, + calc_mic) < 0) + goto out; + } + + /* Verify MIC */ + if (os_memcmp_const(calc_mic, mic_elem + 2, mic_len) != 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Invalid MIC in Authentication frame"); + goto out; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: MIC verified successfully"); + ret = 0; + +out: + bin_clear_free(data, data_len); + return ret; +} + + +static int sme_validate_8021x_auth_elems(struct wpa_supplicant *wpa_s, + const struct ieee802_11_elems *elems, + struct wpabuf *pdu) +{ + struct wpa_ie_data ie; + u16 group; + + if (!elems->rsn_ie || !elems->owe_dh || !elems->nonce || + elems->nonce_len != WPA_NONCE_LEN) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Missing required encryption elements (RSNE=%p, DH=%p, Nonce=%p)", + elems->rsn_ie, elems->owe_dh, elems->nonce); + return -1; + } + + if (wpa_parse_wpa_ie(elems->rsn_ie - 2, elems->rsn_ie_len + 2, &ie) < 0) + { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to parse received RSNE"); + return -1; + } + + if (wpa_compare_rsne_params(wpa_s->auth_1x->rsne, + wpa_s->auth_1x->rsne_len, + elems->rsn_ie - 2, + elems->rsn_ie_len + 2) != 0) { + wpa_msg(wpa_s, MSG_INFO, "IEEE 802.1X: RSNE mismatch"); + return -1; + } + + if (wpa_s->auth_1x->pmksa_caching) { + if (ie.num_pmkid) { + if (pdu) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Unexpected EAPOL PDU with PMKID indicating PMKSA caching"); + return -1; + } + + if (ie.num_pmkid != 1) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Expected only one PMKID, got %u", + (unsigned int) ie.num_pmkid); + return -1; + } + + if (os_memcmp(ie.pmkid, wpa_s->auth_1x->pmkid, + PMKID_LEN) != 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: PMKID mismatch"); + return -1; + } + wpa_s->auth_1x->pmkid_found = true; + } else if (!pdu) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Missing EAPOL PDU for fallback (no PMKID)"); + return -1; + } + } + + if (elems->owe_dh_len < 2) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: DH parameter too short (%u)", + (unsigned int) elems->owe_dh_len); + return -1; + } + + group = WPA_GET_LE16(elems->owe_dh); + if (group != wpa_s->auth_1x->dh_group) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: DH group mismatch (AP=%u, local=%u)", + group, wpa_s->auth_1x->dh_group); + return -1; + } + + wpa_s->auth_1x->dhss = + crypto_ecdh_set_peerkey(wpa_s->auth_1x->ecdh, 1, + elems->owe_dh + 2, + elems->owe_dh_len - 2); + if (!wpa_s->auth_1x->dhss) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to compute DH shared secret"); + return -1; + } + + os_memcpy(wpa_s->auth_1x->anonce, elems->nonce, WPA_NONCE_LEN); + + return 0; +} + + +static int sme_add_802_1x_mic_in_assoc(struct wpa_supplicant *wpa_s, + const u8 *aa) +{ + struct ptksa_cache_entry *ptk_entry; + const u8 *rsne, *rsnxe; + size_t rsne_len = 0, rsnxe_len = 0; + u8 mic[SHA384_MAC_LEN]; + size_t mic_len = 0; + const u8 *spa = wpa_s->own_addr; + struct wpabuf *buf = NULL; + int ret = -1; + u8 *pos; + + ptk_entry = ptksa_cache_get(wpa_s->ptksa, aa, wpa_s->pairwise_cipher); + if (!ptk_entry || !ptk_entry->ptk.kck_len) + return -1; + + rsne = get_ie(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSN); + rsnxe = get_ie(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSNX); + if (!rsne || !rsnxe || rsne[1] == 0 || rsnxe[1] == 0) + return -1; + + rsne_len = 2 + rsne[1]; + rsnxe_len = 2 + rsnxe[1]; + + buf = wpabuf_alloc(2 * ETH_ALEN + rsne_len + rsnxe_len); + if (!buf) + return -1; + + /* Data to MAC: AA || SPA || RSNE || RSNXE */ + wpabuf_put_data(buf, aa, ETH_ALEN); + wpabuf_put_data(buf, spa, ETH_ALEN); + wpabuf_put_data(buf, rsne, rsne_len); + wpabuf_put_data(buf, rsnxe, rsnxe_len); + + if (wpa_key_mgmt_sha384(wpa_s->key_mgmt)) { + mic_len = SHA384_MAC_LEN / 2; + if (hmac_sha384(ptk_entry->ptk.kck, ptk_entry->ptk.kck_len, + wpabuf_head(buf), wpabuf_len(buf), mic) < 0) + goto out; + } else { + mic_len = SHA256_MAC_LEN / 2; + if (hmac_sha256(ptk_entry->ptk.kck, ptk_entry->ptk.kck_len, + wpabuf_head(buf), wpabuf_len(buf), mic) < 0) + goto out; + } + + /* Append MIC element to assoc_req_ie */ + if (wpa_s->sme.assoc_req_ie_len + 2 + mic_len > + sizeof(wpa_s->sme.assoc_req_ie)) + goto out; + + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + *pos++ = WLAN_EID_MIC; + *pos++ = mic_len; + os_memcpy(pos, mic, mic_len); + wpa_s->sme.assoc_req_ie_len += 2 + mic_len; + ret = 0; + +out: + wpabuf_free(buf); + return ret; +} + + +static int sme_parse_802_1x_auth_frame(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len, + struct ieee802_11_elems *elems, + struct wpabuf **pdu) +{ + const u8 *pos = ies, *end = ies + ies_len; + u16 encap_len; + + *pdu = NULL; + + if (end - pos < 2) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Authentication frame too short for the Encapsulation Length field"); + return -1; + } + + encap_len = WPA_GET_LE16(pos); + pos += 2; + + if (encap_len) { + if (end - pos < encap_len) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Encapsulated data exceeds frame length"); + return -1; + } + + *pdu = wpabuf_alloc_copy(pos, encap_len); + if (!*pdu) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to allocate EAPOL PDU buffer"); + return -1; + } + + pos += encap_len; + } + + if (ieee802_11_parse_elems(pos, end - pos, elems, 0) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to parse Authentication frame elements"); + wpabuf_free(*pdu); + *pdu = NULL; + return -1; + } + + return 0; +} + + +static size_t wpas_get_kdk_len(struct wpa_supplicant *wpa_s) +{ + const u8 *rsnxe; + + if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) || + !wpa_s->current_bss) + return 0; + + rsnxe = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX); + + if (rsnxe && ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) + return WPA_KDK_MAX_LEN; + + return 0; +} + + +static void sme_process_802_1x_auth_response(struct wpa_supplicant *wpa_s, + struct auth_info *auth, + bool external) +{ + struct ieee802_11_elems elems; + struct wpabuf *pdu = NULL; + bool validation_failed = false; + const u8 *peer_addr = sme_get_peer_addr(wpa_s, external); + int key_mgmt = sme_get_key_mgmt(wpa_s, external); + + if (!wpa_s->auth_1x) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: No authentication data, cannot process response"); + return; + } + + if (auth->status_code != WLAN_STATUS_SUCCESS && + auth->status_code != WLAN_STATUS_802_1_X_AUTH_SUCCESS) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Authentication failed (status=%u)", + auth->status_code); + goto fail; + } + + if (auth->auth_transaction != wpa_s->auth_1x->auth_trans + 1) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Unexpected transaction number (received=%u, expected=%u) - discard", + auth->auth_transaction, + wpa_s->auth_1x->auth_trans + 1); + return; + } + + wpa_s->auth_1x->auth_trans = auth->auth_transaction; + + if (sme_parse_802_1x_auth_frame(wpa_s, auth->ies, auth->ies_len, &elems, + &pdu) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to parse Authentication frame"); + return; + } + + if (sme_is_ml_auth(wpa_s, external) && + sme_validate_basic_mle(&elems, peer_addr) < 0) { + validation_failed = true; + goto cleanup; + } + + if (auth->auth_transaction == 2) { + if (wpa_s->auth_1x->derive_ptk) { + if (sme_validate_8021x_auth_elems(wpa_s, &elems, pdu) < + 0) { + validation_failed = true; + goto cleanup; + } + + /* Fall back to EAP handshake if PMKSA entry for caching + * was not identified */ + if (wpa_s->auth_1x->pmksa_caching && + !wpa_s->auth_1x->pmkid_found) { + eapol_sm_set_eap_over_auth_frame(wpa_s->eapol, + true); + eapol_sm_notify_portEnabled(wpa_s->eapol, true); + wpa_s->auth_1x->pmksa_caching = false; + } + } else if (!elems.akm_suite_selector || + elems.akm_suite_selector_len != 4 || + WPA_GET_BE32(elems.akm_suite_selector) != + wpa_akm_to_suite(key_mgmt)) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Invalid/missing AKM Suite Selector"); + wpa_s->auth_1x->status = WLAN_STATUS_INVALID_AKMP; + if (external) { + sme_external_auth_send_802_1x( + wpa_s, wpa_s->sme.ext_auth_wpa_ssid, 0); + goto cleanup; + } + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + goto cleanup; + } + } + + if (wpa_s->auth_1x->pmksa_caching && + auth->status_code != WLAN_STATUS_SUCCESS) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: PMKSA caching failed (status=%u)", + auth->status_code); + goto fail; + } + + if (!wpa_s->auth_1x->pmkid_found) { + if (!pdu) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Missing EAPOL PDU"); + goto fail; + } + + eapol_sm_rx_eapol(wpa_s->eapol, peer_addr, + wpabuf_head(pdu), wpabuf_len(pdu), + FRAME_ENCRYPTION_UNKNOWN); + + if (eapol_sm_get_failure(wpa_s->eapol)) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: EAP authentication failed"); + goto fail; + } + + if (eapol_sm_get_success(wpa_s->eapol) && + auth->status_code != WLAN_STATUS_802_1_X_AUTH_SUCCESS) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Invalid status code in EAP-Success authentication frame"); + goto fail; + } + } + + if (auth->status_code == WLAN_STATUS_802_1_X_AUTH_SUCCESS || + wpa_s->auth_1x->pmkid_found) { + if (wpa_s->auth_1x->derive_ptk) { + struct wpa_ptk ptk; + const u8 *pmk; + size_t pmk_len; + size_t kdk_len; + static const u8 zero[6] = { 0 }; + enum wpa_alg alg; + int pairwise_cipher; + + pairwise_cipher = sme_get_pairwise_cipher(wpa_s, + external); + pmk = wpa_sm_get_pmk(wpa_s->wpa, peer_addr, + wpa_s->auth_1x->pmkid_found ? + wpa_s->auth_1x->pmkid : + NULL, &pmk_len); + if (!pmk) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Failed to get PMK"); + goto fail; + } + + kdk_len = wpas_get_kdk_len(wpa_s); + + if (!wpa_s->auth_1x->dhss || + wpa_auth_802_1x_pmk_to_ptk( + pmk, pmk_len, wpa_s->own_addr, peer_addr, + wpa_s->auth_1x->snonce, + wpa_s->auth_1x->anonce, + key_mgmt, pairwise_cipher, + wpabuf_head(wpa_s->auth_1x->dhss), + wpabuf_len(wpa_s->auth_1x->dhss), + &ptk, kdk_len) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "SME: PTK derivation failed"); + goto fail; + } + + /* Clear DHss after successful PTK derivation */ + wpabuf_clear_free(wpa_s->auth_1x->dhss); + wpa_s->auth_1x->dhss = NULL; + + if (sme_validate_802_1x_auth_mic( + wpa_s, auth->frame_body, + auth->frame_body_len, &ptk, + external) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: MIC validation failed"); + forced_memzero(&ptk, sizeof(ptk)); + goto fail; + } + + alg = wpa_cipher_to_alg(pairwise_cipher); + if (wpa_drv_set_key(wpa_s, -1, alg, peer_addr, 0, 1, + zero, sizeof(zero), + ptk.tk, ptk.tk_len, + KEY_FLAG_PAIRWISE_RX_TX) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: TK configuration failed"); + forced_memzero(&ptk, sizeof(ptk)); + goto fail; + } + + ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, + peer_addr, pairwise_cipher, + dot11RSNAConfigPMKLifetime, &ptk, NULL, + NULL, key_mgmt, + WLAN_AUTH_802_1X); + + forced_memzero(&ptk, sizeof(ptk)); + } + + wpa_msg(wpa_s, MSG_INFO, + "IEEE 802.1X: Authentication successful"); + + eapol_sm_set_eap_over_auth_frame(wpa_s->eapol, false); + + if (external) { + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_SUCCESS); + sme_802_1x_auth_data_free(wpa_s); + goto cleanup; + } + sme_associate(wpa_s, wpa_s->current_ssid->mode, + auth->peer, auth->auth_type); + sme_802_1x_auth_data_free(wpa_s); + goto cleanup; + } + + if (auth->status_code == WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "IEEE 802.1X: Authentication in progress, sending next frame"); + if (external) { + sme_external_auth_send_802_1x( + wpa_s, wpa_s->sme.ext_auth_wpa_ssid, 0); + goto cleanup; + } + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + goto cleanup; + } + +fail: + wpa_msg(wpa_s, MSG_INFO, "IEEE 802.1X: Authentication failed"); + validation_failed = true; + +cleanup: + wpabuf_free(pdu); + + if (validation_failed) { + sme_802_1x_auth_data_free(wpa_s); + if (external) + sme_send_external_auth_status( + wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); + else { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid, + NULL); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } } } -#endif /* CONFIG_SAE */ +#endif /* CONFIG_IEEE8021X_AUTH */ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -2020,6 +3842,119 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); +#ifdef CONFIG_IEEE8021X_AUTH + if (data->auth.auth_type == WLAN_AUTH_802_1X) { + sme_process_802_1x_auth_response(wpa_s, &data->auth, false); + return; + } +#endif /* CONFIG_IEEE8021X_AUTH */ + +#ifdef CONFIG_ENC_ASSOC + if (data->auth.auth_type == WLAN_AUTH_EPPKE) { + struct pasn_data *pasn = &wpa_s->pasn; + struct wpa_pasn_params_data pasn_params; + int res; + enum wpa_alg alg; + struct ptksa_cache_entry *entry; + + /* + * Handle unknown password identifier rejection for EPPKE: + * remove the rejected alternative password identifier and + * report the event. + */ + if (data->auth.status_code == + WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + sme_handle_eppke_unknown_password_id(wpa_s, false); + return; + } + + res = wpas_parse_pasn_frame(pasn, data->auth.auth_type, + data->auth.auth_transaction, + data->auth.status_code, + data->auth.ies, data->auth.ies_len, + &pasn_params); + if (res < 0) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid, + NULL); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + return; + } + + /* + * TODO: Add support for comeback if AP rejected group + * negotiation temporarily. + */ + + /* + * AP rejected the proposed group; retry with the + * next valid group. + */ + if (res == 2) { + struct wpa_bss *bss = wpa_s->current_bss; + + if (!bss) { + bss = wpa_bss_get_bssid_latest( + wpa_s, wpa_s->pending_bssid); + if (!bss) { + wpas_connection_failed( + wpa_s, wpa_s->pending_bssid, + NULL); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } + } + + wpa_printf(MSG_DEBUG, + "EPPKE: Group %u rejected by AP - retrying with next group", + pasn->group); + wpas_connect_work_done(wpa_s); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_connect(wpa_s, bss, ssid); + return; + } + + ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr, + pasn_get_cipher(pasn), + dot11RSNAConfigPMKLifetime, + pasn_get_ptk(pasn), NULL, NULL, + pasn_get_akmp(pasn), pasn->auth_alg); + + if (pasn->pmksa_entry) + wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry); + wpa_sm_set_pmk(wpa_s->wpa, pasn->pmk, pasn->pmk_len, + pasn->sae.pmkid, NULL); + + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + + if (wpa_s->conf->sae_pmkid_in_assoc) { + if (wpa_s->sme.assoc_req_ie_len + 2 + PMKID_LEN > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_msg(wpa_s, MSG_WARNING, + "RSN: Not enough room for inserting own PMKID into RSNE"); + return; + } + if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + pasn->sae.pmkid, true) < 0) + return; + } + + alg = wpa_cipher_to_alg(pasn_get_cipher(pasn)); + entry = ptksa_cache_get(wpa_s->ptksa, pasn->peer_addr, + pasn_get_cipher(pasn)); + if (!entry) { + wpa_printf(MSG_INFO, + "EPPKE: No PTKSA found to configure TK"); + return; + } + + wpa_drv_set_key(wpa_s, -1, alg, pasn->peer_addr, 0, 1, + NULL, 0, entry->ptk.tk, entry->ptk.tk_len, + KEY_FLAG_PAIRWISE_RX_TX); + } +#endif /* CONFIG_ENC_ASSOC */ + #ifdef CONFIG_SAE if (data->auth.auth_type == WLAN_AUTH_SAE) { const u8 *addr = wpa_s->pending_bssid; @@ -2030,6 +3965,11 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) data->auth.ies_len, 0, data->auth.peer, &ie_offset); if (res < 0) { + if (data->auth.auth_transaction == + WLAN_AUTH_TR_SEQ_SAE_CONFIRM && + data->auth.status_code == + WLAN_STATUS_CHALLENGE_FAIL) + wpas_notify_sae_password_mismatch(wpa_s); wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); @@ -2226,7 +4166,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, struct ieee802_11_elems elems; struct wpa_ssid *ssid = wpa_s->current_ssid; #ifdef CONFIG_FILS - u8 nonces[2 * FILS_NONCE_LEN]; + u8 nonces[2 * NONCE_LEN]; #endif /* CONFIG_FILS */ #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; @@ -2236,6 +4176,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, struct ieee80211_vht_capabilities vhtcaps; struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ +#ifdef CONFIG_PMKSA_PRIVACY + const u8 *ap_rsnxe; +#endif /* CONFIG_PMKSA_PRIVACY */ os_memset(¶ms, 0, sizeof(params)); @@ -2318,8 +4261,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len); - os_memcpy(nonces, snonce, FILS_NONCE_LEN); - os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN); + os_memcpy(nonces, snonce, NONCE_LEN); + os_memcpy(nonces + NONCE_LEN, anonce, NONCE_LEN); params.fils_nonces = nonces; params.fils_nonces_len = sizeof(nonces); } @@ -2444,6 +4387,12 @@ pfs_fail: mscs_fail: #endif /* CONFIG_NO_ROBUST_AV */ + wpa_s->sme.assoc_req_ie_len = + wpas_populate_wfa_capa(wpa_s, wpa_s->current_bss, + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie)); + if (ssid && ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; struct multi_ap_params multi_ap = { 0 }; @@ -2464,6 +4413,95 @@ mscs_fail: wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len; } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT, + wpas_rsn_overriding(wpa_s, ssid)); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_NOT_USED); + if (wpas_rsn_overriding(wpa_s, ssid) && + wpas_ap_supports_rsn_overriding(wpa_s, wpa_s->current_bss) && + wpa_s->sme.assoc_req_ie_len + 2 + 4 <= + sizeof(wpa_s->sme.assoc_req_ie)) { + u8 *pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + const u8 *ie; + enum rsn_selection_variant variant = RSN_SELECTION_RSNE; + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE); + ie = wpa_bss_get_rsne(wpa_s, wpa_s->current_bss, ssid, + wpa_s->valid_links); + if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4) { + u32 type; + + type = WPA_GET_BE32(&ie[2]); + if (type == RSNE_OVERRIDE_IE_VENDOR_TYPE) { + variant = RSN_SELECTION_RSNE_OVERRIDE; + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE_OVERRIDE); + } else if (type == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) { + variant = RSN_SELECTION_RSNE_OVERRIDE_2; + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE_OVERRIDE_2); + } + } + + /* Indicate which RSNE variant was used */ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 1; + WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE); + pos += 4; + *pos = variant; + wpa_s->sme.assoc_req_ie_len += 2 + 4 + 1; + } + +#ifdef CONFIG_PMKSA_PRIVACY + ap_rsnxe = wpa_bss_get_rsnxe(wpa_s, wpa_s->current_bss, + NULL, wpa_s->valid_links); + if (ssid->pmksa_privacy && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION) && + ieee802_11_rsnx_capab(ap_rsnxe, + WLAN_RSNX_CAPAB_PMKSA_CACHING_PRIVACY)) { + size_t len = 3 + NONCE_LEN; + u8 *pos; + + if (wpa_s->sme.assoc_req_ie_len + len > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_INFO, + "PMKID privacy: AssocReq IE buffer too small for SNonce"); + return; + } + + if (os_get_random(wpa_s->pmkid_snonce, NONCE_LEN) < 0) { + wpa_printf(MSG_INFO, + "PMKID privacy: Failed to generate SNonce"); + return; + } + + pos = &wpa_s->sme.assoc_req_ie[wpa_s->sme.assoc_req_ie_len]; + *pos++ = WLAN_EID_EXTENSION; /* EID Extension */ + *pos++ = 1 + NONCE_LEN; /* Length */ + *pos++ = WLAN_EID_EXT_NONCE; /* Element ID */ + os_memcpy(pos, wpa_s->pmkid_snonce, NONCE_LEN); + wpa_s->sme.assoc_req_ie_len += len; + wpa_hexdump(MSG_DEBUG, "PMKID privacy: SNonce in Assoc Request", + wpa_s->pmkid_snonce, NONCE_LEN); + wpa_s->pmkid_snonce_set = true; + } +#endif /* CONFIG_PMKSA_PRIVACY */ + +#ifdef CONFIG_IEEE8021X_AUTH + /* + * Append MIC element to (Re)Association Request frame when using + * IEEE 802.1X Authentication algorithm. + */ + if (auth_type == WLAN_AUTH_802_1X && wpa_s->auth_1x && + wpa_s->auth_1x->derive_ptk) + sme_add_802_1x_mic_in_assoc(wpa_s, wpa_s->valid_links ? + wpa_s->ap_mld_addr : bssid); +#endif /* CONFIG_IEEE8021X_AUTH */ + params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; @@ -2527,6 +4565,13 @@ mscs_fail: remove_ie(wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len, WLAN_EID_FAST_BSS_TRANSITION); + /* Remove own RSNXE if AP does not advertise RSNXE to match FT + * specific backwards compatibility expectations. */ + if (!wpa_bss_get_rsnxe(wpa_s, wpa_s->current_bss, ssid, + wpa_s->valid_links)) + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSNX); rm_en = get_ie(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, WLAN_EID_RRM_ENABLED_CAPABILITIES); @@ -2581,6 +4626,7 @@ mscs_fail: #endif /* CONFIG_IEEE80211R */ params.mode = mode; params.mgmt_frame_protection = wpa_s->sme.mfp; + params.spp_amsdu = wpa_s->sme.spp_amsdu; params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; @@ -2606,10 +4652,6 @@ mscs_fail: params.wpa_proto = WPA_PROTO_WPA; wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2, elems.wpa_ie_len + 2); - } else if (elems.osen) { - params.wpa_proto = WPA_PROTO_OSEN; - wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2, - elems.osen_len + 2); } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); if (elems.rsnxe) @@ -2642,11 +4684,19 @@ mscs_fail: wpa_s->links[i].freq; params.mld_params.mld_links[i].disabled = wpa_s->links[i].disabled; + if (wpa_s->links[i].ies) { + params.mld_params.mld_links[i].ies = + wpabuf_head(wpa_s->links[i].ies); + params.mld_params.mld_links[i].ies_len = + wpabuf_len(wpa_s->links[i].ies); + } wpa_printf(MSG_DEBUG, - "MLD: id=%u, freq=%d, disabled=%u, " MACSTR, + "MLD: id=%u, freq=%d, disabled=%u, ies_len=%zu, " + MACSTR, i, wpa_s->links[i].freq, wpa_s->links[i].disabled, + params.mld_params.mld_links[i].ies_len, MAC2STR(wpa_s->links[i].bssid)); } } @@ -2659,9 +4709,8 @@ mscs_fail: "driver failed"); /* Prepare list of failed links for error report */ - for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { - if (!(wpa_s->valid_links & BIT(i)) || - wpa_s->mlo_assoc_link_id == i || + for_each_link(wpa_s->valid_links, i) { + if (wpa_s->mlo_assoc_link_id == i || !params.mld_params.mld_links[i].error) continue; @@ -2680,6 +4729,18 @@ mscs_fail: wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + if (wpa_s->ptksa && + ptksa_cache_get(wpa_s->ptksa, wpa_s->bssid, + WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, + WPA_CIPHER_NONE); + } +#endif /* CONFIG_ENC_ASSOC */ + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } return; @@ -2742,6 +4803,16 @@ static void sme_deauth(struct wpa_supplicant *wpa_s, const u8 **link_bssids) wpas_connection_failed(wpa_s, wpa_s->pending_bssid, link_bssids); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + +#ifdef CONFIG_ENC_ASSOC + /* Clear configured keys and PTKSA */ + if (wpa_s->ptksa && + ptksa_cache_get(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE)) { + wpa_clear_keys(wpa_s, wpa_s->bssid); + ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE); + } +#endif /* CONFIG_ENC_ASSOC */ + os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); if (bssid_changed) @@ -2862,12 +4933,23 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SAE if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid && wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) { + struct wpa_bss *bss = wpa_s->current_bss; + const u8 *aa; + wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication"); wpa_sm_aborted_cached(wpa_s->wpa); - wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); + if (wpa_s->valid_links) + aa = wpa_s->ap_mld_addr; + else if (bss) + aa = bss->bssid; + else + aa = NULL; + + if (aa) + wpa_sm_pmksa_cache_flush_addr(wpa_s->wpa, + wpa_s->current_ssid, aa); if (wpa_s->current_bss) { - struct wpa_bss *bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; wpa_drv_deauthenticate(wpa_s, bssid, @@ -2937,6 +5019,40 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, } +void sme_event_deauth(struct wpa_supplicant *wpa_s, struct deauth_info *info) +{ +#ifdef CONFIG_SAE + const u8 *aa; + + /* Some APs reject a (Re)Association Request frame that tries to use + * PMKSA caching by deauthenticating the STA with reason code 9 + * (STA_REQ_ASSOC_WITHOUT_AUTH) instead of responding to it with an + * error status code. Drop the PMKSA cache entry for that AP in that + * case so that the next attempt uses a full SAE authentication + * instead of finding the same PMKSA cache entry again and repeating + * the same failure. */ + if (!wpa_s->sme.sae_pmksa_caching || !info || info->locally_generated || + info->reason_code != WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH || + wpa_s->wpa_state != WPA_ASSOCIATING || !wpa_s->current_ssid || + !wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) + return; + + if (wpa_s->valid_links) + aa = wpa_s->ap_mld_addr; + else if (wpa_s->current_bss) + aa = wpa_s->current_bss->bssid; + else + return; /* Do not flush the PMKSA cache entries of the full ESS + * based on an unprotected frame from a single AP. */ + + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: PMKSA caching attempt rejected with deauthentication - drop PMKSA cache entry"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush_addr(wpa_s->wpa, wpa_s->current_ssid, aa); +#endif /* CONFIG_SAE */ +} + + void sme_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info) { @@ -3000,6 +5116,9 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE8021X_AUTH + sme_802_1x_auth_data_free(wpa_s); +#endif /* CONFIG_IEEE8021X_AUTH */ sme_stop_sa_query(wpa_s); } diff --git a/contrib/wpa/wpa_supplicant/sme.h b/contrib/wpa/wpa_supplicant/sme.h index f8fd06b95f5a..721270fbdace 100644 --- a/contrib/wpa/wpa_supplicant/sme.h +++ b/contrib/wpa/wpa_supplicant/sme.h @@ -25,6 +25,7 @@ void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data); void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, union wpa_event_data *data); +void sme_event_deauth(struct wpa_supplicant *wpa_s, struct deauth_info *info); void sme_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info); void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, @@ -39,9 +40,11 @@ void sme_deinit(struct wpa_supplicant *wpa_s); int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable); void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, - union wpa_event_data *data); + struct external_auth *ext_auth); void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, const u8 *auth_frame, size_t len); +void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, u16 status); +const u8 * sme_get_ext_auth_pmkid(struct wpa_supplicant *wpa_s); #else /* CONFIG_SME */ @@ -79,6 +82,11 @@ static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, { } +static inline void sme_event_deauth(struct wpa_supplicant *wpa_s, + struct deauth_info *info) +{ +} + static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info) { @@ -117,7 +125,7 @@ static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, } static inline void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct external_auth *ext_auth) { } @@ -126,6 +134,16 @@ static inline void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, { } +static inline void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, + u16 status) +{ +} + +static inline const u8 * sme_get_ext_auth_pmkid(struct wpa_supplicant *wpa_s) +{ + return NULL; +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c index 7dc93ebff553..7e3b355c9bb6 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.c +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -27,7 +27,6 @@ #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 -#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, @@ -305,6 +304,7 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, + const u8 *da, const u8 *sa, const u8 *frm, int len) { /* @@ -329,6 +329,14 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, return; } + if (is_multicast_ether_addr(da)) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore group-addressed WNM-Sleep Mode Response frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(da), MAC2STR(sa)); + return; + } + if (len < 3) return; key_len_total = WPA_GET_LE16(frm + 1); @@ -431,6 +439,8 @@ void wnm_btm_reset(struct wpa_supplicant *wpa_s) os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; + wpa_s->wnm_target_bss = NULL; + wpa_s->wnm_cand_valid_until.sec = 0; wpa_s->wnm_cand_valid_until.usec = 0; @@ -445,6 +455,114 @@ void wnm_btm_reset(struct wpa_supplicant *wpa_s) } +static void wnm_parse_neighbor_report_multi_link(struct neighbor_report *rep, + u8 id, u8 elen, const u8 *pos) +{ + const struct ieee80211_eht_ml *ml = + (const struct ieee80211_eht_ml *) pos; + bool has_link_id; + u8 common_info_len; + + /* The Basic Multi-Link subelement has the same body as the Basic MLE. + * It includes at least the 2 octet Multi-Link Control field, 1 octet + * Common Info Length, and the 6 oxtet MLD MAC Address fields. */ + if (elen < sizeof(*ml) + 1 + ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WNM: Too short ML element"); + return; + } + + /* The ML control should be all zeroes except for the Link ID Info + * Present field. */ + if ((le_to_host16(ml->ml_control) & + ~BASIC_MULTI_LINK_CTRL_PRES_LINK_ID)) + wpa_printf(MSG_DEBUG, + "WNM: Ignore unsupported ML Control field bits: 0x%04x", + le_to_host16(ml->ml_control) & + ~BASIC_MULTI_LINK_CTRL_PRES_LINK_ID); + + has_link_id = !!(le_to_host16(ml->ml_control) & + BASIC_MULTI_LINK_CTRL_PRES_LINK_ID); + + /* Followed by the Common Info Length and the MLD MAC Address fields */ + common_info_len = pos[2]; + if (common_info_len < 1 + ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WNM: Too short ML Common Info: %u < 7", + common_info_len); + return; + } + + /* MLD MAC Address */ + os_memcpy(rep->mld_addr, &pos[3], ETH_ALEN); + + if (!has_link_id) + return; + + if (common_info_len < 1 + ETH_ALEN + 1 || common_info_len + 2 > elen) { + wpa_printf(MSG_DEBUG, + "WNM: ML Common Info too short or does not fit: %u (elen: %u)", + common_info_len, elen); + return; + } + + /* Link ID Info */ + if ((pos[9] & EHT_ML_LINK_ID_MSK) >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, + "WNM: ML common info contains invalid link ID"); + return; + } + + rep->mld_links = BIT(pos[9] & EHT_ML_LINK_ID_MSK); + + elen -= common_info_len + 2; + pos += common_info_len + 2; + + /* Parse out per-STA information */ + while (elen >= 2) { + u8 sub_elem_len = pos[1]; + + if (2 + sub_elem_len > elen) { + wpa_printf(MSG_DEBUG, + "WNM: Invalid sub-element length: %u %u", + 2 + sub_elem_len, elen); + rep->mld_links = 0; + break; + } + + if (*pos == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) { + const struct ieee80211_eht_per_sta_profile *sta_prof = + (const struct ieee80211_eht_per_sta_profile *) + (pos + 2); + u16 control; + u8 link_id; + + if (sub_elem_len < sizeof(*sta_prof)) { + wpa_printf(MSG_DEBUG, + "WNM: Invalid STA-profile length: %u", + sub_elem_len); + rep->mld_links = 0; + break; + } + + control = le_to_host16(sta_prof->sta_control); + + link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK; + if (link_id < MAX_NUM_MLD_LINKS) + rep->mld_links |= BIT(link_id); + } + + pos += 2 + sub_elem_len; + elen -= 2 + sub_elem_len; + } + + if (elen != 0) { + wpa_printf(MSG_DEBUG, + "WNM: Data left at end of multi-link element: %u", + elen); + rep->mld_links = 0; + } +} + + static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, u8 id, u8 elen, const u8 *pos) { @@ -533,6 +651,9 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, rep->mul_bssid->subelem_len = elen - 1; os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); break; + case WNM_NEIGHBOR_MULTI_LINK: + wnm_parse_neighbor_report_multi_link(rep, id, elen, pos); + break; default: wpa_printf(MSG_DEBUG, "WNM: Unsupported neighbor report subelement id %u", @@ -556,7 +677,7 @@ static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) } freq = ieee80211_chan_to_freq(country, op_class, chan); - if (freq <= 0 && op_class == 0) { + if (freq <= 0 && (op_class == 0 || op_class == 255)) { /* * Some APs do not advertise correct operating class * information. Try to determine the most likely operating @@ -615,38 +736,37 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, } -static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) -{ - unsigned int i; - - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) - wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; -} - -#ifdef CONFIG_MBO -static struct wpa_bss * -get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, +static void +fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s, enum mbo_transition_reject_reason *reason) { - struct wpa_bss *target = NULL; +#ifdef CONFIG_MBO struct wpa_bss_trans_info params; struct wpa_bss_candidate_info *info = NULL; - struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; - u8 *first_candidate_bssid = NULL, *pos; + struct neighbor_report *nei; + u8 *pos; unsigned int i; + if (!wpa_s->wnm_mbo_trans_reason_present) + return; + params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; params.n_candidates = 0; params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); if (!params.bssid) - return NULL; + return; pos = params.bssid; - for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { - if (nei->is_first) - first_candidate_bssid = nei->bssid; - if (!nei->acceptable) + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + nei = &wpa_s->wnm_neighbor_report_elements[i]; + + nei->drv_mbo_reject = 0; + + if (nei->preference_present && nei->preference == 0) continue; + + /* Should we query BSSIDs that we reject for other reasons? */ + os_memcpy(pos, nei->bssid, ETH_ALEN); pos += ETH_ALEN; params.n_candidates++; @@ -656,203 +776,36 @@ get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, goto end; info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); - if (!info) { - /* If failed to get candidate BSS transition status from driver, - * get the first acceptable candidate from wpa_supplicant. - */ - target = wpa_bss_get_bssid(wpa_s, params.bssid); + if (!info) goto end; - } - /* Get the first acceptable candidate from driver */ for (i = 0; i < info->num; i++) { - if (info->candidates[i].is_accept) { - target = wpa_bss_get_bssid(wpa_s, - info->candidates[i].bssid); - goto end; - } - } + int j; - /* If Disassociation Imminent is set and driver rejects all the - * candidate select first acceptable candidate which has - * rssi > disassoc_imminent_rssi_threshold - */ - if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { - for (i = 0; i < info->num; i++) { - target = wpa_bss_get_bssid(wpa_s, - info->candidates[i].bssid); - if (target && - (target->level < - wpa_s->conf->disassoc_imminent_rssi_threshold)) + for (j = 0; j < wpa_s->wnm_num_neighbor_report; j++) { + nei = &wpa_s->wnm_neighbor_report_elements[j]; + + if (!ether_addr_equal(info->candidates[i].bssid, + nei->bssid)) continue; - goto end; - } - } - /* While sending BTM reject use reason code of the first candidate - * received in BTM request frame - */ - if (reason) { - for (i = 0; i < info->num; i++) { - if (first_candidate_bssid && - ether_addr_equal(first_candidate_bssid, - info->candidates[i].bssid)) { + nei->drv_mbo_reject = !info->candidates[i].is_accept; + + /* Use the reject reason from the first candidate */ + if (i == 0 && nei->drv_mbo_reject) *reason = info->candidates[i].reject_reason; - break; - } + + break; } } - target = NULL; - end: os_free(params.bssid); if (info) { os_free(info->candidates); os_free(info); } - return target; -} #endif /* CONFIG_MBO */ - - -static struct wpa_bss * find_better_target(struct wpa_bss *a, - struct wpa_bss *b) -{ - if (!a) - return b; - if (!b) - return a; - - if (a->est_throughput > b->est_throughput) { - wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR - " est-tput: %d B: " MACSTR " est-tput: %d", - MAC2STR(a->bssid), a->est_throughput, - MAC2STR(b->bssid), b->est_throughput); - return a; - } - - wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR - " est-tput: %d B: " MACSTR " est-tput: %d", - MAC2STR(a->bssid), a->est_throughput, - MAC2STR(b->bssid), b->est_throughput); - return b; -} - -static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, - enum mbo_transition_reject_reason *reason) -{ - u8 i; - struct wpa_bss *bss = wpa_s->current_bss; - struct wpa_bss *target; - struct wpa_bss *best_target = NULL; - struct wpa_bss *bss_in_list = NULL; - - if (!bss) - return NULL; - - wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", - MAC2STR(wpa_s->bssid), bss->level); - - wnm_clear_acceptable(wpa_s); - - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { - struct neighbor_report *nei; - - nei = &wpa_s->wnm_neighbor_report_elements[i]; - if (nei->preference_present && nei->preference == 0) { - wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, - MAC2STR(nei->bssid)); - continue; - } - - target = wpa_bss_get_bssid(wpa_s, nei->bssid); - if (!target) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) not found in scan results", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1); - continue; - } - - if (age_secs) { - struct os_reltime now; - - if (os_get_reltime(&now) == 0 && - os_reltime_expired(&now, &target->last_update, - age_secs)) { - wpa_printf(MSG_DEBUG, - "Candidate BSS is more than %ld seconds old", - age_secs); - continue; - } - } - - /* - * TODO: Could consider allowing transition to another ESS if - * PMF was enabled for the association. - */ - if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, - 1, 0)) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) does not match the current network profile", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1); - continue; - } - - if (target->level < bss->level && target->level < -80) { - wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR - " (pref %d) does not have sufficient signal level (%d)", - MAC2STR(nei->bssid), - nei->preference_present ? nei->preference : - -1, - target->level); - continue; - } - - nei->acceptable = 1; - - best_target = find_better_target(target, best_target); - if (target == bss) - bss_in_list = bss; - } - -#ifdef CONFIG_MBO - if (wpa_s->wnm_mbo_trans_reason_present) - target = get_mbo_transition_candidate(wpa_s, reason); - else - target = best_target; -#else /* CONFIG_MBO */ - target = best_target; -#endif /* CONFIG_MBO */ - - if (!target) - return NULL; - - wpa_printf(MSG_DEBUG, - "WNM: Found an acceptable preferred transition candidate BSS " - MACSTR " (RSSI %d, tput: %d bss-tput: %d)", - MAC2STR(target->bssid), target->level, - target->est_throughput, bss->est_throughput); - - if (!bss_in_list) - return target; - - if ((!target->est_throughput && !bss_in_list->est_throughput) || - (target->est_throughput > bss_in_list->est_throughput && - target->est_throughput - bss_in_list->est_throughput > - bss_in_list->est_throughput >> 4)) { - /* It is more than 100/16 percent better, so switch. */ - return target; - } - - wpa_printf(MSG_DEBUG, - "WNM: Stay with our current BSS, not enough change in estimated throughput to switch"); - return bss_in_list; } @@ -892,10 +845,6 @@ static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) info |= NEI_REP_BSSID_INFO_APSD; if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) info |= NEI_REP_BSSID_INFO_RM; - if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) - info |= NEI_REP_BSSID_INFO_DELAYED_BA; - if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) - info |= NEI_REP_BSSID_INFO_IMM_BA; if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) @@ -941,45 +890,45 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpabuf **buf, u8 pref) { - const u8 *ie; u8 op_class, chan; - int sec_chan = 0; - enum oper_chan_width vht = CONF_OPER_CHWIDTH_USE_HT; + int sec_chan = 0, chanwidth = 0; + struct ieee802_11_elems elems; + struct ieee80211_ht_operation *ht_oper; enum phy_type phy_type; u32 info; - struct ieee80211_ht_operation *ht_oper = NULL; - struct ieee80211_vht_operation *vht_oper = NULL; - ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); - if (ie && ie[1] >= 2) { - ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + if (ieee802_11_parse_elems(wpa_bss_ie_ptr(bss), bss->ie_len, &elems, + 1) == ParseFailed) + return -2; - if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + chanwidth = get_operation_channel_width(&elems); + if (chanwidth == CHAN_WIDTH_UNKNOWN) { + wpa_printf(MSG_DEBUG, "Cannot determine channel width"); + return -2; + } + ht_oper = (struct ieee80211_ht_operation *) elems.ht_operation; + if (ht_oper) { + u8 sec_chan_offset = ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + + if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) sec_chan = 1; - else if (ht_oper->ht_param & + else if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) sec_chan = -1; } - ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); - if (ie && ie[1] >= 1) { - vht_oper = (struct ieee80211_vht_operation *) (ie + 2); - - if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ || - vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ || - vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ) - vht = vht_oper->vht_op_info_chwidth; - } - - if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, - &chan) == NUM_HOSTAPD_MODES) { + if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, chanwidth, + &op_class, &chan) == + NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine operating class and channel"); return -2; } - phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), - (vht_oper != NULL)); + phy_type = ieee80211_get_phy_type(bss->freq, elems.ht_operation != NULL, + elems.vht_operation != NULL, + elems.he_operation != NULL); if (phy_type == PHY_TYPE_UNSPECIFIED) { wpa_printf(MSG_DEBUG, "WNM: Cannot determine BSS phy type for Neighbor Report"); @@ -1016,7 +965,7 @@ static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) struct wpa_bss *bss = wpa_s->last_scan_res[i]; int res; - if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { + if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0, false)) { res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); if (res == -2) continue; /* could not build entry for BSS */ @@ -1076,9 +1025,10 @@ static int wnm_send_bss_transition_mgmt_resp( wpabuf_put_data(buf, target_bssid, ETH_ALEN); } else if (status == WNM_BSS_TM_ACCEPT) { /* - * P802.11-REVmc clarifies that the Target BSSID field is always - * present when status code is zero, so use a fake value here if - * no BSSID is yet known. + * IEEE Std 802.11-2024, 9.6.13.10 (BSS Transition Management + * Response frame format) clarifies that the Target BSSID field + * is always present when status code is zero, so use a fake + * value here if no BSSID is yet known. */ wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); } @@ -1173,13 +1123,14 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) { - struct wpa_bss *bss; + struct wpa_bss *bss, *current_bss = wpa_s->current_bss; struct wpa_ssid *ssid = wpa_s->current_ssid; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; enum mbo_transition_reject_reason reason = MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; + struct wpa_ssid *selected_ssid = NULL; - if (!wpa_s->wnm_dialog_token) + if (!ssid || !wpa_s->wnm_dialog_token) return 0; wpa_dbg(wpa_s, MSG_DEBUG, @@ -1192,8 +1143,50 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) goto send_bss_resp_fail; } + if (!pre_scan_check && !wpa_s->wnm_transition_scan) + return 0; + + wpa_s->wnm_transition_scan = false; + + /* Fetch MBO transition candidate rejection information from driver */ + fetch_drv_mbo_candidate_info(wpa_s, &reason); + /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s, 0, &reason); + bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1); +#ifdef CONFIG_MBO + if (!bss && wpa_s->wnm_mbo_trans_reason_present && + (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) { + int i; + bool changed = false; + + /* + * We didn't find any candidate, the driver had a say about + * which targets to reject and disassociation is immiment. + * + * We should still try to roam, so retry after ignoring the + * driver reject for any BSS that has an RSSI better than + * disassoc_imminent_rssi_threshold. + */ + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + bss = wpa_bss_get_bssid(wpa_s, nei->bssid); + if (bss && bss->level > + wpa_s->conf->disassoc_imminent_rssi_threshold) { + nei->drv_mbo_reject = 0; + changed = true; + } + } + + if (changed) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore driver rejection due to imminent disassociation and acceptable RSSI"); + bss = wpa_supplicant_select_bss(wpa_s, ssid, + &selected_ssid, 1); + } + } +#endif /* CONFIG_MBO */ /* * If this is a pre-scan check, returning 0 will trigger a scan and @@ -1216,34 +1209,57 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check) return 0; #ifndef CONFIG_NO_ROAMING - if (wpa_s->current_bss && bss != wpa_s->current_bss && - wpa_supplicant_need_to_roam_within_ess(wpa_s, - wpa_s->current_bss, - bss)) + if (current_bss && bss != current_bss && + wpa_supplicant_need_to_roam_within_ess(wpa_s, bss, + current_bss, false)) return 0; #endif /* CONFIG_NO_ROAMING */ } +#ifndef CONFIG_NO_ROAMING + /* Apply normal roaming rules if we can stay with the current BSS */ + if (current_bss && bss != current_bss && + wpa_scan_res_match(wpa_s, 0, current_bss, wpa_s->current_ssid, + 1, 0, false) && + !wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss, + true)) + bss = current_bss; +#endif /* CONFIG_NO_ROAMING */ + if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; goto send_bss_resp_fail; } + wpa_printf(MSG_DEBUG, + "WNM: Found an acceptable preferred transition candidate BSS " + MACSTR " (RSSI %d, tput: %d bss-tput: %d)", + MAC2STR(bss->bssid), bss->level, bss->est_throughput, + current_bss ? (int) current_bss->est_throughput : -1); + /* Associate to the network */ wnm_bss_tm_connect(wpa_s, bss, ssid, 1); return 1; send_bss_resp_fail: - /* Send reject response for all the failures */ + if (wpa_s->wnm_reply) { + /* If disassoc imminent is set, we must not reject */ + if (wpa_s->wnm_mode & + (WNM_BSS_TM_REQ_DISASSOC_IMMINENT | + WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)) { + wpa_printf(MSG_DEBUG, + "WNM: Accept BTM request because disassociation imminent bit is set"); + status = WNM_BSS_TM_ACCEPT; + } - if (wpa_s->wnm_reply) wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason, 0, NULL); + } wnm_btm_reset(wpa_s); - return 0; + return 1; } @@ -1290,15 +1306,22 @@ static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) return; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; + char mld_info[42] = ""; nei = &wpa_s->wnm_neighbor_report_elements[i]; + + if (!is_zero_ether_addr(nei->mld_addr)) + os_snprintf(mld_info, sizeof(mld_info) - 1, + " mld_addr=" MACSTR " links=0x%02x", + MAC2STR(nei->mld_addr), nei->mld_links); + wpa_printf(MSG_DEBUG, "%u: " MACSTR - " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", + " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d%s", i, MAC2STR(nei->bssid), nei->bssid_info, nei->regulatory_class, nei->channel_number, nei->phy_type, nei->preference_present ? nei->preference : -1, - nei->freq); + nei->freq, mld_info); } } @@ -1327,9 +1350,8 @@ static int chan_supported(struct wpa_supplicant *wpa_s, int freq) static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) { - int *freqs; - int num_freqs = 0; unsigned int i; + bool has_6ghz = false; if (!wpa_s->wnm_neighbor_report_elements) return; @@ -1340,10 +1362,6 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; - freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); - if (freqs == NULL) - return; - for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; @@ -1357,22 +1375,134 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) "WNM: Unknown neighbor operating frequency for " MACSTR " - scan all channels", MAC2STR(nei->bssid)); - os_free(freqs); + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; return; } if (chan_supported(wpa_s, nei->freq)) - add_freq(freqs, &num_freqs, nei->freq); + int_array_add_unique(&wpa_s->next_scan_freqs, + nei->freq); + has_6ghz |= is_6ghz_freq(nei->freq); } - if (num_freqs == 0) { - os_free(freqs); + if (!wpa_s->next_scan_freqs) return; - } wpa_printf(MSG_DEBUG, - "WNM: Scan %d frequencies based on transition candidate list", - num_freqs); - wpa_s->next_scan_freqs = freqs; + "WNM: Scan %zu frequencies based on transition candidate list", + int_array_len(wpa_s->next_scan_freqs)); + + /* + * Candidates on 6 GHz channels might be collocated ones, and thus, in + * order to discover them need to include the frequencies on the 2.4 + * and 5 GHz bands. Since the scan time can be long, optimize the case + * of a single channel by forcing passive scan instead of doing a + * collocated scan. + */ + if (has_6ghz) { + struct hostapd_channel_data *chan; + struct os_reltime now; + + /* In case the candidate validity time is too short, force it + * to be long enough to account for the longer scan time. + */ + os_get_reltime(&now); + now.sec += 5; + + if (os_reltime_initialized(&wpa_s->wnm_cand_valid_until) && + !os_reltime_before(&wpa_s->wnm_cand_valid_until, &now)) { + wpa_printf(MSG_DEBUG, + "WNM: Scan: 6 GHz: update validity time"); + + os_memcpy(&wpa_s->wnm_cand_valid_until, &now, + sizeof(now)); + } + + if (int_array_len(wpa_s->next_scan_freqs) == 1) { + wpa_printf(MSG_DEBUG, + "WNM: Scan: Single 6 GHz channel: passive"); + + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->manual_scan_passive = 1; + return; + } + + wpa_printf(MSG_DEBUG, + "WNM: Scan: Add 2.4/5 GHz channels as well for 6 GHz discovery"); + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + int j; + + /* skip all the irrelevant modes */ + if ((mode->mode != HOSTAPD_MODE_IEEE80211B && + mode->mode != HOSTAPD_MODE_IEEE80211G && + mode->mode != HOSTAPD_MODE_IEEE80211A) || + mode->is_6ghz) + continue; + + for (j = 0; j < mode->num_channels; j++) { + chan = &mode->channels[j]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + int_array_add_unique(&wpa_s->next_scan_freqs, + chan->freq); + } + } + + wpa_printf(MSG_DEBUG, + "WNM: Scan %zu frequencies (including collocated)", + int_array_len(wpa_s->next_scan_freqs)); + } +} + + +static int wnm_parse_candidate_list(struct wpa_supplicant *wpa_s, + const u8 *pos, const u8 *end, + int *num_valid_candidates) +{ + *num_valid_candidates = 0; + + while (end - pos >= 2 && + wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { + u8 tag = *pos++; + u8 len = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "WNM: Truncated request"); + return -1; + } + if (tag == WLAN_EID_NEIGHBOR_REPORT) { + struct neighbor_report *rep; + + if (!wpa_s->wnm_num_neighbor_report) { + wpa_s->wnm_neighbor_report_elements = os_calloc( + WNM_MAX_NEIGHBOR_REPORT, + sizeof(struct neighbor_report)); + if (!wpa_s->wnm_neighbor_report_elements) + return -1; + } + + rep = &wpa_s->wnm_neighbor_report_elements[ + wpa_s->wnm_num_neighbor_report]; + wnm_parse_neighbor_report(wpa_s, pos, len, rep); + if ((wpa_s->wnm_mode & + WNM_BSS_TM_REQ_DISASSOC_IMMINENT) && + ether_addr_equal(rep->bssid, wpa_s->bssid)) + rep->disassoc_imminent = 1; + + if (rep->preference_present && rep->preference) + *num_valid_candidates += 1; + + wpa_s->wnm_num_neighbor_report++; + } + + pos += len; + } + + return 0; } @@ -1386,6 +1516,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *vendor; #endif /* CONFIG_MBO */ bool disassoc_imminent; + int num_valid_candidates; if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm) return; @@ -1402,7 +1533,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; - wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); + wpa_s->wnm_disassoc_timer = WPA_GET_LE16(pos + 2); wpa_s->wnm_link_removal = false; valid_int = pos[4]; wpa_s->wnm_reply = reply; @@ -1411,7 +1542,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, - wpa_s->wnm_dissoc_timer, valid_int); + wpa_s->wnm_disassoc_timer, valid_int); + + if (!wpa_s->wnm_dialog_token) { + wpa_printf(MSG_DEBUG, "WNM: Invalid dialog token"); + goto reset; + } #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) if (wpa_s->reject_btm_req_reason) { @@ -1421,7 +1557,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->reject_btm_req_reason, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); - return; + goto reset; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ @@ -1430,7 +1566,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); - return; + goto reset; } os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ @@ -1443,13 +1579,13 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (end - pos < 1) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); - return; + goto reset; } url_len = *pos++; if (url_len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition Management Request (URL truncated)"); - return; + goto reset; } os_memcpy(url, pos, url_len); url[url_len] = '\0'; @@ -1457,17 +1593,20 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), - wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); + wpa_s->wnm_disassoc_timer * beacon_int * 128 / 125, + url); } disassoc_imminent = wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT; /* - * Based on IEEE P802.11be/D5.0, when a station is a non-AP MLD with - * more than one affiliated link, the Link Removal Imminent field is - * set to 1, and the BSS Termination Included field is set to 1, only - * one of the links is removed and the other links remain associated. - * Ignore the Disassociation Imminent field in such a case. + * Based on IEEE Std 802.11be-2024, Table 9-538a (BSS Termination + * Included and Link Removal Imminent fields encoding), when a station + * is a non-AP MLD with more than one affiliated link, the Link Removal + * Imminent field is set to 1, and the BSS Termination Included field + * is set to 1, only one of the links is removed and the other links + * remain associated. Ignore the Disassociation Imminent field in such + * a case. * * TODO: We should check if the AP has more than one link. * TODO: We should pass the RX link and use that @@ -1484,29 +1623,28 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL); - return; + goto reset; } /* The last link is being removed (which must be the assoc link) */ wpa_s->wnm_link_removal = true; - os_memcpy(wpa_s->wnm_dissoc_addr, + wpa_s->wnm_disassoc_mld = false; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, ETH_ALEN); + } else if (wpa_s->valid_links) { + wpa_s->wnm_disassoc_mld = true; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->ap_mld_addr, + ETH_ALEN); } else { - os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ? - wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN); + wpa_s->wnm_disassoc_mld = false; + os_memcpy(wpa_s->wnm_disassoc_addr, wpa_s->bssid, ETH_ALEN); } - if (disassoc_imminent) { + if (disassoc_imminent) wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " - "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); - if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning && - (!wpa_s->current_ssid || !wpa_s->current_ssid->bssid_set)) { - wpa_printf(MSG_DEBUG, "Trying to find another BSS"); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - } + "Disassociation Timer %u", wpa_s->wnm_disassoc_timer); #ifdef CONFIG_MBO vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); @@ -1514,53 +1652,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); #endif /* CONFIG_MBO */ + if (wnm_parse_candidate_list(wpa_s, pos, end, + &num_valid_candidates) < 0) + goto reset; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { - unsigned int valid_ms; - - wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); - wpa_s->wnm_neighbor_report_elements = os_calloc( - WNM_MAX_NEIGHBOR_REPORT, - sizeof(struct neighbor_report)); - if (wpa_s->wnm_neighbor_report_elements == NULL) - return; - - while (end - pos >= 2 && - wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) - { - u8 tag = *pos++; - u8 len = *pos++; - - wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", - tag); - if (len > end - pos) { - wpa_printf(MSG_DEBUG, "WNM: Truncated request"); - return; - } - if (tag == WLAN_EID_NEIGHBOR_REPORT) { - struct neighbor_report *rep; - rep = &wpa_s->wnm_neighbor_report_elements[ - wpa_s->wnm_num_neighbor_report]; - wnm_parse_neighbor_report(wpa_s, pos, len, rep); - if ((wpa_s->wnm_mode & - WNM_BSS_TM_REQ_DISASSOC_IMMINENT) && - ether_addr_equal(rep->bssid, wpa_s->bssid)) - rep->disassoc_imminent = 1; - - wpa_s->wnm_num_neighbor_report++; -#ifdef CONFIG_MBO - if (wpa_s->wnm_mbo_trans_reason_present && - wpa_s->wnm_num_neighbor_report == 1) { - rep->is_first = 1; - wpa_printf(MSG_DEBUG, - "WNM: First transition candidate is " - MACSTR, MAC2STR(rep->bssid)); - } -#endif /* CONFIG_MBO */ - } - - pos += len; - } - if (!wpa_s->wnm_num_neighbor_report) { wpa_printf(MSG_DEBUG, "WNM: Candidate list included bit is set, but no candidates found"); @@ -1568,18 +1664,13 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); - return; + goto reset; } + wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); + } - if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) { - wpa_printf(MSG_DEBUG, - "WNM: Configuration prevents roaming (BSSID set)"); - wnm_send_bss_transition_mgmt_resp( - wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, - NULL); - return; - } + if (wpa_s->wnm_num_neighbor_report) { + unsigned int valid_ms; wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); @@ -1587,40 +1678,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", valid_ms); os_get_reltime(&wpa_s->wnm_cand_valid_until); - wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; - wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; - wpa_s->wnm_cand_valid_until.sec += - wpa_s->wnm_cand_valid_until.usec / 1000000; - wpa_s->wnm_cand_valid_until.usec %= 1000000; - - /* - * Try fetching the latest scan results from the kernel. - * This can help in finding more up-to-date information should - * the driver have done some internal scanning operations after - * the last scan result update in wpa_supplicant. - * - * It is not a new scan, this does not update the last_scan - * timestamp nor will it expire old BSSs. - */ - wpa_supplicant_update_scan_results(wpa_s, NULL); - if (wnm_scan_process(wpa_s, true) > 0) - return; - wpa_printf(MSG_DEBUG, - "WNM: No valid match in previous scan results - try a new scan"); - - wnm_set_scan_freqs(wpa_s); - if (wpa_s->wnm_num_neighbor_report == 1) { - os_memcpy(wpa_s->next_scan_bssid, - wpa_s->wnm_neighbor_report_elements[0].bssid, - ETH_ALEN); - wpa_printf(MSG_DEBUG, - "WNM: Scan only for a specific BSSID since there is only a single candidate " - MACSTR, MAC2STR(wpa_s->next_scan_bssid)); - } - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (reply) { + os_reltime_add_ms(&wpa_s->wnm_cand_valid_until, valid_ms); + } else if (!disassoc_imminent) { enum bss_trans_mgmt_status_code status; + /* No candidate list and disassociation is not imminent */ + if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) || wpa_s->wnm_link_removal) status = WNM_BSS_TM_ACCEPT; @@ -1628,10 +1691,66 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } - wnm_send_bss_transition_mgmt_resp( - wpa_s, status, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); + + if (reply) + wnm_send_bss_transition_mgmt_resp( + wpa_s, status, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); + + goto reset; } + + /* + * Try fetching the latest scan results from the kernel. + * This can help in finding more up-to-date information should + * the driver have done some internal scanning operations after + * the last scan result update in wpa_supplicant. + * + * It is not a new scan, this does not update the last_scan + * timestamp nor will it expire old BSSs. + */ + wpa_supplicant_update_scan_results(wpa_s, NULL); + if (wnm_scan_process(wpa_s, true) > 0) + return; + wpa_printf(MSG_DEBUG, + "WNM: No valid match in previous scan results - try a new scan"); + + /* + * If we have a fixed BSSID configured, just reject at this point. + * NOTE: We could actually check if we are allowed to stay (and we do + * above if we have scan results available). + */ + if (wpa_s->current_ssid && wpa_s->current_ssid->bssid_set) { + wpa_printf(MSG_DEBUG, "WNM: Fixed BSSID, rejecting request"); + + if (reply) + wnm_send_bss_transition_mgmt_resp( + wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); + + goto reset; + } + + wnm_set_scan_freqs(wpa_s); + if (num_valid_candidates == 1) { + /* Any invalid candidate was sorted to the end */ + os_memcpy(wpa_s->next_scan_bssid, + wpa_s->wnm_neighbor_report_elements[0].bssid, + ETH_ALEN); + wpa_printf(MSG_DEBUG, + "WNM: Scan only for a specific BSSID since there is only a single candidate " + MACSTR, MAC2STR(wpa_s->next_scan_bssid)); + } + wpa_s->wnm_transition_scan = true; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + /* Continue from scan handler */ + return; + +reset: + wnm_btm_reset(wpa_s); } @@ -1753,45 +1872,6 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, WPA_GET_BE24(pos), pos[3]); #ifdef CONFIG_HS20 - if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && - WPA_GET_BE24(pos) == OUI_WFA && - pos[3] == HS20_WNM_SUB_REM_NEEDED) { - /* Subscription Remediation subelement */ - const u8 *ie_end; - u8 url_len; - char *url; - u8 osu_method; - - wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " - "subelement"); - ie_end = pos + ie_len; - pos += 4; - url_len = *pos++; - if (url_len == 0) { - wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); - url = NULL; - osu_method = 1; - } else { - if (url_len + 1 > ie_end - pos) { - wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", - url_len, - (int) (ie_end - pos)); - break; - } - url = os_malloc(url_len + 1); - if (url == NULL) - break; - os_memcpy(url, pos, url_len); - url[url_len] = '\0'; - osu_method = pos[url_len]; - } - hs20_rx_subscription_remediation(wpa_s, url, - osu_method); - os_free(url); - pos = next; - continue; - } - if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && WPA_GET_BE24(pos) == OUI_WFA && pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { @@ -1858,11 +1938,20 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, - const u8 *sa, const u8 *frm, int len) + const u8 *da, const u8 *sa, + const u8 *frm, int len) { const u8 *pos, *end; u8 dialog_token, type; + if (is_multicast_ether_addr(da)) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore group-addressed WNM Notification Request frame (A1=" + MACSTR " A2=" MACSTR ")", + MAC2STR(da), MAC2STR(sa)); + return; + } + /* Dialog Token [1] | Type [1] | Subelements */ if (len < 2 || sa == NULL) @@ -1971,10 +2060,12 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, !(mgmt->da[0] & 0x01)); break; case WNM_SLEEP_MODE_RESP: - ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); + ieee802_11_rx_wnmsleep_resp(wpa_s, mgmt->da, mgmt->sa, + pos, end - pos); break; case WNM_NOTIFICATION_REQ: - ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); + ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->da, mgmt->sa, + pos, end - pos); break; case WNM_COLLOCATED_INTERFERENCE_REQ: ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, @@ -2052,22 +2143,59 @@ void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { - if (!(wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) - return false; + int i; /* * In case disassociation imminent is set, do no try to use a BSS to * which we are connected. */ - if (wpa_s->wnm_link_removal || - !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) || - is_zero_ether_addr(bss->mld_addr)) { - if (ether_addr_equal(bss->bssid, wpa_s->wnm_dissoc_addr)) - return true; - } else { - if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_dissoc_addr)) - return true; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + if (!wpa_s->wnm_disassoc_mld) { + if (ether_addr_equal(bss->bssid, + wpa_s->wnm_disassoc_addr)) + return true; + } else { + if (ether_addr_equal(bss->mld_addr, + wpa_s->wnm_disassoc_addr)) + return true; + } } + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (!ether_addr_equal(nei->bssid, bss->bssid) && + (is_zero_ether_addr(bss->mld_addr) || + !ether_addr_equal(nei->mld_addr, bss->mld_addr))) + continue; + + if (nei->preference_present && nei->preference == 0) + return true; + +#ifdef CONFIG_MBO + if (nei->drv_mbo_reject) + return true; +#endif /* CONFIG_MBO */ + + /* + * NOTE: We should select one entry and stick with it, but to + * do that we need to refactor the BSS selection to be MLD + * aware from the beginning. Instead we just check whether the + * link is permitted in any possible configuration. We are not + * supposed to do that, however the AP is able to reject a + * subset of the requested links. + */ + if (nei->mld_links && !(nei->mld_links & BIT(bss->mld_link_id))) + continue; + + break; + } + + /* If the abridged bit is set, the BSS must be a known neighbor. */ + if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ABRIDGED) && + wpa_s->wnm_num_neighbor_report == i) + return true; + return false; } diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.h b/contrib/wpa/wpa_supplicant/wnm_sta.h index 235a838fab77..2977e482bffd 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.h +++ b/contrib/wpa/wpa_supplicant/wnm_sta.h @@ -23,6 +23,8 @@ struct multiple_bssid { struct neighbor_report { u8 bssid[ETH_ALEN]; + u8 mld_addr[ETH_ALEN]; + u16 mld_links; /* may be zero if no link IDs were specified */ u32 bssid_info; u8 regulatory_class; u8 channel_number; @@ -44,9 +46,8 @@ struct neighbor_report { unsigned int rm_capab_present:1; unsigned int bearing_present:1; unsigned int bss_term_present:1; - unsigned int acceptable:1; #ifdef CONFIG_MBO - unsigned int is_first:1; + unsigned int drv_mbo_reject:1; #endif /* CONFIG_MBO */ struct measurement_pilot *meas_pilot; struct multiple_bssid *mul_bssid; diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 02e243a27367..dd4f993b1dc8 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -29,7 +29,7 @@ static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2024, Jouni Malinen and contributors"; +"Copyright (c) 2004-2026, Jouni Malinen and contributors"; #define VENDOR_ELEM_FRAME_ID \ " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \ @@ -425,6 +425,13 @@ static int wpa_cli_cmd_mlo_signal_poll(struct wpa_ctrl *ctrl, int argc, char *ar } +static int wpa_cli_cmd_setup_link_reconfig(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "SETUP_LINK_RECONFIG", 1, argc, argv); +} + + static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -507,7 +514,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "ap_vendor_elements", "ignore_old_scan_res", "freq_list", "scan_cur_freq", "scan_res_valid_for_connect", "sched_scan_interval", - "tdls_external_control", "osu_dir", "wowlan_triggers", + "tdls_external_control", "wowlan_triggers", "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", "reassoc_same_bss_optim", "wps_priority", @@ -517,6 +524,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) #endif /* CONFIG_TESTING_OPTIONS */ "relative_rssi", "relative_band_adjust", "extended_key_id", + "disable_op_classes_80_80_mhz", }; int i, num_fields = ARRAY_SIZE(fields); @@ -614,10 +622,11 @@ static char ** wpa_cli_complete_get(const char *str, int pos) "scan_cur_freq", "scan_res_valid_for_connect", "sched_scan_interval", "sched_scan_start_delay", - "tdls_external_control", "osu_dir", "wowlan_triggers", + "tdls_external_control", "wowlan_triggers", "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", - "reassoc_same_bss_optim", "extended_key_id" + "reassoc_same_bss_optim", "extended_key_id", + "disable_op_classes_80_80_mhz" }; int i, num_fields = ARRAY_SIZE(fields); @@ -1878,21 +1887,21 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, printf("Invalid INTERFACE_ADD command: needs at least one " "argument (interface name)\n" "All arguments: ifname confname driver ctrl_interface " - "driver_param bridge_name [create]\n"); + "driver_param bridge_name [create [addr]]\n"); return -1; } /* * INTERFACE_ADD TABTABTABTAB - * TAB[TAB[TAB]] + * TAB[TAB[TAB][TAB]] */ res = os_snprintf(cmd, sizeof(cmd), - "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", argv[0], argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "", - argc > 7 ? argv[7] : ""); + argc > 7 ? argv[7] : "", argc > 8 ? argv[8] : ""); if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -2778,37 +2787,6 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, return wpa_ctrl_command(ctrl, cmd); } - -static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - char cmd[512]; - - if (argc < 2) { - printf("Command needs two arguments (dst mac addr and " - "icon name)\n"); - return -1; - } - - if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0) - return -1; - - return wpa_ctrl_command(ctrl, cmd); -} - - -static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - return wpa_ctrl_command(ctrl, "FETCH_OSU"); -} - - -static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU"); -} - #endif /* CONFIG_HS20 */ @@ -3308,6 +3286,38 @@ static int wpa_cli_cmd_pasn_deauth(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "PASN_DEAUTH", 1, argc, argv); } + +#ifdef CONFIG_PR + +static int wpa_cli_cmd_pr_pasn_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "PR_PASN_START", 4, argc, argv); +} + + +static int wpa_cli_cmd_pr_set_dik_context(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "PR_SET_DIK_CONTEXT", 1, argc, argv); +} + + +static int wpa_cli_cmd_pr_clear_dik_context(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PR_CLEAR_DIK_CONTEXT"); +} + + +static int wpa_cli_cmd_pr_ranging_abort(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PR_RANGING_ABORT"); +} + +#endif /* CONFIG_PR */ + #endif /* CONFIG_PASN */ @@ -3335,6 +3345,170 @@ static int wpa_cli_cmd_dscp_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_NAN_USD + +static int wpa_cli_cmd_nan_publish(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_PUBLISH", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_cancel_publish(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_CANCEL_PUBLISH", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_update_publish(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_UPDATE_PUBLISH", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_subscribe(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_SUBSCRIBE", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_cancel_subscribe(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_CANCEL_SUBSCRIBE", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_transmit(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_TRANSMIT", 3, argc, argv); +} + + +static int wpa_cli_cmd_nan_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "NAN_FLUSH"); +} + +#endif /* CONFIG_NAN_USD */ + + +#ifdef CONFIG_NAN + +static int wpa_cli_cmd_nan_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_START", 0, argc, argv); +} + + +static int wpa_cli_cmd_nan_stop(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_STOP", 0, argc, argv); +} + + +static int wpa_cli_cmd_nan_set(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_SET", 2, argc, argv); +} + + +static int wpa_cli_cmd_nan_update_conf(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_UPDATE_CONF", 0, argc, argv); +} + + +static int wpa_cli_cmd_nan_sched_config_map(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_SCHED_CONFIG_MAP", 1, argc, argv); +} + + +static int wpa_cli_cmd_nan_ndp_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_NDP_REQUEST", 4, argc, argv); +} + + +static int wpa_cli_cmd_nan_ndp_response(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_NDP_RESPONSE", 3, argc, argv); +} + + +static int wpa_cli_cmd_nan_ndp_terminate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_NDP_TERMINATE", 3, argc, argv); +} + + +static int wpa_cli_cmd_nan_peer_info(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_PEER_INFO", 2, argc, argv); +} + + +static int wpa_cli_cmd_nan_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "NAN_STATUS"); +} + + +static int wpa_cli_cmd_nan_bootstrap(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_BOOTSTRAP", 4, argc, argv); +} + + +static int wpa_cli_cmd_nan_bootstrap_reset(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_BOOTSTRAP_RESET", 1, argc, argv); +} + + +#ifdef CONFIG_PASN + +static int wpa_cli_cmd_nan_pair(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_PAIR", 5, argc, argv); +} + + +static int wpa_cli_cmd_nan_pair_abort(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NAN_PAIR_ABORT", 1, argc, argv); +} + +#endif /* CONFIG_PASN */ + +#endif /* CONFIG_NAN */ + + +static int wpa_cli_cmd_generate_new_mac(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "NEW_RANDOM_MAC_ADDRESS"); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -3869,14 +4043,6 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list, wpa_cli_complete_bss, cli_cmd_flag_none, " = get HS20 nai home realm list" }, - { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request, - wpa_cli_complete_bss, cli_cmd_flag_none, - " = get Hotspot 2.0 OSU icon" }, - { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none, - "= fetch OSU provider information from all APs" }, - { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL, - cli_cmd_flag_none, - "= cancel fetch_osu command" }, #endif /* CONFIG_HS20 */ { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL, cli_cmd_flag_none, @@ -4065,6 +4231,18 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "pasn_deauth", wpa_cli_cmd_pasn_deauth, NULL, cli_cmd_flag_none, "bssid= = Remove PASN PTKSA state" }, +#ifdef CONFIG_PR + { "pr_pasn_start", wpa_cli_cmd_pr_pasn_start, NULL, + cli_cmd_flag_none, + "bssid= role= ranging_type=" }, + { "pr_set_dik_context", wpa_cli_cmd_pr_set_dik_context, NULL, + cli_cmd_flag_sensitive, + "self dik= password= pmk=" }, + { "pr_clear_dik_context", wpa_cli_cmd_pr_clear_dik_context, NULL, + cli_cmd_flag_none, "= Clear all DIK contexts" }, + { "pr_ranging_abort", wpa_cli_cmd_pr_ranging_abort, NULL, + cli_cmd_flag_none, "= Abort an ongoing proximity ranging session" }, +#endif /* CONFIG_PR */ #endif /* CONFIG_PASN */ { "mscs", wpa_cli_cmd_mscs, NULL, cli_cmd_flag_none, @@ -4078,12 +4256,76 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "dscp_query", wpa_cli_cmd_dscp_query, NULL, cli_cmd_flag_none, "wildcard/domain_name= = Send DSCP Query" }, + { "setup_link_reconfig", wpa_cli_cmd_setup_link_reconfig, NULL, + cli_cmd_flag_none, + "< [ID2]...> = Add new setup links and/or remove existing ones for the current MLO connection in STA mode" }, { "mlo_status", wpa_cli_cmd_mlo_status, NULL, cli_cmd_flag_none, "= get MLO status" }, { "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL, cli_cmd_flag_none, "= get mlo signal parameters" }, +#ifdef CONFIG_NAN_USD + { "nan_publish", wpa_cli_cmd_nan_publish, NULL, + cli_cmd_flag_none, + "service_name= [ttl=] [freq=] [freq_list=] [srv_proto_type=] [ssi=] [solicited=0] [unsolicited=0] [fsd=0] [p2p=1] [pr=1] = Publish NAN service" }, + { "nan_cancel_publish", wpa_cli_cmd_nan_cancel_publish, NULL, + cli_cmd_flag_none, "publish_id= = Cancel NAN USD publish instance" }, + { "nan_update_publish", wpa_cli_cmd_nan_update_publish, NULL, + cli_cmd_flag_none, "publish_id= [ssi= = Update publish" }, + { "nan_subscribe", wpa_cli_cmd_nan_subscribe, NULL, + cli_cmd_flag_none, + "service_name= [active=1] [ttl=] [freq=] [srv_proto_type=] [ssi=] [p2p=1] [pr=1] = Subscribe to NAN service" }, + { "nan_cancel_subscribe", wpa_cli_cmd_nan_cancel_subscribe, NULL, + cli_cmd_flag_none, "subscribe_id= = Cancel NAN USD subscribe instance" }, + { "nan_transmit", wpa_cli_cmd_nan_transmit, NULL, + cli_cmd_flag_none, "handle= req_instance_id= address= [ssi=] = Transmit NAN follow up" }, + { "nan_flush", wpa_cli_cmd_nan_flush, NULL, + cli_cmd_flag_none, "= Flush all NAN USD services" }, +#endif /* CONFIG_NAN_USD */ +#ifdef CONFIG_NAN + { "nan_start", wpa_cli_cmd_nan_start, NULL, cli_cmd_flag_none, + "= start/join NAN cluster with current configuration" }, + { "nan_stop", wpa_cli_cmd_nan_stop, NULL, cli_cmd_flag_none, + "= stop NAN operation" }, + { "nan_set", wpa_cli_cmd_nan_set, NULL, cli_cmd_flag_none, + "= set NAN configuration variable" }, + { "nan_update_conf", wpa_cli_cmd_nan_update_conf, NULL, + cli_cmd_flag_none, "= update NAN configuration" }, + { "nan_sched_config_map", wpa_cli_cmd_nan_sched_config_map, NULL, + cli_cmd_flag_none, + "map_id= [freq:bitmap_hex] = Configure NAN schedule. Clear if no bitmaps provided" }, + { "nan_ndp_request", wpa_cli_cmd_nan_ndp_request, NULL, + cli_cmd_flag_sensitive, + "handle= ndi= peer_nmi= peer_id= [ssi=] [qos=] [csid = |pmk=>] = Request NAN data path" }, + { "nan_ndp_response", wpa_cli_cmd_nan_ndp_response, NULL, + cli_cmd_flag_sensitive, + "accept|reject peer_nmi= [reason_code=] [ndi= handle= init_ndi= ndp_id= [ssi=] [qos=] [csid= |pmk=]] = Respond to NAN data path request" }, + { "nan_ndp_terminate", wpa_cli_cmd_nan_ndp_terminate, NULL, + cli_cmd_flag_none, + "peer_nmi= init_ndi= ndp_id= = Terminate NAN data path" }, + { "nan_peer_info", wpa_cli_cmd_nan_peer_info, NULL, + cli_cmd_flag_none, + " [map_id] = Get NAN peer information" }, + { "nan_status", wpa_cli_cmd_nan_status, NULL, + cli_cmd_flag_none, "= Get NAN status" }, + { "nan_bootstrap", wpa_cli_cmd_nan_bootstrap, NULL, + cli_cmd_flag_none, + " = > > > [auth] = Request or authorize NAN boostrapping with peer" }, + { "nan_bootstrap_reset", wpa_cli_cmd_nan_bootstrap_reset, NULL, + cli_cmd_flag_none, + " = = Reset NAN boostrapping with peer" }, +#ifdef CONFIG_PASN + { "nan_pair", wpa_cli_cmd_nan_pair, NULL, + cli_cmd_flag_sensitive, + " = > > > > [password=] [responder] = Request or authorize NAN pairing with peer" }, + { "nan_pair_abort", wpa_cli_cmd_nan_pair_abort, NULL, + cli_cmd_flag_none, + " = = Abort NAN pairing with peer" }, +#endif /* CONFIG_PASN */ +#endif /* CONFIG_NAN */ + { "new_random_mac_address", wpa_cli_cmd_generate_new_mac, NULL, + cli_cmd_flag_none, "= Generate new random MAC address" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -4426,8 +4668,6 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) { - wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) { diff --git a/contrib/wpa/wpa_supplicant/wpa_passphrase.c b/contrib/wpa/wpa_supplicant/wpa_passphrase.c index cfab4f1b5551..704a23353ea1 100644 --- a/contrib/wpa/wpa_supplicant/wpa_passphrase.c +++ b/contrib/wpa/wpa_supplicant/wpa_passphrase.c @@ -33,10 +33,14 @@ int main(int argc, char *argv[]) if (argc > 2) { passphrase = argv[2]; } else { - bool ctrl_echo; + bool ctrl_echo, notty = false; fprintf(stderr, "# reading passphrase from stdin\n"); if (tcgetattr(STDIN_FILENO, &term) < 0) { + if (errno == ENOTTY) { + notty = true; + goto read_pw; + } perror("tcgetattr"); return 1; } @@ -46,12 +50,14 @@ int main(int argc, char *argv[]) perror("tcsetattr:error disabling echo"); return 1; } + read_pw: if (fgets(buf, sizeof(buf), stdin) == NULL) { fprintf(stderr, "Failed to read passphrase\n"); return 1; } term.c_lflag |= ECHO; - if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) { + if (!notty && + ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) { perror("tcsetattr:error enabling echo"); return 1; } diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 6f4ceb017236..2b5ca594524f 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -43,6 +43,7 @@ #include "common/gas_server.h" #include "common/dpp.h" #include "common/ptksa_cache.h" +#include "common/proc_coord.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "bssid_ignore.h" @@ -65,7 +66,8 @@ #include "wpas_kay.h" #include "mesh.h" #include "dpp_supplicant.h" -#include "nan_usd.h" +#include "pr_supplicant.h" +#include "nan_supplicant.h" #ifdef CONFIG_MESH #include "ap/ap_config.h" #include "ap/hostapd.h" @@ -73,7 +75,7 @@ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2024, Jouni Malinen and contributors"; +"Copyright (c) 2003-2026, Jouni Malinen and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -132,6 +134,8 @@ static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s); #ifdef CONFIG_OWE static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s); #endif /* CONFIG_OWE */ +static void radio_remove_pending_connect(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid); #ifdef CONFIG_WEP @@ -416,8 +420,13 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsne_override(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifndef CONFIG_NO_WPA wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); +#endif /* CONFIG_NO_WPA */ wpa_s->rsnxe_len = 0; wpa_s->pairwise_cipher = WPA_CIPHER_NONE; wpa_s->group_cipher = WPA_CIPHER_NONE; @@ -445,6 +454,7 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, 0); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ASSOC_ENC, 0); pmksa_cache_clear_current(wpa_s->wpa); os_memset(&mlo, 0, sizeof(mlo)); @@ -500,6 +510,89 @@ void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s) } +static struct wpabuf * wpas_wfa_gen_capab_attr(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *attr; + size_t gen_len, supp_len; + const u8 *supp; + u8 supp_buf[1]; + + if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_DISABLED) + return NULL; + + if (!wpa_s->conf->wfa_gen_capa_supp || + wpabuf_len(wpa_s->conf->wfa_gen_capa_supp) == 0) { + supp_len = 1; + supp_buf[0] = 0; + if (wpa_s->hw_capab & BIT(CAPAB_HT)) + supp_buf[0] |= BIT(0); /* Wi-Fi 4 */ + if (wpa_s->hw_capab & BIT(CAPAB_VHT)) + supp_buf[0] |= BIT(1); /* Wi-Fi 5 */ + if (wpa_s->hw_capab & BIT(CAPAB_HE)) + supp_buf[0] |= BIT(2); /* Wi-Fi 6 */ + if (wpa_s->hw_capab & BIT(CAPAB_EHT)) + supp_buf[0] |= BIT(3); /* Wi-Fi 7 */ + supp = supp_buf; + } else { + supp_len = wpabuf_len(wpa_s->conf->wfa_gen_capa_supp); + supp = wpabuf_head(wpa_s->conf->wfa_gen_capa_supp); + } + + gen_len = 1 + supp_len; + + attr = wpabuf_alloc(2 + gen_len); + if (!attr) + return NULL; + + wpabuf_put_u8(attr, WFA_CAPA_ATTR_GENERATIONAL_CAPAB); + wpabuf_put_u8(attr, gen_len); + wpabuf_put_u8(attr, supp_len); + wpabuf_put_data(attr, supp, supp_len); + + return attr; +} + + + +static void wpas_wfa_capab_tx(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpabuf *attr, *buf; + size_t buf_len; + + if (wpa_s->conf->wfa_gen_capa != WFA_GEN_CAPA_PROTECTED || + wpa_s->wpa_state != WPA_COMPLETED || + !pmf_in_use(wpa_s, wpa_s->bssid)) + return; + + attr = wpas_wfa_gen_capab_attr(wpa_s); + if (!attr) + return; + + buf_len = 1 + 3 + 1 + 1 + wpabuf_len(attr); + buf = wpabuf_alloc(buf_len); + if (!buf) { + wpabuf_free(attr); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED); + wpabuf_put_be32(buf, WFA_CAPAB_VENDOR_TYPE); + wpabuf_put_u8(buf, 0); /* Capabilities Length */ + wpabuf_put_buf(buf, attr); + wpabuf_free(attr); + + wpa_printf(MSG_DEBUG, "WFA: Send WFA Capabilities frame"); + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) + wpa_printf(MSG_DEBUG, + "WFA: Failed to send WFA Capabilities frame"); + + wpabuf_free(buf); +} + + void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -557,6 +650,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->rsnxe_override_eapol); wpa_s->rsnxe_override_eapol = NULL; wpas_clear_driver_signal_override(wpa_s); + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + wpabuf_free(wpa_s->link_ies[i]); + wpa_s->link_ies[i] = NULL; + } #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { @@ -613,6 +710,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL); eloop_cancel_timeout(wpas_verify_ssid_beacon, wpa_s, NULL); + eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL); wpas_wps_deinit(wpa_s); @@ -632,6 +730,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpas_p2p_deinit(wpa_s); + wpas_pr_deinit(wpa_s); + #ifdef CONFIG_OFFCHANNEL offchannel_deinit(wpa_s); #endif /* CONFIG_OFFCHANNEL */ @@ -692,11 +792,13 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->last_scan_res); wpa_s->last_scan_res = NULL; -#ifdef CONFIG_HS20 +#ifdef CONFIG_P2P + os_free(wpa_s->p2p_pmksa_entry); + wpa_s->p2p_pmksa_entry = NULL; +#endif /* CONFIG_P2P */ + if (wpa_s->drv_priv) wpa_drv_configure_frame_filters(wpa_s, 0); - hs20_deinit(wpa_s); -#endif /* CONFIG_HS20 */ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { wpabuf_free(wpa_s->vendor_elem[i]); @@ -752,12 +854,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD - wpas_nan_usd_deinit(wpa_s); -#endif /* CONFIG_NAN_USD */ + wpas_nan_de_deinit(wpa_s); #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); + wpas_pasn_free_params(wpa_s); #endif /* CONFIG_PASN */ #ifndef CONFIG_NO_ROBUST_AV wpas_scs_deinit(wpa_s); @@ -768,6 +869,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->owe_trans_scan_freq); wpa_s->owe_trans_scan_freq = NULL; #endif /* CONFIG_OWE */ + + for (i = 0; i < MAX_NUM_MLD_LINKS; i++) { + wpabuf_free(wpa_s->links[i].ies); + wpa_s->links[i].ies = NULL; + } } @@ -783,12 +889,33 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { int i, max = 6; - /* MLME-DELETEKEYS.request */ - for (i = 0; i < max; i++) { - if (wpa_s->keys_cleared & BIT(i)) - continue; - wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, i, 0, NULL, 0, - NULL, 0, KEY_FLAG_GROUP); + /* MLME-DELETEKEYS.request + * + * For still associated MLO connections, group keys are per-link and at + * least in the case of Linux nl80211 interface, the cfg80211 validator + * rejects DEL_KEY for group keys with link_id=-1 when wdev->valid_links + * is set. Iterate over each valid link and pass the corresponding + * link_id so the keys are properly cleared. + */ + if (wpa_s->valid_links && wpa_s->wpa_state > WPA_ASSOCIATED) { + int link_id; + + for_each_link(wpa_s->valid_links, link_id) { + for (i = 0; i < max; i++) { + if (wpa_s->keys_cleared & BIT(i)) + continue; + wpa_drv_set_key(wpa_s, link_id, WPA_ALG_NONE, + NULL, i, 0, NULL, 0, + NULL, 0, KEY_FLAG_GROUP); + } + } + } else { + for (i = 0; i < max; i++) { + if (wpa_s->keys_cleared & BIT(i)) + continue; + wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, i, 0, + NULL, 0, NULL, 0, KEY_FLAG_GROUP); + } } /* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */ if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr && @@ -956,6 +1083,13 @@ static void wpas_verify_ssid_beacon(void *eloop_ctx, void *timeout_ctx) if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0) return; + /* wpa->current_bss might have changed due to memory reallocation, so + * need to update ssid/ssid_len */ + if (!wpa_s->current_bss) + return; + ssid = wpa_s->current_bss->ssid; + ssid_len = wpa_s->current_bss->ssid_len; + bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->bssid); if (!bss) return; @@ -1026,6 +1160,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state) { enum wpa_states old_state = wpa_s->wpa_state; + int new_connection = wpa_s->new_connection; #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) bool update_fils_connect_params = false; #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ @@ -1054,6 +1189,15 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (state == WPA_INTERFACE_DISABLED) { /* Assure normal scan when interface is restored */ wpa_s->normal_scans = 0; + + /* + * A NAN management interface is not expected to be disabled. If + * it disabled, it means that NAN functionality is no longer + * possible so deinit (which would also stop any ongoing NAN + * operations). + */ + if (wpa_s->nan_mgmt) + wpas_nan_deinit(wpa_s); } if (state == WPA_COMPLETED) { @@ -1149,6 +1293,20 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, } wpa_s->wpa_state = state; + if (state == WPA_COMPLETED && new_connection && + wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_PROTECTED && + pmf_in_use(wpa_s, wpa_s->bssid)) { + eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL); + eloop_register_timeout(0, 100000, wpas_wfa_capab_tx, + wpa_s, NULL); + } + +#ifndef CONFIG_NO_ROBUST_AV + if (state == WPA_COMPLETED && dl_list_len(&wpa_s->active_scs_ids) && + wpa_s->scs_reconfigure) + wpas_scs_reconfigure(wpa_s); +#endif /* CONFIG_NO_ROBUST_AV */ + #ifdef CONFIG_BGSCAN if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid) wpa_supplicant_reset_bgscan(wpa_s); @@ -1196,6 +1354,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (update_fils_connect_params) wpas_update_fils_connect_params(wpa_s); #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ + + wpas_nan_usd_state_change_notif(wpa_s); } @@ -1271,14 +1431,16 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) if (wpa_s->confname == NULL) return -1; - conf = wpa_config_read(wpa_s->confname, NULL, false); + conf = wpa_config_read(wpa_s->confname, NULL, false, + wpa_s->global->params.show_details); if (conf == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " "file '%s' - exiting", wpa_s->confname); return -1; } if (wpa_s->confanother && - !wpa_config_read(wpa_s->confanother, conf, true)) { + !wpa_config_read(wpa_s->confanother, conf, true, + wpa_s->global->params.show_details)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration file '%s' - exiting", wpa_s->confanother); @@ -1571,6 +1733,9 @@ static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s, { int akm_count = wpa_s->max_num_akms; u8 capab = 0; +#ifdef CONFIG_SAE + enum sae_pwe sae_pwe; +#endif /* CONFIG_SAE */ if (akm_count < 2) return; @@ -1649,13 +1814,16 @@ static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s, return; } - if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK && - wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) +#ifdef CONFIG_SAE + sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); + if (sae_pwe != SAE_PWE_HUNT_AND_PECK && + sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); #ifdef CONFIG_SAE_PK if (ssid->sae_pk) capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK); #endif /* CONFIG_SAE_PK */ +#endif /* CONFIG_SAE */ if (!((wpa_s->allowed_key_mgmts & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab)) @@ -1694,17 +1862,19 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, { struct wpa_ie_data ie; int sel, proto; +#ifdef CONFIG_SAE enum sae_pwe sae_pwe; - const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen; +#endif /* CONFIG_SAE */ + const u8 *bss_wpa, *bss_rsn, *bss_rsnx; bool wmm; + struct rsn_pmksa_cache_entry *pmksa; if (bss) { bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); - bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); - bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + bss_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); + bss_rsnx = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false); } else { - bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL; + bss_wpa = bss_rsn = bss_rsnx = NULL; } if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && @@ -1720,34 +1890,17 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); proto = WPA_PROTO_WPA; -#ifdef CONFIG_HS20 - } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) && - wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 && - (ie.group_cipher & ssid->group_cipher) && - (ie.pairwise_cipher & ssid->pairwise_cipher) && - (ie.key_mgmt & ssid->key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN"); - proto = WPA_PROTO_OSEN; - } else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) && - wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && - (ie.group_cipher & ssid->group_cipher) && - (ie.pairwise_cipher & ssid->pairwise_cipher) && - (ie.key_mgmt & ssid->key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)"); - proto = WPA_PROTO_RSN; -#endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); wpa_dbg(wpa_s, MSG_DEBUG, "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", ssid->proto, ssid->pairwise_cipher, ssid->group_cipher, ssid->key_mgmt); - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s", + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s", MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), bss_wpa ? " WPA" : "", - bss_rsn ? " RSN" : "", - bss_osen ? " OSEN" : ""); + bss_rsn ? " RSN" : ""); if (bss_rsn) { wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]); if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) { @@ -1774,9 +1927,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } return -1; } else { - if (ssid->proto & WPA_PROTO_OSEN) - proto = WPA_PROTO_OSEN; - else if (ssid->proto & WPA_PROTO_RSN) + if (ssid->proto & WPA_PROTO_RSN) proto = WPA_PROTO_RSN; else proto = WPA_PROTO_WPA; @@ -1806,7 +1957,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_OWE if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only && - !bss_wpa && !bss_rsn && !bss_osen) { + !bss_wpa && !bss_rsn) { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); wpa_s->wpa_proto = 0; *wpa_ie_len = 0; @@ -1830,15 +1981,34 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->wpa_proto = proto; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, - !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN))); + !!(ssid->proto & WPA_PROTO_RSN)); if (bss || !wpa_s->ap_ies_from_associnfo) { + const u8 *rsnoe = NULL, *rsno2e = NULL, *rsnxoe = NULL; + + if (bss) { + bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + rsnoe = wpa_bss_get_vendor_ie( + bss, RSNE_OVERRIDE_IE_VENDOR_TYPE); + rsno2e = wpa_bss_get_vendor_ie( + bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + rsnxoe = wpa_bss_get_vendor_ie( + bss, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + } + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, bss_rsn ? 2 + bss_rsn[1] : 0) || wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx, - bss_rsnx ? 2 + bss_rsnx[1] : 0)) + bss_rsnx ? 2 + bss_rsnx[1] : 0) || + wpa_sm_set_ap_rsne_override(wpa_s->wpa, rsnoe, + rsnoe ? 2 + rsnoe[1] : 0) || + wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, rsno2e, + rsno2e ? 2 + rsno2e[1] : 0) || + wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, rsnxoe, + rsnxoe ? 2 + rsnxoe[1] : 0)) return -1; } @@ -1999,22 +2169,42 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } else if (sel & WPA_KEY_MGMT_WPA_NONE) { wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); -#ifdef CONFIG_HS20 - } else if (sel & WPA_KEY_MGMT_OSEN) { - wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN; - wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN"); -#endif /* CONFIG_HS20 */ #ifdef CONFIG_OWE } else if (sel & WPA_KEY_MGMT_OWE) { wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); #endif /* CONFIG_OWE */ +#ifdef CONFIG_ENC_ASSOC + } else if (sel & WPA_KEY_MGMT_EPPKE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_EPPKE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT EPPKE"); +#endif /* CONFIG_ENC_ASSOC */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); return -1; } + /* There might be a PMKSA cache entry for the target AP, but the current + * connection is trying to use PSK (which does not use PMKSA caching) or + * an AKM that does not match the one that was used to generate the + * selected PMKSA entry. The previously selected PMKSA cache entry needs + * to be cleared in such cases to avoid indicating an incorrect PMKID + * and exchange that would likely end up failing with the AP attempting + * to use a different PMK. This is not really supposed to happen in + * normal use cases, but it is possible that some corner cases of the AP + * changing its configuration might trigger a failure due to mismatching + * PMK. */ + pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (pmksa && + (wpa_key_mgmt_wpa_psk_no_sae(wpa_s->key_mgmt) || + (pmksa->akmp && pmksa->akmp != wpa_s->key_mgmt))) { + wpa_printf(MSG_DEBUG, + "RSN: Disable PMKSA caching due to incompatible AKMP (PMKSA: 0x%x, selected: 0x%x)", + pmksa->akmp, wpa_s->key_mgmt); + pmksa_cache_clear_current(wpa_s->wpa); + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, wpa_s->pairwise_cipher); @@ -2034,7 +2224,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); #endif /* CONFIG_OCV */ - sae_pwe = wpa_s->conf->sae_pwe; +#ifdef CONFIG_SAE + sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); if ((ssid->sae_password_id || wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) @@ -2055,6 +2246,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, (!ssid->sae_password && ssid->passphrase && sae_pk_valid_password(ssid->passphrase)))); #endif /* CONFIG_SAE_PK */ +#endif /* CONFIG_SAE */ if (bss && is_6ghz_freq(bss->freq) && wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz"); @@ -2074,6 +2266,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->oci_freq_override_fils_assoc); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX, wpa_s->disable_eapol_g2_tx); + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK, + wpa_s->eapol_2_key_info_set_mask); #endif /* CONFIG_TESTING_OPTIONS */ /* Extended Key ID is only supported in infrastructure BSS so far */ @@ -2132,6 +2327,44 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, false); } +#ifdef CONFIG_ENC_ASSOC + if (proto == WPA_PROTO_RSN && + (wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) { + bool assoc_enc; + + /* Enable association frame encryption based on the AP + * advertising support for it to avoid potential + * interoperability issues with incorrect AP behavior if we + * were to send an "unexpected" RSNXE with multiple octets of + * payload. */ + assoc_enc = ieee802_11_rsnx_capab( + bss_rsnx, WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION); + if (!skip_default_rsne) + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ASSOC_ENC, + assoc_enc); + } else { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ASSOC_ENC, false); + } +#endif /* CONFIG_ENC_ASSOC */ + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SPP_AMSDU, + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) && + ieee802_11_rsnx_capab(bss_rsnx, + WLAN_RSNX_CAPAB_SPP_A_MSDU) && + wpa_s->pairwise_cipher & (WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256 | + WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP) && + (wpa_s->wpa_proto & WPA_PROTO_RSN)); + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PW_ID_CHANGE, + ssid->sae_password_id && ssid->sae_password_id_change); +#ifdef CONFIG_PMKSA_PRIVACY + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PMKSA_CACHING_PRIVACY, + ssid->pmksa_privacy); +#endif /* CONFIG_PMKSA_PRIVACY */ + if (!skip_default_rsne) { if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { @@ -2140,6 +2373,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } +#ifndef CONFIG_NO_WPA wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe); if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe, &wpa_s->rsnxe_len)) { @@ -2147,6 +2381,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, "RSN: Failed to generate RSNXE"); return -1; } +#endif /* CONFIG_NO_WPA */ } if (0) { @@ -2177,9 +2412,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, (ssid->sae_password || ssid->passphrase || ssid->ext_psk)) psk_set = 1; - if (!psk_set) { + if (!psk_set && !ssid->pmk_valid) { wpa_msg(wpa_s, MSG_INFO, - "No PSK available for association"); + "No PSK/PMK available for association"); wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL); return -1; } @@ -2295,6 +2530,8 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx, if (!wpa_s->disable_fils) *pos |= 0x01; #endif /* CONFIG_FILS */ + if (wpa_s->conf->twt_requester) + *pos |= 0x20; /* Bit 77 - TWT Requester Support */ break; case 10: /* Bits 80-87 */ #ifndef CONFIG_NO_ROBUST_AV @@ -2429,7 +2666,8 @@ int wpas_update_random_addr(struct wpa_supplicant *wpa_s, if (style == WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS) { /* Pregenerated addresses do not expire but their value * might have changed, so let's check that. */ - if (ether_addr_equal(wpa_s->own_addr, ssid->mac_value)) + if (ssid && + ether_addr_equal(wpa_s->own_addr, ssid->mac_value)) return 0; } else if ((wpa_s->last_mac_addr_change.sec != 0 || wpa_s->last_mac_addr_change.usec != 0) && @@ -2481,6 +2719,9 @@ int wpas_update_random_addr(struct wpa_supplicant *wpa_s, return -1; } + wpas_p2p_update_dev_addr(wpa_s); + wpas_pr_update_dev_addr(wpa_s); + wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, MAC2STR(addr)); @@ -2499,13 +2740,19 @@ int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s) } -void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid, +void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, bool force) { #ifdef CONFIG_SAE + struct wpa_config *conf = wpa_s->conf; int *groups = conf->sae_groups; int default_groups[] = { 19, 20, 21, 0 }; const char *password; + enum sae_pwe sae_pwe; + const u8 *password_id = (const u8 *) ssid->sae_password_id; + size_t password_id_len = ssid->sae_password_id ? + os_strlen(ssid->sae_password_id) : 0; + struct wpabuf_array *ids; if (!groups || groups[0] <= 0) groups = default_groups; @@ -2514,24 +2761,51 @@ void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid, if (!password) password = ssid->passphrase; + sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); + if (!password || !wpa_key_mgmt_sae(ssid->key_mgmt) || - (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id && + (sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id && !wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) && !force && !sae_pk_valid_password(password)) || - conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) { + sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) { /* PT derivation not needed */ sae_deinit_pt(ssid->pt); ssid->pt = NULL; return; } - if (ssid->pt) - return; /* PT already derived */ + ids = ssid->alt_sae_password_ids; + if (ids && ids->num) { + unsigned int idx = os_random() % ids->num; + struct wpabuf *id = ids->buf[idx]; + + password_id = wpabuf_head(id); + password_id_len = wpabuf_len(id); + wpa_hexdump(MSG_DEBUG, + "SAE: Prepare PT for alternative password ID", + password_id, password_id_len); + ssid->alt_sae_passwords_ids_idx = idx; + ssid->alt_sae_passwords_ids_used = true; + } + + if (ssid->pt) { + if (!password_id && !ssid->pt->password_id) + return; /* PT already derived for no PW ID */ + if (password_id && ssid->pt->password_id && + password_id_len == wpabuf_len(ssid->pt->password_id) && + os_memcmp(password_id, wpabuf_head(ssid->pt->password_id), + password_id_len) == 0) + return; /* PT already derived for same PW ID */ + + /* PT was derived for another password identifier */ + sae_deinit_pt(ssid->pt); + ssid->pt = NULL; + } ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, (const u8 *) password, os_strlen(password), - ssid->sae_password_id); + password_id, password_id_len); #endif /* CONFIG_SAE */ } @@ -2571,6 +2845,9 @@ int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s) "Could not update MAC address information"); return -1; } + + wpas_p2p_update_dev_addr(wpa_s); + wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); return 0; } @@ -2641,7 +2918,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_s_clear_sae_rejected(wpa_s); #ifdef CONFIG_SAE - wpa_s_setup_sae_pt(wpa_s->conf, ssid, false); + wpa_s_setup_sae_pt(wpa_s, ssid, false); #endif /* CONFIG_SAE */ if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) { @@ -2770,8 +3047,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, cwork->bss = bss; cwork->ssid = ssid; - if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, - wpas_start_assoc_cb, cwork) < 0) { + if (!radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, + wpas_start_assoc_cb, cwork)) { os_free(cwork); } } @@ -2945,18 +3222,25 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_hw_modes *mode, struct hostapd_freq_params *freq, - int obss_scan) { + int obss_scan, bool is_6ghz) +{ int chan_idx; struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; int i, res; unsigned int j; - static const int ht40plus[] = { + static const int ht40plus_5ghz[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173, 184, 192 }; + static const int ht40plus_6ghz[] = { + 1, 9, 17, 25, 33, 41, 49, 57, 65, 73, + 81, 89, 97, 105, 113, 121, 129, 137, 145, 153, + 161, 169, 177, 185, 193, 201, 209, 217, 225 + }; + int ht40 = -1; - if (!freq->ht_enabled) + if (!freq->ht_enabled && !is_6ghz) return; for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) { @@ -2978,10 +3262,19 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s, #endif /* Check/setup HT40+/HT40- */ - for (j = 0; j < ARRAY_SIZE(ht40plus); j++) { - if (ht40plus[j] == freq->channel) { - ht40 = 1; - break; + if (is_6ghz) { + for (j = 0; j < ARRAY_SIZE(ht40plus_6ghz); j++) { + if (ht40plus_6ghz[j] == freq->channel) { + ht40 = 1; + break; + } + } + } else { + for (j = 0; j < ARRAY_SIZE(ht40plus_5ghz); j++) { + if (ht40plus_5ghz[j] == freq->channel) { + ht40 = 1; + break; + } } } @@ -2999,12 +3292,14 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s, if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; - if (ht40 == -1) { - if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) - return; - } else { - if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) - return; + if (freq->ht_enabled) { + if (ht40 == -1) { + if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) + return; + } else { + if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) + return; + } } freq->sec_channel_offset = ht40; @@ -3048,6 +3343,27 @@ static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s, } +static int ibss_get_center_320mhz(int channel) +{ + int seg0; + + if (channel >= 1 && channel <= 45) + seg0 = 31; + else if (channel >= 49 && channel <= 77) + seg0 = 63; + else if (channel >= 81 && channel <= 109) + seg0 = 95; + else if (channel >= 113 && channel <= 141) + seg0 = 127; + else if (channel >= 145 && channel <= 173) + seg0 = 159; + else + seg0 = 191; + + return seg0; +} + + static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_hw_modes *mode, @@ -3059,12 +3375,20 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s, 6515, 6595, 6675, 6755, 6835, 6915, 6995 }; static const int bw160[] = { - 5955, 6115, 6275, 6435, 6595, 6755, 6915 + 5180, 5500, 5745, 5955, 6115, 6275, 6435, + 6595, 6755, 6915 }; + static const int bw320[]= { + 5955, 6255, 6115, 6415, 6275, 6575, 6435, + 6735, 6595, 6895, 6755, 7055 + }; + struct hostapd_freq_params vht_freq; int i; unsigned int j, k; int chwidth, seg0, seg1; + int offset_in_160 = 1; + int offset_in_320 = 0; u32 vht_caps = 0; u8 channel = freq->channel; @@ -3104,18 +3428,74 @@ static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s, seg0 = channel + 6; seg1 = 0; + for (k = 0; k < ARRAY_SIZE(bw160); k++) { + if (bw80[j] >= bw160[k] && + bw80[j] < bw160[k] + 160) { + if (bw80[j] == bw160[k]) + offset_in_160 = 1; + else + offset_in_160 = -1; + break; + } + } + + for (k = 0; k < ARRAY_SIZE(bw320); k++) { + if (bw80[j] >= bw320[k] && + bw80[j] < bw320[k] + 320) { + if (bw80[j] == bw320[k]) + offset_in_320 = 0; + else if (bw80[j] == bw320[k] + 80) + offset_in_320 = 1; + else if (bw80[j] == bw320[k] + 160) + offset_in_320 = 2; + else + offset_in_320 = 3; + break; + } + } + /* In 160 MHz, the initial four 20 MHz channels were validated * above. If 160 MHz is supported, check the remaining four 20 MHz - * channels for the total of 160 MHz bandwidth for 6 GHz. + * channels for the total of 160 MHz bandwidth. */ if ((mode->he_capab[ieee80211_mode].phy_cap[ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & - HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz && - ibss_mesh_is_80mhz_avail(channel + 16, mode)) { + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && + (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_160MHZ || + ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) && + ibss_mesh_is_80mhz_avail(channel + 16 * offset_in_160, mode)) { for (j = 0; j < ARRAY_SIZE(bw160); j++) { - if (freq->freq == bw160[j]) { + u8 start_chan; + + if (freq->freq >= bw160[j] && + freq->freq < bw160[j] + 160) { chwidth = CONF_OPER_CHWIDTH_160MHZ; - seg0 = channel + 14; + ieee80211_freq_to_chan(bw160[j], &start_chan); + seg0 = start_chan + 14; + break; + } + } + } + + /* In 320 MHz, the initial four 20 MHz channels were validated + * above. If 320 MHz is supported, check the remaining 12 20 MHz + * channels for the total of 320 MHz bandwidth for 6 GHz. + */ + if ((mode->eht_capab[ieee80211_mode].phy_cap[ + EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] & + EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK) && is_6ghz && + ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_320MHZ && + ibss_mesh_is_80mhz_avail(channel + 16 - + 64 * ((offset_in_320 + 1) / 4), mode) && + ibss_mesh_is_80mhz_avail(channel + 32 - + 64 * ((offset_in_320 + 2) / 4), mode) && + ibss_mesh_is_80mhz_avail(channel + 48 - + 64 * ((offset_in_320 + 3) / 4), mode)) { + for (j = 0; j < ARRAY_SIZE(bw320); j += 2) { + if (freq->freq >= bw320[j] && + freq->freq <= bw320[j + 1]) { + chwidth = CONF_OPER_CHWIDTH_320MHZ; + seg0 = ibss_get_center_320mhz(freq->channel); break; } } @@ -3237,9 +3617,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode, ieee80211_mode); freq->channel = channel; - /* Setup higher BW only for 5 GHz */ + /* Setup higher BW only for 5 and 6 GHz */ if (mode->mode == HOSTAPD_MODE_IEEE80211A) { - ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan); + ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan, + is_6ghz); if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq, ieee80211_mode, is_6ghz)) freq->he_enabled = freq->vht_enabled = false; @@ -3365,13 +3746,12 @@ bool wpa_is_non_eht_scs_traffic_desc_supported(struct wpa_bss *bss) } -static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, - u8 *wpa_ie, size_t wpa_ie_len, - size_t max_wpa_ie_len) +int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + u8 *wpa_ie, size_t wpa_ie_len, size_t max_wpa_ie_len) { - struct wpabuf *wfa_ie = NULL; + struct wpabuf *wfa_ie = NULL, *attr = NULL; u8 wfa_capa[1]; + u8 capab_len = 0; size_t wfa_ie_len, buf_len; os_memset(wfa_capa, 0, sizeof(wfa_capa)); @@ -3383,7 +3763,13 @@ static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, if (wpa_is_non_eht_scs_traffic_desc_supported(bss)) wfa_capa[0] |= WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC; - if (!wfa_capa[0]) + if (wfa_capa[0]) + capab_len = 1; + + if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_UNPROTECTED) + attr = wpas_wfa_gen_capab_attr(wpa_s); + + if (capab_len == 0 && !attr) return wpa_ie_len; /* Wi-Fi Alliance element */ @@ -3392,17 +3778,23 @@ static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, 3 + /* OUI */ 1 + /* OUI Type */ 1 + /* Capabilities Length */ - sizeof(wfa_capa); /* Capabilities */ + capab_len + /* Capabilities */ + (attr ? wpabuf_len(attr) : 0) /* Attributes */; wfa_ie = wpabuf_alloc(buf_len); - if (!wfa_ie) + if (!wfa_ie) { + wpabuf_free(attr); return wpa_ie_len; + } wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(wfa_ie, buf_len - 2); wpabuf_put_be24(wfa_ie, OUI_WFA); wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE); - wpabuf_put_u8(wfa_ie, sizeof(wfa_capa)); - wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa)); + wpabuf_put_u8(wfa_ie, capab_len); + wpabuf_put_data(wfa_ie, wfa_capa, capab_len); + if (attr) + wpabuf_put_buf(wfa_ie, attr); + wpabuf_free(attr); wfa_ie_len = wpabuf_len(wfa_ie); if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) { @@ -3418,6 +3810,138 @@ static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_IEEE8021X_AUTH +static bool wpas_set_802_1x_auth_alg(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + const u8 *rsnxe; + + if (!ssid->eap_over_auth_frame || + !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_802_1X_AUTH) || + !wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt & + ~WPA_KEY_MGMT_IEEE8021X)) + return false; + + params->ieee8021x_auth_supported = true; + + if (!bss) + return false; + + if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt & + ~WPA_KEY_MGMT_IEEE8021X)) + return false; + + rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX); + if (ieee802_11_rsnx_capab(rsnxe, + WLAN_RSNX_CAPAB_802_1X_IN_AUTH_FRAMES)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Using IEEE 802.1X authentication using Authentication frames"); + return true; + } + + return false; +} +#endif /* CONFIG_IEEE8021X_AUTH */ + + +#ifdef CONFIG_ENC_ASSOC + +bool wpas_eppke_ap_capable(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, bool unauth_eppke) +{ + const u8 *ap_rsnxe; + + if (!(wpa_s->drv_flags2 & + WPA_DRIVER_FLAGS2_ASSOCIATION_FRAME_ENCRYPTION)) { + wpa_printf(MSG_DEBUG, + "EPPKE: Driver does not support association frame encryption"); + return false; + } + + ap_rsnxe = wpa_bss_get_rsnxe(wpa_s, bss, NULL, false); + + if (!ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_KEK_IN_PASN)) { + wpa_printf(MSG_DEBUG, "EPPKE: AP does not support KEK_IN_PASN"); + return false; + } + + if (!ieee802_11_rsnx_capab(ap_rsnxe, + WLAN_RSNX_CAPAB_ASSOC_FRAME_ENCRYPTION)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support association frame encryption"); + return false; + } + + if (unauth_eppke && + !ieee802_11_rsnx_capab(ap_rsnxe, WLAN_RSNX_CAPAB_UNAUTH_EPPKE)) { + wpa_printf(MSG_DEBUG, + "EPPKE: AP does not support unauthenticated EPPKE"); + return false; + } + + return true; +} + + +static bool wpas_set_eppke_auth_alg(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + const u8 *rsn; + struct wpa_ie_data ied; + + if (!wpa_key_mgmt_eppke(ssid->key_mgmt) || + !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_EPPKE)) + return false; + + params->eppke_supported = true; + + if (!bss) + return false; + + rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); + if (!rsn) { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Target BSS does not advertise RSN"); + return false; + } + + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied)) { + wpa_printf(MSG_DEBUG, "EPPKE: Failed parsing RSNE data"); + return false; + } + + if (!(ied.key_mgmt & WPA_KEY_MGMT_EPPKE)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Target BSS does not advertise EPPKE AKM"); + return false; + } + + if (!wpa_key_mgmt_eppke(wpa_s->key_mgmt) && + !wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Negotiated AKM is not an EPPKE Authentication AKM"); + return false; + } + + if (!wpas_eppke_ap_capable(wpa_s, bss, + !!wpa_key_mgmt_eppke(wpa_s->key_mgmt))) { + wpa_dbg(wpa_s, MSG_DEBUG, + "EPPKE: Target BSS does not indicate support for EPPKE"); + return false; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Using EPPKE Authentication"); + + return true; +} + +#endif /* CONFIG_ENC_ASSOC */ + + static u8 * wpas_populate_assoc_ies( struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, @@ -3456,7 +3980,7 @@ static u8 * wpas_populate_assoc_ies( } if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || - wpa_bss_get_ie(bss, WLAN_EID_RSN)) && + wpa_bss_get_rsne(wpa_s, bss, ssid, false)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; const u8 *cache_id = NULL; @@ -3495,19 +4019,6 @@ static u8 * wpas_populate_assoc_ies( os_free(wpa_ie); return NULL; } -#ifdef CONFIG_HS20 - } else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && - (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { - /* No PMKSA caching, but otherwise similar to RSN/WPA */ - wpa_ie_len = max_wpa_ie_len; - if (wpa_supplicant_set_suites(wpa_s, bss, ssid, - wpa_ie, &wpa_ie_len, false)) { - wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " - "key management and encryption suites"); - os_free(wpa_ie); - return NULL; - } -#endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* @@ -3596,6 +4107,16 @@ static u8 * wpas_populate_assoc_ies( algs = WPA_AUTH_ALG_SAE; #endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE8021X_AUTH + if (wpas_set_802_1x_auth_alg(wpa_s, bss, ssid, params)) + algs = WPA_AUTH_ALG_802_1X; +#endif /* CONFIG_IEEE8021X_AUTH */ + +#ifdef CONFIG_ENC_ASSOC + if (wpas_set_eppke_auth_alg(wpa_s, bss, ssid, params)) + algs = WPA_AUTH_ALG_EPPKE; +#endif /* CONFIG_ENC_ASSOC */ + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); if (ssid->auth_alg) { algs = ssid->auth_alg; @@ -3711,12 +4232,12 @@ static u8 * wpas_populate_assoc_ies( wpa_ie_len += wpabuf_len(hs20); } wpabuf_free(hs20); - - hs20_configure_frame_filters(wpa_s); } } #endif /* CONFIG_HS20 */ + wpas_configure_frame_filters(wpa_s); + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -3943,6 +4464,48 @@ mscs_end: wpa_ie_len += multi_ap_ie_len; } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT, + wpas_rsn_overriding(wpa_s, ssid)); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_NOT_USED); + if (wpas_rsn_overriding(wpa_s, ssid) && + wpas_ap_supports_rsn_overriding(wpa_s, bss) && + wpa_ie_len + 2 + 4 + 1 <= max_wpa_ie_len) { + u8 *pos = wpa_ie + wpa_ie_len, *start = pos; + const u8 *ie; + enum rsn_selection_variant variant = RSN_SELECTION_RSNE; + + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE); + ie = wpa_bss_get_rsne(wpa_s, bss, ssid, wpa_s->valid_links); + if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4) { + u32 type; + + type = WPA_GET_BE32(&ie[2]); + if (type == RSNE_OVERRIDE_IE_VENDOR_TYPE) { + variant = RSN_SELECTION_RSNE_OVERRIDE; + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE_OVERRIDE); + } else if (type == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) { + variant = RSN_SELECTION_RSNE_OVERRIDE_2; + wpa_sm_set_param(wpa_s->wpa, + WPA_PARAM_RSN_OVERRIDE, + RSN_OVERRIDE_RSNE_OVERRIDE_2); + } + } + + /* Indicate which RSNE variant was used */ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 1; + WPA_PUT_BE32(pos, RSN_SELECTION_IE_VENDOR_TYPE); + pos += 4; + *pos++ = variant; + wpa_hexdump(MSG_MSGDUMP, "RSN Selection", start, pos - start); + wpa_ie_len += pos - start; + } + + params->rsn_overriding = wpas_rsn_overriding(wpa_s, ssid); params->wpa_ie = wpa_ie; params->wpa_ie_len = wpa_ie_len; params->auth_alg = algs; @@ -4167,6 +4730,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #ifdef CONFIG_WNM wpa_s->bss_trans_mgmt_in_progress = false; #endif /* CONFIG_WNM */ + wpa_s->no_suitable_network = 0; if (deinit) { if (work->started) { @@ -4206,7 +4770,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) /* Starting new association, so clear the possibly used WPA IE from the * previous association. */ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifndef CONFIG_NO_WPA wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); +#endif /* CONFIG_NO_WPA */ wpa_s->rsnxe_len = 0; #ifndef CONFIG_NO_ROBUST_AV wpa_s->mscs_setup_done = false; @@ -4339,6 +4905,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; } + params.bssid_filter = wpa_s->bssid_filter; + params.bssid_filter_count = wpa_s->bssid_filter_count; + if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && wpa_s->conf->ap_scan == 2) { params.bssid = ssid->bssid; @@ -4497,7 +5066,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid); if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { - const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + const u8 *rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false); struct wpa_ie_data ie; if (!wpas_driver_bss_selection(wpa_s) && rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && @@ -4569,7 +5138,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.prev_bssid = prev_bssid; #ifdef CONFIG_SAE - params.sae_pwe = wpa_s->conf->sae_pwe; + params.sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid); #endif /* CONFIG_SAE */ ret = wpa_drv_associate(wpa_s, ¶ms); @@ -4627,6 +5196,15 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } +#ifdef CONFIG_P2P + if (ssid->pmk_valid && wpa_s->p2p_pmksa_entry && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, + wpa_s->p2p_pmksa_entry); + wpa_s->p2p_pmksa_entry = NULL; + } +#endif /* CONFIG_P2P */ + #ifdef CONFIG_WEP if (wep_keys_set && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) { @@ -4649,9 +5227,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_P2P */ ssid->bssid_set) { wpa_s->current_bss = bss; -#ifdef CONFIG_HS20 - hs20_configure_frame_filters(wpa_s); -#endif /* CONFIG_HS20 */ + wpas_configure_frame_filters(wpa_s); } wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); @@ -4841,6 +5417,7 @@ int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id) if (!ssid) return -1; wpas_notify_network_removed(wpa_s, ssid); + radio_remove_pending_connect(wpa_s, ssid); if (ssid == prev || !prev) { #ifdef CONFIG_SME @@ -5023,6 +5600,51 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, } +static bool ssid_in_last_scan(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + size_t i; + + /* Check if the previous scan included the selected network */ + if (wpa_s->last_scan_num_ssids <= 1 || + !ssid->ssid || ssid->ssid_len == 0) + return false; + + /* Iterate through the previous scan SSIDs */ + for (i = 0; i < wpa_s->last_scan_num_ssids; i++) { + if (os_memcmp(wpa_s->last_scan_ssids[i].ssid, ssid->ssid, + ssid->ssid_len) == 0) + return true; + } + + return false; +} + + +/** + * Checks whether an SSID was discovered in the last scan. + * @wpa_s: wpa_supplicant structure for a network interface. + * @ssid: wpa_ssid structure for a configured network. + * Returns: true if ssid found, false otherwise. + */ +static bool ssid_in_last_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + size_t i; + + if (!wpa_s->last_scan_res || !ssid->ssid || ssid->ssid_len == 0) + return false; + + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (os_memcmp(wpa_s->last_scan_res[i]->ssid, + ssid->ssid, ssid->ssid_len) == 0) + return true; + } + + return false; +} + + /** * wpa_supplicant_select_network - Attempt association with a network * @wpa_s: wpa_supplicant structure for a network interface @@ -5034,6 +5656,7 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *other_ssid; int disconnected = 0; + bool request_new_scan = false; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { if (wpa_s->wpa_state >= WPA_AUTHENTICATING) @@ -5079,6 +5702,27 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, (ssid->mode == WPAS_MODE_MESH || ssid->mode == WPAS_MODE_AP) ? ssid : NULL; + if (ssid->scan_ssid) { + if (ssid_in_last_scan(wpa_s, ssid)) { + wpa_printf(MSG_DEBUG, + "Hidden network was scanned for in last scan"); + } else if (ssid_in_last_scan_res(wpa_s, ssid)) { + wpa_printf(MSG_DEBUG, + "Hidden network was found in last scan results"); + } else { + request_new_scan = true; + wpa_printf(MSG_DEBUG, + "Request a new scan for hidden network"); + } + } + + if (!request_new_scan && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) && + !ssid->owe_only) { + wpa_printf(MSG_DEBUG, + "Request a new scan for OWE transition SSID"); + request_new_scan = true; + } + /* * Don't optimize next scan freqs since a new ESS has been * selected. @@ -5095,10 +5739,10 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->last_owe_group = 0; if (ssid) { ssid->owe_transition_bss_select_count = 0; - wpa_s_setup_sae_pt(wpa_s->conf, ssid, false); + wpa_s_setup_sae_pt(wpa_s, ssid, false); } - if (wpa_s->connect_without_scan || + if (wpa_s->connect_without_scan || request_new_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_s->scan_req = NORMAL_SCAN_REQ; wpas_scan_reset_sched_scan(wpa_s); @@ -5398,8 +6042,8 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *entry_ssid, size_t entry_ssid_len) { - const u8 *owe, *pos, *end; - u8 ssid_len; + const u8 *owe, *owe_bssid, *owe_ssid; + size_t owe_ssid_len; struct wpa_bss *bss; /* Check network profile SSID aganst the SSID in the @@ -5413,18 +6057,12 @@ static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, if (!owe) return 0; - pos = owe + 6; - end = owe + 2 + owe[1]; - - if (end - pos < ETH_ALEN + 1) - return 0; - pos += ETH_ALEN; - ssid_len = *pos++; - if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid, + &owe_ssid_len)) return 0; - return entry_ssid_len == ssid_len && - os_memcmp(pos, entry_ssid, ssid_len) == 0; + return entry_ssid_len == owe_ssid_len && + os_memcmp(owe_ssid, entry_ssid, owe_ssid_len) == 0; } #endif /* CONFIG_OWE */ @@ -5467,14 +6105,14 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) (!entry->ssid || os_memcmp(ssid, entry->ssid, ssid_len) == 0)) || wired) && - (!entry->bssid_set || + (wpa_s->valid_links || !entry->bssid_set || ether_addr_equal(bssid, entry->bssid))) return entry; #ifdef CONFIG_WPS if (!wpas_network_disabled(wpa_s, entry) && (entry->key_mgmt & WPA_KEY_MGMT_WPS) && (entry->ssid == NULL || entry->ssid_len == 0) && - (!entry->bssid_set || + (wpa_s->valid_links || !entry->bssid_set || ether_addr_equal(bssid, entry->bssid))) return entry; #endif /* CONFIG_WPS */ @@ -5484,7 +6122,7 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) (entry->ssid && owe_trans_ssid_match(wpa_s, bssid, entry->ssid, entry->ssid_len)) && - (!entry->bssid_set || + (wpa_s->valid_links || !entry->bssid_set || ether_addr_equal(bssid, entry->bssid))) return entry; #endif /* CONFIG_OWE */ @@ -5615,6 +6253,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->wpa_state < WPA_ASSOCIATED || + wpa_s->ext_auth_to_same_bss || (wpa_s->last_eapol_matches_bssid && #ifdef CONFIG_AP !wpa_s->ap_iface && @@ -5637,6 +6276,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, MACSTR ")", wpa_supplicant_state_txt(wpa_s->wpa_state), MAC2STR(connected_addr)); + delay_processing: wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { @@ -5734,9 +6374,23 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, encrypted) > 0) return; wpa_drv_poll(wpa_s); - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) - wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len, encrypted); - else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) { + if (wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len, + encrypted) == -2 && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ + wpa_s->last_eapol_matches_bssid) { + /* Handle the case where reassociation occurs to the + * current connected AP */ + wpa_dbg(wpa_s, MSG_DEBUG, + "Delay processing of received EAPOL frame for reassociation to the current connected AP (state=%s connected_addr=" + MACSTR ")", + wpa_supplicant_state_txt(wpa_s->wpa_state), + MAC2STR(connected_addr)); + goto delay_processing; + } + } else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* * Set portValid = true here since we are going to skip 4-way * handshake processing which would normally set portValid. We @@ -5771,7 +6425,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) if ((!wpa_s->p2p_mgmt || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) && + !wpa_s->nan_mgmt) { l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), @@ -5801,6 +6456,11 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); wpas_wps_update_mac_addr(wpa_s); +#ifdef CONFIG_NAN_USD + if (wpa_s->nan_de) + nan_de_update_nmi(wpa_s->nan_de, wpa_s->own_addr); +#endif /* CONFIG_NAN_USD */ + #ifdef CONFIG_FST if (wpa_s->fst) fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr); @@ -5991,6 +6651,10 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->new_connection = 1; wpa_s->parent = parent ? parent : wpa_s; wpa_s->p2pdev = wpa_s->parent; +#ifdef CONFIG_P2P + if (parent) + wpa_s->p2p_mode = parent->p2p_mode; +#endif /* CONFIG_P2P */ wpa_s->sched_scanning = 0; wpa_s->setband_mask = WPA_SETBAND_AUTO; @@ -6005,6 +6669,12 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) #endif /* CONFIG_NO_ROBUST_AV */ wpa_s->ml_probe_mld_id = -1; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + dl_list_init(&wpa_s->mesh_external_pmksa_cache); +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + return wpa_s; } @@ -6220,7 +6890,7 @@ static int wpa_set_tx_stbc(struct wpa_supplicant *wpa_s, htcaps_mask->ht_capabilities_info |= msk; htcaps->ht_capabilities_info &= ~msk; - htcaps->ht_capabilities_info |= (tx_stbc << 7) & msk; + htcaps->ht_capabilities_info |= host_to_le16(tx_stbc << 7) & msk; return 0; } @@ -6246,7 +6916,7 @@ static int wpa_set_rx_stbc(struct wpa_supplicant *wpa_s, htcaps_mask->ht_capabilities_info |= msk; htcaps->ht_capabilities_info &= ~msk; - htcaps->ht_capabilities_info |= (rx_stbc << 8) & msk; + htcaps->ht_capabilities_info |= host_to_le16(rx_stbc << 8) & msk; return 0; } @@ -6313,13 +6983,15 @@ void wpa_supplicant_apply_vht_overrides( #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_sgi) { - vhtcaps_mask->vht_capabilities_info |= (VHT_CAP_SHORT_GI_80 | - VHT_CAP_SHORT_GI_160); - vhtcaps->vht_capabilities_info &= ~(VHT_CAP_SHORT_GI_80 | - VHT_CAP_SHORT_GI_160); + vhtcaps_mask->vht_capabilities_info |= + host_to_le32(VHT_CAP_SHORT_GI_80 | + VHT_CAP_SHORT_GI_160); + vhtcaps->vht_capabilities_info &= + host_to_le32(~(VHT_CAP_SHORT_GI_80 | + VHT_CAP_SHORT_GI_160)); wpa_msg(wpa_s, MSG_DEBUG, "disable-sgi override specified, vht-caps: 0x%x", - vhtcaps->vht_capabilities_info); + le_to_host32(vhtcaps->vht_capabilities_info)); } /* if max ampdu is <= 3, we have to make the HT cap the same */ @@ -6716,7 +7388,6 @@ static void radio_work_free(struct wpa_radio_work *work) work->wpa_s->radio->num_active_works); } - dl_list_del(&work->list); os_free(work); } @@ -6909,6 +7580,7 @@ void radio_remove_works(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s", work->type, work, work->started ? " (started)" : ""); + dl_list_del(&work->list); work->cb(work, 1); radio_work_free(work); } @@ -6918,18 +7590,28 @@ void radio_remove_works(struct wpa_supplicant *wpa_s, } -void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx) +static void radio_remove_pending_connect(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) { - struct wpa_radio_work *work; + struct wpa_radio_work *work, *tmp; struct wpa_radio *radio = wpa_s->radio; + struct wpa_connect_work *cwork; - dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { - if (work->ctx != ctx) + dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work, + list) { + if (!radio_work_is_connect(work)) continue; - wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s", - work->type, work, work->started ? " (started)" : ""); + + cwork = work->ctx; + if (cwork->ssid != ssid) + continue; + + wpa_printf(MSG_DEBUG, "Remove radio work '%s'@%p ssid=%s", + work->type, work, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + dl_list_del(&work->list); + work->cb(work, 1); radio_work_free(work); - break; } } @@ -6983,7 +7665,7 @@ void radio_work_check_next(struct wpa_supplicant *wpa_s) * @next: Force as the next work to be executed * @cb: Callback function for indicating when radio is available * @ctx: Context pointer for the work (work->ctx in cb()) - * Returns: 0 on success, -1 on failure + * Returns: Pointer to the newly created work, or %NULL on failure * * This function is used to request time for an operation that requires * exclusive radio control. Once the radio is available, the registered callback @@ -7000,10 +7682,11 @@ void radio_work_check_next(struct wpa_supplicant *wpa_s) * Setting this to 0 indicates that the work item may use multiple channels or * requires exclusive control of the radio. */ -int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, - const char *type, int next, - void (*cb)(struct wpa_radio_work *work, int deinit), - void *ctx) +struct wpa_radio_work * +radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx) { struct wpa_radio *radio = wpa_s->radio; struct wpa_radio_work *work; @@ -7011,7 +7694,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, work = os_zalloc(sizeof(*work)); if (work == NULL) - return -1; + return NULL; wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work); os_get_reltime(&work->time); work->freq = freq; @@ -7046,7 +7729,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, radio_work_check_next(wpa_s); } - return 0; + return work; } @@ -7063,11 +7746,16 @@ void radio_work_done(struct wpa_radio_work *work) struct os_reltime now, diff; unsigned int started = work->started; + /* If next is poisoned, then we are free'ing it already */ + if (work->list.next == NULL) + return; + os_get_reltime(&now); os_reltime_sub(&now, &work->time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds", work->type, work, started ? "done" : "canceled", diff.sec, diff.usec); + dl_list_del(&work->list); radio_work_free(work); if (started) radio_work_check_next(wpa_s); @@ -7080,6 +7768,9 @@ radio_work_pending(struct wpa_supplicant *wpa_s, const char *type) struct wpa_radio_work *work; struct wpa_radio *radio = wpa_s->radio; + if (!radio) + return NULL; + dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0) return work; @@ -7207,7 +7898,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, #else /* CONFIG_BACKEND_FILE */ wpa_s->confname = os_strdup(iface->confname); #endif /* CONFIG_BACKEND_FILE */ - wpa_s->conf = wpa_config_read(wpa_s->confname, NULL, false); + wpa_s->conf = wpa_config_read( + wpa_s->confname, NULL, false, + wpa_s->global->params.show_details); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "Failed to read or parse " "configuration '%s'.", wpa_s->confname); @@ -7215,7 +7908,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, } wpa_s->confanother = os_rel2abs_path(iface->confanother); if (wpa_s->confanother && - !wpa_config_read(wpa_s->confanother, wpa_s->conf, true)) { + !wpa_config_read(wpa_s->confanother, wpa_s->conf, true, + wpa_s->global->params.show_details)) { wpa_printf(MSG_ERROR, "Failed to read or parse configuration '%s'.", wpa_s->confanother); @@ -7334,35 +8028,44 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_PREPEND_PMKID, wpa_s->conf->ft_prepend_pmkid); - wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, - &wpa_s->hw.num_modes, - &wpa_s->hw.flags, - &dfs_domain); + wpa_s->hw.modes = wpa_drv_get_hw_feature_data( + wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags, &dfs_domain, + wpa_s->device_country, sizeof(wpa_s->device_country)); + wpa_s->hw_dfs_domain = dfs_domain; + if (!wpa_s->device_country_set && + wpa_s->device_country[0] && wpa_s->device_country[1]) { + wpa_s->device_country_set = true; + wpa_printf(MSG_DEBUG, + "Device country code set to '%s' from hw feature data at init", + wpa_s->device_country); + } if (wpa_s->hw.modes) { u16 i; for (i = 0; i < wpa_s->hw.num_modes; i++) { - if (wpa_s->hw.modes[i].vht_capab) { - wpa_s->hw_capab = CAPAB_VHT; - break; - } - - if (wpa_s->hw.modes[i].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) - wpa_s->hw_capab = CAPAB_HT40; - else if (wpa_s->hw.modes[i].ht_capab && - wpa_s->hw_capab == CAPAB_NO_HT_VHT) - wpa_s->hw_capab = CAPAB_HT; + if (wpa_s->hw.modes[i].eht_capab[IEEE80211_MODE_INFRA]. + eht_supported) + wpa_s->hw_capab |= BIT(CAPAB_EHT); + if (wpa_s->hw.modes[i].he_capab[IEEE80211_MODE_INFRA]. + he_supported) + wpa_s->hw_capab |= BIT(CAPAB_HE); + if (wpa_s->hw.modes[i].vht_capab) + wpa_s->hw_capab |= BIT(CAPAB_VHT); + if (wpa_s->hw.modes[i].ht_capab) + wpa_s->hw_capab |= BIT(CAPAB_HT); } wpa_s->support_6ghz = wpas_is_6ghz_supported(wpa_s, false); } capa_res = wpa_drv_get_capa(wpa_s, &capa); if (capa_res == 0) { + u16 eml_capa, mld_capa; + wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; wpa_s->drv_flags2 = capa.flags2; wpa_s->drv_enc = capa.enc; + wpa_s->drv_key_mgmt = capa.key_mgmt; wpa_s->drv_rrm_flags = capa.rrm_flags; wpa_s->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; wpa_s->probe_resp_offloads = capa.probe_resp_offloads; @@ -7393,16 +8096,30 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, capa.mac_addr_rand_sched_scan_supported) wpa_s->mac_addr_rand_supported |= (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO); + wpa_s->drv_max_probe_req_ie_len = capa.max_probe_req_ie_len; wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); if (wpa_s->extended_capa && wpa_s->extended_capa_len >= 3 && wpa_s->extended_capa[2] & 0x40) wpa_s->multi_bss_support = 1; + + if (wpa_drv_get_mld_capa(wpa_s, WPA_IF_STATION, + &eml_capa, &mld_capa) == 0) { + wpa_s->eml_capa = eml_capa; + wpa_s->mld_capa = mld_capa; + } + } else { + wpa_s->drv_max_probe_req_ie_len = 1500; } #ifdef CONFIG_PASN wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2); #endif /* CONFIG_PASN */ + +#ifdef CONFIG_IEEE8021X_AUTH + wpa_sm_set_802_1x_auth_caps(wpa_s->wpa, wpa_s->drv_flags2); +#endif /* CONFIG_IEEE8021X_AUTH */ + wpa_sm_set_driver_bss_selection(wpa_s->wpa, !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION)); @@ -7417,6 +8134,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) wpa_s->p2p_mgmt = iface->p2p_mgmt; + wpa_s->nan_mgmt = iface->nan_mgmt; + wpa_s->nan_data = iface->nan_data; + + if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_P2P_ASSISTED_DFS) && + wpa_s->conf->p2p_assisted_dfs_chan_enable) + wpa_s->allow_p2p_assisted_dfs = true; + if (wpa_s->num_multichan_concurrent == 0) wpa_s->num_multichan_concurrent = 1; @@ -7424,7 +8148,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; #ifdef CONFIG_TDLS - if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) + if (!iface->p2p_mgmt && !iface->nan_mgmt && wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -7472,10 +8196,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; #endif /* CONFIG_DPP */ -#ifdef CONFIG_NAN_USD - if (wpas_nan_usd_init(wpa_s) < 0) + if (wpas_nan_de_init(wpa_s) < 0) return -1; -#endif /* CONFIG_NAN_USD */ + +#ifdef CONFIG_NAN + os_memcpy(&wpa_s->nan_capa, &capa.nan_capa, + sizeof(wpa_s->nan_capa)); +#endif /* CONFIG_NAN */ if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; @@ -7501,6 +8228,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } +#ifdef CONFIG_P2P + if (wpa_s->drv_flags2 & (WPA_DRIVER_FLAGS2_P2P_FEATURE_V2 | + WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE)) { + wpa_s->p2p_pairing_setup = true; + wpa_s->p2p_pairing_cache = true; + } +#endif /* CONFIG_P2P */ + if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || wpa_s->p2p_mgmt) && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { @@ -7508,15 +8243,16 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } + if (!capa.ranging_type.pd_support) { + wpa_printf(MSG_DEBUG, + "PR: Driver does not support Proximity Ranging - PR disabled"); + } else if (wpas_pr_init(wpa_s->global, wpa_s, &capa) < 0) { + return -1; + } + if (wpa_bss_init(wpa_s) < 0) return -1; -#ifdef CONFIG_PMKSA_CACHE_EXTERNAL -#ifdef CONFIG_MESH - dl_list_init(&wpa_s->mesh_external_pmksa_cache); -#endif /* CONFIG_MESH */ -#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ - /* * Set Wake-on-WLAN triggers, if configured. * Note: We don't restore/remove the triggers on shutdown (it doesn't @@ -7552,9 +8288,6 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); -#ifdef CONFIG_HS20 - hs20_init(wpa_s); -#endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO if (!wpa_s->disable_mbo_oce && wpa_s->conf->oce) { if ((wpa_s->conf->oce & OCE_STA) && @@ -7572,6 +8305,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_supplicant_set_default_scan_ies(wpa_s); + if (wpa_s->nan_mgmt && wpas_nan_init(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init NAN"); + return -1; + } + return 0; } @@ -7623,6 +8361,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_supplicant_cleanup(wpa_s); wpas_p2p_deinit_iface(wpa_s); + wpas_nan_deinit(wpa_s); + wpas_ctrl_radio_work_flush(wpa_s); radio_remove_interface(wpa_s); @@ -7790,7 +8530,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, return NULL; } - if (iface->p2p_mgmt == 0) { + if (iface->p2p_mgmt == 0 && !iface->nan_mgmt) { /* Notify the control interfaces about new iface */ if (wpas_notify_iface_added(wpa_s)) { wpa_supplicant_deinit_iface(wpa_s, 1, 0); @@ -7808,7 +8548,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); #ifdef CONFIG_P2P - if (wpa_s->global->p2p == NULL && + if (!wpa_s->global->p2p && !wpas_is_nan_iface(wpa_s) && !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && wpas_p2p_add_p2pdev_interface( @@ -8019,6 +8759,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) global->params.daemonize = params->daemonize; global->params.wait_for_monitor = params->wait_for_monitor; global->params.dbus_ctrl_interface = params->dbus_ctrl_interface; + global->params.show_details = params->show_details; if (params->pid_file) { global->params.pid_file = os_strdup(params->pid_file); @@ -8110,6 +8851,16 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) random_init(params->entropy_file); +#ifdef CONFIG_PROCESS_COORDINATION + if (params->proc_coord_dir) { + global->pc = proc_coord_init(params->proc_coord_dir); + if (!global->pc) { + wpa_supplicant_deinit(global); + return NULL; + } + } +#endif /* CONFIG_PROCESS_COORDINATION */ + global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); if (global->ctrl_iface == NULL) { wpa_supplicant_deinit(global); @@ -8230,6 +8981,10 @@ void wpa_supplicant_deinit(struct wpa_global *global) random_deinit(); +#ifdef CONFIG_PROCESS_COORDINATION + proc_coord_deinit(global->pc); +#endif /* CONFIG_PROCESS_COORDINATION */ + eloop_destroy(); if (global->params.pid_file) { @@ -8258,6 +9013,24 @@ void wpa_supplicant_deinit(struct wpa_global *global) } +int wpa_supplicant_parse_config(const char *fname) +{ + struct wpa_config *conf; + int ret = -1; + + wpa_printf(MSG_INFO, "Validating parsing of %s", fname); + conf = wpa_config_read(fname, NULL, false, true); + if (conf) { + wpa_printf(MSG_INFO, "Parsing succeeded"); + ret = 0; + wpa_config_free(conf); + } else { + wpa_printf(MSG_INFO, "Parsing failed"); + } + return ret; +} + + void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) { if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && @@ -8295,6 +9068,11 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_PREPEND_PMKID, wpa_s->conf->ft_prepend_pmkid); +#ifdef CONFIG_P2P + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_DISABLED) + wpas_p2p_disabled_changed(wpa_s); +#endif /* CONFIG_P2P */ + #ifdef CONFIG_BGSCAN /* * We default to global bgscan parameters only when per-network bgscan @@ -8315,60 +9093,11 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) } -void add_freq(int *freqs, int *num_freqs, int freq) -{ - int i; - - for (i = 0; i < *num_freqs; i++) { - if (freqs[i] == freq) - return; - } - - freqs[*num_freqs] = freq; - (*num_freqs)++; -} - - -static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) -{ - struct wpa_bss *bss, *cbss; - const int max_freqs = 10; - int *freqs; - int num_freqs = 0; - - freqs = os_calloc(max_freqs + 1, sizeof(int)); - if (freqs == NULL) - return NULL; - - cbss = wpa_s->current_bss; - - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - if (bss == cbss) - continue; - if (bss->ssid_len == cbss->ssid_len && - os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && - !wpa_bssid_ignore_is_listed(wpa_s, bss->bssid)) { - add_freq(freqs, &num_freqs, bss->freq); - if (num_freqs == max_freqs) - break; - } - } - - if (num_freqs == 0) { - os_free(freqs); - freqs = NULL; - } - - return freqs; -} - - void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 **link_bssids) { int timeout; int count; - int *freqs = NULL; wpas_connect_work_done(wpa_s); @@ -8405,27 +9134,15 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid, * attempt if there could be other APs that could accept association. */ count = wpa_bssid_ignore_add(wpa_s, bssid); - if (count == 1 && wpa_s->current_bss) { - /* - * This BSS was not in the ignore list before. If there is - * another BSS available for the same ESS, we should try that - * next. Otherwise, we may as well try this one once more - * before allowing other, likely worse, ESSes to be considered. - */ - freqs = get_bss_freqs_in_ess(wpa_s); - if (freqs) { - wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS " - "has been seen; try it next"); - wpa_bssid_ignore_add(wpa_s, bssid); - /* - * On the next scan, go through only the known channels - * used in this ESS based on previous scans to speed up - * common load balancing use case. - */ - os_free(wpa_s->next_scan_freqs); - wpa_s->next_scan_freqs = freqs; - } - } + + /* + * This BSS was not in the ignore list before. If there is + * another BSS available for the same ESS, we should try that + * next. Otherwise, we may as well try this one once more + * before allowing other, likely worse, ESSes to be considered. + */ + if (count == 1 && wpa_supplicant_fast_associate(wpa_s) == 1) + return; wpa_s->consecutive_conn_failures++; @@ -8461,11 +9178,6 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid, "Consecutive connection failures: %d --> request scan in %d ms", wpa_s->consecutive_conn_failures, timeout); - /* - * TODO: if more than one possible AP is available in scan results, - * could try the other ones before requesting a new scan. - */ - /* speed up the connection attempt with normal scan */ wpa_s->normal_scans = 0; wpa_supplicant_req_scan(wpa_s, timeout / 1000, @@ -8549,6 +9261,35 @@ int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) } +static bool wpas_driver_rsn_override(struct wpa_supplicant *wpa_s) +{ + return !!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA); +} + + +bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + enum wpas_rsn_overriding rsno; + + if (ssid && ssid->rsn_overriding != RSN_OVERRIDING_NOT_SET) + rsno = ssid->rsn_overriding; + else + rsno = wpa_s->conf->rsn_overriding; + + if (rsno == RSN_OVERRIDING_DISABLED) + return false; + + if (rsno == RSN_OVERRIDING_ENABLED) + return true; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + wpas_driver_bss_selection(wpa_s)) + return wpas_driver_rsn_override(wpa_s); + + return true; +} + + #if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW) int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, @@ -8699,7 +9440,8 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && - !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) && + !(wpa_key_mgmt_sae(ssid->key_mgmt) && + (ssid->passphrase || ssid->sae_password || ssid->pmk_valid)) && !ssid->mem_only_psk) return 1; @@ -8768,6 +9510,16 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) #ifdef CONFIG_SAE + +enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (!ssid || ssid->sae_pwe == DEFAULT_SAE_PWE) + return wpa_s->conf->sae_pwe; + return ssid->sae_pwe; +} + + bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const struct wpa_ie_data *ie) @@ -8777,6 +9529,7 @@ bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s, (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) || wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION); } + #endif /* CONFIG_SAE */ @@ -8948,6 +9701,9 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->last_owe_group = 0; +#ifdef CONFIG_PASN + wpa_pasn_reset(&wpa_s->pasn); +#endif /* CONFIG_PASN */ if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -9011,7 +9767,6 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, { struct wpa_supplicant *ifs; u8 bssid[ETH_ALEN]; - int freq; unsigned int idx = 0, i; wpa_dbg(wpa_s, MSG_DEBUG, @@ -9020,36 +9775,64 @@ int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { + int freqs[MAX_NUM_MLD_LINKS]; + unsigned int j, n_freqs = 0; + if (idx == len) break; if (exclude_current && ifs == wpa_s) continue; - if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + if (!ifs->current_ssid || + (!ifs->assoc_freq && !ifs->valid_links)) continue; if (ifs->current_ssid->mode == WPAS_MODE_AP || ifs->current_ssid->mode == WPAS_MODE_P2P_GO || - ifs->current_ssid->mode == WPAS_MODE_MESH) - freq = ifs->current_ssid->frequency; - else if (wpa_drv_get_bssid(ifs, bssid) == 0) - freq = ifs->assoc_freq; - else + ifs->current_ssid->mode == WPAS_MODE_MESH) { + freqs[n_freqs++] = ifs->current_ssid->frequency; + } else if (ifs->valid_links) { + struct driver_sta_mlo_info drv_mlo; + + os_memset(&drv_mlo, 0, sizeof(drv_mlo)); + + if (wpas_drv_get_sta_mlo_info(ifs, &drv_mlo)) { + wpa_dbg(wpa_s, MSG_INFO, + "Failed to get MLO link info"); + continue; + } + + if (!drv_mlo.valid_links) + continue; + + for_each_link(drv_mlo.valid_links, j) { + if (!drv_mlo.links[j].freq) + continue; + + freqs[n_freqs++] = drv_mlo.links[j].freq; + } + } else if (wpa_drv_get_bssid(ifs, bssid) == 0) { + freqs[n_freqs++] = ifs->assoc_freq; + } else { continue; + } /* Hold only distinct freqs */ - for (i = 0; i < idx; i++) - if (freqs_data[i].freq == freq) - break; + for (j = 0; j < n_freqs && idx < len; j++) { + for (i = 0; i < idx; i++) + if (freqs_data[i].freq == freqs[j]) + break; - if (i == idx) - freqs_data[idx++].freq = freq; + if (i == idx) + freqs_data[idx++].freq = freqs[j]; - if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { - freqs_data[i].flags |= ifs->current_ssid->p2p_group ? - WPA_FREQ_USED_BY_P2P_CLIENT : - WPA_FREQ_USED_BY_INFRA_STATION; + if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { + freqs_data[i].flags |= + ifs->current_ssid->p2p_group ? + WPA_FREQ_USED_BY_P2P_CLIENT : + WPA_FREQ_USED_BY_INFRA_STATION; + } } } @@ -9499,7 +10282,7 @@ int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, } return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src, - bssid, data, data_len, no_cck); + bssid, data, data_len, no_cck, -1); } @@ -9523,3 +10306,158 @@ bool wpas_is_6ghz_supported(struct wpa_supplicant *wpa_s, bool only_enabled) return false; } + + +bool wpas_ap_supports_rsn_overriding(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + int i; + + if (!bss) + return false; + if (wpa_bss_get_vendor_ie(bss, RSNE_OVERRIDE_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie(bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE)) + return true; + + if (!wpa_s->valid_links) + return false; + + for_each_link(wpa_s->valid_links, i) { + if (wpa_s->links[i].bss && + (wpa_bss_get_vendor_ie(wpa_s->links[i].bss, + RSNE_OVERRIDE_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie(wpa_s->links[i].bss, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE))) + return true; + } + + return false; +} + + +bool wpas_ap_supports_rsn_overriding_2(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + int i; + + if (!bss) + return false; + if (wpa_bss_get_vendor_ie(bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE)) + return true; + + if (!wpa_s->valid_links) + return false; + + for_each_link(wpa_s->valid_links, i) { + if (wpa_s->links[i].bss && + wpa_bss_get_vendor_ie(wpa_s->links[i].bss, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE)) + return true; + } + + return false; +} + + +int wpas_get_owe_trans_network(const u8 *owe_ie, const u8 **bssid, + const u8 **ssid, size_t *ssid_len) +{ +#ifdef CONFIG_OWE + const u8 *pos, *end; + u8 ssid_len_tmp; + + if (!owe_ie) + return -1; + + pos = owe_ie + 6; + end = owe_ie + 2 + owe_ie[1]; + + if (end - pos < ETH_ALEN + 1) + return -1; + *bssid = pos; + pos += ETH_ALEN; + ssid_len_tmp = *pos++; + if (end - pos < ssid_len_tmp || ssid_len_tmp > SSID_MAX_LEN) + return -1; + + *ssid = pos; + *ssid_len = ssid_len_tmp; + + return 0; +#else /* CONFIG_OWE */ + return -1; +#endif /* CONFIG_OWE */ +} + + +void wpas_update_dfs_ap_info(struct wpa_supplicant *wpa_s, int freq, + enum chan_width ap_ch_width, + bool disconnect_evt) +{ + if (disconnect_evt) { + wpa_printf(MSG_DEBUG, "Disconnect event of DFS AP"); + wpa_s->sta_connected_freq = 0; + wpa_s->sta_connected_chan_width = CHAN_WIDTH_UNKNOWN; + } else { + wpa_s->sta_connected_freq = freq; + wpa_s->sta_connected_chan_width = ap_ch_width; + } + wpa_s->dfs_ap_connected = !disconnect_evt; + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) + p2p_update_dfs_ap_info(wpa_s->global->p2p, freq, ap_ch_width, + disconnect_evt); +#endif /* CONFIG_P2P */ +} + + +void wpas_configure_frame_filters(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss = wpa_s->current_bss; + u32 filter = 0; + bool hs20, proxy_arp_capa; + + if (!bss) + return; + +#ifdef CONFIG_HS20 + hs20 = is_hs20_network(wpa_s, wpa_s->current_ssid, bss); +#else /* CONFIG_HS20 */ + hs20 = false; +#endif /* CONFIG_HS20 */ + + if (wpa_s->current_ssid && + wpa_s->current_ssid->drop_unicast_ip_in_l2_multicast) { + filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; + } else if (!hs20) { + /* Not configuring frame filtering - BSS is not a Hotspot 2.0 + * network */ + return; + } else { +#ifdef CONFIG_HS20 + const u8 *ie; + + ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); + + /* Check if DGAF disabled bit is zero (5th byte in the IE) */ + if (!ie || ie[1] < 5) + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - Can't extract DGAF bit"); + else if (!(ie[6] & HS20_DGAF_DISABLED)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; +#endif /* CONFIG_HS20 */ + } + + proxy_arp_capa = wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP); + + if ((hs20 && proxy_arp_capa) || + (wpa_s->current_ssid && + wpa_s->current_ssid->always_use_proxy_arp == 2) || + (proxy_arp_capa && wpa_s->current_ssid && + wpa_s->current_ssid->always_use_proxy_arp == 1)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | + WPA_DATA_FRAME_FILTER_FLAG_NA; + + wpa_drv_configure_frame_filters(wpa_s, filter); +} diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index b08f5417ad88..56533cb17354 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -401,6 +401,44 @@ fast_reauth=1 # it from taking 100% of radio resources. The default value is 500 ms. #p2p_search_delay=500 +# Enable/disable P2P pairing verification with cached NIK/NPK +#p2p_pairing_verification=0 + +# Supported P2P bootstrapping method bitmap +# b0: whether opportunistic bootstrapping is supported +# b1: whether PIN display is supported +# b2: whether passphrase display is supported +# b3: whether QR Code display is supported +# b4: whether NFC tag is supported +# b5: whether keypad (PIN only) is supported +# b6: whether keypad (passphrase) is supported +# b7: whether QR Code scan is supported +# b8: whether NFC reader is supported +# b14: whether service managed bootstrapping is supported +# b15: whether bootstrapping handshakes skipped is supported +#p2p_bootstrap_methods=0 + +# Bitmap of supported PASN types +# B0: whether DH Group 19 with unauthenticated PASN is supported +# B1: whether DH Group 19 with authenticated PASN is supported +# B2: whether DH Group 20 with unauthenticated PASN is supported +# B3: whether DH Group 20 authenticated PASN is supported +#p2p_pasn_type=0 + +# Bootstrap request for unauthorized peer is asked to come back after +# this many TUs. +#p2p_comeback_after=977 + +# Enable/disable TWT based power management for P2P +#p2p_twt_power_mgmt=0 + +# Enable/disable P2P client channel switch request +#p2p_chan_switch_req_enable=0 + +# Regulatory info encoding for operation in 6 GHz band +# As defined in Table E-12 and E-13 of IEEE P802.11-REVme/D7.0. +#p2p_reg_info=0 + # Opportunistic Key Caching (also known as Proactive Key Caching) default # This parameter can be used to set the default behavior for the # proactive_key_caching parameter. By default, OKC is disabled unless enabled @@ -608,6 +646,29 @@ fast_reauth=1 # 1 = Publish #ftm_initiator=0 +#twt_requester: Whether TWT requester is enabled +# 0 = disabled (default) +# 1 = enabled if supported by the driver +#twt_requester=0 + +# Wi-Fi Alliance generational capabilities indication +# +# wfa_gen_capa: Whether to indicate Wi-Fi generational capability to the AP +# 0 = do not indicate (default) +# 1 = indicate in protected Action frame +# 2 = indicate in unprotected (Re)Association Request frame +#wfa_gen_capa=0 +# +# wfa_gen_capa_supp: Supported Generations (hexdump of a bit field) +# A bit field of supported Wi-Fi generations. This is encoded as an little +# endian octet string. If this is not set, the driver capabilities are +# determined automatically. +# bit 0: Wi-Fi 4 +# bit 1: Wi-Fi 5 +# bit 2: Wi-Fi 6 +# bit 3: Wi-Fi 7 +#wfa_gen_capa_supp=07 + # credential block # # Each credential used for automatic network selection is configured as a set @@ -878,6 +939,17 @@ fast_reauth=1 # 1 = auto: Activate Extended Key ID support if the driver supports it #extended_key_id=0 +# RSN overriding +# NOTE: The protocol used for this mechanism is still subject to change and as +# such, this should not yet be enabled for production uses to avoid issues if +# something were to change. +# A per-network block parameter with the same name can be used to override this +# global parameter. +# 0 = Disabled (default) +# 1 = Enabled automatically if the driver indicates support +# 2 = Forced to be enabled even without driver capability indication +#rsn_overriding=0 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -1173,6 +1245,10 @@ fast_reauth=1 # mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being # default priority # +# macsec_icv_indicator: Always include ICV indicator +# 0 = ICV Indicator is not included when ICV has default length (default) +# 1 = ICV Indicator is always included (compatibility mode) +# # mixed_cell: This option can be used to configure whether so called mixed # cells, i.e., networks that use both plaintext and encryption in the same # SSID, are allowed when selecting a BSS from scan results. @@ -1698,6 +1774,10 @@ fast_reauth=1 # 2: MCS 0-9 # 3: not supported +# disable_he: Whether HE should be disabled. +# 0 = HE enabled (if supported) (default) +# 1 = HE disabled + # disable_eht: Whether EHT should be disabled. # 0 = EHT enabled (if supported) (default) # 1 = EHT disabled @@ -1715,6 +1795,22 @@ fast_reauth=1 # 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification #multi_ap_profile=2 +# Unicast IP packet in L2 multicast frame filtering +# Allow turning off IP unicast in L2 multicast frame filtering, which is enabled +# by default. +#drop_unicast_ip_in_l2_multicast=1 + +# Proxy ARP/NS reliance +# Enable Proxy ARP/NS reliance, dropping gratuitous ARP and unsolicited NA +# frames when the AP supports proxy ARP even on networks that aren't Passpoint. +# If set to 0 (the default), this is only done on Passpoint networks. +# If set to 1, the AP's extended capabilities are checked, and proxy ARP/NS +# is used when available. +# If set to 2, the feature is enabled regardless of the AP's extended +# capabilities, to allow forcing it in networks known to have it, since +# the extended capabilities are not cryptographically checked. +#always_use_proxy_arp=0 + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 110a8648c81f..cd763693f3c4 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -21,6 +21,7 @@ #include "config_ssid.h" #include "wmm_ac.h" #include "pasn/pasn_common.h" +#include "nan/nan.h" extern const char *const wpa_supplicant_version; extern const char *const wpa_supplicant_license; @@ -135,6 +136,16 @@ struct wpa_interface { WPA_IFACE_MATCHED } matched; #endif /* CONFIG_MATCH_IFACE */ + + /** + * nan_mgmt - Interface used for NAN management (NAN Device operations) + */ + bool nan_mgmt; + + /** + * nan_data - Interface used for NAN data path operations + */ + bool nan_data; }; /** @@ -258,6 +269,16 @@ struct wpa_params { */ int match_iface_count; #endif /* CONFIG_MATCH_IFACE */ + + /** + * show_details - Whether to show config parsing details in debug log + */ + bool show_details; + + /** + * proc_coord_dir - Process coordination directory + */ + const char *proc_coord_dir; }; struct p2p_srv_bonjour { @@ -288,9 +309,11 @@ struct wpa_global { size_t drv_count; struct os_time suspend_time; struct p2p_data *p2p; + struct pr_data *pr; struct wpa_supplicant *p2p_init_wpa_s; struct wpa_supplicant *p2p_group_formation; struct wpa_supplicant *p2p_invite_group; + struct wpa_supplicant *pr_init_wpa_s; u8 p2p_dev_addr[ETH_ALEN]; struct os_reltime p2p_go_wait_client; struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ @@ -319,6 +342,10 @@ struct wpa_global { #endif /* CONFIG_WIFI_DISPLAY */ struct psk_list_entry *add_psk; /* From group formation */ + +#ifdef CONFIG_PROCESS_COORDINATION + struct proc_coord *pc; +#endif /* CONFIG_PROCESS_COORDINATION */ }; @@ -367,14 +394,14 @@ struct wpa_radio_work { unsigned int bands; }; -int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, - const char *type, int next, - void (*cb)(struct wpa_radio_work *work, int deinit), - void *ctx); +struct wpa_radio_work * +radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx); void radio_work_done(struct wpa_radio_work *work); void radio_remove_works(struct wpa_supplicant *wpa_s, const char *type, int remove_all); -void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx); void radio_work_check_next(struct wpa_supplicant *wpa_s); struct wpa_radio_work * radio_work_pending(struct wpa_supplicant *wpa_s, const char *type); @@ -468,15 +495,6 @@ enum wpa_supplicant_test_failure { WPAS_TEST_FAILURE_SCAN_TRIGGER, }; -struct icon_entry { - struct dl_list list; - u8 bssid[ETH_ALEN]; - u8 dialog_token; - char *file_name; - u8 *image; - size_t image_len; -}; - struct wpa_bss_tmp_disallowed { struct dl_list list; u8 bssid[ETH_ALEN]; @@ -659,6 +677,7 @@ struct active_scs_elem { struct dl_list list; u8 scs_id; enum scs_response_status status; + struct scs_desc_elem desc_elem; }; @@ -669,6 +688,56 @@ struct ml_sta_link_info { }; +enum local_hw_capab { + CAPAB_HT, + CAPAB_VHT, + CAPAB_HE, + CAPAB_EHT, +}; + +struct last_scan_ssid { + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; +}; + +#ifdef CONFIG_IEEE8021X_AUTH +/** + * struct auth_802_1x_data - Data for IEEE 802.1X Authentication algorithm + * @auth_trans: Authentication transaction sequence number + * @status: Status code + * @derive_ptk: Whether to derive a PTK for association + * @pmksa_caching: Whether PMKSA caching is being used + * @pmkid_found: Whether PMKID is identified by AP + * @pmkid: PMKID for PMKSA caching + * @snonce: SNonce + * @anonce: ANonce + * @dh_group: DH group for key exchange + * @ecdh: ECDH context + * @dhss: DH shared secret + * @rsne: RSNE + * @rsne_len: Length of RSNE + * @rsnxe: RSNXE + * @rsnxe_len: Length of RSNXE + */ +struct auth_802_1x_data { + u16 auth_trans; + u16 status; + bool derive_ptk; + bool pmksa_caching; + bool pmkid_found; + u8 pmkid[PMKID_LEN]; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; + u16 dh_group; + struct crypto_ecdh *ecdh; + struct wpabuf *dhss; + u8 rsne[257]; + size_t rsne_len; + u8 rsnxe[257]; + size_t rsnxe_len; +}; +#endif /* CONFIG_IEEE8021X_AUTH */ + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -693,6 +762,7 @@ struct wpa_supplicant { unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; + u8 hw_dfs_domain; #ifdef CONFIG_MATCH_IFACE int matched; #endif /* CONFIG_MATCH_IFACE */ @@ -737,9 +807,14 @@ struct wpa_supplicant { unsigned int freq; struct wpa_bss *bss; bool disabled; + struct wpabuf *ies; + unsigned int max_nss_rx:4; + unsigned int max_nss_tx:4; + enum chan_width channel_bandwidth; } links[MAX_NUM_MLD_LINKS]; u8 *last_con_fail_realm; size_t last_con_fail_realm_len; + bool sta_roaming_disabled; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ int pairwise_cipher; @@ -805,6 +880,9 @@ struct wpa_supplicant { size_t last_scan_res_used; size_t last_scan_res_size; struct os_reltime last_scan; + bool last_scan_external; + struct last_scan_ssid last_scan_ssids[WPAS_MAX_SCAN_SSIDS]; + size_t last_scan_num_ssids; const struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been @@ -826,7 +904,7 @@ struct wpa_supplicant { int eapol_received; /* number of EAPOL packets received after the * previous association event */ - u8 rsnxe[20]; + u8 rsnxe[257]; size_t rsnxe_len; struct scard_data *scard; @@ -908,8 +986,7 @@ struct wpa_supplicant { struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; - int *last_scan_freqs; - unsigned int num_last_scan_freqs; + int *last_scan_freqs; /* int_array */ unsigned int suitable_network; unsigned int no_suitable_network; @@ -920,8 +997,10 @@ struct wpa_supplicant { u64 drv_flags; u64 drv_flags2; unsigned int drv_enc; + unsigned int drv_key_mgmt; unsigned int drv_rrm_flags; unsigned int drv_max_acl_mac_addrs; + size_t drv_max_probe_req_ie_len; /* * A bitmap of supported protocols for probe response offload. See @@ -933,6 +1012,9 @@ struct wpa_supplicant { const u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + /* EML and MLD capabilities supported by the driver */ + u16 eml_capa, mld_capa; + int max_scan_ssids; int max_sched_scan_ssids; unsigned int max_sched_scan_plans; @@ -974,6 +1056,9 @@ struct wpa_supplicant { unsigned int connection_he:1; unsigned int connection_eht:1; unsigned int disable_mbo_oce:1; + u8 connection_max_nss_rx; + u8 connection_max_nss_tx; + enum chan_width connection_channel_bandwidth; struct os_reltime last_mac_addr_change; enum wpas_mac_addr_style last_mac_addr_style; @@ -1018,22 +1103,31 @@ struct wpa_supplicant { u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; + bool spp_amsdu; #ifdef CONFIG_SAE struct sae_data sae; struct wpabuf *sae_token; int sae_group_index; unsigned int sae_pmksa_caching:1; u16 seq_num; + int *sae_rejected_groups; +#endif /* CONFIG_SAE */ u8 ext_auth_bssid[ETH_ALEN]; + unsigned int ext_auth_freq; struct wpa_ssid *ext_auth_wpa_ssid; u8 ext_auth_ssid[SSID_MAX_LEN]; size_t ext_auth_ssid_len; int ext_auth_key_mgmt; u8 ext_auth_ap_mld_addr[ETH_ALEN]; bool ext_ml_auth; - int *sae_rejected_groups; -#endif /* CONFIG_SAE */ u16 assoc_auth_type; + int ext_auth_alg; + int ext_pairwise_cipher; + int ext_group_cipher; + int ext_mgmt_group_cipher; + u16 ext_rsn_capab; + u8 ext_rsnxe[257]; + size_t ext_rsnxe_len; } sme; #endif /* CONFIG_SME */ @@ -1083,6 +1177,7 @@ struct wpa_supplicant { #ifdef CONFIG_P2P struct p2p_go_neg_results *go_params; + struct rsn_pmksa_cache_entry *p2p_pmksa_entry; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; char pending_interface_name[100]; @@ -1105,13 +1200,17 @@ struct wpa_supplicant { int p2p_in_invitation; int p2p_retry_limit; int p2p_invite_go_freq; + bool p2p_pairing_setup; + bool p2p_pairing_cache; int pending_invite_ssid_id; int show_group_started; u8 go_dev_addr[ETH_ALEN]; int pending_pd_before_join; u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; + u8 p2p_bootstrap_dev_addr[ETH_ALEN]; int pending_join_wps_method; + char pending_join_password[100]; u8 p2p_join_ssid[SSID_MAX_LEN]; size_t p2p_join_ssid_len; int p2p_join_scan_count; @@ -1165,7 +1264,10 @@ struct wpa_supplicant { unsigned int p2ps_method_config_any:1; unsigned int p2p_cli_probe:1; unsigned int p2p_go_allow_dfs:1; + unsigned int p2p2:1; + u16 p2p_bootstrap; enum hostapd_hw_mode p2p_go_acs_band; + enum wpa_p2p_mode p2p_mode; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -1175,6 +1277,7 @@ struct wpa_supplicant { struct wpa_radio_work *p2p_scan_work; struct wpa_radio_work *p2p_listen_work; struct wpa_radio_work *p2p_send_action_work; + bool p2p_removing_listen_work; u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group @@ -1190,6 +1293,7 @@ struct wpa_supplicant { unsigned int p2p_go_max_oper_chwidth; unsigned int p2p_go_vht_center_freq2; int p2p_lo_started; + bool p2p_neg_go_setup; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; @@ -1228,17 +1332,7 @@ struct wpa_supplicant { unsigned int auto_network_select:1; unsigned int interworking_fast_assoc_tried:1; unsigned int fetch_all_anqp:1; - unsigned int fetch_osu_info:1; - unsigned int fetch_osu_waiting_scan:1; - unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; - unsigned int osu_icon_id; - struct dl_list icon_head; /* struct icon_entry */ - struct osu_provider *osu_prov; - size_t osu_prov_count; - struct os_reltime osu_icon_fetch_start; - unsigned int num_osu_scans; - unsigned int num_prov_found; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; @@ -1247,12 +1341,7 @@ struct wpa_supplicant { u16 num_modes; u16 flags; } hw; - enum local_hw_capab { - CAPAB_NO_HT_VHT, - CAPAB_HT, - CAPAB_HT40, - CAPAB_VHT, - } hw_capab; + unsigned int hw_capab; /* bitmap of enum local_hw_capab bits */ #ifdef CONFIG_MACSEC struct ieee802_1x_kay *kay; #endif /* CONFIG_MACSEC */ @@ -1300,13 +1389,15 @@ struct wpa_supplicant { u8 *mac_addr_pno; #ifdef CONFIG_WNM + bool wnm_transition_scan; u8 wnm_dialog_token; u8 wnm_reply; u8 wnm_num_neighbor_report; u8 wnm_mode; bool wnm_link_removal; - u8 wnm_dissoc_addr[ETH_ALEN]; - u16 wnm_dissoc_timer; + bool wnm_disassoc_mld; + u8 wnm_disassoc_addr[ETH_ALEN]; + u16 wnm_disassoc_timer; u8 wnm_bss_termination_duration[12]; struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; @@ -1367,7 +1458,9 @@ struct wpa_supplicant { unsigned int oci_freq_override_fils_assoc; unsigned int oci_freq_override_wnm_sleep; unsigned int disable_eapol_g2_tx; + unsigned int eapol_2_key_info_set_mask; int test_assoc_comeback_type; + struct wpabuf *link_ies[MAX_NUM_MLD_LINKS]; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; @@ -1579,12 +1672,24 @@ struct wpa_supplicant { bool wps_scan_done; /* Set upon receiving scan results event */ bool supp_pbc_active; /* Set for interface when PBC is triggered */ bool wps_overlap; + bool scan_in_progress_6ghz; /* Set upon a 6 GHz scan being triggered */ + +#ifdef CONFIG_IEEE8021X_AUTH + struct auth_802_1x_data *auth_1x; +#endif /* CONFIG_IEEE8021X_AUTH */ #ifdef CONFIG_PASN struct pasn_data pasn; struct wpa_radio_work *pasn_auth_work; unsigned int pasn_count; struct pasn_auth *pasn_params; + bool urnm_mfpr_x20; + bool disable_urnm_mfpr; +#ifdef CONFIG_P2P + struct wpa_radio_work *p2p_pasn_auth_work; +#endif /* CONFIG_P2P */ + struct wpa_radio_work *pr_pasn_auth_work; + struct wpa_radio_work *pr_roc_work; #endif /* CONFIG_PASN */ bool is_6ghz_enabled; @@ -1592,6 +1697,14 @@ struct wpa_supplicant { bool last_scan_all_chan; bool last_scan_non_coloc_6ghz; bool support_6ghz; + bool allow_p2p_assisted_dfs; + char device_country[3]; + bool device_country_set; + bool assisted_dfs; + + bool dfs_ap_connected; + int sta_connected_freq; + enum chan_width sta_connected_chan_width; struct wpa_signal_info last_signal_info; @@ -1615,6 +1728,65 @@ struct wpa_supplicant { u64 first_beacon_tsf; unsigned int beacons_checked; unsigned int next_beacon_check; + + bool scs_reconfigure; + bool ext_auth_to_same_bss; /* Whether external authentication has been + * completed successfully with the BSS that + * we are already associated with. */ + + bool nan_mgmt; + bool nan_data; + +#ifdef CONFIG_NAN +#define MAX_NAN_RADIOS 2 + struct nan_capa nan_capa; + struct nan_data *nan; + struct nan_cluster_config nan_cluster_config; + u8 schedule_sequence_id; + struct nan_schedule_config nan_sched[MAX_NAN_RADIOS]; + u16 nan_supported_csids; + struct nan_schedule_update { + struct nan_schedule_config sched; + u8 map_id; + } nan_sched_update; + struct wpabuf *nan_ulw_attr; + struct wpa_freq_range_list nan_disallowed_freqs; + u16 nan_max_bw; + struct nan_channels nan_override_potential_avail; + unsigned int nan_ndi_ndp_refcount; /* Active NDP count on this NDI */ + struct nan_gtk ndi_gtk; +#ifdef CONFIG_TESTING_OPTIONS + bool nan_force_conditional_sched; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_NAN */ +#ifdef CONFIG_ENC_ASSOC + bool assoc_resp_encrypted; /* Whether (Re)Association Response frame + * is encrypted. */ +#endif /* CONFIG_ENC_ASSOC */ +#ifdef CONFIG_PMKSA_PRIVACY + u8 pmkid_snonce[NONCE_LEN]; + bool pmkid_snonce_set; + u8 pmkid_anonce[NONCE_LEN]; + bool pmkid_anonce_set; +#endif /* CONFIG_PMKSA_PRIVACY */ + u8 pd_addr[ETH_ALEN]; + + /** + * pr_responder_mode - Waiting for PASN M1 as responder + * + * Set when ROC has been started on this interface to listen for an + * incoming PASN Auth1 frame. Cleared once the dedicated PR interface + * is created on M1 reception. + */ + bool pr_responder_mode; + + /** + * pr_responder_src_addr - Source MAC address used for responder ROC + * + * Stored when responder mode is activated so that the dedicated PR + * interface can be created with the same address when M1 arrives. + */ + u8 pr_responder_src_addr[ETH_ALEN]; }; @@ -1712,6 +1884,7 @@ struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, struct wpa_global * wpa_supplicant_init(struct wpa_params *params); int wpa_supplicant_run(struct wpa_global *global); void wpa_supplicant_deinit(struct wpa_global *global); +int wpa_supplicant_parse_config(const char *fname); int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -1726,6 +1899,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid, void fils_connection_failure(struct wpa_supplicant *wpa_s); void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); +bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason, const u8 *bssid); @@ -1738,11 +1912,13 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s); void wpas_request_disconnection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen, struct wpa_bss *bss); +int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + u8 *wpa_ie, size_t wpa_ie_len, + size_t max_wpa_ie_len); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, enum wpas_mac_addr_style style, struct wpa_ssid *ssid); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); -void add_freq(int *freqs, int *num_freqs, int freq); int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, u8 *op_class, u8 *chan, u8 *phy_type); @@ -1756,6 +1932,7 @@ int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags); void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *da, const u8 *sa, const u8 *report, size_t report_len); int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, const struct wpa_ssid_value *ssid, @@ -1853,13 +2030,15 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, - struct wpa_ssid **selected_ssid); + struct wpa_ssid **selected_ssid, + bool clear_ignorelist); int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, struct channel_list_changed *info); int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss, - struct wpa_bss *seleceted); + struct wpa_bss *selected, + bool poll_current); void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s); /* eap_register.c */ @@ -1896,8 +2075,10 @@ static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode) int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr); -void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid, +void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, bool force); void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s); @@ -1953,7 +2134,13 @@ void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s); struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, - int only_first_ssid, int debug_print); + int only_first_ssid, int debug_print, + bool link); + +struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, + struct wpa_ssid *group, + struct wpa_ssid **selected_ssid, + int only_first_ssid); int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type if_type, @@ -1969,19 +2156,20 @@ int wpas_send_mscs_req(struct wpa_supplicant *wpa_s); void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av, struct wpabuf *buf); void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, const u8 *buf, - size_t len); + const u8 *dst, const u8 *src, + const u8 *buf, size_t len); void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ies, size_t ies_len); int wpas_send_scs_req(struct wpa_supplicant *wpa_s); +int wpas_scs_reconfigure(struct wpa_supplicant *wpa_s); void free_up_tclas_elem(struct scs_desc_elem *elem); void free_up_scs_desc(struct scs_robust_av_data *data); void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, const u8 *buf, - size_t len); + const u8 *dst, const u8 *src, + const u8 *buf, size_t len); void wpas_scs_deinit(struct wpa_supplicant *wpa_s); void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s, - const u8 *src, + const u8 *dst, const u8 *src, const u8 *buf, size_t len); void wpas_dscp_deinit(struct wpa_supplicant *wpa_s); int wpas_send_dscp_response(struct wpa_supplicant *wpa_s, @@ -1994,21 +2182,51 @@ int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name, int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *own_addr, const u8 *bssid, int akmp, int cipher, u16 group, int network_id, - const u8 *comeback, size_t comeback_len); + const u8 *comeback, size_t comeback_len, + unsigned int auth_alg, int group_cipher, + int group_mgmt_cipher, u16 rsn_capab, + const u8 *rsnxe_data, bool is_ml_peer); void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s); +void wpas_pasn_free_params(struct wpa_supplicant *wpa_s); int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, size_t data_len, u8 acked); int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); +int wpas_pasn_get_group(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct pasn_data *pasn); int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr, const u8 *bssid); void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s, struct pasn_auth *pasn_auth); void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status); +struct sae_pt * wpas_pasn_sae_derive_pt_for_eppke(struct wpa_ssid *ssid, + int group); bool wpas_is_6ghz_supported(struct wpa_supplicant *wpa_s, bool only_enabled); bool wpa_is_non_eht_scs_traffic_desc_supported(struct wpa_bss *bss); bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr); +bool wpas_ap_supports_rsn_overriding(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); +bool wpas_ap_supports_rsn_overriding_2(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); +int wpas_get_owe_trans_network(const u8 *owe_ie, const u8 **bssid, + const u8 **ssid, size_t *ssid_len); +void wpas_update_dfs_ap_info(struct wpa_supplicant *wpa_s, int freq, + enum chan_width ap_ch_width, + bool disconnect_evt); +void wpas_configure_frame_filters(struct wpa_supplicant *wpa_s); + +static inline bool wpas_is_nan_iface(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_NAN + return wpa_s->nan_mgmt || wpa_s->nan_data; +#else /* CONFIG_NAN */ + return false; +#endif /* CONFIG_NAN */ +} + +bool wpas_eppke_ap_capable(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, bool unauth_eppke); #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 5199f950bb47..e57114503228 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -148,6 +148,7 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, { struct wpa_supplicant *wpa_s = ctx; u8 *msg, *dst, bssid[ETH_ALEN]; + struct driver_sta_mlo_info drv_mlo; size_t msglen; int res; @@ -197,11 +198,16 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, if (is_zero_ether_addr(wpa_s->bssid)) { wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an " "EAPOL frame"); + os_memset(&drv_mlo, 0, sizeof(drv_mlo)); if (wpa_drv_get_bssid(wpa_s, bssid) == 0 && + (!wpa_s->valid_links || + wpas_drv_get_sta_mlo_info(wpa_s, &drv_mlo) == 0) && !is_zero_ether_addr(bssid)) { - dst = bssid; - wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR + dst = drv_mlo.valid_links ? drv_mlo.ap_mld_addr : bssid; + wpa_printf(MSG_DEBUG, "Using current %s " MACSTR " from the driver as the EAPOL destination", + drv_mlo.valid_links ? "AP MLD MAC address" : + "BSSID", MAC2STR(dst)); } else { dst = wpa_s->last_eapol_src; @@ -211,9 +217,10 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, MAC2STR(dst)); } } else { - /* BSSID was already set (from (Re)Assoc event, so use it as - * the EAPOL destination. */ - dst = wpa_s->bssid; + /* BSSID was already set (from (Re)Assoc event, so use BSSID or + * AP MLD MAC address (in the case of MLO connection) as the + * EAPOL destination. */ + dst = wpa_s->valid_links ? wpa_s->ap_mld_addr : wpa_s->bssid; } msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL); @@ -338,7 +345,10 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, "driver-based 4-way hs and FT"); res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN); if (res == 0) { - os_memcpy(pmk, buf + PMK_LEN, PMK_LEN); + if (wpa_key_mgmt_sha384(wpa_s->key_mgmt)) + os_memcpy(pmk, buf, pmk_len); + else + os_memcpy(pmk, buf + PMK_LEN, PMK_LEN); os_memset(buf, 0, sizeof(buf)); } #else /* CONFIG_IEEE80211R */ @@ -432,6 +442,22 @@ static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s) ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX); if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0)) ret = -1; + + ie = wpa_bss_get_vendor_ie(curr, RSNE_OVERRIDE_IE_VENDOR_TYPE); + if (wpa_sm_set_ap_rsne_override(wpa_s->wpa, ie, + ie ? 2 + ie[1] : 0)) + ret = -1; + + ie = wpa_bss_get_vendor_ie(curr, + RSNE_OVERRIDE_2_IE_VENDOR_TYPE); + if (wpa_sm_set_ap_rsne_override_2(wpa_s->wpa, ie, + ie ? 2 + ie[1] : 0)) + ret = -1; + + ie = wpa_bss_get_vendor_ie(curr, RSNXE_OVERRIDE_IE_VENDOR_TYPE); + if (wpa_sm_set_ap_rsnxe_override(wpa_s->wpa, ie, + ie ? 2 + ie[1] : 0)) + ret = -1; } else { ret = -1; } @@ -1386,11 +1412,9 @@ static void wpa_supplicant_store_ptk(void *ctx, const u8 *addr, int cipher, struct wpa_supplicant *wpa_s = ctx; ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, addr, cipher, life_time, - ptk, NULL, NULL, 0); + ptk, NULL, NULL, 0, 0); } -#endif /* CONFIG_NO_WPA */ - #ifdef CONFIG_PASN static int wpa_supplicant_set_ltf_keyseed(void *_wpa_s, const u8 *own_addr, @@ -1426,6 +1450,32 @@ static void wpa_supplicant_ssid_verified(void *_wpa_s) } +static void wpa_supplicant_sae_pw_id_change(void *_wpa_s, + struct wpabuf_array *wa) +{ + struct wpa_supplicant *wpa_s = _wpa_s; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + wpa_msg(wpa_s, MSG_INFO, "RSN: Received %u SAE Password Identifier(s)", + wa->num); + if (!ssid) { + wpabuf_array_free(wa); + return; + } + + wpabuf_array_free(ssid->alt_sae_password_ids); + ssid->alt_sae_password_ids = wa; + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, "SAE: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + +#endif /* CONFIG_NO_WPA */ + + int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_WPA @@ -1493,6 +1543,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) #endif /* CONFIG_PASN */ ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry; ctx->ssid_verified = wpa_supplicant_ssid_verified; + ctx->sae_pw_id_change = wpa_supplicant_sae_pw_id_change; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { diff --git a/contrib/wpa/wpa_supplicant/wpas_kay.c b/contrib/wpa/wpa_supplicant/wpas_kay.c index 600b3bc54527..7893a39bc387 100644 --- a/contrib/wpa/wpa_supplicant/wpas_kay.c +++ b/contrib/wpa/wpa_supplicant/wpas_kay.c @@ -249,6 +249,7 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) ssid->macsec_replay_window, ssid->macsec_offload, ssid->macsec_port, ssid->mka_priority, ssid->macsec_csindex, + ssid->macsec_icv_indicator, wpa_s->ifname, wpa_s->own_addr); /* ieee802_1x_kay_init() frees kay_ctx on failure */ if (res == NULL) diff --git a/contrib/wpa/wpa_supplicant/wpas_module_tests.c b/contrib/wpa/wpa_supplicant/wpas_module_tests.c index 9e7a57c4f134..b9140fba875f 100644 --- a/contrib/wpa/wpa_supplicant/wpas_module_tests.c +++ b/contrib/wpa/wpa_supplicant/wpas_module_tests.c @@ -113,5 +113,10 @@ int wpas_module_tests(void) if (crypto_module_tests() < 0) ret = -1; +#ifdef CONFIG_NAN + if (nan_module_tests() < 0) + ret = -1; +#endif /* CONFIG_NAN */ + return ret; } diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index 8cd355f6b3b5..25a5598ce777 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -148,7 +148,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association " "without a new scan can be used"); - bss = wpa_supplicant_pick_network(wpa_s, &ssid); + bss = wpa_supplicant_pick_network(wpa_s, &ssid, true); if (bss) { struct wpabuf *wps; struct wps_parse_attr attr; @@ -226,7 +226,7 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table"); - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + ie = wpa_bss_get_rsne(wpa_s, bss, ssid, false); if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) { wpa2 = 1; if (adv.pairwise_cipher & WPA_CIPHER_CCMP) @@ -1375,7 +1375,7 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); wpa_supplicant_cancel_scan(wpa_s); wpas_clear_wps(wpa_s); - } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { + } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " "deauthenticate"); wpa_s->own_disconnect_req = 1; @@ -1709,6 +1709,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS_ER */ wps_registrar_deinit(wpa_s->wps->registrar); + dh5_free(wpa_s->wps->dh_ctx); wpabuf_free(wpa_s->wps->dh_pubkey); wpabuf_free(wpa_s->wps->dh_privkey); wpabuf_free(wpa_s->wps->dev.vendor_ext_m1); diff --git a/usr.sbin/wpa/Makefile.inc b/usr.sbin/wpa/Makefile.inc index b332ead24556..d470d12ac526 100644 --- a/usr.sbin/wpa/Makefile.inc +++ b/usr.sbin/wpa/Makefile.inc @@ -35,6 +35,8 @@ CFLAGS+=-DCONFIG_DRIVER_RADIUS_ACL CFLAGS+=-DCONFIG_CTRL_IFACE CFLAGS+=-DCONFIG_CTRL_IFACE_UNIX CFLAGS+=-DCONFIG_IEEE80211AC +CFLAGS+=-DCONFIG_IEEE80211AX +CFLAGS+=-DCONFIG_IEEE80211BE CFLAGS+=-DCONFIG_IEEE80211N CFLAGS+=-DCONFIG_IEEE80211R CFLAGS+=-DCONFIG_IEEE80211W @@ -65,6 +67,15 @@ CFLAGS+=-DCONFIG_WNM CFLAGS+=-DCONFIG_MBO CFLAGS+=-DCONFIG_RSN_PREAUTH CFLAGS+=-DCONFIG_WEP +CFLAGS+=-DCONFIG_LIBWPA +CFLAGS+=-DCONFIG_SAE +CFLAGS+=-DNEED_AP_MLME + +CFLAGS+=-Wno-incompatible-function-pointer-types +CFLAGS+=-Wno-undefined-internal + +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y .if ${MK_WPA_SUPPLICANT_EAPOL} != "no" CFLAGS+=-DCONFIG_HS20 \ diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile index 958ba901bd6b..2145033cf893 100644 --- a/usr.sbin/wpa/hostapd/Makefile +++ b/usr.sbin/wpa/hostapd/Makefile @@ -24,7 +24,10 @@ FILES= hostapd.conf hostapd.eap_user hostapd.wpa_psk CFLAGS+=-I${.CURDIR:H}/wpa_supplicant \ -I${WPA_DISTDIR}/src/eap_peer \ - -DHOSTAPD + -DHOSTAPD \ + -DCONFIG_L2_FREEBSD \ + -DCONFIG_DNET_PCAP + .if ${MK_INET6} != "no" CFLAGS+= -DCONFIG_IPV6 .endif @@ -41,10 +44,6 @@ CFLAGS+=${HOSTAPD_CFLAGS} LDADD+=${HOSTAPD_LDADD} #LDFLAGS+=${HOSTAPD_LDFLAGS} -# For WPS, EAP modes, etc -NEED_DH_GROUPS=y -NEED_DH_GROUPS_ALL=y - .if !empty(CFLAGS:M*-DCONFIG_WPS) NEED_SIM_COMMON=y .endif diff --git a/usr.sbin/wpa/src/ap/Makefile b/usr.sbin/wpa/src/ap/Makefile index 801f45fca8a6..222ad325c903 100644 --- a/usr.sbin/wpa/src/ap/Makefile +++ b/usr.sbin/wpa/src/ap/Makefile @@ -12,20 +12,27 @@ INTERNALLIB= SRCS= accounting.c \ ap_config.c \ ap_drv_ops.c \ + ap_list.c \ ap_mlme.c \ authsrv.c \ beacon.c \ bss_load.c \ + comeback_token.c \ ctrl_iface_ap.c \ dfs.c \ drv_callbacks.c \ eap_user_db.c \ hostapd.c \ + hw_features.c \ + ieee802_11.c \ ieee802_11_auth.c \ + ieee802_11_eht.c \ + ieee802_11_he.c \ ieee802_11_ht.c \ ieee802_11_shared.c \ ieee802_11_vht.c \ ieee802_1x.c \ + interference.c \ mbo_ap.c \ neighbor_db.c \ pmksa_cache_auth.c \ diff --git a/usr.sbin/wpa/src/common/Makefile b/usr.sbin/wpa/src/common/Makefile index ed27c3eb6bf4..0a2f59574dc5 100644 --- a/usr.sbin/wpa/src/common/Makefile +++ b/usr.sbin/wpa/src/common/Makefile @@ -17,6 +17,7 @@ SRCS= cli.c \ hw_features_common.c \ ieee802_11_common.c \ ptksa_cache.c \ + sae.c \ wpa_common.c \ wpa_ctrl.c diff --git a/usr.sbin/wpa/src/crypto/Makefile b/usr.sbin/wpa/src/crypto/Makefile index d71b740bb816..1777a7abf62f 100644 --- a/usr.sbin/wpa/src/crypto/Makefile +++ b/usr.sbin/wpa/src/crypto/Makefile @@ -10,9 +10,11 @@ INTERNALLIB= .PATH: ${WPA_DISTDIR}/src/crypto .if ${MK_OPENSSL} != "no" -SRCS= crypto_openssl.c \ +SRCS= aes-siv.c \ + crypto_openssl.c \ random.c \ sha1-prf.c \ + sha256-kdf.c \ sha256-prf.c \ sha256-tlsprf.c \ sha384-prf.c \ @@ -40,7 +42,6 @@ SRCS+= aes-unwrap.c aes-wrap.c \ aes-internal.c \ aes-internal-dec.c \ aes-internal-enc.c -.else .endif .if defined(NEED_AES_CBC) diff --git a/usr.sbin/wpa/src/pasn/Makefile b/usr.sbin/wpa/src/pasn/Makefile index 129884431002..ea19b7f8baa2 100644 --- a/usr.sbin/wpa/src/pasn/Makefile +++ b/usr.sbin/wpa/src/pasn/Makefile @@ -10,7 +10,8 @@ INTERNALLIB= .PATH: ${WPA_DISTDIR}/src/pasn SRCS= pasn_common.c \ - pasn_initiator.c + pasn_initiator.c \ + pasn_responder.c CFLAGS+= -I${WPA_DISTDIR}/src/pasn -I${WPA_DISTDIR}/src