Merge tag 'android-15.0.0_r6' of https://android.googlesource.com/platform/external/wpa_supplicant_8 into HEAD
Android 15.0.0 Release 6 (AP4A.241205.013)
Change-Id: I2e1fceb006b532c1cebb2fb411134cb68382e19e
# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ1IssgAKCRDorT+BmrEO
# eJfRAJ0Vuu43aK4NR5MTPdnmvvvRAdJBrACgj2KHABGJcXvySOtYQQUIz5J/cPQ=
# =s77y
# -----END PGP SIGNATURE-----
# gpg: Signature faite le jeu 05 déc 2024 17:44:02 EST
# gpg: avec la clef DSA 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Impossible de vérifier la signature : Pas de clef publique
diff --git a/README b/README
index 1470c4f..8392bb3 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 8a7dacf..8b79dd5 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -44,7 +44,7 @@
defaults: ["hostapd_cflags_defaults"],
srcs: [":hostapd_srcs"],
shared_libs: [
- "android.hardware.wifi.hostapd-V2-ndk",
+ "android.hardware.wifi.hostapd-V3-ndk",
"libbase",
"libutils",
"libbinder_ndk",
@@ -66,37 +66,8 @@
],
}
-soong_config_module_type {
- name: "hostapd_cflags_cc_defaults",
- module_type: "cc_defaults",
- config_namespace: "hostapd",
- value_variables: [
- "platform_version",
- "nl80211_driver",
- ],
- properties: ["cflags"],
-}
-
-hostapd_cflags_cc_defaults {
+cc_defaults {
name: "hostapd_cflags_defaults",
- soong_config_variables: {
- // Devices that include this module should set these soong config vars appropriately.
- // For example:
- // $(call add_soong_config_namespace, wpa_supplicant)
- // $(call add_soong_config_var_value, wpa_supplicant, platform_version, $(PLATFORM_VERSION))
- // $(call add_soong_config_var_value, wpa_supplicant, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
- platform_version: {
- cflags: ["-DVERSION_STR_POSTFIX=\"-%s\""],
- conditions_default: {
- // Default value
- cflags: ["-DVERSION_STR_POSTFIX=\"-Android\""],
- },
- },
- nl80211_driver: {
- cflags: ["-D%s"],
- // Flag is optional, so no default value provided.
- },
- },
// Generated by building hostapd and printing LOCAL_CFLAGS.
cflags: [
"-DWPA_IGNORE_CONFIG_ERRORS",
@@ -166,7 +137,21 @@
"-Wno-unused-parameter",
"-Wno-unused-variable",
"-Wno-macro-redefined",
- ],
+ ] +
+ // Devices that include this module should set these soong config vars appropriately.
+ // For example:
+ // $(call soong_config_set, hostapd, platform_version, $(PLATFORM_VERSION))
+ // $(call soong_config_set, hostapd, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
+ select(soong_config_variable("hostapd", "platform_version"), {
+ any @ version: ["-DVERSION_STR_POSTFIX=\"-" + version + "\""],
+ default: ["-DVERSION_STR_POSTFIX=\"-Android\""],
+ }) +
+ select(soong_config_variable("hostapd", "nl80211_driver"), {
+ any @ driver: ["-D" + driver],
+ // Flag is optional, so no default value provided.
+ default: [],
+ }),
+
// Similar to suppressing clang compiler warnings, here we
// suppress clang-tidy warnings to reduce noises in Android build.log.
tidy_checks: [
@@ -239,6 +224,7 @@
"src/common/ieee802_11_common.c",
"src/common/wpa_common.c",
"src/common/hw_features_common.c",
+ "src/common/ptksa_cache.c",
"src/eapol_auth/eapol_auth_sm.c",
"src/eapol_auth/eapol_auth_dump.c",
"src/ap/vlan_init.c",
@@ -330,6 +316,7 @@
"src/ap/ap_list.c",
"src/ap/comeback_token.c",
"src/pasn/pasn_responder.c",
+ "src/pasn/pasn_common.c",
"src/ap/ieee802_11.c",
"src/ap/hw_features.c",
"src/ap/dfs.c",
@@ -348,9 +335,10 @@
}
// This manifest can used by a vendor apex module for hostapd as well.
-filegroup {
+vintf_fragment {
name: "android.hardware.wifi.hostapd.xml",
- srcs: ["android.hardware.wifi.hostapd.xml"],
+ src: "android.hardware.wifi.hostapd.xml",
+ soc_specific: true,
}
// Generated by building hostapd_cli and printing LOCAL_SRC_FILES
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 35ada83..d5d1190 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -170,6 +170,7 @@
OBJS += src/common/ieee802_11_common.c
OBJS += src/common/wpa_common.c
OBJS += src/common/hw_features_common.c
+OBJS += src/common/ptksa_cache.c
OBJS += src/eapol_auth/eapol_auth_sm.c
@@ -253,6 +254,8 @@
OBJS += src/common/ocv.c
endif
+NEED_AES_UNWRAP=y
+
ifdef CONFIG_IEEE80211R
L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
OBJS += src/ap/wpa_auth_ft.c
@@ -599,6 +602,12 @@
endif
endif
+ifdef CONFIG_NAN_USD
+OBJS += src/common/nan_de.c
+OBJS += src/ap/nan_usd_ap.c
+L_CFLAGS += -DCONFIG_NAN_USD
+endif
+
ifdef CONFIG_PASN
L_CFLAGS += -DCONFIG_PASN
L_CFLAGS += -DCONFIG_PTKSA_CACHE
@@ -606,7 +615,6 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
endif
ifdef CONFIG_EAP_IKEV2
@@ -659,6 +667,11 @@
OBJS += src/eap_common/chap.c
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+L_CFLAGS += -DCONFIG_RADIUS_TLS
+endif
+
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
@@ -1058,6 +1071,7 @@
OBJS += src/ap/ap_list.c
OBJS += src/ap/comeback_token.c
OBJS += src/pasn/pasn_responder.c
+OBJS += src/pasn/pasn_common.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
@@ -1178,7 +1192,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_VINTF_FRAGMENTS := android.hardware.wifi.hostapd.xml
+LOCAL_REQUIRED_MODULES += android.hardware.wifi.hostapd.xml
ifdef CONFIG_DRIVER_CUSTOM
LOCAL_STATIC_LIBRARIES := libCustomWifi
endif
@@ -1194,7 +1208,7 @@
endif
endif
ifeq ($(HOSTAPD_USE_AIDL), y)
-LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd-V2-ndk
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd-V3-ndk
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.common-V1-ndk
LOCAL_SHARED_LIBRARIES += libbase libutils
LOCAL_SHARED_LIBRARIES += libbinder_ndk
@@ -1249,7 +1263,7 @@
aidl/aidl.cpp \
aidl/hostapd.cpp
LOCAL_SHARED_LIBRARIES := \
- android.hardware.wifi.hostapd-V2-ndk \
+ android.hardware.wifi.hostapd-V3-ndk \
android.hardware.wifi.common-V1-ndk \
libbinder_ndk \
libbase \
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 279298e..1c8240d 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,42 @@
ChangeLog for hostapd
+2024-07-20 - v2.11
+ * Wi-Fi Easy Connect
+ - add support for DPP release 3
+ - allow Configurator parameters to be provided during config exchange
+ * HE/IEEE 802.11ax/Wi-Fi 6
+ - various fixes
+ * EHT/IEEE 802.11be/Wi-Fi 7
+ - add preliminary support
+ * SAE: add support for fetching the password from a RADIUS server
+ * support OpenSSL 3.0 API changes
+ * support background radar detection and CAC with some additional
+ drivers
+ * support RADIUS ACL/PSK check during 4-way handshake (wpa_psk_radius=3)
+ * EAP-SIM/AKA: support IMSI privacy
+ * improve 4-way handshake operations
+ - use Secure=1 in message 3 during PTK rekeying
+ * OCV: do not check Frequency Segment 1 Channel Number for 160 MHz cases
+ to avoid interoperability issues
+ * support new SAE AKM suites with variable length keys
+ * support new AKM for 802.1X/EAP with SHA384
+ * extend PASN support for secure ranging
+ * FT: Use SHA256 to derive PMKID for AKM 00-0F-AC:3 (FT-EAP)
+ - this is based on additional details being added in the IEEE 802.11
+ standard
+ - the new implementation is not backwards compatible
+ * improved ACS to cover additional channel types/bandwidths
+ * extended Multiple BSSID support
+ * fix beacon protection with FT protocol (incorrect BIGTK was provided)
+ * support unsynchronized service discovery (USD)
+ * add preliminary support for RADIUS/TLS
+ * add support for explicit SSID protection in 4-way handshake
+ (a mitigation for CVE-2023-52424; disabled by default for now, can be
+ enabled with ssid_protection=1)
+ * fix SAE H2E rejected groups validation to avoid downgrade attacks
+ * use stricter validation for some RADIUS messages
+ * a large number of other fixes, cleanup, and extensions
+
2022-01-16 - v2.10
* SAE changes
- improved protection against side channel attacks
diff --git a/hostapd/Makefile b/hostapd/Makefile
index a2adc85..489922c 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -84,6 +84,7 @@
OBJS += ../src/ap/bss_load.o
OBJS += ../src/ap/neighbor_db.o
OBJS += ../src/ap/rrm.o
+OBJS += ../src/common/ptksa_cache.o
OBJS_c = hostapd_cli.o
OBJS_c += ../src/common/wpa_ctrl.o
@@ -167,7 +168,7 @@
ifdef CONFIG_CODE_COVERAGE
-CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
LIBS += -lgcov
LIBS_c += -lgcov
LIBS_h += -lgcov
@@ -276,6 +277,8 @@
OBJS += ../src/common/ocv.o
endif
+NEED_AES_UNWRAP=y
+
ifdef CONFIG_IEEE80211R
CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
OBJS += ../src/ap/wpa_auth_ft.o
@@ -609,6 +612,12 @@
endif
endif
+ifdef CONFIG_NAN_USD
+OBJS += ../src/common/nan_de.o
+OBJS += ../src/ap/nan_usd_ap.o
+CFLAGS += -DCONFIG_NAN_USD
+endif
+
ifdef CONFIG_PASN
CFLAGS += -DCONFIG_PASN
CFLAGS += -DCONFIG_PTKSA_CACHE
@@ -616,7 +625,6 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
endif
ifdef CONFIG_EAP_IKEV2
@@ -678,6 +686,11 @@
OBJS += ../src/eap_common/chap.o
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
+
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
@@ -1188,6 +1201,7 @@
OBJS += ../src/ap/ap_list.o
OBJS += ../src/ap/comeback_token.o
OBJS += ../src/pasn/pasn_responder.o
+OBJS += ../src/pasn/pasn_common.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
diff --git a/hostapd/README b/hostapd/README
index 739c964..1a0248f 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
Authenticator and RADIUS authentication server
================================================================
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
diff --git a/hostapd/aidl/Android.bp b/hostapd/aidl/Android.bp
index 410e701..2c10d78 100644
--- a/hostapd/aidl/Android.bp
+++ b/hostapd/aidl/Android.bp
@@ -31,7 +31,7 @@
defaults: ["hostapd_cflags_defaults"],
soc_specific: true,
shared_libs: [
- "android.hardware.wifi.hostapd-V2-ndk",
+ "android.hardware.wifi.hostapd-V3-ndk",
"libbinder_ndk",
"libbase",
"libutils",
diff --git a/hostapd/android.config b/hostapd/android.config
index 1c4dab2..4040d41 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -126,6 +126,9 @@
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
+# Include support fo RADIUS/TLS into the RADIUS client
+#CONFIG_RADIUS_TLS=y
+
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y
@@ -264,3 +267,6 @@
# certain percentage of probe requests or auth/(re)assoc frames.
# Each test case requires a flag set in hostapd.conf or through hostapd_cli
#CONFIG_TESTING_OPTIONS=y
+
+# Wi-Fi Aware unsynchronized service discovery (NAN USD)
+#CONFIG_NAN_USD=y
diff --git a/hostapd/android.hardware.wifi.hostapd.xml b/hostapd/android.hardware.wifi.hostapd.xml
index c0fe942..fc49253 100644
--- a/hostapd/android.hardware.wifi.hostapd.xml
+++ b/hostapd/android.hardware.wifi.hostapd.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.wifi.hostapd</name>
- <version>2</version>
+ <version>3</version>
<fqname>IHostapd/default</fqname>
</hal>
</manifest>
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index bfb152e..1d2bdb8 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration file parser
- * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -1020,6 +1020,78 @@
return 0;
}
+
+
+int hostapd_config_read_rxkh_file(struct hostapd_bss_config *conf,
+ const char *fname)
+{
+ FILE *f;
+ char buf[256], *pos;
+ int line = 0, errors = 0;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR, "rxkh file '%s' not found.", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ pos = os_strchr(buf, '=');
+ if (!pos) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid line '%s'",
+ line, buf);
+ errors++;
+ continue;
+ }
+ *pos = '\0';
+ pos++;
+
+ if (os_strcmp(buf, "r0kh") == 0) {
+ if (add_r0kh(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid r0kh '%s'",
+ line, pos);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "r1kh") == 0) {
+ if (add_r1kh(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid r1kh '%s'",
+ line, pos);
+ errors++;
+ }
+ }
+ }
+
+ fclose(f);
+
+ if (errors) {
+ wpa_printf(MSG_ERROR,
+ "%d errors in configuring RxKHs from '%s'",
+ errors, fname);
+ return -1;
+ }
+ return 0;
+}
+
#endif /* CONFIG_IEEE80211R_AP */
@@ -1606,6 +1678,8 @@
return 0;
}
+#endif /* CONFIG_INTERWORKING */
+
static int parse_qos_map_set(struct hostapd_bss_config *bss,
char *buf, int line)
@@ -1647,8 +1721,6 @@
return 0;
}
-#endif /* CONFIG_INTERWORKING */
-
#ifdef CONFIG_HS20
static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
@@ -2159,6 +2231,7 @@
#ifdef CONFIG_SAE
+
static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
{
struct sae_password_entry *pw;
@@ -2262,6 +2335,40 @@
os_free(pw);
return -1;
}
+
+
+static int parse_sae_password_file(struct hostapd_bss_config *bss,
+ const char *fname)
+{
+ FILE *f;
+ char buf[500], *pos;
+ unsigned int line = 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR, "sae_password_file '%s' not found.",
+ fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = os_strchr(buf, '\n');
+ if (pos)
+ *pos = '\0';
+ line++;
+ if (parse_sae_password(bss, buf)) {
+ wpa_printf(MSG_ERROR,
+ "Invalid SAE password at line %d in '%s'",
+ line, fname);
+ fclose(f);
+ return -1;
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
#endif /* CONFIG_SAE */
@@ -2311,6 +2418,24 @@
}
+#ifdef CONFIG_IEEE80211BE
+static int get_u16(const char *pos, int line, u16 *ret_val)
+{
+ char *end;
+ long int val = strtol(pos, &end, 0);
+
+ if (*end || val < 0 || val > 0xffff) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid value '%s'",
+ line, pos);
+ return -1;
+ }
+
+ *ret_val = val;
+ return 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
const char *buf, char *pos, int line)
@@ -2424,6 +2549,27 @@
bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
bss->skip_inactivity_poll = atoi(pos);
+ } else if (os_strcmp(buf, "bss_max_idle") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid bss_max_idle value", line);
+ return 1;
+ }
+ bss->bss_max_idle = val;
+ } else if (os_strcmp(buf, "max_acceptable_idle_period") == 0) {
+ bss->max_acceptable_idle_period = atoi(pos);
+ } else if (os_strcmp(buf, "no_disconnect_on_group_keyerror") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid no_disconnect_on_group_keyerror",
+ line);
+ return 1;
+ }
+ bss->no_disconnect_on_group_keyerror = val;
} else if (os_strcmp(buf, "config_id") == 0) {
os_free(bss->config_id);
bss->config_id = os_strdup(pos);
@@ -2744,6 +2890,37 @@
os_free(bss->radius->auth_server->shared_secret);
bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->auth_server->shared_secret_len = len;
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_type") == 0) {
+ if (os_strcmp(pos, "UDP") == 0) {
+ bss->radius->auth_server->tls = false;
+#ifdef CONFIG_RADIUS_TLS
+ } else if (os_strcmp(pos, "TLS") == 0) {
+ bss->radius->auth_server->tls = true;
+#endif /* CONFIG_RADIUS_TLS */
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
+ line, pos);
+ return 1;
+ }
+#ifdef CONFIG_RADIUS_TLS
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_ca_cert") == 0) {
+ os_free(bss->radius->auth_server->ca_cert);
+ bss->radius->auth_server->ca_cert = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_client_cert") == 0) {
+ os_free(bss->radius->auth_server->client_cert);
+ bss->radius->auth_server->client_cert = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_private_key") == 0) {
+ os_free(bss->radius->auth_server->private_key);
+ bss->radius->auth_server->private_key = os_strdup(pos);
+ } else if (bss->radius->auth_server &&
+ os_strcmp(buf, "auth_server_private_key_passwd") == 0) {
+ os_free(bss->radius->auth_server->private_key_passwd);
+ bss->radius->auth_server->private_key_passwd = os_strdup(pos);
+#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "acct_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->acct_servers,
@@ -2778,8 +2955,42 @@
os_free(bss->radius->acct_server->shared_secret);
bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->acct_server->shared_secret_len = len;
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_type") == 0) {
+ if (os_strcmp(pos, "UDP") == 0) {
+ bss->radius->acct_server->tls = false;
+#ifdef CONFIG_RADIUS_TLS
+ } else if (os_strcmp(pos, "TLS") == 0) {
+ bss->radius->acct_server->tls = true;
+#endif /* CONFIG_RADIUS_TLS */
+ } else {
+ wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
+ line, pos);
+ return 1;
+ }
+#ifdef CONFIG_RADIUS_TLS
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_ca_cert") == 0) {
+ os_free(bss->radius->acct_server->ca_cert);
+ bss->radius->acct_server->ca_cert = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_client_cert") == 0) {
+ os_free(bss->radius->acct_server->client_cert);
+ bss->radius->acct_server->client_cert = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_private_key") == 0) {
+ os_free(bss->radius->acct_server->private_key);
+ bss->radius->acct_server->private_key = os_strdup(pos);
+ } else if (bss->radius->acct_server &&
+ os_strcmp(buf, "acct_server_private_key_passwd") == 0) {
+ os_free(bss->radius->acct_server->private_key_passwd);
+ bss->radius->acct_server->private_key_passwd = os_strdup(pos);
+#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
bss->radius->retry_primary_interval = atoi(pos);
+ } else if (os_strcmp(buf,
+ "radius_require_message_authenticator") == 0) {
+ bss->radius_require_message_authenticator = atoi(pos);
} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
bss->acct_interim_interval = atoi(pos);
} else if (os_strcmp(buf, "radius_request_cui") == 0) {
@@ -2945,6 +3156,16 @@
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 &&
@@ -2976,6 +3197,32 @@
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)
@@ -3047,6 +3294,21 @@
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "rxkh_file") == 0) {
+ os_free(bss->rxkh_file);
+ bss->rxkh_file = os_strdup(pos);
+ if (!bss->rxkh_file) {
+ wpa_printf(MSG_ERROR, "Line %d: allocation failed",
+ line);
+ return 1;
+ }
+ if (hostapd_config_read_rxkh_file(bss, pos)) {
+ wpa_printf(MSG_DEBUG,
+ "Line %d: failed to read rxkh_file '%s'",
+ line, pos);
+ /* Allow the file to be created later and read into
+ * already operating AP context. */
+ }
} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
bss->pmk_r1_push = atoi(pos);
} else if (os_strcmp(buf, "ft_over_ds") == 0) {
@@ -3416,6 +3678,10 @@
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, "group_mgmt_cipher") == 0) {
if (os_strcmp(pos, "AES-128-CMAC") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
@@ -3462,6 +3728,8 @@
}
} else if (os_strcmp(buf, "require_ht") == 0) {
conf->require_ht = atoi(pos);
+ } else if (os_strcmp(buf, "ht_vht_twt_responder") == 0) {
+ conf->ht_vht_twt_responder = atoi(pos);
} else if (os_strcmp(buf, "obss_interval") == 0) {
conf->obss_interval = atoi(pos);
#ifdef CONFIG_IEEE80211AC
@@ -3624,6 +3892,18 @@
}
} else if (os_strcmp(buf, "he_6ghz_reg_pwr_type") == 0) {
conf->he_6ghz_reg_pwr_type = atoi(pos);
+ if (conf->he_6ghz_reg_pwr_type > HE_REG_INFO_6GHZ_AP_TYPE_MAX) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid he_6ghz_reg_pwr_type value",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "reg_def_cli_eirp_psd") == 0) {
+ conf->reg_def_cli_eirp_psd = atoi(pos);
+ } else if (os_strcmp(buf, "reg_sub_cli_eirp_psd") == 0) {
+ conf->reg_sub_cli_eirp_psd = atoi(pos);
+ } else if (os_strcmp(buf, "reg_def_cli_eirp") == 0) {
+ conf->reg_def_cli_eirp = atoi(pos);
} else if (os_strcmp(buf, "he_oper_chwidth") == 0) {
conf->he_oper_chwidth = atoi(pos);
} else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) {
@@ -4044,10 +4324,10 @@
bss->gas_frag_limit = val;
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
bss->gas_comeback_delay = atoi(pos);
+#endif /* CONFIG_INTERWORKING */
} else if (os_strcmp(buf, "qos_map_set") == 0) {
if (parse_qos_map_set(bss, pos, line) < 0)
return 1;
-#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_RADIUS_TEST
} else if (os_strcmp(buf, "dump_msk_file") == 0) {
os_free(bss->dump_msk_file);
@@ -4296,6 +4576,19 @@
bss->eap_skip_prot_success = atoi(pos);
} else if (os_strcmp(buf, "delay_eapol_tx") == 0) {
conf->delay_eapol_tx = atoi(pos);
+ } else if (os_strcmp(buf, "eapol_m1_elements") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->eapol_m1_elements, pos))
+ return 1;
+ } else if (os_strcmp(buf, "eapol_m3_elements") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->eapol_m3_elements, pos))
+ return 1;
+ } else if (os_strcmp(buf, "eapol_m3_no_encrypt") == 0) {
+ bss->eapol_m3_no_encrypt = 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) {
+ if (parse_wpabuf_hex(line, buf, &bss->presp_elements, pos))
+ return 1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -4304,6 +4597,13 @@
line);
return 1;
}
+ } else if (os_strcmp(buf, "sae_password_file") == 0) {
+ if (parse_sae_password_file(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid sae_password in file",
+ line);
+ return 1;
+ }
#endif /* CONFIG_SAE */
} else if (os_strcmp(buf, "vendor_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
@@ -4435,6 +4735,10 @@
WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+ } else if (os_strcmp(buf, "rrm_link_measurement_report") == 0) {
+ if (atoi(pos))
+ bss->radio_measurements[0] |=
+ WLAN_RRM_CAPS_LINK_MEASUREMENT;
} else if (os_strcmp(buf, "gas_address3") == 0) {
bss->gas_address3 = atoi(pos);
} else if (os_strcmp(buf, "stationary_ap") == 0) {
@@ -4575,6 +4879,36 @@
}
bss->multi_ap = val;
+ } else if (os_strcmp(buf, "multi_ap_profile") == 0) {
+ int val = atoi(pos);
+
+ if (val < MULTI_AP_PROFILE_1 || val > MULTI_AP_PROFILE_MAX) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_profile '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_profile = val;
+ } else if (os_strcmp(buf, "multi_ap_client_disallow") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 3) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_client_allow '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_client_disallow = val;
+ } else if (os_strcmp(buf, "multi_ap_vlanid") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > MAX_VLAN_ID) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid multi_ap_vlan_id '%s'",
+ line, buf);
+ return -1;
+ }
+ bss->multi_ap_vlanid = val;
} else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) {
conf->rssi_reject_assoc_rssi = atoi(pos);
} else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
@@ -4757,6 +5091,12 @@
return 1;
} else if (os_strcmp(buf, "rnr") == 0) {
bss->rnr = atoi(pos);
+ } else if (os_strcmp(buf, "ssid_protection") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 1)
+ return 1;
+ bss->ssid_protection = val;
#ifdef CONFIG_IEEE80211BE
} else if (os_strcmp(buf, "ieee80211be") == 0) {
conf->ieee80211be = atoi(pos);
@@ -4770,8 +5110,11 @@
conf->eht_phy_capab.su_beamformee = atoi(pos);
} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
conf->eht_phy_capab.mu_beamformer = atoi(pos);
+ } else if (os_strcmp(buf, "eht_default_pe_duration") == 0) {
+ conf->eht_default_pe_duration = atoi(pos);
} else if (os_strcmp(buf, "punct_bitmap") == 0) {
- conf->punct_bitmap = atoi(pos);
+ if (get_u16(pos, line, &conf->punct_bitmap))
+ return 1;
} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
int val = atoi(pos);
@@ -4784,14 +5127,21 @@
conf->punct_acs_threshold = val;
} else if (os_strcmp(buf, "mld_ap") == 0) {
bss->mld_ap = !!atoi(pos);
- } else if (os_strcmp(buf, "mld_id") == 0) {
- bss->mld_id = atoi(pos);
} else if (os_strcmp(buf, "mld_addr") == 0) {
if (hwaddr_aton(pos, bss->mld_addr)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
line);
return 1;
}
+ } else if (os_strcmp(buf, "eht_bw320_offset") == 0) {
+ conf->eht_bw320_offset = atoi(pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcmp(buf, "eht_oper_puncturing_override") == 0) {
+ if (get_u16(pos, line, &bss->eht_oper_puncturing_override))
+ return 1;
+ } else if (os_strcmp(buf, "mld_indicate_disabled") == 0) {
+ bss->mld_indicate_disabled = atoi(pos);
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
} else {
wpa_printf(MSG_ERROR,
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index c98bdb6..9ef6ac8 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -10,6 +10,8 @@
#define CONFIG_FILE_H
struct hostapd_config * hostapd_config_read(const char *fname);
+int hostapd_config_read_rxkh_file(struct hostapd_bss_config *conf,
+ const char *fname);
int hostapd_set_iface(struct hostapd_config *conf,
struct hostapd_bss_config *bss, const char *field,
char *value);
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 57c9997..8e2b8bd 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -39,6 +39,7 @@
#include "common/wpa_ctrl.h"
#include "common/ptksa_cache.h"
#include "common/hw_features_common.h"
+#include "common/nan_de.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
@@ -63,6 +64,7 @@
#include "ap/rrm.h"
#include "ap/dpp_hostapd.h"
#include "ap/dfs.h"
+#include "ap/nan_usd_ap.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
#include "fst/fst_ctrl_iface.h"
@@ -1307,6 +1309,8 @@
hostapd_disassoc_deny_mac(hapd);
} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
hostapd_disassoc_accept_mac(hapd);
+ } else if (os_strcasecmp(cmd, "ssid") == 0) {
+ 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++;
@@ -1435,7 +1439,7 @@
pmk_match = PMK_LEN == pmk_len &&
os_memcmp(psk->psk, pmk, pmk_len) == 0;
sta_match = psk->group == 0 &&
- os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0;
+ ether_addr_equal(sta->addr, psk->addr);
bss_match = psk->group == 1;
if (pmk_match && (sta_match || bss_match))
@@ -1474,6 +1478,79 @@
}
+#ifdef CONFIG_IEEE80211R_AP
+
+static int hostapd_ctrl_iface_get_rxkhs(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ int ret, start_pos;
+ char *pos, *end;
+ struct ft_remote_r0kh *r0kh;
+ struct ft_remote_r1kh *r1kh;
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ pos = buf;
+ end = buf + buflen;
+
+ for (r0kh = conf->r0kh_list; r0kh; r0kh=r0kh->next) {
+ start_pos = pos - buf;
+ ret = os_snprintf(pos, end - pos, "r0kh=" MACSTR " ",
+ MAC2STR(r0kh->addr));
+ if (os_snprintf_error(end - pos, ret))
+ return start_pos;
+ pos += ret;
+ if (r0kh->id_len + 1 >= (size_t) (end - pos))
+ return start_pos;
+ os_memcpy(pos, r0kh->id, r0kh->id_len);
+ pos += r0kh->id_len;
+ *pos++ = ' ';
+ pos += wpa_snprintf_hex(pos, end - pos, r0kh->key,
+ sizeof(r0kh->key));
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return start_pos;
+ pos += ret;
+ }
+
+ for (r1kh = conf->r1kh_list; r1kh; r1kh=r1kh->next) {
+ start_pos = pos - buf;
+ ret = os_snprintf(pos, end - pos, "r1kh=" MACSTR " " MACSTR " ",
+ MAC2STR(r1kh->addr), MAC2STR(r1kh->id));
+ if (os_snprintf_error(end - pos, ret))
+ return start_pos;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, r1kh->key,
+ sizeof(r1kh->key));
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return start_pos;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int hostapd_ctrl_iface_reload_rxkhs(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ int err;
+
+ hostapd_config_clear_rxkhs(conf);
+
+ err = hostapd_config_read_rxkh_file(conf, conf->rxkh_file);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "Reloading RxKHs failed: %d",
+ err);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
#ifdef CONFIG_TESTING_OPTIONS
static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
@@ -1871,7 +1948,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap)
- addr = hapd->mld_addr;
+ addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
hapd->l2_test = l2_packet_init(ifname, addr,
ETHERTYPE_IP, hostapd_data_test_rx,
@@ -2010,74 +2087,6 @@
}
-static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
-{
-#ifdef WPA_TRACE_BFD
- char *pos;
-
- wpa_trace_fail_after = atoi(cmd);
- pos = os_strchr(cmd, ':');
- if (pos) {
- pos++;
- os_strlcpy(wpa_trace_fail_func, pos,
- sizeof(wpa_trace_fail_func));
- } else {
- wpa_trace_fail_after = 0;
- }
-
- return 0;
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
- char *buf, size_t buflen)
-{
-#ifdef WPA_TRACE_BFD
- return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
- wpa_trace_fail_func);
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
-{
-#ifdef WPA_TRACE_BFD
- char *pos;
-
- wpa_trace_test_fail_after = atoi(cmd);
- pos = os_strchr(cmd, ':');
- if (pos) {
- pos++;
- os_strlcpy(wpa_trace_test_fail_func, pos,
- sizeof(wpa_trace_test_fail_func));
- } else {
- wpa_trace_test_fail_after = 0;
- }
-
- return 0;
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
- char *buf, size_t buflen)
-{
-#ifdef WPA_TRACE_BFD
- return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
- wpa_trace_test_fail_func);
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
{
struct sta_info *sta;
@@ -2472,6 +2481,21 @@
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) {
@@ -2635,11 +2659,19 @@
unsigned int i;
int bandwidth;
u8 chan;
+ unsigned int num_err = 0;
+ int err = 0;
ret = hostapd_parse_csa_settings(pos, &settings);
if (ret)
return ret;
+ settings.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ 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) {
@@ -2702,6 +2734,7 @@
settings.freq_params.center_freq1);
/* Perform CAC and switch channel */
+ iface->is_ch_switch_dfs = true;
hostapd_switch_channel_fallback(iface, &settings.freq_params);
return 0;
}
@@ -2712,19 +2745,111 @@
hostapd_chan_switch_config(iface->bss[i],
&settings.freq_params);
- ret = hostapd_switch_channel(iface->bss[i], &settings);
- if (ret) {
- /* FIX: What do we do if CSA fails in the middle of
- * submitting multi-BSS CSA requests? */
- return ret;
+ err = hostapd_switch_channel(iface->bss[i], &settings);
+ if (err) {
+ ret = err;
+ num_err++;
}
}
+ return (iface->num_bss == num_err) ? ret : 0;
+#else /* NEED_AP_MLME */
+ return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface,
+ const char *pos)
+{
+#ifdef NEED_AP_MLME
+ struct cca_settings settings;
+ struct hostapd_data *hapd = iface->bss[0];
+ int ret, color;
+ unsigned int i;
+ char *end;
+
+ os_memset(&settings, 0, sizeof(settings));
+
+ color = strtol(pos, &end, 10);
+ if (pos == end || color < 0 || color > 63) {
+ wpa_printf(MSG_ERROR, "color_change: Invalid color provided");
+ return -1;
+ }
+
+ /* Color value is expected to be [1-63]. If 0 comes, assumption is this
+ * is to disable the color. In this case no need to do CCA, just
+ * changing Beacon frames is sufficient. */
+ if (color == 0) {
+ if (iface->conf->he_op.he_bss_color_disabled) {
+ wpa_printf(MSG_ERROR,
+ "color_change: Color is already disabled");
+ return -1;
+ }
+
+ iface->conf->he_op.he_bss_color_disabled = 1;
+
+ for (i = 0; i < iface->num_bss; i++)
+ ieee802_11_set_beacon(iface->bss[i]);
+
+ return 0;
+ }
+
+ if (color == iface->conf->he_op.he_bss_color) {
+ if (!iface->conf->he_op.he_bss_color_disabled) {
+ wpa_printf(MSG_ERROR,
+ "color_change: Provided color is already set");
+ return -1;
+ }
+
+ iface->conf->he_op.he_bss_color_disabled = 0;
+
+ for (i = 0; i < iface->num_bss; i++)
+ ieee802_11_set_beacon(iface->bss[i]);
+
+ return 0;
+ }
+
+ if (hapd->cca_in_progress) {
+ wpa_printf(MSG_ERROR,
+ "color_change: CCA is already in progress");
+ return -1;
+ }
+
+ iface->conf->he_op.he_bss_color_disabled = 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+
+ hostapd_cleanup_cca_params(bss);
+
+ bss->cca_color = color;
+ bss->cca_count = 10;
+
+ if (hostapd_fill_cca_settings(bss, &settings)) {
+ wpa_printf(MSG_DEBUG,
+ "color_change: Filling CCA settings failed for color: %d\n",
+ color);
+ hostapd_cleanup_cca_params(bss);
+ continue;
+ }
+
+ wpa_printf(MSG_DEBUG, "Setting user selected color: %d", color);
+ ret = hostapd_drv_switch_color(bss, &settings);
+ if (ret)
+ hostapd_cleanup_cca_params(bss);
+
+ free_beacon_data(&settings.beacon_cca);
+ free_beacon_data(&settings.beacon_after);
+ }
+
return 0;
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
}
+#endif /* CONFIG_IEEE80211AX */
static u8 hostapd_maxnss(struct hostapd_data *hapd, struct sta_info *sta)
@@ -3194,6 +3319,26 @@
}
+static int hostapd_ctrl_iface_req_link_measurement(struct hostapd_data *hapd,
+ const char *cmd, char *reply,
+ size_t reply_size)
+{
+ u8 addr[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: REQ_LINK_MEASUREMENT: Invalid MAC address");
+ return -1;
+ }
+
+ ret = hostapd_send_link_measurement_req(hapd, addr);
+ if (ret >= 0)
+ ret = os_snprintf(reply, reply_size, "%d", ret);
+ return ret;
+}
+
+
static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
@@ -3449,6 +3594,395 @@
#endif /* ANDROID */
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_ctrl_iface_enable_mld(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ if (!iface || !iface->bss[0]->conf->mld_ap) {
+ wpa_printf(MSG_ERROR,
+ "Trying to enable AP MLD on an interface that is not affiliated with an AP MLD");
+ return -1;
+ }
+
+ for (i = 0; i < iface->interfaces->count; ++i) {
+ struct hostapd_iface *h_iface = iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h_iface->bss[0];
+
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
+ continue;
+
+ if (hostapd_enable_iface(h_iface)) {
+ wpa_printf(MSG_ERROR, "Enabling of AP MLD failed");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static void hostapd_disable_iface_bss(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ for (i = 0; i < iface->num_bss; i++)
+ hostapd_bss_deinit_no_free(iface->bss[i]);
+}
+
+
+static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ if (!iface || !iface->bss[0]->conf->mld_ap) {
+ wpa_printf(MSG_ERROR,
+ "Trying to disable AP MLD on an interface that is not affiliated with an AP MLD.");
+ return -1;
+ }
+
+ /* First, disable BSSs before stopping beaconing and doing driver
+ * deinit so that the broadcast Deauthentication frames go out. */
+
+ for (i = 0; i < iface->interfaces->count; ++i) {
+ struct hostapd_iface *h_iface = iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h_iface->bss[0];
+
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
+ continue;
+
+ hostapd_disable_iface_bss(iface);
+ }
+
+ /* Then, fully disable interfaces */
+ for (i = 0; i < iface->interfaces->count; ++i) {
+ struct hostapd_iface *h_iface = iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h_iface->bss[0];
+
+ if (!hostapd_is_ml_partner(h_hapd, iface->bss[0]))
+ continue;
+
+ if (hostapd_disable_iface(h_iface)) {
+ wpa_printf(MSG_ERROR, "Disabling AP MLD failed");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ 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 ret;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+
+#ifdef CONFIG_NAN_USD
+
+static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ char *token, *context = NULL;
+ int publish_id;
+ struct nan_publish_params params;
+ const char *service_name = NULL;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+ enum nan_service_protocol_type srv_proto_type = 0;
+
+ 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;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "service_name=", 13) == 0) {
+ service_name = token + 13;
+ continue;
+ }
+
+ if (os_strncmp(token, "ttl=", 4) == 0) {
+ params.ttl = atoi(token + 4);
+ continue;
+ }
+
+ if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
+ srv_proto_type = atoi(token + 15);
+ continue;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+
+ if (os_strcmp(token, "solicited=0") == 0) {
+ params.solicited = false;
+ continue;
+ }
+
+ if (os_strcmp(token, "unsolicited=0") == 0) {
+ params.unsolicited = false;
+ continue;
+ }
+
+ if (os_strcmp(token, "fsd=0") == 0) {
+ params.fsd = false;
+ continue;
+ }
+
+ wpa_printf(MSG_INFO, "CTRL: Invalid NAN_PUBLISH parameter: %s",
+ token);
+ goto fail;
+ }
+
+ publish_id = hostapd_nan_usd_publish(hapd, service_name, srv_proto_type,
+ ssi, ¶ms);
+ if (publish_id > 0)
+ ret = os_snprintf(buf, buflen, "%d", publish_id);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+
+static int hostapd_ctrl_nan_cancel_publish(struct hostapd_data *hapd,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int publish_id = 0;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "publish_id=%i", &publish_id) == 1)
+ continue;
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_CANCEL_PUBLISH parameter: %s",
+ token);
+ return -1;
+ }
+
+ if (publish_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_CANCEL_PUBLISH publish_id");
+ return -1;
+ }
+
+ hostapd_nan_usd_cancel_publish(hapd, publish_id);
+ return 0;
+}
+
+
+static int hostapd_ctrl_nan_update_publish(struct hostapd_data *hapd,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int publish_id = 0;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "publish_id=%i", &publish_id) == 1)
+ continue;
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_UPDATE_PUBLISH parameter: %s",
+ token);
+ goto fail;
+ }
+
+ if (publish_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_UPDATE_PUBLISH publish_id");
+ goto fail;
+ }
+
+ ret = hostapd_nan_usd_update_publish(hapd, publish_id, ssi);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+
+static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ char *token, *context = NULL;
+ int subscribe_id;
+ struct nan_subscribe_params params;
+ const char *service_name = NULL;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+ enum nan_service_protocol_type srv_proto_type = 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "service_name=", 13) == 0) {
+ service_name = token + 13;
+ continue;
+ }
+
+ if (os_strcmp(token, "active=1") == 0) {
+ params.active = true;
+ continue;
+ }
+
+ if (os_strncmp(token, "ttl=", 4) == 0) {
+ params.ttl = atoi(token + 4);
+ continue;
+ }
+
+ if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
+ srv_proto_type = atoi(token + 15);
+ continue;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_SUBSCRIBE parameter: %s",
+ token);
+ goto fail;
+ }
+
+ subscribe_id = hostapd_nan_usd_subscribe(hapd, service_name,
+ srv_proto_type, ssi,
+ ¶ms);
+ if (subscribe_id > 0)
+ ret = os_snprintf(buf, buflen, "%d", subscribe_id);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+
+static int hostapd_ctrl_nan_cancel_subscribe(struct hostapd_data *hapd,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int subscribe_id = 0;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "subscribe_id=%i", &subscribe_id) == 1)
+ continue;
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_CANCEL_SUBSCRIBE parameter: %s",
+ token);
+ return -1;
+ }
+
+ if (subscribe_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_CANCEL_SUBSCRIBE subscribe_id");
+ return -1;
+ }
+
+ hostapd_nan_usd_cancel_subscribe(hapd, subscribe_id);
+ return 0;
+}
+
+
+static int hostapd_ctrl_nan_transmit(struct hostapd_data *hapd, char *cmd)
+{
+ char *token, *context = NULL;
+ int handle = 0;
+ int req_instance_id = 0;
+ struct wpabuf *ssi = NULL;
+ u8 peer_addr[ETH_ALEN];
+ int ret = -1;
+
+ os_memset(peer_addr, 0, ETH_ALEN);
+
+ while ((token = str_token(cmd, " ", &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, "address=", 8) == 0) {
+ if (hwaddr_aton(token + 8, peer_addr) < 0)
+ return -1;
+ continue;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_TRANSMIT parameter: %s",
+ token);
+ goto fail;
+ }
+
+ if (handle <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_TRANSMIT handle");
+ goto fail;
+ }
+
+ if (is_zero_ether_addr(peer_addr)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_TRANSMIT address");
+ goto fail;
+ }
+
+ ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr,
+ req_instance_id);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+#endif /* CONFIG_NAN_USD */
+
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -3633,19 +4167,30 @@
} else if (os_strncmp(buf, "GET ", 4) == 0) {
reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
reply_size);
- } else if (os_strncmp(buf, "ENABLE", 6) == 0) {
+ } else if (os_strcmp(buf, "ENABLE") == 0) {
if (hostapd_ctrl_iface_enable(hapd->iface))
reply_len = -1;
} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
reply_len = -1;
+#ifdef CONFIG_IEEE80211R_AP
+ } else if (os_strcmp(buf, "GET_RXKHS") == 0) {
+ reply_len = hostapd_ctrl_iface_get_rxkhs(hapd, reply,
+ reply_size);
+ } else if (os_strcmp(buf, "RELOAD_RXKHS") == 0) {
+ if (hostapd_ctrl_iface_reload_rxkhs(hapd))
+ reply_len = -1;
+#endif /* CONFIG_IEEE80211R_AP */
} else if (os_strcmp(buf, "RELOAD_BSS") == 0) {
if (hostapd_ctrl_iface_reload_bss(hapd))
reply_len = -1;
- } else if (os_strncmp(buf, "RELOAD", 6) == 0) {
+ } else if (os_strcmp(buf, "RELOAD_CONFIG") == 0) {
+ if (hostapd_reload_config(hapd->iface))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "RELOAD") == 0) {
if (hostapd_ctrl_iface_reload(hapd->iface))
reply_len = -1;
- } else if (os_strncmp(buf, "DISABLE", 7) == 0) {
+ } else if (os_strcmp(buf, "DISABLE") == 0) {
if (hostapd_ctrl_iface_disable(hapd->iface))
reply_len = -1;
} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
@@ -3681,16 +4226,15 @@
if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
- if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
+ if (testing_set_fail_pattern(true, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
- reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
- reply_size);
+ reply_len = testing_get_fail_pattern(true, reply, reply_size);
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
- if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
+ if (testing_set_fail_pattern(false, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
- reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
+ reply_len = testing_get_fail_pattern(false, reply, reply_size);
} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
reply_len = -1;
@@ -3722,6 +4266,11 @@
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
reply_len = -1;
+#ifdef CONFIG_IEEE80211AX
+ } else if (os_strncmp(buf, "COLOR_CHANGE ", 13) == 0) {
+ if (hostapd_ctrl_iface_color_change(hapd->iface, buf + 13))
+ reply_len = -1;
+#endif /* CONFIG_IEEE80211AX */
} else if (os_strncmp(buf, "NOTIFY_CW_CHANGE ", 17) == 0) {
if (hostapd_ctrl_iface_notify_cw_change(hapd, buf + 17))
reply_len = -1;
@@ -3773,6 +4322,9 @@
} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
reply, reply_size);
+ } else if (os_strncmp(buf, "REQ_LINK_MEASUREMENT ", 21) == 0) {
+ reply_len = hostapd_ctrl_iface_req_link_measurement(
+ hapd, buf + 21, reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
@@ -3983,6 +4535,26 @@
reply_len = -1;
#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ } else if (os_strncmp(buf, "NAN_PUBLISH ", 12) == 0) {
+ reply_len = hostapd_ctrl_nan_publish(hapd, buf + 12, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "NAN_CANCEL_PUBLISH ", 19) == 0) {
+ if (hostapd_ctrl_nan_cancel_publish(hapd, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NAN_UPDATE_PUBLISH ", 19) == 0) {
+ if (hostapd_ctrl_nan_update_publish(hapd, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NAN_SUBSCRIBE ", 14) == 0) {
+ reply_len = hostapd_ctrl_nan_subscribe(hapd, buf + 14, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "NAN_CANCEL_SUBSCRIBE ", 21) == 0) {
+ if (hostapd_ctrl_nan_cancel_subscribe(hapd, buf + 21) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) {
+ if (hostapd_ctrl_nan_transmit(hapd, buf + 13) < 0)
+ reply_len = -1;
+#endif /* CONFIG_NAN_USD */
#ifdef RADIUS_SERVER
} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
@@ -4000,6 +4572,20 @@
reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
reply_size);
#endif /* ANDROID */
+#ifdef CONFIG_IEEE80211BE
+ } else if (os_strcmp(buf, "ENABLE_MLD") == 0) {
+ if (hostapd_ctrl_iface_enable_mld(hapd->iface))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DISABLE_MLD") == 0) {
+ if (hostapd_ctrl_iface_disable_mld(hapd->iface))
+ 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))
+ reply_len = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -4491,6 +5077,7 @@
#ifdef CONFIG_DPP
dpp_global_clear(interfaces->dpp);
#ifdef CONFIG_DPP3
+ interfaces->dpp_pb_bi = NULL;
{
int i;
@@ -4894,7 +5481,7 @@
reply_len = -1;
} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
reply_len = hostapd_global_ctrl_iface_interfaces(
- interfaces, buf + 10, reply, sizeof(buffer));
+ interfaces, buf + 10, reply, reply_size);
} else if (os_strcmp(buf, "TERMINATE") == 0) {
eloop_terminate();
} else {
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 0a5ceee..43cf99f 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -141,6 +141,9 @@
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
+# Include support fo RADIUS/TLS into the RADIUS client
+#CONFIG_RADIUS_TLS=y
+
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y
@@ -424,3 +427,6 @@
# DPP version 3 support (experimental and still changing; do not enable for
# production use)
#CONFIG_DPP3=y
+
+# Wi-Fi Aware unsynchronized service discovery (NAN USD)
+#CONFIG_NAN_USD=y
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index f02cd92..24f3986 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -522,6 +522,25 @@
# even if they are still in range of the AP. This can be done by setting
# skip_inactivity_poll to 1 (default 0).
#skip_inactivity_poll=0
+#
+# BSS max idle period management
+# 0 = disabled (do not advertise and manage BSS max idle period)
+# 1 = enabled (advertise and manage BSS max idle period; default)
+# 2 = enabled requiring protected frames (advertise and manage BSS max idle
+# period and require STAs to use protected keep-alive frames)
+#bss_max_idle=1
+#
+# Maximum acceptable BSS maximum idle period
+# If this is set to a nonzero value, the AP allows STAs to request different
+# maximum idle period values. This is in the units to 1000 TUs (1.024 s)
+#max_acceptable_idle_period=600
+#
+# Allow STA to skip group key handshake without getting disconnection when
+# BSS max idle period management is enabled.
+# 0 = disconnect STA if it does not reply to group key handshake (default)
+# 1 = do not disconnect STA if it does not reply to group key handshake and
+# if BSS max idle period management is enabled
+#no_disconnect_on_group_keyerror=0
# Disassociate stations based on excessive transmission failures or other
# indications of connection loss. This depends on the driver capabilities and
@@ -646,6 +665,12 @@
# no co-existence issues with neighboring devices are found.
#obss_interval=0
+# ht_vht_twt_responder: Whether TWT responder is enabled in HT and VHT modes
+# 0 = disable; Disable TWT responder support in HT and VHT modes (default).
+# 1 = enable; Enable TWT responder support in HT and VHT modes if supported by
+# the driver.
+#ht_vht_twt_responder=0
+
##### IEEE 802.11ac related configuration #####################################
# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
@@ -980,10 +1005,22 @@
# 6 GHz Access Point type
# This config is to set the 6 GHz Access Point type. Possible options are:
-# 0 = Indoor AP (default)
-# 1 = Standard Power AP
+# 0 = Indoor AP
+# 1 = Standard power AP
+# 2 = Very low power AP (default)
+# 3 = Indoor enabled AP
+# 4 = Indoor standard power AP
# This has no impact for operation on other bands.
+# See IEEE P802.11-REVme/D4.0, Table E-12 (Regulatory Info subfield encoding)
+# for more details.
#he_6ghz_reg_pwr_type=0
+#
+# 6 GHz Maximum Tx Power used in Transmit Power Envelope elements, where the
+# "Transmit Power Interpretation" is set to "Regulatory client EIRP PSD".
+# For Maximum Transmit Power Category subfield encoding set to default (0):
+#reg_def_cli_eirp_psd=-1
+# For Maximum Transmit Power Category subfield encoding set to subordinate (1):
+#reg_sub_cli_eirp_psd=-1
# Unsolicited broadcast Probe Response transmission settings
# This is for the 6 GHz band only. If the interval is set to a non-zero value,
@@ -1027,6 +1064,20 @@
#eht_oper_chwidth (see vht_oper_chwidth)
#eht_oper_centr_freq_seg0_idx
+#eht_default_pe_duration: The duration of PE field in EHT TB PPDU
+# 0 = PE field duration is the same as he_default_pe_duration (default)
+# 1 = PE field duration is 20 us
+#eht_default_pe_duration=0
+
+#eht_bw320_offset: For automatic channel selection (ACS) to indicate a preferred
+# 320 MHz channelization in EHT mode.
+# If the channel is decided or the bandwidth is not 320 MHz, this option is
+# meaningless.
+# 0 = auto-detect by hostapd
+# 1 = 320 MHz-1 (channel center frequency 31, 95, 159)
+# 2 = 320 MHz-2 (channel center frequency 63, 127, 191)
+#eht_bw320_offset=0
+
# Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0,
# Figure 9-1002c (EHT Operation Information field format). Each bit corresponds
# to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A
@@ -1045,9 +1096,6 @@
# 1 = yes (MLO)
#mld_ap=0
-# MLD ID - Affiliated MLD ID
-#mld_id=1
-
# AP MLD MAC address
# The configured address will be set as the interface hardware address and used
# as the AP MLD MAC address. If not set, the current interface hardware address
@@ -1555,6 +1603,16 @@
#acct_server_port=1813
#acct_server_shared_secret=secret2
+# RADIUS/TLS instead of RADIUS/UDP
+#auth_server_addr=127.0.0.1
+#auth_server_port=2083
+#auth_server_type=TLS
+#auth_server_shared_secret=radsec
+#auth_server_ca_cert=<path to trusted CA certificate(s)>
+#auth_server_client_cert=<path to client certificate>
+#auth_server_private_key=<path to private key>
+#auth_server_private_key_passwd=<password for decrypting private key>
+
# Retry interval for trying to return to the primary RADIUS server (in
# seconds). RADIUS client code will automatically try to use the next server
# when the current server is not replying to requests. If this interval is set,
@@ -1562,6 +1620,17 @@
# currently used secondary server is still working.
#radius_retry_primary_interval=600
+# Message-Authenticator attribute requirement for non-EAP cases
+# hostapd requires Message-Authenticator attribute to be included in all cases
+# where RADIUS is used for EAP authentication. This is also required for cases
+# where RADIUS is used for MAC ACL (macaddr_acl=2) by default, but that case
+# can be configured to not require this for compatibility with RADIUS servers
+# that do not include the attribute. This is not recommended due to potential
+# security concerns, but can be used as a temporary workaround in networks where
+# the connection to the RADIUS server is secure.
+# 0 = Do not require Message-Authenticator in MAC ACL response
+# 1 = Require Message-Authenticator in all authentication cases (default)
+#radius_require_message_authenticator=1
# Interim accounting update interval
# If this is set (larger than 0) and acct_server is configured, hostapd will
@@ -1775,6 +1844,10 @@
# Tunnel-Password
# 3 = ask RADIUS server during 4-way handshake if there is no locally
# configured PSK/passphrase for the STA
+#
+# The Tunnel-Password attribute in Access-Accept can contain either the
+# 8..63 character ASCII passphrase or a 64 hex character encoding of the PSK.
+#
#wpa_psk_radius=0
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
@@ -2032,6 +2105,10 @@
#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
#sae_password=example secret|vlanid=3|id=pw identifier
+#
+# SAE passwords can also be read from a separate file in which each line
+# contains and entry in the same format as sae_password uses.
+#sae_password_file=/tc/hostapd.sae_passwords
# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
# This parameter defines how many open SAE instances can be in progress at the
@@ -2042,7 +2119,7 @@
# Maximum number of SAE synchronization errors (dot11RSNASAESync)
# The offending SAE peer will be disconnected if more than this many
# synchronization errors happen.
-#sae_sync=5
+#sae_sync=3
# Enabled SAE finite cyclic groups
# SAE implementation are required to support group 19 (ECC group defined over a
@@ -2202,6 +2279,77 @@
# (default: 1 = activated)
#pasn_noauth=1
+# 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
+# 4-way handshake. This capability allows a STA to confirm that the AP has the
+# same understanding on which SSID is being used for an association in a
+# protected manner in cases where both the AP and the STA has this capability.
+# This can be used to mitigate CVE-2023-52424 (a.k.a. the SSID Confusion
+# Attack).
+#
+# Ideally, this capability would be enabled by default on the AP, but since this
+# is new functionality with limited testing, the default is to disable this for
+# now and require explicitly configuration to enable. The default behavior is
+# like to change once this capability has received more testing.
+#
+# 0 = SSID protection in 4-way handshake disabled (default)
+# 1 = SSID protection in 4-way handshake enabled
+#
+#ssid_protection=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=<accepted key management algorithms>
+#
+# Pairwise cipher suites; see rsn_pairwise for RSNE configuration
+#rsn_override_pairwise=<accepted cipher suites)
+#
+# Management frame protection (MFP/PMF); see ieee80211w for RSNE configuration
+# 0 = disabled
+# 1 = optional
+# 2 = required
+#rsn_override_mfp=<0/1/2>
+#
+# 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
+#
+# 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.11r configuration ##############################################
# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -2261,6 +2409,12 @@
# list and thus will receive push notifications.
#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
+# Optionally, the list of RxKHs can be read from a text file. Format is the same
+# as specified above. File shall contain both r0kh and r1kh. Once this variable
+# is set, RxKHs can be reloaded at runtime without bringing down an interface
+# using the RELOAD_RXKHS command.
+#rxkh_file=<path>
+
# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
# Special values: 0 -> do not expire
# Warning: do not cache implies no sequence number validation with wildcards
@@ -2515,6 +2669,23 @@
#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#multi_ap_backhaul_wpa_passphrase=secret passphrase
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile (default: 2)
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
+# Multi-AP client disallow
+# Used to disallow profile specific backhaul STA association
+# Bitmap of the disallowed Profile-X profiles
+# 1 = Profile-1 Backhaul STA association disallowed
+# 2 = Profile-2 Backhaul STA association disallowed
+#multi_ap_client_disallow=0
+
+# Multi-AP VLAN ID
+# A valid non-zero VLAN ID will be used to update Default IEEE 802.1Q Setting
+#multi_ap_vlanid=0
+
# WPS UPnP interface
# If set, support for external Registrars is enabled.
#upnp_iface=br0
@@ -3048,6 +3219,9 @@
# Enable neighbor report via radio measurements
#rrm_neighbor_report=1
+# Enable link measurement report via radio measurements
+#rrm_link_measurement_report=1
+
# Enable beacon report via radio measurements
#rrm_beacon_report=1
@@ -3149,6 +3323,13 @@
# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
# previous attempts and thus delays the frame. (testing only)
#delay_eapol_tx=0
+#
+# Additional elements for Probe Response frames.
+# This parameter can be used to add additional element(s) to the end of the
+# Probe Response frames. The format for these element(s) is a hexdump of the
+# raw information elements (id+len+payload for one or more elements).
+# These elements are added after the 'vendor_elements'.
+#presp_elements=
##### Multiple BSSID support ##################################################
#
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 45497cd..eb8a383 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -21,7 +21,7 @@
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi> and contributors";
static struct wpa_ctrl *ctrl_conn;
static int hostapd_cli_quit = 0;
@@ -1168,6 +1168,15 @@
#endif /* CONFIG_FST */
+#ifdef CONFIG_IEEE80211AX
+static int hostapd_cli_cmd_color_change(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "COLOR_CHANGE", 1, argc, argv);
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
@@ -1215,14 +1224,14 @@
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "ENABLE");
}
static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOAD");
}
@@ -1235,20 +1244,48 @@
}
+static int hostapd_cli_cmd_reload_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOAD_CONFIG");
+}
+
+
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+ char *argv[])
{
return wpa_ctrl_command(ctrl, "DISABLE");
}
-static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+static int hostapd_cli_cmd_enable_mld(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
+ return wpa_ctrl_command(ctrl, "ENABLE_MLD");
+}
+
+
+static int hostapd_cli_cmd_disable_mld(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DISABLE_MLD");
+}
+
+
+static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
}
+static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "STOP_AP");
+}
+
+
static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
@@ -1577,6 +1614,13 @@
}
+static int hostapd_cli_cmd_req_link_measurement(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "REQ_LINK_MEASUREMENT", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1584,6 +1628,24 @@
}
+#ifdef CONFIG_IEEE80211R_AP
+
+static int hostapd_cli_cmd_get_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "GET_RXKHS");
+}
+
+
+static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOAD_RXKHS");
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
#ifdef ANDROID
static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
@@ -1693,6 +1755,11 @@
"<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
" [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
" = initiate channel switch announcement" },
+#ifdef CONFIG_IEEE80211AX
+ { "color_change", hostapd_cli_cmd_color_change, NULL,
+ "<color> = initiate BSS color change to set the specified color\n"
+ "Value 0 will disable the color.\n"},
+#endif /* CONFIG_IEEE80211AX */
{ "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
"<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
@@ -1710,10 +1777,18 @@
"= reload configuration for current interface" },
{ "reload_bss", hostapd_cli_cmd_reload_bss, NULL,
"= reload configuration for current BSS" },
+ { "reload_config", hostapd_cli_cmd_reload_config, NULL,
+ "= reload configuration for current interface" },
{ "disable", hostapd_cli_cmd_disable, NULL,
"= disable hostapd on current interface" },
+ { "enable_mld", hostapd_cli_cmd_enable_mld, NULL,
+ "= enable AP MLD to which the interface is affiliated" },
+ { "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,
"= update Beacon frame contents\n"},
+ { "stop_ap", hostapd_cli_cmd_stop_ap, NULL,
+ "= stop AP\n"},
{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
"= drop all ERP keys"},
{ "log_level", hostapd_cli_cmd_log_level, NULL,
@@ -1793,8 +1868,16 @@
"<addr> = poll a STA to check connectivity with a QoS null frame" },
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
+ { "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL,
+ "<addr> = send a link measurement report request to a station"},
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" },
+#ifdef CONFIG_IEEE80211R_AP
+ { "reload_rxkhs", hostapd_cli_cmd_reload_rxkhs, NULL,
+ "= reload R0KHs and R1KHs" },
+ { "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL,
+ "= get R0KHs and R1KHs" },
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef ANDROID
{ "driver", hostapd_cli_cmd_driver, NULL,
"<driver sub command> [<hex formatted data>] = send driver command data" },
diff --git a/hostapd/main.c b/hostapd/main.c
index 615dc2f..640a169 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -160,6 +160,9 @@
struct hostapd_bss_config *conf = hapd->conf;
u8 *b = conf->bssid;
struct wpa_driver_capa capa;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *h_hapd = NULL;
+#endif /* CONFIG_IEEE80211BE */
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
@@ -167,30 +170,12 @@
}
#ifdef CONFIG_IEEE80211BE
- for (i = 0; conf->mld_ap && i < iface->interfaces->count; i++) {
- struct hostapd_iface *h = iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
- struct hostapd_bss_config *hconf = h_hapd->conf;
+ if (conf->mld_ap)
+ h_hapd = hostapd_mld_get_first_bss(hapd);
- if (h == iface) {
- wpa_printf(MSG_DEBUG, "MLD: Skip own interface");
- continue;
- }
-
- if (!hconf->mld_ap || hconf->mld_id != conf->mld_id) {
- wpa_printf(MSG_DEBUG,
- "MLD: Skip non matching mld_id");
- continue;
- }
-
- wpa_printf(MSG_DEBUG, "MLD: Found matching MLD interface");
- if (!h_hapd->drv_priv) {
- wpa_printf(MSG_DEBUG,
- "MLD: Matching MLD BSS not initialized yet");
- continue;
- }
-
+ if (h_hapd) {
hapd->drv_priv = h_hapd->drv_priv;
+ hapd->interface_added = h_hapd->interface_added;
/*
* All interfaces participating in the AP MLD would have
@@ -200,20 +185,18 @@
* is not configured, and otherwise it would be the
* configured BSSID.
*/
- os_memcpy(hapd->mld_addr, h_hapd->mld_addr, ETH_ALEN);
if (is_zero_ether_addr(b)) {
- os_memcpy(hapd->own_addr, h_hapd->mld_addr, ETH_ALEN);
+ os_memcpy(hapd->own_addr, h_hapd->mld->mld_addr,
+ ETH_ALEN);
random_mac_addr_keep_oui(hapd->own_addr);
} else {
os_memcpy(hapd->own_addr, b, ETH_ALEN);
}
- /*
- * Mark the interface as a secondary interface, as this
- * is needed for the de-initialization flow
- */
- hapd->mld_first_bss = h_hapd;
- hapd->mld_link_id = hapd->mld_first_bss->mld_next_link_id++;
+ 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);
goto setup_mld;
}
@@ -249,8 +232,12 @@
* Use the configured MLD MAC address as the interface hardware address
* if this AP is a part of an AP MLD.
*/
- if (!is_zero_ether_addr(hapd->conf->mld_addr) && hapd->conf->mld_ap)
- params.bssid = hapd->conf->mld_addr;
+ if (hapd->conf->mld_ap) {
+ if (!is_zero_ether_addr(hapd->conf->mld_addr))
+ params.bssid = hapd->conf->mld_addr;
+ else
+ params.bssid = NULL;
+ }
#endif /* CONFIG_IEEE80211BE */
params.ifname = hapd->conf->iface;
@@ -286,13 +273,16 @@
* configured, and otherwise it would be the configured BSSID.
*/
if (hapd->conf->mld_ap) {
- os_memcpy(hapd->mld_addr, hapd->own_addr, ETH_ALEN);
- hapd->mld_next_link_id = 0;
- hapd->mld_link_id = hapd->mld_next_link_id++;
+ os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN);
+
if (!b)
random_mac_addr_keep_oui(hapd->own_addr);
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);
}
setup_mld:
@@ -304,6 +294,7 @@
iface->drv_flags = capa.flags;
iface->drv_flags2 = capa.flags2;
+ iface->drv_rrm_flags = capa.rrm_flags;
iface->probe_resp_offloads = capa.probe_resp_offloads;
/*
* Use default extended capa values from per-radio information
@@ -340,10 +331,13 @@
return -1;
}
+ /* Initialize the BSS parameter change to 1 */
+ hapd->eht_mld_bss_param_change = 1;
+
wpa_printf(MSG_DEBUG,
"MLD: Set link_id=%u, mld_addr=" MACSTR
", own_addr=" MACSTR,
- hapd->mld_link_id, MAC2STR(hapd->mld_addr),
+ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
MAC2STR(hapd->own_addr));
hostapd_drv_link_add(hapd, hapd->mld_link_id,
@@ -570,7 +564,7 @@
"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-2022, Jouni Malinen <j@w1.fi> "
+ "Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi> "
"and contributors\n",
VERSION_STR);
}
@@ -751,6 +745,29 @@
}
+static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ size_t i;
+
+ if (!interfaces || !interfaces->mld)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ if (!interfaces->mld[i])
+ continue;
+
+ os_free(interfaces->mld[i]);
+ interfaces->mld[i] = NULL;
+ }
+
+ os_free(interfaces->mld);
+ interfaces->mld = NULL;
+ interfaces->mld_count = 0;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
@@ -1036,6 +1053,8 @@
interfaces.iface = NULL;
interfaces.count = 0;
+ hostapd_global_cleanup_mld(&interfaces);
+
#ifdef CONFIG_DPP
dpp_global_deinit(interfaces.dpp);
#endif /* CONFIG_DPP */
diff --git a/src/ap/acs.c b/src/ap/acs.c
index e3cfe1d..f5b36d3 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "utils/list.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
@@ -75,7 +76,7 @@
*
* This corresponds to:
* ---
- * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf - band_min_nf)
* ---
*
* The coefficient of 2 reflects the way power in "far-field"
@@ -92,7 +93,7 @@
* calculated easily.
* ---
* (busy time - tx time) / (active time - tx time) *
- * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * 2^(10^(chan_nf/10) - 10^(band_min_nf/10))
* ---
*
* However to account for cases where busy/rx time is 0 (channel load is then
@@ -100,7 +101,7 @@
* channel with lower noise floor is preferred. The equation becomes:
* ---
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
- * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * 2^(10^(chan_nf/10) - 10^(band_min_nf/10))
* ---
*
* All this "interference factor" is purely subjective and only time
@@ -245,6 +246,8 @@
ACS_BW40,
ACS_BW80,
ACS_BW160,
+ ACS_BW320_1,
+ ACS_BW320_2,
};
struct bw_item {
@@ -286,15 +289,26 @@
{ 6435, 6575, 111 }, { 6595, 6735, 143 },
{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
};
+static const struct bw_item bw_320_1[] = {
+ { 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
+ { -1, -1, -1 }
+};
+static const struct bw_item bw_320_2[] = {
+ { 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
+ { -1, -1, -1 }
+};
static const struct bw_item *bw_desc[] = {
[ACS_BW40] = bw_40,
[ACS_BW80] = bw_80,
[ACS_BW160] = bw_160,
+ [ACS_BW320_1] = bw_320_1,
+ [ACS_BW320_2] = bw_320_2,
};
static int acs_request_scan(struct hostapd_iface *iface);
static int acs_survey_is_sufficient(struct freq_survey *survey);
+static void acs_scan_retry(void *eloop_data, void *user_data);
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
@@ -340,6 +354,8 @@
iface->chans_surveyed = 0;
iface->acs_num_completed_scans = 0;
+ iface->acs_num_retries = 0;
+ eloop_cancel_timeout(acs_scan_retry, iface, NULL);
}
@@ -495,7 +511,7 @@
}
if (ret == -1)
- ret = 1; /* no survey list entries */
+ ret = 0; /* no survey list entries */
if (!ret) {
wpa_printf(MSG_INFO,
@@ -768,6 +784,42 @@
#endif /* CONFIG_IEEE80211BE */
+static bool
+acs_usable_bw320_chan(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan, int *bw320_offset)
+{
+ const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
+ int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
+
+ *bw320_offset = 0;
+ switch (conf_bw320_offset) {
+ case 1:
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ break;
+ case 2:
+ if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ case 0:
+ default:
+ conf_bw320_offset = 0;
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ else if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ }
+
+ if (!*bw320_offset)
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
+ chan->chan, bw320_str[conf_bw320_offset]);
+
+ return *bw320_offset != 0;
+}
+
+
static void
acs_find_ideal_chan_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode,
@@ -779,14 +831,21 @@
struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
long double factor;
int i, j;
+ int bw320_offset = 0, ideal_bw320_offset = 0;
unsigned int k;
+ int secondary_channel = 1, freq_offset;
+#ifdef CONFIG_IEEE80211BE
+ int index_primary = 0;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (is_24ghz_mode(mode->mode))
+ secondary_channel = iface->conf->secondary_channel;
for (i = 0; i < mode->num_channels; i++) {
- double total_weight;
+ double total_weight = 0;
struct acs_bias *bias, tmp_bias;
- bool update_best = true;
- best = chan = &mode->channels[i];
+ chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
@@ -818,7 +877,7 @@
iface->conf->country[2] == 0x4f)
continue;
- if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ 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);
@@ -839,7 +898,8 @@
}
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ (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)) {
@@ -859,13 +919,25 @@
}
}
+ 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))
+ continue;
+ }
+
factor = 0;
- if (acs_usable_chan(chan))
+ best = NULL;
+ if (acs_usable_chan(chan)) {
factor = chan->interference_factor;
- total_weight = 1;
+ total_weight = 1;
+ best = chan;
+ }
for (j = 1; j < n_chans; j++) {
- adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ adj_chan = acs_find_chan(iface, chan->freq +
+ j * secondary_channel * 20);
if (!adj_chan)
break;
@@ -876,16 +948,14 @@
break;
}
- if (acs_usable_chan(adj_chan)) {
- factor += adj_chan->interference_factor;
- total_weight += 1;
- } else {
- update_best = false;
- }
+ if (!acs_usable_chan(adj_chan))
+ continue;
+
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
/* find the best channel in this segment */
- if (update_best &&
- adj_chan->interference_factor <
+ if (!best || adj_chan->interference_factor <
best->interference_factor)
best = adj_chan;
}
@@ -898,13 +968,17 @@
/* 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->mode == HOSTAPD_MODE_IEEE80211A &&
- chan != best) {
+ if (iface->current_mode &&
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ 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;
+#endif /* CONFIG_IEEE80211BE */
chan = best;
}
@@ -912,8 +986,9 @@
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
for (j = 0; j < n_chans; j++) {
+ freq_offset = j * 20 * secondary_channel;
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 5);
+ freq_offset - 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -921,7 +996,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 10);
+ freq_offset - 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -929,7 +1004,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 5);
+ freq_offset + 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -937,7 +1012,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 10);
+ freq_offset + 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -946,6 +1021,9 @@
}
}
+ if (total_weight == 0)
+ continue;
+
factor /= total_weight;
bias = NULL;
@@ -983,19 +1061,25 @@
*ideal_factor = factor;
*ideal_chan = chan;
+ ideal_bw320_offset = bw320_offset;
#ifdef CONFIG_IEEE80211BE
if (iface->conf->ieee80211be)
acs_update_puncturing_bitmap(iface, mode, bw,
n_chans, chan,
- factor, 0);
+ factor,
+ index_primary);
#endif /* CONFIG_IEEE80211BE */
}
/* This channel would at least be usable */
- if (!(*rand_chan))
+ if (!(*rand_chan)) {
*rand_chan = chan;
+ ideal_bw320_offset = bw320_offset;
+ }
}
+
+ hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
}
@@ -1022,19 +1106,12 @@
goto bw_selected;
}
- /* TODO: HT40- support */
-
- if (iface->conf->ieee80211n &&
- iface->conf->secondary_channel == -1) {
- wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
- return NULL;
- }
-
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel)
n_chans = 2;
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_80MHZ:
n_chans = 4;
@@ -1042,6 +1119,9 @@
case CONF_OPER_CHWIDTH_160MHZ:
n_chans = 8;
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ n_chans = 16;
+ break;
default:
break;
}
@@ -1091,7 +1171,8 @@
acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
return;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+ wpa_printf(MSG_DEBUG,
+ "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
if (iface->freq == bw_desc[ACS_BW40][i].first)
@@ -1106,7 +1187,7 @@
{
int center;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
@@ -1125,11 +1206,28 @@
case CONF_OPER_CHWIDTH_160MHZ:
center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ switch (hostapd_get_bw320_offset(iface->conf)) {
+ case 1:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_1);
+ break;
+ case 2:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_2);
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "ACS: BW320 offset is not selected");
+ return;
+ }
+
+ break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
wpa_printf(MSG_INFO,
- "ACS: Only VHT20/40/80/160 is supported now");
+ "ACS: Only VHT20/40/80/160/320 is supported now");
return;
}
@@ -1192,7 +1290,8 @@
iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
acs_adjust_secondary(iface);
acs_adjust_center_freq(iface);
}
@@ -1229,6 +1328,7 @@
int err;
iface->scan_cb = NULL;
+ iface->acs_num_retries = 0;
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
iface->conf->acs_num_scans);
@@ -1241,7 +1341,7 @@
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
err = acs_request_scan(iface);
- if (err) {
+ if (err && err != -EBUSY) {
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
goto fail;
}
@@ -1294,7 +1394,7 @@
static int acs_request_scan(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
- int i, *freq;
+ int i, *freq, ret;
int num_channels;
struct hostapd_hw_modes *mode;
@@ -1327,24 +1427,62 @@
return -1;
}
- iface->scan_cb = acs_scan_complete;
+ if (!iface->acs_num_retries)
+ wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
+ else
+ wpa_printf(MSG_DEBUG,
+ "ACS: Re-try scanning attempt %d (%d / %d)",
+ iface->acs_num_retries,
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
- wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
- iface->acs_num_completed_scans + 1,
- iface->conf->acs_num_scans);
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ os_free(params.freqs);
- if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
+ if (ret == -EBUSY) {
+ iface->acs_num_retries++;
+ if (iface->acs_num_retries >= ACS_SCAN_RETRY_MAX_COUNT) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Failed to request initial scan (all re-attempts failed)");
+ acs_fail(iface);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO,
+ "Failed to request acs scan ret=%d (%s) - try to scan after %d seconds",
+ ret, strerror(-ret), ACS_SCAN_RETRY_INTERVAL);
+ eloop_cancel_timeout(acs_scan_retry, iface, NULL);
+ eloop_register_timeout(ACS_SCAN_RETRY_INTERVAL, 0,
+ acs_scan_retry, iface, NULL);
+ return 0;
+ }
+
+ if (ret < 0) {
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
acs_cleanup(iface);
- os_free(params.freqs);
return -1;
}
- os_free(params.freqs);
+ iface->scan_cb = acs_scan_complete;
+
return 0;
}
+static void acs_scan_retry(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ if (acs_request_scan(iface)) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Failed to request re-try of initial scan");
+ acs_fail(iface);
+ }
+}
+
+
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
int err;
diff --git a/src/ap/acs.h b/src/ap/acs.h
index ec84f0e..8be3de5 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -15,6 +15,9 @@
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
void acs_cleanup(struct hostapd_iface *iface);
+#define ACS_SCAN_RETRY_MAX_COUNT 15
+#define ACS_SCAN_RETRY_INTERVAL 5
+
#else /* CONFIG_ACS */
static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
index abe817c..6844311 100644
--- a/src/ap/airtime_policy.c
+++ b/src/ap/airtime_policy.c
@@ -232,7 +232,7 @@
struct airtime_sta_weight *wt;
wt = hapd->conf->airtime_weight_list;
- while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+ while (wt && !ether_addr_equal(wt->addr, sta))
wt = wt->next;
return wt ? wt->weight : hapd->conf->airtime_weight;
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 60d3566..a5a821b 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -92,6 +92,7 @@
bss->eap_sim_id = 3;
bss->eap_sim_aka_fast_reauth_limit = 1000;
bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+ bss->bss_max_idle = 1;
bss->eapol_version = EAPOL_VERSION;
bss->max_listen_interval = 65535;
@@ -121,9 +122,10 @@
#endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300;
+ bss->radius_require_message_authenticator = 1;
bss->anti_clogging_threshold = 5;
- bss->sae_sync = 5;
+ bss->sae_sync = 3;
bss->gas_frag_limit = 1400;
@@ -163,8 +165,11 @@
/* Default to strict CRL checking. */
bss->check_crl_strict = 1;
+ bss->multi_ap_profile = MULTI_AP_PROFILE_2;
+
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
+ bss->test_assoc_comeback_type = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_PASN
@@ -283,6 +288,10 @@
conf->he_6ghz_max_ampdu_len_exp = 7;
conf->he_6ghz_rx_ant_pat = 1;
conf->he_6ghz_tx_ant_pat = 1;
+ conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
+ conf->reg_def_cli_eirp_psd = -1;
+ conf->reg_sub_cli_eirp_psd = -1;
+ conf->reg_def_cli_eirp = -1;
#endif /* CONFIG_IEEE80211AX */
/* The third octet of the country string uses an ASCII space character
@@ -297,6 +306,8 @@
conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
#endif /* CONFIG_AIRTIME_POLICY */
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
return conf;
}
@@ -485,10 +496,14 @@
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 */
sae_deinit_pt(ssid->pt);
@@ -550,6 +565,10 @@
for (i = 0; i < num_servers; i++) {
os_free(servers[i].shared_secret);
+ os_free(servers[i].ca_cert);
+ os_free(servers[i].client_cert);
+ os_free(servers[i].private_key);
+ os_free(servers[i].private_key_passwd);
}
os_free(servers);
}
@@ -695,6 +714,33 @@
}
+#ifdef CONFIG_IEEE80211R_AP
+
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+ r0kh = conf->r0kh_list;
+ conf->r0kh_list = NULL;
+ while (r0kh) {
+ r0kh_prev = r0kh;
+ r0kh = r0kh->next;
+ os_free(r0kh_prev);
+ }
+
+ r1kh = conf->r1kh_list;
+ conf->r1kh_list = NULL;
+ while (r1kh) {
+ r1kh_prev = r1kh;
+ r1kh = r1kh->next;
+ os_free(r1kh_prev);
+ }
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
{
struct anqp_element *elem;
@@ -827,26 +873,9 @@
os_free(conf->time_zone);
#ifdef CONFIG_IEEE80211R_AP
- {
- struct ft_remote_r0kh *r0kh, *r0kh_prev;
- struct ft_remote_r1kh *r1kh, *r1kh_prev;
-
- r0kh = conf->r0kh_list;
- conf->r0kh_list = NULL;
- while (r0kh) {
- r0kh_prev = r0kh;
- r0kh = r0kh->next;
- os_free(r0kh_prev);
- }
-
- r1kh = conf->r1kh_list;
- conf->r1kh_list = NULL;
- while (r1kh) {
- r1kh_prev = r1kh;
- r1kh = r1kh->next;
- os_free(r1kh_prev);
- }
- }
+ hostapd_config_clear_rxkhs(conf);
+ os_free(conf->rxkh_file);
+ conf->rxkh_file = NULL;
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_WPS
@@ -944,6 +973,9 @@
wpabuf_free(conf->rsnxe_override_ft);
wpabuf_free(conf->gtk_rsc_override);
wpabuf_free(conf->igtk_rsc_override);
+ wpabuf_free(conf->eapol_m1_elements);
+ wpabuf_free(conf->eapol_m3_elements);
+ wpabuf_free(conf->presp_elements);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
@@ -1131,10 +1163,9 @@
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
(psk->group ||
- (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (addr && ether_addr_equal(psk->addr, addr)) ||
(!addr && p2p_dev_addr &&
- os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
- 0))) {
+ ether_addr_equal(psk->p2p_dev_addr, p2p_dev_addr)))) {
if (vlan_id)
*vlan_id = psk->vlan_id;
return psk->psk;
@@ -1558,6 +1589,10 @@
"Cannot set ieee80211be without ieee80211ax");
return -1;
}
+
+ if (full_config)
+ hostapd_set_and_check_bw320_offset(conf,
+ conf->eht_bw320_offset);
#endif /* CONFIG_IEEE80211BE */
if (full_config && conf->mbssid && !conf->ieee80211ax) {
@@ -1750,7 +1785,7 @@
int i = 0;
while (i < *num) {
- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal((*acl)[i].addr, addr)) {
os_remove_in_array(*acl, *num, sizeof(**acl), i);
(*num)--;
} else {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 99a6d18..1a4c912 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -309,6 +309,7 @@
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;
+ int radius_require_message_authenticator;
int acct_interim_interval;
int radius_request_cui;
struct hostapd_radius_attr *radius_auth_req_attr;
@@ -357,7 +358,11 @@
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) */
@@ -386,6 +391,8 @@
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;
@@ -405,6 +412,7 @@
int ft_over_ds;
int ft_psk_generate_local;
int r1_max_key_lifetime;
+ char *rxkh_file;
#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -464,6 +472,9 @@
*/
int ap_max_inactivity;
+ int bss_max_idle;
+ int max_acceptable_idle_period;
+ bool no_disconnect_on_group_keyerror;
int ignore_broadcast_ssid;
int no_probe_resp_if_max_sta;
@@ -704,6 +715,15 @@
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
+ int test_assoc_comeback_type;
+ struct wpabuf *presp_elements;
+
+#ifdef CONFIG_IEEE80211BE
+ u16 eht_oper_puncturing_override;
+#endif /* CONFIG_IEEE80211BE */
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -791,6 +811,14 @@
#define BACKHAUL_BSS 1
#define FRONTHAUL_BSS 2
int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+ int multi_ap_profile;
+ /* Multi-AP Profile-1 clients not allowed to connect */
+#define PROFILE1_CLIENT_ASSOC_DISALLOW BIT(0)
+ /* Multi-AP Profile-2 clients not allowed to connect */
+#define PROFILE2_CLIENT_ASSOC_DISALLOW BIT(1)
+ unsigned int multi_ap_client_disallow;
+ /* Primary VLAN ID to use in Multi-AP */
+ int multi_ap_vlanid;
#ifdef CONFIG_AIRTIME_POLICY
unsigned int airtime_weight;
@@ -939,6 +967,8 @@
char *config_id;
bool xrates_supported;
+ bool ssid_protection;
+
#ifdef CONFIG_IEEE80211BE
/* The AP is part of an AP MLD */
u8 mld_ap;
@@ -948,6 +978,14 @@
/* The AP's MLD MAC address within the AP MLD */
u8 mld_addr[ETH_ALEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * If set indicate the AP as disabled in the RNR element included in the
+ * other APs in the AP MLD.
+ */
+ bool mld_indicate_disabled;
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
};
@@ -1139,6 +1177,19 @@
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
u8 he_6ghz_reg_pwr_type;
+
+ int reg_def_cli_eirp_psd;
+ int reg_sub_cli_eirp_psd;
+
+ /*
+ * This value should be used when regulatory client EIRP PSD values
+ * advertised by an AP that is an SP AP or an indoor SP AP are
+ * insufficient to ensure that regulatory client limits on total EIRP
+ * are always met for all transmission bandwidths within the bandwidth
+ * of the AP’s BSS.
+ */
+ int reg_def_cli_eirp;
+
bool require_he;
#endif /* CONFIG_IEEE80211AX */
@@ -1175,6 +1226,8 @@
struct eht_phy_capabilities_info eht_phy_capab;
u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
u8 punct_acs_threshold;
+ u8 eht_default_pe_duration;
+ u8 eht_bw320_offset;
#endif /* CONFIG_IEEE80211BE */
/* EHT enable/disable config from CHAN_SWITCH */
@@ -1187,6 +1240,9 @@
MBSSID_ENABLED = 1,
ENHANCED_MBSSID_ENABLED = 2,
} mbssid;
+
+ /* Whether to enable TWT responder in HT and VHT modes */
+ bool ht_vht_twt_responder;
};
@@ -1242,7 +1298,8 @@
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+ if (is_6ghz_op_class(conf->op_class) &&
+ center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
oper_centr_freq_seg0_idx +=
conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
#endif /* CONFIG_IEEE80211BE */
@@ -1274,6 +1331,43 @@
conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
}
+static inline u8
+hostapd_get_bw320_offset(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ)
+ return conf->eht_bw320_offset;
+#endif /* CONFIG_IEEE80211BE */
+ return 0;
+}
+
+static inline void
+hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ u8 bw320_offset)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ op_class_to_ch_width(conf->op_class) == CONF_OPER_CHWIDTH_320MHZ) {
+ if (conf->channel) {
+ /* If the channel is set, then calculate bw320_offset
+ * by center frequency segment 0.
+ */
+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+ conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1;
+ } else {
+ /* If the channel is not set, bw320_offset indicates
+ * preferred offset of 320 MHz.
+ */
+ conf->eht_bw320_offset = bw320_offset;
+ }
+ } else {
+ conf->eht_bw320_offset = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void);
@@ -1282,6 +1376,7 @@
void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf);
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,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8f9cc5b..c473491 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -208,6 +208,9 @@
add_buf(&beacon, hapd->conf->vendor_elements);
add_buf(&proberesp, hapd->conf->vendor_elements);
+#ifdef CONFIG_TESTING_OPTIONS
+ add_buf(&proberesp, hapd->conf->presp_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
add_buf(&assocresp, hapd->conf->assocresp_elements);
*beacon_ret = beacon;
@@ -265,9 +268,35 @@
}
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return true;
+#endif /* CONFIG_IEEE80211BE */
+
+ return false;
+}
+
+
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized)
{
+ /*
+ * The WPA_STA_AUTHORIZED flag is relevant only for the MLD station and
+ * not to the link stations (as the authorization is done between the
+ * MLD peers). Thus, do not propagate the change to the driver for the
+ * link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
if (authorized) {
return hostapd_sta_set_flags(hapd, sta->addr,
hostapd_sta_flags_to_drv(
@@ -285,11 +314,24 @@
{
int set_flags, total_flags, flags_and, flags_or;
total_flags = hostapd_sta_flags_to_drv(sta->flags);
- set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
- if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
- sta->auth_alg == WLAN_AUTH_FT) &&
- sta->flags & WLAN_STA_AUTHORIZED)
- set_flags |= WPA_STA_AUTHORIZED;
+ set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP |
+ WPA_STA_AUTHORIZED;
+
+ /*
+ * All the station flags other than WPA_STA_SHORT_PREAMBLE are relevant
+ * only for the MLD station and not to the link stations (as these flags
+ * are related to the MLD state and not the link state). As for the
+ * WPA_STA_SHORT_PREAMBLE, since the station is an EHT station, it must
+ * support short preamble. Thus, do not propagate the change to the
+ * driver for the link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
flags_or = total_flags & set_flags;
flags_and = total_flags | ~set_flags;
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
@@ -533,12 +575,33 @@
}
+#ifdef CONFIG_IEEE80211BE
+int hostapd_if_link_remove(struct hostapd_data *hapd,
+ enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id)
+{
+ if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_remove)
+ return -1;
+
+ return hapd->driver->link_remove(hapd->drv_priv, type, ifname,
+ hapd->mld_link_id);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname)
{
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
hapd->driver->if_remove == NULL)
return -1;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ return hostapd_if_link_remove(hapd, type, ifname,
+ hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
}
@@ -564,9 +627,17 @@
int hostapd_flush(struct hostapd_data *hapd)
{
+ int link_id = -1;
+
if (hapd->driver == NULL || hapd->driver->flush == NULL)
return 0;
- return hapd->driver->flush(hapd->drv_priv);
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf && hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->flush(hapd->drv_priv, link_id);
}
@@ -590,7 +661,7 @@
&cmode->he_capab[IEEE80211_MODE_AP] : NULL,
cmode ?
&cmode->eht_capab[IEEE80211_MODE_AP] :
- NULL))
+ NULL, hostapd_get_punct_bitmap(hapd)))
return -1;
if (hapd->driver == NULL)
@@ -719,6 +790,8 @@
struct wpa_scan_results * hostapd_driver_get_scan_results(
struct hostapd_data *hapd)
{
+ if (hapd->driver && hapd->driver->get_scan_results)
+ return hapd->driver->get_scan_results(hapd->drv_priv, NULL);
if (hapd->driver && hapd->driver->get_scan_results2)
return hapd->driver->get_scan_results2(hapd->drv_priv);
return NULL;
@@ -793,15 +866,21 @@
const u8 *addr, int reason)
{
int link_id = -1;
+ const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
link_id = hapd->mld_link_id;
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+ }
#endif /* CONFIG_IEEE80211BE */
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_deauth(hapd->drv_priv, own_addr, addr,
reason, link_id);
}
@@ -809,9 +888,20 @@
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
const u8 *addr, int reason)
{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_disassoc(hapd->drv_priv, own_addr, addr,
reason);
}
@@ -826,22 +916,22 @@
}
-int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
- unsigned int wait, const u8 *dst, const u8 *data,
- size_t len)
+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 *own_addr = hapd->own_addr;
const u8 *bssid;
const u8 wildcard_bssid[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
+ struct sta_info *sta;
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
return 0;
bssid = hapd->own_addr;
- if (!is_multicast_ether_addr(dst) &&
+ if (!addr3_ap && !is_multicast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
- struct sta_info *sta;
-
/*
* Public Action frames to a STA that is not a member of the BSS
* shall use wildcard BSSID value.
@@ -849,7 +939,7 @@
sta = ap_get_sta(hapd, dst);
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
bssid = wildcard_bssid;
- } else if (is_broadcast_ether_addr(dst) &&
+ } else if (!addr3_ap && is_broadcast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
/*
* The only current use case of Public Action frames with
@@ -858,9 +948,27 @@
* so have to use the wildcard BSSID value.
*/
bssid = wildcard_bssid;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->conf->mld_ap) {
+ sta = ap_get_sta(hapd, dst);
+
+ if (ap_sta_is_mld(hapd, sta)) {
+ own_addr = hapd->mld->mld_addr;
+ bssid = own_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
+
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, bssid, data, len, 0);
+ own_addr, bssid, data, len, 0);
+}
+
+
+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);
}
@@ -869,11 +977,7 @@
unsigned int wait, const u8 *dst,
const u8 *data, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->send_action == NULL)
- return 0;
- return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, hapd->own_addr, data,
- len, 0);
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
}
@@ -907,7 +1011,8 @@
center_segment1,
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP],
- &cmode->eht_capab[IEEE80211_MODE_AP])) {
+ &cmode->eht_capab[IEEE80211_MODE_AP],
+ hostapd_get_punct_bitmap(hapd))) {
wpa_printf(MSG_ERROR, "Can't set freq params");
return -1;
}
@@ -929,7 +1034,8 @@
int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
const u8 *qos_map_set, u8 qos_map_set_len)
{
- if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
+ if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
return 0;
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
qos_map_set_len);
@@ -1025,6 +1131,12 @@
os_memset(¶ms, 0, sizeof(params));
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)
+ params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
/*
* If no chanlist config parameter is provided, include all enabled
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 331b0ea..d7e79c8 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -26,6 +26,8 @@
struct wpabuf *assocresp);
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta);
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
@@ -59,6 +61,9 @@
const char *bridge, int use_existing);
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
+int hostapd_if_link_remove(struct hostapd_data *hapd,
+ enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
struct wpa_bss_params *params);
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
@@ -388,9 +393,15 @@
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
{
+ int link_id = -1;
+
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
return 0;
- return hapd->driver->stop_ap(hapd->drv_priv);
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+ return hapd->driver->stop_ap(hapd->drv_priv, link_id);
}
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
@@ -443,15 +454,28 @@
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_IEEE80211BE
+
static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
u8 link_id, const u8 *addr)
{
if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_add)
return -1;
- return hapd->driver->link_add(hapd->drv_priv, link_id, addr);
+ return hapd->driver->link_add(hapd->drv_priv, link_id, addr, hapd);
}
+
+static inline int hostapd_drv_link_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (!hapd->conf->mld_ap || !hapd->driver || !hapd->drv_priv ||
+ !hapd->driver->link_sta_remove)
+ return -1;
+
+ return hapd->driver->link_sta_remove(hapd->drv_priv, hapd->mld_link_id,
+ addr);
+}
+
#endif /* CONFIG_IEEE80211BE */
#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 20be7f8..13facab 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -55,7 +55,7 @@
struct ap_info *s;
s = iface->ap_hash[STA_HASH(ap)];
- while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+ while (s != NULL && !ether_addr_equal(s->addr, ap))
s = s->hnext;
return s;
}
@@ -100,13 +100,13 @@
s = iface->ap_hash[STA_HASH(ap->addr)];
if (s == NULL) return;
- if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(s->addr, ap->addr)) {
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
return;
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, ap->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 1488dcc..837b690 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -107,13 +107,20 @@
struct radius_server_conf srv;
struct hostapd_bss_config *conf = hapd->conf;
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
wpa_printf(MSG_DEBUG,
"MLD: Using RADIUS server of the first BSS");
- hapd->radius_srv = hapd->mld_first_bss->radius_srv;
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
+ hapd->radius_srv = first->radius_srv;
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
@@ -249,18 +256,34 @@
int authsrv_init(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
+
+ if (!first->eap_cfg) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS auth_serv does not exist. Init on its behalf");
+
+ if (authsrv_init(first))
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
#ifdef EAP_TLS_FUNCS
- hapd->ssl_ctx = hapd->mld_first_bss->ssl_ctx;
+ hapd->ssl_ctx = first->ssl_ctx;
#endif /* EAP_TLS_FUNCS */
- hapd->eap_cfg = hapd->mld_first_bss->eap_cfg;
+ hapd->eap_cfg = first->eap_cfg;
#ifdef EAP_SIM_DB
- hapd->eap_sim_db_priv = hapd->mld_first_bss->eap_sim_db_priv;
+ hapd->eap_sim_db_priv = first->eap_sim_db_priv;
#endif /* EAP_SIM_DB */
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef EAP_TLS_FUNCS
if (hapd->conf->eap_server &&
@@ -376,7 +399,8 @@
void authsrv_deinit(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
wpa_printf(MSG_DEBUG,
"MLD: Deinit auth_serv of a non-first BSS");
@@ -390,6 +414,7 @@
#endif /* EAP_TLS_FUNCS */
return;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef RADIUS_SERVER
radius_server_deinit(hapd->radius_srv);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 1b5cea9..f8ce810 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -239,12 +239,10 @@
continue; /* can use same entry */
}
- if (start && prev) {
+ if (start && prev)
pos = hostapd_eid_country_add(hapd, pos, end,
chan_spacing,
start, prev);
- start = NULL;
- }
/* Start new group */
start = prev = chan;
@@ -405,6 +403,81 @@
}
+static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_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 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
@@ -565,19 +638,78 @@
}
-static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *req,
- int is_p2p, size_t *resp_len,
- const u8 *known_bss, u8 known_bss_len)
+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)
+ return len;
+
+ len += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+ 3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+ /* An additional Transmit Power Envelope element for
+ * subordinate client */
+ if (he_reg_is_indoor(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+
+ /* An additional Transmit Power Envelope element for
+ * default client with unit interpretation of regulatory
+ * client EIRP */
+ if (hapd->iconf->reg_def_cli_eirp != -1 &&
+ he_reg_is_sp(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ return len;
+}
+
+
+struct probe_resp_params {
+ const struct ieee80211_mgmt *req;
+ 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;
+
struct ieee80211_mgmt *resp;
- u8 *pos, *epos, *csa_pos;
- size_t buflen;
+ size_t resp_len;
+ u8 *csa_pos;
+ u8 *ecsa_pos;
+ const u8 *known_bss;
+ u8 known_bss_len;
- hapd = hostapd_mbssid_get_tx_bss(hapd);
+#ifdef CONFIG_IEEE80211AX
+ u8 *cca_pos;
+#endif /* CONFIG_IEEE80211AX */
+};
-#define MAX_PROBERESP_LEN 768
- buflen = MAX_PROBERESP_LEN;
+
+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 */
+}
+
+
+static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ size_t buflen = 0;
+
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
@@ -592,28 +724,16 @@
#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->presp_elements)
+ buflen += wpabuf_len(hapd->conf->presp_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
if (hapd->conf->vendor_vht) {
buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
2 + sizeof(struct ieee80211_vht_operation);
}
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- buflen += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ buflen += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -622,58 +742,49 @@
if (hapd->iconf->punct_bitmap)
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
- /*
- * TODO: Multi-Link element has variable length and can be
- * long based on the common info and number of per
- * station profiles. For now use 256.
- */
- if (hapd->conf->mld_ap)
- buflen += 256;
+ if (!params->is_ml_sta_info && hapd->conf->mld_ap) {
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ buflen += hostapd_eid_eht_ml_beacon_len(
+ ml_elem_ap, params->mld_info, !!params->mld_ap);
+ }
}
#endif /* CONFIG_IEEE80211BE */
buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
- known_bss, known_bss_len, NULL);
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ 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_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);
- resp = os_zalloc(buflen);
- if (resp == NULL)
- return NULL;
+ return buflen;
+}
- epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
- resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_RESP);
- /* Unicast the response to all requests on bands other than 6 GHz. For
- * the 6 GHz, unicast is used only if the actual SSID is not included in
- * the Beacon frames. Otherwise, broadcast response is used per IEEE
- * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
- * the Probe Response frame template for the unsolicited (i.e., not as
- * a response to a specific request) case. */
- if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
- hapd->conf->ignore_broadcast_ssid))
- os_memcpy(resp->da, req->sa, ETH_ALEN);
- else
- os_memset(resp->da, 0xff, ETH_ALEN);
+static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ u8 *pos, size_t len)
+{
+ u8 *csa_pos;
+ u8 *epos;
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ epos = pos + len;
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
- resp->u.probe_resp.beacon_int =
- host_to_le16(hapd->iconf->beacon_int);
-
- /* hardware or low-level driver will setup seq_ctrl and timestamp */
- resp->u.probe_resp.capab_info =
- host_to_le16(hostapd_own_capab_info(hapd));
-
- pos = resp->u.probe_resp.variable;
- *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;
+ 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;
+ }
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
@@ -686,11 +797,18 @@
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
- /* CSA IE */
- csa_pos = hostapd_eid_csa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_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;
+ }
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -701,16 +819,23 @@
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,
- NULL, known_bss, known_bss_len, NULL, NULL,
- NULL, 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 */
- csa_pos = hostapd_eid_ecsa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_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;
+ }
pos = hostapd_eid_supported_op_classes(hapd, pos);
pos = hostapd_eid_ht_capabilities(hapd, pos);
@@ -720,7 +845,7 @@
* when a list of known BSSes is included in the Probe Request frame. */
pos = hostapd_eid_ext_capab(hapd, pos,
hapd->iconf->mbssid >= MBSSID_ENABLED &&
- !known_bss_len);
+ !params->known_bss_len);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
@@ -754,7 +879,9 @@
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ if (!params->is_ml_sta_info)
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+ true);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
@@ -768,7 +895,9 @@
/* BSS Color Change Announcement element */
cca_pos = hostapd_eid_cca(hapd, pos);
if (cca_pos != pos)
- hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+ params->cca_pos = cca_pos - 2;
+ else
+ params->cca_pos = NULL;
pos = cca_pos;
pos = hostapd_eid_spatial_reuse(hapd, pos);
@@ -779,8 +908,14 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
- if (hapd->conf->mld_ap)
- pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true);
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ if (ml_elem_ap->conf->mld_ap)
+ pos = hostapd_eid_eht_ml_beacon(
+ ml_elem_ap, params->mld_info,
+ pos, !!params->mld_ap);
+
pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_eht_operation(hapd, pos);
}
@@ -807,7 +942,7 @@
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
- if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+ if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p &&
hapd->p2p_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
wpabuf_len(hapd->p2p_probe_resp_ie));
@@ -824,9 +959,13 @@
pos = hostapd_eid_hs20_indication(hapd, pos);
#endif /* CONFIG_HS20 */
- pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_mbo(hapd, pos, epos - pos);
+ 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),
@@ -834,11 +973,171 @@
pos += wpabuf_len(hapd->conf->vendor_elements);
}
- *resp_len = pos - (u8 *) resp;
- return (u8 *) resp;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->presp_elements) {
+ os_memcpy(pos, wpabuf_head(hapd->conf->presp_elements),
+ wpabuf_len(hapd->conf->presp_elements));
+ pos += wpabuf_len(hapd->conf->presp_elements);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return pos;
}
+static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ u8 *pos;
+ size_t buflen;
+
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
+#define MAX_PROBERESP_LEN 768
+ buflen = MAX_PROBERESP_LEN;
+ buflen += hostapd_probe_resp_elems_len(hapd, params);
+ params->resp = os_zalloc(buflen);
+ if (!params->resp) {
+ params->resp_len = 0;
+ return;
+ }
+
+ params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_PROBE_RESP);
+ /* Unicast the response to all requests on bands other than 6 GHz. For
+ * the 6 GHz, unicast is used only if the actual SSID is not included in
+ * the Beacon frames. Otherwise, broadcast response is used per IEEE
+ * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+ * the Probe Response frame template for the unsolicited (i.e., not as
+ * a response to a specific request) case. */
+ if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+ hapd->conf->ignore_broadcast_ssid))
+ os_memcpy(params->resp->da, params->req->sa, ETH_ALEN);
+ else
+ os_memset(params->resp->da, 0xff, ETH_ALEN);
+ os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN);
+
+ os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN);
+ params->resp->u.probe_resp.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ params->resp->u.probe_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+
+ pos = hostapd_probe_resp_fill_elems(hapd, params,
+ params->resp->u.probe_resp.variable,
+ buflen);
+
+ params->resp_len = pos - (u8 *) params->resp;
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ 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;
+ params->mld_info = os_zalloc(sizeof(*params->mld_info));
+ if (!params->mld_info)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Got ML probe request with AP MLD ID %d for links %04x",
+ mld_id, links);
+
+ 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)
+ continue;
+
+ /* Only include requested links */
+ if (!(BIT(mld_link_id) & links))
+ 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);
+
+ 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);
+ }
+
+ if (mld_id != -1 && !params->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No nontransmitted BSSID for MLD ID %d",
+ mld_id);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ hostapd_free_probe_resp_params(params);
+ params->mld_ap = NULL;
+ params->mld_info = NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
enum ssid_match_result {
NO_SSID_MATCH,
EXACT_SSID_MATCH,
@@ -953,7 +1252,7 @@
struct hostapd_sta_info *info;
dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
- if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, info->addr))
return info;
return NULL;
@@ -1037,21 +1336,109 @@
#endif /* CONFIG_TAXONOMY */
+#ifdef CONFIG_IEEE80211BE
+static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
+ int *mld_id, u16 *links)
+{
+ u16 ml_control;
+ const struct element *sub;
+ const u8 *pos;
+ size_t len;
+
+ *mld_id = -1;
+ *links = 0xffff;
+
+ if (ml_len < sizeof(struct ieee80211_eht_ml))
+ return false;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_PROBE_REQ) {
+ wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req");
+ return false;
+ }
+
+ if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: ML probe req too short");
+ return false;
+ }
+
+ pos = ml->variable;
+ len = pos[0];
+ if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe request with invalid length");
+ return false;
+ }
+
+ if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) {
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req too short for MLD ID");
+ return false;
+ }
+
+ *mld_id = pos[1];
+ }
+ pos += len;
+
+ /* Parse subelements (if there are any) */
+ len = ml_len - len - sizeof(struct ieee80211_eht_ml);
+ for_each_element_id(sub, 0, pos, len) {
+ const struct ieee80211_eht_per_sta_profile *sta;
+ u16 sta_control;
+
+ if (*links == 0xffff)
+ *links = 0;
+
+ if (sub->datalen <
+ sizeof(struct ieee80211_eht_per_sta_profile)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req %d too short for sta profile",
+ sub->datalen);
+ return false;
+ }
+
+ sta = (struct ieee80211_eht_per_sta_profile *) sub->data;
+
+ /*
+ * Extract the link ID, do not return whether a complete or
+ * partial profile was requested.
+ */
+ sta_control = le_to_host16(sta->sta_control);
+ *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
+ }
+
+ if (!for_each_element_completed(sub, pos, len)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req sub-elements parsing error");
+ return false;
+ }
+
+ return true;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal)
{
- u8 *resp;
struct ieee802_11_elems elems;
const u8 *ie;
size_t ie_len;
- size_t i, resp_len;
+ size_t i;
int noack;
enum ssid_match_result res;
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
struct radius_sta rad_info;
+ struct probe_resp_params params;
+#ifdef CONFIG_IEEE80211BE
+ int mld_id;
+ u16 links;
+#endif /* CONFIG_IEEE80211BE */
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
@@ -1217,7 +1604,7 @@
else
hessid = elems.interworking + 1 + 2;
if (!is_broadcast_ether_addr(hessid) &&
- os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ !ether_addr_equal(hessid, hapd->conf->hessid)) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for mismatching HESSID " MACSTR
" ignored",
@@ -1283,10 +1670,28 @@
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
" signal=%d", MAC2STR(mgmt->sa), ssi_signal);
- resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
- &resp_len, elems.mbssid_known_bss,
- elems.mbssid_known_bss_len);
- if (resp == NULL)
+ os_memset(¶ms, 0, sizeof(params));
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && elems.probe_req_mle &&
+ parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle,
+ elems.probe_req_mle_len, &mld_id, &links)) {
+ hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt,
+ mld_id, links);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ params.req = mgmt;
+ 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);
+
+ hostapd_free_probe_resp_params(¶ms);
+
+ if (!params.resp)
return;
/*
@@ -1298,24 +1703,23 @@
csa_offs_len = 0;
if (hapd->csa_in_progress) {
- if (hapd->cs_c_off_proberesp)
+ if (params.csa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_proberesp;
+ params.csa_pos - (u8 *) params.resp;
- if (hapd->cs_c_off_ecsa_proberesp)
+ if (params.ecsa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_ecsa_proberesp;
+ params.ecsa_pos - (u8 *) params.resp;
}
- ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
- resp_len, noack,
+ ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
if (ret < 0)
wpa_printf(MSG_INFO, "handle_probe_req: send failed");
- os_free(resp);
+ os_free(params.resp);
wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
@@ -1326,6 +1730,8 @@
static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
size_t *resp_len)
{
+ struct probe_resp_params params;
+
/* check probe response offloading caps and print warnings */
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
return NULL;
@@ -1355,7 +1761,32 @@
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+ os_memset(¶ms, 0, sizeof(params));
+ params.req = NULL;
+ 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;
+
+ hostapd_gen_probe_resp(hapd, ¶ms);
+ *resp_len = params.resp_len;
+ if (!params.resp)
+ return NULL;
+
+ /* TODO: Avoid passing these through struct hostapd_data */
+ if (params.csa_pos)
+ hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp;
+ if (params.ecsa_pos)
+ hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos -
+ (u8 *) params.resp;
+#ifdef CONFIG_IEEE80211AX
+ if (params.cca_pos)
+ hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp;
+#endif /* CONFIG_IEEE80211AX */
+
+ return (u8 *) params.resp;
}
#endif /* NEED_AP_MLME */
@@ -1363,18 +1794,29 @@
#ifdef CONFIG_IEEE80211AX
/* Unsolicited broadcast Probe Response transmission, 6 GHz only */
-static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
- struct wpa_driver_ap_params *params)
+u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ struct unsol_bcast_probe_resp *ubpr)
{
+ struct probe_resp_params probe_params;
+
if (!is_6ghz_op_class(hapd->iconf->op_class))
return NULL;
- params->unsol_bcast_probe_resp_interval =
+ ubpr->unsol_bcast_probe_resp_interval =
hapd->conf->unsol_bcast_probe_resp_interval;
- return hostapd_gen_probe_resp(hapd, NULL, 0,
- ¶ms->unsol_bcast_probe_resp_tmpl_len,
- NULL, 0);
+ os_memset(&probe_params, 0, sizeof(probe_params));
+ probe_params.req = NULL;
+ 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;
+
+ hostapd_gen_probe_resp(hapd, &probe_params);
+ ubpr->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
+ return (u8 *) probe_params.resp;
}
#endif /* CONFIG_IEEE80211AX */
@@ -1588,6 +2030,17 @@
FD_FRAME_CTL_CAP_PRESENT;
total_len += 4 + 1 + 2;
+ /* Fill primary channel information for 6 GHz channels with over 20 MHz
+ * bandwidth, if the primary channel is not a PSC */
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ !is_6ghz_psc_frequency(ieee80211_chan_to_freq(
+ NULL, hapd->iconf->op_class,
+ hapd->iconf->channel)) &&
+ op_class_to_bandwidth(hapd->iconf->op_class) > 20) {
+ ctl |= FD_FRAME_CTL_PRI_CHAN_PRESENT;
+ total_len += 2;
+ }
+
/* Check for optional subfields and calculate length */
if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
@@ -1600,20 +2053,15 @@
total_len += 3;
}
- total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
pos = hostapd_eid_fils_indic(hapd, buf, 0);
buf_len = pos - buf;
total_len += buf_len;
-#ifdef CONFIG_IEEE80211AX
- /* Transmit Power Envelope element(s) */
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- total_len += 4;
- if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
- total_len += 4;
- }
-#endif /* CONFIG_IEEE80211AX */
+ /* he_elem_len() may return too large a value for FD frame, but that is
+ * fine here since this is used as the maximum length of the buffer. */
+ total_len += he_elem_len(hapd);
head = os_zalloc(total_len);
if (!head)
@@ -1654,9 +2102,11 @@
WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
pos += 2;
- /* Operating Class - not present */
-
- /* Primary Channel - not present */
+ /* Operating Class and Primary Channel - if a 6 GHz chan is non PSC */
+ if (ctl & FD_FRAME_CTL_PRI_CHAN_PRESENT) {
+ *pos++ = hapd->iconf->op_class;
+ *pos++ = hapd->iconf->channel;
+ }
/* AP Configuration Sequence Number - not present */
@@ -1679,7 +2129,7 @@
/* Fill in the Length field value */
*length_pos = pos - (length_pos + 1);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
/* FILS Indication element */
if (buf_len) {
@@ -1763,23 +2213,7 @@
}
#endif /* CONFIG_IEEE80211AC */
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- tail_len += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ tail_len += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -1801,10 +2235,13 @@
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
hapd == hostapd_mbssid_get_tx_bss(hapd))
tail_len += 5; /* Multiple BSSID Configuration element */
- tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
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) {
@@ -1937,7 +2374,7 @@
tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
- tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
+ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
@@ -1966,8 +2403,8 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
- true);
+ 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);
@@ -2016,6 +2453,13 @@
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));
@@ -2048,7 +2492,9 @@
/* 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,
@@ -2093,7 +2539,9 @@
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;
@@ -2194,8 +2642,8 @@
params->fd_frame_tmpl = NULL;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
- os_free(params->unsol_bcast_probe_resp_tmpl);
- params->unsol_bcast_probe_resp_tmpl = NULL;
+ os_free(params->ubpr.unsol_bcast_probe_resp_tmpl);
+ params->ubpr.unsol_bcast_probe_resp_tmpl = NULL;
#endif /* CONFIG_IEEE80211AX */
os_free(params->allowed_freqs);
params->allowed_freqs = NULL;
@@ -2210,6 +2658,7 @@
struct hostapd_config *iconf = iface->conf;
struct hostapd_hw_modes *cmode = iface->current_mode;
struct wpabuf *beacon, *proberesp, *assocresp;
+ bool twt_he_responder = false;
int res, ret = -1, i;
struct hostapd_hw_modes *mode;
@@ -2253,11 +2702,13 @@
params.he_bss_color_partial =
hapd->iface->conf->he_op.he_bss_color_partial;
params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
- params.twt_responder = hostapd_get_he_twt_responder(hapd,
- IEEE80211_MODE_AP);
- params.unsol_bcast_probe_resp_tmpl =
- hostapd_unsol_bcast_probe_resp(hapd, ¶ms);
+ twt_he_responder = hostapd_get_he_twt_responder(hapd,
+ IEEE80211_MODE_AP);
+ params.ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, ¶ms.ubpr);
#endif /* CONFIG_IEEE80211AX */
+ params.twt_responder =
+ twt_he_responder || hostapd_get_ht_vht_twt_responder(hapd);
hapd->reenable_beacon = 0;
#ifdef CONFIG_SAE
params.sae_pwe = hapd->conf->sae_pwe;
@@ -2283,8 +2734,15 @@
hostapd_get_oper_centr_freq_seg1_idx(iconf),
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP],
- &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
+ &cmode->eht_capab[IEEE80211_MODE_AP],
+ hostapd_get_punct_bitmap(hapd)) == 0) {
+ freq.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ freq.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
params.freq = &freq;
+ }
for (i = 0; i < hapd->iface->num_hw_features; i++) {
mode = &hapd->iface->hw_features[i];
@@ -2322,7 +2780,7 @@
struct hostapd_iface *iface = hapd->iface;
int ret;
size_t i, j;
- bool is_6g;
+ bool is_6g, hapd_mld = false;
ret = __ieee802_11_set_beacon(hapd);
if (ret != 0)
@@ -2331,27 +2789,33 @@
if (!iface->interfaces || iface->interfaces->count <= 1)
return 0;
+#ifdef CONFIG_IEEE80211BE
+ hapd_mld = hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
is_6g = is_6ghz_op_class(iface->conf->op_class);
for (j = 0; j < iface->interfaces->count; j++) {
struct hostapd_iface *other;
- bool mld_ap = false;
+ bool other_iface_6g;
other = iface->interfaces->iface[j];
if (other == iface || !other || !other->conf)
continue;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == other->bss[0]->conf->mld_id)
- mld_ap = true;
-#endif /* CONFIG_IEEE80211BE */
+ other_iface_6g = is_6ghz_op_class(other->conf->op_class);
- if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
- !mld_ap)
+ if (is_6g == other_iface_6g && !hapd_mld)
continue;
for (i = 0; i < other->num_bss; i++) {
+#ifdef CONFIG_IEEE80211BE
+ if (is_6g == other_iface_6g &&
+ !(hapd_mld && other->bss[i]->conf->mld_ap &&
+ hostapd_is_ml_partner(hapd, other->bss[i])))
+ continue;
+#endif /* CONFIG_IEEE80211BE */
+
if (other->bss[i] && other->bss[i]->started)
__ieee802_11_set_beacon(other->bss[i]);
}
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index b32b2a7..e381542 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -33,4 +33,7 @@
const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
+u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ struct unsol_bcast_probe_resp *ubpr);
+
#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index a6fcb7e..d4d73de 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -277,6 +277,14 @@
return len;
len += ret;
+ if (sta->max_idle_period) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_idle_period=%d\n", sta->max_idle_period);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0)
len += res;
@@ -348,11 +356,15 @@
if (sta->supp_op_classes &&
buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
- len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+ res = os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->supp_op_classes + 1,
sta->supp_op_classes[0]);
- len += os_snprintf(buf + len, buflen - len, "\n");
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
if (sta->power_capab) {
@@ -364,6 +376,34 @@
len += ret;
}
+#ifdef CONFIG_IEEE80211AX
+ if ((sta->flags & WLAN_STA_HE) && sta->he_capab) {
+ res = os_snprintf(buf + len, buflen - len, "he_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->he_capab,
+ sta->he_capab_len);
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+ if ((sta->flags & WLAN_STA_EHT) && sta->eht_capab) {
+ res = os_snprintf(buf + len, buflen - len, "eht_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->eht_capab,
+ sta->eht_capab_len);
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AC
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
res = os_snprintf(buf + len, buflen - len,
@@ -372,6 +412,16 @@
vht_capabilities_info));
if (!os_snprintf_error(buflen - len, res))
len += res;
+
+ res = os_snprintf(buf + len, buflen - len, "vht_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ (const u8 *) sta->vht_capabilities,
+ sizeof(*sta->vht_capabilities));
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
#endif /* CONFIG_IEEE80211AC */
@@ -386,11 +436,15 @@
if (sta->ext_capability &&
buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
- len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+ res = os_snprintf(buf + len, buflen - len, "ext_capab=");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->ext_capability + 1,
sta->ext_capability[0]);
- len += os_snprintf(buf + len, buflen - len, "\n");
+ res = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
}
if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
@@ -419,6 +473,21 @@
len += ret;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sta->mld_info.mld_sta) {
+ for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) {
+ if (!sta->mld_info.links[i].valid)
+ continue;
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "peer_addr[%d]=" MACSTR "\n",
+ i, MAC2STR(sta->mld_info.links[i].peer_addr));
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return len;
}
@@ -830,6 +899,53 @@
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+
+ if (is_6ghz_op_class(iface->conf->op_class) &&
+ hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_320MHZ) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "eht_bw320_offset=%d\n",
+ iface->conf->eht_bw320_offset);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *link_bss;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "num_links=%d\n",
+ hapd->mld->num_links);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* Self BSS */
+ ret = os_snprintf(buf + len, buflen - len,
+ "link_id=%d\n"
+ "link_addr=" MACSTR "\n",
+ hapd->mld_link_id,
+ MAC2STR(hapd->own_addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* Partner BSSs */
+ for_each_mld_link(link_bss, hapd) {
+ if (link_bss == hapd)
+ continue;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "partner_link[%d]=" MACSTR
+ "\n",
+ link_bss->mld_link_id,
+ MAC2STR(link_bss->own_addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -955,8 +1071,8 @@
"mld_addr[%d]=" MACSTR "\n"
"mld_id[%d]=%d\n"
"mld_link_id[%d]=%d\n",
- (int) i, MAC2STR(bss->mld_addr),
- (int) i, bss->conf->mld_id,
+ (int) i, MAC2STR(bss->mld->mld_addr),
+ (int) i, hostapd_get_mld_id(bss),
(int) i, bss->mld_link_id);
if (os_snprintf_error(buflen - len, ret))
return len;
@@ -1094,7 +1210,7 @@
return -1;
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
- pmkid, expiration, akmp);
+ pmkid, expiration, akmp, NULL);
}
@@ -1315,6 +1431,8 @@
req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
if (os_strstr(cmd, " disassoc_imminent=1"))
req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ if (os_strstr(cmd, " link_removal_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
#ifdef CONFIG_MBO
pos = os_strstr(cmd, "mbo=");
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 9a5d3c8..af9dc16 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -14,6 +14,7 @@
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
+#include "beacon.h"
#include "ap_drv_ops.h"
#include "drivers/driver.h"
#include "dfs.h"
@@ -188,7 +189,7 @@
* If it's not allowed to use the first channel as primary, decline the
* whole channel range. */
if (!chan_pri_allowed(first_chan)) {
- wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
+ wpa_printf(MSG_DEBUG, "DFS: primary channel not allowed");
return 0;
}
@@ -551,6 +552,8 @@
if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
return NULL;
chan_idx = _rand % num_available_chandefs;
+ wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ chan_idx, num_available_chandefs);
dfs_find_channel(iface, &chan, chan_idx, type);
if (!chan) {
wpa_printf(MSG_DEBUG, "DFS: no random channel found");
@@ -970,6 +973,7 @@
struct csa_settings csa_settings;
u8 new_vht_oper_chwidth;
unsigned int i;
+ unsigned int num_err = 0;
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
@@ -983,6 +987,11 @@
os_memset(&csa_settings, 0, sizeof(csa_settings));
csa_settings.cs_count = 5;
csa_settings.block_tx = 1;
+ csa_settings.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+ csa_settings.link_id = iface->bss[0]->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_MESH
if (iface->mconf)
ieee80211_mode = IEEE80211_MODE_MESH;
@@ -1002,7 +1011,8 @@
oper_centr_freq_seg1_idx,
cmode->vht_capab,
&cmode->he_capab[ieee80211_mode],
- &cmode->eht_capab[ieee80211_mode]);
+ &cmode->eht_capab[ieee80211_mode],
+ hostapd_get_punct_bitmap(iface->bss[0]));
if (err) {
wpa_printf(MSG_ERROR,
@@ -1014,10 +1024,10 @@
for (i = 0; i < iface->num_bss; i++) {
err = hostapd_switch_channel(iface->bss[i], &csa_settings);
if (err)
- break;
+ num_err++;
}
- if (err) {
+ if (num_err == iface->num_bss) {
wpa_printf(MSG_WARNING,
"DFS failed to schedule CSA (%d) - trying fallback",
err);
@@ -1044,7 +1054,7 @@
}
-static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
{
int sec = 0;
enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
@@ -1119,7 +1129,7 @@
hostapd_set_oper_centr_freq_seg1_idx(
iface->conf, iface->radar_background.centr_freq_seg1_idx);
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return hostapd_dfs_request_channel_switch(
iface, iface->conf->channel, iface->freq,
@@ -1134,14 +1144,23 @@
int cf1, int cf2)
{
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",
- success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ "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,
+ iface->radar_detected);
if (success) {
/* Complete iface/ap configuration */
if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
- /* Complete AP configuration for the first bring up. */
- if (iface->state != HAPD_IFACE_ENABLED)
+ /* Complete AP configuration for the first bring up. If
+ * a radar was detected in this channel, interface setup
+ * will be handled in
+ * 1. hostapd_event_ch_switch() if switching to a
+ * non-DFS channel
+ * 2. on next CAC complete event if switching to another
+ * DFS channel.
+ */
+ if (iface->state != HAPD_IFACE_ENABLED &&
+ !iface->radar_detected)
hostapd_setup_interface_complete(iface, 0);
else
iface->cac_started = 0;
@@ -1183,9 +1202,10 @@
}
} else if (hostapd_dfs_is_background_event(iface, freq)) {
iface->radar_background.cac_started = 0;
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
+ iface->radar_detected = false;
return 0;
}
@@ -1317,7 +1337,7 @@
* Just select a new random channel according to the
* regulations for monitoring.
*/
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return 0;
}
@@ -1429,6 +1449,8 @@
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+ iface->radar_detected = true;
+
/* Proceed only if DFS is not offloaded to the driver */
if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
return 0;
@@ -1479,7 +1501,7 @@
} else if (dfs_use_radar_background(iface) &&
iface->radar_background.channel == -1) {
/* Reset radar background chain if disabled */
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
return 0;
@@ -1522,9 +1544,17 @@
iface->radar_background.cac_started = 1;
} else {
/* This is called when the driver indicates that an offloaded
- * DFS has started CAC. */
+ * DFS has started CAC. radar_detected might be set for previous
+ * DFS channel. Clear it for this new CAC process. */
hostapd_set_state(iface, HAPD_IFACE_DFS);
iface->cac_started = 1;
+
+ /* Clear radar_detected in case it is for the previous
+ * frequency. Also remove disabled link's information in RNR
+ * element from other links. */
+ iface->radar_detected = false;
+ if (iface->interfaces && iface->interfaces->count > 1)
+ ieee802_11_set_beacons(iface);
}
/* TODO: How to check CAC time for ETSI weather channels? */
iface->dfs_cac_ms = 60000;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 7a8ea4e..d1bffa8 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -539,8 +539,15 @@
return;
}
- if (hapd->dpp_auth_ok_on_ack)
+ if (hapd->dpp_auth_ok_on_ack) {
hostapd_dpp_auth_success(hapd, 1);
+ if (!hapd->dpp_auth) {
+ /* The authentication session could have been removed in
+ * some error cases, e.g., when starting GAS client and
+ * failing to send the initial request. */
+ return;
+ }
+ }
if (!is_broadcast_ether_addr(dst) && !ok) {
wpa_printf(MSG_DEBUG,
@@ -1413,7 +1420,7 @@
}
if (!is_zero_ether_addr(auth->peer_mac_addr) &&
- os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1463,7 +1470,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1572,7 +1579,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1858,7 +1865,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2133,7 +2140,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -2907,7 +2914,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -3073,7 +3080,7 @@
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
- os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, auth->peer_mac_addr)) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
data_len) == 0) {
@@ -3094,6 +3101,13 @@
* exchange. */
dpp_notify_auth_success(hapd->dpp_auth, 1);
hapd->dpp_auth_ok_on_ack = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_hexdump(MSG_DEBUG,
@@ -3927,6 +3941,7 @@
eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
hapd, NULL);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
@@ -3948,11 +3963,25 @@
ifaces->dpp_pb_time.usec = 0;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
+ hapd->dpp_pkex_bi = NULL;
os_free(hapd->dpp_pkex_auth_cmd);
hapd->dpp_pkex_auth_cmd = NULL;
if (ifaces->dpp_pb_bi) {
char id[20];
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (h->dpp_pkex_bi == ifaces->dpp_pb_bi)
+ h->dpp_pkex_bi = NULL;
+ }
+ }
os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
dpp_bootstrap_remove(ifaces->dpp, id);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 98794c2..233984f 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -42,6 +42,7 @@
#include "dpp_hostapd.h"
#include "fils_hlp.h"
#include "neighbor_db.h"
+#include "nan_usd_ap.h"
#ifdef CONFIG_FILS
@@ -52,6 +53,7 @@
struct ieee802_11_elems elems;
u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
int new_assoc;
+ bool updated;
wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
__func__, MAC2STR(sta->addr));
@@ -76,11 +78,13 @@
sta->fils_pending_assoc_is_reassoc,
WLAN_STATUS_SUCCESS,
buf, p - buf);
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
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);
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
@@ -158,7 +162,7 @@
return -1;
}
- mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlebuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlebuf) {
wpa_printf(MSG_ERROR,
"MLO: Basic Multi-Link element not found in (Re)Association Response frame");
@@ -263,6 +267,7 @@
#ifdef CONFIG_OWE
struct hostapd_iface *iface = hapd->iface;
#endif /* CONFIG_OWE */
+ bool updated = false;
if (addr == NULL) {
/*
@@ -279,7 +284,7 @@
if (is_multicast_ether_addr(addr) ||
is_zero_ether_addr(addr) ||
- os_memcmp(addr, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(addr, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -358,7 +363,7 @@
int i, num_valid_links = 0;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, addr, ETH_ALEN);
info->links[link_id].valid = true;
@@ -508,11 +513,15 @@
"Failed to initialize WPA state machine");
return -1;
}
+ wpa_auth_set_rsn_override(sta->wpa_sm,
+ elems.rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm,
+ elems.rsne_override_2 != NULL);
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ wpa_auth_set_ml_info(sta->wpa_sm,
sta->mld_assoc_link_id,
&sta->mld_info);
}
@@ -523,7 +532,7 @@
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);
+ elems.owe_dh, elems.owe_dh_len, NULL);
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
switch (res) {
@@ -845,18 +854,30 @@
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
/* Keep compiler silent about unused variables */
if (status) {
}
#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
+ !!reassoc, WLAN_STATUS_SUCCESS,
+ true)) {
+ 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);
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -893,6 +914,54 @@
}
+static void hostapd_remove_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ 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_DISASSOC);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_notif_disassoc_mld(struct hostapd_data *assoc_hapd,
+ struct sta_info *sta,
+ const u8 *addr)
+{
+ unsigned int link_id, 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)
+ 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);
+ }
+ }
+
+ /* Remove STA in assoc link */
+ hostapd_remove_sta(assoc_hapd, sta);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -914,6 +983,50 @@
HOSTAPD_LEVEL_INFO, "disassociated");
sta = ap_get_sta(hapd, addr);
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_is_mld_ap(hapd)) {
+ struct hostapd_data *assoc_hapd;
+ unsigned int i;
+
+ if (!sta) {
+ /* Find non-MLO cases from any of the affiliated AP
+ * links. */
+ for (i = 0; i < hapd->iface->interfaces->count; ++i) {
+ struct hostapd_iface *h =
+ hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (!hconf->mld_ap ||
+ hconf->mld_id != hapd->conf->mld_id)
+ continue;
+
+ sta = ap_get_sta(h_hapd, addr);
+ if (sta) {
+ if (!sta->mld_info.mld_sta) {
+ hapd = h_hapd;
+ goto legacy;
+ }
+ break;
+ }
+ }
+ } else if (!sta->mld_info.mld_sta) {
+ goto legacy;
+ }
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "Disassociation notification for unknown STA "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (sta)
+ hostapd_notif_disassoc_mld(assoc_hapd, sta, addr);
+ return;
+ }
+
+legacy:
+#endif /* CONFIG_IEEE80211BE */
if (sta == NULL) {
wpa_printf(MSG_DEBUG,
"Disassociation notification for unknown STA "
@@ -921,13 +1034,7 @@
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_DISASSOC);
- sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
- ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
- ap_free_sta(hapd, sta);
+ hostapd_remove_sta(hapd, sta);
}
@@ -1164,6 +1271,8 @@
hostapd_set_oper_chwidth(hapd->iconf, chwidth);
hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+ /* Auto-detect new bw320_offset */
+ hostapd_set_and_check_bw320_offset(hapd->iconf, 0);
#ifdef CONFIG_IEEE80211BE
hapd->iconf->punct_bitmap = punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
@@ -1270,6 +1379,18 @@
int err = 0;
struct hostapd_channel_data *pri_chan;
+#ifdef CONFIG_IEEE80211BE
+ if (acs_res->link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(hapd, acs_res->link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for EVENT_ACS_CHANNEL_SELECTED link_id=%d",
+ acs_res->link_id);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (hapd->iconf->channel) {
wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
hapd->iconf->channel);
@@ -1420,7 +1541,6 @@
#ifdef CONFIG_IEEE80211R_AP
static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
- const u8 *bssid,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
{
@@ -1496,7 +1616,7 @@
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+ wpa_ft_process_auth(sta->wpa_sm,
rx_auth->auth_transaction, rx_auth->ies,
rx_auth->ies_len,
hostapd_notify_auth_ft_finish, hapd);
@@ -1576,6 +1696,7 @@
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
if (plen >= 2 + 4 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
@@ -1591,6 +1712,23 @@
return;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ 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) {
+ 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);
+ return;
+ }
+#endif /* CONFIG_NAN_USD */
}
#endif /* NEED_AP_MLME */
@@ -1614,10 +1752,38 @@
}
+static struct hostapd_data *
+switch_link_scan(struct hostapd_data *hapd, u64 scan_cookie)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && scan_cookie != 0) {
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h;
+ struct hostapd_data *h_hapd;
+
+ h = hapd->iface->interfaces->iface[i];
+ h_hapd = h->bss[0];
+ if (!hostapd_is_ml_partner(hapd, h_hapd))
+ continue;
+
+ if (h_hapd->scan_cookie == scan_cookie) {
+ h_hapd->scan_cookie = 0;
+ return h_hapd;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd;
+}
+
+
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- const u8 *bssid)
+ const u8 *bssid, int link_id)
{
size_t i;
@@ -1628,8 +1794,35 @@
return HAPD_BROADCAST;
for (i = 0; i < iface->num_bss; i++) {
- if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
- return iface->bss[i];
+ struct hostapd_data *hapd;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *p_hapd;
+#endif /* CONFIG_IEEE80211BE */
+
+ hapd = iface->bss[i];
+ if (ether_addr_equal(bssid, hapd->own_addr))
+ return hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (ether_addr_equal(bssid, hapd->own_addr) ||
+ (hapd->conf->mld_ap &&
+ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+ link_id == hapd->mld_link_id))
+ return hapd;
+
+ if (!hapd->conf->mld_ap)
+ continue;
+
+ for_each_mld_link(p_hapd, hapd) {
+ if (p_hapd == hapd)
+ continue;
+
+ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+ link_id == p_hapd->mld_link_id))
+ return p_hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
return NULL;
@@ -1640,7 +1833,7 @@
const u8 *bssid, const u8 *addr,
int wds)
{
- hapd = get_hapd_bssid(hapd->iface, bssid);
+ hapd = get_hapd_bssid(hapd->iface, bssid, -1);
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
@@ -1655,8 +1848,9 @@
const u8 *bssid;
struct hostapd_frame_info fi;
int ret;
- bool is_mld = false;
+ if (rx_mgmt->ctx)
+ hapd = rx_mgmt->ctx;
hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
iface = hapd->iface;
@@ -1680,14 +1874,7 @@
if (bssid == NULL)
return 0;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
- is_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (!is_mld)
- hapd = get_hapd_bssid(iface, bssid);
+ hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
if (!hapd) {
u16 fc = le_to_host16(hdr->frame_control);
@@ -1739,23 +1926,17 @@
struct ieee80211_hdr *hdr;
struct hostapd_data *orig_hapd, *tmp_hapd;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && link_id != -1) {
- tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
- if (tmp_hapd)
- hapd = tmp_hapd;
- }
-#endif /* CONFIG_IEEE80211BE */
orig_hapd = hapd;
hdr = (struct ieee80211_hdr *) buf;
- tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ hapd = switch_link_hapd(hapd, link_id);
+ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
if (tmp_hapd) {
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
} else if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, get_hdr_bssid(hdr, len),
- ETH_ALEN) == 0) {
+ ether_addr_equal(hapd->mld->mld_addr,
+ get_hdr_bssid(hdr, len))) {
/* AP MLD address match - use hapd pointer as-is */
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -1766,7 +1947,7 @@
if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
buf[24] != WLAN_ACTION_PUBLIC)
return;
- hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
+ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
if (!hapd || hapd == HAPD_BROADCAST)
return;
/*
@@ -1803,15 +1984,43 @@
static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
- const u8 *src)
+ 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++) {
- sta = ap_get_sta(iface->bss[j], src);
- if (sta && sta->flags & WLAN_STA_ASSOC)
- return iface->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;
@@ -1826,40 +2035,10 @@
struct hostapd_data *orig_hapd = hapd;
#ifdef CONFIG_IEEE80211BE
- if (link_id != -1) {
- struct hostapd_data *h_hapd;
-
- hapd = switch_link_hapd(hapd, link_id);
- h_hapd = hostapd_find_by_sta(hapd->iface, src);
- if (!h_hapd)
- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
- if (h_hapd)
- hapd = h_hapd;
- } else if (hapd->conf->mld_ap) {
- unsigned int i;
-
- /* Search for STA on other MLO BSSs */
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *h =
- hapd->iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
- struct hostapd_bss_config *hconf = h_hapd->conf;
-
- if (!hconf->mld_ap ||
- hconf->mld_id != hapd->conf->mld_id)
- continue;
-
- h_hapd = hostapd_find_by_sta(h, src);
- if (h_hapd) {
- hapd = h_hapd;
- break;
- }
- }
- } else {
- hapd = hostapd_find_by_sta(hapd->iface, src);
- }
+ hapd = switch_link_hapd(hapd, link_id);
+ hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
#else /* CONFIG_IEEE80211BE */
- hapd = hostapd_find_by_sta(hapd->iface, src);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
#endif /* CONFIG_IEEE80211BE */
if (!hapd) {
@@ -2164,8 +2343,8 @@
struct mld_info *info = &sta->mld_info;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
- sta->mld_assoc_link_id = link_id;;
+ ap_sta_set_mld(sta, true);
+ sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, peer, ETH_ALEN);
info->links[link_id].valid = true;
os_memcpy(info->links[link_id].local_addr, hapd->own_addr,
@@ -2189,6 +2368,54 @@
#endif /* CONFIG_OWE */
+#ifdef NEED_AP_MLME
+static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack,
+ int link_id)
+{
+ struct sta_info *sta;
+
+ hapd = switch_link_hapd(hapd, link_id);
+ hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
+
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+ return;
+ }
+
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+#endif /* NEED_AP_MLME */
+
+
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+{
+ struct hostapd_data *bss;
+ size_t i;
+
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ bss = hapd->iface->bss[i];
+ if (bss->cca_color == 0)
+ continue;
+
+ if (success)
+ hapd->iface->conf->he_op.he_bss_color = bss->cca_color;
+
+ bss->cca_in_progress = 0;
+ if (ieee802_11_set_beacon(bss)) {
+ wpa_printf(MSG_ERROR, "Failed to remove BCCA element");
+ bss->cca_in_progress = 1;
+ } else {
+ hostapd_cleanup_cca_params(bss);
+ }
+ }
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -2221,8 +2448,29 @@
michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
break;
case EVENT_SCAN_RESULTS:
+#ifdef NEED_AP_MLME
+ if (data)
+ hapd = switch_link_scan(hapd,
+ data->scan_info.scan_cookie);
+#endif /* NEED_AP_MLME */
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->iface->scan_cb && hapd->conf->mld_ap) {
+ /* Other links may be waiting for HT scan result */
+ unsigned int i;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *h =
+ hapd->iface->interfaces->iface[i];
+ struct hostapd_data *h_hapd = h->bss[0];
+
+ if (hostapd_is_ml_partner(hapd, h_hapd) &&
+ h_hapd->iface->scan_cb)
+ h_hapd->iface->scan_cb(h_hapd->iface);
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
break;
case EVENT_WPS_BUTTON_PUSHED:
hostapd_wps_button_pushed(hapd, NULL);
@@ -2246,11 +2494,11 @@
}
break;
case EVENT_EAPOL_TX_STATUS:
- hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
data->eapol_tx_status.data,
data->eapol_tx_status.data_len,
- data->eapol_tx_status.ack);
+ data->eapol_tx_status.ack,
+ data->eapol_tx_status.link_id);
break;
case EVENT_DRIVER_CLIENT_POLL_OK:
hostapd_client_poll_ok(hapd, data->client_poll.addr);
@@ -2364,6 +2612,18 @@
case EVENT_CH_SWITCH:
if (!data)
break;
+#ifdef CONFIG_IEEE80211BE
+ if (data->ch_switch.link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(
+ hapd, data->ch_switch.link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link (ID %d) BSS for EVENT_CH_SWITCH/EVENT_CH_SWITCH_STARTED",
+ data->ch_switch.link_id);
+ break;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset,
@@ -2390,26 +2650,31 @@
case EVENT_DFS_RADAR_DETECTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
break;
case EVENT_DFS_PRE_CAC_EXPIRED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_ABORTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
break;
case EVENT_DFS_NOP_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
break;
case EVENT_CHANNEL_LIST_CHANGED:
@@ -2423,6 +2688,7 @@
case EVENT_DFS_CAC_STARTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
break;
#endif /* NEED_AP_MLME */
@@ -2475,26 +2741,32 @@
/* The BSS color is shared amongst all BBSs on a specific phy.
* Therefore we always start the color change on the primary
* BSS. */
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
wpa_printf(MSG_DEBUG, "BSS color collision on %s",
hapd->conf->iface);
hostapd_switch_color(hapd->iface->bss[0],
data->bss_color_collision.bitmap);
break;
case EVENT_CCA_STARTED_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA started on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA started on %s",
hapd->conf->iface);
break;
case EVENT_CCA_ABORTED_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA aborted on %s",
hapd->conf->iface);
- hostapd_cleanup_cca_params(hapd);
+ hostapd_event_color_change(hapd, false);
break;
case EVENT_CCA_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA finished on %s",
hapd->conf->iface);
- if (hapd->cca_color)
- hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
- hostapd_cleanup_cca_params(hapd);
+ hostapd_event_color_change(hapd, true);
break;
#endif /* CONFIG_IEEE80211AX */
default:
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index d64fb8c..a34b5ba 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -546,7 +546,7 @@
" src=" MACSTR " len=%u)",
MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
(unsigned int) len);
- if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta->addr, pos + ETH_ALEN)) {
wpa_printf(MSG_DEBUG,
"FILS: Ignore HLP request with unexpected source address"
MACSTR, MAC2STR(pos + ETH_ALEN));
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
index 3d94407..a471c79 100644
--- a/src/ap/gas_query_ap.c
+++ b/src/ap/gas_query_ap.c
@@ -185,7 +185,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(q->addr, addr) &&
q->dialog_token == dialog_token)
return q;
}
@@ -223,7 +223,7 @@
wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
" ok=%d query=%p dialog_token=%u dur=%d ms",
MAC2STR(dst), ok, query, query->dialog_token, dur);
- if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(dst, query->addr)) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
@@ -618,7 +618,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(dst, q->addr) &&
dialog_token == q->dialog_token)
return 0;
}
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 236381f..a05de03 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -35,6 +35,7 @@
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "dpp_hostapd.h"
+#include "nan_usd_ap.h"
#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
@@ -183,6 +184,8 @@
hostapd_set_generic_elem(hapd, (u8 *) "", 0);
}
+ hostapd_neighbor_sync_own_report(hapd);
+
ieee802_11_set_beacon(hapd);
hostapd_update_wps(hapd);
@@ -394,25 +397,62 @@
#endif /* CONFIG_WEP */
-static void hostapd_clear_drv_priv(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)
{
- unsigned int i;
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
+ if (hapd->eht_mld_link_removal_count == 0)
+ return;
+ hapd->eht_mld_link_removal_count--;
- if (hapd->iface == iface || !iface)
- continue;
+ wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id,
+ hapd->eht_mld_link_removal_count);
- if (iface->bss && iface->bss[0] &&
- iface->bss[0]->mld_first_bss == hapd)
- iface->bss[0]->drv_priv = NULL;
+ ieee802_11_set_beacon(hapd);
+
+ if (!hapd->eht_mld_link_removal_count) {
+ hostapd_free_link_stas(hapd);
+ hostapd_disable_iface(hapd->iface);
+ return;
}
- hapd->drv_priv = NULL;
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
}
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
+{
+ if (!hapd->conf->mld_ap)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id, count);
+
+ hapd->eht_mld_link_removal_count = count;
+ hapd->eht_mld_bss_param_change++;
+
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
+
+ ieee802_11_set_beacon(hapd);
+ return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
os_free(hapd->probereq_cb);
@@ -440,7 +480,27 @@
vlan_deinit(hapd);
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
- if (!hapd->mld_first_bss) {
+ if (hostapd_mld_is_first_bss(hapd)) {
+#ifdef CONFIG_IEEE80211BE
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (hapd == h)
+ continue;
+ if (h->radius == hapd->radius)
+ h->radius = NULL;
+ if (h->radius_das == hapd->radius_das)
+ h->radius_das = NULL;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
radius_client_deinit(hapd->radius);
radius_das_deinit(hapd->radius_das);
}
@@ -455,6 +515,9 @@
gas_query_ap_deinit(hapd->gas);
hapd->gas = NULL;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ hostapd_nan_usd_deinit(hapd);
+#endif /* CONFIG_NAN_USD */
authsrv_deinit(hapd);
@@ -471,10 +534,20 @@
* driver wrapper may have removed its internal instance
* and hapd->drv_priv is not valid anymore.
*/
- hostapd_clear_drv_priv(hapd);
+ hapd->drv_priv = NULL;
}
}
+#ifdef CONFIG_IEEE80211BE
+ /* If the interface was not added as well as it is not the first BSS,
+ * at least the link should be removed here since deinit will take care
+ * of only the first BSS. */
+ if (hapd->conf->mld_ap && !hapd->interface_added &&
+ hapd->iface->bss[0] != hapd)
+ hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+ hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
wpabuf_free(hapd->time_adv);
hapd->time_adv = NULL;
@@ -502,7 +575,9 @@
hapd->setup_complete_cb = NULL;
#endif /* CONFIG_MESH */
+#ifndef CONFIG_NO_RRM
hostapd_clean_rrm(hapd);
+#endif /* CONFIG_NO_RRM */
fils_hlp_deinit(hapd);
#ifdef CONFIG_OCV
@@ -525,10 +600,51 @@
#ifdef CONFIG_IEEE80211AX
eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_IEEE80211BE
+ eloop_cancel_timeout(hostapd_link_remove_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211BE */
+#endif /* CONFIG_TESTING_OPTIONS */
+
#endif /* CONFIG_IEEE80211AX */
}
+/* hostapd_bss_link_deinit - Per-BSS ML cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to unlink the BSS from the AP MLD.
+ * If the BSS being removed is the first link, the next link becomes the first
+ * link.
+ */
+static void hostapd_bss_link_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf || !hapd->conf->mld_ap)
+ return;
+
+ if (!hapd->mld->num_links)
+ return;
+
+ /* If not started, not yet linked to the MLD. However, the first
+ * BSS is always linked since it is linked during driver_init(), and
+ * hence, need to remove it from the AP MLD.
+ */
+ if (!hapd->started && hapd->iface->bss[0] != hapd)
+ return;
+
+ /* The first BSS can also be only linked when at least driver_init() is
+ * executed. But if previous interface fails, it is not, and hence,
+ * safe to skip.
+ */
+ if (hapd->iface->bss[0] == hapd && !hapd->drv_priv)
+ return;
+
+ hostapd_mld_remove_link(hapd);
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
/**
* hostapd_cleanup - Per-BSS cleanup (deinitialization)
* @hapd: Pointer to BSS data
@@ -569,6 +685,7 @@
void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
#ifdef NEED_AP_MLME
hostapd_stop_setup_timers(iface);
#endif /* NEED_AP_MLME */
@@ -598,7 +715,6 @@
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
- eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
NULL);
@@ -1191,6 +1307,59 @@
}
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf;
+
+ if (!hapd)
+ return -1;
+
+ conf = hapd->conf;
+
+ if (hapd->radius) {
+ wpa_printf(MSG_DEBUG,
+ "Skipping RADIUS client init (already done)");
+ return 0;
+ }
+
+ hapd->radius = radius_client_init(hapd, conf->radius);
+ if (!hapd->radius) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS client initialization failed.");
+ return -1;
+ }
+
+ if (conf->radius_das_port) {
+ struct radius_das_conf das_conf;
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &conf->radius_das_client_addr;
+ das_conf.time_window = conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ conf->radius_das_require_event_timestamp;
+ das_conf.require_message_authenticator =
+ conf->radius_das_require_message_authenticator;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (!hapd->radius_das) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS DAS initialization failed.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
@@ -1218,7 +1387,7 @@
u8 if_addr[ETH_ALEN];
int flush_old_stations = 1;
- if (hapd->mld_first_bss)
+ if (!hostapd_mld_is_first_bss(hapd))
wpa_printf(MSG_DEBUG,
"MLD: %s: Setting non-first BSS", __func__);
@@ -1263,6 +1432,23 @@
} while (mac_in_conf(hapd->iconf, hapd->own_addr));
}
+#ifdef CONFIG_IEEE80211BE
+ if (conf->mld_ap) {
+ struct hostapd_data *h_hapd;
+
+ h_hapd = hostapd_mld_get_first_bss(hapd);
+ 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);
+ goto setup_mld;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
conf->iface, addr, hapd,
@@ -1277,8 +1463,34 @@
if (!addr)
os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "Setup of first link (%d) BSS of MLD %s",
+ 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 */
}
+#ifdef CONFIG_IEEE80211BE
+setup_mld:
+ if (hapd->conf->mld_ap && !first) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set link_id=%u, mld_addr=" MACSTR
+ ", own_addr=" MACSTR,
+ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
+ MAC2STR(hapd->own_addr));
+
+ if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
+ hapd->own_addr))
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (conf->wmm_enabled < 0)
conf->wmm_enabled = hapd->iconf->ieee80211n |
hapd->iconf->ieee80211ax;
@@ -1380,43 +1592,30 @@
}
#endif /* CONFIG_SQLITE */
- if (!hapd->mld_first_bss) {
- hapd->radius = radius_client_init(hapd, conf->radius);
- if (!hapd->radius) {
- wpa_printf(MSG_ERROR,
- "RADIUS client initialization failed.");
+ if (hostapd_mld_is_first_bss(hapd)) {
+ if (hostapd_bss_radius_init(hapd))
return -1;
- }
-
- if (conf->radius_das_port) {
- struct radius_das_conf das_conf;
-
- os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = conf->radius_das_port;
- das_conf.shared_secret = conf->radius_das_shared_secret;
- das_conf.shared_secret_len =
- conf->radius_das_shared_secret_len;
- das_conf.client_addr = &conf->radius_das_client_addr;
- das_conf.time_window = conf->radius_das_time_window;
- das_conf.require_event_timestamp =
- conf->radius_das_require_event_timestamp;
- das_conf.require_message_authenticator =
- conf->radius_das_require_message_authenticator;
- das_conf.ctx = hapd;
- das_conf.disconnect = hostapd_das_disconnect;
- das_conf.coa = hostapd_das_coa;
- hapd->radius_das = radius_das_init(&das_conf);
- if (!hapd->radius_das) {
- wpa_printf(MSG_ERROR,
- "RADIUS DAS initialization failed.");
- return -1;
- }
- }
} else {
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *f_bss;
+
+ f_bss = hostapd_mld_get_first_bss(hapd);
+ if (!f_bss)
+ return -1;
+
+ if (!f_bss->radius) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS RADIUS client does not exist. Init on its behalf");
+
+ if (hostapd_bss_radius_init(f_bss))
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG,
"MLD: Using RADIUS client of the first BSS");
- hapd->radius = hapd->mld_first_bss->radius;
- hapd->radius_das = hapd->mld_first_bss->radius_das;
+ hapd->radius = f_bss->radius;
+ hapd->radius_das = f_bss->radius_das;
+#endif /* CONFIG_IEEE80211BE */
}
#endif /* CONFIG_NO_RADIUS */
@@ -1435,6 +1634,11 @@
return -1;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (hostapd_nan_usd_init(hapd) < 0)
+ return -1;
+#endif /* CONFIG_NAN_USD */
+
if (authsrv_init(hapd) < 0)
return -1;
@@ -1456,6 +1660,7 @@
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
+#endif /* CONFIG_INTERWORKING */
if (conf->qos_map_set_len &&
hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
@@ -1463,7 +1668,6 @@
wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
return -1;
}
-#endif /* CONFIG_INTERWORKING */
if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
wpa_printf(MSG_ERROR, "BSS Load initialization failed");
@@ -1649,6 +1853,7 @@
static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
{
hostapd_bss_deinit_no_free(bss);
+ hostapd_bss_link_deinit(bss);
hostapd_free_hapd_data(bss);
hostapd_cleanup_iface_partial(bss->iface);
}
@@ -1945,10 +2150,11 @@
} else {
int ret;
- if (iface->conf->acs) {
+ if (iface->conf->acs && !iface->is_ch_switch_dfs) {
iface->freq = 0;
iface->conf->channel = 0;
}
+ iface->is_ch_switch_dfs = false;
ret = configured_fixed_chan_to_freq(iface);
if (ret < 0)
@@ -2707,6 +2913,8 @@
hapd->rad_attr_db = NULL;
}
#endif /* CONFIG_SQLITE */
+
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
}
@@ -2745,6 +2953,40 @@
}
+#ifdef CONFIG_IEEE80211BE
+
+static void hostapd_mld_ref_inc(struct hostapd_mld *mld)
+{
+ if (!mld)
+ return;
+
+ if (mld->refcount == HOSTAPD_MLD_MAX_REF_COUNT) {
+ wpa_printf(MSG_ERROR, "AP MLD %s: Ref count overflow",
+ mld->name);
+ return;
+ }
+
+ mld->refcount++;
+}
+
+
+static void hostapd_mld_ref_dec(struct hostapd_mld *mld)
+{
+ if (!mld)
+ return;
+
+ if (!mld->refcount) {
+ wpa_printf(MSG_ERROR, "AP MLD %s: Ref count underflow",
+ mld->name);
+ return;
+ }
+
+ mld->refcount--;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_interface_free(struct hostapd_iface *iface)
{
size_t j;
@@ -2752,6 +2994,10 @@
for (j = 0; j < iface->num_bss; j++) {
if (!iface->bss)
break;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[j])
+ hostapd_mld_ref_dec(iface->bss[j]->mld);
+#endif /* CONFIG_IEEE80211BE */
wpa_printf(MSG_DEBUG, "%s: free hapd %p",
__func__, iface->bss[j]);
os_free(iface->bss[j]);
@@ -2774,6 +3020,157 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_bss_alloc_link_id(struct hostapd_data *hapd)
+{
+ hapd->mld_link_id = hapd->mld->next_link_id++;
+ wpa_printf(MSG_DEBUG, "AP MLD: %s: Link ID %d assigned.",
+ hapd->mld->name, hapd->mld_link_id);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static 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;
+
+ conf = hapd->conf;
+
+ if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
+ conf->disable_11be)
+ return;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+
+ if (!mld || os_strcmp(conf->iface, mld->name) != 0)
+ continue;
+
+ hapd->mld = mld;
+ hostapd_mld_ref_inc(mld);
+ hostapd_bss_alloc_link_id(hapd);
+ break;
+ }
+
+ if (hapd->mld)
+ return;
+
+ mld = os_zalloc(sizeof(struct hostapd_mld));
+ if (!mld)
+ goto fail;
+
+ os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
+ dl_list_init(&mld->links);
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
+
+ hapd->mld = mld;
+ hostapd_mld_ref_inc(mld);
+ hostapd_bss_alloc_link_id(hapd);
+
+ all_mld = os_realloc_array(interfaces->mld, interfaces->mld_count + 1,
+ sizeof(struct hostapd_mld *));
+ if (!all_mld)
+ goto fail;
+
+ interfaces->mld = all_mld;
+ interfaces->mld[interfaces->mld_count] = mld;
+ interfaces->mld_count++;
+
+ return;
+fail:
+ if (!mld)
+ return;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: free mld %p", mld->name, mld);
+ os_free(mld);
+ hapd->mld = NULL;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld *mld, **all_mld;
+ size_t i, j, num_mlds;
+ bool forced_remove, remove;
+
+ if (!interfaces->mld)
+ return;
+
+ num_mlds = interfaces->mld_count;
+
+ for (i = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+ if (!mld)
+ continue;
+
+ remove = false;
+ forced_remove = false;
+
+ if (!mld->refcount)
+ remove = true;
+
+ /* If MLD is still being referenced but the number of interfaces
+ * is zero, it is safe to force its deletion. Normally, this
+ * should not happen but even if it does, let us free the
+ * memory.
+ */
+ if (!remove && !interfaces->count)
+ forced_remove = true;
+
+ if (!remove && !forced_remove)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
+ forced_remove ? " (forced)" : "");
+ os_free(mld);
+ interfaces->mld[i] = NULL;
+ num_mlds--;
+ }
+
+ if (!num_mlds) {
+ interfaces->mld_count = 0;
+ os_free(interfaces->mld);
+ interfaces->mld = NULL;
+ return;
+ }
+
+ all_mld = os_zalloc(num_mlds * sizeof(struct hostapd_mld *));
+ if (!all_mld) {
+ wpa_printf(MSG_ERROR,
+ "AP MLD: Failed to re-allocate the MLDs. Expect issues");
+ return;
+ }
+
+ for (i = 0, j = 0; i < interfaces->mld_count; i++) {
+ mld = interfaces->mld[i];
+ if (!mld)
+ continue;
+
+ all_mld[j++] = mld;
+ }
+
+ /* This should not happen */
+ if (j != num_mlds) {
+ wpa_printf(MSG_DEBUG,
+ "AP MLD: Some error occurred while reallocating MLDs. Expect issues.");
+ os_free(all_mld);
+ return;
+ }
+
+ os_free(interfaces->mld);
+ interfaces->mld = all_mld;
+ interfaces->mld_count = num_mlds;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
/**
* hostapd_init - Allocate and initialize per-interface data
* @config_file: Path to the configuration file
@@ -2817,8 +3214,10 @@
if (hapd == NULL)
goto fail;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
}
+ hapd_iface->is_ch_switch_dfs = false;
return hapd_iface;
fail:
@@ -2938,6 +3337,8 @@
iface->conf->last_bss = bss;
iface->bss[iface->num_bss] = hapd;
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, interfaces);
+
bss_idx = iface->num_bss++;
conf->num_bss--;
@@ -2971,6 +3372,37 @@
}
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv, struct hostapd_iface *iface)
+{
+ if (!driver || !driver->hapd_deinit || !drv_priv)
+ 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) {
+ 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)) {
+ wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+ iface->bss[0]->conf->iface);
+ }
+#else /* CONFIG_IEEE80211BE */
+ driver->hapd_deinit(drv_priv);
+#endif /* CONFIG_IEEE80211BE */
+ iface->bss[0]->drv_priv = NULL;
+}
+
+
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
const struct wpa_driver_ops *driver;
@@ -2987,11 +3419,7 @@
hostapd_interface_deinit(iface);
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
- if (driver && driver->hapd_deinit && drv_priv) {
- if (!iface->bss[0]->mld_first_bss)
- driver->hapd_deinit(drv_priv);
- hostapd_clear_drv_priv(iface->bss[0]);
- }
+ hostapd_cleanup_driver(driver, drv_priv, iface);
hostapd_interface_free(iface);
}
@@ -3004,15 +3432,16 @@
wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
__func__, driver, drv_priv);
+
+ hostapd_cleanup_driver(driver, drv_priv, hapd_iface);
+
if (driver && driver->hapd_deinit && drv_priv) {
- if (!hapd_iface->bss[0]->mld_first_bss)
- driver->hapd_deinit(drv_priv);
for (j = 0; j < hapd_iface->num_bss; j++) {
wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
__func__, (int) j,
hapd_iface->bss[j]->drv_priv);
if (hapd_iface->bss[j]->drv_priv == drv_priv) {
- hostapd_clear_drv_priv(hapd_iface->bss[j]);
+ hapd_iface->bss[j]->drv_priv = NULL;
hapd_iface->extended_capa = NULL;
hapd_iface->extended_capa_mask = NULL;
hapd_iface->extended_capa_len = 0;
@@ -3022,6 +3451,22 @@
}
+static void hostapd_refresh_all_iface_beacons(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ if (!hapd_iface->interfaces || hapd_iface->interfaces->count <= 1)
+ return;
+
+ for (j = 0; j < hapd_iface->interfaces->count; j++) {
+ if (hapd_iface->interfaces->iface[j] == hapd_iface)
+ continue;
+
+ ieee802_11_update_beacons(hapd_iface->interfaces->iface[j]);
+ }
+}
+
+
int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
@@ -3060,6 +3505,8 @@
return -1;
}
+ hostapd_refresh_all_iface_beacons(hapd_iface);
+
return 0;
}
@@ -3134,6 +3581,7 @@
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
+ hostapd_bss_link_deinit(hapd);
hostapd_free_hapd_data(hapd);
}
@@ -3147,6 +3595,7 @@
wpa_printf(MSG_DEBUG, "Interface %s disabled",
hapd_iface->bss[0]->conf->iface);
hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
+ hostapd_refresh_all_iface_beacons(hapd_iface);
return 0;
}
@@ -3255,6 +3704,7 @@
return -1;
}
hapd->msg_ctx = hapd;
+ hostapd_bss_setup_multi_link(hapd, hapd_iface->interfaces);
}
hapd_iface->conf = conf;
@@ -3328,6 +3778,7 @@
if (start_ctrl_iface_bss(hapd) < 0 ||
(hapd_iface->state == HAPD_IFACE_ENABLED &&
hostapd_setup_bss(hapd, -1, true))) {
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
hapd_iface->conf->num_bss--;
@@ -3336,6 +3787,9 @@
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
return -1;
}
@@ -3425,7 +3879,11 @@
wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
__func__, hapd_iface->bss[i],
hapd->conf->iface);
+ hostapd_bss_link_deinit(hapd);
hostapd_cleanup(hapd);
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
hapd_iface->bss[i] = NULL;
}
@@ -3435,6 +3893,7 @@
if (new_iface) {
interfaces->count--;
interfaces->iface[interfaces->count] = NULL;
+ hostapd_cleanup_unused_mlds(interfaces);
}
hostapd_cleanup_iface(hapd_iface);
}
@@ -3457,6 +3916,9 @@
__func__, hapd, hapd->conf->iface);
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
+#ifdef CONFIG_IEEE80211BE
+ hostapd_mld_ref_dec(hapd->mld);
+#endif /* CONFIG_IEEE80211BE */
os_free(hapd);
iface->num_bss--;
@@ -3499,6 +3961,8 @@
k++;
}
interfaces->count--;
+ hostapd_cleanup_unused_mlds(interfaces);
+
return 0;
}
@@ -3536,7 +4000,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
sta->mld_assoc_link_id != hapd->mld_link_id)
return;
#endif /* CONFIG_IEEE80211BE */
@@ -3657,7 +4121,7 @@
#ifdef NEED_AP_MLME
-static void free_beacon_data(struct beacon_data *beacon)
+void free_beacon_data(struct beacon_data *beacon)
{
os_free(beacon->head);
beacon->head = NULL;
@@ -3768,7 +4232,7 @@
struct hostapd_freq_params *old_params)
{
int channel;
- u8 seg0, seg1;
+ u8 seg0 = 0, seg1 = 0;
struct hostapd_hw_modes *mode;
if (!params->channel) {
@@ -3798,7 +4262,8 @@
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL,
mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
- NULL))
+ NULL,
+ hostapd_get_punct_bitmap(hapd)))
return -1;
switch (params->bandwidth) {
@@ -3844,10 +4309,14 @@
conf->ieee80211n = params->ht_enabled;
conf->ieee80211ac = params->vht_enabled;
conf->secondary_channel = params->sec_channel_offset;
- ieee80211_freq_to_chan(params->center_freq1,
- &seg0);
- ieee80211_freq_to_chan(params->center_freq2,
- &seg1);
+ if (params->center_freq1 &&
+ ieee80211_freq_to_chan(params->center_freq1, &seg0) ==
+ NUM_HOSTAPD_MODES)
+ return -1;
+ if (params->center_freq2 &&
+ ieee80211_freq_to_chan(params->center_freq2,
+ &seg1) == NUM_HOSTAPD_MODES)
+ return -1;
hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
@@ -3945,6 +4414,16 @@
settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+ settings->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ settings->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+ settings->ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, &settings->ubpr);
+#endif /* CONFIG_IEEE80211AX */
return 0;
}
@@ -4007,6 +4486,9 @@
ret = hostapd_drv_switch_channel(hapd, settings);
free_beacon_data(&settings->beacon_csa);
free_beacon_data(&settings->beacon_after);
+#ifdef CONFIG_IEEE80211AX
+ os_free(settings->ubpr.unsol_bcast_probe_resp_tmpl);
+#endif /* CONFIG_IEEE80211AX */
if (ret) {
/* if we failed, clean cs parameters */
@@ -4023,15 +4505,16 @@
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params)
{
- int seg0_idx = 0, seg1_idx = 0;
+ u8 seg0_idx = 0, seg1_idx = 0;
enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT;
+ u8 op_class, chan = 0;
wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
if (freq_params->center_freq1)
- seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+ ieee80211_freq_to_chan(freq_params->center_freq1, &seg0_idx);
if (freq_params->center_freq2)
- seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+ ieee80211_freq_to_chan(freq_params->center_freq2, &seg1_idx);
switch (freq_params->bandwidth) {
case 0:
@@ -4040,13 +4523,17 @@
bw = CONF_OPER_CHWIDTH_USE_HT;
break;
case 80:
- if (freq_params->center_freq2)
+ if (freq_params->center_freq2) {
bw = CONF_OPER_CHWIDTH_80P80MHZ;
- else
+ iface->conf->vht_capab |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ } else {
bw = CONF_OPER_CHWIDTH_80MHZ;
+ }
break;
case 160:
bw = CONF_OPER_CHWIDTH_160MHZ;
+ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
break;
case 320:
bw = CONF_OPER_CHWIDTH_320MHZ;
@@ -4060,6 +4547,15 @@
iface->freq = freq_params->freq;
iface->conf->channel = freq_params->channel;
iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ if (ieee80211_freq_to_channel_ext(freq_params->freq,
+ freq_params->sec_channel_offset, bw,
+ &op_class, &chan) ==
+ NUM_HOSTAPD_MODES ||
+ chan != freq_params->channel)
+ wpa_printf(MSG_INFO, "CSA: Channel mismatch: %d -> %d",
+ freq_params->channel, chan);
+
+ iface->conf->op_class = op_class;
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
hostapd_set_oper_chwidth(iface->conf, bw);
@@ -4090,8 +4586,8 @@
}
-static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
- struct cca_settings *settings)
+int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings)
{
struct hostapd_iface *iface = hapd->iface;
u8 old_color;
@@ -4100,6 +4596,12 @@
if (!iface || iface->conf->he_op.he_bss_color_disabled)
return -1;
+ settings->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ settings->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
old_color = iface->conf->he_op.he_bss_color;
iface->conf->he_op.he_bss_color = hapd->cca_color;
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
@@ -4118,6 +4620,9 @@
return ret;
}
+ settings->ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, &settings->ubpr);
+
settings->counter_offset_beacon = hapd->cca_c_off_beacon;
settings->counter_offset_presp = hapd->cca_c_off_proberesp;
@@ -4180,6 +4685,7 @@
free_beacon_data(&settings.beacon_cca);
free_beacon_data(&settings.beacon_after);
+ os_free(settings.ubpr.unsol_bcast_probe_resp_tmpl);
}
}
@@ -4275,23 +4781,171 @@
#ifdef CONFIG_IEEE80211BE
+
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id)
{
- unsigned int i;
+ struct hostapd_iface *iface;
+ struct hostapd_data *bss;
+ unsigned int i, j;
for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
- struct hostapd_bss_config *hconf = h_hapd->conf;
-
- if (!hconf->mld_ap || hconf->mld_id != hapd->conf->mld_id)
+ iface = hapd->iface->interfaces->iface[i];
+ if (!iface)
continue;
- if (h_hapd->mld_link_id == link_id)
- return h_hapd;
+ for (j = 0; j < iface->num_bss; j++) {
+ bss = iface->bss[j];
+
+ if (!bss->conf->mld_ap ||
+ !hostapd_is_ml_partner(hapd, bss))
+ continue;
+
+ if (!bss->drv_priv)
+ continue;
+
+ if (bss->mld_link_id == link_id)
+ return bss;
+ }
}
return NULL;
}
+
+
+bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ struct hostapd_data *hapd2)
+{
+ if (!hapd1->conf->mld_ap || !hapd2->conf->mld_ap)
+ return false;
+
+ return !os_strcmp(hapd1->conf->iface, hapd2->conf->iface);
+}
+
+
+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 */
+}
+
+
+int hostapd_mld_add_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_add_tail(&mld->links, &hapd->link);
+ mld->num_links++;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d added. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss)
+ return 0;
+
+ mld->fbss = hapd;
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+int hostapd_mld_remove_link(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+ struct hostapd_data *next_fbss;
+
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ /* Should not happen */
+ if (!mld)
+ return -1;
+
+ dl_list_del(&hapd->link);
+ mld->num_links--;
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: Link ID %d removed. num_links: %d",
+ mld->name, hapd->mld_link_id, mld->num_links);
+
+ if (mld->fbss != hapd)
+ return 0;
+
+ /* If the list is empty, all links are removed */
+ if (dl_list_empty(&mld->links)) {
+ mld->fbss = NULL;
+ } else {
+ next_fbss = dl_list_entry(mld->links.next, struct hostapd_data,
+ link);
+ mld->fbss = next_fbss;
+ }
+
+ wpa_printf(MSG_DEBUG, "AP MLD %s: First link BSS set to %p",
+ mld->name, mld->fbss);
+ return 0;
+}
+
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return true;
+
+ /* Should not happen */
+ if (!mld)
+ return false;
+
+ /* If fbss is not set, it is safe to assume the caller is the first BSS.
+ */
+ if (!mld->fbss)
+ return true;
+
+ return hapd == mld->fbss;
+}
+
+
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd)
+{
+ struct hostapd_mld *mld = hapd->mld;
+
+ if (!hapd->conf->mld_ap)
+ return NULL;
+
+ /* Should not happen */
+ if (!mld)
+ return NULL;
+
+ return mld->fbss;
+}
+
#endif /* CONFIG_IEEE80211BE */
+
+
+u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd)
+{
+ u16 punct_bitmap = 0;
+
+#ifdef CONFIG_IEEE80211BE
+ punct_bitmap = hapd->iconf->punct_bitmap;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!punct_bitmap)
+ punct_bitmap = hapd->conf->eht_oper_puncturing_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+ return punct_bitmap;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 7f703be..85122d4 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -44,6 +44,7 @@
#endif /* CONFIG_CTRL_IFACE_UDP */
struct hostapd_iface;
+struct hostapd_mld;
struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface);
@@ -93,6 +94,10 @@
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_mld **mld;
+ size_t mld_count;
+#endif /* CONFIG_IEEE80211BE */
};
enum hostapd_chan_status {
@@ -175,12 +180,6 @@
unsigned int reenable_beacon:1;
u8 own_addr[ETH_ALEN];
- u8 mld_addr[ETH_ALEN];
- u8 mld_link_id;
- /* Used for mld_link_id assignment - valid on the first MLD BSS only */
- u8 mld_next_link_id;
-
- struct hostapd_data *mld_first_bss;
int num_sta; /* number of entries in sta_list */
struct sta_info *sta_list; /* STA info list head */
@@ -406,8 +405,10 @@
u8 beacon_req_token;
u8 lci_req_token;
u8 range_req_token;
+ u8 link_measurement_req_token;
unsigned int lci_req_active:1;
unsigned int range_req_active:1;
+ unsigned int link_mesr_req_active:1;
int dhcp_sock; /* UDP socket used with the DHCP server */
@@ -469,6 +470,23 @@
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+
+#ifdef CONFIG_IEEE80211BE
+ u8 eht_mld_bss_param_change;
+ struct hostapd_mld *mld;
+ struct dl_list link;
+ u8 mld_link_id;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 eht_mld_link_removal_count;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_NAN_USD
+ struct nan_de *nan_de;
+#endif /* CONFIG_NAN_USD */
+
+ u64 scan_cookie; /* Scan instance identifier for the ongoing HT40 scan
+ */
};
@@ -493,6 +511,29 @@
HAPD_IFACE_ENABLED
};
+#ifdef CONFIG_IEEE80211BE
+/**
+ * struct hostapd_mld - hostapd per-mld data structure
+ */
+struct hostapd_mld {
+ char name[IFNAMSIZ + 1];
+ u8 mld_addr[ETH_ALEN];
+ u8 next_link_id;
+ u8 num_links;
+ /* Number of hostapd_data (hapd) referencing this. num_links cannot be
+ * used since num_links can go to 0 even when a BSS is disabled and
+ * when it is re-enabled, the MLD should exist and hence it cannot be
+ * freed when num_links is 0.
+ */
+ u8 refcount;
+
+ struct hostapd_data *fbss;
+ struct dl_list links; /* List head of all affiliated links */
+};
+
+#define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
+#endif /* CONFIG_IEEE80211BE */
+
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
@@ -540,6 +581,7 @@
u64 drv_flags;
u64 drv_flags2;
+ unsigned int drv_rrm_flags;
/*
* A bitmap of supported protocols for probe response offload. See
@@ -565,6 +607,8 @@
int *basic_rates;
int freq;
+ bool radar_detected;
+
/* Background radar configuration */
struct {
int channel;
@@ -640,6 +684,7 @@
#ifdef CONFIG_ACS
unsigned int acs_num_completed_scans;
+ unsigned int acs_num_retries;
#endif /* CONFIG_ACS */
void (*scan_cb)(struct hostapd_iface *iface);
@@ -666,6 +711,8 @@
/* Configured freq of interface is NO_IR */
bool is_no_ir;
+
+ bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
};
/* hostapd.c */
@@ -771,5 +818,37 @@
int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id);
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+bool hostapd_is_ml_partner(struct hostapd_data *hapd1,
+ struct hostapd_data *hapd2);
+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);
+struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
+
+void free_beacon_data(struct beacon_data *beacon);
+int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings);
+
+#ifdef CONFIG_IEEE80211BE
+
+bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
+
+#define for_each_mld_link(partner, self) \
+ dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
+
+#else /* CONFIG_IEEE80211BE */
+
+static inline bool hostapd_mld_is_first_bss(struct hostapd_data *hapd)
+{
+ return true;
+}
+
+#define for_each_mld_link(partner, self) \
+ if (false)
+
+#endif /* CONFIG_IEEE80211BE */
+
+u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 9edbb5a..c455660 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -107,9 +107,7 @@
*/
orig_mode_valid = true;
mode = iface->current_mode->mode;
- is_6ghz = mode == HOSTAPD_MODE_IEEE80211A &&
- iface->current_mode->num_channels > 0 &&
- is_6ghz_freq(iface->current_mode->channels[0].freq);
+ is_6ghz = iface->current_mode->is_6ghz;
iface->current_mode = NULL;
}
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
@@ -508,6 +506,12 @@
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);
@@ -523,6 +527,7 @@
if (ret == 0) {
iface->scan_cb = ieee80211n_check_scan;
+ iface->bss[0]->scan_cookie = params.scan_cookie;
return;
}
@@ -558,6 +563,11 @@
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);
@@ -579,6 +589,7 @@
}
iface->scan_cb = ieee80211n_check_scan;
+ iface->bss[0]->scan_cookie = params.scan_cookie;
return 1;
}
@@ -1001,7 +1012,7 @@
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
- int err;
+ int err, err2;
if (!iface->current_mode)
return 0;
@@ -1044,15 +1055,15 @@
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_freq = iface->freq + 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+ 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;
}
secondary_freq = iface->freq - 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+ 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;
return 1;
}
@@ -1070,9 +1081,7 @@
return true;
if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 &&
- (mode->mode != HOSTAPD_MODE_IEEE80211A ||
- mode->num_channels == 0 ||
- !is_6ghz_freq(mode->channels[0].freq)))
+ !mode->is_6ghz)
return true;
return false;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 1f39107..1cd76ca 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -56,6 +56,7 @@
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#include "comeback_token.h"
+#include "nan_usd_ap.h"
#include "pasn/pasn_common.h"
@@ -87,18 +88,31 @@
struct sta_info *sta, int reassoc);
-u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+static u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid, size_t len)
{
- u8 multi_ap_val = 0;
+ struct multi_ap_params multi_ap = { 0 };
if (!hapd->conf->multi_ap)
return eid;
- if (hapd->conf->multi_ap & BACKHAUL_BSS)
- multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
- if (hapd->conf->multi_ap & FRONTHAUL_BSS)
- multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
- return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap.capability |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap.capability |= MULTI_AP_FRONTHAUL_BSS;
+
+ if (hapd->conf->multi_ap_client_disallow &
+ PROFILE1_CLIENT_ASSOC_DISALLOW)
+ multi_ap.capability |=
+ MULTI_AP_PROFILE1_BACKHAUL_STA_DISALLOWED;
+ if (hapd->conf->multi_ap_client_disallow &
+ PROFILE2_CLIENT_ASSOC_DISALLOW)
+ multi_ap.capability |=
+ MULTI_AP_PROFILE2_BACKHAUL_STA_DISALLOWED;
+
+ multi_ap.profile = hapd->conf->multi_ap_profile;
+ multi_ap.vlanid = hapd->conf->multi_ap_vlanid;
+
+ return eid + add_multi_ap_ie(eid, len, &multi_ap);
}
@@ -390,7 +404,7 @@
static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *dst, const u8 *bssid,
+ const u8 *dst,
u16 auth_alg, u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len, const char *dbg)
{
@@ -402,14 +416,7 @@
struct wpabuf *ml_resp = NULL;
#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * the MLD MAC address. Thus, use the MLD address instead of translating
- * the addresses.
- */
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
- sa = hapd->mld_addr;
-
+ if (ap_sta_is_mld(hapd, sta)) {
ml_resp = hostapd_ml_auth_resp(hapd);
if (!ml_resp)
return -1;
@@ -430,7 +437,7 @@
WLAN_FC_STYPE_AUTH);
os_memcpy(reply->da, dst, ETH_ALEN);
os_memcpy(reply->sa, sa, ETH_ALEN);
- os_memcpy(reply->bssid, bssid, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
reply->u.auth.auth_alg = host_to_le16(auth_alg);
reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
@@ -494,7 +501,7 @@
#ifdef CONFIG_IEEE80211R_AP
-static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+static void handle_auth_ft_finish(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
{
@@ -502,7 +509,7 @@
struct sta_info *sta;
int reply_res;
- reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT,
+ reply_res = send_auth_reply(hapd, NULL, dst, WLAN_AUTH_FT,
auth_transaction, status, ies, ies_len,
"auth-ft-finish");
@@ -556,7 +563,7 @@
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
(!sta ||
- os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0))
+ !ether_addr_equal(pw->peer_addr, sta->addr)))
continue;
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
@@ -608,8 +615,8 @@
const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
- own_addr = hapd->mld_addr;
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
#endif /* CONFIG_IEEE80211BE */
if (sta->sae->tmp) {
@@ -698,7 +705,7 @@
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
- const u8 *bssid, int update, int status_code)
+ int update, int status_code)
{
struct wpabuf *data;
int reply_res;
@@ -725,7 +732,7 @@
status = hapd->conf->sae_commit_status;
}
#endif /* CONFIG_TESTING_OPTIONS */
- reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ reply_res = send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
wpabuf_len(data), "sae-send-commit");
@@ -737,8 +744,7 @@
static int auth_sae_send_confirm(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *bssid)
+ struct sta_info *sta)
{
struct wpabuf *data;
int reply_res;
@@ -747,7 +753,7 @@
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ reply_res = send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
wpabuf_len(data), "sae-send-confirm");
@@ -807,12 +813,41 @@
if (sta->sae->sync > hapd->conf->sae_sync) {
sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
+ if (sta->sae->tmp) {
+ /* Disable this SAE instance for 10 seconds to avoid
+ * unnecessary flood of multiple SAE commits in
+ * unexpected mesh cases. */
+ if (os_get_reltime(&sta->sae->tmp->disabled_until) == 0)
+ sta->sae->tmp->disabled_until.sec += 10;
+ }
return -1;
}
return 0;
}
+static bool sae_proto_instance_disabled(struct sta_info *sta)
+{
+ struct sae_temporary_data *tmp;
+
+ if (!sta->sae)
+ return false;
+ tmp = sta->sae->tmp;
+ if (!tmp)
+ return false;
+
+ if (os_reltime_initialized(&tmp->disabled_until)) {
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&now, &tmp->disabled_until))
+ return true;
+ }
+
+ return false;
+}
+
+
static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
{
struct hostapd_data *hapd = eloop_ctx;
@@ -829,13 +864,13 @@
switch (sta->sae->state) {
case SAE_COMMITTED:
- ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ ret = auth_sae_send_commit(hapd, sta, 0, -1);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
case SAE_CONFIRMED:
- ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+ ret = auth_sae_send_confirm(hapd, sta);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
@@ -877,7 +912,7 @@
params.status = status;
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
params.bssid =
sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -902,23 +937,27 @@
" to VLAN ID %d",
MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
- vlan_desc.notempty = 1;
- vlan_desc.untagged = sta->sae->tmp->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;
- }
+ 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;
+ 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;
+ }
- 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,
- MAC2STR(sta->addr));
- return;
+ 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,
+ MAC2STR(sta->addr));
+ return;
+ }
+ } else {
+ sta->vlan_id = sta->sae->tmp->vlan_id;
}
}
#endif /* CONFIG_NO_VLAN */
@@ -939,7 +978,7 @@
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *bssid, u16 auth_transaction, u16 status_code,
+ u16 auth_transaction, u16 status_code,
int allow_reuse, int *sta_removed)
{
int ret;
@@ -952,6 +991,13 @@
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)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Protocol instance temporarily disabled - discard received SAE commit");
+ return WLAN_STATUS_SUCCESS;
+ }
+
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
@@ -963,7 +1009,7 @@
sta->sae->pk =
status_code == WLAN_STATUS_SAE_PK;
}
- ret = auth_sae_send_commit(hapd, sta, bssid,
+ ret = auth_sae_send_commit(hapd, sta,
!allow_reuse, status_code);
if (ret)
return ret;
@@ -989,7 +1035,7 @@
* based on SAE finite state machine
* Nothing -> Confirm transition.
*/
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED,
@@ -1019,7 +1065,7 @@
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
@@ -1034,8 +1080,7 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_commit(hapd, sta, bssid, 0,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 0, status_code);
if (ret)
return ret;
@@ -1046,7 +1091,7 @@
* Nothing -> Confirmed transition that was reduced to
* Nothing -> Committed above.
*/
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
@@ -1057,7 +1102,7 @@
* step to get to Accepted without waiting for
* additional events.
*/
- return sae_sm_step(hapd, sta, bssid, auth_transaction,
+ return sae_sm_step(hapd, sta, auth_transaction,
WLAN_STATUS_SUCCESS, 0, sta_removed);
}
break;
@@ -1068,15 +1113,14 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_commit(hapd, sta, bssid, 1,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 1, status_code);
if (ret)
return ret;
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
@@ -1097,8 +1141,7 @@
*sta_removed = 1;
} else if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
- ret = auth_sae_send_commit(hapd, sta, bssid, 1,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 1, status_code);
if (ret)
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -1112,7 +1155,7 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
sae_clear_temp_data(sta->sae);
if (ret)
return ret;
@@ -1224,7 +1267,7 @@
struct sae_data *sae)
{
const struct wpabuf *groups;
- size_t i, count;
+ size_t i, count, len;
const u8 *pos;
if (!sae->tmp)
@@ -1234,7 +1277,15 @@
return 0;
pos = wpabuf_head(groups);
- count = wpabuf_len(groups) / 2;
+ len = wpabuf_len(groups);
+ if (len & 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid length of the Rejected Groups element payload: %zu",
+ len);
+ return 1;
+ }
+
+ count = len / 2;
for (i = 0; i < count; i++) {
int enabled;
u16 group;
@@ -1273,7 +1324,8 @@
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
resp = status_code;
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr,
+ WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
goto remove_sta;
@@ -1281,7 +1333,8 @@
if (hapd->conf->sae_commit_override && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr,
+ WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
wpabuf_len(hapd->conf->sae_commit_override),
@@ -1360,8 +1413,7 @@
* Authentication frame, and the commit-scalar and
* COMMIT-ELEMENT previously sent.
*/
- resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
- status_code);
+ resp = auth_sae_send_commit(hapd, sta, 0, status_code);
if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
"SAE: Failed to send commit message");
@@ -1387,6 +1439,12 @@
if (!sae_status_success(hapd, status_code))
goto remove_sta;
+ if (sae_proto_instance_disabled(sta)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Protocol instance temporarily disabled - discard received SAE commit");
+ return;
+ }
+
if (!(hapd->conf->mesh & MESH_ENABLED) &&
sta->sae->state == SAE_COMMITTED) {
/* This is needed in the infrastructure BSS case to
@@ -1484,7 +1542,7 @@
goto reply;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ resp = sae_sm_step(hapd, sta, auth_transaction,
status_code, allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1527,7 +1585,7 @@
}
sta->sae->rc = peer_send_confirm;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ resp = sae_sm_step(hapd, sta, auth_transaction,
status_code, 0, &sta_removed);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1552,10 +1610,19 @@
data = wpabuf_alloc_copy(pos, 2);
sae_sme_send_external_auth_status(hapd, sta, resp);
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ 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;
+ }
}
remove_sta:
@@ -1592,7 +1659,7 @@
if (sta->sae->state != SAE_NOTHING)
return -1;
- ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ ret = auth_sae_send_commit(hapd, sta, 0, -1);
if (ret)
return -1;
@@ -1664,7 +1731,7 @@
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
- if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(mgmt->sa, mgmt2->sa) &&
mgmt->u.auth.auth_transaction ==
mgmt2->u.auth.auth_transaction) {
wpa_printf(MSG_DEBUG,
@@ -1695,7 +1762,7 @@
dl_list_for_each(q, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt = (const struct ieee80211_mgmt *) q->msg;
- if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, mgmt->sa))
return 1;
}
@@ -1873,11 +1940,14 @@
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);
+ elems.mdie, elems.mdie_len, NULL, 0, NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
+ wpa_auth_set_rsn_override(sta->wpa_sm, elems.rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm, elems.rsne_override_2 != NULL);
+
if (!elems.fils_nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2021,7 +2091,7 @@
}
os_memcpy(ie_buf, ie, ielen);
- if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid, true) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2149,7 +2219,8 @@
pmk, pmk_len,
sta->fils_erp_pmkid,
session_timeout,
- wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+ wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ NULL) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
}
@@ -2204,7 +2275,7 @@
auth_alg = (pub ||
resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
- send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp,
+ send_auth_reply(hapd, sta, sta->addr, auth_alg, 2, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-fils-finish");
wpabuf_free(data);
@@ -2381,7 +2452,7 @@
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
fils->anonce, FILS_NONCE_LEN);
- ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce,
+ ret = fils_rmsk_to_pmk(pasn_get_akmp(pasn), msk, msk_len, fils->nonce,
fils->anonce, NULL, 0, pmk, &pmk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
@@ -2391,15 +2462,16 @@
ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
wpabuf_head(pasn->secret),
wpabuf_len(pasn->secret),
- &sta->pasn->ptk, sta->pasn->akmp,
- sta->pasn->cipher, sta->pasn->kdk_len);
+ pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
+ pasn_get_cipher(sta->pasn), sta->pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
goto fail;
}
if (pasn->secure_ltf) {
- ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ ret = wpa_ltf_keyseed(pasn_get_ptk(pasn), pasn_get_akmp(pasn),
+ pasn_get_cipher(pasn));
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Failed to derive LTF keyseed");
@@ -2520,8 +2592,8 @@
FILS_SESSION_LEN);
os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
+ true);
if (!fils_wd) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
@@ -2545,7 +2617,8 @@
* Calculate pending PMKID here so that we do not need to maintain a
* copy of the EAP-Initiate/Reautt message.
*/
- fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd),
+ fils_pmkid_erp(pasn_get_akmp(pasn),
+ wpabuf_head(fils_wd), wpabuf_len(fils_wd),
fils->erp_pmkid);
wpabuf_free(fils_wd);
@@ -2570,32 +2643,35 @@
{
struct pasn_data *pasn = sta->pasn;
- pasn->cb_ctx = hapd;
- pasn->send_mgmt = hapd_pasn_send_mlme;
+ pasn_register_callbacks(pasn, hapd, hapd_pasn_send_mlme, NULL);
+ pasn_set_bssid(pasn, hapd->own_addr);
+ pasn_set_own_addr(pasn, hapd->own_addr);
+ 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->noauth = hapd->conf->pasn_noauth;
- pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
- pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
- pasn->derive_kdk = hapd->iface->drv_flags2 &
- WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ 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;
if (hapd->conf->force_kdk_derivation)
- pasn->derive_kdk = true;
+ pasn_enable_kdk_derivation(pasn);
#endif /* CONFIG_TESTING_OPTIONS */
pasn->use_anti_clogging = use_anti_clogging(hapd);
- pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
- NULL);
+ 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);
- pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ pasn_set_rsnxe_ie(pasn, hostapd_wpa_ie(hapd, WLAN_EID_RSNX));
pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
- pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+ pasn_set_responder_pmksa(pasn,
+ wpa_auth_get_pmksa_cache(hapd->wpa_auth));
pasn->comeback_after = hapd->conf->pasn_comeback_after;
pasn->comeback_idx = hapd->comeback_idx;
pasn->comeback_key = hapd->comeback_key;
pasn->comeback_pending_idx = hapd->comeback_pending_idx;
- os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
}
@@ -2612,7 +2688,7 @@
return -1;
}
- if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(entry->own_addr, own_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: own addr " MACSTR " and PTKSA entry own addr "
MACSTR " differ",
@@ -2639,8 +2715,11 @@
struct pasn_data *pasn = sta->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
+#ifdef CONFIG_FILS
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
+#endif /* CONFIG_FILS */
+ int akmp;
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
@@ -2664,10 +2743,20 @@
return;
}
- pasn->akmp = rsn_data.key_mgmt;
- pasn->cipher = rsn_data.pairwise_cipher;
+ pasn_set_akmp(pasn, rsn_data.key_mgmt);
+ pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
- if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+ if (pasn->derive_kdk &&
+ !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn_disable_kdk_derivation(pasn);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->force_kdk_derivation)
+ pasn_enable_kdk_derivation(pasn);
+#endif /* CONFIG_TESTING_OPTIONS */
+ akmp = pasn_get_akmp(pasn);
+
+ if (wpa_key_mgmt_ft(akmp) && rsn_data.num_pmkid) {
#ifdef CONFIG_IEEE80211R_AP
pasn->pmk_r1_len = 0;
wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
@@ -2678,8 +2767,8 @@
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_FILS
- if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
- pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+ if (akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+ akmp != WPA_KEY_MGMT_FILS_SHA384)
return;
if (!elems.pasn_params ||
wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
@@ -2690,8 +2779,8 @@
return;
}
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len, true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
return;
@@ -2732,7 +2821,7 @@
return;
}
- sta->pasn = os_zalloc(sizeof(*sta->pasn));
+ sta->pasn = pasn_data_init();
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to allocate PASN context");
@@ -2762,13 +2851,14 @@
if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
sta->addr, mgmt, len) == 0) {
ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
- sta->pasn->cipher, 43200,
- &sta->pasn->ptk, NULL, NULL,
- sta->pasn->akmp);
+ pasn_get_cipher(sta->pasn), 43200,
+ pasn_get_ptk(sta->pasn), NULL, NULL,
+ pasn_get_akmp(sta->pasn));
pasn_set_keys_from_cache(hapd, hapd->own_addr,
- sta->addr, sta->pasn->cipher,
- sta->pasn->akmp);
+ sta->addr,
+ pasn_get_cipher(sta->pasn),
+ pasn_get_akmp(sta->pasn));
}
ap_free_sta(hapd, sta);
} else {
@@ -2794,8 +2884,10 @@
size_t resp_ies_len = 0;
u16 seq_ctrl;
struct radius_sta rad_info;
- const u8 *dst, *sa, *bssid;
+ 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)",
@@ -2872,7 +2964,10 @@
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
@@ -2906,22 +3001,24 @@
goto fail;
}
- if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
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 &&
- (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
- os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+ (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;
@@ -3021,15 +3118,6 @@
seq_ctrl);
return;
}
-#ifdef CONFIG_MESH
- if ((hapd->conf->mesh & MESH_ENABLED) &&
- sta->plink_state == PLINK_BLOCKED) {
- wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
- " is blocked - drop Authentication frame",
- MAC2STR(sa));
- return;
- }
-#endif /* CONFIG_MESH */
#ifdef CONFIG_PASN
if (auth_alg == WLAN_AUTH_PASN &&
(sta->flags & WLAN_STA_ASSOC)) {
@@ -3067,13 +3155,19 @@
}
#ifdef CONFIG_IEEE80211BE
- if (auth_transaction == 1) {
+ /* Set the non-AP MLD information based on the initial Authentication
+ * frame. Once the STA entry has been added to the driver, the driver
+ * will translate addresses in the frame and we need to avoid overriding
+ * peer_addr based on mgmt->sa which would have been translated to the
+ * MLD MAC address. */
+ if (!sta->added_unassoc && auth_transaction == 1) {
+ ap_sta_free_sta_profile(&sta->mld_info);
os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
if (mld_sta) {
u8 link_id = hapd->mld_link_id;
- sta->mld_info.mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
/*
@@ -3180,7 +3274,7 @@
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+ wpa_ft_process_auth(sta->wpa_sm,
auth_transaction, mgmt->u.auth.variable,
len - IEEE80211_HDRLEN -
sizeof(mgmt->u.auth),
@@ -3228,21 +3322,13 @@
fail:
dst = mgmt->sa;
- bssid = mgmt->bssid;
#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * the MLD MAC address. It is the responsibility of the driver to
- * handle the translations.
- */
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta))
dst = sta->addr;
- bssid = hapd->mld_addr;
- }
#endif /* CONFIG_IEEE80211BE */
- reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
+ reply_res = send_auth_reply(hapd, sta, dst, auth_alg,
auth_alg == WLAN_AUTH_SAE ?
auth_transaction : auth_transaction + 1,
resp, resp_ies, resp_ies_len,
@@ -3281,7 +3367,7 @@
/* Do not assign an AID that is in use on any of the affiliated links
* when finding an AID for a non-AP MLD. */
- if (hapd->conf->mld_ap) {
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
int j;
for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
@@ -3398,37 +3484,58 @@
static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *multi_ap_ie, size_t multi_ap_len)
{
- u8 multi_ap_value = 0;
+ struct multi_ap_params multi_ap;
+ u16 status;
sta->flags &= ~WLAN_STA_MULTI_AP;
if (!hapd->conf->multi_ap)
return WLAN_STATUS_SUCCESS;
- if (multi_ap_ie) {
- const u8 *multi_ap_subelem;
-
- multi_ap_subelem = get_ie(multi_ap_ie + 4,
- multi_ap_len - 4,
- MULTI_AP_SUB_ELEM_TYPE);
- if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
- multi_ap_value = multi_ap_subelem[2];
- } else {
+ if (!multi_ap_ie) {
+ if (!(hapd->conf->multi_ap & FRONTHAUL_BSS)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "Multi-AP IE has missing or invalid Multi-AP subelement");
- return WLAN_STATUS_INVALID_IE;
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
+
+ return WLAN_STATUS_SUCCESS;
}
- if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+ status = check_multi_ap_ie(multi_ap_ie + 4, multi_ap_len - 4,
+ &multi_ap);
+ if (status != WLAN_STATUS_SUCCESS)
+ return status;
+
+ if (multi_ap.capability && multi_ap.capability != MULTI_AP_BACKHAUL_STA)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Multi-AP IE with unexpected value 0x%02x",
- multi_ap_value);
+ multi_ap.capability);
- if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+ if (multi_ap.profile == MULTI_AP_PROFILE_1 &&
+ (hapd->conf->multi_ap_client_disallow &
+ PROFILE1_CLIENT_ASSOC_DISALLOW)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP Profile-1 clients not allowed");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (multi_ap.profile >= MULTI_AP_PROFILE_2 &&
+ (hapd->conf->multi_ap_client_disallow &
+ PROFILE2_CLIENT_ASSOC_DISALLOW)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP Profile-2 clients not allowed");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (!(multi_ap.capability & MULTI_AP_BACKHAUL_STA)) {
if (hapd->conf->multi_ap & FRONTHAUL_BSS)
return WLAN_STATUS_SUCCESS;
@@ -3655,7 +3762,7 @@
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);
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
return WLAN_STATUS_SUCCESS;
}
@@ -3727,15 +3834,15 @@
goto end;
}
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
+ if (ap_sta_is_mld(hapd, sta))
+ wpa_auth_set_ml_info(sta->wpa_sm,
sta->mld_assoc_link_id, &sta->mld_info);
#endif /* CONFIG_IEEE80211BE */
rsn_ie -= 2;
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, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
status = wpa_res_to_status_code(res);
if (status != WLAN_STATUS_SUCCESS)
goto end;
@@ -3824,6 +3931,8 @@
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;
resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
if (resp != WLAN_STATUS_SUCCESS)
@@ -3989,14 +4098,18 @@
if (hapd->conf->wpa && wpa_ie) {
enum wpa_validate_result res;
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info *info = &sta->mld_info;
+ bool init = !sta->wpa_sm;
+#endif /* CONFIG_IEEE80211BE */
wpa_ie -= 2;
wpa_ie_len += 2;
if (!sta->wpa_sm) {
-#ifdef CONFIG_IEEE80211BE
- struct mld_info *info = &sta->mld_info;
-#endif /* CONFIG_IEEE80211BE */
+ if (!link)
+ assoc_sta = hostapd_ml_get_assoc_sta(
+ hapd, sta, &assoc_hapd);
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
@@ -4007,20 +4120,24 @@
"Failed to initialize RSN state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
-
-#ifdef CONFIG_IEEE80211BE
- if (info->mld_sta) {
- wpa_printf(MSG_DEBUG,
- "MLD: Set ML info in RSN Authenticator");
- wpa_auth_set_ml_info(sta->wpa_sm,
- hapd->mld_addr,
- sta->mld_assoc_link_id,
- info);
- }
-#endif /* CONFIG_IEEE80211BE */
}
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s ML info in RSN Authenticator",
+ init ? "Set" : "Reset");
+ wpa_auth_set_ml_info(sta->wpa_sm,
+ sta->mld_assoc_link_id,
+ info);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ wpa_auth_set_rsn_override(sta->wpa_sm,
+ elems->rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm,
+ elems->rsne_override_2 != NULL);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
wpa_ie, wpa_ie_len,
@@ -4029,7 +4146,8 @@
elems->rsnxe ? elems->rsnxe_len + 2 :
0,
elems->mdie, elems->mdie_len,
- elems->owe_dh, elems->owe_dh_len);
+ elems->owe_dh, elems->owe_dh_len,
+ assoc_sta ? assoc_sta->wpa_sm : NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -4150,6 +4268,13 @@
"association");
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
+
+ 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));
#ifdef CONFIG_HS20
} else if (hapd->conf->osen) {
if (!elems->osen) {
@@ -4286,6 +4411,19 @@
sta->power_capab = 0;
}
+ if (elems->bss_max_idle_period &&
+ hapd->conf->max_acceptable_idle_period) {
+ u16 req;
+
+ req = WPA_GET_LE16(elems->bss_max_idle_period);
+ if (req <= hapd->conf->max_acceptable_idle_period)
+ sta->max_idle_period = req;
+ else if (hapd->conf->max_acceptable_idle_period >
+ hapd->conf->ap_max_inactivity)
+ sta->max_idle_period =
+ hapd->conf->max_acceptable_idle_period;
+ }
+
return WLAN_STATUS_SUCCESS;
}
@@ -4309,22 +4447,23 @@
#ifdef CONFIG_IEEE80211BE
-static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
- u16 status_code,
- u8 *buf, size_t buflen)
+static 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;
+ size_t buflen = sizeof(buf);
/* Capability Info */
WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
p += 2;
/* Status Code */
- WPA_PUT_LE16(p, status_code);
+ WPA_PUT_LE16(p, link->status);
p += 2;
- if (status_code != WLAN_STATUS_SUCCESS)
- return p - buf;
+ if (link->status != WLAN_STATUS_SUCCESS)
+ goto out;
/* AID is not included */
p = hostapd_eid_supp_rates(hapd, p);
@@ -4362,20 +4501,24 @@
p += wpabuf_len(hapd->conf->assocresp_elements);
}
- return p - buf;
+out:
+ os_free(link->resp_sta_profile);
+ link->resp_sta_profile = os_memdup(buf, p - buf);
+ link->resp_sta_profile_len = link->resp_sta_profile ? p - buf : 0;
}
-static void 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)
+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)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf = NULL;
struct sta_info *sta = NULL;
u16 status = WLAN_STATUS_SUCCESS;
+ int i;
wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
hapd->mld_link_id, MAC2STR(link->peer_addr));
@@ -4401,7 +4544,7 @@
goto out;
}
- mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlbuf)
goto out;
@@ -4415,35 +4558,46 @@
}
sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
goto out;
}
- sta->mld_info.mld_sta = true;
- sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+ 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];
- /*
- * Get the AID from the station on which the association was performed,
- * and mark it as used.
- */
- sta->aid = origin_sta->aid;
- if (sta->aid == 0) {
- wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto out;
+ li->resp_sta_profile = NULL;
+ li->resp_sta_profile_len = 0;
}
- 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);
- /* RSN Authenticator should always be the one on the original station */
+ if (!offload) {
+ /*
+ * Get the AID from the station on which the association was
+ * performed, and mark it as used.
+ */
+ sta->aid = origin_sta->aid;
+ if (sta->aid == 0) {
+ wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+ 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);
+ }
+
+ /* 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 = NULL;
+ sta->wpa_sm = origin_sta->wpa_sm;
/*
* Do not initialize the EAPOL state machine.
@@ -4454,31 +4608,27 @@
wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
hapd->mld_link_id, sta->aid);
- /*
- * Get RSNE and RSNXE for the current BSS as they are required by the
- * Authenticator.
- */
- link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
- link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
-
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
/* TODO: What other processing is required? */
- if (add_associated_sta(hapd, sta, reassoc))
+ if (!offload && add_associated_sta(hapd, sta, reassoc))
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
out:
wpabuf_free(mlbuf);
link->status = status;
- wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
- if (sta && status != WLAN_STATUS_SUCCESS)
- ap_free_sta(hapd, sta);
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+ if (status != WLAN_STATUS_SUCCESS) {
+ if (sta)
+ ap_free_sta(hapd, sta);
+ return -1;
+ }
+
+ return 0;
}
@@ -4497,61 +4647,59 @@
#endif /* CONFIG_IEEE80211BE */
-static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *ies, size_t ies_len,
- bool reassoc)
+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)
{
#ifdef CONFIG_IEEE80211BE
- unsigned int i, j;
+ unsigned int i;
if (!hostapd_is_mld_ap(hapd))
- return;
-
- /*
- * This is not really needed, but make the interaction with the RSN
- * Authenticator more consistent
- */
- sta->mld_info.links[hapd->mld_link_id].rsne =
- hostapd_wpa_ie(hapd, WLAN_EID_RSN);
- sta->mld_info.links[hapd->mld_link_id].rsnxe =
- hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+ return 0;
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- struct hostapd_iface *iface = NULL;
+ struct hostapd_data *bss = NULL;
struct mld_link_info *link = &sta->mld_info.links[i];
+ bool link_bss_found = false;
- if (!link->valid)
+ if (!link->valid || i == sta->mld_assoc_link_id)
continue;
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- iface = hapd->iface->interfaces->iface[j];
-
- if (hapd->iface == iface)
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd)
continue;
- if (iface->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
- i == iface->bss[0]->mld_link_id)
- break;
+ if (bss->mld_link_id != i)
+ continue;
+
+ link_bss_found = true;
+ break;
}
- if (!iface || j == hapd->iface->interfaces->count) {
+ if (!link_bss_found || TEST_FAIL()) {
wpa_printf(MSG_DEBUG,
"MLD: No link match for link_id=%u", i);
link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(
- hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
+ } else if (tx_link_status != WLAN_STATUS_SUCCESS) {
+ /* TX link rejected the connection */
+ link->status = WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED;
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
} else {
- ieee80211_ml_process_link(iface->bss[0], sta, link,
- ies, ies_len, reassoc);
+ if (ieee80211_ml_process_link(bss, sta, link,
+ ies, ies_len, reassoc,
+ offload))
+ return -1;
}
}
#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
}
@@ -4589,7 +4737,7 @@
bool mld_link_sta = false;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
@@ -4700,7 +4848,7 @@
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)
+ int omit_rsnxe, bool allow_mld_addr_trans)
{
int send_len;
u8 *buf;
@@ -4708,7 +4856,6 @@
struct ieee80211_mgmt *reply;
u8 *p;
u16 res = WLAN_STATUS_SUCCESS;
- const u8 *sa = hapd->own_addr;
buflen = sizeof(struct ieee80211_mgmt) + 1024;
#ifdef CONFIG_FILS
@@ -4745,18 +4892,9 @@
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
-#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * MLD MAC address.
- */
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta)
- sa = hapd->mld_addr;
-#endif /* CONFIG_IEEE80211BE */
-
os_memcpy(reply->da, addr, ETH_ALEN);
- os_memcpy(reply->sa, sa, ETH_ALEN);
- os_memcpy(reply->bssid, sa, ETH_ALEN);
+ os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
send_len = IEEE80211_HDRLEN;
send_len += sizeof(reply->u.assoc_resp);
@@ -4860,7 +4998,7 @@
#endif /* CONFIG_IEEE80211AX */
p = hostapd_eid_ext_capab(hapd, p, false);
- p = hostapd_eid_bss_max_idle_period(hapd, p);
+ p = hostapd_eid_bss_max_idle_period(hapd, p, sta->max_idle_period);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
@@ -4893,7 +5031,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+ 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);
}
@@ -4955,7 +5093,7 @@
#endif /* CONFIG_WPS */
if (sta && (sta->flags & WLAN_STA_MULTI_AP))
- p = hostapd_eid_multi_ap(hapd, p);
+ p = hostapd_eid_multi_ap(hapd, p, buf + buflen - p);
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
@@ -5116,7 +5254,8 @@
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);
+ sta->fils_pending_assoc_req_len, 0, 0,
+ true);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -5151,6 +5290,48 @@
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool reassoc,
+ struct hostapd_data **assoc_hapd)
+{
+ struct sta_info *sta;
+ struct ieee802_11_elems elems;
+ u8 mld_addr[ETH_ALEN];
+ const u8 *pos;
+
+ if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
+ return NULL;
+
+ if (reassoc) {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req);
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req);
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return NULL;
+
+ if (hostapd_process_ml_assoc_req_addr(hapd, elems.basic_mle,
+ elems.basic_mle_len,
+ mld_addr))
+ return NULL;
+
+ sta = ap_get_sta(hapd, mld_addr);
+ if (!sta)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "MLD: assoc: mld=" MACSTR ", link=" MACSTR,
+ MAC2STR(mld_addr), MAC2STR(mgmt->sa));
+
+ return hostapd_ml_get_assoc_sta(hapd, sta, assoc_hapd);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc, int rssi)
@@ -5167,6 +5348,7 @@
#endif /* CONFIG_FILS */
int omit_rsnxe = 0;
bool set_beacon = false;
+ bool mld_addrs_not_translated = false;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -5224,6 +5406,28 @@
}
sta = ap_get_sta(hapd, mgmt->sa);
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * It is possible that the association frame is from an associated
+ * non-AP MLD station, that tries to re-associate using different link
+ * addresses. In such a case, try to find the station based on the AP
+ * MLD MAC address.
+ */
+ if (!sta) {
+ struct hostapd_data *assoc_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;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
@@ -5478,7 +5682,7 @@
#endif /* CONFIG_FILS */
if (set_beacon)
- ieee802_11_set_beacons(hapd->iface);
+ ieee802_11_update_beacons(hapd->iface);
fail:
@@ -5500,8 +5704,9 @@
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS)
- hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+ if (sta)
+ hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
+ resp, false);
if (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
@@ -5545,8 +5750,12 @@
#endif /* CONFIG_FILS */
if (resp >= 0)
- reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
- pos, left, rssi, omit_rsnxe);
+ 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);
os_free(tmp);
/*
@@ -5637,44 +5846,6 @@
}
-#ifdef CONFIG_IEEE80211BE
-static struct sta_info *
-hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- struct hostapd_data **assoc_hapd)
-{
- struct hostapd_data *other_hapd = NULL;
- struct sta_info *tmp_sta;
-
- *assoc_hapd = hapd;
-
- /* The station is the one on which the association was performed */
- if (sta->mld_assoc_link_id == hapd->mld_link_id)
- return sta;
-
- other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
- if (!other_hapd) {
- wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
- sta->mld_assoc_link_id);
- return sta;
- }
-
- /*
- * Iterate over the stations and find the one with the matching link ID
- * and association ID.
- */
- for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
- if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
- tmp_sta->aid == sta->aid) {
- *assoc_hapd = other_hapd;
- return tmp_sta;
- }
- }
-
- return sta;
-}
-#endif /* CONFIG_IEEE80211BE */
-
-
static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
struct sta_info *sta,
const struct ieee80211_mgmt *mgmt,
@@ -5683,7 +5854,7 @@
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *assoc_hapd, *tmp_hapd;
struct sta_info *assoc_sta;
- unsigned int i, link_id;
+ struct sta_info *tmp_sta;
if (!hostapd_is_mld_ap(hapd))
return false;
@@ -5693,47 +5864,28 @@
* the information about all the other links.
*/
assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
- struct sta_info *tmp_sta;
+ for_each_mld_link(tmp_hapd, assoc_hapd) {
+ if (tmp_hapd == assoc_hapd)
+ continue;
- if (!assoc_sta->mld_info.links[link_id].valid)
+ if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id !=
+ assoc_sta->mld_assoc_link_id ||
+ tmp_sta->aid != assoc_sta->aid)
continue;
- tmp_hapd =
- assoc_hapd->iface->interfaces->iface[i]->bss[0];
-
- if (!tmp_hapd->conf->mld_ap ||
- assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
- continue;
-
- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
- tmp_sta = tmp_sta->next) {
- /*
- * Remove the station on which the association
- * was done only after all other link stations
- * are removed. Since there is only a single
- * station per struct hostapd_hapd with the
- * same association link simply break out from
- * the loop.
- */
- if (tmp_sta == assoc_sta)
- break;
-
- if (tmp_sta->mld_assoc_link_id !=
- assoc_sta->mld_assoc_link_id ||
- tmp_sta->aid != assoc_sta->aid)
- continue;
-
- if (!disassoc)
- hostapd_deauth_sta(tmp_hapd, tmp_sta,
- mgmt);
- else
- hostapd_disassoc_sta(tmp_hapd, tmp_sta,
- mgmt);
- break;
- }
+ if (!disassoc)
+ hostapd_deauth_sta(tmp_hapd, tmp_sta, mgmt);
+ else
+ hostapd_disassoc_sta(tmp_hapd, tmp_sta, mgmt);
+ break;
}
}
@@ -5964,6 +6116,25 @@
return 1;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ len >= IEEE80211_HDRLEN + 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ 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) {
+ 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);
+ return 1;
+ }
+#endif /* CONFIG_NAN_USD */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len, freq);
@@ -5982,9 +6153,11 @@
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 */
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6069,6 +6242,10 @@
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;
@@ -6084,7 +6261,7 @@
if (is_multicast_ether_addr(mgmt->sa) ||
is_zero_ether_addr(mgmt->sa) ||
- os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mgmt->sa, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -6110,9 +6287,9 @@
#endif /* CONFIG_MESH */
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
MAC2STR(mgmt->bssid));
return 0;
@@ -6133,9 +6310,12 @@
stype != WLAN_FC_STYPE_ACTION) &&
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_NAN_USD
+ !ether_addr_equal(mgmt->da, nan_network_id) &&
+#endif /* CONFIG_NAN_USD */
+ !ether_addr_equal(mgmt->da, hapd->own_addr)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
@@ -6285,6 +6465,8 @@
struct mld_link_info *link,
bool ok)
{
+ bool updated = false;
+
if (!ok) {
hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -6305,9 +6487,11 @@
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
/*
* TODOs:
@@ -6324,39 +6508,33 @@
struct sta_info *sta, bool ok)
{
#ifdef CONFIG_IEEE80211BE
- unsigned int i, link_id;
+ struct hostapd_data *tmp_hapd;
if (!hostapd_is_mld_ap(hapd))
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_each_mld_link(tmp_hapd, hapd) {
+ struct mld_link_info *link;
+ struct sta_info *tmp_sta;
+ if (tmp_hapd == 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 (tmp_hapd->conf->mld_ap ||
- hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ 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;
-
- ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
- tmp_sta, link,
- ok);
- break;
- }
+ ieee80211_ml_link_sta_assoc_cb(tmp_hapd, tmp_sta, link,
+ ok);
+ break;
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -6379,7 +6557,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
hapd->mld_link_id != sta->mld_assoc_link_id) {
/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
wpa_printf(MSG_DEBUG,
@@ -6479,6 +6657,7 @@
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
(hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ hapd->conf->wds_sta &&
!(sta->flags & WLAN_STA_WPS))) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
@@ -6589,7 +6768,9 @@
size_t len, int ok)
{
struct sta_info *sta;
+#ifndef CONFIG_NO_RRM
const struct rrm_measurement_report_element *report;
+#endif /* CONFIG_NO_RRM */
#ifdef CONFIG_DPP
if (len >= IEEE80211_HDRLEN + 6 &&
@@ -6643,6 +6824,7 @@
}
#endif /* CONFIG_HS20 */
+#ifndef CONFIG_NO_RRM
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
@@ -6653,6 +6835,7 @@
report->len >= 3 &&
report->type == MEASURE_TYPE_BEACON)
hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+#endif /* CONFIG_NO_RRM */
}
@@ -6767,33 +6950,6 @@
}
-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- const u8 *data, size_t len, int ack)
-{
- struct sta_info *sta;
- struct hostapd_iface *iface = hapd->iface;
-
- sta = ap_get_sta(hapd, dst);
- if (sta == NULL && iface->num_bss > 1) {
- size_t j;
- for (j = 0; j < iface->num_bss; j++) {
- hapd = iface->bss[j];
- sta = ap_get_sta(hapd, dst);
- if (sta)
- break;
- }
- }
- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
- wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
- MACSTR " that is not currently associated",
- MAC2STR(dst));
- return;
- }
-
- ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
-}
-
-
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -6864,7 +7020,7 @@
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
- os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(src, hapd->own_addr)) {
/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
* silently. */
return;
@@ -6963,21 +7119,35 @@
tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
/* Default Transmit Power Envelope for Global Operating Class */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+ 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;
+
eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
REG_DEFAULT_CLIENT, tx_pwr);
/* Indoor Access Point must include an additional TPE for
* subordinate devices */
- if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
/* TODO: Extract PSD limits from channel data */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+ 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;
eid = hostapd_add_tpe_info(eid, tx_pwr_count,
tx_pwr_intrpn,
REG_SUBORDINATE_CLIENT,
tx_pwr);
}
+ 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);
+
return eid;
}
#endif /* CONFIG_IEEE80211AX */
@@ -7165,24 +7335,61 @@
u8 end;
};
+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)
+{
+ if (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)
+ return true;
+
+ /* No need to report if length is for MLD TBTT and the BSS is not
+ * affiliated with an aP MLD. Normal TBTT will include this. */
+ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
+ return true;
+
+#ifdef CONFIG_IEEE80211BE
+ /* If building for co-location and they are ML partners, no need to
+ * include since the ML RNR will carry this. */
+ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
+ return true;
+
+ /* If building for ML RNR and they are not ML partners, don't include.
+ */
+ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
+ return true;
+#endif /* CONFIG_IEEE80211BE */
+
+ return false;
+}
+
+
static size_t
hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
size_t *current_len,
- struct mbssid_ie_profiles *skip_profiles)
+ struct mbssid_ie_profiles *skip_profiles,
+ bool mld_update)
{
size_t total_len = 0, len = *current_len;
- int tbtt_count = 0;
- size_t i, start = 0;
- bool ap_mld = false;
+ int tbtt_count, total_tbtt_count = 0;
+ size_t i, start;
+ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
+ RNR_TBTT_INFO_LEN;
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
+repeat_rnr_len:
+ start = 0;
+ tbtt_count = 0;
while (start < hapd->iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
len = RNR_HEADER_LEN;
total_len += RNR_HEADER_LEN;
@@ -7194,35 +7401,61 @@
for (i = start; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
+ bool ap_mld = false;
if (!bss || !bss->conf || !bss->started)
continue;
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = bss->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
if (bss == reporting_hapd ||
bss->conf->ignore_broadcast_ssid)
continue;
- if (skip_profiles &&
- i >= skip_profiles->start && i < skip_profiles->end)
+ if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
+ tbtt_info_len, mld_update,
+ reporting_hapd, bss))
continue;
- if (len + RNR_TBTT_INFO_LEN > 255 ||
+ if (len + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
break;
- if (!ap_mld) {
- len += RNR_TBTT_INFO_LEN;
- total_len += RNR_TBTT_INFO_LEN;
- } else {
- len += RNR_TBTT_INFO_MLD_LEN;
- total_len += RNR_TBTT_INFO_MLD_LEN;
- }
+ len += tbtt_info_len;
+ total_len += tbtt_info_len;
tbtt_count++;
}
start = i;
}
- if (!tbtt_count)
+ 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;
+ }
+
+ 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;
+ }
+
+ if (!total_tbtt_count)
total_len = 0;
else
*current_len = len;
@@ -7271,8 +7504,8 @@
}
-static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
- size_t *current_len)
+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t len = 0;
@@ -7283,66 +7516,89 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
- bool ap_mld = false;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
- ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (iface == hapd->iface ||
- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ if (!iface || iface == hapd->iface ||
+ iface->state != HAPD_IFACE_ENABLED ||
+ !is_6ghz_op_class(iface->conf->op_class))
continue;
len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
- current_len, NULL);
+ current_len, NULL, false);
}
return len;
}
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
+ size_t *current_len)
+{
+ size_t len = 0;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ return 0;
+
+ /* TODO: Allow for FILS/Action as well */
+ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+ return 0;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (!iface || iface == hapd->iface ||
+ hapd->iface->freq == iface->freq)
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+ current_len, NULL, true);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return len;
+}
+
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
+ bool include_mld_params)
{
size_t total_len = 0, current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
if (hapd->conf->rnr)
total_len += hostapd_eid_nr_db_len(hapd, ¤t_len);
/* fallthrough */
-
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ if (mode == COLOCATED_LOWER_BAND)
total_len +=
- hostapd_eid_rnr_multi_iface_len(hapd,
- ¤t_len);
+ hostapd_eid_rnr_colocation_len(hapd,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
¤t_len,
- NULL);
+ NULL, false);
break;
-
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
¤t_len,
- NULL);
- break;
-
- default:
+ NULL, false);
break;
}
+ /* For EMA Beacons, MLD neighbor repoting is added as part of
+ * MBSSID RNR. */
+ 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);
+
return total_len;
}
@@ -7392,7 +7648,7 @@
/* BSS parameters */
*eid++ = nr->bss_parameters;
/* 20 MHz PSD */
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
len += RNR_TBTT_INFO_LEN;
*size_offset = (eid - size_offset) - 1;
}
@@ -7402,23 +7658,118 @@
}
-static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
- struct hostapd_data *reporting_hapd,
- u8 *eid, size_t *current_len,
- struct mbssid_ie_profiles *skip_profiles)
+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)
{
- struct hostapd_data *bss;
struct hostapd_iface *iface = hapd->iface;
- size_t i, start = 0;
- size_t len = *current_len;
- u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
- u8 tbtt_count = 0, op_class, channel, bss_param;
+ struct hostapd_data *bss = iface->bss[i];
+ u8 bss_param = 0;
bool ap_mld = false;
+ 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)
+ return false;
+
+ if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
+ mld_update, reporting_hapd, bss))
+ return false;
+
+ if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ return true;
+
+ if (!(*tbtt_count)) {
+ /* Add neighbor report header info only if there is at least
+ * one TBTT info available. */
+ *tbtt_count_pos = eid++;
+ *eid++ = tbtt_info_len;
+ *eid++ = op_class;
+ *eid++ = bss->iconf->channel;
+ *len += RNR_TBTT_HEADER_LEN;
+ }
+
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+ os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+ eid += 4;
+ if (bss->conf->ssid.short_ssid == reporting_hapd->conf->ssid.short_ssid)
+ bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+ if (iface->conf->mbssid != MBSSID_DISABLED && iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (bss == hostapd_mbssid_get_tx_bss(hapd))
+ bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ bss->conf->unsol_bcast_probe_resp_interval)
+ bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+ bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
+
+#ifdef CONFIG_IEEE80211BE
+ if (ap_mld) {
+ u8 param_ch = bss->eht_mld_bss_param_change;
+ bool is_partner;
+
+ /* If BSS is not a partner of the reporting_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;
+ /* Link ID (Bit 3 to Bit 0)
+ * BPCC (Bit 4 to Bit 7) */
+ *eid++ = is_partner ?
+ 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;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (bss->conf->mld_indicate_disabled)
+ *eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
+#endif /* CONFIG_TESTING_OPTIONS */
+ eid++;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ *len += tbtt_info_len;
+ (*tbtt_count)++;
+ *pos = eid;
+
+ return false;
+}
+
+
+static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ u8 *eid, size_t *current_len,
+ struct mbssid_ie_profiles *skip_profiles,
+ bool mld_update)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ size_t i, start;
+ 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 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
+ RNR_TBTT_INFO_LEN;
+
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
return eid;
@@ -7429,9 +7780,12 @@
NUM_HOSTAPD_MODES)
return eid;
+repeat_rnr:
+ start = 0;
+ tbtt_count = 0;
while (start < iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
eid_start = eid;
*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
@@ -7440,77 +7794,34 @@
tbtt_count = 0;
}
- tbtt_count_pos = eid++;
- *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- *eid++ = op_class;
- *eid++ = hapd->iconf->channel;
- len += RNR_TBTT_HEADER_LEN;
-
for (i = start; i < iface->num_bss; i++) {
- bss_param = 0;
- bss = iface->bss[i];
- if (!bss || !bss->conf || !bss->started)
- continue;
-
- if (bss == reporting_hapd ||
- bss->conf->ignore_broadcast_ssid)
- continue;
-
- if (skip_profiles &&
- i >= skip_profiles->start && i < skip_profiles->end)
- continue;
-
- if (len + RNR_TBTT_INFO_LEN > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
+ skip_profiles, i,
+ &tbtt_count, &len, &eid,
+ &tbtt_count_pos, tbtt_info_len,
+ op_class, mld_update))
break;
-
- *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- os_memcpy(eid, bss->own_addr, ETH_ALEN);
- eid += ETH_ALEN;
- os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
- eid += 4;
- if (bss->conf->ssid.short_ssid ==
- reporting_hapd->conf->ssid.short_ssid)
- bss_param |= RNR_BSS_PARAM_SAME_SSID;
-
- if (iface->conf->mbssid != MBSSID_DISABLED &&
- iface->num_bss > 1) {
- bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
- if (bss == hostapd_mbssid_get_tx_bss(hapd))
- bss_param |=
- RNR_BSS_PARAM_TRANSMITTED_BSSID;
- }
-
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
- bss->conf->unsol_bcast_probe_resp_interval)
- bss_param |=
- RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
-
- bss_param |= RNR_BSS_PARAM_CO_LOCATED;
-
- *eid++ = bss_param;
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
-
- if (!ap_mld) {
- len += RNR_TBTT_INFO_LEN;
- } else {
-#ifdef CONFIG_IEEE80211BE
- *eid++ = hapd->conf->mld_id;
- *eid++ = hapd->mld_link_id | (1 << 4);
- *eid++ = 0;
- len += RNR_TBTT_INFO_MLD_LEN;
-#endif /* CONFIG_IEEE80211BE */
- }
-
- tbtt_count += 1;
}
start = i;
- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
- *size_offset = (eid - size_offset) - 1;
+
+ if (tbtt_count) {
+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+ *size_offset = (eid - size_offset) - 1;
+ }
}
- if (tbtt_count == 0)
+ 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;
+ goto repeat_rnr;
+ }
+
+ if (!total_tbtt_count)
return eid_start;
*current_len = len;
@@ -7518,8 +7829,8 @@
}
-static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
- size_t *current_len)
+u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t i;
@@ -7529,64 +7840,88 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
- bool ap_mld = false;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
- hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
- ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (iface == hapd->iface ||
- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ if (!iface || iface == hapd->iface ||
+ iface->state != HAPD_IFACE_ENABLED ||
+ !is_6ghz_op_class(iface->conf->op_class))
continue;
eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
- current_len, NULL);
+ current_len, NULL, false);
}
return eid;
}
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ u8 *eid, size_t *current_len)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ return eid;
+
+ /* TODO: Allow for FILS/Action as well */
+ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+ return eid;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (!iface || iface == hapd->iface ||
+ hapd->iface->freq == iface->freq)
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len, NULL, true);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
+ bool include_mld_params)
{
u8 *eid_start = eid;
size_t current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
if (hapd->conf->rnr)
eid = hostapd_eid_nr_db(hapd, eid, ¤t_len);
/* fallthrough */
-
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND || ap_mld)
- eid = hostapd_eid_rnr_multi_iface(hapd, eid,
- ¤t_len);
+ if (mode == COLOCATED_LOWER_BAND)
+ eid = hostapd_eid_rnr_colocation(hapd, eid,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
- ¤t_len, NULL);
+ ¤t_len, NULL, false);
break;
-
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
- ¤t_len, NULL);
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ ¤t_len, NULL, false);
break;
-
default:
return eid_start;
}
+ /* For EMA Beacons, MLD neighbor repoting is added as part of
+ * MBSSID RNR. */
+ 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);
+
if (eid == eid_start + 2)
return eid_start;
@@ -7604,13 +7939,46 @@
}
+static size_t hostapd_mbssid_ext_capa(struct hostapd_data *bss,
+ struct hostapd_data *tx_bss, u8 *buf)
+{
+ u8 ext_capa_tx[20], *ext_capa_tx_end, ext_capa[20], *ext_capa_end;
+ size_t ext_capa_len, ext_capa_tx_len;
+
+ ext_capa_tx_end = hostapd_eid_ext_capab(tx_bss, ext_capa_tx,
+ true);
+ ext_capa_tx_len = ext_capa_tx_end - ext_capa_tx;
+ ext_capa_end = hostapd_eid_ext_capab(bss, ext_capa, true);
+ ext_capa_len = ext_capa_end - ext_capa;
+ if (ext_capa_tx_len != ext_capa_len ||
+ os_memcmp(ext_capa_tx, ext_capa, ext_capa_len) != 0) {
+ os_memcpy(buf, ext_capa, ext_capa_len);
+ return ext_capa_len;
+ }
+
+ return 0;
+}
+
+
static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
u32 frame_type, size_t *bss_index,
const u8 *known_bss,
size_t known_bss_len)
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- size_t len = 3, i;
+ size_t len, i;
+ u8 ext_capa[20];
+
+ /* Element ID: 1 octet
+ * Length: 1 octet
+ * MaxBSSID Indicator: 1 octet
+ * Optional Subelements: vatiable
+ *
+ * Total fixed length: 3 octets
+ *
+ * 1 octet in len for the MaxBSSID Indicator field.
+ */
+ len = 1;
for (i = *bss_index; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
@@ -7645,6 +8013,10 @@
if (rsnx)
nontx_profile_len += 2 + rsnx[1];
}
+
+ nontx_profile_len += hostapd_mbssid_ext_capa(bss, tx_bss,
+ ext_capa);
+
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
ie_count++;
if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
@@ -7663,7 +8035,9 @@
}
*bss_index = i;
- return len;
+
+ /* Add 2 octets to get the full size of the element */
+ return len + 2;
}
@@ -7672,6 +8046,11 @@
size_t known_bss_len, size_t *rnr_len)
{
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 ||
(frame_type != WLAN_FC_STYPE_BEACON &&
@@ -7704,12 +8083,12 @@
*rnr_len += hostapd_eid_rnr_iface_len(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- &rnr_cur_len, &skip_profiles);
+ &rnr_cur_len, &skip_profiles, ap_mld);
}
}
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
- *rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
+ *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
return len;
}
@@ -7792,13 +8171,17 @@
eid += 2 + rsnx[1];
}
}
+
+ eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
+
+ /* 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 (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
- non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
if (hapd->conf->xrates_supported &&
!bss->conf->xrates_supported)
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;
if (ie_count) {
*eid++ = WLAN_EID_EXTENSION;
*eid++ = 2 + ie_count + 1;
@@ -7834,7 +8217,11 @@
{
size_t bss_index = 1, cur_len = 0;
u8 elem_index = 0, *rnr_start_eid = rnr_eid;
- bool add_rnr;
+ bool add_rnr, ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
(frame_stype != WLAN_FC_STYPE_BEACON &&
@@ -7879,7 +8266,7 @@
cur_len = 0;
rnr_eid = hostapd_eid_rnr_iface(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- rnr_eid, &cur_len, &skip_profiles);
+ rnr_eid, &cur_len, &skip_profiles, ap_mld);
}
}
@@ -7891,80 +8278,11 @@
if (hapd->conf->rnr)
rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
- rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
- &cur_len);
+ rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
+ &cur_len);
}
return eid;
}
-
-static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
-{
- u8 first_chan = *seg0 - 6, sec_chan;
-
- switch (bitmap) {
- case 0x6:
- *seg0 = 0;
- return;
- case 0x8:
- case 0x4:
- case 0x2:
- case 0x1:
- case 0xC:
- case 0x3:
- if (pri_chan < *seg0)
- *seg0 -= 4;
- else
- *seg0 += 4;
- break;
- }
-
- if (pri_chan < *seg0)
- sec_chan = pri_chan + 4;
- else
- sec_chan = pri_chan - 4;
-
- if (bitmap & BIT((sec_chan - first_chan) / 4))
- *seg0 = 0;
-}
-
-
-static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
- enum oper_chan_width *width, u8 *seg0)
-{
- if (pri < *seg0) {
- *seg0 -= 8;
- if (bitmap & 0x0F) {
- *width = 0;
- punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
- }
- } else {
- *seg0 += 8;
- if (bitmap & 0xF0) {
- *width = 0;
- punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
- seg0);
- }
- }
-}
-
-
-void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
- u8 *seg0, u8 *seg1)
-{
- if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
- *width = CONF_OPER_CHWIDTH_USE_HT;
- punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
- }
-
- if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
- *width = CONF_OPER_CHWIDTH_80MHZ;
- *seg1 = 0;
- punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
- }
-
- /* TODO: 320 MHz */
-}
-
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 4b58fee..dd4995f 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -23,6 +23,7 @@
struct sae_pk;
struct sae_pt;
struct sae_password_entry;
+struct mld_info;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -88,8 +89,14 @@
const struct ieee80211_eht_capabilities *src,
struct ieee80211_eht_capabilities *dest,
size_t len);
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+ struct mld_info *mld_info,
+ u8 *eid, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid);
+size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ struct mld_info *info,
+ bool include_mld_id);
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
@@ -97,6 +104,9 @@
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta);
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
@@ -119,11 +129,10 @@
const u8 *he_6ghz_capab);
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);
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);
-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- const u8 *data, size_t len, int ack);
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
@@ -138,7 +147,8 @@
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
-u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
+ u16 value);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE
@@ -217,8 +227,10 @@
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len);
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
+ bool include_mld_params);
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
+ bool include_mld_params);
int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
int res, struct radius_sta *info);
size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
@@ -238,12 +250,18 @@
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);
-void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
- enum oper_chan_width *width, u8 *seg0, u8 *seg1);
bool hostapd_is_mld_ap(struct hostapd_data *hapd);
const char * sae_get_password(struct hostapd_data *hapd,
struct sta_info *sta, const char *rx_id,
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,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd);
+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);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 4277d82..913a995 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -84,7 +84,7 @@
os_get_reltime(&now);
for (entry = hapd->acl_cache; entry; entry = entry->next) {
- if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->addr, addr))
continue;
if (os_reltime_expired(&now, &entry->timestamp,
@@ -128,6 +128,9 @@
goto fail;
}
+ if (!radius_msg_add_msg_auth(msg))
+ goto fail;
+
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
os_strlen(buf))) {
@@ -281,7 +284,7 @@
query = hapd->acl_queries;
while (query) {
- if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(query->addr, addr)) {
/* pending query in RADIUS retransmit queue;
* do not generate a new one */
return HOSTAPD_ACL_PENDING;
@@ -505,7 +508,9 @@
"Found matching Access-Request for RADIUS message (id=%d)",
query->radius_id);
- if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ if (radius_msg_verify(
+ msg, shared_secret, shared_secret_len, req,
+ hapd->conf->radius_require_message_authenticator)) {
wpa_printf(MSG_INFO,
"Incoming RADIUS packet did not have correct authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
@@ -596,7 +601,8 @@
if (query->radius_psk) {
struct sta_info *sta;
- bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+ bool success = cache->accepted == HOSTAPD_ACL_ACCEPT ||
+ cache->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT;
sta = ap_get_sta(hapd, query->addr);
if (!sta || !sta->wpa_sm) {
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index c1ccda4..afb2e16 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -44,12 +44,13 @@
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- u8 he_oper_chwidth, const u8 *he_phy_cap,
+ int he_oper_chwidth, 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:
@@ -66,7 +67,11 @@
break;
}
- he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ 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 ||
@@ -199,12 +204,25 @@
struct ieee80211_eht_operation *oper;
u8 *pos = eid, seg0 = 0, seg1 = 0;
enum oper_chan_width chwidth;
- size_t elen = 1 + 4 + 3;
+ size_t elen = 1 + 4;
+ bool eht_oper_info_present;
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
if (!hapd->iface->current_mode)
return eid;
- if (hapd->iconf->punct_bitmap)
+ if (is_6ghz_op_class(conf->op_class))
+ chwidth = op_class_to_ch_width(conf->op_class);
+ else
+ chwidth = conf->eht_oper_chwidth;
+
+ eht_oper_info_present = chwidth == CONF_OPER_CHWIDTH_320MHZ ||
+ punct_bitmap;
+
+ if (eht_oper_info_present)
+ elen += 3;
+
+ if (punct_bitmap)
elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
*pos++ = WLAN_EID_EXTENSION;
@@ -212,7 +230,10 @@
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
oper = (struct ieee80211_eht_operation *) pos;
- oper->oper_params = EHT_OPER_INFO_PRESENT;
+ oper->oper_params = 0;
+
+ if (hapd->iconf->eht_default_pe_duration)
+ oper->oper_params |= EHT_OPER_DEFAULT_PE_DURATION;
/* TODO: Fill in appropriate EHT-MCS max Nss information */
oper->basic_eht_mcs_nss_set[0] = 0x11;
@@ -220,11 +241,10 @@
oper->basic_eht_mcs_nss_set[2] = 0x00;
oper->basic_eht_mcs_nss_set[3] = 0x00;
- if (is_6ghz_op_class(conf->op_class))
- chwidth = op_class_to_ch_width(conf->op_class);
- else
- chwidth = conf->eht_oper_chwidth;
+ if (!eht_oper_info_present)
+ return pos + elen;
+ oper->oper_params |= EHT_OPER_INFO_PRESENT;
seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
switch (chwidth) {
@@ -258,10 +278,10 @@
oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
oper->oper_info.ccfs1 = seg1;
- if (hapd->iconf->punct_bitmap) {
+ if (punct_bitmap) {
oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
oper->oper_info.disabled_chan_bitmap =
- host_to_le16(hapd->iconf->punct_bitmap);
+ host_to_le16(punct_bitmap);
}
return pos + elen;
@@ -338,9 +358,8 @@
static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
- u8 opclass, u8 he_oper_chwidth,
- const u8 *he_cap, const u8 *eht_cap,
- size_t len)
+ u8 opclass, const u8 *he_cap,
+ const u8 *eht_cap, size_t len)
{
const struct ieee80211_he_capabilities *he_capab;
struct ieee80211_eht_capabilities *cap;
@@ -355,8 +374,8 @@
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
- he_phy_cap, cap->phy_cap);
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap,
+ cap->phy_cap);
if (len < cap_len)
return true;
@@ -380,7 +399,6 @@
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
@@ -421,8 +439,9 @@
}
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id)
+static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ u8 *eid, struct mld_info *mld_info,
+ bool include_mld_id)
{
struct wpabuf *buf;
u16 control;
@@ -431,6 +450,8 @@
size_t len, slice_len;
u8 link_id;
u8 common_info_len;
+ u16 mld_cap;
+ u8 max_simul_links, active_links;
/*
* As the Multi-Link element can exceed the size of 255 bytes need to
@@ -454,7 +475,8 @@
* BSS Parameters Change Count (1) + EML Capabilities (2) +
* MLD Capabilities and Operations (2)
*/
- common_info_len = 13;
+#define EHT_ML_COMMON_INFO_LEN 13
+ common_info_len = EHT_ML_COMMON_INFO_LEN;
if (include_mld_id) {
/* AP MLD ID */
@@ -467,7 +489,7 @@
wpabuf_put_u8(buf, common_info_len);
/* Own MLD MAC Address */
- wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+ wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
/* Own Link ID */
wpabuf_put_u8(buf, hapd->mld_link_id);
@@ -479,22 +501,39 @@
hapd->iface->mld_eml_capa);
wpabuf_put_le16(buf, hapd->iface->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;
+
+ if (active_links > max_simul_links) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Error in max simultaneous links, advertised: 0x%x current: 0x%x",
+ max_simul_links, active_links);
+ active_links = max_simul_links;
+ }
+
+ mld_cap &= ~EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+ mld_cap |= active_links & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+
+ /* TODO: Advertise T2LM based on driver support as well */
+ mld_cap &= ~EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK;
+
wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
- hapd->iface->mld_mld_capa);
- wpabuf_put_le16(buf, hapd->iface->mld_mld_capa);
+ mld_cap);
+ wpabuf_put_le16(buf, mld_cap);
if (include_mld_id) {
wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
- hapd->conf->mld_id);
- wpabuf_put_u8(buf, hapd->conf->mld_id);
+ hostapd_get_mld_id(hapd));
+ wpabuf_put_u8(buf, hostapd_get_mld_id(hapd));
}
- if (!info)
+ if (!mld_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 mld_link_info *link = &mld_info->links[link_id];
struct hostapd_data *link_bss;
/*
@@ -502,8 +541,9 @@
* beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
* parameters change counter (1) + station profile length.
*/
- const size_t fixed_len = 22;
- size_t total_len = fixed_len + link->resp_sta_profile_len;
+#define EHT_ML_STA_INFO_LEN 22
+ size_t total_len = EHT_ML_STA_INFO_LEN +
+ link->resp_sta_profile_len;
/* Skip the local one */
if (link_id == hapd->mld_link_id || !link->valid)
@@ -537,7 +577,7 @@
/* STA Info */
/* STA Info Length */
- wpabuf_put_u8(buf, fixed_len - 2);
+ wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
@@ -549,12 +589,14 @@
wpabuf_put_le64(buf, 0);
/* DTIM Info */
- wpabuf_put_le16(buf, link_bss->conf->dtim_period);
+ wpabuf_put_u8(buf, 0); /* DTIM Count */
+ wpabuf_put_u8(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
- /* TODO: 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);
+
+ if (!link->resp_sta_profile)
+ continue;
/* Fragment the sub element if needed */
if (total_len <= 255) {
@@ -564,7 +606,7 @@
ptr = link->resp_sta_profile;
len = link->resp_sta_profile_len;
- slice_len = 255 - fixed_len;
+ slice_len = 255 - EHT_ML_STA_INFO_LEN;
wpabuf_put_data(buf, ptr, slice_len);
len -= slice_len;
@@ -625,6 +667,151 @@
}
+static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct hostapd_data *other_hapd;
+ u16 control;
+ u8 *pos = eid;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML");
+
+ /* First check if the element needs to be added */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: %u",
+ other_hapd->eht_mld_link_removal_count);
+
+ if (other_hapd->eht_mld_link_removal_count)
+ break;
+ }
+
+ /* No link is going to be removed */
+ if (i == hapd->iface->interfaces->count)
+ return eid;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: Adding element");
+
+ /* The length will be set at the end */
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 0;
+ *pos++ = WLAN_EID_EXT_MULTI_LINK;
+
+ /* Set the Multi-Link Control field */
+ control = MULTI_LINK_CONTROL_TYPE_RECONF;
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* Common Info doesn't include any information */
+ *pos++ = 1;
+
+ /* Add the per station profiles */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+ if (!other_hapd->eht_mld_link_removal_count)
+ continue;
+
+ /* Subelement ID is 0 */
+ *pos++ = 0;
+ *pos++ = 5;
+
+ control = other_hapd->mld_link_id |
+ EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER;
+
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* STA profile length */
+ *pos++ = 3;
+
+ WPA_PUT_LE16(pos, other_hapd->eht_mld_link_removal_count);
+ pos += 2;
+ }
+
+ eid[1] = pos - eid - 2;
+
+ wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration ML", eid, eid[1] + 2);
+ return pos;
+#else /* CONFIG_TESTING_OPTIONS */
+ return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ bool include_mld_id)
+{
+ size_t len = 0;
+ size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
+ u8 link_id;
+
+ if (include_mld_id)
+ eht_ml_len++;
+
+ for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
+ link_id++) {
+ struct mld_link_info *link;
+ size_t sta_len = EHT_ML_STA_INFO_LEN;
+
+ link = &info->links[link_id];
+ if (!link->valid)
+ continue;
+
+ sta_len += link->resp_sta_profile_len;
+
+ /* Element data and (fragmentation) headers */
+ eht_ml_len += sta_len;
+ eht_ml_len += 2 + sta_len / 255 * 2;
+ }
+
+ /* Element data */
+ len += eht_ml_len;
+
+ /* First header (254 bytes of data) */
+ len += 3;
+
+ /* Fragmentation headers; +1 for shorter first chunk */
+ len += (eht_ml_len + 1) / 255 * 2;
+
+ return len;
+}
+#undef EHT_ML_COMMON_INFO_LEN
+#undef EHT_ML_STA_INFO_LEN
+
+
+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);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid)
+{
+ if (!ap_sta_is_mld(hapd, info))
+ return eid;
+
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
+ false);
+ ap_sta_free_sta_profile(&info->mld_info);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+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);
+}
+
+
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
{
struct wpabuf *buf = wpabuf_alloc(12);
@@ -637,7 +824,7 @@
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, hapd->mld_addr, ETH_ALEN);
+ wpabuf_put_data(buf, hapd->mld->mld_addr, ETH_ALEN);
return buf;
}
@@ -841,11 +1028,12 @@
static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- struct mld_info *info)
+ struct sta_info *sta)
{
- u8 i, link_id;
+ u8 link_id;
+ struct mld_info *info = &sta->mld_info;
- if (!info->mld_sta) {
+ if (!ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
return 0;
}
@@ -862,38 +1050,103 @@
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct hostapd_data *other_hapd;
- if (!info->links[link_id].valid)
+ if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
continue;
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
-
- if (hapd == other_hapd)
- continue;
-
- if (other_hapd->conf->mld_ap &&
- other_hapd->conf->mld_id == hapd->conf->mld_id &&
- link_id == other_hapd->mld_link_id)
- break;
- }
-
- if (i == hapd->iface->interfaces->count &&
- link_id != hapd->mld_link_id) {
+ other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!other_hapd) {
wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
link_id);
return -1;
}
- if (i < hapd->iface->interfaces->count)
- os_memcpy(info->links[link_id].local_addr,
- other_hapd->own_addr,
- ETH_ALEN);
+ os_memcpy(info->links[link_id].local_addr, other_hapd->own_addr,
+ ETH_ALEN);
}
return 0;
}
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr)
+{
+ struct wpabuf *mlbuf = ieee802_11_defrag(basic_mle, basic_mle_len,
+ true);
+ struct ieee80211_eht_ml *ml;
+ struct eht_ml_basic_common_info *common_info;
+ size_t ml_len, common_info_len;
+ int ret = -1;
+ u16 ml_control;
+
+ if (!mlbuf)
+ return WLAN_STATUS_SUCCESS;
+
+ ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+
+ if (ml_len < sizeof(*ml))
+ goto out;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+ ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+ goto out;
+ }
+
+ /* 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;
+
+ if (sizeof(*ml) + common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+ goto out;
+ }
+
+ common_info = (struct eht_ml_basic_common_info *) ml->variable;
+
+ /* Common information length includes the length octet */
+ if (common_info->len != common_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid common info len=%u", common_info->len);
+ goto out;
+ }
+
+ /* Get the MLD MAC Address */
+ os_memcpy(mld_addr, common_info->mld_addr, ETH_ALEN);
+ ret = 0;
+
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta)
@@ -908,7 +1161,7 @@
int ret = -1;
u16 ml_control;
- mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
if (!mlbuf)
return WLAN_STATUS_SUCCESS;
@@ -990,8 +1243,8 @@
info->common_info.eml_capa, info->common_info.mld_capa);
/* Check the MLD MAC Address */
- if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(info->common_info.mld_addr,
+ common_info->mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: MLD address mismatch between authentication ("
MACSTR ") and association (" MACSTR ")",
@@ -1000,7 +1253,7 @@
goto out;
}
- info->links[hapd->mld_link_id].valid = true;
+ info->links[hapd->mld_link_id].valid = 1;
/* Parse the link info field */
ml_len -= sizeof(*ml) + common_info_len;
@@ -1031,9 +1284,11 @@
if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
wpa_printf(MSG_DEBUG,
- "MLD: Unexpected Multi-Link element subelement ID=%u",
+ "MLD: Skip unknown Multi-Link element subelement ID=%u",
*pos);
- goto out;
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
}
/* Skip the subelement ID and the length */
@@ -1138,7 +1393,7 @@
goto out;
}
- ret = hostapd_mld_validate_assoc_info(hapd, info);
+ ret = hostapd_mld_validate_assoc_info(hapd, sta);
out:
wpabuf_free(mlbuf);
if (ret) {
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 548a448..a2deda6 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
@@ -224,10 +225,11 @@
u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
u8 control;
-
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->punct_bitmap) {
- punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+
+ if (punct_bitmap) {
+ punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
}
@@ -254,16 +256,15 @@
control = 3;
else
control = center_idx_to_bw_6ghz(seg0);
- if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
- control |= HE_6GHZ_STANDARD_POWER_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
- else
- control |= HE_6GHZ_INDOOR_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
+ control |= hapd->iconf->he_6ghz_reg_pwr_type <<
+ HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
*pos++ = control;
/* Channel Center Freq Seg0/Seg1 */
- if (oper_chwidth == 2) {
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+ oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) {
/*
* Seg 0 indicates the channel center frequency index of
* the 160 MHz channel.
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 31dfb62..3dd3a6a 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,16 +21,25 @@
#include "ieee802_11.h"
+static u8 * hostapd_eid_timeout_interval(u8 *pos, u8 type, u32 value)
+{
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = type;
+ WPA_PUT_LE32(pos, value);
+ pos += 4;
+
+ return pos;
+}
+
+
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid)
{
- u8 *pos = eid;
u32 timeout, tu;
struct os_reltime now, passed;
+ u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK;
- *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
- *pos++ = 5;
- *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
os_get_reltime(&now);
os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
@@ -40,10 +49,12 @@
timeout = 0;
if (timeout < hapd->conf->assoc_sa_query_max_timeout)
timeout++; /* add some extra time for local timers */
- WPA_PUT_LE32(pos, timeout);
- pos += 4;
- return pos;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->test_assoc_comeback_type != -1)
+ type = hapd->conf->test_assoc_comeback_type;
+#endif /* CONFIG_TESTING_OPTIONS */
+ return hostapd_eid_timeout_interval(eid, type, timeout);
}
@@ -51,13 +62,14 @@
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
-#ifdef CONFIG_OCV
- struct sta_info *sta;
-#endif /* CONFIG_OCV */
+#if defined(CONFIG_OCV) || defined(CONFIG_IEEE80211BE)
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+#endif /* CONFIG_OCV || CONFIG_IEEE80211BE */
struct ieee80211_mgmt *mgmt;
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
MACSTR, MAC2STR(addr));
@@ -65,7 +77,6 @@
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
- sta = ap_get_sta(hapd, addr);
if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
@@ -108,11 +119,16 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
@@ -141,6 +157,7 @@
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
MACSTR, MAC2STR(sa));
@@ -200,11 +217,16 @@
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(resp->da, sa, ETH_ALEN);
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->sa, own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, own_addr, ETH_ALEN);
resp->u.action.category = WLAN_ACTION_SA_QUERY;
resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
@@ -419,6 +441,8 @@
if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
*pos |= 0x40; /* Bit 78 - TWT responder */
#endif /* CONFIG_IEEE80211AX */
+ if (hostapd_get_ht_vht_twt_responder(hapd))
+ *pos |= 0x40; /* Bit 78 - TWT responder */
break;
case 10: /* Bits 80-87 */
#ifdef CONFIG_SAE
@@ -713,12 +737,14 @@
}
-u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
+ u16 value)
{
u8 *pos = eid;
#ifdef CONFIG_WNM_AP
- if (hapd->conf->ap_max_inactivity > 0) {
+ if (hapd->conf->ap_max_inactivity > 0 &&
+ hapd->conf->bss_max_idle) {
unsigned int val;
*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
*pos++ = 3;
@@ -731,9 +757,13 @@
val = 1;
if (val > 65535)
val = 65535;
+ if (value)
+ val = value;
WPA_PUT_LE16(pos, val);
pos += 2;
- *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ /* Set the Protected Keep-Alive Required bit based on
+ * configuration */
+ *pos++ = hapd->conf->bss_max_idle == 2 ? BIT(0) : 0x00;
}
#endif /* CONFIG_WNM_AP */
@@ -1067,7 +1097,7 @@
{
u8 *pos = eid;
bool sae_pk = false;
- u16 capab = 0;
+ u32 capab = 0, tmp;
size_t flen;
if (!(hapd->conf->wpa & WPA_PROTO_RSN))
@@ -1096,18 +1126,28 @@
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->conf->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
- flen = (capab & 0xff00) ? 2 : 1;
- if (len < 2 + flen || !capab)
+ if (!capab)
+ return eid; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
+
+ if (len < 2 + flen)
return eid; /* no supported extended RSN capabilities */
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
- *pos++ = capab & 0x00ff;
- capab >>= 8;
- if (capab)
- *pos++ = capab;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
return pos;
}
@@ -1116,13 +1156,11 @@
u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len)
{
-#ifdef CONFIG_INTERWORKING
/* check for QoS Map support */
if (ext_capab_ie_len >= 5) {
if (ext_capab_ie[4] & 0x01)
sta->qos_map_enabled = 1;
}
-#endif /* CONFIG_INTERWORKING */
if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
@@ -1137,3 +1175,54 @@
return WLAN_STATUS_SUCCESS;
}
+
+
+struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *other_hapd = NULL;
+ struct sta_info *tmp_sta;
+
+ if (!ap_sta_is_mld(hapd, sta))
+ return NULL;
+
+ *assoc_hapd = hapd;
+
+ /* The station is the one on which the association was performed */
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ return sta;
+
+ other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+ sta->mld_assoc_link_id);
+ return sta;
+ }
+
+ /*
+ * Iterate over the stations and find the one with the matching link ID
+ * and association ID.
+ */
+ for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+ tmp_sta->aid == sta->aid) {
+ *assoc_hapd = other_hapd;
+ return tmp_sta;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return sta;
+}
+
+
+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)) &&
+ (hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER);
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index db615a3..4dc325c 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
@@ -79,6 +80,9 @@
hostapd_get_oper_chwidth(hapd->iconf);
u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
+#ifdef CONFIG_IEEE80211BE
+ u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+#endif /* CONFIG_IEEE80211BE */
if (is_6ghz_op_class(hapd->iconf->op_class))
return eid;
@@ -90,8 +94,8 @@
os_memset(oper, 0, sizeof(*oper));
#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->punct_bitmap) {
- punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+ if (punct_bitmap) {
+ punct_update_legacy_bw(punct_bitmap,
hapd->iconf->channel,
&oper_chwidth, &seg0, &seg1);
}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 052231e..f4103ac 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -114,12 +114,15 @@
bool authorized, bool mld)
{
int res;
+ bool update;
if (sta->flags & WLAN_STA_PREAUTH)
return;
- ap_sta_set_authorized(hapd, sta, authorized);
+ update = ap_sta_set_authorized_flag(hapd, sta, authorized);
res = hostapd_set_authorized(hapd, sta, authorized);
+ if (update)
+ ap_sta_set_authorized_event(hapd, sta, authorized);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
authorized ? "" : "un");
@@ -169,8 +172,7 @@
struct hostapd_data *tmp_hapd =
hapd->iface->interfaces->iface[i]->bss[0];
- if (!tmp_hapd->conf->mld_ap ||
- hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ if (!hostapd_is_ml_partner(hapd, tmp_hapd))
continue;
for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
@@ -765,6 +767,9 @@
goto fail;
}
+ if (!radius_msg_add_msg_auth(msg))
+ goto fail;
+
if (sm->identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
sm->identity, sm->identity_len)) {
@@ -2037,16 +2042,7 @@
}
sta = sm->sta;
- /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
- * present when packet contains an EAP-Message attribute */
- if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
- radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
- 0) < 0 &&
- radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
- } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
- req, 1)) {
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) {
wpa_printf(MSG_INFO,
"Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
@@ -2537,13 +2533,29 @@
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
+ struct hostapd_data *first;
+
+ first = hostapd_mld_get_first_bss(hapd);
+ if (!first)
+ return -1;
+
+ if (!first->eapol_auth) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS IEEE 802.1X state machine does not exist. Init on its behalf");
+
+ if (ieee802_1x_init(first))
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG,
"MLD: Using IEEE 802.1X state machine of the first BSS");
- hapd->eapol_auth = hapd->mld_first_bss->eapol_auth;
+ hapd->eapol_auth = first->eapol_auth;
return 0;
}
+#endif /* CONFIG_IEEE80211BE */
dl_list_init(&hapd->erp_keys);
@@ -2629,13 +2641,15 @@
void ieee802_1x_deinit(struct hostapd_data *hapd)
{
- if (hapd->mld_first_bss) {
+#ifdef CONFIG_IEEE80211BE
+ if (!hostapd_mld_is_first_bss(hapd)) {
wpa_printf(MSG_DEBUG,
"MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
hapd->eapol_auth = NULL;
return;
}
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_WEP
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
new file mode 100644
index 0000000..52a967a
--- /dev/null
+++ b/src/ap/nan_usd_ap.c
@@ -0,0 +1,267 @@
+/*
+ * 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/wpa_ctrl.h"
+#include "common/nan_de.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "nan_usd_ap.h"
+
+
+static int hostapd_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 hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
+ " A3=" MACSTR " len=%zu",
+ MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ 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));
+}
+
+
+static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
+ unsigned int duration)
+{
+ 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)
+{
+ struct hostapd_data *hapd = 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(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);
+ os_free(ssi_hex);
+}
+
+
+static void
+hostapd_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 hostapd_data *hapd = 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(hapd->msg_ctx, 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 hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
+ "publish_id=%d reason=%s",
+ publish_id, nan_reason_txt(reason));
+}
+
+
+static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
+ "subscribe_id=%d reason=%s",
+ subscribe_id, nan_reason_txt(reason));
+}
+
+
+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)
+{
+ struct hostapd_data *hapd = 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(hapd->msg_ctx, 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 hostapd_nan_usd_init(struct hostapd_data *hapd)
+{
+ struct nan_callbacks cb;
+
+ os_memset(&cb, 0, sizeof(cb));
+ cb.ctx = hapd;
+ cb.tx = hostapd_nan_de_tx;
+ cb.listen = hostapd_nan_de_listen;
+ cb.discovery_result = hostapd_nan_de_discovery_result;
+ cb.replied = hostapd_nan_de_replied;
+ cb.publish_terminated = hostapd_nan_de_publish_terminated;
+ 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);
+ if (!hapd->nan_de)
+ return -1;
+ return 0;
+}
+
+
+void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
+{
+ nan_de_deinit(hapd->nan_de);
+ 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)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+}
+
+
+void hostapd_nan_usd_flush(struct hostapd_data *hapd)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_flush(hapd->nan_de);
+}
+
+
+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)
+{
+ int publish_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
+ ssi, elems, params);
+ wpabuf_free(elems);
+ return publish_id;
+}
+
+
+void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_publish(hapd->nan_de, publish_id);
+}
+
+
+int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
+ const struct wpabuf *ssi)
+{
+ int ret;
+
+ if (!hapd->nan_de)
+ return -1;
+ ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
+ return ret;
+}
+
+
+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)
+{
+ int subscribe_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
+ srv_proto_type, ssi, elems, params);
+ wpabuf_free(elems);
+ return subscribe_id;
+}
+
+
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
+}
+
+
+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)
+{
+ if (!hapd->nan_de)
+ return -1;
+ return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
+ req_instance_id);
+}
diff --git a/src/ap/nan_usd_ap.h b/src/ap/nan_usd_ap.h
new file mode 100644
index 0000000..58ff5fc
--- /dev/null
+++ b/src/ap/nan_usd_ap.h
@@ -0,0 +1,46 @@
+/*
+ * 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_AP_H
+#define NAN_USD_AP_H
+
+struct nan_subscribe_params;
+struct nan_publish_params;
+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,
+ 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);
+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);
+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);
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id);
+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);
+void hostapd_nan_usd_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int duration);
+void hostapd_nan_usd_cancel_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq);
+void hostapd_nan_usd_tx_wait_expire(struct hostapd_data *hapd);
+
+#endif /* NAN_USD_AP_H */
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 788c12f..bc1eb62 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -61,6 +61,7 @@
dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
list) {
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+ dl_list_del(&ip6addr->list);
os_free(ip6addr);
}
}
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 5b276e8..f7a7d83 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -24,7 +24,7 @@
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
list) {
- if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(bssid, nr->bssid) &&
(!ssid ||
(ssid->ssid_len == nr->ssid.ssid_len &&
os_memcmp(ssid->ssid, nr->ssid.ssid,
@@ -99,7 +99,10 @@
nr->civic = NULL;
os_memset(nr->bssid, 0, sizeof(nr->bssid));
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+ os_memset(&nr->lci_date, 0, sizeof(nr->lci_date));
nr->stationary = 0;
+ nr->short_ssid = 0;
+ nr->bss_parameters = 0;
}
@@ -165,6 +168,14 @@
}
+static void hostapd_neighbor_free(struct hostapd_neighbor_entry *nr)
+{
+ hostapd_neighbor_clear_entry(nr);
+ dl_list_del(&nr->list);
+ os_free(nr);
+}
+
+
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid)
{
@@ -174,9 +185,7 @@
if (!nr)
return -1;
- hostapd_neighbor_clear_entry(nr);
- dl_list_del(&nr->list);
- os_free(nr);
+ hostapd_neighbor_free(nr);
return 0;
}
@@ -188,9 +197,7 @@
dl_list_for_each_safe(nr, prev, &hapd->nr_db,
struct hostapd_neighbor_entry, list) {
- hostapd_neighbor_clear_entry(nr);
- dl_list_del(&nr->list);
- os_free(nr);
+ hostapd_neighbor_free(nr);
}
}
@@ -325,3 +332,35 @@
wpabuf_free(nr);
#endif /* NEED_AP_MLME */
}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_get_diff_short_ssid(struct hostapd_data *hapd, const u8 *bssid)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (ether_addr_equal(bssid, nr->bssid) &&
+ nr->short_ssid != hapd->conf->ssid.short_ssid)
+ return nr;
+ }
+ return NULL;
+}
+
+
+int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
+{
+ struct hostapd_neighbor_entry *nr;
+
+ nr = hostapd_neighbor_get_diff_short_ssid(hapd, hapd->own_addr);
+ if (!nr)
+ return -1;
+
+ /* Clear old entry due to SSID change */
+ hostapd_neighbor_free(nr);
+
+ hostapd_neighbor_set_own_report(hapd);
+
+ return 0;
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index 992671b..53f7142 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -20,6 +20,7 @@
const struct wpabuf *civic, int stationary,
u8 bss_parameters);
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
+int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
void hostapd_free_neighbor_db(struct hostapd_data *hapd);
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index ee4232f..2fce838 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -487,14 +487,14 @@
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
entry = entry->hnext) {
if ((spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ ether_addr_equal(entry->spa, spa)) &&
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
} else {
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+ ether_addr_equal(entry->spa, spa))
return entry;
}
}
@@ -521,7 +521,7 @@
u8 new_pmkid[PMKID_LEN];
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->spa, spa))
continue;
if (wpa_key_mgmt_sae(entry->akmp) ||
wpa_key_mgmt_fils(entry->akmp)) {
@@ -575,7 +575,7 @@
int match = 0;
if (attr->sta_addr) {
- if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(attr->sta_addr, entry->spa))
return 0;
match++;
}
@@ -717,7 +717,7 @@
* <BSSID> <PMKID> <PMK> <expiration in seconds>
*/
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ if (addr && !ether_addr_equal(entry->spa, addr))
continue;
ret = os_snprintf(pos, end - pos, MACSTR " ",
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
index 3284a10..cb225c6 100644
--- a/src/ap/preauth_auth.c
+++ b/src/ap/preauth_auth.c
@@ -58,7 +58,7 @@
ethhdr = (struct l2_ethhdr *) buf;
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(ethhdr->h_dest, hapd->own_addr)) {
wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
MACSTR, MAC2STR(ethhdr->h_dest));
return;
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index f2d5cd1..fbcddf3 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -334,6 +334,53 @@
}
+static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "RRM: Link measurement request (token %u) timed out",
+ hapd->link_measurement_req_token);
+ hapd->link_mesr_req_active = 0;
+}
+
+
+static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ const struct rrm_link_measurement_report *report;
+ const u8 *pos, *end;
+ char report_msg[2 * 8 + 1];
+
+ end = buf + len;
+ pos = mgmt->u.action.u.rrm.variable;
+ report = (const struct rrm_link_measurement_report *) (pos - 1);
+ if (end - (const u8 *) report < (int) sizeof(*report))
+ return;
+
+ if (!hapd->link_mesr_req_active ||
+ (hapd->link_measurement_req_token != report->dialog_token)) {
+ wpa_printf(MSG_INFO,
+ "Unexpected Link measurement report, token %u",
+ report->dialog_token);
+ return;
+ }
+
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
+
+ report_msg[0] = '\0';
+ if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
+ pos, end - pos) < 0)
+ return;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
+ MAC2STR(mgmt->sa), report->dialog_token, report_msg);
+}
+
+
void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
@@ -356,6 +403,9 @@
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
hostapd_handle_nei_report_req(hapd, buf, len);
break;
+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ hostapd_handle_link_mesr_report(hapd, buf, len);
+ break;
default:
wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
mgmt->u.action.u.rrm.action);
@@ -563,6 +613,7 @@
hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
}
@@ -672,3 +723,73 @@
" %u ack=%d", MAC2STR(mgmt->da),
mgmt->u.action.u.rrm.dialog_token, ok);
}
+
+
+int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
+ MAC2STR(addr));
+
+ if (!(hapd->iface->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: the driver does not support TX power insertion");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: specied STA is not connected");
+ return -1;
+ }
+
+ if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: destination STA does not support link measurement");
+ return -1;
+ }
+
+ if (hapd->link_mesr_req_active) {
+ wpa_printf(MSG_DEBUG,
+ "Request Link Measurement: request already in process - overriding");
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
+ hapd, NULL);
+ }
+
+ /* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
+ buf = wpabuf_alloc(5);
+ if (!buf)
+ return -1;
+
+ hapd->link_measurement_req_token++;
+ if (!hapd->link_measurement_req_token)
+ hapd->link_measurement_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->link_measurement_req_token);
+ /* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
+ * Power */
+ wpabuf_put_u8(buf, 0);
+ 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);
+ if (ret < 0)
+ return ret;
+
+ hapd->link_mesr_req_active = 1;
+
+ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+ hostapd_link_mesr_rep_timeout_handler, hapd,
+ NULL);
+
+ return hapd->link_measurement_req_token;
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
index 02cd522..17751e0 100644
--- a/src/ap/rrm.h
+++ b/src/ap/rrm.h
@@ -29,5 +29,7 @@
void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok);
+int hostapd_send_link_measurement_req(struct hostapd_data *hapd,
+ const u8 *addr);
#endif /* RRM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index a00f896..13613db 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -92,7 +92,7 @@
if (p2p_dev_addr == NULL)
continue;
- if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(p2p_dev_addr, addr))
return sta;
}
@@ -140,7 +140,7 @@
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, sta->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
@@ -180,13 +180,48 @@
sta->pasn->fils.erp_resp = NULL;
#endif /* CONFIG_FILS */
- bin_clear_free(sta->pasn, sizeof(*sta->pasn));
+ pasn_data_deinit(sta->pasn);
sta->pasn = NULL;
}
}
#endif /* CONFIG_PASN */
+
+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))
+ return;
+#endif /* CONFIG_IEEE80211BE */
+
+ hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta)
+{
+ struct sta_info *lsta;
+ struct hostapd_data *lhapd;
+
+ if (!ap_sta_is_mld(hapd, psta))
+ return;
+
+ for_each_mld_link(lhapd, hapd) {
+ if (lhapd == hapd)
+ continue;
+
+ lsta = ap_get_sta(lhapd, psta->addr);
+ if (lsta)
+ lsta->wpa_sm = NULL;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_beacon = 0;
@@ -200,6 +235,7 @@
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
(hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ hapd->conf->wds_sta &&
!(sta->flags & WLAN_STA_WPS)))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
@@ -209,7 +245,7 @@
if (!hapd->iface->driver_ap_teardown &&
!(sta->flags & WLAN_STA_PREAUTH)) {
- hostapd_drv_sta_remove(hapd, sta->addr);
+ __ap_free_sta(hapd, sta);
sta->added_unassoc = 0;
}
@@ -303,10 +339,18 @@
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
- if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
- hapd->mld_link_id == sta->mld_assoc_link_id)
+ if (!ap_sta_is_mld(hapd, sta) ||
+ hapd->mld_link_id == sta->mld_assoc_link_id) {
wpa_auth_sta_deinit(sta->wpa_sm);
-#else
+ /* 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);
+#else /* CONFIG_IEEE80211BE */
wpa_auth_sta_deinit(sta->wpa_sm);
#endif /* CONFIG_IEEE80211BE */
@@ -350,6 +394,7 @@
#ifdef CONFIG_INTERWORKING
if (sta->gas_dialog) {
int i;
+
for (i = 0; i < GAS_DIALOG_MAX; i++)
gas_serv_dialog_clear(&sta->gas_dialog[i]);
os_free(sta->gas_dialog);
@@ -420,6 +465,10 @@
os_free(sta->ifname_wds);
+#ifdef CONFIG_IEEE80211BE
+ ap_sta_free_sta_profile(&sta->mld_info);
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_TESTING_OPTIONS
os_free(sta->sae_postponed_commit);
forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
@@ -449,6 +498,27 @@
}
+#ifdef CONFIG_IEEE80211BE
+void hostapd_free_link_stas(struct hostapd_data *hapd)
+{
+ struct sta_info *sta, *prev;
+
+ sta = hapd->sta_list;
+ while (sta) {
+ prev = sta;
+ sta = sta->next;
+
+ if (!hostapd_sta_is_link_sta(hapd, prev))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "Removing link station from MLD " MACSTR,
+ MAC2STR(prev->addr));
+ ap_free_sta(hapd, prev);
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
/**
* ap_handle_timer - Per STA timer handler
* @eloop_ctx: struct hostapd_data *
@@ -463,6 +533,7 @@
struct sta_info *sta = timeout_ctx;
unsigned long next_time = 0;
int reason;
+ int max_inactivity = hapd->conf->ap_max_inactivity;
wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
@@ -475,6 +546,9 @@
return;
}
+ if (sta->max_idle_period)
+ max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+
if ((sta->flags & WLAN_STA_ASSOC) &&
(sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC)) {
@@ -496,7 +570,7 @@
* Anyway, try again after the next inactivity timeout,
* but do not disconnect the station now.
*/
- next_time = hapd->conf->ap_max_inactivity + fuzz;
+ next_time = max_inactivity + fuzz;
} else if (inactive_sec == -ENOENT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has lost its driver entry",
@@ -505,20 +579,19 @@
/* Avoid sending client probe on removed client */
sta->timeout_next = STA_DISASSOC;
goto skip_poll;
- } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
+ } else if (inactive_sec < max_inactivity) {
/* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago",
MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC;
- next_time = hapd->conf->ap_max_inactivity + fuzz -
- inactive_sec;
+ next_time = max_inactivity + fuzz - inactive_sec;
} else {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been "
"inactive too long: %d sec, max allowed: %d",
MAC2STR(sta->addr), inactive_sec,
- hapd->conf->ap_max_inactivity);
+ max_inactivity);
if (hapd->conf->skip_inactivity_poll)
sta->timeout_next = STA_DISASSOC;
@@ -534,7 +607,7 @@
/* data nullfunc frame poll did not produce TX errors; assume
* station ACKed it */
sta->timeout_next = STA_NULLFUNC;
- next_time = hapd->conf->ap_max_inactivity;
+ next_time = max_inactivity;
}
skip_poll:
@@ -722,6 +795,7 @@
{
struct sta_info *sta;
int i;
+ int max_inactivity = hapd->conf->ap_max_inactivity;
sta = ap_get_sta(hapd, addr);
if (sta)
@@ -755,12 +829,15 @@
}
sta->supported_rates_len = i;
+ if (sta->max_idle_period)
+ max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
"for " MACSTR " (%d seconds - ap_max_inactivity)",
__func__, MAC2STR(addr),
- hapd->conf->ap_max_inactivity);
- eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ max_inactivity);
+ eloop_register_timeout(max_inactivity, 0,
ap_handle_timer, hapd, sta);
}
@@ -846,12 +923,42 @@
}
-void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_disconnect_common(struct hostapd_data *hapd,
+ struct sta_info *sta, unsigned int timeout)
+{
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+
+ ap_sta_set_authorized(hapd, sta, 0);
+ hostapd_set_sta_flags(hapd, sta);
+
+ wpa_printf(MSG_DEBUG,
+ "reschedule ap_handle_timer timeout (%u sec) for " MACSTR,
+ MAC2STR(sta->addr), timeout);
+
+ 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);
+#ifdef CONFIG_IEEE80211BE
+ if (!hapd->conf->mld_ap ||
+ hapd->mld_link_id == sta->mld_assoc_link_id) {
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+ }
+#else /* CONFIG_IEEE80211BE */
+ wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
+ sta->wpa_sm = NULL;
+}
+
+
+static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
{
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
/* Skip deauthentication in DMG/IEEE 802.11ad */
@@ -862,27 +969,8 @@
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
sta->timeout_next = STA_DEAUTH;
}
- ap_sta_set_authorized(hapd, sta, 0);
- hostapd_set_sta_flags(hapd, sta);
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
- __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DISASSOC);
- eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
- ap_handle_timer, hapd, sta);
- accounting_sta_stop(hapd, sta);
- ieee802_1x_free_station(hapd, sta);
-#ifdef CONFIG_IEEE80211BE
- if (!hapd->conf->mld_ap ||
- hapd->mld_link_id == sta->mld_assoc_link_id)
- wpa_auth_sta_deinit(sta->wpa_sm);
-#else
- wpa_auth_sta_deinit(sta->wpa_sm);
-#endif /* CONFIG_IEEE80211BE */
- sta->wpa_sm = NULL;
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
sta->disassoc_reason = reason;
sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -905,8 +993,8 @@
}
-void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
{
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
@@ -918,21 +1006,11 @@
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
- ap_sta_set_authorized(hapd, sta, 0);
- hostapd_set_sta_flags(hapd, sta);
+
sta->timeout_next = STA_REMOVE;
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
- __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);
- accounting_sta_stop(hapd, sta);
- ieee802_1x_free_station(hapd, sta);
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
sta->deauth_reason = reason;
sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
@@ -943,6 +1021,104 @@
}
+static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason,
+ bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+ unsigned int i, link_id;
+ struct hapd_interfaces *interfaces;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+
+ /*
+ * Get the station on which the association was performed, as it holds
+ * the information about all the other links.
+ */
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
+ interfaces = assoc_hapd->iface->interfaces;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!hostapd_is_ml_partner(tmp_hapd, assoc_hapd))
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ /*
+ * Handle the station on which the association
+ * was done only after all other link station
+ * are removed. Since there is a only a single
+ * station per hapd with the same association
+ * link simply break;
+ */
+ if (tmp_sta == assoc_sta)
+ break;
+
+ if (tmp_sta->mld_assoc_link_id !=
+ assoc_sta->mld_assoc_link_id ||
+ tmp_sta->aid != assoc_sta->aid)
+ continue;
+
+ if (disassoc)
+ ap_sta_handle_disassociate(tmp_hapd,
+ tmp_sta,
+ reason);
+ else
+ ap_sta_handle_deauthenticate(tmp_hapd,
+ tmp_sta,
+ reason);
+
+ break;
+ }
+ }
+ }
+
+ /* Disconnect the station on which the association was performed. */
+ if (disassoc)
+ ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
+ else
+ ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+
+ return true;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+ return;
+
+ ap_sta_handle_disassociate(hapd, sta, reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+ return;
+
+ ap_sta_handle_deauthenticate(hapd, sta, reason);
+}
+
+
#ifdef CONFIG_WPS
int ap_sta_wps_cancel(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx)
@@ -1292,25 +1468,17 @@
}
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
- int authorized)
+bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
{
- const u8 *dev_addr = NULL;
- char buf[100];
-#ifdef CONFIG_P2P
- u8 addr[ETH_ALEN];
- u8 ip_addr_buf[4];
-#endif /* CONFIG_P2P */
- u8 *ip_ptr = NULL;
-
if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
- return;
+ return false;
if (authorized) {
int mld_assoc_link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
if (sta->mld_assoc_link_id == hapd->mld_link_id)
mld_assoc_link_id = sta->mld_assoc_link_id;
else
@@ -1325,6 +1493,21 @@
sta->flags &= ~WLAN_STA_AUTHORIZED;
}
+ return true;
+}
+
+
+void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ const u8 *dev_addr = NULL;
+ char buf[100];
+#ifdef CONFIG_P2P
+ u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
+#endif /* CONFIG_P2P */
+ const u8 *ip_ptr = NULL;
+
#ifdef CONFIG_P2P
if (hapd->p2p_group == NULL) {
if (sta->p2p_ie != NULL &&
@@ -1413,6 +1596,15 @@
}
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
+ return;
+ ap_sta_set_authorized_event(hapd, sta, authorized);
+}
+
+
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason)
{
@@ -1604,6 +1796,32 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_data *tmp_hapd;
+
+ for_each_mld_link(tmp_hapd, hapd) {
+ struct sta_info *tmp_sta;
+
+ if (hapd == tmp_hapd)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ !ether_addr_equal(tmp_sta->addr, sta->addr))
+ continue;
+
+ ap_free_sta(tmp_hapd, tmp_sta);
+ break;
+ }
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
{
const u8 *mld_link_addr = NULL;
@@ -1618,11 +1836,17 @@
*/
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+
+ /*
+ * In case the AP is affiliated with an AP MLD, we need to
+ * remove the station from all relevant links/APs.
+ */
+ ap_sta_remove_link_sta(hapd, sta);
}
#endif /* CONFIG_IEEE80211BE */
@@ -1646,3 +1870,19 @@
sta->added_unassoc = 1;
return 0;
}
+
+
+#ifdef CONFIG_IEEE80211BE
+void ap_sta_free_sta_profile(struct mld_info *info)
+{
+ int i;
+
+ if (!info)
+ return;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(info->links[i].resp_sta_profile);
+ info->links[i].resp_sta_profile = NULL;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e2b9dde..8462935 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -17,6 +17,7 @@
#include "common/sae.h"
#include "crypto/sha384.h"
#include "pasn/pasn_common.h"
+#include "hostapd.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -81,20 +82,18 @@
} common_info;
struct mld_link_info {
- u8 valid;
+ u8 valid:1;
+ u8 nstr_bitmap_len:2;
u8 local_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
- size_t nstr_bitmap_len;
u8 nstr_bitmap[2];
u16 capability;
u16 status;
- size_t resp_sta_profile_len;
- u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN];
-
- const u8 *rsne, *rsnxe;
+ u16 resp_sta_profile_len;
+ u8 *resp_sta_profile;
} links[MAX_NUM_MLD_LINKS];
};
@@ -333,6 +332,9 @@
struct mld_info mld_info;
u8 mld_assoc_link_id;
#endif /* CONFIG_IEEE80211BE */
+
+ u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+ * units of 1000 TUs */
};
@@ -393,6 +395,10 @@
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
+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,
struct sta_info *sta, int authorized);
static inline int ap_sta_is_authorized(struct sta_info *sta)
@@ -415,4 +421,26 @@
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (sta)
+ sta->mld_info.mld_sta = mld;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+void ap_sta_free_sta_profile(struct mld_info *info);
+
+void hostapd_free_link_stas(struct hostapd_data *hapd);
+
#endif /* STA_INFO_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 9ebb01e..dad768e 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -20,13 +20,6 @@
#include "ap_drv_ops.h"
#include "wmm.h"
-#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-#ifndef MAX
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
{
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 153ee40..af8ccca 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -44,6 +44,20 @@
}
+static const u8 * wnm_ap_get_own_addr(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && (!sta || ap_sta_is_mld(hapd, sta)))
+ own_addr = hapd->mld->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ return own_addr;
+}
+
+
/* MLME-SLEEPMODE.response */
static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
const u8 *addr, u8 dialog_token,
@@ -63,6 +77,7 @@
struct sta_info *sta;
enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+ const u8 *own_addr;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
@@ -143,9 +158,12 @@
res = -1;
goto fail;
}
+
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -366,6 +384,8 @@
u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr;
+ struct sta_info *sta;
size_t len;
u8 *pos;
int res;
@@ -373,9 +393,13 @@
mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -821,14 +845,15 @@
{
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -887,14 +912,15 @@
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -939,6 +965,7 @@
u8 *buf, *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR
@@ -952,8 +979,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
@@ -1002,6 +1029,26 @@
os_free(buf);
if (disassoc_timer) {
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ int i;
+ unsigned int links = 0;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (sta->mld_info.links[i].valid)
+ links++;
+ }
+
+ if (links > 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Only terminating one link - other links remains associated for "
+ MACSTR,
+ MAC2STR(sta->mld_info.common_info.mld_addr));
+ return 0;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
/* send disassociation frame after time-out */
set_disassoc_timer(hapd, sta, disassoc_timer);
}
@@ -1016,6 +1063,7 @@
u8 buf[100], *pos;
struct ieee80211_mgmt *mgmt;
u8 dialog_token = 1;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
if (auto_report > 3 || timeout > 63)
return -1;
@@ -1024,8 +1072,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.coloc_intf_req.action =
WNM_COLLOCATED_INTERFERENCE_REQ;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index a662201..bbf41d3 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -62,7 +62,7 @@
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256,
u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
- size_t *key_len);
+ 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,
@@ -71,6 +71,9 @@
struct wpa_group *group);
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 const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
@@ -87,7 +90,7 @@
{
#ifdef CONFIG_IEEE80211BE
if (sm->mld_assoc_link_id >= 0)
- return sm->own_mld_addr;
+ return sm->wpa_auth->mld_addr;
#endif /* CONFIG_IEEE80211BE */
return sm->wpa_auth->addr;
}
@@ -103,6 +106,125 @@
}
+static void wpa_gkeydone_sta(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm->wpa_auth)
+ return;
+
+ sm->wpa_auth->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations--;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ int release_link_id)
+{
+ int link_id;
+
+ if (!sm || release_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;
+ }
+ }
+}
+
+
+struct wpa_get_link_auth_ctx {
+ const u8 *addr;
+ const u8 *mld_addr;
+ int link_id;
+ struct wpa_authenticator *wpa_auth;
+};
+
+static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
+{
+ struct wpa_get_link_auth_ctx *ctx = data;
+
+ if (!wpa_auth->is_ml)
+ return 0;
+
+ if (ctx->mld_addr &&
+ !ether_addr_equal(wpa_auth->mld_addr, ctx->mld_addr))
+ return 0;
+
+ if ((ctx->addr && ether_addr_equal(wpa_auth->addr, ctx->addr)) ||
+ (ctx->link_id > -1 && wpa_auth->is_ml &&
+ wpa_auth->link_id == ctx->link_id)) {
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+
+ }
+ return 0;
+}
+
+
+static struct wpa_authenticator *
+wpa_get_link_auth(struct wpa_authenticator *wpa_auth, int link_id)
+{
+ struct wpa_get_link_auth_ctx ctx;
+
+ ctx.addr = NULL;
+ ctx.mld_addr = wpa_auth->mld_addr;
+ ctx.link_id = link_id;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_link_sta_auth, &ctx);
+ return ctx.wpa_auth;
+}
+
+
+static int wpa_get_primary_auth_cb(struct wpa_authenticator *wpa_auth,
+ void *data)
+{
+ struct wpa_get_link_auth_ctx *ctx = data;
+
+ if (!wpa_auth->is_ml ||
+ !ether_addr_equal(wpa_auth->mld_addr, ctx->addr) ||
+ !wpa_auth->primary_auth)
+ return 0;
+
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static struct wpa_authenticator *
+wpa_get_primary_auth(struct wpa_authenticator *wpa_auth)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct wpa_get_link_auth_ctx ctx;
+
+ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
+ return wpa_auth;
+
+ ctx.addr = wpa_auth->mld_addr;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_auth_cb, &ctx);
+
+ return ctx.wpa_auth;
+#else /* CONFIG_IEEE80211BE */
+ return wpa_auth;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
@@ -194,6 +316,9 @@
if (!wpa_auth->cb->get_seqnum)
return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+ os_memset(seq, 0, WPA_KEY_RSC_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
#ifdef CONFIG_TESTING_OPTIONS
if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
@@ -369,14 +494,16 @@
}
-static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
{
- struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_group *group, *next;
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
group = wpa_auth->group;
while (group) {
+ wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator ("
+ MACSTR "), group vlan %d",
+ MAC2STR(wpa_auth->addr), group->vlan_id);
wpa_group_get(wpa_auth, group);
group->GTKReKey = true;
@@ -389,6 +516,83 @@
wpa_group_put(wpa_auth, group);
group = next;
}
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *next;
+
+ group = wpa_auth->group;
+ while (group) {
+ wpa_group_get(wpa_auth, group);
+
+ wpa_group_update_gtk(wpa_auth, group);
+ next = group->next;
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
+}
+
+
+static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
+{
+ const u8 *mld_addr = ctx;
+
+ if (!ether_addr_equal(wpa_auth->mld_addr, mld_addr))
+ return 0;
+
+ wpa_update_all_gtks(wpa_auth);
+ return 0;
+}
+
+
+static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
+ void *ctx)
+{
+ const u8 *mld_addr = ctx;
+
+ if (!ether_addr_equal(wpa_auth->mld_addr, mld_addr))
+ return 0;
+
+ wpa_rekey_all_groups(wpa_auth);
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml) {
+ /* Non-primary ML authenticator eloop timer for group rekey is
+ * never started and shouldn't fire. Check and warn just in
+ * case. */
+ if (!wpa_auth->primary_auth) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Cannot start GTK rekey on non-primary ML authenticator");
+ return;
+ }
+
+ /* Generate all the new group keys */
+ wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
+ wpa_auth->mld_addr);
+
+ /* Send all the generated group keys to the respective stations
+ * with group key handshake. */
+ wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
+ wpa_auth->mld_addr);
+ } else {
+ wpa_rekey_all_groups(wpa_auth);
+ }
+#else /* CONFIG_IEEE80211BE */
+ wpa_rekey_all_groups(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
if (wpa_auth->conf.wpa_group_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
@@ -538,8 +742,19 @@
wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
if (!wpa_auth)
return NULL;
+
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+
+#ifdef CONFIG_IEEE80211BE
+ if (conf->mld_addr) {
+ wpa_auth->is_ml = true;
+ wpa_auth->link_id = conf->link_id;
+ wpa_auth->primary_auth = !conf->first_link_auth;
+ os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_auth->cb = cb;
wpa_auth->cb_ctx = cb_ctx;
@@ -583,7 +798,15 @@
wpa_rekey_gmk, wpa_auth, NULL);
}
+#ifdef CONFIG_IEEE80211BE
+ /* For AP MLD, run group rekey timer only on one link (first) and
+ * whenever it fires do rekey on all associated ML links in one shot.
+ */
+ if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
+ wpa_auth->conf.wpa_group_rekey) {
+#else /* CONFIG_IEEE80211BE */
if (wpa_auth->conf.wpa_group_rekey) {
+#endif /* CONFIG_IEEE80211BE */
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
wpa_rekey_gtk, wpa_auth, NULL);
}
@@ -599,6 +822,15 @@
}
#endif /* CONFIG_P2P */
+ 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;
}
@@ -618,6 +850,17 @@
}
+static void wpa_auth_free_conf(struct wpa_auth_config *conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->eapol_m1_elements);
+ conf->eapol_m1_elements = NULL;
+ wpabuf_free(conf->eapol_m3_elements);
+ conf->eapol_m3_elements = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -627,6 +870,9 @@
struct wpa_group *group, *prev;
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);
@@ -643,6 +889,9 @@
os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth->rsne_override);
+ os_free(wpa_auth->rsne_override_2);
+ os_free(wpa_auth->rsnxe_override);
group = wpa_auth->group;
while (group) {
@@ -651,6 +900,7 @@
bin_clear_free(prev, sizeof(*prev));
}
+ wpa_auth_free_conf(&wpa_auth->conf);
os_free(wpa_auth);
}
@@ -668,6 +918,7 @@
if (!wpa_auth)
return 0;
+ wpa_auth_free_conf(&wpa_auth->conf);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -777,6 +1028,10 @@
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr)) {
wpa_printf(MSG_DEBUG,
@@ -789,10 +1044,8 @@
bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
}
#endif /* CONFIG_P2P */
- if (sm->GUpdateStationKeys) {
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
- }
+ if (sm->GUpdateStationKeys)
+ wpa_gkeydone_sta(sm);
#ifdef CONFIG_IEEE80211R_AP
os_free(sm->assoc_resp_ftie);
wpabuf_free(sm->ft_pending_req_ies);
@@ -800,6 +1053,13 @@
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm->rsnxe);
+#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);
+ sm->mld_links[link_id].wpa_auth = NULL;
+ }
+#endif /* CONFIG_IEEE80211BE */
wpa_group_put(sm->wpa_auth, sm->group);
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
@@ -817,12 +1077,20 @@
wpa_auth = sm->wpa_auth;
if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+ struct wpa_authenticator *primary_auth = wpa_auth;
+
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA is leaving");
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml && !wpa_auth->primary_auth)
+ primary_auth = wpa_get_primary_auth(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
+
if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
- wpa_auth, NULL) == -1)
+ primary_auth, NULL) == -1)
eloop_register_timeout(0, 500000, wpa_rekey_gtk,
- wpa_auth, NULL);
+ primary_auth, NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
@@ -900,19 +1168,70 @@
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
{
- struct wpa_ie_data ie;
+ struct wpa_ie_data ie, assoc_ie;
struct rsn_mdie *mdie;
+ unsigned int i, j;
+ bool found = false;
+
+ /* Verify that PMKR1Name from EAPOL-Key message 2/4 matches the value
+ * we derived. */
if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
- ie.num_pmkid != 1 || !ie.pmkid) {
+ ie.num_pmkid < 1 || !ie.pmkid) {
wpa_printf(MSG_DEBUG,
"FT: No PMKR1Name in FT 4-way handshake message 2/4");
return -1;
}
- os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, PMKID_LEN);
+ if (wpa_parse_wpa_ie_rsn(sm->wpa_ie, sm->wpa_ie_len, &assoc_ie) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Could not parse (Re)Association Request frame RSNE");
+ os_memset(&assoc_ie, 0, sizeof(assoc_ie));
+ /* Continue to allow PMKR1Name matching to be done to cover the
+ * case where it is the only listed PMKID. */
+ }
+
+ for (i = 0; i < ie.num_pmkid; i++) {
+ const u8 *pmkid = ie.pmkid + i * PMKID_LEN;
+
+ if (os_memcmp_const(pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant matches PMKR1Name",
+ i);
+ found = true;
+ } else {
+ for (j = 0; j < assoc_ie.num_pmkid; j++) {
+ if (os_memcmp(pmkid,
+ assoc_ie.pmkid + j * PMKID_LEN,
+ PMKID_LEN) == 0)
+ break;
+ }
+
+ if (j == assoc_ie.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is neither PMKR1Name nor included in AssocReq",
+ i);
+ found = false;
+ break;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is not PMKR1Name, but matches a PMKID in AssocReq",
+ i);
+ }
+ }
+
+ if (!found) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way handshake");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: PMKIDs/PMKR1Name from Supplicant",
+ ie.pmkid, ie.num_pmkid * PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return -1;
+ }
if (!kde->mdie || !kde->ftie) {
wpa_printf(MSG_DEBUG,
@@ -1011,7 +1330,8 @@
}
if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0,
- pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0)
+ pmk_r0, pmk_r1, pmk_r0_name, &key_len,
+ false) < 0)
break;
if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -1076,28 +1396,166 @@
}
+enum eapol_key_msg { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST };
+
+static bool wpa_auth_valid_key_desc_ver(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, u16 ver)
+{
+ if (ver > WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_INFO, "RSN: " MACSTR
+ " used undefined Key Descriptor Version %d",
+ MAC2STR(wpa_auth_get_spa(sm)), ver);
+ return false;
+ }
+
+ if (!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "advertised support for AES-128-CMAC, but did not use it");
+ return false;
+ }
+
+ if (sm->pairwise != WPA_CIPHER_TKIP &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ !wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use HMAC-SHA1-AES with CCMP/GCMP");
+ return false;
+ }
+
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_request_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *replay_counter)
+{
+
+ if (sm->req_replay_counter_used &&
+ os_memcmp(replay_counter, sm->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "received EAPOL-Key request with replayed counter");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const struct wpa_eapol_key *key,
+ enum eapol_key_msg msg,
+ const char *msgtxt)
+{
+ int i;
+
+ if (msg == REQUEST)
+ return wpa_auth_valid_request_counter(wpa_auth, sm,
+ key->replay_counter);
+
+ if (wpa_replay_counter_valid(sm->key_replay, key->replay_counter))
+ return true;
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ /*
+ * Some supplicant implementations (e.g., Windows XP
+ * WZC) update SNonce for each EAPOL-Key 2/4. This
+ * breaks the workaround on accepting any of the
+ * pending requests, so allow the SNonce to be updated
+ * even if we have already sent out EAPOL-Key 3/4.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
+ sm->update_snonce = 1;
+ os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+ sm->alt_snonce_valid = true;
+ os_memcpy(sm->alt_replay_counter,
+ sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ return true;
+ }
+
+ if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(key->replay_counter, sm->alt_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) == 0) {
+ /*
+ * Supplicant may still be using the old SNonce since
+ * there was two EAPOL-Key 2/4 messages and they had
+ * different SNonce values.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+ return true;
+ }
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "ignore retransmitted EAPOL-Key %s - SNonce did not change",
+ msgtxt);
+ } else {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "received EAPOL-Key %s with unexpected replay counter",
+ msgtxt);
+ }
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ wpa_hexdump(MSG_DEBUG, "pending replay counter",
+ sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ wpa_hexdump(MSG_DEBUG, "received replay counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ return false;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- u16 key_info, key_data_length;
- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
- char *msgtxt;
- struct wpa_eapol_ie_parse kde;
+ u16 key_info, ver, key_data_length;
+ enum eapol_key_msg msg;
+ const char *msgtxt;
const u8 *key_data;
size_t keyhdrlen, mic_len;
u8 *mic;
- bool is_mld = false;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return;
-#ifdef CONFIG_IEEE80211BE
- is_mld = sm->mld_assoc_link_id >= 0;
-#endif /* CONFIG_IEEE80211BE */
-
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
@@ -1167,11 +1625,31 @@
return;
}
- /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
- * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
- * MLD mechanism here does not work without the somewhat undesired check
- * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be
- * able to know which message this is in MLO cases.. */
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (!wpa_auth_valid_key_desc_ver(wpa_auth, sm, ver))
+ goto out;
+ if (mic_len > 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ sm->PTK_valid &&
+ (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) &&
+ key_data_length >= 8 && key_data_length % 8 == 0) {
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ key_data_buf_len = key_data_length;
+ if (aes_unwrap(sm->PTK.kek, sm->PTK.kek_len,
+ key_data_length / 8, key_data, key_data_buf)) {
+ wpa_printf(MSG_INFO,
+ "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+ goto out;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
@@ -1179,10 +1657,13 @@
msg = GROUP_2;
msgtxt = "2/2 Group";
} else if (key_data_length == 0 ||
+ (sm->wpa == WPA_VERSION_WPA2 &&
+ (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ||
+ key_data_buf) &&
+ (key_info & WPA_KEY_INFO_SECURE) &&
+ !get_ie(key_data, key_data_length, WLAN_EID_RSN)) ||
(mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
- key_data_length == AES_BLOCK_SIZE) ||
- (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
+ key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -1190,127 +1671,15 @@
msgtxt = "2/4 Pairwise";
}
- if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
- msg == GROUP_2) {
- u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
- if (sm->pairwise == WPA_CIPHER_CCMP ||
- sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "advertised support for AES-128-CMAC, but did not use it");
- return;
- }
+ if (!wpa_auth_valid_counter(wpa_auth, sm, key, msg, msgtxt))
+ goto out;
- if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use HMAC-SHA1-AES with CCMP/GCMP");
- return;
- }
- }
-
- if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
- return;
- }
- }
-
- if (key_info & WPA_KEY_INFO_REQUEST) {
- if (sm->req_replay_counter_used &&
- os_memcmp(key->replay_counter, sm->req_replay_counter,
- WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "received EAPOL-Key request with replayed counter");
- return;
- }
- }
-
- if (!(key_info & WPA_KEY_INFO_REQUEST) &&
- !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
- int i;
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
- {
- /*
- * Some supplicant implementations (e.g., Windows XP
- * WZC) update SNonce for each EAPOL-Key 2/4. This
- * breaks the workaround on accepting any of the
- * pending requests, so allow the SNonce to be updated
- * even if we have already sent out EAPOL-Key 3/4.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
- sm->update_snonce = 1;
- os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
- sm->alt_snonce_valid = true;
- os_memcpy(sm->alt_replay_counter,
- sm->key_replay[0].counter,
- WPA_REPLAY_COUNTER_LEN);
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(key->replay_counter, sm->alt_replay_counter,
- WPA_REPLAY_COUNTER_LEN) == 0) {
- /*
- * Supplicant may still be using the old SNonce since
- * there was two EAPOL-Key 2/4 messages and they had
- * different SNonce values.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "ignore retransmitted EAPOL-Key %s - SNonce did not change",
- msgtxt);
- } else {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "received EAPOL-Key %s with unexpected replay counter",
- msgtxt);
- }
- for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
- if (!sm->key_replay[i].valid)
- break;
- wpa_hexdump(MSG_DEBUG, "pending replay counter",
- sm->key_replay[i].counter,
- WPA_REPLAY_COUNTER_LEN);
- }
- wpa_hexdump(MSG_DEBUG, "received replay counter",
- key->replay_counter, WPA_REPLAY_COUNTER_LEN);
- return;
- }
-
-continue_processing:
#ifdef CONFIG_FILS
if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1324,7 +1693,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
if (sm->group->reject_4way_hs_for_entropy) {
@@ -1342,7 +1711,7 @@
random_mark_pool_ready();
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
- return;
+ goto out;
}
break;
case PAIRWISE_4:
@@ -1352,7 +1721,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
break;
case GROUP_2:
@@ -1362,10 +1731,20 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
sm->wpa_ptk_group_state);
- return;
+ goto out;
}
break;
case REQUEST:
+ if (sm->wpa_ptk_state == WPA_PTK_PTKSTART ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING2 ||
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request in invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ goto out;
+ }
break;
}
@@ -1375,14 +1754,14 @@
if (key_info & WPA_KEY_INFO_ACK) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key Ack set");
- return;
+ goto out;
}
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
!(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
- return;
+ goto out;
}
#ifdef CONFIG_FILS
@@ -1390,7 +1769,7 @@
(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC set");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1409,7 +1788,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#ifdef CONFIG_FILS
if (!mic_len &&
@@ -1423,7 +1802,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
#ifdef TEST_FUZZ
@@ -1435,6 +1814,12 @@
}
if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (!(key_info & WPA_KEY_INFO_SECURE)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key request without Secure=1");
+ goto out;
+ }
if (sm->MICVerified) {
sm->req_replay_counter_used = 1;
os_memcpy(sm->req_replay_counter, key->replay_counter,
@@ -1443,38 +1828,33 @@
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key request with invalid MIC");
- return;
+ goto out;
}
- /*
- * TODO: should decrypt key data field if encryption was used;
- * even though MAC address KDE is not normally encrypted,
- * supplicant is allowed to encrypt it.
- */
if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
- return; /* STA entry was removed */
+ goto out; /* STA entry was removed */
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for new 4-Way Handshake");
wpa_request_new_ptk(sm);
- } else if (key_data_length > 0 &&
- wpa_parse_kde_ies(key_data, key_data_length,
- &kde) == 0 &&
- kde.mac_addr) {
} else {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for GTK rekeying");
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth),
+ NULL);
if (wpa_auth_gtk_rekey_in_process(wpa_auth))
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
"skip new GTK rekey - already in process");
else
- wpa_rekey_gtk(wpa_auth, NULL);
+ wpa_rekey_gtk(wpa_get_primary_auth(wpa_auth),
+ NULL);
}
} else {
/* Do not allow the same key replay counter to be reused. */
@@ -1506,7 +1886,7 @@
os_free(sm->last_rx_eapol_key);
sm->last_rx_eapol_key = os_memdup(data, data_len);
if (!sm->last_rx_eapol_key)
- return;
+ goto out;
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1515,6 +1895,9 @@
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
wpa_sm_step(sm);
+
+out:
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -2009,8 +2392,7 @@
* Reauthentication cancels the pending group key
* update for this STA.
*/
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
sm->PtkGroupInit = true;
}
sm->ReAuthenticationRequest = true;
@@ -2086,8 +2468,7 @@
sm->keycount = 0;
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = false;
if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
@@ -2332,10 +2713,14 @@
SM_STATE(WPA_PTK, PTKSTART)
{
- u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+ u8 *buf;
+ size_t buf_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
u8 *pmkid = NULL;
size_t kde_len = 0;
u16 key_info;
+#ifdef CONFIG_TESTING_OPTIONS
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#endif /* CONFIG_TESTING_OPTIONS */
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = false;
@@ -2350,6 +2735,19 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ buf_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements)
+ buf_len += wpabuf_len(conf->eapol_m1_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ buf = os_zalloc(buf_len);
+ if (!buf)
+ return;
+
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
@@ -2448,16 +2846,25 @@
"RSN: MLD: Add MAC Address KDE: kde_len=%zu",
kde_len);
wpa_add_kde(buf + kde_len, RSN_KEY_DATA_MAC_ADDR,
- sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ sm->wpa_auth->mld_addr, ETH_ALEN, NULL, 0);
kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements) {
+ os_memcpy(buf + kde_len, wpabuf_head(conf->eapol_m1_elements),
+ wpabuf_len(conf->eapol_m1_elements));
+ kde_len += wpabuf_len(conf->eapol_m1_elements);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
key_info |= WPA_KEY_INFO_SECURE;
wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
+ os_free(buf);
}
@@ -2465,7 +2872,7 @@
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256,
u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
- size_t *key_len)
+ size_t *key_len, bool no_kdk)
{
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
@@ -2473,7 +2880,7 @@
int ret;
if (sm->wpa_auth->conf.force_kdk_derivation ||
- (sm->wpa_auth->conf.secure_ltf &&
+ (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
@@ -2502,7 +2909,7 @@
}
#ifdef CONFIG_PASN
- if (sm->wpa_auth->conf.secure_ltf &&
+ if (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe,
WLAN_RSNX_CAPAB_SECURE_LTF)) {
ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
@@ -2538,7 +2945,7 @@
}
#ifdef CONFIG_PASN
- if (sm->wpa_auth->conf.secure_ltf &&
+ if (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
if (ret) {
@@ -3178,16 +3585,13 @@
/* MLD MAC address must be the same */
if (!kde->mac_addr ||
- os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde->mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
return -1;
}
/* Find matching link ID and the MAC address for each link */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(kde->valid_mlo_links & BIT(i)))
- continue;
-
+ for_each_link(kde->valid_mlo_links, i) {
/*
* Each entry should contain the link information and the MAC
* address.
@@ -3205,8 +3609,8 @@
return -1;
}
- if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sm->mld_links[i].peer_addr,
+ kde->mlo_link[i] + 1)) {
wpa_printf(MSG_DEBUG,
"RSN: MLD: invalid MAC address=" MACSTR
" expected " MACSTR " (link ID %u)",
@@ -3240,7 +3644,7 @@
size_t pmk_len;
int ft;
const u8 *eapol_key_ie, *key_data, *mic;
- u16 key_data_length;
+ u16 key_info, ver, key_data_length;
size_t mic_len, eapol_key_ie_len;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
@@ -3250,6 +3654,9 @@
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN_MAX];
size_t key_len;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
+ bool derive_kdk, no_kdk = false;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = false;
@@ -3258,6 +3665,9 @@
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ derive_kdk = sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF);
+
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
@@ -3287,9 +3697,11 @@
pmk_len = sm->pmksa->pmk_len;
}
+ no_kdk = false;
+ try_without_kdk:
if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
owe_ptk_workaround == 2, pmk_r0, pmk_r1,
- pmk_r0_name, &key_len) < 0)
+ pmk_r0_name, &key_len, no_kdk) < 0)
break;
if (mic_len &&
@@ -3323,11 +3735,31 @@
}
#endif /* CONFIG_OWE */
+ /* Some deployed STAs that advertise SecureLTF support in the
+ * RSNXE in (Re)Association Request frames, do not derive KDK
+ * during PTK generation. Try to work around this by checking if
+ * a PTK derived without KDK would result in a matching MIC. */
+ if (!sm->wpa_auth->conf.force_kdk_derivation &&
+ derive_kdk && !no_kdk) {
+ wpa_printf(MSG_DEBUG,
+ "Try new PTK derivation without KDK as a workaround");
+ no_kdk = true;
+ goto try_without_kdk;
+ }
+
if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
+ if (no_kdk && ok) {
+ /* The workaround worked, so allow the 4-way handshake to be
+ * completed with the PTK that was derived without the KDK. */
+ wpa_printf(MSG_DEBUG,
+ "PTK without KDK worked - misbehaving STA "
+ MACSTR, MAC2STR(sm->addr));
+ }
+
if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
!sm->waiting_radius_psk) {
@@ -3357,12 +3789,46 @@
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key) - mic_len - 2)
goto out;
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ !wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO,
+ "Unsupported EAPOL-Key Key Data field encryption");
+ goto out;
+ }
+
+ if (key_data_length < 8 || key_data_length % 8) {
+ wpa_printf(MSG_INFO,
+ "RSN: Unsupported AES-WRAP len %u",
+ key_data_length);
+ goto out;
+ }
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ 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;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
@@ -3507,27 +3973,6 @@
return;
}
-#ifdef CONFIG_IEEE80211R_AP
- if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
- /*
- * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
- * with the value we derived.
- */
- if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
- WPA_PMK_NAME_LEN) != 0) {
- wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "PMKR1Name mismatch in FT 4-way handshake");
- wpa_hexdump(MSG_DEBUG,
- "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
- sm->pmk_r1_name, WPA_PMK_NAME_LEN);
- goto out;
- }
- }
-#endif /* CONFIG_IEEE80211R_AP */
-
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
@@ -3562,6 +4007,7 @@
out:
forced_memzero(pmk_r0, sizeof(pmk_r0));
forced_memzero(pmk_r1, sizeof(pmk_r1));
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -3575,14 +4021,18 @@
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
size_t len = 0;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
if (sm->mgmt_frame_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
+
+ if (wpa_auth->conf.tx_bss_auth)
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
return len;
@@ -3595,7 +4045,8 @@
struct wpa_bigtk_kde bigtk;
struct wpa_group *gsm = sm->group;
u8 rsc[WPA_KEY_RSC_LEN];
- struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
@@ -3627,7 +4078,14 @@
NULL, 0);
forced_memzero(&igtk, sizeof(igtk));
- if (!conf->beacon_prot)
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ conf = &wpa_auth->conf;
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ gsm = wpa_auth->group;
+ }
+
+ if (!sm->wpa_auth->conf.beacon_prot)
return pos;
bigtk.keyid[0] = gsm->GN_bigtk;
@@ -3729,27 +4187,6 @@
#ifdef CONFIG_IEEE80211BE
-void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
- struct wpa_auth_ml_link_rsn_info *info)
-{
- info->rsn_ies = a->wpa_ie;
- info->rsn_ies_len = a->wpa_ie_len;
-
- wpa_printf(MSG_DEBUG, "RSN: MLD: link_id=%u, rsn_ies_len=%zu",
- info->link_id, info->rsn_ies_len);
-}
-
-
-static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
- struct wpa_auth_ml_rsn_info *info)
-{
- if (!wpa_auth->cb->get_ml_rsn_info)
- return;
-
- wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, 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)
@@ -3785,6 +4222,11 @@
if (!beacon_prot)
return;
+ if (a->conf.tx_bss_auth) {
+ a = a->conf.tx_bss_auth;
+ gsm = a->group;
+ }
+
info->bigtkidx = gsm->GN_bigtk;
info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3807,32 +4249,47 @@
static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
{
- struct wpa_group *gsm = sm->group;
- size_t gtk_len = gsm->GTK_len;
- size_t igtk_len;
- size_t kde_len;
- unsigned int n_links;
+ struct wpa_authenticator *wpa_auth;
+ size_t kde_len = 0;
+ int link_id;
if (sm->mld_assoc_link_id < 0)
return 0;
- n_links = sm->n_mld_affiliated_links + 1;
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
- /* MLO GTK KDE for each link */
- kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+ wpa_auth = sm->mld_links[link_id].wpa_auth;
+ if (!wpa_auth || !wpa_auth->group)
+ continue;
- if (!sm->mgmt_frame_prot)
- return kde_len;
+ /* MLO GTK KDE
+ * Header + Key ID + Tx + LinkID + PN + GTK */
+ kde_len += KDE_HDR_LEN + 1 + RSN_PN_LEN;
+ kde_len += wpa_auth->group->GTK_len;
- /* MLO IGTK KDE for each link */
- igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ if (!sm->mgmt_frame_prot)
+ continue;
- if (!sm->wpa_auth->conf.beacon_prot)
- return kde_len;
+ if (wpa_auth->conf.tx_bss_auth)
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
- /* MLO BIGTK KDE for each link */
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ /* MLO IGTK KDE
+ * Header + Key ID + IPN + LinkID + IGTK */
+ kde_len += KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1;
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+ if (!wpa_auth->conf.beacon_prot)
+ continue;
+
+ /* MLO BIGTK KDE
+ * Header + Key ID + BIPN + LinkID + BIGTK */
+ kde_len += KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1;
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+ }
+
+ wpa_printf(MSG_DEBUG, "MLO Group KDEs len = %zu", kde_len);
return kde_len;
}
@@ -3842,6 +4299,7 @@
{
struct wpa_auth_ml_key_info ml_key_info;
unsigned int i, link_id;
+ u8 *start = pos;
/* First fetch the key information from all the authenticators */
os_memset(&ml_key_info, 0, sizeof(ml_key_info));
@@ -3865,7 +4323,8 @@
/* Add MLO GTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].gtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
@@ -3892,12 +4351,16 @@
i++;
}
- if (!sm->mgmt_frame_prot)
+ if (!sm->mgmt_frame_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ pos - start);
return pos;
+ }
/* Add MLO IGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
@@ -3931,12 +4394,17 @@
i++;
}
- if (!sm->wpa_auth->conf.beacon_prot)
+ if (!sm->wpa_auth->conf.beacon_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ pos - start);
return pos;
+ }
/* Add MLO BIGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].bigtk ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
@@ -3970,6 +4438,7 @@
i++;
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start);
return pos;
}
@@ -3991,12 +4460,33 @@
/* MLO Link KDE for each link */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ struct wpa_authenticator *wpa_auth;
+ const u8 *ie, *ieo;
+
+ wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ if (!wpa_auth)
continue;
- kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len;
+ kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN;
+ ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSN);
+ ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+ kde_len += 2 + ieo[1 - 4];
+ else
+ kde_len += 2 + ie[1];
+
+ ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSNX);
+ ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+ kde_len += 2 + ieo[1] - 4;
+ else if (ie)
+ kde_len += 2 + ie[1];
}
kde_len += wpa_auth_ml_group_kdes_len(sm);
@@ -4010,56 +4500,95 @@
{
#ifdef CONFIG_IEEE80211BE
u8 link_id;
+ u8 *start = pos;
if (sm->mld_assoc_link_id < 0)
return pos;
wpa_printf(MSG_DEBUG, "RSN: MLD: Adding MAC Address KDE");
pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR,
- sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ sm->wpa_auth->mld_addr, ETH_ALEN, NULL, 0);
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ struct wpa_authenticator *wpa_auth;
+ const u8 *rsne, *rsnxe, *rsneo, *rsnxeo;
+ size_t rsne_len, rsnxe_len;
+
+ wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ if (!wpa_auth)
continue;
+ rsne = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSN);
+ rsne_len = rsne ? 2 + rsne[1] : 0;
+ rsneo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && rsneo)
+ rsne_len = 2 + rsneo[1] - 4;
+ else
+ rsneo = NULL;
+
+ rsnxe = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSNX);
+ rsnxe_len = rsnxe ? 2 + rsnxe[1] : 0;
+ rsnxeo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && rsnxeo)
+ rsnxe_len = 2 + rsnxeo[1] - 4;
+ else
+ rsnxeo = NULL;
+
wpa_printf(MSG_DEBUG,
"RSN: MLO Link: link=%u, len=%zu", link_id,
RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len);
+ rsne_len + rsnxe_len);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len;
+ rsne_len + rsnxe_len;
RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_LINK);
pos += RSN_SELECTOR_LEN;
/* Add the Link Information */
*pos = link_id;
- if (sm->mld_links[link_id].rsne_len)
+ if (rsne_len)
*pos |= RSN_MLO_LINK_KDE_LI_RSNE_INFO;
- if (sm->mld_links[link_id].rsnxe_len)
+ if (rsnxe_len)
*pos |= RSN_MLO_LINK_KDE_LI_RSNXE_INFO;
pos++;
- os_memcpy(pos, sm->mld_links[link_id].own_addr, ETH_ALEN);
+ os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
pos += ETH_ALEN;
- if (sm->mld_links[link_id].rsne_len) {
- os_memcpy(pos, sm->mld_links[link_id].rsne,
- sm->mld_links[link_id].rsne_len);
- pos += sm->mld_links[link_id].rsne_len;
+ if (rsne_len) {
+ if (rsneo) {
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = rsneo[1] - 4;
+ os_memcpy(pos, &rsneo[2 + 4], rsneo[1] - 4);
+ pos += rsneo[1] - 4;
+ } else {
+ os_memcpy(pos, rsne, rsne_len);
+ pos += rsne_len;
+ }
}
- if (sm->mld_links[link_id].rsnxe_len) {
- os_memcpy(pos, sm->mld_links[link_id].rsnxe,
- sm->mld_links[link_id].rsnxe_len);
- pos += sm->mld_links[link_id].rsnxe_len;
+ if (rsnxe_len) {
+ if (rsnxeo) {
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = rsnxeo[1] - 4;
+ os_memcpy(pos, &rsnxeo[2 + 4], rsnxeo[1] - 4);
+ pos += rsnxeo[1] - 4;
+ } else {
+ os_memcpy(pos, rsnxe, rsnxe_len);
+ pos += rsnxe_len;
+ }
}
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Link KDE len = %ld", pos - start);
pos = wpa_auth_ml_group_kdes(sm, pos);
#endif /* CONFIG_IEEE80211BE */
@@ -4074,7 +4603,7 @@
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
@@ -4115,6 +4644,80 @@
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
+ if ((sm->rsn_override &&
+ get_vendor_ie(wpa_ie, wpa_ie_len, RSNE_OVERRIDE_IE_VENDOR_TYPE)) ||
+ (sm->rsn_override_2 &&
+ get_vendor_ie(wpa_ie, wpa_ie_len,
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE))) {
+ const u8 *mde, *fte, *tie, *tie2 = NULL;
+ const u8 *override_rsne = NULL, *override_rsnxe = NULL;
+ const struct element *elem;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: Use RSNE/RSNXE override element contents");
+ mde = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_MOBILITY_DOMAIN);
+ fte = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_FAST_BSS_TRANSITION);
+ tie = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_TIMEOUT_INTERVAL);
+ if (tie) {
+ const u8 *next = tie + 2 + tie[1];
+
+ tie2 = get_ie(next, wpa_ie + wpa_ie_len - next,
+ WLAN_EID_TIMEOUT_INTERVAL);
+ }
+ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC,
+ wpa_ie, wpa_ie_len) {
+ if (elem->datalen >= 4) {
+ if (WPA_GET_BE32(elem->data) ==
+ (sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE))
+ override_rsne = &elem->id;
+ if (WPA_GET_BE32(elem->data) ==
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE)
+ override_rsnxe = &elem->id;
+ }
+ }
+ wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs before edits",
+ wpa_ie, wpa_ie_len);
+ wpa_ie_buf3 = os_malloc(wpa_ie_len);
+ if (!wpa_ie_buf3)
+ goto done;
+ pos = wpa_ie_buf3;
+ if (override_rsne) {
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = override_rsne[1] - 4;
+ os_memcpy(pos, &override_rsne[2 + 4],
+ override_rsne[1] - 4);
+ pos += override_rsne[1] - 4;
+ }
+ if (mde) {
+ os_memcpy(pos, mde, 2 + mde[1]);
+ pos += 2 + mde[1];
+ }
+ if (fte) {
+ os_memcpy(pos, fte, 2 + fte[1]);
+ pos += 2 + fte[1];
+ }
+ if (tie) {
+ os_memcpy(pos, tie, 2 + tie[1]);
+ pos += 2 + tie[1];
+ }
+ if (tie2) {
+ os_memcpy(pos, tie2, 2 + tie2[1]);
+ pos += 2 + tie2[1];
+ }
+ if (override_rsnxe) {
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = override_rsnxe[1] - 4;
+ os_memcpy(pos, &override_rsnxe[2 + 4],
+ override_rsnxe[1] - 4);
+ pos += override_rsnxe[1] - 4;
+ }
+ wpa_ie = wpa_ie_buf3;
+ wpa_ie_len = pos - wpa_ie_buf3;
+ 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(
@@ -4233,6 +4836,14 @@
kde_len += wpa_auth_ml_kdes_len(sm);
+ if (sm->ssid_protection)
+ kde_len += 2 + conf->ssid_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements)
+ kde_len += wpabuf_len(conf->eapol_m3_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
@@ -4248,7 +4859,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
@@ -4347,6 +4958,24 @@
pos = wpa_auth_ml_kdes(sm, pos);
+ if (sm->ssid_protection) {
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = conf->ssid_len;
+ os_memcpy(pos, conf->ssid, conf->ssid_len);
+ pos += conf->ssid_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements) {
+ os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
+ wpabuf_len(conf->eapol_m3_elements));
+ pos += wpabuf_len(conf->eapol_m3_elements);
+ }
+
+ if (conf->eapol_m3_no_encrypt)
+ encr = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
@@ -4358,6 +4987,7 @@
bin_clear_free(kde, kde_len);
os_free(wpa_ie_buf);
os_free(wpa_ie_buf2);
+ os_free(wpa_ie_buf3);
}
@@ -4398,7 +5028,7 @@
/* MLD MAC address must be the same */
if (!kde.mac_addr ||
- os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde.mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
return -1;
@@ -4732,7 +5362,8 @@
return;
kde = pos = kde_buf;
- wpa_auth_ml_group_kdes(sm, pos);
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+ kde_len = pos - kde_buf;
}
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -4825,8 +5456,7 @@
#endif /* CONFIG_OCV */
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
sm->GTimeoutCtr = 0;
/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
@@ -4840,8 +5470,15 @@
{
SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
+ if (sm->wpa_auth->conf.no_disconnect_on_group_keyerror &&
+ sm->wpa == WPA_VERSION_WPA2) {
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "group key handshake failed after %u tries - allow STA to remain connected",
+ sm->wpa_auth->conf.wpa_group_update_count);
+ return;
+ }
sm->Disconnect = true;
sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
@@ -4912,19 +5549,30 @@
group->IGTK[group->GN_igtk - 4], len);
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- conf->beacon_prot) {
- 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);
- if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
- wpa_auth->addr, group->GNonce,
- group->BIGTK[group->GN_bigtk - 6], len) < 0)
- ret = -1;
- wpa_hexdump_key(MSG_DEBUG, "BIGTK",
- group->BIGTK[group->GN_bigtk - 6], len);
+ if (!wpa_auth->non_tx_beacon_prot &&
+ conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ return ret;
+ if (!conf->beacon_prot)
+ return ret;
+
+ if (wpa_auth->conf.tx_bss_auth) {
+ group = wpa_auth->conf.tx_bss_auth->group;
+ if (group->bigtk_set)
+ return ret;
+ wpa_printf(MSG_DEBUG, "Set up BIGTK for TX BSS");
}
+ 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);
+ if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->BIGTK[group->GN_bigtk - 6], len) < 0)
+ return -1;
+ group->bigtk_set = true;
+ wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+ group->BIGTK[group->GN_bigtk - 6], len);
+
return ret;
}
@@ -5085,9 +5733,10 @@
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
u8 *start = pos;
- size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ size_t len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/*
* BIGTK subelement:
@@ -5097,7 +5746,7 @@
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+ if (wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
return 0;
pos += 6;
@@ -5123,17 +5772,11 @@
#endif /* CONFIG_WNM_AP */
-static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
int tmp;
- wpa_printf(MSG_DEBUG,
- "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
- group->vlan_id);
- group->changed = true;
- group->wpa_group_state = WPA_GROUP_SETKEYS;
- group->GTKReKey = false;
tmp = group->GM;
group->GM = group->GN;
group->GN = tmp;
@@ -5147,6 +5790,25 @@
* 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);
+}
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_SETKEYS;
+ group->GTKReKey = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml)
+ goto skip_update;
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_group_update_gtk(wpa_auth, group);
if (group->GKeyDoneStations) {
wpa_printf(MSG_DEBUG,
@@ -5154,6 +5816,10 @@
group->GKeyDoneStations);
group->GKeyDoneStations = 0;
}
+
+#ifdef CONFIG_IEEE80211BE
+skip_update:
+#endif /* CONFIG_IEEE80211BE */
wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
@@ -5187,12 +5853,21 @@
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
- if (ret == 0 && conf->beacon_prot &&
- wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ if (ret || !conf->beacon_prot)
+ 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;
}
return ret;
@@ -5263,6 +5938,61 @@
}
+static void wpa_clear_changed(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ sm->changed = false;
+ sm->wpa_auth->group->changed = false;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->changed = false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm || !sm->wpa_auth)
+ return;
+ wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->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);
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+ bool changed;
+
+ if (!sm || !sm->wpa_auth)
+ return false;
+ changed = sm->wpa_auth->group->changed;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ changed |= sm->mld_links[link_id].wpa_auth->group->changed;
+#endif /* CONFIG_IEEE80211BE */
+
+ return changed;
+}
+
+
static int wpa_sm_step(struct wpa_state_machine *sm)
{
if (!sm)
@@ -5281,8 +6011,7 @@
if (sm->pending_deinit)
break;
- sm->changed = false;
- sm->wpa_auth->group->changed = false;
+ wpa_clear_changed(sm);
SM_STEP_RUN(WPA_PTK);
if (sm->pending_deinit)
@@ -5290,8 +6019,8 @@
SM_STEP_RUN(WPA_PTK_GROUP);
if (sm->pending_deinit)
break;
- wpa_group_sm_step(sm->wpa_auth, sm->group);
- } while (sm->changed || sm->wpa_auth->group->changed);
+ wpa_group_sm_step_links(sm);
+ } while (sm->changed || wpa_group_sm_changed(sm));
sm->in_step_loop = 0;
if (sm->pending_deinit) {
@@ -5337,9 +6066,11 @@
tmp = group->GM_igtk;
group->GM_igtk = group->GN_igtk;
group->GN_igtk = tmp;
- tmp = group->GM_bigtk;
- group->GM_bigtk = group->GN_bigtk;
- group->GN_bigtk = tmp;
+ if (!wpa_auth->conf.tx_bss_auth) {
+ tmp = group->GM_bigtk;
+ group->GM_bigtk = group->GN_bigtk;
+ group->GN_bigtk = tmp;
+ }
wpa_gtk_update(wpa_auth, group);
wpa_group_config_group_keys(wpa_auth, group);
}
@@ -5689,28 +6420,11 @@
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)
-{
- if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
- NULL, 0, wpa_auth->addr, addr, session_timeout,
- NULL, akmp))
- return 0;
-
- return -1;
-}
-
-
-int wpa_auth_pmksa_add3(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)
{
struct rsn_pmksa_cache_entry *entry;
- if (wpa_auth->conf.disable_pmksa_caching)
+ if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
@@ -5834,13 +6548,14 @@
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)
+ u8 *pmkid, u8 *pmk, size_t *pmk_len)
{
if (!sm)
return;
sm->pmksa = pmksa;
- os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+ os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len);
+ *pmk_len = pmksa->pmk_len;
os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
}
@@ -6224,6 +6939,20 @@
}
+void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->rsn_override = val;
+}
+
+
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->rsn_override_2 = val;
+}
+
+
#ifdef CONFIG_DPP2
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
{
@@ -6235,6 +6964,13 @@
#endif /* CONFIG_DPP2 */
+void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->ssid_protection = val;
+}
+
+
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
u8 val)
{
@@ -6358,7 +7094,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
@@ -6519,8 +7255,10 @@
{
if (!wpa_auth)
return -1;
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
- return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth), NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth), NULL);
}
@@ -6608,79 +7346,64 @@
}
-void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
u8 mld_assoc_link_id, struct mld_info *info)
{
#ifdef CONFIG_IEEE80211BE
- struct wpa_auth_ml_rsn_info ml_rsn_info;
- unsigned int link_id, i;
+ unsigned int link_id;
if (!info)
return;
os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+ sm->n_mld_affiliated_links = 0;
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"MLD: Initialization");
- os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
sm->mld_assoc_link_id = mld_assoc_link_id;
- os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
-
- for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct mld_link_info *link = &info->links[link_id];
struct mld_link *sm_link = &sm->mld_links[link_id];
+ struct wpa_get_link_auth_ctx ctx;
sm_link->valid = link->valid;
if (!link->valid)
continue;
os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
- os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+ "WPA_AUTH: MLD: id=%u, peer=" MACSTR,
link_id,
- MAC2STR(sm_link->own_addr),
MAC2STR(sm_link->peer_addr));
- if (link_id != mld_assoc_link_id)
+ if (link_id != mld_assoc_link_id) {
sm->n_mld_affiliated_links++;
-
- ml_rsn_info.links[i++].link_id = link_id;
- }
-
- ml_rsn_info.n_mld_links = i;
-
- wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
-
- for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
- struct mld_link *sm_link;
- const u8 *rsn_ies;
- u8 rsn_ies_len;
-
- sm_link = &sm->mld_links[ml_rsn_info.links[i].link_id];
- rsn_ies = ml_rsn_info.links[i].rsn_ies;
- rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
-
- /* This should not really happen */
- if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN ||
- rsn_ies[1] + 2 > rsn_ies_len) {
- wpa_printf(MSG_INFO, "WPA_AUTH: MLD: Invalid RSNE");
- continue;
+ ctx.addr = link->local_addr;
+ ctx.mld_addr = NULL;
+ ctx.link_id = -1;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(sm->wpa_auth,
+ wpa_get_link_sta_auth, &ctx);
+ 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;
}
- sm_link->rsne = rsn_ies;
- sm_link->rsne_len = rsn_ies[1] + 2;
-
- if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
- rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
- sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
- sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
- }
+ if (!sm_link->wpa_auth)
+ wpa_printf(MSG_ERROR,
+ "Unable to find authenticator object for ML STA "
+ MACSTR " on link id %d",
+ MAC2STR(sm->wpa_auth->mld_addr),
+ link_id);
}
#endif /* CONFIG_IEEE80211BE */
}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 57fda8a..86cb4e8 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -173,6 +173,8 @@
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 @@
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,15 +196,17 @@
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;
#ifdef CONFIG_OCV
int ocv; /* Operating Channel Validation */
#endif /* CONFIG_OCV */
-#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
+#ifdef CONFIG_IEEE80211R_AP
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
size_t r0_key_holder_len;
@@ -244,6 +250,9 @@
unsigned int skip_send_eapol:1;
unsigned int enable_eapol_large_timeout:1;
bool delay_eapol_tx;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -279,6 +288,21 @@
bool force_kdk_derivation;
bool radius_psk;
+
+ bool no_disconnect_on_group_keyerror;
+
+ /* Pointer to Multi-BSSID transmitted BSS authenticator instance.
+ * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
+ * and in BSSs that are not part of a Multi-BSSID set. */
+ struct wpa_authenticator *tx_bss_auth;
+
+#ifdef CONFIG_IEEE80211BE
+ const u8 *mld_addr;
+ int link_id;
+ struct wpa_authenticator *first_link_auth;
+#endif /* CONFIG_IEEE80211BE */
+
+ bool ssid_protection;
};
typedef enum {
@@ -291,16 +315,6 @@
WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
} wpa_eapol_variable;
-struct wpa_auth_ml_rsn_info {
- unsigned int n_mld_links;
-
- struct wpa_auth_ml_link_rsn_info {
- unsigned int link_id;
- const u8 *rsn_ies;
- size_t rsn_ies_len;
- } links[MAX_NUM_MLD_LINKS];
-};
-
struct wpa_auth_ml_key_info {
unsigned int n_mld_links;
bool mgmt_frame_prot;
@@ -394,7 +408,6 @@
size_t ltf_keyseed_len);
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
- int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
#endif /* CONFIG_IEEE80211BE */
int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
@@ -423,7 +436,8 @@
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len);
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
@@ -479,9 +493,6 @@
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);
-int wpa_auth_pmksa_add3(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);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
@@ -507,7 +518,7 @@
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);
+ u8 *pmkid, u8 *pmk, size_t *pmk_len);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
@@ -517,9 +528,9 @@
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len,
int omit_rsnxe);
-void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+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, const u8 *bssid,
+ void (*cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len),
void *ctx);
@@ -602,7 +613,10 @@
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_rsn_override(struct wpa_state_machine *sm, bool val);
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val);
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);
@@ -640,12 +654,19 @@
void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
-void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+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_rsn_info(struct wpa_authenticator *a,
- struct wpa_auth_ml_link_rsn_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);
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ int release_link_id);
+
+#define for_each_sm_auth(sm, link_id) \
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
+ if (sm->mld_links[link_id].valid && \
+ sm->mld_links[link_id].wpa_auth && \
+ sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 4b16f62..de16c31 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1427,7 +1427,7 @@
os_get_reltime(&now);
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r0->spa, spa) &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
*r0_out = r0;
@@ -1522,7 +1522,7 @@
os_get_reltime(&now);
dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
- if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r1->spa, spa) &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
@@ -2024,7 +2024,7 @@
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
- if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0kh->addr, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG,
"FT: R0KH-ID points to self - no matching key available");
return -1;
@@ -2366,7 +2366,8 @@
static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
size_t subelem_len;
const u8 *kek, *bigtk;
size_t kek_len;
@@ -2381,7 +2382,7 @@
kek_len = sm->PTK.kek_len;
}
- bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ bigtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
* Key[16+8] */
@@ -2395,7 +2396,7 @@
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
+ wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3441,9 +3442,9 @@
}
-void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+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, const u8 *bssid,
+ void (*cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len),
void *ctx)
@@ -3461,7 +3462,8 @@
wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
" BSSID=" MACSTR " transaction=%d",
- MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+ MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr),
+ auth_transaction);
sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction;
@@ -3479,8 +3481,7 @@
MAC2STR(sm->addr), auth_transaction + 1, status,
status2str(status));
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
- cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
- resp_ies, resp_ies_len);
+ cb(ctx, sm->addr, auth_transaction + 1, status, resp_ies, resp_ies_len);
os_free(resp_ies);
}
@@ -3765,7 +3766,7 @@
" Target AP=" MACSTR " Action=%d)",
MAC2STR(sta_addr), MAC2STR(target_ap), action);
- if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta_addr, sm->addr)) {
wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
"STA=" MACSTR " STA-Address=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sta_addr));
@@ -3778,7 +3779,7 @@
* APs in the MD (if such a list were configured).
*/
if ((target_ap[0] & 0x01) ||
- os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(target_ap, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
"frame");
return -1;
@@ -3809,7 +3810,7 @@
}
-static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst,
u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len)
{
@@ -4036,7 +4037,7 @@
seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype, no_defer);
if (!no_defer && r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4203,7 +4204,7 @@
cb ? 0 : 1);
}
if (cb && r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4338,7 +4339,7 @@
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status);
- sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+ sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr,
sm->ft_pending_auth_transaction + 1, status,
resp_ies, resp_ies_len);
os_free(resp_ies);
@@ -4357,7 +4358,7 @@
struct ft_get_sta_ctx *info = ctx;
if ((info->s1kh_id &&
- os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ !ether_addr_equal(info->s1kh_id, sm->addr)) ||
os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
FT_RRB_NONCE_LEN) != 0 ||
sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
@@ -4482,7 +4483,7 @@
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh, &r0kh_wildcard);
if (!r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
goto out;
@@ -4500,7 +4501,7 @@
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
&r1kh_wildcard);
if (!r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
f_r1kh_id, FT_R1KH_ID_LEN);
goto out;
@@ -4806,7 +4807,7 @@
return -1;
}
- if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(target_ap_addr, wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
"RRB Request does not match with own "
"address");
@@ -4969,7 +4970,7 @@
return;
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0->spa, addr)) {
r0found = r0;
break;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 82d79f2..e88644f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -45,6 +45,8 @@
wconf->wpa = conf->wpa;
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 +58,8 @@
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,14 +74,17 @@
#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;
-#ifdef CONFIG_IEEE80211R_AP
+ wconf->ssid_protection = conf->ssid_protection;
wconf->ssid_len = conf->ssid.ssid_len;
if (wconf->ssid_len > SSID_MAX_LEN)
wconf->ssid_len = SSID_MAX_LEN;
os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+#ifdef CONFIG_IEEE80211R_AP
os_memcpy(wconf->mobility_domain, conf->mobility_domain,
MOBILITY_DOMAIN_ID_LEN);
if (conf->nas_identifier &&
@@ -183,8 +190,15 @@
wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
wconf->oci_freq_override_fils_assoc =
conf->oci_freq_override_fils_assoc;
+
wconf->skip_send_eapol = iconf->skip_send_eapol;
wconf->enable_eapol_large_timeout = iconf->enable_eapol_large_timeout;
+
+ if (conf->eapol_m1_elements)
+ wconf->eapol_m1_elements = wpabuf_dup(conf->eapol_m1_elements);
+ 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;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -220,6 +234,9 @@
#endif /* CONFIG_PASN */
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;
}
@@ -556,7 +573,8 @@
if (sta) {
flags = hostapd_sta_flags_to_drv(sta->flags);
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+ if (ap_sta_is_mld(hapd, sta) &&
+ (sta->flags & WLAN_STA_AUTHORIZED))
link_id = -1;
#endif /* CONFIG_IEEE80211BE */
}
@@ -669,7 +687,7 @@
hapd = iface->bss[j];
if (hapd == idata->src_hapd ||
!hapd->wpa_auth ||
- os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst))
continue;
wpa_printf(MSG_DEBUG,
@@ -859,7 +877,7 @@
MOBILITY_DOMAIN_ID_LEN) != 0)
continue; /* no matching FT SSID/mobility domain */
if (!is_multicast_ether_addr(idata->dst_addr) &&
- os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst_addr))
continue; /* destination address does not match */
/* defer eth_p_oui_deliver until next eloop step as this is
@@ -1157,17 +1175,25 @@
if (!sta || !sta->wpa_sm)
return -1;
- if (vlan->notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from FT",
- vlan->untagged, vlan->tagged[0] ? "+" : "");
- return -1;
- }
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ?
+ "+" : "");
+ return -1;
+ }
- if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
- return -1;
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+
+ } else {
+ if (vlan->notempty)
+ sta->vlan_id = vlan->untagged;
+ }
/* Configure wpa_group for GTK but ignore error due to driver not
* knowing this STA. */
ap_sta_bind_vlan(hapd, sta);
@@ -1190,10 +1216,15 @@
if (!sta)
return -1;
- if (sta->vlan_desc)
+ if (sta->vlan_desc) {
*vlan = *sta->vlan_desc;
- else
+ } else if ((hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+ sta->vlan_id) {
+ vlan->notempty = 1;
+ vlan->untagged = sta->vlan_id;
+ } else {
os_memset(vlan, 0, sizeof(*vlan));
+ }
return 0;
}
@@ -1396,7 +1427,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
if (!is_multicast_ether_addr(ethhdr->h_dest) &&
- os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, ethhdr->h_dest))
return;
wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
len - sizeof(*ethhdr));
@@ -1412,7 +1443,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
if (!is_multicast_ether_addr(dst_addr) &&
- os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, dst_addr))
return;
wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
len);
@@ -1515,53 +1546,11 @@
#ifdef CONFIG_IEEE80211BE
-static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- struct wpa_auth_ml_rsn_info *info)
-{
- struct hostapd_data *hapd = ctx;
- unsigned int i, j;
-
- wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
- info->n_mld_links);
-
- if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
- return -1;
-
- for (i = 0; i < info->n_mld_links; i++) {
- unsigned int link_id = info->links[i].link_id;
-
- wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
- link_id);
-
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- struct hostapd_iface *iface =
- hapd->iface->interfaces->iface[j];
-
- if (!iface->bss[0]->conf->mld_ap ||
- hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
- continue;
-
- wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
- &info->links[i]);
- break;
- }
-
- if (j == hapd->iface->interfaces->count)
- wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: link=%u not found", link_id);
- }
-
- return 0;
-}
-
-
static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
struct wpa_auth_ml_key_info *info)
{
struct hostapd_data *hapd = ctx;
- unsigned int i, j;
+ unsigned int i;
wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
info->n_mld_links);
@@ -1570,29 +1559,35 @@
return -1;
for (i = 0; i < info->n_mld_links; i++) {
+ struct hostapd_data *bss;
u8 link_id = info->links[i].link_id;
+ bool link_bss_found = false;
wpa_printf(MSG_DEBUG,
"WPA_AUTH: MLD: Get link info CB: link_id=%u",
link_id);
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- struct hostapd_iface *iface =
- hapd->iface->interfaces->iface[j];
-
- if (!iface->bss[0]->conf->mld_ap ||
- hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
- continue;
-
- wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
+ if (hapd->mld_link_id == link_id) {
+ wpa_auth_ml_get_key_info(hapd->wpa_auth,
&info->links[i],
info->mgmt_frame_prot,
info->beacon_prot);
+ continue;
+ }
+
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd || bss->mld_link_id != link_id)
+ continue;
+
+ wpa_auth_ml_get_key_info(bss->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
+ link_bss_found = true;
break;
}
- if (j == hapd->iface->interfaces->count)
+ if (!link_bss_found)
wpa_printf(MSG_DEBUG,
"WPA_AUTH: MLD: link=%u not found", link_id);
}
@@ -1668,16 +1663,19 @@
.set_ltf_keyseed = hostapd_set_ltf_keyseed,
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
- .get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
#endif /* CONFIG_IEEE80211BE */
.get_drv_flags = hostapd_wpa_auth_get_drv_flags,
};
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)
@@ -1715,6 +1713,27 @@
!!(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 */
+
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 74ae5ad..29988c2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -124,6 +124,9 @@
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 */
@@ -134,11 +137,9 @@
* Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
size_t r0kh_id_len;
- u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
- * message 2/4 */
u8 *assoc_resp_ftie;
- void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ void (*ft_pending_cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len);
void *ft_pending_cb_ctx;
@@ -174,7 +175,6 @@
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_IEEE80211BE
- u8 own_mld_addr[ETH_ALEN];
u8 peer_mld_addr[ETH_ALEN];
s8 mld_assoc_link_id;
u8 n_mld_affiliated_links;
@@ -182,14 +182,12 @@
struct mld_link {
bool valid;
u8 peer_addr[ETH_ALEN];
- u8 own_addr[ETH_ALEN];
- const u8 *rsne;
- size_t rsne_len;
- const u8 *rsnxe;
- size_t rsnxe_len;
+ struct wpa_authenticator *wpa_auth;
} mld_links[MAX_NUM_MLD_LINKS];
#endif /* CONFIG_IEEE80211BE */
+
+ bool ssid_protection;
};
@@ -222,6 +220,8 @@
u8 BIGTK[2][WPA_IGTK_MAX_LEN];
int GN_igtk, GM_igtk;
int GN_bigtk, GM_bigtk;
+ bool bigtk_set;
+ bool bigtk_configured;
/* Number of references except those in struct wpa_group->next */
unsigned int references;
unsigned int num_setup_iface;
@@ -251,15 +251,27 @@
u8 *wpa_ie;
size_t wpa_ie_len;
+ u8 *rsne_override; /* RSNE with overridden payload */
+ u8 *rsne_override_2; /* RSNE with overridden (2) payload */
+ u8 *rsnxe_override; /* RSNXE with overridden payload */
u8 addr[ETH_ALEN];
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+ bool non_tx_beacon_prot;
+
#ifdef CONFIG_P2P
struct bitfield *ip_pool;
#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211BE
+ bool is_ml;
+ u8 mld_addr[ETH_ALEN];
+ u8 link_id;
+ bool primary_auth;
+#endif /* CONFIG_IEEE80211BE */
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index a5f2861..f4f9cc8 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -89,7 +89,8 @@
}
-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 @@
/* 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 @@
}
-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 @@
}
#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 @@
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 @@
}
#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,28 +283,28 @@
#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) {
+ if (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++;
@@ -325,18 +321,18 @@
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,18 +340,19 @@
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) {
+ switch (group_mgmt_cipher) {
case WPA_CIPHER_AES_128_CMAC:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
break;
@@ -371,8 +368,8 @@
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 +381,12 @@
* 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 +396,27 @@
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 +424,74 @@
}
-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;
- u16 capab = 0;
- 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;
+}
+
+
+static u32 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt)
+{
+ u32 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)
@@ -429,6 +505,53 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (conf->prot_range_neg)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (conf->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+
+ return capab;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u32 capab = 0, tmp;
+ size_t flen;
+
+ capab = rsnxe_capab(conf, conf->wpa_key_mgmt);
+
+ if (!capab)
+ return 0; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
+ if (len < 2 + flen)
+ return -1;
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = flen;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
+{
+ u8 *pos = buf;
+ u16 capab;
+ size_t flen;
+
+ capab = rsnxe_capab(conf, conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
@@ -437,8 +560,11 @@
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
- *pos++ = WLAN_EID_RSNX;
- *pos++ = flen;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4 + flen;
+ WPA_PUT_BE32(pos, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
+
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab)
@@ -501,7 +627,7 @@
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
- u8 *pos, buf[128];
+ u8 *pos, buf[256];
int res;
#ifdef CONFIG_TESTING_OPTIONS
@@ -554,6 +680,31 @@
return res;
pos += res;
}
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt) {
+ res = wpa_write_rsne_override(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt_2) {
+ res = wpa_write_rsne_override_2(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ (wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2)) {
+ res = wpa_write_rsnxe_override(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
os_free(wpa_auth->wpa_ie);
wpa_auth->wpa_ie = os_malloc(pos - buf);
@@ -562,6 +713,59 @@
os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
wpa_auth->wpa_ie_len = pos - buf;
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt) {
+ res = wpa_write_rsne_override(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsne_override);
+ wpa_auth->rsne_override = os_malloc(res - 4);
+ if (!wpa_auth->rsne_override)
+ return -1;
+ pos = wpa_auth->rsne_override;
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt_2) {
+ res = wpa_write_rsne_override_2(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsne_override_2);
+ wpa_auth->rsne_override_2 = os_malloc(res - 4);
+ if (!wpa_auth->rsne_override_2)
+ return -1;
+ pos = wpa_auth->rsne_override_2;
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ (wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2)) {
+ res = wpa_write_rsnxe_override(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsnxe_override);
+ if (res == 0) {
+ wpa_auth->rsnxe_override = NULL;
+ return 0;
+ }
+ wpa_auth->rsnxe_override = os_malloc(res - 4);
+ if (!wpa_auth->rsnxe_override)
+ return -1;
+ pos = wpa_auth->rsnxe_override;
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
return 0;
}
@@ -608,7 +812,8 @@
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len)
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data;
@@ -765,7 +970,9 @@
return WPA_INVALID_GROUP;
}
- key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+ key_mgmt = data.key_mgmt & (wpa_auth->conf.wpa_key_mgmt |
+ wpa_auth->conf.rsn_override_key_mgmt |
+ wpa_auth->conf.rsn_override_key_mgmt_2);
if (!key_mgmt) {
wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
MACSTR, data.key_mgmt, MAC2STR(sm->addr));
@@ -835,7 +1042,10 @@
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
if (version == WPA_PROTO_RSN)
- ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+ ciphers = data.pairwise_cipher &
+ (wpa_auth->conf.rsn_pairwise |
+ wpa_auth->conf.rsn_override_pairwise |
+ wpa_auth->conf.rsn_override_pairwise_2);
else
ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
if (!ciphers) {
@@ -956,6 +1166,15 @@
else
sm->wpa = WPA_VERSION_WPA;
+ if (assoc_sm) {
+ /* For ML association link STA cannot choose a different
+ * AKM or pairwise cipher from association STA */
+ if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
+ return WPA_INVALID_AKMP;
+ if (sm->pairwise != assoc_sm->pairwise)
+ return WPA_INVALID_PAIRWISE;
+ }
+
#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
@@ -1212,7 +1431,7 @@
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/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index aacfa33..82d4d5f 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -288,7 +288,7 @@
any_psk = wpa_psk->psk;
if (mac_addr && !dev_psk &&
- os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mac_addr, wpa_psk->addr)) {
dev_psk = wpa_psk->psk;
break;
}
diff --git a/src/build.rules b/src/build.rules
index acda884..c756ccb 100644
--- a/src/build.rules
+++ b/src/build.rules
@@ -80,7 +80,7 @@
_DIRS := $(BUILDDIR)/$(PROJ)
.PHONY: _make_dirs
_make_dirs:
- @mkdir -p $(_DIRS)
+ @mkdir -p $(sort $(_DIRS))
$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index d226a8a..2fad3e1 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -563,7 +563,7 @@
struct dpp_connection *conn;
dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(src, conn->mac_addr))
return conn;
if ((type == DPP_PA_PKEX_EXCHANGE_RESP ||
type == DPP_PA_AUTHENTICATION_RESP) &&
@@ -661,7 +661,7 @@
struct dpp_connection *conn;
dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(src, conn->mac_addr))
return conn;
}
@@ -2601,7 +2601,8 @@
}
-static void dpp_tcp_send_conn_status_msg(struct dpp_connection *conn,
+static void dpp_tcp_send_conn_status_msg(struct dpp_global *dpp,
+ struct dpp_connection *conn,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list)
@@ -2609,6 +2610,7 @@
struct dpp_authentication *auth = conn->auth;
int res;
struct wpabuf *msg;
+ struct dpp_connection *c;
auth->conn_status_requested = 0;
@@ -2627,8 +2629,16 @@
return;
}
- /* This exchange will be terminated in the TX status handler */
- conn->on_tcp_tx_complete_remove = 1;
+ /* conn might have been removed during the dpp_tcp_send_msg() call, so
+ * need to check that it is still present before modifying it. */
+ dl_list_for_each(c, &dpp->tcp_init, struct dpp_connection, list) {
+ if (conn == c) {
+ /* This exchange will be terminated in the TX status
+ * handler */
+ conn->on_tcp_tx_complete_remove = 1;
+ break;
+ }
+ }
}
@@ -2641,7 +2651,7 @@
dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
if (conn->auth && conn->auth->conn_status_requested) {
- dpp_tcp_send_conn_status_msg(conn, result, ssid,
+ dpp_tcp_send_conn_status_msg(dpp, conn, result, ssid,
ssid_len, channel_list);
break;
}
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
index 745a13f..1075500 100644
--- a/src/common/gas_server.c
+++ b/src/common/gas_server.c
@@ -352,7 +352,7 @@
dl_list_for_each(response, &gas->responses, struct gas_server_response,
list) {
if (response->dialog_token != dialog_token ||
- os_memcmp(sa, response->dst, ETH_ALEN) != 0)
+ !ether_addr_equal(sa, response->dst))
continue;
gas_server_handle_rx_comeback_req(response);
return 0;
@@ -470,7 +470,7 @@
dl_list_for_each(response, &gas->responses, struct gas_server_response,
list) {
if (response->dialog_token != dialog_token ||
- os_memcmp(dst, response->dst, ETH_ALEN) != 0)
+ !ether_addr_equal(dst, response->dst))
continue;
gas_server_handle_tx_status(response, ack);
return;
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 57b5a8e..2c47bf8 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -381,6 +381,75 @@
}
+static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
+{
+ u8 first_chan = *seg0 - 6, sec_chan;
+
+ switch (bitmap) {
+ case 0x6:
+ *seg0 = 0;
+ return;
+ case 0x8:
+ case 0x4:
+ case 0x2:
+ case 0x1:
+ case 0xC:
+ case 0x3:
+ if (pri_chan < *seg0)
+ *seg0 -= 4;
+ else
+ *seg0 += 4;
+ break;
+ }
+
+ if (pri_chan < *seg0)
+ sec_chan = pri_chan + 4;
+ else
+ sec_chan = pri_chan - 4;
+
+ if (bitmap & BIT((sec_chan - first_chan) / 4))
+ *seg0 = 0;
+}
+
+
+static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
+ enum oper_chan_width *width, u8 *seg0)
+{
+ if (pri < *seg0) {
+ *seg0 -= 8;
+ if (bitmap & 0x0F) {
+ *width = 0;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+ } else {
+ *seg0 += 8;
+ if (bitmap & 0xF0) {
+ *width = 0;
+ punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
+ seg0);
+ }
+ }
+}
+
+
+void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
+ u8 *seg0, u8 *seg1)
+{
+ if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
+ *width = CONF_OPER_CHWIDTH_USE_HT;
+ punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+ }
+
+ if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
+ *width = CONF_OPER_CHWIDTH_80MHZ;
+ *seg1 = 0;
+ punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
+ }
+
+ /* TODO: 320 MHz */
+}
+
+
int hostapd_set_freq_params(struct hostapd_freq_params *data,
enum hostapd_hw_mode mode,
int freq, int channel, int enable_edmg,
@@ -391,8 +460,12 @@
int center_segment0,
int center_segment1, u32 vht_caps,
struct he_capabilities *he_cap,
- struct eht_capabilities *eht_cap)
+ struct eht_capabilities *eht_cap,
+ u16 punct_bitmap)
{
+ enum oper_chan_width oper_chwidth_legacy;
+ u8 seg0_legacy, seg1_legacy;
+
if (!he_cap || !he_cap->he_supported)
he_enabled = 0;
if (!eht_cap || !eht_cap->eht_supported)
@@ -448,6 +521,7 @@
} else {
int freq1, freq2 = 0;
int bw = center_idx_to_bw_6ghz(center_segment0);
+ int opclass;
if (bw < 0) {
wpa_printf(MSG_ERROR,
@@ -455,7 +529,10 @@
return -1;
}
- freq1 = ieee80211_chan_to_freq(NULL, 131,
+ /* The 6 GHz channel 2 uses a different operating class
+ */
+ opclass = center_segment0 == 2 ? 136 : 131;
+ freq1 = ieee80211_chan_to_freq(NULL, opclass,
center_segment0);
if (freq1 < 0) {
wpa_printf(MSG_ERROR,
@@ -574,6 +651,14 @@
break;
}
+ oper_chwidth_legacy = oper_chwidth;
+ seg0_legacy = center_segment0;
+ seg1_legacy = center_segment1;
+ if (punct_bitmap)
+ punct_update_legacy_bw(punct_bitmap, channel,
+ &oper_chwidth_legacy,
+ &seg0_legacy, &seg1_legacy);
+
if (data->eht_enabled || data->he_enabled ||
data->vht_enabled) switch (oper_chwidth) {
case CONF_OPER_CHWIDTH_USE_HT:
@@ -598,7 +683,8 @@
/* fall through */
case CONF_OPER_CHWIDTH_80MHZ:
data->bandwidth = 80;
- if (!sec_channel_offset) {
+ if (!sec_channel_offset &&
+ oper_chwidth_legacy != CONF_OPER_CHWIDTH_USE_HT) {
wpa_printf(MSG_ERROR,
"80/80+80 MHz: no second channel offset");
return -1;
@@ -656,7 +742,8 @@
"160 MHz: center segment 1 should not be set");
return -1;
}
- if (!sec_channel_offset) {
+ if (!sec_channel_offset &&
+ oper_chwidth_legacy != CONF_OPER_CHWIDTH_USE_HT) {
wpa_printf(MSG_ERROR,
"160 MHz: second channel offset not set");
return -1;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 82e0282..e791c33 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -35,6 +35,8 @@
int check_40mhz_2g4(struct hostapd_hw_modes *mode,
struct wpa_scan_results *scan_res, int pri_chan,
int sec_chan);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+ enum oper_chan_width *width, u8 *seg0, u8 *seg1);
int hostapd_set_freq_params(struct hostapd_freq_params *data,
enum hostapd_hw_mode mode,
int freq, int channel, int edmg, u8 edmg_channel,
@@ -45,7 +47,8 @@
int center_segment0,
int center_segment1, u32 vht_caps,
struct he_capabilities *he_caps,
- struct eht_capabilities *eht_cap);
+ struct eht_capabilities *eht_cap,
+ u16 punct_bitmap);
void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
int disabled);
int ieee80211ac_cap_check(u32 hw, u32 conf);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index dbe7b29..10f9c4a 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -140,6 +140,14 @@
elems->sae_pk = pos + 4;
elems->sae_pk_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;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -615,6 +623,12 @@
elems->rrm_enabled = pos;
elems->rrm_enabled_len = elen;
break;
+ case WLAN_EID_MULTIPLE_BSSID:
+ if (elen < 1)
+ break;
+ elems->mbssid = pos;
+ elems->mbssid_len = elen;
+ break;
case WLAN_EID_CAG_NUMBER:
elems->cag_number = pos;
elems->cag_number_len = elen;
@@ -1012,7 +1026,7 @@
continue;
}
- if (sub_elem_len < 3) {
+ if (sub_elem_len < 5) {
if (show_errors)
wpa_printf(MSG_DEBUG,
"MLD: error: sub_elem_len=%zu < 5",
@@ -1081,7 +1095,8 @@
non_inherit_len -= 1 + non_inherit[0];
non_inherit += 1 + non_inherit[0];
- if (non_inherit_len < 1UL + non_inherit[0]) {
+ if (non_inherit_len < 1UL ||
+ non_inherit_len < 1UL + non_inherit[0]) {
if (show_errors)
wpa_printf(MSG_DEBUG,
"MLD: Invalid inheritance");
@@ -1496,8 +1511,6 @@
*op_class = 126;
else if (sec_channel == -1)
*op_class = 127;
- else if (freq <= 5805)
- *op_class = 124;
else
*op_class = 125;
@@ -2049,6 +2062,13 @@
}
+bool is_80plus_op_class(u8 op_class)
+{
+ /* Operating classes with "80+" behavior indication in Table E-4 */
+ return op_class == 130 || op_class == 135;
+}
+
+
static int is_11b(u8 rate)
{
return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16
@@ -2416,9 +2436,17 @@
* channel center frequency index value, but it happens to be a 20 MHz
* channel and the channel number in the channel set would match the
* value in for the frequency center.
+ *
+ * Operating class value pair 128 and 130 is used to describe a 80+80
+ * MHz channel on the 5 GHz band. 130 is identified with "80+", so this
+ * is encoded with two octets 130 and 128. Similarly, operating class
+ * value pair 133 and 135 is used to describe a 80+80 MHz channel on
+ * the 6 GHz band (135 being the one with "80+" indication). All other
+ * operating classes listed here are used as 1-octet values.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, P2P_SUPP },
@@ -2426,6 +2454,9 @@
{ 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) */
+ { HOSTAPD_MODE_IEEE80211A, 137, 31, 191, 32, BW320, NO_P2P_SUPP },
+
/*
* IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
* Class 180 has the legacy channels 1-6. Classes 181-183 include
@@ -2436,11 +2467,6 @@
{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
- /* Keep the operating class 130 as the last entry as a workaround for
- * the OneHundredAndThirty Delimiter value used in the Supported
- * Operating Classes element to indicate the end of the Operating
- * Classes field. */
- { HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
};
@@ -2507,35 +2533,6 @@
/**
- * get_ie_nth - Fetch a specified information element from IEs buffer
- * @ies: Information elements buffer
- * @len: Information elements buffer length
- * @eid: Information element identifier (WLAN_EID_*)
- * @nth: Return the nth element of the requested type (2 returns the second)
- * Returns: Pointer to the information element (id field) or %NULL if not found
- *
- * This function returns the nth matching information element in the IEs
- * buffer or %NULL in case the element is not found.
- */
-const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth)
-{
- const struct element *elem;
- int sofar = 0;
-
- if (!ies)
- return NULL;
-
- for_each_element_id(elem, eid, ies, len) {
- sofar++;
- if (sofar == nth)
- return &elem->id;
- }
-
- return NULL;
-}
-
-
-/**
* get_ie_ext - Fetch a specified extended information element from IEs buffer
* @ies: Information elements buffer
* @len: Information elements buffer length
@@ -2597,21 +2594,141 @@
}
-size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
+u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
+ struct multi_ap_params *multi_ap)
+{
+ const struct element *elem;
+ bool ext_present = false;
+ unsigned int vlan_id;
+
+ os_memset(multi_ap, 0, sizeof(*multi_ap));
+
+ /* Default profile is 1, when Multi-AP profile subelement is not
+ * present in the element. */
+ multi_ap->profile = 1;
+
+ for_each_element(elem, multi_ap_ie, multi_ap_len) {
+ u8 id = elem->id, elen = elem->datalen;
+ const u8 *pos = elem->data;
+
+ switch (id) {
+ case MULTI_AP_SUB_ELEM_TYPE:
+ if (elen >= 1) {
+ multi_ap->capability = *pos;
+ ext_present = true;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ 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;
+ }
+
+ multi_ap->profile = *pos;
+ if (multi_ap->profile > MULTI_AP_PROFILE_MAX) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE with invalid profile 0x%02x",
+ multi_ap->profile);
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+ break;
+ case MULTI_AP_VLAN_SUB_ELEM_TYPE:
+ 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;
+ }
+ if (elen < 2) {
+ wpa_printf(MSG_DEBUG,
+ "Multi-AP IE invalid Multi-AP VLAN subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ vlan_id = WPA_GET_LE16(pos);
+ if (vlan_id < 1 || vlan_id > 4094) {
+ wpa_printf(MSG_INFO,
+ "Multi-AP IE invalid Multi-AP VLAN ID %d",
+ vlan_id);
+ return WLAN_STATUS_INVALID_IE;
+ }
+ multi_ap->vlanid = vlan_id;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Ignore unknown subelement %u in Multi-AP IE",
+ id);
+ break;
+ }
+ }
+
+ if (!for_each_element_completed(elem, multi_ap_ie, multi_ap_len)) {
+ wpa_printf(MSG_DEBUG, "Multi AP IE parse failed @%d",
+ (int) (multi_ap_ie + multi_ap_len -
+ (const u8 *) elem));
+ wpa_hexdump(MSG_MSGDUMP, "IEs", multi_ap_ie, 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_SUCCESS;
+}
+
+
+size_t add_multi_ap_ie(u8 *buf, size_t len,
+ const struct multi_ap_params *multi_ap)
{
u8 *pos = buf;
+ u8 *len_ptr;
- if (len < 9)
+ if (len < 6)
return 0;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
+ len_ptr = pos; /* Length field to be set at the end */
+ pos++;
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = MULTI_AP_OUI_TYPE;
+
+ /* Multi-AP Extension subelement */
+ if (buf + len - pos < 3)
+ return 0;
*pos++ = MULTI_AP_SUB_ELEM_TYPE;
*pos++ = 1; /* len */
- *pos++ = value;
+ *pos++ = multi_ap->capability;
+
+ /* Add Multi-AP Profile subelement only for R2 or newer configuration */
+ if (multi_ap->profile >= MULTI_AP_PROFILE_2) {
+ if (buf + len - pos < 3)
+ return 0;
+ *pos++ = MULTI_AP_PROFILE_SUB_ELEM_TYPE;
+ *pos++ = 1;
+ *pos++ = multi_ap->profile;
+ }
+
+ /* Add Multi-AP Default 802.1Q Setting subelement only for backhaul BSS
+ */
+ if (multi_ap->vlanid &&
+ multi_ap->profile >= MULTI_AP_PROFILE_2 &&
+ (multi_ap->capability & MULTI_AP_BACKHAUL_BSS)) {
+ if (buf + len - pos < 4)
+ return 0;
+ *pos++ = MULTI_AP_VLAN_SUB_ELEM_TYPE;
+ *pos++ = 2;
+ WPA_PUT_LE16(pos, multi_ap->vlanid);
+ pos += 2;
+ }
+
+ *len_ptr = pos - len_ptr - 1;
return pos - buf;
}
@@ -2776,6 +2893,8 @@
case BW80P80:
case BW160:
return 160;
+ case BW320:
+ return 320;
case BW2160:
return 2160;
default:
@@ -2874,6 +2993,21 @@
}
+bool is_same_band(int freq1, int freq2)
+{
+ if (IS_2P4GHZ(freq1) && IS_2P4GHZ(freq2))
+ return true;
+
+ if (IS_5GHZ(freq1) && IS_5GHZ(freq2))
+ return true;
+
+ if (is_6ghz_freq(freq1) && is_6ghz_freq(freq2))
+ return true;
+
+ return false;
+}
+
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{
@@ -3008,8 +3142,12 @@
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);
}
@@ -3212,8 +3350,39 @@
}
-struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
- bool ext_elem)
+/**
+ * chwidth_freq2_to_ch_width - Determine channel width as enum oper_chan_width
+ * @chwidth: Channel width integer
+ * @freq2: Value for frequency 2. 0 is not used
+ * Returns: enum oper_chan_width, -1 on failure
+ */
+int chwidth_freq2_to_ch_width(int chwidth, int freq2)
+{
+ if (freq2 < 0)
+ return -1;
+ if (freq2)
+ return CONF_OPER_CHWIDTH_80P80MHZ;
+
+ switch (chwidth) {
+ case 0:
+ case 20:
+ case 40:
+ return CONF_OPER_CHWIDTH_USE_HT;
+ case 80:
+ return CONF_OPER_CHWIDTH_80MHZ;
+ case 160:
+ return CONF_OPER_CHWIDTH_160MHZ;
+ case 320:
+ return CONF_OPER_CHWIDTH_320MHZ;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
+ chwidth);
+ return -1;
+ }
+}
+
+
+struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem)
{
struct wpabuf *buf;
const u8 *pos, *end = data + len;
@@ -3253,43 +3422,42 @@
}
-struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext)
+const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type)
{
- const u8 *data;
- size_t len;
+ const struct element *elem;
- /*
- * TODO: Defragmentation mechanism can be supported for all IEs. For now
- * handle only those that are used (or use ieee802_11_defrag_data()).
- */
- switch (eid) {
- case WLAN_EID_EXTENSION:
- switch (eid_ext) {
- case WLAN_EID_EXT_FILS_HLP_CONTAINER:
- data = elems->fils_hlp;
- len = elems->fils_hlp_len;
- break;
- case WLAN_EID_EXT_WRAPPED_DATA:
- data = elems->wrapped_data;
- len = elems->wrapped_data_len;
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "Defragmentation not supported. eid_ext=%u",
- eid_ext);
- return NULL;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "Defragmentation not supported. eid=%u", eid);
+ if (!ies)
return NULL;
+
+ for_each_element_extid(elem, WLAN_EID_EXT_MULTI_LINK, ies, len) {
+ if (elem->datalen >= 2 &&
+ (elem->data[1] & MULTI_LINK_CONTROL_TYPE_MASK) == type)
+ return &elem->id;
}
- return ieee802_11_defrag_data(data, len, true);
+ return NULL;
}
+
+const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len)
+{
+ const size_t mld_addr_pos =
+ 2 /* Control field */ +
+ 1 /* Common Info Length field */;
+ const size_t fixed_len = mld_addr_pos +
+ ETH_ALEN /* MLD MAC Address field */;
+
+ if (len < fixed_len)
+ return NULL;
+
+ if ((buf[0] & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC)
+ return NULL;
+
+ return &buf[mld_addr_pos];
+}
+
+
/* Parse HT capabilities to get maximum number of supported spatial streams */
static int parse_ht_mcs_set_for_max_nss(
struct ieee80211_ht_capabilities *htcaps,
@@ -3419,6 +3587,7 @@
return supported_width;
}
+
/*
* Parse VHT operation info fields to get operation channel width
* note that VHT operation info fields could come from VHT operation IE
@@ -3453,6 +3622,7 @@
return channel_width;
}
+
/* Parse 6GHz operation info fields to get operation channel width */
static enum chan_width get_6ghz_operation_channel_width(
struct ieee80211_6ghz_operation_info * six_ghz_oper_info)
@@ -3525,6 +3695,7 @@
return channel_width;
}
+
/* Parse EHT operation IE to get EHT operation channel width */
static enum chan_width get_eht_operation_channel_width(
struct ieee80211_eht_operation *eht_oper,
@@ -3558,6 +3729,7 @@
return channel_width;
}
+
/* Parse HT/VHT/HE operation IEs to get operation channel width */
enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems)
{
@@ -3594,6 +3766,8 @@
return channel_width;
}
+
+
/*
* Get STA operation channel width from AP's operation channel width and
* STA's supported channel width
@@ -3615,78 +3789,6 @@
return ap_operation_chan_width;
}
-const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type)
-{
- const struct element *elem;
-
- if (!ies)
- return NULL;
-
- for_each_element_extid(elem, WLAN_EID_EXT_MULTI_LINK, ies, len) {
- if (elem->datalen >= 2 &&
- (elem->data[1] & MULTI_LINK_CONTROL_TYPE_MASK) == type)
- return &elem->id;
- }
-
- return NULL;
-}
-
-
-const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len)
-{
- const size_t mld_addr_pos =
- 2 /* Control field */ +
- 1 /* Common Info Length field */;
- const size_t fixed_len = mld_addr_pos +
- ETH_ALEN /* MLD MAC Address field */;
-
- if (len < fixed_len)
- return NULL;
-
- if ((buf[0] & MULTI_LINK_CONTROL_TYPE_MASK) !=
- MULTI_LINK_CONTROL_TYPE_BASIC)
- return NULL;
-
- return &buf[mld_addr_pos];
-}
-
-
-struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type)
-{
- const u8 *data;
- size_t len;
-
- switch (type) {
- case MULTI_LINK_CONTROL_TYPE_BASIC:
- data = elems->basic_mle;
- len = elems->basic_mle_len;
- break;
- case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
- data = elems->probe_req_mle;
- len = elems->probe_req_mle_len;
- break;
- case MULTI_LINK_CONTROL_TYPE_RECONF:
- data = elems->reconf_mle;
- len = elems->reconf_mle_len;
- break;
- case MULTI_LINK_CONTROL_TYPE_TDLS:
- data = elems->tdls_mle;
- len = elems->tdls_mle_len;
- break;
- case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
- data = elems->prior_access_mle;
- len = elems->prior_access_mle_len;
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "Defragmentation not supported for Multi-Link element type=%u",
- type);
- return NULL;
- }
-
- return ieee802_11_defrag_data(data, len, true);
-}
-
unsigned int is_ap_t2lm_negotiation_supported(const u8 *mle, size_t mle_len)
{
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 6f4fe80..17e0822 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -30,6 +30,12 @@
u8 nof_ies;
};
+struct multi_ap_params {
+ u8 capability;
+ u8 profile;
+ u16 vlanid;
+};
+
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
@@ -109,6 +115,9 @@
const u8 *tdls_mle;
const u8 *prior_access_mle;
const u8 *mbssid_known_bss;
+ const u8 *mbssid;
+ const u8 *rsne_override;
+ const u8 *rsne_override_2;
u8 ssid_len;
u8 supp_rates_len;
@@ -171,6 +180,9 @@
size_t tdls_mle_len;
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;
struct mb_ies_info mb_ies;
@@ -235,6 +247,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);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
@@ -253,7 +266,7 @@
u8 max_chan;
u8 inc;
enum { BW20, BW40PLUS, BW40MINUS, BW40, BW80, BW2160, BW160, BW80P80,
- BW4320, BW6480, BW8640} bw;
+ BW320, BW4320, BW6480, BW8640} bw;
enum { P2P_SUPP, NO_P2P_SUPP } p2p;
};
@@ -261,13 +274,15 @@
extern size_t global_op_class_size;
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
-const u8 * get_ie_nth(const u8 *ies, size_t len, u8 eid, int nth);
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
-size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
+u16 check_multi_ap_ie(const u8 *multi_ap_ie, size_t multi_ap_len,
+ struct multi_ap_params *multi_ap);
+size_t add_multi_ap_ie(u8 *buf, size_t len,
+ const struct multi_ap_params *multi_ap);
struct country_op_class {
u8 country_op_class;
@@ -284,6 +299,10 @@
bool is_6ghz_psc_frequency(int freq);
int get_6ghz_sec_channel(int channel);
+bool is_same_band(int freq1, int freq2);
+#define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
+#define IS_5GHZ(n) (n > 4000 && n < 5895)
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len);
@@ -293,6 +312,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);
/* element iteration helpers */
#define for_each_element(_elem, _data, _datalen) \
@@ -350,11 +370,7 @@
int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested);
-struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
- bool ext_elem);
-struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
- u8 eid, u8 eid_ext);
-struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type);
+struct wpabuf * ieee802_11_defrag(const u8 *data, size_t len, bool ext_elem);
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);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index a895f9c..4cc6e41 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -24,6 +24,13 @@
#define WLAN_FC_ISWEP 0x4000
#define WLAN_FC_HTC 0x8000
+#define WLAN_FC_S1G_BEACON_NEXT_TBTT 0x0100
+#define WLAN_FC_S1G_BEACON_COMP_SSID 0x0200
+#define WLAN_FC_S1G_BEACON_ANO 0x0400
+#define WLAN_FC_S1G_BEACON_BSS_BW 0x3800
+#define WLAN_FC_S1G_BEACON_SECURITY 0x4000
+#define WLAN_FC_S1G_BEACON_AP_PM 0x8000
+
#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2)
#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4)
@@ -36,6 +43,7 @@
#define WLAN_FC_TYPE_MGMT 0
#define WLAN_FC_TYPE_CTRL 1
#define WLAN_FC_TYPE_DATA 2
+#define WLAN_FC_TYPE_EXT 3
/* management */
#define WLAN_FC_STYPE_ASSOC_REQ 0
@@ -77,6 +85,10 @@
#define WLAN_FC_STYPE_QOS_CFPOLL 14
#define WLAN_FC_STYPE_QOS_CFACKPOLL 15
+/* extension */
+#define WLAN_FC_STYPE_DMG_BEACON 0
+#define WLAN_FC_STYPE_S1G_BEACON 1
+
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
@@ -107,7 +119,7 @@
#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
-/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
+/* Status codes (IEEE Std 802.11-2020, 9.4.1.9, Table 9-50) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
@@ -209,11 +221,20 @@
#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
#define WLAN_STATUS_SAE_PK 127
+#define WLAN_STATUS_DENIED_STA_AFF_WITH_MLD_WITH_EXISTING_ASSOC 130
+#define WLAN_STATUS_EPCS_DENIED_UNAUTHORIZED 131
+#define WLAN_STATUS_EPCS_DENIED 132
+#define WLAN_STATUS_DENIED_TID_TO_LINK_MAPPING 133
+#define WLAN_STATUS_PREFERRED_TID_TO_LINK_MAPPING_SUGGESTED 134
+#define WLAN_STATUS_DENIED_EHT_NOT_SUPPORTED 135
#define WLAN_STATUS_INVALID_PUBLIC_KEY 136
#define WLAN_STATUS_PASN_BASE_AKMP_FAILED 137
#define WLAN_STATUS_OCI_MISMATCH 138
+#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
-/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
+/* Reason codes (IEEE Std 802.11-2020, 9.4.1.7, Table 9-90) */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
#define WLAN_REASON_DEAUTH_LEAVING 3
@@ -277,7 +298,7 @@
#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66
-/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
+/* Element IDs (IEEE Std 802.11-2020, 9.4.2.1, Table 9-92) */
#define WLAN_EID_SSID 0
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_DS_PARAMS 3
@@ -595,11 +616,12 @@
#define WLAN_RSNX_CAPAB_SECURE_RTT 9
#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
/* Multiple BSSID element subelements */
#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
-/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
+/* Action frame categories (IEEE Std 802.11-2020, 9.4.1.11, Table 9-51) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
#define WLAN_ACTION_QOS 1
#define WLAN_ACTION_DLS 2
@@ -640,7 +662,7 @@
#define WLAN_ACTION_VENDOR_SPECIFIC 127
/* Note: 128-255 used to report errors by setting category | 0x80 */
-/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
+/* Public action codes (IEEE Std 802.11-2020, 9.6.7.1, Table 9-364) */
#define WLAN_PA_20_40_BSS_COEX 0
#define WLAN_PA_DSE_ENABLEMENT 1
#define WLAN_PA_DSE_DEENABLEMENT 2
@@ -691,8 +713,8 @@
#define WLAN_VHT_ACTION_GROUP_ID_MGMT 1
#define WLAN_VHT_ACTION_OPMODE_NOTIF 2
-/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
- * Table 9-332) */
+/* Protected Dual of Public Action frames (IEEE Std 802.11-2020, 9.6.10,
+ * Table 9-404) */
#define WLAN_PROT_DSE_ENABLEMENT 1
#define WLAN_PROT_DSE_DEENABLEMENT 2
#define WLAN_PROT_EXT_CSA 4
@@ -720,7 +742,7 @@
#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30
#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31
-/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
+/* SA Query Action frame (IEEE Std 802.11-2020, 9.6.9) */
#define WLAN_SA_QUERY_REQUEST 0
#define WLAN_SA_QUERY_RESPONSE 1
@@ -753,7 +775,7 @@
#define WLAN_PROT_FTM_REPORT 3
/* Radio Measurement capabilities (from RM Enabled Capabilities element)
- * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
+ * IEEE Std 802.11-2020, 9.4.2.44, Table 9-179 */
/* byte 1 (out of 5) */
#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
@@ -766,8 +788,8 @@
#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2)
/*
- * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range
- * request) - Minimum AP count
+ * IEEE Std 802.11-2020, 9.4.2.20.19 (Fine Timing Measurement Range
+ * request) - Minimum AP Count
*/
#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15
@@ -776,7 +798,8 @@
#define WLAN_TIMEOUT_KEY_LIFETIME 2
#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
-/* Interworking element (IEEE 802.11u) - Access Network Options */
+/* Interworking element (IEEE Std 802.11-2020, 9.4.2.91) -
+ * Access Network Options */
#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
#define INTERWORKING_ANO_INTERNET 0x10
#define INTERWORKING_ANO_ASRA 0x20
@@ -792,7 +815,7 @@
#define INTERWORKING_ANT_TEST 6
#define INTERWORKING_ANT_WILDCARD 15
-/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */
+/* Advertisement Protocol ID definitions (IEEE Std 802.11-2020, Table 9-237) */
enum adv_proto_id {
ACCESS_NETWORK_QUERY_PROTOCOL = 0,
MIH_INFO_SERVICE = 1,
@@ -802,8 +825,7 @@
ADV_PROTO_VENDOR_SPECIFIC = 221
};
-/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016,
- * Table 9-271; P802.11ai) */
+/* ANQP-element definitions (IEEE Std 802.11-2020, Table 9-331) */
enum anqp_info_id {
ANQP_QUERY_LIST = 256,
ANQP_CAPABILITY_LIST = 257,
@@ -878,7 +900,7 @@
#define S1G_ACT_TWT_INFORMATION 11
/*
- * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for
+ * IEEE Std 802.11-2020, Table 9-98 - Measurement type definitions for
* measurement requests
*/
enum measure_type {
@@ -902,7 +924,7 @@
MEASURE_TYPE_MEASURE_PAUSE = 255,
};
-/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */
+/* IEEE Std 802.11-2020, Table 9-110 - Location Subject field definition */
enum location_subject {
LOCATION_SUBJECT_LOCAL = 0,
LOCATION_SUBJECT_REMOTE = 1,
@@ -910,7 +932,7 @@
};
/*
- * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request
+ * IEEE Std 802.11-2020, Table 9-111 - Optional subelement IDs for LCI request
*/
enum lci_req_subelem {
LCI_REQ_SUBELEM_AZIMUTH_REQ = 1,
@@ -945,6 +967,23 @@
#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+struct ieee80211_hdr_s1g_beacon {
+ le16 frame_control;
+ le16 duration_id;
+ u8 sa[6];
+ u8 timestamp[4];
+ u8 change_seq[1];
+ /* followed by:
+ * 'u8 next_tbtt[3];' if the Next TBTT Present field in the
+ * Frame Control field is 1
+ * 'u8 compressed_ssid[4];' if the Compressed SSID Present field in
+ * the Frame Control is 1
+ * 'u8 ano[1];' if the ANO field in the Frame Control field is 1
+ */
+} STRUCT_PACKED;
+
+#define IEEE80211_HDRLEN_S1G_BEACON (sizeof(struct ieee80211_hdr_s1g_beacon))
+
#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
struct ieee80211_mgmt {
@@ -1396,7 +1435,11 @@
#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 MBO_IE_VENDOR_TYPE 0x506f9a16
+#define NAN_NAF_VENDOR_TYPE 0x506f9a18
#define MBO_OUI_TYPE 22
#define OWE_IE_VENDOR_TYPE 0x506f9a1c
#define OWE_OUI_TYPE 28
@@ -1409,13 +1452,28 @@
#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 RSNE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a29
+#define RSNE_OVERRIDE_2_IE_VENDOR_TYPE 0x506f9a2a
+#define RSNXE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a2b
#define MULTI_AP_SUB_ELEM_TYPE 0x06
+#define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07
+#define MULTI_AP_VLAN_SUB_ELEM_TYPE 0x08
+
+#define MULTI_AP_PROFILE2_BACKHAUL_STA_DISALLOWED BIT(2)
+#define MULTI_AP_PROFILE1_BACKHAUL_STA_DISALLOWED BIT(3)
#define MULTI_AP_TEAR_DOWN BIT(4)
#define MULTI_AP_FRONTHAUL_BSS BIT(5)
#define MULTI_AP_BACKHAUL_BSS BIT(6)
#define MULTI_AP_BACKHAUL_STA BIT(7)
+#define MULTI_AP_PROFILE_1 1
+#define MULTI_AP_PROFILE_2 2
+#define MULTI_AP_PROFILE_MAX 6
+
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
@@ -1882,8 +1940,9 @@
#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+#define WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT BIT(5)
-/* IEEE Std 802.11-2012 - Table 8-253 */
+/* IEEE Std 802.11-2020, Table 9-428 (BTM status code definitions) */
enum bss_trans_mgmt_status_code {
WNM_BSS_TM_ACCEPT = 0,
WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
@@ -1896,9 +1955,36 @@
WNM_BSS_TM_REJECT_LEAVING_ESS = 8
};
+/* BSS transition management reasons
+ * IEEE Std 802.11-2020, Table 9-198 (Transition and Transition Query reasons)
+ */
+enum bss_trans_mgmt_reason {
+ WNM_TRANSITION_REASON_UNSPECIFIED = 0,
+ WNM_TRANSITION_REASON_EXCESSIVE_FRAME_LOSS = 1,
+ WNM_TRANSITION_REASON_EXCESSIVE_DELAY = 2,
+ WNM_TRANSITION_REASON_INSUFFICIENT_QOS = 3,
+ WNM_TRANSITION_REASON_FIRST_ESS_ASSOC = 4,
+ WNM_TRANSITION_REASON_LOAD_BALANCING = 5,
+ WNM_TRANSITION_REASON_BETTER_AP_FOUND = 6,
+ WNM_TRANSITION_REASON_DEAUTH_FROM_PREV_AP = 7,
+ WNM_TRANSITION_REASON_AP_FAILED_EAP = 8,
+ WNM_TRANSITION_REASON_AP_FAILED_4WAY_HS = 9,
+ WNM_TRANSITION_REASON_RX_TOO_MANY_REPLAYS = 10,
+ WNM_TRANSITION_REASON_RX_TOO_MANY_MIC_FAILURES = 11,
+ WNM_TRANSITION_REASON_EXCEEDED_MAX_RETRANS = 12,
+ WNM_TRANSITION_REASON_RX_TOO_MANY_BC_DISASSOC = 13,
+ WNM_TRANSITION_REASON_RX_TOO_MANY_BC_DEAUTH = 14,
+ WNM_TRANSITION_REASON_PREV_TRANSITION_FAILED = 15,
+ WNM_TRANSITION_REASON_LOW_RSSI = 16,
+ WNM_TRANSITION_REASON_ROAM_FROM_NON_802_11 = 17,
+ WNM_TRANSITION_REASON_TRANSITION_DUE_TO_BTM_REQ = 18,
+ WNM_TRANSITION_REASON_PREF_TRANSITION_CANDIDATE_LIST = 19,
+ WNM_TRANSITION_REASON_LEAVING_ESS = 20,
+};
+
/*
- * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for
- * neighbor report
+ * IEEE Std 802.11-2020, Table 9-173 - Optional subelement IDs for
+ * Neighbor Report
*/
#define WNM_NEIGHBOR_TSF 1
#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
@@ -1925,7 +2011,7 @@
QOS_QOS_MAP_CONFIG = 4,
};
-/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
+/* IEEE Std 802.11-2020, 9.4.2.59 (20/40 BSS Coexistence element) */
#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2)
@@ -1973,7 +2059,7 @@
WNM_SLEEP_SUBELEM_BIGTK = 2,
};
-/* WNM notification type (IEEE P802.11-REVmd/D3.0, Table 9-430) */
+/* WNM notification type (IEEE Std 802.11-2020, Table 9-431) */
enum wnm_notification_Type {
WNM_NOTIF_TYPE_FIRMWARE_UPDATE = 0,
WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE = 2,
@@ -2049,7 +2135,7 @@
#define RRM_CAPABILITIES_IE_LEN 5
-/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+/* IEEE Std 802.11-2020, 9.6.6.4 - Link Measurement Request frame format */
struct rrm_link_measurement_request {
u8 dialog_token;
s8 tx_power;
@@ -2057,7 +2143,7 @@
u8 variable[0];
} STRUCT_PACKED;
-/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
+/* IEEE Std 802.11-2020, 9.6.6.5 - Link Measurement Report frame format */
struct rrm_link_measurement_report {
u8 dialog_token;
struct tpc_report tpc;
@@ -2068,7 +2154,7 @@
u8 variable[0];
} STRUCT_PACKED;
-/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
+/* IEEE Std 802.11-2020, 9.4.2.20 - Measurement Request element */
struct rrm_measurement_request_element {
u8 eid; /* Element ID */
u8 len; /* Length */
@@ -2078,14 +2164,14 @@
u8 variable[0]; /* Measurement Request */
} STRUCT_PACKED;
-/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
+/* IEEE Std 802.11-2020, Figure 9-180 - Measurement Request Mode field format */
#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
-/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
+/* IEEE Std 802.11-2020, 9.4.2.20.7 - Beacon request */
struct rrm_measurement_beacon_request {
u8 oper_class; /* Operating Class */
u8 channel; /* Channel Number */
@@ -2097,7 +2183,7 @@
} STRUCT_PACKED;
/*
- * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
+ * IEEE Std 802.11-2020, Table 9-103 - Measurement Mode definitions for Beacon
* request
*/
enum beacon_report_mode {
@@ -2106,20 +2192,19 @@
BEACON_REPORT_MODE_TABLE = 2,
};
-/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
-/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for
+/* IEEE Std 802.11-2020, Table 9-104 - Optional subelement IDs for
* Beacon request */
#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
+#define WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST 11
#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
+#define WLAN_BEACON_REQUEST_SUBELEM_WIDE_BW_CS 163
#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
-/*
- * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
- */
+/* IEEE Std 802.11-2020, Table 9-106 - Reporting Detail values */
enum beacon_report_detail {
/* No fixed-length fields or elements */
BEACON_REPORT_DETAIL_NONE = 0,
@@ -2131,7 +2216,7 @@
BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
};
-/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
+/* IEEE Std 802.11-2020, 9.4.2.21 - Measurement Report element */
struct rrm_measurement_report_element {
u8 eid; /* Element ID */
u8 len; /* Length */
@@ -2141,13 +2226,13 @@
u8 variable[0]; /* Measurement Report */
} STRUCT_PACKED;
-/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
+/* IEEE Std 802.11-2020, Figure 9-223 - Measurement Report Mode field */
#define MEASUREMENT_REPORT_MODE_ACCEPT 0
#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
-/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
+/* IEEE Std 802.11-2020, 9.4.2.21.7 - Beacon report */
struct rrm_measurement_beacon_report {
u8 op_class; /* Operating Class */
u8 channel; /* Channel Number */
@@ -2163,23 +2248,23 @@
u8 variable[0]; /* Optional Subelements */
} STRUCT_PACKED;
-/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
-/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
+/* IEEE Std 802.11-2020, Table 9-130 - Optional subelement IDs for
* Beacon report */
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
+#define WLAN_BEACON_REPORT_SUBELEM_WID_BW_CS 163
#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
-/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
- * Reported Frame Body Fragment ID subelement */
+/* IEEE Std 802.11-2020, Table 9-232 - Data field format (of the
+ * Reported Frame Body Fragment ID subelement) */
#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
-/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */
+/* IEEE Std 802.11-2020, 9.4.2.21.7 - Beacon report */
#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3
-/* IEEE Std 802.11ad-2012 - Multi-band element */
+/* IEEE Std 802.11-2020, 9.4.2.138 - Multi-band element */
struct multi_band_ie {
u8 eid; /* WLAN_EID_MULTI_BAND */
u8 len;
@@ -2225,7 +2310,7 @@
#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
-/* IEEE Std 802.11ad-2014 - FST Action field */
+/* IEEE Std 802.11-2020, Table 9-479 - FST Action field values */
enum fst_action {
FST_ACTION_SETUP_REQUEST = 0,
FST_ACTION_SETUP_RESPONSE = 1,
@@ -2235,21 +2320,23 @@
FST_ACTION_ON_CHANNEL_TUNNEL = 5,
};
-/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */
+/* IEEE Std 802.11-2020, Annex C - dot11PHYType */
enum phy_type {
PHY_TYPE_UNSPECIFIED = 0,
- PHY_TYPE_FHSS = 1,
PHY_TYPE_DSSS = 2,
- PHY_TYPE_IRBASEBAND = 3,
PHY_TYPE_OFDM = 4,
PHY_TYPE_HRDSSS = 5,
PHY_TYPE_ERP = 6,
PHY_TYPE_HT = 7,
PHY_TYPE_DMG = 8,
PHY_TYPE_VHT = 9,
+ PHY_TYPE_TVHT = 10,
+ PHY_TYPE_S1G = 11,
+ PHY_TYPE_CDMG = 12,
+ PHY_TYPE_CMMG = 13,
};
-/* IEEE P802.11-REVmd/D3.0, 9.4.2.36 - Neighbor Report element */
+/* IEEE Std 802.11-2020, 9.4.2.36 - 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)
@@ -2270,7 +2357,7 @@
#define NEI_REP_BSSID_INFO_EHT BIT(21)
/*
- * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
+ * IEEE Std 802.11-2020, Table 9-175 - HT/VHT Operation Information
* subfields.
* Note: These definitions are not the same as other CHANWIDTH_*.
*/
@@ -2434,16 +2521,31 @@
#define HE_OPERATION_6GHZ_OPER_INFO_LEN 5
/**
- * enum he_6ghz_ap_type - Allowed Access Point types for 6 GHz Band
+ * enum he_reg_info_6ghz_ap_type - Allowed Access Point types for 6 GHz Band
*
- * IEEE Std 802.11ax-2021, Table E-12 (Regulatory Info subfield encoding in the
- * United States)
+ * IEEE P802.11-REVme/D4.0, Table E-12 (Regulatory Info subfield encoding)
*/
-enum he_6ghz_ap_type {
- HE_6GHZ_INDOOR_AP = 0,
- HE_6GHZ_STANDARD_POWER_AP = 1,
+enum he_reg_info_6ghz_ap_type {
+ HE_REG_INFO_6GHZ_AP_TYPE_INDOOR = 0,
+ HE_REG_INFO_6GHZ_AP_TYPE_SP = 1,
+ HE_REG_INFO_6GHZ_AP_TYPE_VLP = 2,
+ HE_REG_INFO_6GHZ_AP_TYPE_INDOOR_ENABLED = 3,
+ HE_REG_INFO_6GHZ_AP_TYPE_INDOOR_SP = 4,
+ HE_REG_INFO_6GHZ_AP_TYPE_MAX = HE_REG_INFO_6GHZ_AP_TYPE_INDOOR_SP,
};
+static inline bool he_reg_is_indoor(enum he_reg_info_6ghz_ap_type type)
+{
+ return type == HE_REG_INFO_6GHZ_AP_TYPE_INDOOR ||
+ type == HE_REG_INFO_6GHZ_AP_TYPE_INDOOR_SP;
+}
+
+static inline bool he_reg_is_sp(enum he_reg_info_6ghz_ap_type type)
+{
+ return type == HE_REG_INFO_6GHZ_AP_TYPE_SP ||
+ type == HE_REG_INFO_6GHZ_AP_TYPE_INDOOR_SP;
+}
+
/* Spatial Reuse defines */
#define SPATIAL_REUSE_SRP_DISALLOWED BIT(0)
#define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED BIT(1)
@@ -2511,7 +2613,18 @@
#define RNR_BSS_PARAM_MEMBER_CO_LOCATED_ESS BIT(4)
#define RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE BIT(5)
#define RNR_BSS_PARAM_CO_LOCATED BIT(6)
-#define RNR_20_MHZ_PSD_MAX_TXPOWER 255 /* dBm */
+/* Maximum transmit power in Y/2 dBm (-127..126); 127 indicates no maximum
+ * 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 */
+/* B0..B7: AP MLD ID */
+/* B8..B11: Link ID */
+/* B12..B19: BSS Parameters Change Count */
+/* B20: All Updates Included */
+#define RNR_TBTT_INFO_MLD_PARAM2_ALL_UPDATE_INC 0x10
+/* 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 */
@@ -2725,6 +2838,17 @@
#define EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK 0x0400
#define EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK 0x0800
+/* 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 */
struct ieee80211_eht_per_sta_profile {
le16 sta_control;
@@ -2733,9 +2857,11 @@
u8 variable[];
} STRUCT_PACKED;
-/* IEEE P802.11be/D2.0, 9.4.2.312.3 - Probe Request Multi-Link element */
+/* IEEE P802.11be/D4.0, 9.4.2.312.3 - 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 0x0001
+#define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0010
struct eht_ml_probe_req_common_info {
u8 len;
@@ -2869,8 +2995,8 @@
};
/*
- * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format,
- * Figure 9-687b - FILS Discovery Frame Control subfield format
+ * IEEE Std 802.11-2020, 9.6.7.36 FILS Discovery frame format,
+ * Figure 9-900 - FILS Discovery Frame Control subfield format
*/
#define FD_FRAME_CTL_CAP_PRESENT ((u16) BIT(5))
#define FD_FRAME_CTL_SHORT_SSID_PRESENT ((u16) BIT(6))
@@ -2883,8 +3009,8 @@
#define FD_FRAME_CTL_MD_PRESENT ((u16) BIT(13))
/*
- * IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format,
- * Figure 9-687c - FD Capability subfield format
+ * IEEE Std 802.11-2020, 9.6.7.36 FILS Discovery frame format,
+ * Figure 9-901 - FD Capability subfield format
*/
#define FD_CAP_ESS BIT(0)
#define FD_CAP_PRIVACY BIT(1)
@@ -2973,6 +3099,15 @@
u8 data[0];
} STRUCT_PACKED;
+/* S1G Beacon Compatibility element */
+struct ieee80211_s1g_beacon_compat {
+ u8 element_id;
+ u8 length;
+ le16 compat_info;
+ le16 beacon_interval;
+ le32 tsf_completion;
+} STRUCT_PACKED;
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
diff --git a/src/common/nan.h b/src/common/nan.h
new file mode 100644
index 0000000..19ab746
--- /dev/null
+++ b/src/common/nan.h
@@ -0,0 +1,98 @@
+/*
+ * 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/src/common/nan_de.c b/src/common/nan_de.c
new file mode 100644
index 0000000..12fad31
--- /dev/null
+++ b/src/common/nan_de.c
@@ -0,0 +1,1395 @@
+/*
+ * NAN Discovery Engine
+ * 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 "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "ieee802_11_defs.h"
+#include "nan.h"
+#include "nan_de.h"
+
+static 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,
+};
+
+struct nan_de_service {
+ int id;
+ enum nan_de_service_type type;
+ char *service_name;
+ u8 service_id[NAN_SERVICE_ID_LEN];
+ struct nan_publish_params publish;
+ struct nan_subscribe_params subscribe;
+ enum nan_service_protocol_type srv_proto_type;
+ struct wpabuf *ssi;
+ struct wpabuf *elems;
+ struct os_reltime time_started;
+ 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;
+
+ /* pauseState information for Publish function */
+ struct os_reltime pause_state_end;
+ u8 sel_peer_id;
+ u8 sel_peer_addr[ETH_ALEN];
+
+ /* Publish state - channel iteration */
+ bool in_multi_chan;
+ bool first_multi_chan;
+ int multi_chan_idx; /* index to freq_list[] */
+ struct os_reltime next_publish_state;
+ struct os_reltime next_publish_chan;
+ unsigned int next_publish_duration;
+};
+
+struct nan_de {
+ u8 nmi[ETH_ALEN];
+ bool ap;
+ struct nan_callbacks cb;
+
+ struct nan_de_service *service[NAN_DE_MAX_SERVICE];
+ unsigned int num_service;
+
+ int next_handle;
+
+ unsigned int ext_listen_freq;
+ unsigned int listen_freq;
+ unsigned int tx_wait_status_freq;
+ unsigned int tx_wait_end_freq;
+};
+
+
+struct nan_de * nan_de_init(const u8 *nmi, bool ap,
+ const struct nan_callbacks *cb)
+{
+ struct nan_de *de;
+
+ de = os_zalloc(sizeof(*de));
+ if (!de)
+ return NULL;
+
+ os_memcpy(de->nmi, nmi, ETH_ALEN);
+ de->ap = ap;
+ os_memcpy(&de->cb, cb, sizeof(*cb));
+
+ return de;
+}
+
+
+static void nan_de_service_free(struct nan_de_service *srv)
+{
+ os_free(srv->service_name);
+ wpabuf_free(srv->ssi);
+ wpabuf_free(srv->elems);
+ os_free(srv->freq_list);
+ os_free(srv);
+}
+
+
+static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv,
+ enum nan_de_reason reason)
+{
+ if (!srv)
+ return;
+ if (srv->type == NAN_DE_PUBLISH && de->cb.publish_terminated)
+ de->cb.publish_terminated(de->cb.ctx, srv->id, reason);
+ if (srv->type == NAN_DE_SUBSCRIBE && de->cb.subscribe_terminated)
+ de->cb.subscribe_terminated(de->cb.ctx, srv->id, reason);
+ nan_de_service_free(srv);
+}
+
+
+static void nan_de_clear_pending(struct nan_de *de)
+{
+ de->listen_freq = 0;
+ de->tx_wait_status_freq = 0;
+ de->tx_wait_end_freq = 0;
+}
+
+
+void nan_de_flush(struct nan_de *de)
+{
+ unsigned int i;
+
+ if (!de)
+ return;
+
+ for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
+ nan_de_service_deinit(de, de->service[i],
+ NAN_DE_REASON_USER_REQUEST);
+ de->service[i] = NULL;
+ }
+
+ de->num_service = 0;
+ nan_de_clear_pending(de);
+}
+
+
+static void nan_de_pause_state(struct nan_de_service *srv, const u8 *peer_addr,
+ u8 peer_id)
+{
+ wpa_printf(MSG_DEBUG, "NAN: Start pauseState");
+ os_get_reltime(&srv->pause_state_end);
+ srv->pause_state_end.sec += 60;
+ os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN);
+ srv->sel_peer_id = peer_id;
+}
+
+
+static void nan_de_unpause_state(struct nan_de_service *srv)
+{
+ wpa_printf(MSG_DEBUG, "NAN: Stop pauseState");
+ srv->pause_state_end.sec = 0;
+ srv->pause_state_end.usec = 0;
+ os_memset(srv->sel_peer_addr, 0, ETH_ALEN);
+ srv->sel_peer_id = 0;
+}
+
+
+static struct wpabuf * nan_de_alloc_sdf(size_t len)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(2 + 4 + len);
+ if (buf) {
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_be32(buf, NAN_SDF_VENDOR_TYPE);
+ }
+
+ return buf;
+}
+
+
+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)
+{
+ int res;
+
+ if (!de->cb.tx)
+ return -1;
+
+ res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf);
+ if (res < 0)
+ return res;
+
+ de->tx_wait_status_freq = freq;
+ de->tx_wait_end_freq = wait_time ? freq : 0;
+
+ return res;
+}
+
+
+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)
+{
+ struct wpabuf *buf;
+ size_t len = 0, sda_len, sdea_len;
+ u8 ctrl = type;
+ u16 sdea_ctrl = 0;
+
+ /* 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 (ssi)
+ sdea_len += 2 + 4 + wpabuf_len(ssi);
+ len += NAN_ATTR_HDR_LEN + sdea_len;
+
+ /* Element Container attribute */
+ if (srv->elems)
+ len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
+
+ buf = nan_de_alloc_sdf(len);
+ if (!buf)
+ return;
+
+ /* 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, req_instance_id); /* Requestor Instance ID */
+ wpabuf_put_u8(buf, ctrl);
+
+ /* 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;
+ }
+ 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);
+ }
+ }
+
+ /* 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, wait_time, dst, de->nmi, wildcard_bssid, buf);
+ wpabuf_free(buf);
+}
+
+
+static int nan_de_time_to_next_chan_change(struct nan_de_service *srv)
+{
+ struct os_reltime tmp, diff, now;
+
+ if (os_reltime_before(&srv->next_publish_state,
+ &srv->next_publish_chan))
+ tmp = srv->next_publish_state;
+ else if (srv->in_multi_chan)
+ tmp = srv->next_publish_chan;
+ else
+ tmp = srv->next_publish_state;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&tmp, &now, &diff);
+ return os_reltime_in_ms(&diff);
+}
+
+
+static void nan_de_set_publish_times(struct nan_de_service *srv)
+{
+ os_get_reltime(&srv->next_publish_state);
+ srv->next_publish_chan = srv->next_publish_state;
+ /* Swap single/multi channel state in N * 100 TU */
+ os_reltime_add_ms(&srv->next_publish_state,
+ srv->next_publish_duration * 1024 / 1000);
+
+ /* Swap channel in multi channel state after 150 ms */
+ os_reltime_add_ms(&srv->next_publish_chan, 150);
+}
+
+
+static void nan_de_check_chan_change(struct nan_de_service *srv)
+{
+ if (srv->next_publish_duration) {
+ /* Update end times for the first operation of the publish
+ * iteration */
+ nan_de_set_publish_times(srv);
+ srv->next_publish_duration = 0;
+ } else if (srv->in_multi_chan) {
+ if (!os_reltime_initialized(&srv->pause_state_end)) {
+ srv->multi_chan_idx++;
+ if (srv->freq_list[srv->multi_chan_idx] == 0)
+ srv->multi_chan_idx = 0;
+ srv->freq = srv->freq_list[srv->multi_chan_idx];
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publish multi-channel change to %u MHz",
+ srv->freq);
+ }
+ os_get_reltime(&srv->next_publish_chan);
+ os_reltime_add_ms(&srv->next_publish_chan, 150);
+ }
+}
+
+
+static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv,
+ u8 req_instance_id)
+{
+ enum nan_service_control_type type;
+ unsigned int wait_time = 100;
+
+ 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;
+ } 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);
+ os_get_reltime(&srv->last_multicast);
+}
+
+
+static void nan_de_add_srv(struct nan_de *de, struct nan_de_service *srv)
+{
+ int ttl;
+
+ os_get_reltime(&srv->time_started);
+ ttl = srv->type == NAN_DE_PUBLISH ? srv->publish.ttl :
+ srv->subscribe.ttl;
+ if (ttl) {
+ srv->end_time = srv->time_started;
+ srv->end_time.sec += ttl;
+ }
+
+ de->service[srv->id - 1] = srv;
+ de->num_service++;
+}
+
+
+static void nan_de_del_srv(struct nan_de *de, struct nan_de_service *srv,
+ enum nan_de_reason reason)
+{
+ de->service[srv->id - 1] = NULL;
+ nan_de_service_deinit(de, srv, reason);
+ de->num_service--;
+ if (de->num_service == 0)
+ nan_de_clear_pending(de);
+}
+
+
+static bool nan_de_srv_expired(struct nan_de_service *srv,
+ struct os_reltime *now)
+{
+ if (os_reltime_initialized(&srv->end_time))
+ return os_reltime_before(&srv->end_time, now);
+
+ if (srv->type == NAN_DE_PUBLISH) {
+ /* Time out after one transmission (and wait for FSD) */
+ if (!os_reltime_initialized(&srv->last_multicast))
+ 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) {
+ /* Time out after first DiscoveryResult event (and wait for
+ * FSD) */
+ if (!os_reltime_initialized(&srv->first_discovered))
+ 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;
+}
+
+
+static int nan_de_next_multicast(struct nan_de *de, struct nan_de_service *srv,
+ struct os_reltime *now)
+{
+ unsigned int period;
+ struct os_reltime next, diff;
+
+ if (srv->type == NAN_DE_PUBLISH && !srv->publish.unsolicited)
+ return -1;
+ if (srv->type == NAN_DE_SUBSCRIBE && !srv->subscribe.active)
+ return -1;
+
+ if (!os_reltime_initialized(&srv->last_multicast))
+ return 0;
+
+ if (srv->type == NAN_DE_PUBLISH && srv->publish.ttl == 0)
+ return -1;
+
+ if (srv->type == NAN_DE_PUBLISH &&
+ os_reltime_initialized(&srv->pause_state_end))
+ return -1;
+
+ period = srv->type == NAN_DE_PUBLISH ?
+ srv->publish.announcement_period :
+ srv->subscribe.query_period;
+ if (period == 0)
+ period = 100;
+ next = srv->last_multicast;
+ os_reltime_add_ms(&next, period);
+
+ if (srv->type == NAN_DE_PUBLISH) {
+ if (!de->tx_wait_end_freq && srv->publish.unsolicited &&
+ os_reltime_before(&next, now))
+ return 0;
+ next = srv->next_publish_state;
+ }
+
+ if (os_reltime_before(&next, now))
+ return 0;
+
+ os_reltime_sub(&next, now, &diff);
+ return os_reltime_in_ms(&diff);
+}
+
+
+static int nan_de_srv_time_to_next(struct nan_de *de,
+ struct nan_de_service *srv,
+ struct os_reltime *now)
+{
+ struct os_reltime diff;
+ int next = -1, tmp;
+
+ if (os_reltime_initialized(&srv->end_time)) {
+ os_reltime_sub(&srv->end_time, now, &diff);
+ tmp = os_reltime_in_ms(&diff);
+ if (next == -1 || tmp < next)
+ next = tmp;
+ }
+
+ tmp = nan_de_next_multicast(de, srv, now);
+ if (tmp >= 0 && (next == -1 || tmp < next))
+ next = tmp;
+
+ if (srv->type == NAN_DE_PUBLISH &&
+ os_reltime_initialized(&srv->last_multicast)) {
+ /* Time out after one transmission (and wait for FSD) */
+ tmp = srv->publish.fsd ? 1000 : 100;
+ if (next == -1 || tmp < next)
+ next = tmp;
+ }
+
+ if (srv->type == NAN_DE_SUBSCRIBE &&
+ os_reltime_initialized(&srv->first_discovered)) {
+ /* Time out after first DiscoveryResult event (and wait for
+ * FSD) */
+ tmp = srv->needs_fsd ? 1000 : 100;
+ if (next == -1 || tmp < next)
+ next = tmp;
+ }
+
+ if (os_reltime_initialized(&srv->next_publish_state)) {
+ os_reltime_sub(&srv->next_publish_state, now, &diff);
+ if (diff.sec < 0 || (diff.sec == 0 && diff.usec < 0))
+ tmp = 0;
+ else
+ tmp = os_reltime_in_ms(&diff);
+ if (next == -1 || tmp < next)
+ next = tmp;
+ }
+
+ return next;
+}
+
+
+static void nan_de_start_new_publish_state(struct nan_de_service *srv,
+ bool force_single)
+{
+ unsigned int n;
+
+ 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;
+ srv->next_publish_duration = n * 100;
+
+ nan_de_set_publish_times(srv);
+
+ if (os_reltime_initialized(&srv->pause_state_end))
+ return;
+
+ if (srv->in_multi_chan && srv->freq_list && srv->freq_list[0]) {
+ if (!srv->first_multi_chan)
+ srv->multi_chan_idx++;
+ if (srv->freq_list[srv->multi_chan_idx] == 0)
+ srv->multi_chan_idx = 0;
+ srv->first_multi_chan = false;
+ srv->freq = srv->freq_list[srv->multi_chan_idx];
+ } else {
+ srv->freq = srv->default_freq;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Publish in %s channel state for %u TU; starting with %u MHz",
+ srv->in_multi_chan ? "multi" : "single", n * 100, srv->freq);
+}
+
+
+static void nan_de_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct nan_de *de = eloop_ctx;
+ unsigned int i;
+ int next = -1;
+ bool started = false;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+
+ for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
+ struct nan_de_service *srv = de->service[i];
+ int srv_next;
+
+ if (!srv)
+ continue;
+
+ if (nan_de_srv_expired(srv, &now)) {
+ wpa_printf(MSG_DEBUG, "NAN: Service id %d expired",
+ srv->id);
+ nan_de_del_srv(de, srv, NAN_DE_REASON_TIMEOUT);
+ 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);
+
+ 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))))
+ 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 &&
+ de->listen_freq == 0 && de->ext_listen_freq == 0 &&
+ de->tx_wait_end_freq == 0 &&
+ nan_de_next_multicast(de, srv, &now) == 0) {
+ started = true;
+ nan_de_tx_multicast(de, srv, 0);
+ }
+
+ if (!started && 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;
+
+ 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;
+ }
+
+ started = true;
+ if (de->cb.listen(de->cb.ctx, srv->freq, duration) == 0)
+ de->listen_freq = srv->freq;
+ }
+
+ }
+
+ if (next < 0)
+ return;
+
+ 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);
+}
+
+
+static void nan_de_run_timer(struct nan_de *de)
+{
+ eloop_cancel_timeout(nan_de_timer, de, NULL);
+ eloop_register_timeout(0, 0, nan_de_timer, de, NULL);
+}
+
+
+void nan_de_deinit(struct nan_de *de)
+{
+ eloop_cancel_timeout(nan_de_timer, de, NULL);
+ nan_de_flush(de);
+ os_free(de);
+}
+
+
+void nan_de_listen_started(struct nan_de *de, unsigned int freq,
+ unsigned int duration)
+{
+ if (freq != de->listen_freq)
+ de->ext_listen_freq = freq;
+}
+
+
+void nan_de_listen_ended(struct nan_de *de, unsigned int freq)
+{
+ if (freq == de->ext_listen_freq)
+ de->ext_listen_freq = 0;
+
+ if (freq == de->listen_freq) {
+ de->listen_freq = 0;
+ nan_de_run_timer(de);
+ }
+}
+
+
+void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst)
+{
+ if (freq == de->tx_wait_status_freq)
+ de->tx_wait_status_freq = 0;
+}
+
+
+void nan_de_tx_wait_ended(struct nan_de *de)
+{
+ de->tx_wait_end_freq = 0;
+ nan_de_run_timer(de);
+}
+
+
+static const u8 *
+nan_de_get_attr(const u8 *buf, size_t len, enum nan_attr_id id,
+ unsigned int skip)
+{
+ const u8 *pos = buf, *end = buf + len;
+
+ while (end - pos >= NAN_ATTR_HDR_LEN) {
+ const u8 *attr = pos;
+ u8 attr_id;
+ u16 attr_len;
+
+ attr_id = *pos++;
+ attr_len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (attr_len > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Truncated attribute %u (len %u; left %zu)",
+ attr_id, attr_len, end - pos);
+ break;
+ }
+
+ if (attr_id == id) {
+ if (skip == 0)
+ return attr;
+ skip--;
+ }
+
+ pos += attr_len;
+ }
+
+ return NULL;
+}
+
+
+static void nan_de_get_sdea(const u8 *buf, size_t len, u8 instance_id,
+ u16 *sdea_control,
+ enum nan_service_protocol_type *srv_proto_type,
+ const u8 **ssi, size_t *ssi_len)
+{
+ unsigned int skip;
+ const u8 *sdea, *end;
+ u16 sdea_len;
+
+ for (skip = 0; ; skip++) {
+ sdea = nan_de_get_attr(buf, len, NAN_ATTR_SDEA, skip);
+ if (!sdea)
+ break;
+
+ sdea++;
+ sdea_len = WPA_GET_LE16(sdea);
+ sdea += 2;
+ if (sdea_len < 1 + 2)
+ continue;
+ end = sdea + sdea_len;
+
+ if (instance_id != *sdea++)
+ continue; /* Mismatching Instance ID */
+
+ *sdea_control = WPA_GET_LE16(sdea);
+ sdea += 2;
+
+ if (*sdea_control & NAN_SDEA_CTRL_RANGE_LIMIT) {
+ if (end - sdea < 4)
+ continue;
+ sdea += 4;
+ }
+
+ if (*sdea_control & NAN_SDEA_CTRL_SRV_UPD_INDIC) {
+ if (end - sdea < 1)
+ continue;
+ sdea++;
+ }
+
+ if (end - sdea >= 2) {
+ u16 srv_info_len;
+
+ srv_info_len = WPA_GET_LE16(sdea);
+ sdea += 2;
+
+ if (srv_info_len > end - sdea)
+ continue;
+
+ if (srv_info_len >= 4 &&
+ WPA_GET_BE24(sdea) == OUI_WFA) {
+ *srv_proto_type = sdea[3];
+ *ssi = sdea + 4;
+ *ssi_len = srv_info_len - 4;
+ }
+ }
+ }
+}
+
+
+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)
+{
+ /* 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 (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 (!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);
+ }
+
+ 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);
+}
+
+
+static bool nan_de_filter_match(struct nan_de_service *srv,
+ const u8 *matching_filter,
+ size_t matching_filter_len)
+{
+ const u8 *pos, *end;
+
+ /* Since we do not currently support matching_filter_rx values for the
+ * local Publish function, any matching filter with at least one
+ * <length,value> pair with length larger than zero implies a mismatch.
+ */
+
+ if (!matching_filter)
+ return true;
+
+ pos = matching_filter;
+ end = matching_filter + matching_filter_len;
+
+ while (pos < end) {
+ u8 len;
+
+ 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. */
+ 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,
+ const u8 *matching_filter,
+ size_t matching_filter_len,
+ enum nan_service_protocol_type srv_proto_type,
+ const u8 *ssi, size_t ssi_len)
+{
+ struct wpabuf *buf;
+ size_t len = 0, sda_len, sdea_len;
+ u8 ctrl = 0;
+ u16 sdea_ctrl = 0;
+
+ /* Publish function processing of a receive Subscribe message */
+
+ if (!nan_de_filter_match(srv, matching_filter, matching_filter_len))
+ return;
+
+ if (!srv->publish.solicited)
+ return;
+
+ 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;
+ }
+
+ /* 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;
+
+ /* Element Container attribute */
+ if (srv->elems)
+ len += NAN_ATTR_HDR_LEN + 1 + wpabuf_len(srv->elems);
+
+ buf = nan_de_alloc_sdf(len);
+ if (!buf)
+ return;
+
+ /* 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->publish.disable_events && de->cb.replied)
+ de->cb.replied(de->cb.ctx, srv->id, peer_addr, instance_id,
+ srv_proto_type, ssi, ssi_len);
+}
+
+
+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)
+{
+ /* Follow-up function processing of a receive Follow-up message for a
+ * Subscribe or Publish instance */
+
+ if (srv->type == NAN_DE_PUBLISH &&
+ os_reltime_initialized(&srv->pause_state_end) &&
+ (!ether_addr_equal(peer_addr, srv->sel_peer_addr) ||
+ instance_id != srv->sel_peer_id ||
+ !ssi)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: In pauseState - ignore Follow-up message from another subscriber or without ssi");
+ return;
+ }
+
+ os_get_reltime(&srv->last_followup);
+
+ if (srv->type == NAN_DE_PUBLISH && !ssi)
+ nan_de_pause_state(srv, peer_addr, instance_id);
+
+ if (de->cb.receive)
+ de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len,
+ peer_addr);
+}
+
+
+static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
+ unsigned int freq, const u8 *buf, size_t len,
+ const u8 *sda, size_t sda_len)
+{
+ const u8 *service_id;
+ u8 instance_id, req_instance_id, ctrl;
+ u16 sdea_control = 0;
+ unsigned int i;
+ enum nan_service_control_type type = 0;
+ enum nan_service_protocol_type srv_proto_type = 0;
+ const u8 *ssi = NULL;
+ size_t ssi_len = 0;
+ bool first = true;
+ const u8 *end;
+ const u8 *matching_filter = NULL;
+ size_t matching_filter_len = 0;
+
+ if (sda_len < NAN_SERVICE_ID_LEN + 1 + 1 + 1)
+ return;
+ end = sda + sda_len;
+
+ service_id = sda;
+ sda += NAN_SERVICE_ID_LEN;
+ instance_id = *sda++;
+ req_instance_id = *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)",
+ MAC2STR(service_id), instance_id, req_instance_id,
+ ctrl, type);
+ if (type != NAN_SRV_CTRL_PUBLISH &&
+ type != NAN_SRV_CTRL_SUBSCRIBE &&
+ type != NAN_SRV_CTRL_FOLLOW_UP) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Discard SDF with unknown Service Control Type %u",
+ type);
+ return;
+ }
+
+ if (ctrl & NAN_SRV_CTRL_BINDING_BITMAP) {
+ if (end - sda < 2)
+ return;
+ sda += 2;
+ }
+
+ if (ctrl & NAN_SRV_CTRL_MATCHING_FILTER) {
+ u8 flen;
+
+ if (end - sda < 1)
+ return;
+ flen = *sda++;
+ if (end - sda < flen)
+ return;
+ matching_filter = sda;
+ matching_filter_len = flen;
+ sda += flen;
+ }
+
+ if (ctrl & NAN_SRV_CTRL_RESP_FILTER) {
+ u8 flen;
+
+ if (end - sda < 1)
+ return;
+ flen = *sda++;
+ if (end - sda < flen)
+ return;
+ sda += flen;
+ }
+
+ if (ctrl & NAN_SRV_CTRL_SRV_INFO) {
+ u8 flen;
+
+ if (end - sda < 1)
+ return;
+ 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);
+ wpa_hexdump(MSG_MSGDUMP, "NAN: ssi", ssi, ssi_len);
+ }
+ sda += flen;
+ }
+
+ for (i = 0; i < NAN_DE_MAX_SERVICE; i++) {
+ struct nan_de_service *srv = de->service[i];
+
+ if (!srv)
+ continue;
+ if (os_memcmp(srv->service_id, service_id,
+ NAN_SERVICE_ID_LEN) != 0)
+ continue;
+ if (type == NAN_SRV_CTRL_PUBLISH) {
+ if (srv->type == NAN_DE_PUBLISH)
+ continue;
+ if (req_instance_id && srv->id != req_instance_id)
+ continue;
+ }
+ if (type == NAN_SRV_CTRL_SUBSCRIBE &&
+ srv->type == NAN_DE_SUBSCRIBE)
+ continue;
+ wpa_printf(MSG_DEBUG, "NAN: Received SDF matches service ID %u",
+ i + 1);
+
+ if (first) {
+ first = false;
+ nan_de_get_sdea(buf, len, instance_id, &sdea_control,
+ &srv_proto_type, &ssi, &ssi_len);
+
+ if (ssi) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Service Protocol Type %d",
+ srv_proto_type);
+ wpa_hexdump(MSG_MSGDUMP, "NAN: ssi",
+ ssi, ssi_len);
+ }
+ }
+
+ 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);
+ 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);
+ break;
+ case NAN_SRV_CTRL_FOLLOW_UP:
+ nan_de_rx_follow_up(de, srv, peer_addr, instance_id,
+ ssi, ssi_len);
+ break;
+ }
+ }
+}
+
+
+void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
+ const u8 *buf, size_t len)
+{
+ const u8 *sda;
+ u16 sda_len;
+ unsigned int skip;
+
+ if (!de->num_service)
+ return;
+
+ wpa_printf(MSG_DEBUG, "NAN: RX SDF from " MACSTR " freq=%u len=%zu",
+ MAC2STR(peer_addr), freq, len);
+
+ wpa_hexdump(MSG_MSGDUMP, "NAN: SDF payload", buf, len);
+
+ for (skip = 0; ; skip++) {
+ sda = nan_de_get_attr(buf, len, NAN_ATTR_SDA, skip);
+ if (!sda)
+ break;
+
+ sda++;
+ sda_len = WPA_GET_LE16(sda);
+ sda += 2;
+ nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len);
+ }
+}
+
+
+static int nan_de_get_handle(struct nan_de *de)
+{
+ int i = de->next_handle;
+
+ if (de->num_service >= NAN_DE_MAX_SERVICE)
+ goto fail;
+
+ do {
+ if (!de->service[i]) {
+ de->next_handle = (i + 1) % NAN_DE_MAX_SERVICE;
+ return i + 1;
+ }
+ i = (i + 1) % NAN_DE_MAX_SERVICE;
+ } while (i != de->next_handle);
+
+fail:
+ wpa_printf(MSG_DEBUG, "NAN: No more room for a new service");
+ return -1;
+}
+
+
+static int nan_de_derive_service_id(struct nan_de_service *srv)
+{
+ u8 hash[SHA256_MAC_LEN];
+ char *name, *pos;
+ int ret;
+ const u8 *addr[1];
+ size_t len[1];
+
+ name = os_strdup(srv->service_name);
+ if (!name)
+ return -1;
+ pos = name;
+ while (*pos) {
+ *pos = tolower(*pos);
+ pos++;
+ }
+
+ addr[0] = (u8 *) name;
+ len[0] = os_strlen(name);
+ ret = sha256_vector(1, addr, len, hash);
+ os_free(name);
+ if (ret == 0)
+ os_memcpy(srv->service_id, hash, NAN_SERVICE_ID_LEN);
+
+ return ret;
+}
+
+
+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)
+{
+ int publish_id;
+ struct nan_de_service *srv;
+
+ if (!service_name) {
+ wpa_printf(MSG_DEBUG, "NAN: Publish() - no service_name");
+ return -1;
+ }
+
+ if (!params->unsolicited && !params->solicited) {
+ wpa_printf(MSG_INFO,
+ "NAN: Publish() - both unsolicited and solicited disabled is invalid");
+ return -1;
+ }
+
+ publish_id = nan_de_get_handle(de);
+ if (publish_id < 1)
+ return -1;
+
+ srv = os_zalloc(sizeof(*srv));
+ if (!srv)
+ 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)
+ goto fail;
+ os_memcpy(&srv->publish, 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->publish.freq_list = NULL;
+
+ srv->srv_proto_type = srv_proto_type;
+ if (ssi) {
+ srv->ssi = wpabuf_dup(ssi);
+ if (!srv->ssi)
+ goto fail;
+ }
+ if (elems) {
+ srv->elems = wpabuf_dup(elems);
+ if (!srv->elems)
+ goto fail;
+ }
+
+ /* Prepare for single and multi-channel states; starting with
+ * single channel */
+ srv->first_multi_chan = true;
+ nan_de_start_new_publish_state(srv, true);
+
+ wpa_printf(MSG_DEBUG, "NAN: Assigned new publish handle %d for %s",
+ publish_id, service_name);
+ srv->id = publish_id;
+ nan_de_add_srv(de, srv);
+ nan_de_run_timer(de);
+ return publish_id;
+fail:
+ nan_de_service_free(srv);
+ return -1;
+}
+
+
+void nan_de_cancel_publish(struct nan_de *de, int publish_id)
+{
+ struct nan_de_service *srv;
+
+ wpa_printf(MSG_DEBUG, "NAN: CancelPublish(publish_id=%d)", publish_id);
+
+ if (publish_id < 1 || publish_id > NAN_DE_MAX_SERVICE)
+ return;
+ srv = de->service[publish_id - 1];
+ if (!srv || srv->type != NAN_DE_PUBLISH)
+ return;
+ nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST);
+}
+
+
+int nan_de_update_publish(struct nan_de *de, int publish_id,
+ const struct wpabuf *ssi)
+{
+ struct nan_de_service *srv;
+
+ wpa_printf(MSG_DEBUG, "NAN: UpdatePublish(publish_id=%d)", publish_id);
+
+ 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;
+
+ wpabuf_free(srv->ssi);
+ srv->ssi = NULL;
+ if (!ssi)
+ return 0;
+ srv->ssi = wpabuf_dup(ssi);
+ if (!srv->ssi)
+ return -1;
+ return 0;
+}
+
+
+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)
+{
+ int subscribe_id;
+ struct nan_de_service *srv;
+
+ if (!service_name) {
+ wpa_printf(MSG_DEBUG, "NAN: Subscribe() - no service_name");
+ return -1;
+ }
+
+ subscribe_id = nan_de_get_handle(de);
+ if (subscribe_id < 1)
+ return -1;
+
+ srv = os_zalloc(sizeof(*srv));
+ if (!srv)
+ 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)
+ goto fail;
+ os_memcpy(&srv->subscribe, params, sizeof(*params));
+ srv->srv_proto_type = srv_proto_type;
+ if (ssi) {
+ srv->ssi = wpabuf_dup(ssi);
+ if (!srv->ssi)
+ goto fail;
+ }
+ if (elems) {
+ srv->elems = wpabuf_dup(elems);
+ if (!srv->elems)
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NAN: Assigned new subscribe handle %d for %s",
+ subscribe_id, service_name);
+ srv->id = subscribe_id;
+ nan_de_add_srv(de, srv);
+ nan_de_run_timer(de);
+ return subscribe_id;
+fail:
+ nan_de_service_free(srv);
+ return -1;
+}
+
+
+void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id)
+{
+ struct nan_de_service *srv;
+
+ if (subscribe_id < 1 || subscribe_id > NAN_DE_MAX_SERVICE)
+ return;
+ srv = de->service[subscribe_id - 1];
+ if (!srv || srv->type != NAN_DE_SUBSCRIBE)
+ return;
+ nan_de_del_srv(de, srv, NAN_DE_REASON_USER_REQUEST);
+}
+
+
+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)
+{
+ struct nan_de_service *srv;
+
+ if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
+ return -1;
+
+ 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);
+ return 0;
+}
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
new file mode 100644
index 0000000..6223506
--- /dev/null
+++ b/src/common/nan_de.h
@@ -0,0 +1,145 @@
+/*
+ * NAN Discovery Engine
+ * 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_DE_H
+#define NAN_DE_H
+
+#include "nan.h"
+
+/* Maximum number of active local publish and subscribe instances */
+#ifndef NAN_DE_MAX_SERVICE
+#define NAN_DE_MAX_SERVICE 20
+#endif /* NAN_DE_MAX_SERVICE */
+
+struct nan_de;
+
+enum nan_de_reason {
+ NAN_DE_REASON_TIMEOUT,
+ NAN_DE_REASON_USER_REQUEST,
+ NAN_DE_REASON_FAILURE,
+};
+
+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);
+
+ /* 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 (*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);
+
+ void (*publish_terminated)(void *ctx, int publish_id,
+ enum nan_de_reason reason);
+
+ void (*subscribe_terminated)(void *ctx, int subscribe_id,
+ enum nan_de_reason reason);
+
+ void (*receive)(void *ctx, int id, int peer_instance_id,
+ const u8 *ssi, size_t ssi_len,
+ const u8 *peer_addr);
+};
+
+struct nan_de * nan_de_init(const u8 *nmi, bool ap,
+ const struct nan_callbacks *cb);
+void nan_de_flush(struct nan_de *de);
+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_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);
+
+struct nan_publish_params {
+ /* configuration_parameters */
+
+ /* Publish type */
+ bool unsolicited;
+ bool solicited;
+
+ /* Solicited transmission type */
+ bool solicited_multicast;
+
+ /* Time to live (in seconds); 0 = one TX only */
+ unsigned int ttl;
+
+ /* Event conditions */
+ bool disable_events;
+
+ /* Further Service Discovery flag */
+ bool fsd;
+
+ /* Further Service Discovery function */
+ bool fsd_gas;
+
+ /* Default frequency (defaultPublishChannel) */
+ unsigned int freq;
+
+ /* Multi-channel frequencies (publishChannelList) */
+ const int *freq_list;
+
+ /* Announcement period in ms; 0 = use default */
+ unsigned int announcement_period;
+};
+
+/* 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);
+
+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);
+
+struct nan_subscribe_params {
+ /* configuration_parameters */
+
+ /* Subscribe type */
+ bool active;
+
+ /* Time to live (in seconds); 0 = until first result */
+ unsigned int ttl;
+
+ /* Selected frequency */
+ unsigned int freq;
+
+ /* Query period in ms; 0 = use default */
+ unsigned int query_period;
+};
+
+/* 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);
+
+void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id);
+
+/* handle = publish_id or 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);
+
+#endif /* NAN_DE_H */
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index 3b5c0b8..918a1cc 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -19,6 +19,8 @@
unsigned int n_ptksa;
};
+#ifdef CONFIG_PTKSA_CACHE
+
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
@@ -141,7 +143,7 @@
return NULL;
dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
- if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+ if ((!addr || ether_addr_equal(e->addr, addr)) &&
(cipher == WPA_CIPHER_NONE || cipher == e->cipher))
return e;
}
@@ -238,7 +240,7 @@
dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
list) {
- if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+ if ((!addr || ether_addr_equal(e->addr, addr)) &&
(cipher == WPA_CIPHER_NONE || cipher == e->cipher)) {
wpa_printf(MSG_DEBUG,
"Flush PTKSA cache entry for " MACSTR,
@@ -342,3 +344,44 @@
return entry;
}
+
+#else /* CONFIG_PTKSA_CACHE */
+
+struct ptksa_cache * ptksa_cache_init(void)
+{
+ return (struct ptksa_cache *) 1;
+}
+
+
+void ptksa_cache_deinit(struct ptksa_cache *ptksa)
+{
+}
+
+
+struct ptksa_cache_entry *
+ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+ return NULL;
+}
+
+
+int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+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)
+{
+ return NULL;
+}
+
+
+void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+}
+
+#endif /* CONFIG_PTKSA_CACHE */
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index 6182215..dd5e7db 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -29,7 +29,6 @@
u32 akmp;
};
-#ifdef CONFIG_PTKSA_CACHE
struct ptksa_cache;
@@ -48,41 +47,4 @@
void *ctx, u32 akmp);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
-#else /* CONFIG_PTKSA_CACHE */
-
-static inline struct ptksa_cache * ptksa_cache_init(void)
-{
- return (struct ptksa_cache *) 1;
-}
-
-static inline void ptksa_cache_deinit(struct ptksa_cache *ptksa)
-{
-}
-
-static inline struct ptksa_cache_entry *
-ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
-{
- return NULL;
-}
-
-static inline int ptksa_cache_list(struct ptksa_cache *ptksa,
- char *buf, size_t len)
-{
- return -1;
-}
-
-static inline 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)
-{
- return NULL;
-}
-
-static inline void ptksa_cache_flush(struct ptksa_cache *ptksa,
- const u8 *addr, u32 cipher)
-{
-}
-
-#endif /* CONFIG_PTKSA_CACHE */
#endif /* PTKSA_CACHE_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 7620f03..5dab120 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -32,6 +32,119 @@
};
/**
+ * DOC: TX/RX NSS and chain configurations
+ * This document describes all of the attributes used in the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION to configure the number of
+ * spatial streams (NSS) and the number of chains used for transmitting (TX) and
+ * receiving (RX) the data.
+ *
+ * Global NSS configuration - Applies to all bands (2.4 GHz and 5/6 GHz)
+ * The following attributes are used to dynamically configure the number of
+ * spatial streams to be used for transmitting or receiving the data in the
+ * 2.4 GHz and 5/6 GHz bands. When configured in disconnected state, the
+ * updated configuration will be considered for the immediately following
+ * connection attempt. If the NSS is updated during a connection, the updated
+ * NSS value is notified to the peer using operating mode notification/spatial
+ * multiplexing power save frame. The updated NSS value after the connection
+ * shall not be greater than the one negotiated during the connection. The
+ * driver rejects any such higher value configuration with a failure.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NSS: Only symmetric NSS configuration
+ * (such as 2X2 or 1X1) can be done using this attribute.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS: Configure NSS for transmitting the data
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS: Configure NSS for receiving the data
+ *
+ * The QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS
+ * attributes must be defined together or the driver will reject the command
+ * with a failure. They can be used to configure either symmetric NSS
+ * configuration (such as 2X2 or 1X1) or asymmetric configuration (such as 1X2).
+ *
+ * Per band NSS configuration - Applies to the 2.4 GHz or 5/6 GHz band
+ * The following attributes are used to dynamically configure the number of
+ * spatial streams to be used for transmitting or receiving the data in the
+ * 2.4 GHz band or 5/6 GHz band. All these attributes must be defined together
+ * to configure symmetric NSS configuration (such as 1X1 or 2X2) or asymmetric
+ * NSS configuration (such as 1X2). If any of the attributes is missing, the
+ * driver will reject the command with a failure. This configuration is allowed
+ * only when in connected state and will be effective until disconnected. The
+ * NSS value configured after the connection shall not be greater than the value
+ * negotiated during the connection. Any such higher value configuration shall
+ * be treated as invalid configuration by the driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_2GHZ: Configure TX_NSS in 2.4 GHz band
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_2GHZ: Configure RX_NSS in 2.4 GHz band
+ * @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
+ *
+ * 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
+ * 5/6 GHz bands. This configuration is allowed only when in connected state
+ * and will be effective until disconnected. The driver rejects this
+ * configuration if the number of spatial streams being used in the current
+ * connection cannot be supported by this configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS: The number of chains to be used
+ * for transmitting the data in both the 2.4 GHz and 5/6 GHz bands.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS: The number of chains to be used
+ * for receiving the data in both the 2.4 GHz and 5/6 GHz bands.
+ *
+ * The attributes QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS and
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS must be defined together or the
+ * driver will reject the command with a failure.
+ *
+ * Per band chain configuration - Applies to the 2.4 GHz or 5/6 GHz band
+ * The following band specific attributes are used to dynamically configure the
+ * number of chains to be used for tranmissting or receiving the data in the
+ * 2.4 GHz or 5/6 GHz band. These attributes must be defined together or the
+ * driver will reject the command. This configuration is allowed only when in
+ * connected state and will be effective until disconnected. The driver rejects
+ * this configuration if the number of spatial streams being used in the
+ * current connection cannot be supported by this configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_2GHZ: The number of chains to be
+ * used for transmitting the data in the 2.4 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_2GHZ: The number of chains to be
+ * used for receiving the data in the 2.4 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_5GHZ: The number of chains to be
+ * used for transmitting the data in the 5/6 GHz band.
+ * @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.
+ *
+ * The following scenarios capture how the driver process the configuration when
+ * different TX/RX NSS and chain config attributes are used in the command.
+ *
+ * Case 1: CONFIG_NSS + CONFIG_TX_NSS/RX_NSS - Only CONFIG_NSS is applied
+ * since only one of the TX_NSS or RX_NSS attribute is present.
+ *
+ * Case 2: CONFIG_NSS + CONFIG_TX_NSS + CONFIG_RX_NSS - Same NSS values are
+ * used to configure TX,RX in both the 2.4 GHz and 5/6 GHz bands.
+ *
+ * Case 3: Case 2 + NUM_TX_CHAINS + NUM_RX_CHAINS - The NSS and the number of
+ * chains values are used to configure TX,RX in both the 2.4 GHz and 5/6 GHz
+ * bands.
+ *
+ * Case 4: TX_NSS_2GHZ/TX_NSS_5GHZ + RX_NSS_2GHZ/RX_NSS_5GHZ - Since per band
+ * TX/RX NSS attribute is missing, the driver rejects the command.
+ *
+ * Case 5: TX_NSS_2GHZ + TX_NSS_5GHZ + RX_NSS_2GHZ + RX_NSS_5GHZ - The 2.4 GHz
+ * band is configured with the TX_NSS_2GHZ, RX_NSS_2GHZ values. The 5/6 GHz band
+ * is configured with the TX_NSS_5GHZ, RX_NSS_5GHZ values.
+ *
+ * Case 6: TX_CHAINS_2GHZ/TX_CHAINS_5GHZ + RX_CHAINS_5GHZ/RX_CHAINS_5GHZ - Since
+ * per band TX/RX chains attribute is missing, the driver rejects the command.
+ *
+ * Case 7: TX_CHAINS_2GHZ + TX_CHAINS_5GHZ + RX_CHAINS_5GHZ + RX_CHAINS_5GHZ -
+ * The 2.4 GHz band is configured with the TX_CHAINS_2GHZ, RX_CHAINS_2GHZ
+ * values. The 5/6 GHz band is configured with the TX_CHAINS_5GHZ,
+ * RX_CHAINS_5GHZ values.
+ *
+ * Case 8: Case 5 + Case 7 - Per band TX,RX NSS and chains are configured.
+ *
+ * Case 9: Case 2 + Case 8 - Per band TX,RX NSS and chains are configured.
+ */
+
+/**
* enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
*
* @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
@@ -1024,6 +1137,106 @@
*
* The attributes used with this subcommand are defined in
* enum qca_wlan_vendor_attr_tx_latency.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_REGULATORY_TPC_INFO: Vendor command is used to
+ * query transmit power information on STA interface from the driver for a
+ * connected AP. The attributes included in response are defined in
+ * enum qca_wlan_vendor_attr_tpc_links. In case of MLO STA, multiple links
+ * TPC info may be returned. The information includes regulatory maximum
+ * transmit power limit, AP local power constraint advertised from AP's
+ * Beacon and Probe Response frames. For PSD power mode, the information
+ * includes PSD power levels for each subchannel of operating bandwidth.
+ * The information is driver calculated power limits based on the current
+ * regulatory domain, AP local power constraint, and other IEs. The
+ * information will be set to target. Target will decide the final TX power
+ * based on this and chip specific power conformance test limits (CTL), and
+ * SAR limits.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT: Event indication from the
+ * driver to user space which is carrying firmware page fault related
+ * summary report. The attributes for this command are defined in
+ * enum qca_wlan_vendor_attr_fw_page_fault_report.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DISASSOC_PEER: Event indication from the driver
+ * to user space to disassociate with a peer based on the peer MAC address
+ * provided. Specify the peer MAC address in
+ * QCA_WLAN_VENDOR_ATTR_MAC_ADDR. For MLO, MLD MAC address is provided.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER: This vendor command is used to
+ * adjust transmit power. The attributes used with this subcommand are
+ * defined in enum qca_wlan_vendor_attr_adjust_tx_power.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE: Event indication from the
+ * driver to notify user application about the spectral scan completion.
+ * The attributes used with this subcommand are defined in
+ * enum qca_wlan_vendor_attr_spectral_scan_complete.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION: Register for receiving
+ * %NL80211_CMD_GET_STATION responses as unicast events when there are
+ * %NL80211_CMD_GET_STATION requests from any userspace module on the same
+ * interface index with which this command is sent. This command is also
+ * used as the unicast event to indicate the %NL80211_CMD_GET_STATION
+ * response. The attributes for this command are defined in
+ * enum qca_wlan_vendor_async_get_station_attr.
+ *
+ * The driver will send the unicast events with same netlink port ID which
+ * is used by userspace application for sending the registration command.
+ * If multiple registration commands are received with different netlink
+ * port IDs, the driver will send unicast events with each netlink port ID
+ * separately.
+ *
+ * Userspace applications can deregister the unicast event reporting with
+ * disable configuration. The registrations will be removed automatically
+ * by the driver when the corresponding netlink socket is closed.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND: Vendor command to put an AP interface
+ * in suspend state. On enabling suspend, AP deauthenticates all associated
+ * stations and stops TX/RX operations on the interface. The driver
+ * retains the AP configuration and on resume, starts all TX/RX operations
+ * with the same AP configuration.
+ *
+ * This subcommand is also used as an event to notify userspace about AP
+ * suspended/resumed state changes.
+ *
+ * The attributes used with this command/event are defined in enum
+ * qca_wlan_vendor_attr_ap_suspend.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS: Event indication from the driver to
+ * the userspace which contains all the statistics collected for a flow to
+ * be classified. This event is sent if the userspace enables the
+ * flow stats reporting via the command
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY and when the driver has
+ * collected the required flow statistics, as specified by the attributes
+ * of this event. The attributes for this event are defined in
+ * enum qca_wlan_vendor_attr_flow_stats.
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT: This vendor command is used
+ * to indicate the flow classification result based on the flow samples
+ * received as a part of @QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS. The
+ * attributes for this command are defined in the
+ * enum qca_wlan_vendor_attr_flow_classify_result.
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY: This vendor command is used to
+ * indicate the ASYNC statistics policy from the userspace to the driver
+ * and it contains the STATS type for which the command is intended. The
+ * attributes for this command are defined in the
+ * enum qca_wlan_vendor_attr_async_stats_policy.
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT: Event indication from the
+ * driver to the userspace containing all the samples of a classified
+ * flow along with its classification result. This event is sent by the
+ * driver to userspace when it receives classification result via the
+ * command @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT and the
+ * 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.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1242,6 +1455,21 @@
/* 232 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY = 233,
/* 234 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_SDWF_PHY_OPS = 235,
+ QCA_NL80211_VENDOR_SUBCMD_SDWF_DEV_OPS = 236,
+ QCA_NL80211_VENDOR_SUBCMD_REGULATORY_TPC_INFO = 237,
+ QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT = 238,
+ QCA_NL80211_VENDOR_SUBCMD_FLOW_POLICY = 239,
+ QCA_NL80211_VENDOR_SUBCMD_DISASSOC_PEER = 240,
+ QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER = 241,
+ QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE = 242,
+ QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION = 243,
+ QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND = 244,
+ QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS = 245,
+ 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,
};
/* Compatibility defines for previously used subcmd names.
@@ -1476,9 +1704,23 @@
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
+/**
+ * enum qca_roaming_policy - Represents the policies for roaming. Used by
+ * QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY.
+ *
+ * QCA_ROAMING_NOT_ALLOWED: Roaming is not allowed/disabled.
+ *
+ * QCA_ROAMING_ALLOWED_WITHIN_ESS: Roaming is allowed with in an ESS with
+ * default RSSI thresholds.
+ *
+ * QCA_ROAMING_MODE_AGGRESSIVE: This mode is an extension of
+ * QCA_ROAMING_ALLOWED_WITHIN_ESS. The driver/firmware roams on higher RSSI
+ * thresholds when compared to QCA_ROAMING_ALLOWED_WITHIN_ESS.
+ */
enum qca_roaming_policy {
QCA_ROAMING_NOT_ALLOWED,
QCA_ROAMING_ALLOWED_WITHIN_ESS,
+ QCA_ROAMING_MODE_AGGRESSIVE,
};
/**
@@ -1795,6 +2037,11 @@
* scoring. In case scan was performed on a partial set of channels configured
* with this command within last QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME
* (in ms), scan only the remaining channels.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID: Mandatory on AP MLD (u8).
+ * Used with command to configure ACS operation for a specific link affiliated
+ * to an AP MLD.
+ *
*/
enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -1818,6 +2065,7 @@
QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP = 18,
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,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -1941,6 +2189,36 @@
* with %QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST.
* @QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN: Flag indicates
* 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.
+ *
+ * For SME offload to the driver case:
+ * - Supplicant should enable RSNO element use only when the driver
+ * indicates this feature flag.
+ * - The driver should enable RSNO element use with the supplicant selected
+ * BSS only when the supplicant sends an RSNO element with an empty
+ * payload in the connect request elements buffer in NL80211_CMD_CONNECT.
+ *
+ * For BSS selection offload to the driver case:
+ * - Supplicant should enable RSNO element use only when the driver
+ * indicates this feature flag.
+ * - Supplicant should always send RSNO elements in the connect request
+ * elements buffer in NL80211_CMD_CONNECT irrespective of whether RSNO
+ * elements are supported by the BSS that the supplicant selected
+ * - The driver should enable RSNO element use only when the supplicant
+ * sends an RSNO element with an empty payload in connect request
+ * elements in NL80211_CMD_CONNECT.
+ * - The driver should remove RSNO elements from the connect request
+ * elements while preparing the (Re)Association Request frame elements
+ * if the driver selects a different BSS which is not advertising RSNO
+ * elements.
+ *
+ * If both SME and BSS selection offload to the driver, BSS selection
+ * offload to the driver case rules shall be applied.
+ *
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -1968,6 +2246,8 @@
QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP = 21,
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,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -2179,7 +2459,8 @@
* &enum qca_tsf_cmd.
* @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Optional (u64)
* This attribute contains TSF timer value. This attribute is only available
- * in %QCA_TSF_GET or %QCA_TSF_SYNC_GET response.
+ * in %QCA_TSF_GET, %QCA_TSF_SYNC_GET or %QCA_TSF_SYNC_GET_CSA_TIMESTAMP
+ * response.
* @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Optional (u64)
* This attribute contains SOC timer value at TSF capture. This attribute is
* only available in %QCA_TSF_GET or %QCA_TSF_SYNC_GET response.
@@ -2209,13 +2490,15 @@
* @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
* @QCA_TSF_AUTO_REPORT_ENABLE: Used in STA mode only. Once set, the target
* will automatically send TSF report to the host. To query
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be
- * initiated first.
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY or
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER, this operation needs
+ * to be initiated first.
* @QCA_TSF_AUTO_REPORT_DISABLE: Used in STA mode only. Once set, the target
* will not automatically send TSF report to the host. If
* %QCA_TSF_AUTO_REPORT_ENABLE is initiated and
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this
- * operation needs to be initiated.
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY or
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER is not queried
+ * anymore, this operation needs to be initiated.
* @QCA_TSF_SYNC_START: Start periodic TSF sync feature. The driver periodically
* fetches TSF and host time mapping from the firmware with interval configured
* through the %QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL attribute. If the
@@ -2223,6 +2506,12 @@
* userspace can query the TSF and host time mapping via the %QCA_TSF_GET
* command.
* @QCA_TSF_SYNC_STOP: Stop periodic TSF sync feature.
+ * @QCA_TSF_SYNC_GET_CSA_TIMESTAMP: Get TSF timestamp when AP will move and
+ * starts beaconing on a new channel. The driver synchronously responds with the
+ * TSF value using attribute %QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE. Userspace
+ * gets the valid CSA TSF after receiving %NL80211_CMD_CH_SWITCH_STARTED_NOTIFY
+ * on the AP interface. This TSF can be sent via OOB mechanism to connected
+ * clients.
*/
enum qca_tsf_cmd {
QCA_TSF_CAPTURE,
@@ -2232,6 +2521,7 @@
QCA_TSF_AUTO_REPORT_DISABLE,
QCA_TSF_SYNC_START,
QCA_TSF_SYNC_STOP,
+ QCA_TSF_SYNC_GET_CSA_TIMESTAMP,
};
/**
@@ -2373,6 +2663,10 @@
* QCA_WLAN_VENDOR_SCAN_PRIORITY_HIGH as the priority of vendor scan.
* @QCA_WLAN_VENDOR_ATTR_SCAN_PAD: Attribute used for padding for 64-bit
* alignment.
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID: This u8 attribute is used for OBSS scan
+ * 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.
*/
enum qca_wlan_vendor_attr_scan {
QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
@@ -2390,6 +2684,7 @@
QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME = 12,
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_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
@@ -2850,21 +3145,9 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of spatial streams. When configured in the disconnected
- * state, the updated configuration will be considered for the
- * immediately following connection attempt. If the NSS is updated after
- * the connection, the updated NSS value is notified to the peer using
- * the Operating Mode Notification/Spatial Multiplexing Power Save
- * frame. The updated NSS value after the connection shall not be
- * greater than the one negotiated during the connection. Any such
- * higher value configuration shall be returned with a failure.
- * Only symmetric NSS configuration (such as 2X2 or 1X1) can be done
- * using this attribute. QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and
- * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attributes shall be used to
- * configure the asymmetric NSS configuration (such as 1X2).
- */
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
+
/* 8-bit unsigned value to configure Optimized Power Management mode:
* Modes are defined by enum qca_wlan_vendor_opm_mode.
*
@@ -2896,21 +3179,10 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of chains to be used for transmitting data. This
- * configuration is allowed only when in connected state and will be
- * effective until disconnected. The driver rejects this configuration
- * if the number of spatial streams being used in the current connection
- * cannot be supported by this configuration.
- */
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of chains to be used for receiving data. This
- * configuration is allowed only when in connected state and will be
- * effective until disconnected. The driver rejects this configuration
- * if the number of spatial streams being used in the current connection
- * cannot be supported by this configuration.
- */
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
/* 8-bit unsigned value to configure ANI setting type.
@@ -3302,6 +3574,118 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_COEX_TRAFFIC_SHAPING_MODE = 105,
+ /* 8-bit unsigned value.
+ *
+ * This attribute is used to specify whether an associated peer is a QCA
+ * device. The associated peer is specified with
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_PEER_MAC. For MLO cases, the MLD MAC
+ * address of the peer is used.
+ * 1 - QCA device, 0 - non-QCA device.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_QCA_PEER = 106,
+
+ /* 8-bit unsigned value to configure BTM support.
+ *
+ * The attribute is applicable only for STA interface. Uses enum
+ * qca_wlan_btm_support values. This configuration is not allowed in
+ * connected state.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_BTM_SUPPORT = 107,
+
+ /* 16-bit unsigned value to configure client's keep-alive interval in
+ * seconds. The driver will reduce the keep-alive interval to this
+ * configured value if the AP advertises BSS maximum idle period and if
+ * that BSS max idle period is larger than this configured value. If the
+ * AP does not advertise a maximum value, the configured value will be
+ * used as a keep-alive period for unprotected frames.
+ *
+ * This configuration is applicable only during the STA's current
+ * association.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_KEEP_ALIVE_INTERVAL = 108,
+
+ /* 8-bit unsigned value to configure reduced power scan mode.
+ *
+ * This attribute is used to configure the driver to optimize power
+ * during scan. For example, the driver can switch to 1x1 from 2x2 mode
+ * for additional power save.
+ *
+ * 1 - Enable reduced power scan mode.
+ * 0 - Disable reduced power scan mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_REDUCED_POWER_SCAN_MODE = 109,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_2GHZ = 110,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_2GHZ = 111,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_5GHZ = 112,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_5GHZ = 113,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_2GHZ = 114,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_2GHZ = 115,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_5GHZ = 116,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_5GHZ = 117,
+
+ /* 16-bit unsigned value. This attribute is used to dynamically
+ * configure the time duration of data stall detection. Unit is
+ * milliseconds. Valid value range is 0 or 10 ms to 10000 ms. If the
+ * value is 0, the previously configured value is cleared. The driver
+ * rejects this configuration if the value is out of range. This
+ * configuration is effective for all connections on the chip. If the
+ * duration is greater than this configuration and consecutive TX no ack
+ * count is greater than
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_THRESHOLD,
+ * data stall event is sent to userspace.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_DURATION = 118,
+
+ /* 16-bit unsigned value. This attribute is used to dynamically
+ * configure the threshold of data stall detection. Valid value is 0 or
+ * greater than 10. if the value is 0, the previously configured value
+ * is cleared. The driver rejects this configuration if the value is out
+ * of range. This configuration is effective for all connections on the
+ * chip. If consecutive TX no ack count is greater than this
+ * configuration and duration is greater than
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_DURATION,
+ * data stall event is sent to userspace.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_THRESHOLD = 119,
+
+ /* 8-bit unsigned value to configure the interface offload type
+ *
+ * This attribute is used to configure the interface offload capability.
+ * User can configure software based acceleration, hardware based
+ * acceleration, or a combination of both using this option. More
+ * details on each option is described under the enum definition below.
+ * Uses enum qca_wlan_intf_offload_type for values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_INTF_OFFLOAD_TYPE = 120,
+
+ /* 8-bit unsigned integer to configure the driver to follow AP's
+ * preference values to select a roam candidate from BTM request.
+ *
+ * This attribute is used to configure the driver to select the roam
+ * candidate based on AP advertised preference values. If not set,
+ * the driver uses its internal scoring algorithm to do the same.
+ *
+ * 1 - STA follows AP's preference values to select a roam candidate
+ * 0 - STA uses internal scoring algorithm to select a roam candidate
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_FOLLOW_AP_PREFERENCE_FOR_CNDS_SELECT = 121,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -7180,6 +7564,9 @@
* for EHT (IEEE 802.11be). Encoding for this attribute follows the
* convention used in the Disabled Subchannel Bitmap field of the EHT Operation
* element.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_LINK_ID: Mandatory on AP MLD (u8).
+ * Used with command to configure external ACS operation for a specific link
+ * affiliated to an AP MLD.
*/
enum qca_wlan_vendor_attr_external_acs_channels {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -7216,6 +7603,7 @@
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP = 14,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_LINK_ID = 15,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
@@ -7690,6 +8078,20 @@
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_RECAPTURE = 31,
/* Attribute used for padding for 64-bit alignment */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PAD = 32,
+ /* Spectral data transport mode. u32 attribute. It uses values
+ * defined in enum qca_wlan_vendor_spectral_data_transport_mode.
+ * This is an optional attribute. If this attribute is not populated,
+ * the driver should configure the default transport mode to netlink.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_DATA_TRANSPORT_MODE = 33,
+ /* Spectral scan completion timeout. u32 attribute. This
+ * attribute is used to configure a timeout value (in us). The
+ * timeout value would be from the beginning of a spectral
+ * scan. This is an optional attribute. If this attribute is
+ * not populated, the driver would internally derive the
+ * timeout value.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETION_TIMEOUT = 34,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
@@ -9989,6 +10391,13 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MLD_ID_ML_PROBE_REQ = 72,
+ /* 8-bit unsigned value to configure the SCS traffic description
+ * support in the EHT capabilities of an Association Request frame.
+ * 1-enable, 0-disable
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_SCS_TRAFFIC_SUPPORT = 73,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -10489,10 +10898,13 @@
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE: Optional (u8)
* This attribute contains the value of the Responder PM Mode subfield (0 or 1)
- * from TWT response frame.
+ * from TWT response frame. During TWT setup request, this attribute is used to
+ * configure the Responder PM Mode bit in the control field of the TWT element
+ * for broadcast TWT schedule.
* This parameter is used for
* 1. TWT SET Response
* 2. TWT GET Response
+ * 3. TWT SET Request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT: Optional (u32)
* This attribute is used to configure the announce timeout value (in us) in
@@ -10902,10 +11314,18 @@
* This attribute configures AC parameters to be used for all TWT
* sessions in AP mode.
* Uses the enum qca_wlan_ac_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_UNAVAILABILITY_MODE: Flag attribute,
+ * used by TWT responder to indicate unavailability outside of the SPs.
+ * Enable (flag attribute present) - Indicates that the TWT responder may be
+ * unavailable outside of the SPs of its broadcast TWT schedule.
+ * Disable (flag attribute not present) - Indicates that the responder will be
+ * available for all TWT sessions (including individual TWT).
*/
enum qca_wlan_vendor_attr_twt_set_param {
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AP_AC_VALUE = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_UNAVAILABILITY_MODE = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AFTER_LAST,
@@ -11989,6 +12409,14 @@
* %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM or based on
* regulatory/SAE limits if %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM
* is not provided.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFINDEX: u32 attribute, optional.
+ * This specifies the interface index (netdev) for which the corresponding
+ * configurations are applied. If the interface index is not specified, the
+ * configurations are applied based on
+ * %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK.
+ * %QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK along with this
+ * attribute shall have the matching nl80211_iftype.
*/
enum qca_wlan_vendor_attr_avoid_frequency_ext {
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_INVALID = 0,
@@ -11997,6 +12425,7 @@
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END = 3,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_POWER_CAP_DBM = 4,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFACES_BITMASK = 5,
+ QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_IFINDEX = 6,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX =
@@ -12400,6 +12829,16 @@
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD: Attribute used for padding for
* 64-bit alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER: u32, used in STA mode
+ * only. This represents the average of the delta between successive uplink
+ * frames congestion duration in MAC queue in unit of ms. This can be queried
+ * either in connected state or disconnected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT: Array of nested attributes,
+ * 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.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -12456,6 +12895,8 @@
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS = 51,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS = 52,
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,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -14169,12 +14610,14 @@
* @QCA_WLAN_RATEMASK_PARAMS_TYPE_HT: HT rate mask config
* @QCA_WLAN_RATEMASK_PARAMS_TYPE_VHT: VHT rate mask config
* @QCA_WLAN_RATEMASK_PARAMS_TYPE_HE: HE rate mask config
+ * @QCA_WLAN_RATEMASK_PARAMS_TYPE_EHT: EHT rate mask config
*/
enum qca_wlan_ratemask_params_type {
QCA_WLAN_RATEMASK_PARAMS_TYPE_CCK_OFDM = 0,
QCA_WLAN_RATEMASK_PARAMS_TYPE_HT = 1,
QCA_WLAN_RATEMASK_PARAMS_TYPE_VHT = 2,
QCA_WLAN_RATEMASK_PARAMS_TYPE_HE = 3,
+ QCA_WLAN_RATEMASK_PARAMS_TYPE_EHT = 4,
};
/**
@@ -14195,7 +14638,11 @@
* @QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP: binary, rate mask bitmap.
* A bit value of 1 represents rate is enabled and a value of 0
* represents rate is disabled.
- * For HE targets, 12 bits correspond to one NSS setting.
+ * For EHT targets,
+ * b0-1 => NSS1, MCS 14-15
+ * b2-15 => NSS1, MCS 0-13
+ * b16-29 => NSS2, MCS 0-13
+ * For HE targets, 14 bits correspond to one NSS setting.
* b0-13 => NSS1, MCS 0-13
* b14-27 => NSS2, MCS 0-13 and so on for other NSS.
* For VHT targets, 10 bits correspond to one NSS setting.
@@ -14205,12 +14652,18 @@
* b0-7 => NSS1, MCS 0-7
* b8-15 => NSS2, MCS 0-7 and so on for other NSS.
* For OFDM/CCK targets, 8 bits correspond to one NSS setting.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RATEMASK_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, ratemask will be applied for setup link.
*/
enum qca_wlan_vendor_attr_ratemask_params {
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST = 1,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE = 2,
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP = 3,
+ QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LINK_ID = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_AFTER_LAST,
@@ -14305,11 +14758,14 @@
* details for each peer and used in both an event and a command response.
* The nested attributes used inside QCA_WLAN_VENDOR_ATTR_PASN_PEERS are
* defined in enum qca_wlan_vendor_attr_pasn_peer.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_LINK_ID: u8 attribute used to identify a
+ * specific link affiliated to an MLD.
*/
enum qca_wlan_vendor_attr_pasn {
QCA_WLAN_VENDOR_ATTR_PASN_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_PASN_ACTION = 1,
QCA_WLAN_VENDOR_ATTR_PASN_PEERS = 2,
+ QCA_WLAN_VENDOR_ATTR_PASN_LINK_ID = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PASN_AFTER_LAST,
@@ -14369,7 +14825,8 @@
* attribute, holds the LTF keyseed derived from KDK of PASN handshake.
* The length of this attribute is dependent on the value of
* %QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE.
-
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LINK_ID: This u8 attribute is used
+ * for secure ranging to identify a specific link affiliated to an AP MLD.
*/
enum qca_wlan_vendor_attr_secure_ranging_ctx {
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_INVALID = 0,
@@ -14380,6 +14837,7 @@
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK = 5,
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER = 6,
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED = 7,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LINK_ID = 8,
/* keep last */
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_AFTER_LAST,
@@ -14969,12 +15427,25 @@
* CTL group but user can choose up to 3 SAR set index only, as the top half
* of the SAR index (0 to 2) is used for non DBS purpose and the bottom half of
* the SAR index (3 to 5) is used for DBS mode.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_4: The firmware supports SAR version 4,
+ * known as SAR Smart Transmit (STX) mode. STX is time averaging algorithmic
+ * for power limit computation in collaboration with WWAN.
+ * In STX mode, firmware has 41 indexes and there is no ctl grouping uses.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_5: The firmware supports SAR version 5,
+ * known as TAS (Time Averaging SAR) mode. In TAS mode, as name implies
+ * instead of fixed static SAR power limit firmware uses time averaging
+ * to adjust the SAR limit dynamically. It is wlan soc standalone mechanism.
+ * In this mode firmware has up to 43 indexes.
*/
enum qca_wlan_vendor_sar_version {
QCA_WLAN_VENDOR_SAR_VERSION_INVALID = 0,
QCA_WLAN_VENDOR_SAR_VERSION_1 = 1,
QCA_WLAN_VENDOR_SAR_VERSION_2 = 2,
QCA_WLAN_VENDOR_SAR_VERSION_3 = 3,
+ QCA_WLAN_VENDOR_SAR_VERSION_4 = 4,
+ QCA_WLAN_VENDOR_SAR_VERSION_5 = 5,
};
/**
@@ -16602,4 +17073,978 @@
QCA_CHAN_WIDTH_UPDATE_TYPE_TX_RX_EXT = 2,
};
+/**
+ * enum qca_wlan_vendor_attr_tpc_pwr_level - Definition of attributes
+ * used inside nested attribute %QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY: u32 channel center
+ * frequency (MHz): If PSD power, carries one 20 MHz sub-channel center
+ * frequency. If non PSD power, carries either 20 MHz bandwidth's center
+ * channel frequency or 40 MHz bandwidth's center channel frequency
+ * (or 80/160 MHz bandwidth's center channel frequency).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER: s8 transmit power limit (dBm).
+ * If PSD power, carries PSD power value of the
+ * QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY mentioned sub-channel.
+ * If non PSD power, carries EIRP power value of bandwidth mentioned
+ * by QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY center frequency.
+ */
+enum qca_wlan_vendor_attr_tpc_pwr_level {
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_FREQUENCY = 1,
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_TX_POWER = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_MAX =
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_tpc - Definition of link attributes
+ * used inside nested attribute %QCA_WLAN_VENDOR_ATTR_TPC_LINKS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_BSSID: 6-bytes AP BSSID.
+ * For MLO STA, AP BSSID indicates the AP's link address.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER: PSD power flag
+ * Indicates using PSD power mode if this flag is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER: s8 Regulatory EIRP power
+ * value in dBm
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ: u8 power type of 6 GHz
+ * AP, refer to Table E-12-Regulatory Info subfield encoding in
+ * IEEE P802.11-REVme/D4.0. Only present if link is connected to 6 GHz AP.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER: u8 Local Power Constraint
+ * (dBm) advertised by AP in Power Constraint element, refer to
+ * IEEE Std 802.11-2020, 9.4.2.13 Power Constraint element.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL: A nested attribute containing
+ * attributes defined by enum qca_wlan_vendor_attr_tpc_pwr_level.
+ * If PSD power, each power level describes each 20 MHz subchannel PSD
+ * power value. If non PSD power, each power level describes each supported
+ * bandwidth's EIRP power value (up to Max bandwidth of AP operating on),
+ * each level attribute contains corresponding bandwidth's center channel
+ * frequency and its EIRP power value.
+ */
+enum qca_wlan_vendor_attr_tpc {
+ QCA_WLAN_VENDOR_ATTR_TPC_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TPC_BSSID = 1,
+ QCA_WLAN_VENDOR_ATTR_TPC_PSD_POWER = 2,
+ QCA_WLAN_VENDOR_ATTR_TPC_EIRP_POWER = 3,
+ QCA_WLAN_VENDOR_ATTR_TPC_POWER_TYPE_6GHZ = 4,
+ QCA_WLAN_VENDOR_ATTR_TPC_AP_CONSTRAINT_POWER = 5,
+ QCA_WLAN_VENDOR_ATTR_TPC_PWR_LEVEL = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TPC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TPC_MAX =
+ QCA_WLAN_VENDOR_ATTR_TPC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tpc_links - Definition of attributes
+ * for %QCA_NL80211_VENDOR_SUBCMD_REGULATORY_TPC_INFO subcommand
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TPC_LINKS: A nested attribute containing
+ * per-link TPC information of all the active links of MLO STA.
+ * For non MLO STA, only one link TPC information will be returned
+ * for connected AP in this nested attribute.
+ * The attributes used inside this nested attributes are defined
+ * in enum qca_wlan_vendor_attr_tpc.
+ */
+enum qca_wlan_vendor_attr_tpc_links {
+ QCA_WLAN_VENDOR_ATTR_TPC_LINKS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TPC_LINKS = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TPC_LINKS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TPC_LINKS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TPC_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.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_DATA: The binary blob data
+ * associated with the firmware page fault that is expected to contain the
+ * required dump to analyze frequent page faults.
+ * NLA_BINARY attribute, the maximum size is 1024 bytes.
+ */
+enum qca_wlan_vendor_attr_fw_page_fault_report {
+ QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_DATA = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_LAST,
+ QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_MAX =
+ QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_btm_support: BTM support configuration
+ *
+ * @QCA_WLAN_BTM_SUPPORT_DEFAULT: Restore default BTM support policy. The driver
+ * follows the BSS Transition bit in the Extended Capabilities element from the
+ * connect request IEs with the default BTM support policy.
+ *
+ * @QCA_WLAN_BTM_SUPPORT_DISABLE: Disable BTM support for the subsequent
+ * (re)association attempts. The driver shall restore the default BTM support
+ * policy during the first disconnection after successful association. When this
+ * configuration is enabled, the driver shall overwrite the BSS Transition bit
+ * as zero in the Extended Capabilities element while sending (Re)Association
+ * Request frames. Also, the driver shall drop the BTM frames from userspace and
+ * the connected AP when this configuration is enabled.
+ */
+enum qca_wlan_btm_support {
+ QCA_WLAN_BTM_SUPPORT_DEFAULT = 0,
+ QCA_WLAN_BTM_SUPPORT_DISABLE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_data_rate_type - Represents the possible values for
+ * attribute %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_DATA_RATE_TYPE_LEGACY: Data rate type is a legacy rate code
+ * used in OFDM/CCK.
+ *
+ * @QCA_WLAN_VENDOR_DATA_RATE_TYPE_MCS: Data rate type is an MCS index.
+ *
+ */
+enum qca_wlan_vendor_data_rate_type {
+ QCA_WLAN_VENDOR_DATA_RATE_TYPE_LEGACY = 0,
+ QCA_WLAN_VENDOR_DATA_RATE_TYPE_MCS = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_rate - Definition
+ * of data rate related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE: u8 data rate type.
+ * For this attribute, valid values are enumerated in enum
+ * %qca_wlan_vendor_data_rate_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_VALUE: u8 value.
+ * This attribute value is interpreted according to the value of attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE. For legacy config
+ * type, this attribute value is defined in the units of 0.5 Mbps.
+ * For non legacy config type, this attribute carries the MCS index number.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_POWER_VALUE: u8 value in dBm.
+ * Usually the target computes a final transmit power that is the maximum
+ * power level that doesn't exceed the limits enforced by various sources
+ * like chip-specific conformance test limits (CTL), Specific Absorption
+ * Rate (SAR), Transmit Power Control (TPC), wiphy-specific limits, STA-specific
+ * limits, channel avoidance limits, Automated Frequency Coordination (AFC),
+ * and others. In some cases it may be desirable to use a power level that is
+ * lower than the maximum power level allowed by all of these limits, so this
+ * attribute provides an additional limit that can be used to reduce the
+ * transmit power level.
+ *
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_rate {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_VALUE = 2,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_RATE_POWER_VALUE = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_chain_config - Definition
+ * of chain related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INDEX: u8 value.
+ * Represents a particular chain for which transmit power adjustment needed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG: A nested
+ * attribute containing data rate related information to adjust transmit
+ * power. The attributes used inside this nested attributes are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_rate.
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_chain_config {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_RATE_CONFIG = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_CHAIN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power_band_config - Definition
+ * of band related attributes which is used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INDEX: u8 value to
+ * indicate band for which configuration applies. Valid values are enumerated
+ * in enum %nl80211_band.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG: A nested
+ * attribute containing per chain related information to adjust transmit
+ * power. The attributes used inside this nested attribute are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_chain_config.
+ *
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power_band_config {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CHAIN_CONFIG = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_adjust_tx_power - Definition of attributes
+ * for %QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER subcommand.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG: A nested attribute
+ * containing per band related information to adjust transmit power.
+ * The attributes used inside this nested attributes are defined in
+ * enum qca_wlan_vendor_attr_adjust_tx_power_band_config.
+ */
+enum qca_wlan_vendor_attr_adjust_tx_power {
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_BAND_CONFIG = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_MAX =
+ QCA_WLAN_VENDOR_ATTR_ADJUST_TX_POWER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_spectral_data_transport_mode - Attribute
+ * values for QCA_WLAN_VENDOR_ATTR_SPECTRAL_DATA_TRANSPORT_MODE.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_NETLINK: Use netlink to
+ * send spectral data to userspace applications.
+ * @QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_RELAY: Use relay interface
+ * to send spectral data to userspace applications.
+ */
+enum qca_wlan_vendor_spectral_data_transport_mode {
+ QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_NETLINK = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_DATA_TRANSPORT_RELAY = 1,
+};
+
+/* enum qca_wlan_vendor_spectral_scan_complete_status - Attribute
+ * values for QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS to
+ * indicate the completion status for a spectral scan.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_SUCCESSFUL:
+ * Indicates a successful completion of the scan.
+ *
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_TIMEOUT: Indicates
+ * a timeout has occured while processing the spectral reports.
+ */
+enum qca_wlan_vendor_spectral_scan_complete_status {
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_SUCCESSFUL = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_COMPLETE_STATUS_TIMEOUT = 1,
+};
+
+/* enum qca_wlan_vendor_attr_spectral_scan_complete - Definition of
+ * attributes for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE
+ * to indicate scan status and samples received from hardware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_INVALID: Invalid attribute
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS: u32 attribute.
+ * Indicates completion status, either the scan is successful or a timeout
+ * is issued by the driver.
+ * See enum qca_wlan_vendor_spectral_scan_complete_status.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_RECEIVED_SAMPLES: u32
+ * attribute. Number of spectral samples received after the scan has started.
+ */
+enum qca_wlan_vendor_attr_spectral_scan_complete {
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_STATUS = 1,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_RECEIVED_SAMPLES = 2,
+
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_MAX =
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_async_get_station_attr - Attribute values for
+ * %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_CONFIG: 8-bit unsigned value to
+ * configure the driver to enable/disable reporting
+ * %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION events. 1-Enable, 0-Disable.
+ * This is required in a command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_RESPONSE: Nested attribute. This is
+ * required in %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION event.
+ * This attribute is nested with the station MAC address in %NL80211_ATTR_MAC
+ * and the station information in %NL80211_ATTR_STA_INFO nested attribute, see
+ * enum nl80211_sta_info.
+ */
+enum qca_wlan_vendor_async_get_station_attr {
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_CONFIG = 1,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_RESPONSE = 2,
+
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_MAX =
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_AFTER_LAST - 1,
+};
+
+/* enum qca_wlan_vendor_ap_suspend_state - Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE.
+ *
+ * @QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE: Disable suspend state. When used
+ * with a command, the driver resumes AP with the same configuration that was
+ * applied earlier and starts all TX/RX operations. When used in an event,
+ * indicates the AP interface resumed.
+ *
+ * @QCA_WLAN_VENDOR_AP_SUSPEND_STATE_ENABLE: Enable suspend state. In this
+ * mode, all associated STAs are disconnected and TX/RX is stopped. While an AP
+ * is in this state, it allows only %QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE
+ * or AP stop/teardown operations. When used in an event, indicates the AP
+ * interface suspended.
+ */
+enum qca_wlan_vendor_ap_suspend_state {
+ QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE = 0,
+ QCA_WLAN_VENDOR_AP_SUSPEND_STATE_ENABLE = 1,
+};
+
+/* enum qca_wlan_vendor_attr_ap_suspend - Definition of attributes for
+ * @QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND to configure/notify the suspend state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE: u8 attribute to configure/notify
+ * suspend state defined in enum qca_wlan_vendor_ap_suspend_state.
+ */
+enum qca_wlan_vendor_attr_ap_suspend {
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE = 1,
+
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_MAX =
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_traffic_type - Traffic types into which the flows can be classified.
+ * @QCA_TRAFFIC_TYPE_STREAMING: Traffic type is streaming
+ * @QCA_TRAFFIC_TYPE_GAMING: Traffic type is gaming
+ * @QCA_TRAFFIC_TYPE_VOICE_CALL: Traffic type is a voice call
+ * @QCA_TRAFFIC_TYPE_VIDEO_CALL: Traffic type is a video call
+ * @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
+ */
+enum qca_traffic_type {
+ QCA_TRAFFIC_TYPE_STREAMING = 0,
+ QCA_TRAFFIC_TYPE_GAMING = 1,
+ QCA_TRAFFIC_TYPE_VOICE_CALL = 2,
+ QCA_TRAFFIC_TYPE_VIDEO_CALL = 3,
+ QCA_TRAFFIC_TYPE_SCREEN_SHARE = 4,
+ QCA_TRAFFIC_TYPE_UNKNOWN = 5,
+ QCA_TRAFFIC_TYPE_INVALID = 6,
+};
+
+/**
+ * enum qca_wlan_vendor_flow_tuple_proto - Definition of the values to specify
+ * the flow tuple protocol in QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL.
+ *
+ * @QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_UDP: UDP flow
+ *
+ * @QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_TCP: TCP flow
+ */
+enum qca_wlan_vendor_flow_tuple_proto {
+ QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_UDP = 0,
+ QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_TCP = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_tuple - Definition of attributes to specify a
+ * flow tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR: Optional u32 attribute
+ * indicates the source IPv4 address (in network byte order).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR: Optional u32 attribute
+ * indicates the destination IPv4 address (in network byte order).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR: Optional NLA_BINARY
+ * attribute of 16 bytes length that indicates the source IPv6 address
+ * (in network byte order) for a flow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR: Optional NLA_BINARY
+ * attribute of 16 bytes length that indicates the destination IPv6 address
+ * (in network byte order) for a flow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_SRC_PORT: Mandatory u16 attribute indicates
+ * the TCP/UDP source port.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_DST_PORT: Mandatory u16 attribute indicates
+ * the TCP/UDP destination port.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL: Mandatory u8 attribute indicates
+ * the flow protocol. Uses the enum qca_wlan_vendor_flow_tuple_proto.
+ *
+ * IPv4 flows have to specify @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR
+ * and @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR.
+ * IPv6 flows have to specify @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR
+ * and @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR.
+ */
+enum qca_wlan_vendor_attr_flow_tuple {
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR = 3,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR = 4,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_SRC_PORT = 5,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_DST_PORT = 6,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_stats - Definition of attributes to specify
+ * TX/RX sample for one window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_BYTES: Mandatory u64 attribute indicates
+ * the total number of uplink/downlink bytes within the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_PKTS: Mandatory u32 attribute indicates
+ * the total number of packets (uplink/downlink) within the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MIN: Mandatory u32 attribute
+ * indicates the minimum uplink/downlink packet size (in bytes) during the
+ * sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MAX: Mandatory u32 attribute
+ * indicates the maximum uplink/downlink packet size (in bytes) during the
+ * sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MIN: Mandatory u64 attribute
+ * indicates the minimum uplink/downlink packet IAT (inter-arrival time)
+ * in microseconds, during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MAX: Mandatory u64 attribute
+ * indicates the maximum uplink/downlink packet IAT (inter-arrival time)
+ * in microseconds, during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of uplink/downlink packet IAT
+ * (inter-arrival time) in microseconds, during the sampling window.
+ * This attribute is used to calculate the mean packet (inter-arrival time)
+ * during the sampling window.
+ */
+enum qca_wlan_vendor_attr_txrx_stats {
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_BYTES = 1,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_PKTS = 2,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MIN = 3,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MAX = 4,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MIN = 5,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MAX = 6,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_SUM = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_samples_windows - Definition of attributes
+ * to specify the TX/RX statistics collected in a sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_WINDOW_SIZE: Mandatory u32
+ * attribute indicates window size for packet TX/RX sampling (in milliseconds).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_UL_STATS: Mandatory nested
+ * attribute containing the uplink TX/RX packet statistics for a flow. Uses the
+ * enum qca_wlan_vendor_attr_txrx_stats.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_DL_STATS: Mandatory nested
+ * attribute containing the downlink TX/RX packet statistics for a flow. Uses
+ * the enum qca_wlan_vendor_attr_txrx_stats.
+ */
+enum qca_wlan_vendor_attr_txrx_samples_windows {
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_WINDOW_SIZE = 1,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_UL_STATS = 2,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_DL_STATS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_samples - Definition of attributes to specify
+ * a TX/RX sample.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS: Mandatory array of nested
+ * attributes that indicates the TX/RX samples in multiple overlapping windows.
+ * This uses the attributes defined by
+ * enum qca_wlan_vendor_attr_txrx_samples_windows.
+ */
+enum qca_wlan_vendor_attr_txrx_samples {
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_burst_stats - Definition of attribute to specify
+ * burst statistics.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MIN: Mandatory u32 attribute
+ * indicates minimum burst duration (in milliseconds) during the sampling
+ * window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MAX: Mandatory u32 attribute
+ * indicates maximum burst duration (in milliseconds) during the sampling
+ * window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of burst duration (in milliseconds)
+ * during the sampling window. This attribute is used to calculate the mean
+ * burst duration (in milliseconds) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MIN: Mandatory u64 attribute
+ * indicates minimum burst size (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MAX: Mandatory u64 attribute
+ * indicates maximum burst size (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of burst size (in bytes) during the
+ * sampling window. This attribute is used to calculate the mean burst size
+ * (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_COUNT: Mandatory u32 attribute
+ * indicates the number of bursts during the sampling window.
+ */
+enum qca_wlan_vendor_attr_burst_stats {
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MIN = 1,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MAX = 2,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_SUM = 3,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MIN = 4,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MAX = 5,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_SUM = 6,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_COUNT = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_burst_sample - Definition of attributes to specify
+ * a burst sample.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_TXRX_STATS: Mandatory nested attribute
+ * indicates the uplink and downlink packet statistics collected in a
+ * sampling window, containing attributes defined in
+ * enum qca_wlan_vendor_attr_txrx_samples_windows.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_UL_BURST_STATS: Optional nested attribute
+ * indicates the uplink burst stats, containing attributes defined in
+ * enum qca_wlan_vendor_attr_burst_stats.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_DL_BURST_STATS: Optional nested attribute
+ * indicates the downlink burst stats, containing attributes defined in
+ * enum qca_wlan_vendor_attr_burst_stats.
+ */
+enum qca_wlan_vendor_attr_burst_sample {
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_TXRX_STATS = 1,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_UL_BURST_STATS = 2,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_DL_BURST_STATS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_LAST,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_MAX =
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_stats - Definition of attribute used by
+ * %QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT and
+ * %QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS.
+ *
+ * Presence of one of the attributes
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES and
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_FLOW_TUPLE: Mandatory nested attribute
+ * containing the flow tuple of the flow for which the statistics are being
+ * reported.
+ * Uses the attributes defined by enum qca_wlan_vendor_attr_flow_tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TRAFFIC_TYPE: Optional u8 attribute
+ * indicates the traffic type classified for this flow tuple. Uses the
+ * enum qca_traffic_type values.
+ * This attribute is mandatory for the command
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES: Optional nested attribute
+ * containing nested array of TX/RX samples defined in
+ * enum qca_wlan_vendor_attr_txrx_samples.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES: Optional nested attribute
+ * indicates the packet burst statistics for a flow. Uses attributes defined by
+ * enum qca_wlan_vendor_attr_burst_sample.
+ */
+enum qca_wlan_vendor_attr_flow_stats {
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_FLOW_TUPLE = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TRAFFIC_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES = 3,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_classify_result - Definition of attributes to
+ * specify the flow classification result. This enum is used by
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_FLOW_TUPLE: Mandatory nested
+ * attribute containing attributes defined by
+ * enum qca_wlan_vendor_attr_flow_tuple.
+ *
+ * @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.
+ */
+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,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST - 1,
+};
+
+/**
+ * enum qca_async_stats_sub_module - The statistics type used in async
+ * statistics policy.
+ * Used by @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE.
+ *
+ * @QCA_ASYNC_STATS_TYPE_POWERSAVE: Wi-Fi powersave statistics
+ *
+ * @QCA_ASYNC_STATS_TYPE_FLOW_STATS: Flow statistics
+ *
+ * @QCA_ASYNC_STATS_TYPE_CLASSIFIED_FLOW_STATS: Classified flow statistics
+ */
+enum qca_async_stats_type {
+ QCA_ASYNC_STATS_TYPE_POWERSAVE = 0,
+ QCA_ASYNC_STATS_TYPE_FLOW_STATS = 1,
+ QCA_ASYNC_STATS_TYPE_CLASSIFIED_FLOW_STATS = 2,
+};
+
+/**
+ * enum qca_async_stats_action - ASYNC statistics action. Used by
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION.
+ *
+ * @QCA_ASYNC_STATS_ACTION_START: Start indication for async statistics
+ * collection.
+ * @QCA_ASYNC_STATS_ACTION_STOP: Stop indication for async statistics
+ * collection.
+ */
+enum qca_async_stats_action {
+ QCA_ASYNC_STATS_ACTION_START = 0,
+ QCA_ASYNC_STATS_ACTION_STOP = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_async_stats_policy - Definition of attributes to
+ * specify the ASYNC statistics policy. This enum is used by
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE: Mandatory u8
+ * attribute indicates the statistics type for which the async statistics policy
+ * needs to be applied by the driver. Uses the enum qca_async_stats_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION: Mandatory u8 attribute
+ * indicates the action as part of this policy.
+ * Uses the enum qca_async_stats_action values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_PERIODICITY: Optional u32
+ * attribute indicates the periodicity (in milliseconds) for the statistics to
+ * be reported. This attribute is mandatory for QCA_ASYNC_STATS_TYPE_POWERSAVE.
+ */
+enum qca_wlan_vendor_attr_async_stats_policy {
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION = 2,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_PERIODICITY = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_LAST,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_MAX =
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_nss_pkt - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE: u8 attribute. This
+ * represents the number of spatial streams.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_TX_PACKET_COUNT: u64 attribute. This
+ * represents the number of MSDU packets transmitted with the number of spatial
+ * streams specified in %QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_RX_PACKET_COUNT: u64 attribute. This
+ * represents the number of MSDU packets received with the number of spatial
+ * streams specified in %QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE.
+ */
+enum qca_wlan_vendor_attr_nss_pkt {
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE = 1,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_TX_PACKET_COUNT = 2,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_RX_PACKET_COUNT = 3,
+
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_MAX =
+ QCA_WLAN_VENDOR_ATTR_NSS_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
+ * packets handled through a network device.
+ *
+ * There are three offload paths possible for handling packet forwarding between
+ * Ethernet and Wi-Fi network, and which path to use can be configured on a per
+ * netdevice level based on use case. Userspace can choose different options
+ * based on use cases like performance requirements, traffic control features
+ * and limitations provided in each option.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_NONE: No acceleration configured.
+ * Packets are processed through the Linux kernel networking stack.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_SFE: Packets are processed through
+ * the shortcut forwarding engine (SFE) to bypass the Linux networking stack
+ * for improved throughput performance. This option is applicable for AP, STA,
+ * and Mesh mode and available for all radio designs. From the performance
+ * aspect, this option consumes more CPU compared to the other two options.
+ * Linux traffic control can be further applied with this option to have more
+ * control on the traffic flows.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_ACTIVE_VP: Packets are processed
+ * through both hardware and software in this case. Packet classification is
+ * done by the hardware and then the packets are delivered to software along
+ * with classification results as meta data. Software can choose to do more
+ * classification/QoS based on use cases. This is applicable for AP, STA, and
+ * Mesh modes and this is available for all radio designs. From the performance
+ * aspect, this option consumes relatively less CPU compared to the SFE option
+ * above. Linux traffic control rules cannot be applied with this option.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_PPE_DS: Packets are processed through
+ * special hardware (Direct Switch) rings which can directly forward the packets
+ * between Ethernet hardware and Wi-Fi hardware with very little software
+ * involvement. This is applicable only for AP and STA modes; not applicable
+ * for Mesh mode. From the performance aspect, this option consumes very much
+ * less CPU compared to the other options. Linux traffic control rules cannot be
+ * applied when this option is used. This option is applicable only for
+ * specific radio designs. When this option is not available, the default option
+ * (SFE) would be configured.
+ */
+enum qca_wlan_intf_offload_type {
+ QCA_WLAN_INTF_OFFLOAD_TYPE_NONE = 0,
+ QCA_WLAN_INTF_OFFLOAD_TYPE_SFE = 1,
+ QCA_WLAN_INTF_OFFLOAD_TYPE_ACTIVE_VP = 2,
+ 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.
+ */
+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_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_USD_MAX =
+ QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index d4a196f..a65da61 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -458,7 +458,7 @@
* mask */
u8 mask;
struct crypto_bignum *pwe;
- size_t prime_len = sae->tmp->prime_len * 8;
+ size_t prime_len = sae->tmp->prime_len;
u8 *pwe_buf;
crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
@@ -2103,8 +2103,11 @@
wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
*pos, end - *pos);
- if (!sae_is_rejected_groups_elem(*pos, end))
+ if (!sae_is_rejected_groups_elem(*pos, end)) {
+ wpabuf_free(sae->tmp->peer_rejected_groups);
+ sae->tmp->peer_rejected_groups = NULL;
return WLAN_STATUS_SUCCESS;
+ }
epos = *pos;
epos++; /* skip IE type */
@@ -2113,6 +2116,12 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
epos++; /* skip ext ID */
len--;
+ if (len & 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid length of the Rejected Groups element payload: %u",
+ len);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
wpabuf_free(sae->tmp->peer_rejected_groups);
sae->tmp->peer_rejected_groups = wpabuf_alloc(len);
@@ -2196,6 +2205,9 @@
res = sae_parse_rejected_groups(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
+ } else {
+ wpabuf_free(sae->tmp->peer_rejected_groups);
+ sae->tmp->peer_rejected_groups = NULL;
}
/* Optional Anti-Clogging Token Container element */
diff --git a/src/common/sae.h b/src/common/sae.h
index c446da3..a353aa8 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -82,6 +82,8 @@
bool omit_pk_elem;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_SAE_PK */
+
+ struct os_reltime disabled_until;
};
struct sae_pt {
diff --git a/src/common/version.h b/src/common/version.h
index 4409e1c..9e0fbc0 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.11-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.12-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index d897e0e..8eb4a1d 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1031,9 +1031,6 @@
const u8 *end, *pos;
u8 link_id;
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
pos = opt;
end = ie + ie_len;
wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
@@ -1104,7 +1101,7 @@
link_id = pos[2] & 0x0f;
wpa_printf(MSG_DEBUG, "FT: MLO GTK (Link ID %u)",
link_id);
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_gtks |= BIT(link_id);
parse->mlo_gtk[link_id] = pos;
@@ -1119,7 +1116,7 @@
link_id = pos[2 + 6] & 0x0f;
wpa_printf(MSG_DEBUG, "FT: MLO IGTK (Link ID %u)",
link_id);
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_igtks |= BIT(link_id);
parse->mlo_igtk[link_id] = pos;
@@ -1134,7 +1131,7 @@
link_id = pos[2 + 6] & 0x0f;
wpa_printf(MSG_DEBUG, "FT: MLO BIGTK (Link ID %u)",
link_id);
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
break;
parse->valid_mlo_bigtks |= BIT(link_id);
parse->mlo_bigtk[link_id] = pos;
@@ -1329,8 +1326,7 @@
if (fte_len < 255) {
res = wpa_ft_parse_fte(key_mgmt, fte, fte_len, parse);
} else {
- parse->fte_buf = ieee802_11_defrag_data(fte, fte_len,
- false);
+ parse->fte_buf = ieee802_11_defrag(fte, fte_len, false);
if (!parse->fte_buf)
goto fail;
res = wpa_ft_parse_fte(key_mgmt,
@@ -1340,6 +1336,11 @@
}
if (res < 0)
goto fail;
+
+ /* FTE might be fragmented. If it is, the separate Fragment
+ * elements are included in MIC calculation as full elements. */
+ parse->ftie = fte;
+ parse->ftie_len = fte_len;
}
if (prot_ie_count == 0)
@@ -1354,7 +1355,7 @@
/* TODO: This count should be done based on all _requested_,
* not _accepted_ links. */
- for (link_id = 0; link_id < MAX_NUM_MLO_LINKS; link_id++) {
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
if (parse->mlo_gtk[link_id]) {
if (parse->rsn)
prot_ie_count--;
@@ -1889,6 +1890,14 @@
data->has_group = 1;
data->key_mgmt = WPA_KEY_MGMT_OSEN;
data->proto = WPA_PROTO_OSEN;
+ } else 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;
@@ -2894,7 +2903,7 @@
}
-int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
+int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace)
{
u8 *start, *end, *rpos, *rend;
int added = 0;
@@ -2957,12 +2966,12 @@
if (rend - rpos < 2)
return -1;
num_pmkid = WPA_GET_LE16(rpos);
+ if (num_pmkid * PMKID_LEN > rend - rpos - 2)
+ return -1;
/* PMKID-Count was included; use it */
- if (num_pmkid != 0) {
+ if (replace && num_pmkid != 0) {
u8 *after;
- if (num_pmkid * PMKID_LEN > rend - rpos - 2)
- return -1;
/*
* PMKID may have been included in RSN IE in
* (Re)Association Request frame, so remove the old
@@ -2975,8 +2984,9 @@
os_memmove(rpos + 2, after, end - after);
start[1] -= num_pmkid * PMKID_LEN;
added -= num_pmkid * PMKID_LEN;
+ num_pmkid = 0;
}
- WPA_PUT_LE16(rpos, 1);
+ WPA_PUT_LE16(rpos, num_pmkid + 1);
rpos += 2;
os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
os_memcpy(rpos, pmkid, PMKID_LEN);
@@ -3551,7 +3561,7 @@
selector == RSN_KEY_DATA_MLO_GTK) {
link_id = (p[0] & RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK) >>
RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_gtks |= BIT(link_id);
@@ -3569,7 +3579,7 @@
selector == RSN_KEY_DATA_MLO_IGTK) {
link_id = (p[8] & RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK) >>
RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_igtks |= BIT(link_id);
@@ -3587,7 +3597,7 @@
selector == RSN_KEY_DATA_MLO_BIGTK) {
link_id = (p[8] & RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK) >>
RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_bigtks |= BIT(link_id);
@@ -3605,7 +3615,7 @@
selector == RSN_KEY_DATA_MLO_LINK) {
link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >>
RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT;
- if (link_id >= MAX_NUM_MLO_LINKS)
+ if (link_id >= MAX_NUM_MLD_LINKS)
return 2;
ie->valid_mlo_links |= BIT(link_id);
@@ -3743,6 +3753,11 @@
ie->supp_oper_classes = pos + 2;
ie->supp_oper_classes_len = pos[1];
}
+ } else if (*pos == WLAN_EID_SSID) {
+ ie->ssid = pos + 2;
+ ie->ssid_len = pos[1];
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: SSID in EAPOL-Key",
+ ie->ssid, ie->ssid_len);
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, ie);
if (ret == 1) {
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 1269bf9..1e31368 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -9,12 +9,15 @@
#ifndef WPA_COMMON_H
#define WPA_COMMON_H
+#include "common/defs.h"
+
/* IEEE 802.11i */
#define PMKID_LEN 16
#define PMK_LEN 32
#define PMK_LEN_SUITE_B_192 48
#define PMK_LEN_MAX 64
#define WPA_REPLAY_COUNTER_LEN 8
+#define RSN_PN_LEN 6
#define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8
#define WPA_GMK_LEN 32
@@ -336,52 +339,54 @@
} STRUCT_PACKED;
+#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
+
struct rsn_error_kde {
be16 mui;
be16 error_type;
} STRUCT_PACKED;
-#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN)
struct wpa_igtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
-#define WPA_BIGTK_KDE_PREFIX_LEN (2 + 6)
+#define WPA_BIGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN)
struct wpa_bigtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_GTK_KDE_PREFIX_LENGTH (1 + 6)
+#define RSN_MLO_GTK_KDE_PREFIX_LENGTH (1 + RSN_PN_LEN)
#define RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK 0x03
#define RSN_MLO_GTK_KDE_PREFIX0_TX 0x04
#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT 4
#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK 0xF0
-#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH (2 + RSN_PN_LEN + 1)
#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
struct rsn_mlo_igtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 prefix8;
u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH (2 + RSN_PN_LEN + 1)
#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
struct rsn_mlo_bigtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 prefix8;
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_LINK_KDE_FIXED_LENGTH (1 + 6)
+#define RSN_MLO_LINK_KDE_FIXED_LENGTH (1 + ETH_ALEN)
#define RSN_MLO_LINK_KDE_LINK_INFO_INDEX 0
#define RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT 0
#define RSN_MLO_LINK_KDE_LI_LINK_ID_MASK 0x0F
@@ -555,9 +560,7 @@
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len);
-int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
-
-#define MAX_NUM_MLO_LINKS 15
+int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid, bool replace);
struct wpa_ft_ies {
const u8 *mdie;
@@ -596,14 +599,14 @@
const u8 *rsnxe;
size_t rsnxe_len;
u16 valid_mlo_gtks; /* bitmap of valid link GTK subelements */
- const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
- size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_igtks; /* bitmap of valid link IGTK subelements */
- const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
- size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_igtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK subelements */
- const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
- size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
struct wpabuf *fte_buf;
};
@@ -695,22 +698,24 @@
size_t supp_channels_len;
const u8 *supp_oper_classes;
size_t supp_oper_classes_len;
+ const u8 *ssid;
+ size_t ssid_len;
u8 qosinfo;
u16 aid;
const u8 *wmm;
size_t wmm_len;
u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
- const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
- size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_gtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_igtks; /* bitmap of valid link IGTK KDEs */
- const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
- size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_igtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK KDEs */
- const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
- size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
- const u8 *mlo_link[MAX_NUM_MLO_LINKS];
- size_t mlo_link_len[MAX_NUM_MLO_LINKS];
+ const u8 *mlo_link[MAX_NUM_MLD_LINKS];
+ size_t mlo_link_len[MAX_NUM_MLD_LINKS];
};
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 416e0d6..f614250 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -87,6 +87,8 @@
#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+/** Regulatory beacon hint */
+#define WPA_EVENT_REGDOM_BEACON_HINT "CTRL-EVENT-REGDOM-BEACON-HINT "
/** Channel switch started (followed by freq=<MHz> and other channel parameters)
*/
#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
@@ -230,6 +232,13 @@
#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT "
#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER "
+/* Wi-Fi Aware (NAN USD) 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 "
+
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
@@ -404,6 +413,9 @@
/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
#define BEACON_RESP_RX "BEACON-RESP-RX "
+/* parameters: <STA address> <dialog token> <link measurement report> */
+#define LINK_MSR_RESP_RX "LINK-MSR-RESP-RX "
+
/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 4147f41..ffeddba 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -2446,6 +2446,7 @@
wpabuf_free(res_pt);
wpabuf_free(res_ct);
crypto_ec_key_deinit(own_priv);
+ crypto_ec_key_deinit(peer_pub);
return res;
}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 22f6ab4..2d8ff60 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
/*
* Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -31,6 +31,11 @@
#else /* OpenSSL version >= 3.0 */
#include <openssl/cmac.h>
#endif /* OpenSSL version >= 3.0 */
+#ifdef CONFIG_DPP3
+#if OPENSSL_VERSION_NUMBER >= 0x30200000L
+#include <openssl/hpke.h>
+#endif
+#endif /* CONFIG_DPP3 */
#include "common.h"
#include "utils/const_time.h"
@@ -471,11 +476,11 @@
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return NULL;
- if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1 ||
+ EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
EVP_CIPHER_CTX_free(ctx);
return NULL;
}
- EVP_CIPHER_CTX_set_padding(ctx, 0);
return ctx;
}
@@ -528,11 +533,11 @@
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return NULL;
- if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1 ||
+ EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
EVP_CIPHER_CTX_free(ctx);
return NULL;
}
- EVP_CIPHER_CTX_set_padding(ctx, 0);
return ctx;
}
@@ -1313,6 +1318,7 @@
#else /* OpenSSL version >= 3.0 */
HMAC_CTX *ctx;
#endif /* OpenSSL version >= 3.0 */
+ bool failed;
};
@@ -1425,9 +1431,11 @@
if (ctx == NULL)
return;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- EVP_MAC_update(ctx->ctx, data, len);
+ if (!EVP_MAC_update(ctx->ctx, data, len))
+ ctx->failed = true;
#else /* OpenSSL version >= 3.0 */
- HMAC_Update(ctx->ctx, data, len);
+ if (!HMAC_Update(ctx->ctx, data, len))
+ ctx->failed = true;
#endif /* OpenSSL version >= 3.0 */
}
@@ -1437,6 +1445,7 @@
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
size_t mdlen;
int res;
+ bool failed;
if (!ctx)
return -2;
@@ -1455,11 +1464,15 @@
}
res = EVP_MAC_final(ctx->ctx, mac, &mdlen, mdlen);
EVP_MAC_CTX_free(ctx->ctx);
+ failed = ctx->failed;
bin_clear_free(ctx, sizeof(*ctx));
if (TEST_FAIL())
return -1;
+ if (failed)
+ return -2;
+
if (res == 1) {
*len = mdlen;
return 0;
@@ -1469,6 +1482,7 @@
#else /* OpenSSL version >= 3.0 */
unsigned int mdlen;
int res;
+ bool failed;
if (ctx == NULL)
return -2;
@@ -1482,11 +1496,15 @@
mdlen = *len;
res = HMAC_Final(ctx->ctx, mac, &mdlen);
HMAC_CTX_free(ctx->ctx);
+ failed = ctx->failed;
bin_clear_free(ctx, sizeof(*ctx));
if (TEST_FAIL())
return -1;
+ if (failed)
+ return -2;
+
if (res == 1) {
*len = mdlen;
return 0;
@@ -1817,6 +1835,7 @@
ret = 0;
fail:
EVP_MAC_CTX_free(ctx);
+ EVP_MAC_free(emac);
return ret;
#else /* OpenSSL version >= 3.0 */
CMAC_CTX *ctx;
@@ -2841,8 +2860,10 @@
/* Encode using SECG SEC 1, Sec. 2.3.4 format */
peer = os_malloc(1 + len);
- if (!peer)
+ if (!peer) {
+ EVP_PKEY_free(peerkey);
return NULL;
+ }
peer[0] = inc_y ? 0x04 : 0x02;
os_memcpy(peer + 1, key, len);
@@ -2997,11 +3018,15 @@
NULL, NULL);
if (!ctx ||
OSSL_DECODER_from_data(ctx, &der, &der_len) != 1) {
- wpa_printf(MSG_INFO, "OpenSSL: Decoding EC private key (DER) failed: %s",
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Decoding EC private key (DER) failed: %s",
ERR_error_string(ERR_get_error(), NULL));
+ if (ctx)
+ OSSL_DECODER_CTX_free(ctx);
goto fail;
}
+ OSSL_DECODER_CTX_free(ctx);
return (struct crypto_ec_key *) pkey;
fail:
crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
@@ -3908,9 +3933,10 @@
group = EC_GROUP_new_by_curve_name(nid);
prime = BN_new();
if (!group || !prime)
- return -1;
+ goto fail;
if (EC_GROUP_get_curve(group, prime, NULL, NULL, NULL) == 1)
prime_len = BN_num_bytes(prime);
+fail:
EC_GROUP_free(group);
BN_free(prime);
return prime_len;
@@ -4856,7 +4882,7 @@
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
if (!hmac)
- return -1;
+ goto fail;
params[0] = OSSL_PARAM_construct_utf8_string(
"digest",
@@ -4865,7 +4891,7 @@
#else /* OpenSSL version >= 3.0 */
hctx = HMAC_CTX_new();
if (!hctx)
- return -1;
+ goto fail;
#endif /* OpenSSL version >= 3.0 */
while (left > 0) {
@@ -4874,7 +4900,7 @@
EVP_MAC_CTX_free(hctx);
hctx = EVP_MAC_CTX_new(hmac);
if (!hctx)
- return -1;
+ goto fail;
if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
goto fail;
@@ -5125,13 +5151,13 @@
}
-struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
- enum hpke_kdf_id kdf_id,
- enum hpke_aead_id aead_id,
- struct crypto_ec_key *peer_pub,
- const u8 *info, size_t info_len,
- const u8 *aad, size_t aad_len,
- const u8 *pt, size_t pt_len)
+static struct wpabuf * hpke_base_seal_int(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *peer_pub,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len)
{
struct hpke_context *ctx;
u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
@@ -5289,13 +5315,13 @@
}
-struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
- enum hpke_kdf_id kdf_id,
- enum hpke_aead_id aead_id,
- struct crypto_ec_key *own_priv,
- const u8 *info, size_t info_len,
- const u8 *aad, size_t aad_len,
- const u8 *enc_ct, size_t enc_ct_len)
+static struct wpabuf * hpke_base_open_int(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *own_priv,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *enc_ct, size_t enc_ct_len)
{
struct hpke_context *ctx;
u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
@@ -5324,6 +5350,231 @@
return pt;
}
+
+#if OPENSSL_VERSION_NUMBER >= 0x30200000L
+
+static bool hpke_set_suite(OSSL_HPKE_SUITE *suite,
+ enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id)
+{
+ os_memset(suite, 0, sizeof(*suite));
+
+ switch (kem_id) {
+ case HPKE_DHKEM_P256_HKDF_SHA256:
+ suite->kem_id = OSSL_HPKE_KEM_ID_P256;
+ break;
+ case HPKE_DHKEM_P384_HKDF_SHA384:
+ suite->kem_id = OSSL_HPKE_KEM_ID_P384;
+ break;
+ case HPKE_DHKEM_P521_HKDF_SHA512:
+ suite->kem_id = OSSL_HPKE_KEM_ID_P521;
+ break;
+ default:
+ return false;
+ }
+
+ switch (kdf_id) {
+ case HPKE_KDF_HKDF_SHA256:
+ suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA256;
+ break;
+ case HPKE_KDF_HKDF_SHA384:
+ suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA384;
+ break;
+ case HPKE_KDF_HKDF_SHA512:
+ suite->kdf_id = OSSL_HPKE_KDF_ID_HKDF_SHA512;
+ break;
+ default:
+ return false;
+ }
+
+ switch (aead_id) {
+ case HPKE_AEAD_AES_128_GCM:
+ suite->aead_id = OSSL_HPKE_AEAD_ID_AES_GCM_128;
+ break;
+ case HPKE_AEAD_AES_256_GCM:
+ suite->aead_id = OSSL_HPKE_AEAD_ID_AES_GCM_256;
+ break;
+ default:
+ return false;
+ }
+
+ if (!OSSL_HPKE_suite_check(*suite)) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: HPKE suite kem_id=%d kdf_id=%d aead_id=%d not supported",
+ kem_id, kdf_id, aead_id);
+ return false;
+ }
+
+ return true;
+}
+
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *peer_pub,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len)
+{
+ OSSL_HPKE_SUITE suite;
+ OSSL_HPKE_CTX *ctx = NULL;
+ struct wpabuf *res = NULL, *buf, *pub = NULL;
+ size_t enc_len, ct_len;
+ int group;
+
+ group = crypto_ec_key_group(peer_pub);
+ if (group == 28 || group == 29 || group == 30) {
+ /* Use the internal routines for the special DPP use case with
+ * brainpool curves, */
+ return hpke_base_seal_int(kem_id, kdf_id, aead_id, peer_pub,
+ info, info_len, aad, aad_len,
+ pt, pt_len);
+ }
+
+
+ if (!hpke_set_suite(&suite, kem_id, kdf_id, aead_id))
+ return NULL;
+
+ enc_len = OSSL_HPKE_get_public_encap_size(suite);
+ ct_len = OSSL_HPKE_get_ciphertext_size(suite, pt_len);
+ buf = wpabuf_alloc(enc_len + ct_len);
+ if (!buf)
+ goto out;
+
+ pub = crypto_ec_key_get_pubkey_point(peer_pub, 1);
+ if (!pub)
+ goto out;
+
+ ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_BASE, suite,
+ OSSL_HPKE_ROLE_SENDER, NULL, NULL);
+ if (!ctx)
+ goto out;
+
+ if (OSSL_HPKE_encap(ctx, wpabuf_put(buf, 0), &enc_len,
+ wpabuf_head(pub), wpabuf_len(pub),
+ info, info_len) != 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_encap failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+ wpabuf_put(buf, enc_len);
+
+ if (OSSL_HPKE_seal(ctx, wpabuf_put(buf, 0), &ct_len, aad, aad_len,
+ pt, pt_len) != 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_seal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+ wpabuf_put(buf, ct_len);
+ res = buf;
+ buf = NULL;
+
+out:
+ OSSL_HPKE_CTX_free(ctx);
+ wpabuf_free(buf);
+ wpabuf_free(pub);
+ return res;
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *own_priv,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *enc_ct, size_t enc_ct_len)
+{
+ OSSL_HPKE_SUITE suite;
+ OSSL_HPKE_CTX *ctx;
+ struct wpabuf *buf = NULL, *res = NULL;
+ size_t len, enc_len;
+ int group;
+
+ group = crypto_ec_key_group(own_priv);
+ if (group == 28 || group == 29 || group == 30) {
+ /* Use the internal routines for the special DPP use case with
+ * brainpool curves, */
+ return hpke_base_open_int(kem_id, kdf_id, aead_id, own_priv,
+ info, info_len, aad, aad_len,
+ enc_ct, enc_ct_len);
+ }
+
+ if (!hpke_set_suite(&suite, kem_id, kdf_id, aead_id))
+ return NULL;
+
+ enc_len = OSSL_HPKE_get_public_encap_size(suite);
+ if (enc_ct_len < enc_len) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Too short HPKE enc_ct data");
+ return NULL;
+ }
+
+ ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_BASE, suite,
+ OSSL_HPKE_ROLE_RECEIVER, NULL, NULL);
+ if (!ctx)
+ goto out;
+
+ if (OSSL_HPKE_decap(ctx, enc_ct, enc_len, (EVP_PKEY *) own_priv,
+ info, info_len) != 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_decap failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ len = enc_ct_len;
+ buf = wpabuf_alloc(len);
+ if (!buf)
+ goto out;
+
+ if (OSSL_HPKE_open(ctx, wpabuf_put(buf, 0), &len, aad, aad_len,
+ enc_ct + enc_len, enc_ct_len - enc_len) != 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OSSL_HPKE_open failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto out;
+ }
+
+ wpabuf_put(buf, len);
+ res = buf;
+ buf = NULL;
+
+out:
+ OSSL_HPKE_CTX_free(ctx);
+ wpabuf_free(buf);
+ return res;
+}
+
+#else /* OpenSSL < 3.2 */
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *peer_pub,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len)
+{
+ return hpke_base_seal_int(kem_id, kdf_id, aead_id, peer_pub,
+ info, info_len, aad, aad_len, pt, pt_len);
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *own_priv,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *enc_ct, size_t enc_ct_len)
+{
+ return hpke_base_open_int(kem_id, kdf_id, aead_id, own_priv,
+ info, info_len, aad, aad_len,
+ enc_ct, enc_ct_len);
+}
+
+#endif /* OpenSSL < 3.2 */
+
#endif /* CONFIG_DPP3 */
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index ff1e2ba..81e6e5e 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -76,9 +76,6 @@
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
-#ifndef MIN
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#endif
/* compress 512-bits */
static int sha256_compress(struct sha256_state *md, unsigned char *buf)
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
index c026394..8e98a9c 100644
--- a/src/crypto/sha512-internal.c
+++ b/src/crypto/sha512-internal.c
@@ -97,9 +97,6 @@
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
-#ifndef MIN
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#endif
#define ROR64c(x, y) \
( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 103c333..f5d734d 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -19,14 +19,16 @@
#endif
#endif
+#ifndef OPENSSL_NO_ENGINE
+/* OpenSSL 3.0 has moved away from the engine API */
+#define OPENSSL_SUPPRESS_DEPRECATED
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
-#ifndef OPENSSL_NO_ENGINE
-#include <openssl/engine.h>
-#endif /* OPENSSL_NO_ENGINE */
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/core_names.h>
#include <openssl/decoder.h>
@@ -152,6 +154,7 @@
unsigned int crl_reload_interval;
struct os_reltime crl_last_reload;
char *check_cert_subject;
+ char *openssl_ciphers;
};
struct tls_connection {
@@ -1261,6 +1264,7 @@
}
os_free(data->check_cert_subject);
+ os_free(data->openssl_ciphers);
os_free(data);
}
@@ -3249,6 +3253,9 @@
}
#endif
+ if (!openssl_ciphers)
+ openssl_ciphers = conn->data->openssl_ciphers;
+
#ifdef CONFIG_SUITEB
#ifdef OPENSSL_IS_BORINGSSL
/* Start with defaults from BoringSSL */
@@ -5749,6 +5756,14 @@
return -1;
}
+ os_free(data->openssl_ciphers);
+ if (params->openssl_ciphers) {
+ data->openssl_ciphers = os_strdup(params->openssl_ciphers);
+ if (!data->openssl_ciphers)
+ return -1;
+ } else {
+ data->openssl_ciphers = NULL;
+ }
if (params->openssl_ciphers &&
SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
wpa_printf(MSG_INFO,
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7ae7d90..8be4012 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -43,6 +43,7 @@
#define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
#define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
+#define HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL 0x00002000
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
@@ -247,6 +248,11 @@
enum hostapd_hw_mode mode;
/**
+ * is_6ghz - Whether the mode information is for the 6 GHz band
+ */
+ bool is_6ghz;
+
+ /**
* num_channels - Number of entries in the channels array
*/
int num_channels;
@@ -695,6 +701,14 @@
*/
unsigned int min_probe_req_content:1;
+ /**
+ * link_id - Specify the link that is requesting the scan on an MLD
+ *
+ * This is set when operating as an AP MLD and doing an OBSS scan.
+ * -1 indicates that no particular link ID is set.
+ */
+ s8 link_id;
+
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -949,6 +963,8 @@
const u8 *bssid;
const u8 *ies;
size_t ies_len;
+ int error;
+ bool disabled;
} mld_links[MAX_NUM_MLD_LINKS];
};
@@ -1377,6 +1393,23 @@
u8 rfkill_release;
};
+struct unsol_bcast_probe_resp {
+ /**
+ * Unsolicited broadcast Probe Response interval in TUs
+ */
+ unsigned int unsol_bcast_probe_resp_interval;
+
+ /**
+ * Unsolicited broadcast Probe Response template data
+ */
+ u8 *unsol_bcast_probe_resp_tmpl;
+
+ /**
+ * Unsolicited broadcast Probe Response template length
+ */
+ size_t unsol_bcast_probe_resp_tmpl_len;
+};
+
struct wpa_driver_ap_params {
/**
* head - Beacon head from IEEE 802.11 header to IEs before TIM IE
@@ -1720,21 +1753,6 @@
size_t fd_frame_tmpl_len;
/**
- * Unsolicited broadcast Probe Response interval in TUs
- */
- unsigned int unsol_bcast_probe_resp_interval;
-
- /**
- * Unsolicited broadcast Probe Response template data
- */
- u8 *unsol_bcast_probe_resp_tmpl;
-
- /**
- * Unsolicited broadcast Probe Response template length
- */
- size_t unsol_bcast_probe_resp_tmpl_len;
-
- /**
* mbssid_tx_iface - Transmitting interface of the MBSSID set
*/
const char *mbssid_tx_iface;
@@ -1800,6 +1818,9 @@
*/
u8 **rnr_elem_offset;
+ /* Unsolicited broadcast Probe Response data */
+ struct unsol_bcast_probe_resp ubpr;
+
/**
* allowed_freqs - List of allowed 20 MHz channel center frequencies in
* MHz for AP operation. Drivers which support this parameter will
@@ -2314,6 +2335,10 @@
#define WPA_DRIVER_FLAGS2_OWE_OFFLOAD_AP 0x0000000000080000ULL
/** Driver support AP SAE authentication offload */
#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
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2715,6 +2740,8 @@
* @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
*/
struct csa_settings {
u8 cs_count;
@@ -2728,6 +2755,9 @@
u16 counter_offset_presp[2];
u16 punct_bitmap;
+ int link_id;
+
+ struct unsol_bcast_probe_resp ubpr;
};
/**
@@ -2739,6 +2769,8 @@
* @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info
* @counter_offset_beacon: Offset to the count field in Beacon frame tail
* @counter_offset_presp: Offset to the count field in Probe Response frame
+ * @ubpr: Unsolicited broadcast Probe Response frame data
+ * @link_id: If >= 0 indicates the link of the AP MLD to configure
*/
struct cca_settings {
u8 cca_count;
@@ -2749,6 +2781,10 @@
u16 counter_offset_beacon;
u16 counter_offset_presp;
+
+ struct unsol_bcast_probe_resp ubpr;
+
+ int link_id;
};
/* TDLS peer capabilities for send_tdls_mgmt() */
@@ -2819,6 +2855,9 @@
/* Indicates whether EHT is enabled */
bool eht_enabled;
+
+ /* Indicates the link if MLO case; -1 otherwise */
+ int link_id;
};
struct wpa_bss_trans_info {
@@ -3364,6 +3403,17 @@
size_t ies_len);
/**
+ * get_scan_results - Fetch the latest scan results
+ * @priv: Private driver interface data
+ * @bssid: Return results only for the specified BSSID, %NULL for all
+ *
+ * Returns: Allocated buffer of scan results (caller is responsible for
+ * freeing the data structure) on success, NULL on failure
+ */
+ struct wpa_scan_results * (*get_scan_results)(void *priv,
+ const u8 *bssid);
+
+ /**
* get_scan_results2 - Fetch the latest scan results
* @priv: private driver interface data
*
@@ -3564,13 +3614,15 @@
/**
* flush - Flush all association stations (AP only)
* @priv: Private driver interface data
+ * @link_id: In case of MLO, valid link ID on which all associated
+ * stations will be flushed, -1 otherwise.
* Returns: 0 on success, -1 on failure
*
* This function requests the driver to disassociate all associated
* stations. This function does not need to be implemented if the
* driver does not process association frames internally.
*/
- int (*flush)(void *priv);
+ int (*flush)(void *priv, int link_id);
/**
* set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
@@ -4547,13 +4599,14 @@
/**
* stop_ap - Removes beacon from AP
* @priv: Private driver interface data
+ * @link_id: Link ID of the specified link; -1 for non-MLD
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This optional function can be used to disable AP mode related
* configuration. Unlike deinit_ap, it does not change to station
* mode.
*/
- int (*stop_ap)(void *priv);
+ int (*stop_ap)(void *priv, int link_id);
/**
* get_survey - Retrieve survey data
@@ -5133,9 +5186,44 @@
* @priv: Private driver interface data
* @link_id: The link ID
* @addr: The MAC address to use for the link
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
* Returns: 0 on success, negative value on failure
*/
- int (*link_add)(void *priv, u8 link_id, const u8 *addr);
+ int (*link_add)(void *priv, u8 link_id, const u8 *addr, void *bss_ctx);
+
+ /**
+ * link_remove - Remove a link from the AP MLD interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name of the virtual interface from where the link
+ * is to be removed.
+ * @link_id: Valid link ID to remove
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*link_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id);
+
+ /**
+ * 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
+ *
+ * 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.
+ */
+ bool (*is_drv_shared)(void *priv, void *bss_ctx);
+
+ /**
+ * link_sta_remove - Remove a link STA from an MLD STA
+ * @priv: Private driver interface data
+ * @link_id: The link ID which the link STA is using
+ * @addr: The MLD MAC address of the MLD STA
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*link_sta_remove)(void *priv, u8 link_id, const u8 *addr);
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
@@ -6296,6 +6384,14 @@
void *drv_priv;
/**
+ * ctx - Pointer to store ctx of private BSS information
+ *
+ * If not set to NULL, this is used for forwarding the packet
+ * to right link BSS of ML BSS.
+ */
+ void *ctx;
+
+ /**
* freq - Frequency (in MHz) on which the frame was received
*/
int freq;
@@ -6345,6 +6441,8 @@
* (if available).
* @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
* is set.
+ * @scan_cookie: Unique identification representing the corresponding
+ * scan request. 0 if no unique identification is available.
*/
struct scan_info {
int aborted;
@@ -6356,6 +6454,7 @@
int nl_scan_event;
u64 scan_start_tsf;
u8 scan_start_tsf_bssid[ETH_ALEN];
+ u64 scan_cookie;
} scan_info;
/**
@@ -6535,6 +6634,7 @@
/**
* struct dfs_event - Data for radar detected events
* @freq: Frequency of the channel in MHz
+ * @link_id: If >= 0, Link ID of the MLO link
*/
struct dfs_event {
int freq;
@@ -6543,6 +6643,7 @@
enum chan_width chan_width;
int cf1;
int cf2;
+ int link_id;
} dfs_event;
/**
@@ -6561,11 +6662,22 @@
* @initiator: Initiator of the regulatory change
* @type: Regulatory change type
* @alpha2: Country code (or "" if not available)
+ * @beacon_hint_before: Data for frequency attributes before beacon hint
+ * event if initiator == REGDOM_BEACON_HINT
+ * @beacon_hint_after: Data for frequency attributes after beacon hint
+ * event if initiator == REGDOM_BEACON_HINT
*/
struct channel_list_changed {
enum reg_change_initiator initiator;
enum reg_type type;
char alpha2[3];
+ struct frequency_attrs {
+ unsigned int freq;
+ unsigned int max_tx_power;
+ bool disabled;
+ bool no_ir;
+ bool radar;
+ } beacon_hint_before, beacon_hint_after;
} channel_list_changed;
/**
@@ -6608,7 +6720,9 @@
* @ch_width: Selected Channel width by driver. Driver may choose to
* change hostapd configured ACS channel width due driver internal
* channel restrictions.
- * hw_mode: Selected band (used with hw_mode=any)
+ * @hw_mode: Selected band (used with hw_mode=any)
+ * @puncture_bitmap: Indicate the puncturing channels
+ * @link_id: Indicate the link id if operating as AP MLD; -1 otherwise
*/
struct acs_selected_channels {
unsigned int pri_freq;
@@ -6619,6 +6733,7 @@
u16 ch_width;
enum hostapd_hw_mode hw_mode;
u16 puncture_bitmap;
+ int link_id;
} acs_selected_channels;
/**
@@ -6696,6 +6811,7 @@
*/
struct bss_color_collision {
u64 bitmap;
+ int link_id;
} bss_color_collision;
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 59f65b8..7186330 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -632,7 +632,7 @@
static int
-atheros_flush(void *priv)
+atheros_flush(void *priv, int link_id)
{
u8 allsta[IEEE80211_ADDR_LEN];
os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
@@ -659,7 +659,7 @@
&stats, sizeof(stats))) {
wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
MACSTR ")", __func__, MAC2STR(addr));
- if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+ if (ether_addr_equal(addr, drv->acct_mac)) {
os_memcpy(data, &drv->acct_data, sizeof(*data));
return 0;
}
@@ -892,7 +892,7 @@
}
if (stype == WLAN_FC_STYPE_ACTION &&
- (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 ||
+ (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;
@@ -901,7 +901,7 @@
return;
}
- if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(drv->own_addr, mgmt->bssid)) {
wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
__func__);
return;
@@ -1226,7 +1226,7 @@
no_ie:
drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0);
- if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 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));
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 850637f..82d8a01 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -946,7 +946,7 @@
static int
-bsd_flush(void *priv)
+bsd_flush(void *priv, int link_id)
{
u8 allsta[IEEE80211_ADDR_LEN];
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index d3520aa..3aa5860 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -572,7 +572,7 @@
}
-static int hostap_flush(void *priv)
+static int hostap_flush(void *priv, int link_id)
{
struct hostap_driver_data *drv = priv;
struct prism2_hostapd_param param;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index b32e009..0351705 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -1248,7 +1248,7 @@
prev = NULL;
entry = drv->pmkid;
while (entry) {
- if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(entry->bssid, bssid))
break;
prev = entry;
entry = entry->next;
@@ -1293,7 +1293,7 @@
entry = drv->pmkid;
prev = NULL;
while (entry) {
- if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(entry->bssid, bssid) &&
os_memcmp(entry->pmkid, pmkid, 16) == 0) {
if (prev)
prev->next = entry->next;
@@ -1434,7 +1434,7 @@
pos = (char *) &b->Bssid[0];
for (i = 0; i < b->NumberOfItems; i++) {
NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
- if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(drv->bssid, bss->MacAddress) &&
bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
data.assoc_info.beacon_ies =
((u8 *) bss->IEs) +
@@ -1477,7 +1477,7 @@
}
} else {
/* Connected */
- if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(drv->bssid, bssid)) {
os_memcpy(drv->bssid, bssid, ETH_ALEN);
wpa_driver_ndis_get_associnfo(drv);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index ac2f457..b2b909e 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -12,13 +12,12 @@
#include "includes.h"
#include <sys/types.h>
+#include <sys/utsname.h>
#include <fcntl.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
-#ifdef CONFIG_LIBNL3_ROUTE
-#include <netlink/route/neighbour.h>
-#endif /* CONFIG_LIBNL3_ROUTE */
+#include <netlink/genl/family.h>
#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/errqueue.h>
@@ -170,9 +169,9 @@
static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
- int save_cookie,
- int no_cck, int no_ack, int offchanok,
- const u16 *csa_offs, size_t csa_offs_len);
+ int save_cookie, int no_cck, int no_ack,
+ int offchanok, const u16 *csa_offs,
+ size_t csa_offs_len, int link_id);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
@@ -195,6 +194,7 @@
static int i802_set_iface_flags(struct i802_bss *bss, int up);
static int nl80211_set_param(void *priv, const char *param);
+static void nl80211_remove_links(struct i802_bss *bss);
#ifdef CONFIG_MESH
static int nl80211_put_mesh_config(struct nl_msg *msg,
struct wpa_driver_mesh_bss_params *params);
@@ -345,17 +345,29 @@
return NL_SKIP;
}
+struct nl80211_ack_err_args {
+ int err;
+ struct nl_msg *orig_msg;
+ struct nl80211_err_info *err_info;
+};
+
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
+ struct nl80211_ack_err_args *err_args = arg;
struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
+ struct nlmsghdr *orig_nlh = nlmsg_hdr(err_args->orig_msg);
int len = nlh->nlmsg_len;
struct nlattr *attrs;
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
- int *ret = arg;
int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
+ struct nlattr *mlo_links, *link_attr;
+ u32 offset;
+ int rem;
- *ret = err->error;
+ err_args->err = err->error;
+ if (err_args->err_info)
+ err_args->err_info->link_id = -1;
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
return NL_SKIP;
@@ -377,6 +389,41 @@
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 */
+
+ 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]);
+
+ mlo_links = nlmsg_find_attr(orig_nlh, GENL_HDRLEN,
+ NL80211_ATTR_MLO_LINKS);
+ if (!mlo_links)
+ return NL_SKIP;
+
+ nla_for_each_nested(link_attr, mlo_links, rem) {
+ struct nlattr *link_id;
+ size_t link_offset = (u8 *) link_attr - (u8 *) orig_nlh;
+
+ if (offset < link_offset ||
+ offset >= link_offset + link_attr->nla_len)
+ continue;
+
+ link_id = nla_find(nla_data(link_attr), nla_len(link_attr),
+ NL80211_ATTR_MLO_LINK_ID);
+ if (link_id) {
+ err_args->err_info->link_id = nla_get_u8(link_id);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: kernel reports error for link: %d",
+ err_args->err_info->link_id);
+ break;
+ }
+ }
+
return NL_SKIP;
}
@@ -409,20 +456,61 @@
}
-static 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)
+static int send_event_marker(struct wpa_driver_nl80211_data *drv)
{
- struct nl_cb *cb;
- int err = -ENOMEM, opt;
+ struct nl_sock *handle;
+ struct nl_msg *msg;
+ struct nlmsghdr *hdr;
+ int res = 0;
+ int err = -NLE_NOMEM;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto out;
+
+ /* We only care about the returned sequence number for matching. */
+ if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES))
+ goto out;
+
+ handle = (void *) (((intptr_t) drv->global->nl_event) ^
+ ELOOP_SOCKET_INVALID);
+
+ err = nl_send_auto_complete(handle, msg);
+ if (err < 0)
+ goto out;
+
+ hdr = nlmsg_hdr(msg);
+ res = hdr->nlmsg_seq;
+
+out:
+ nlmsg_free(msg);
+ if (err)
+ 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 nl_cb *cb, *s_nl_cb;
+ struct nl80211_ack_err_args err;
+ int opt;
if (!msg)
return -ENOMEM;
- cb = nl_cb_clone(global->nl_cb);
+ err.err = -ENOMEM;
+
+ s_nl_cb = nl_socket_get_cb(nl_handle);
+ cb = nl_cb_clone(s_nl_cb);
+ nl_cb_put(s_nl_cb);
if (!cb)
goto out;
@@ -436,26 +524,28 @@
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_CAP_ACK, &opt, sizeof(opt));
- err = nl_send_auto_complete(nl_handle, msg);
- if (err < 0) {
+ err.err = nl_send_auto_complete(nl_handle, msg);
+ if (err.err < 0) {
wpa_printf(MSG_INFO,
"nl80211: nl_send_auto_complete() failed: %s",
- nl_geterror(err));
+ nl_geterror(err.err));
/* Need to convert libnl error code to an errno value. For now,
* just hardcode this to EBADF; the real error reason is shown
* in that error print above. */
- err = -EBADF;
+ err.err = -EBADF;
goto out;
}
- err = 1;
+ err.err = 1;
+ err.orig_msg = msg;
+ err.err_info = err_info;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
- nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
if (ack_handler_custom) {
struct nl80211_ack_ext_arg *ext_arg = ack_data;
- ext_arg->err = &err;
+ ext_arg->err = &err.err;
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
ack_handler_custom, ack_data);
} else {
@@ -466,7 +556,7 @@
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
- while (err > 0) {
+ while (err.err > 0) {
int res = nl_recvmsgs(nl_handle, cb);
if (res == -NLE_DUMP_INTR) {
@@ -481,7 +571,7 @@
* will stop and return an error. */
wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
nl_geterror(res));
- err = -EAGAIN;
+ err.err = -EAGAIN;
} else if (res < 0) {
wpa_printf(MSG_INFO,
"nl80211: %s->nl_recvmsgs failed: %d (%s)",
@@ -493,89 +583,20 @@
/* Always clear the message as it can potentially contain keys */
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
- return err;
+ return err.err;
}
-int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
- 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)
+static int nl80211_put_control_port(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg)
{
- return send_and_recv(drv->global, drv->global->nl, msg,
- valid_handler, valid_data,
- ack_handler_custom, ack_data);
-}
-
-
-/* Use this method to mark that it is necessary to own the connection/interface
- * for this operation.
- * handle may be set to NULL, to get the same behavior as send_and_recv_msgs().
- * set_owner can be used to mark this socket for receiving control port frames.
- */
-static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv,
- struct nl_msg *msg,
- struct nl_sock *handle, int set_owner,
- int (*valid_handler)(struct nl_msg *,
- void *),
- void *valid_data,
- int (*ack_handler_custom)(struct nl_msg *,
- void *),
- void *ack_data)
-{
- if (!msg)
- return -ENOMEM;
-
- /* Control port over nl80211 needs the flags and attributes below.
- *
- * The Linux kernel has initial checks for them (in nl80211.c) like:
- * validate_pae_over_nl80211(...)
- * or final checks like:
- * dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid
- *
- * Final operations (e.g., disassociate) don't need to set these
- * attributes, but they have to be performed on the socket, which has
- * the connection owner property set in the kernel.
- */
- if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) &&
- handle && set_owner &&
- (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) ||
- nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER) ||
- nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH)))
+ if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT) ||
+ nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+ ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) &&
+ (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) ||
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH))))
return -1;
-
- return send_and_recv(drv->global, handle ? handle : drv->global->nl,
- msg, valid_handler, valid_data,
- ack_handler_custom, ack_data);
-}
-
-
-static int
-send_and_recv_msgs_connect_handle(struct wpa_driver_nl80211_data *drv,
- struct nl_msg *msg, struct i802_bss *bss,
- int set_owner)
-{
- struct nl_sock *nl_connect = get_connect_handle(bss);
-
- if (nl_connect)
- return send_and_recv_msgs_owner(drv, msg, nl_connect, set_owner,
- process_bss_event, bss, NULL,
- NULL);
- else
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
-}
-
-
-struct nl_sock * get_connect_handle(struct i802_bss *bss)
-{
- if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) ||
- bss->use_nl_connect)
- return bss->nl_connect;
-
- return NULL;
+ return 0;
}
@@ -634,7 +655,7 @@
}
ret = send_and_recv(global, global->nl, msg, family_handler, &res,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret == 0)
ret = res.id;
return ret;
@@ -762,8 +783,7 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
- if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
- NULL, NULL) == 0)
+ if (send_and_recv_resp(bss->drv, msg, netdev_info_handler, &data) == 0)
return data.wiphy_idx;
return -1;
}
@@ -780,8 +800,7 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return NL80211_IFTYPE_UNSPECIFIED;
- if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
- NULL, NULL) == 0)
+ if (send_and_recv_resp(bss->drv, msg, netdev_info_handler, &data) == 0)
return data.nlmode;
return NL80211_IFTYPE_UNSPECIFIED;
}
@@ -797,8 +816,7 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
- return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
- NULL, NULL);
+ return send_and_recv_resp(bss->drv, msg, netdev_info_handler, &data);
}
@@ -810,8 +828,7 @@
};
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)) ||
- send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
- NULL, NULL))
+ send_and_recv_resp(bss->drv, msg, netdev_info_handler, &data))
return -1;
return data.use_4addr;
}
@@ -834,7 +851,7 @@
}
ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
"failed: ret=%d (%s)",
@@ -1091,8 +1108,8 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- if (send_and_recv_msgs(drv, msg, get_mlo_info,
- &drv->sta_mlo_info, NULL, NULL))
+ if (send_and_recv_resp(drv, msg, get_mlo_info,
+ &drv->sta_mlo_info))
return -1;
}
@@ -1249,7 +1266,7 @@
wpa_printf(MSG_DEBUG,
"nl80211: %s: failed to re-read MAC address",
bss->ifname);
- } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+ } else if (bss && !ether_addr_equal(addr, bss->addr)) {
wpa_printf(MSG_DEBUG,
"nl80211: Own MAC address on ifindex %d (%s) changed from "
MACSTR " to " MACSTR,
@@ -1637,8 +1654,8 @@
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
- ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
- &arg, NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1671,8 +1688,8 @@
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
- ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
- &arg, NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1759,8 +1776,7 @@
sig_change->frequency = drv->assoc_freq;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
- NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_link_noise, sig_change);
}
@@ -1824,7 +1840,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_channel_info, ci);
}
@@ -1873,7 +1889,7 @@
nlmsg_free(msg);
return -EINVAL;
}
- if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
+ if (send_and_recv_cmd(drv, msg))
return -EINVAL;
return 0;
}
@@ -1919,8 +1935,7 @@
}
alpha2[0] = '\0';
- ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, nl80211_get_country, alpha2);
if (!alpha2[0])
ret = -1;
@@ -1930,6 +1945,8 @@
static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
{
+ struct nl_cache *cache = NULL;
+ struct genl_family *family = NULL;
int ret;
global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
@@ -2001,6 +2018,29 @@
/* Continue without vendor events */
}
+ /* Resolve maxattr for kernel support checks */
+ ret = genl_ctrl_alloc_cache(global->nl, &cache);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not allocate genl cache: %d (%s)",
+ ret, nl_geterror(ret));
+ goto err;
+ }
+
+ family = genl_ctrl_search(cache, global->nl80211_id);
+ if (!family) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not get nl80211 family from cache: %d (%s)",
+ ret, nl_geterror(ret));
+ goto err;
+ }
+
+ global->nl80211_maxattr = genl_family_get_maxattr(family);
+ wpa_printf(MSG_DEBUG, "nl80211: Maximum supported attribute ID: %u",
+ global->nl80211_maxattr);
+ 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,
@@ -2013,6 +2053,8 @@
return 0;
err:
+ genl_family_put(family);
+ nl_cache_free(cache);
nl_destroy_handles(&global->nl_event);
nl_destroy_handles(&global->nl);
nl_cb_put(global->nl_cb);
@@ -2250,7 +2292,26 @@
{
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
- unsigned int i;
+ char path[128], buf[200], *pos;
+ ssize_t len;
+ int ret;
+
+ ret = os_snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
+ ifname);
+ if (!os_snprintf_error(sizeof(path), ret)) {
+ len = readlink(path, buf, sizeof(buf));
+ if (len > 0 && (size_t) len < sizeof(buf)) {
+ buf[len] = '\0';
+ pos = strrchr(buf, '/');
+ if (pos)
+ pos++;
+ else
+ pos = buf;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Initialize interface %s (driver: %s)",
+ ifname, pos);
+ }
+ }
if (global_priv == NULL)
return NULL;
@@ -2330,16 +2391,12 @@
}
/*
- * Set the default link to be the first one, and set its address to that
- * of the interface.
+ * Use link ID 0 for the single "link" of a non-MLD.
*/
+ bss->valid_links = 0;
bss->flink = &bss->links[0];
- bss->n_links = 1;
os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
return bss;
failed:
@@ -2389,7 +2446,7 @@
}
ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
"failed (type=%u): ret=%d (%s)",
@@ -2425,12 +2482,20 @@
}
-static int nl80211_register_action_frame(struct i802_bss *bss,
- const u8 *match, size_t match_len)
+static int nl80211_register_action_frame2(struct i802_bss *bss,
+ const u8 *match, size_t match_len,
+ bool multicast)
{
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
return nl80211_register_frame(bss, bss->nl_mgmt,
- type, match, match_len, false);
+ type, match, match_len, multicast);
+}
+
+
+static int nl80211_register_action_frame(struct i802_bss *bss,
+ const u8 *match, size_t match_len)
+{
+ return nl80211_register_action_frame2(bss, match, match_len, false);
}
@@ -2505,6 +2570,17 @@
5) < 0)
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) {
+ /* fallback to non-multicast */
+ if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6,
+ false) < 0)
+ ret = -1;
+ }
+#undef NAN_PUB_ACTION
+#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_DPP
/* DPP Public Action */
if (nl80211_register_action_frame(bss,
@@ -2650,7 +2726,7 @@
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
"failed: ret=%d (%s)",
@@ -2804,7 +2880,7 @@
int ret;
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(bss->drv, msg);
wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
bss->ifname, (long long unsigned int) bss->wdev_id,
@@ -2819,7 +2895,7 @@
msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
NL80211_CMD_STOP_P2P_DEVICE);
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(bss->drv, msg);
wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
start ? "Start" : "Stop",
@@ -2890,8 +2966,7 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, qca_vendor_test_cmd_handler, drv);
wpa_printf(MSG_DEBUG,
"nl80211: QCA vendor test command returned %d (%s)",
ret, strerror(-ret));
@@ -3008,46 +3083,46 @@
static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
- struct i802_link *link)
+ int link_id)
{
struct nl_msg *msg;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
if (!link->beacon_set)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
- drv->ifindex);
+ bss->ifindex);
link->beacon_set = 0;
link->freq = 0;
nl80211_put_wiphy_data_ap(bss);
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
if (!msg)
return -ENOBUFS;
- if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+ if (link_id != NL80211_DRV_LINK_ID_NA) {
wpa_printf(MSG_DEBUG,
"nl80211: MLD: stop beaconing on link=%u",
- link->link_id);
+ link_id);
- if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
- link->link_id)) {
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
nlmsg_free(msg);
return -ENOBUFS;
}
}
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
{
- unsigned int i;
+ int link_id;
- for (i = 0; i < bss->n_links; i++)
- wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+ for_each_link_default(bss->valid_links, link_id, NL80211_DRV_LINK_ID_NA)
+ wpa_driver_nl80211_del_beacon(bss, link_id);
}
@@ -3099,8 +3174,10 @@
nl80211_remove_monitor_interface(drv);
- if (is_ap_interface(drv->nlmode))
+ if (is_ap_interface(drv->nlmode)) {
wpa_driver_nl80211_del_beacon_all(bss);
+ nl80211_remove_links(bss);
+ }
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
@@ -3296,57 +3373,6 @@
return num_suites;
}
-#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
- struct drv_acs_params *params)
-{
- struct nl_msg *msg;
- struct nlattr *data;
- int freq_list_len;
- int ret = -1;
-
- freq_list_len = int_array_len(params->freq_list);
- wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
- __func__, freq_list_len);
-
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
- if (!msg ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
- BRCM_VENDOR_SCMD_ACS) ||
- !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
- nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode) ||
- nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
- params->ht_enabled) ||
- nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
- params->ht40_enabled) ||
- nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
- params->vht_enabled) ||
- nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) ||
- (freq_list_len > 0 &&
- nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
- sizeof(int) * freq_list_len, params->freq_list)))
- goto fail;
- nla_nest_end(msg, data);
-
- wpa_printf(MSG_DEBUG,
- "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
- params->hw_mode, params->ht_enabled, params->ht40_enabled,
- params->vht_enabled, params->ch_width);
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
- if (ret) {
- wpa_printf(MSG_ERROR,
- "nl80211: BRCM Failed to invoke driver ACS function: %s",
- strerror(errno));
- }
-
- msg = NULL;
-fail:
- nlmsg_free(msg);
- return ret;
-}
-#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
#if (defined(CONFIG_DRIVER_NL80211_BRCM) && !defined(WIFI_BRCM_OPEN_SOURCE_MULTI_AKM)) || \
defined(CONFIG_DRIVER_NL80211_SYNA)
@@ -3368,6 +3394,7 @@
#endif /* (CONFIG_DRIVER_NL80211_BRCM && !WIFI_BRCM_OPEN_SOURCE_MULTI_AKM) ||
* CONFIG_DRIVER_NL80211_SYNA */
+
#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)
@@ -3387,7 +3414,7 @@
nlmsg_free(msg);
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Key management set key failed: ret=%d (%s)",
@@ -3419,7 +3446,7 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Key mgmt set key failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -3458,7 +3485,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -3639,7 +3666,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
if (ret)
@@ -3708,7 +3735,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG,
"nl80211: set_key default failed; err=%d %s",
@@ -3821,7 +3848,6 @@
{
int ret;
struct nl_msg *msg;
- struct nl_sock *nl_connect = get_connect_handle(bss);
if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
@@ -3832,11 +3858,8 @@
return -1;
}
- if (nl_connect)
- ret = send_and_recv(drv->global, nl_connect, msg,
- process_bss_event, bss, NULL, NULL);
- else
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv(drv->global, bss->nl_connect, msg,
+ NULL, NULL, NULL, NULL, NULL);
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed: reason=%u ret=%d (%s)",
@@ -3851,7 +3874,6 @@
struct i802_bss *bss)
{
int ret;
- int drv_associated = drv->associated;
wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
nl80211_mark_disconnected(drv);
@@ -3862,7 +3884,8 @@
* For locally generated disconnect, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
- drv->ignore_next_local_disconnect = drv_associated && (ret == 0);
+ if (ret == 0)
+ drv->ignore_next_local_disconnect = send_event_marker(drv);
return ret;
}
@@ -3873,7 +3896,6 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret;
- int drv_associated = drv->associated;
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
nl80211_mark_disconnected(drv);
@@ -3891,7 +3913,8 @@
* For locally generated deauthenticate, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
- drv->ignore_next_local_deauth = drv_associated && (ret == 0);
+ if (ret == 0)
+ drv->ignore_next_local_deauth = send_event_marker(drv);
return ret;
}
@@ -4114,7 +4137,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -4194,6 +4217,7 @@
{
struct wpa_driver_auth_params params;
struct i802_bss *bss = drv->first_bss;
+ u8 ap_mld_addr[ETH_ALEN];
int i;
wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
@@ -4219,8 +4243,10 @@
params.auth_data_len = drv->auth_data_len;
params.mld = drv->auth_mld;
params.mld_link_id = drv->auth_mld_link_id;
- if (drv->auth_mld)
- params.ap_mld_addr = drv->auth_ap_mld_addr;
+ if (drv->auth_mld) {
+ os_memcpy(ap_mld_addr, drv->auth_ap_mld_addr, ETH_ALEN);
+ params.ap_mld_addr = ap_mld_addr;
+ }
for (i = 0; i < 4; i++) {
if (drv->auth_wep_key_len[i]) {
@@ -4236,14 +4262,11 @@
struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
{
- unsigned int i;
+ if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+ return bss->flink;
- for (i = 0; i < bss->n_links; i++) {
- if (bss->links[i].link_id != link_id)
- continue;
-
- return &bss->links[i];
- }
+ if (BIT(link_id) & bss->valid_links)
+ return &bss->links[link_id];
return bss->flink;
}
@@ -4260,10 +4283,10 @@
static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
bool bss_freq_debug)
{
- size_t i;
+ u8 i;
- for (i = 0; i < bss->n_links; i++) {
- if (os_memcmp(bss->links[i].addr, addr, ETH_ALEN) == 0) {
+ for_each_link(bss->valid_links, i) {
+ if (ether_addr_equal(bss->links[i].addr, addr)) {
wpa_printf(MSG_DEBUG,
"nl80211: Use link freq=%d for address "
MACSTR,
@@ -4299,9 +4322,11 @@
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
- wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR
+ wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR " sa=" MACSTR
+ " bssid=" MACSTR
" noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u no_encrypt=%d fc=0x%x (%s) nlmode=%d",
- MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+ MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+ noack, freq, no_cck, offchanok, wait_time,
no_encrypt, fc, fc2str(fc), drv->nlmode);
if ((is_sta_interface(drv->nlmode) ||
@@ -4428,7 +4453,7 @@
wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame_cmd");
res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
use_cookie, no_cck, noack, offchanok,
- csa_offs, csa_offs_len);
+ csa_offs, csa_offs_len, link_id);
if (!res)
drv->send_frame_link_id = link_id;
@@ -4454,7 +4479,7 @@
static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
int slot, int ht_opmode, int ap_isolate,
- const int *basic_rates)
+ const int *basic_rates, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
@@ -4470,12 +4495,14 @@
nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
(ap_isolate >= 0 &&
nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
- nl80211_put_basic_rates(msg, basic_rates)) {
+ nl80211_put_basic_rates(msg, basic_rates) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))) {
nlmsg_free(msg);
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -4534,7 +4561,7 @@
}
nlmsg_free(acl);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
ret, strerror(-ret));
@@ -4586,7 +4613,7 @@
return ret;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Mesh config set failed: %d (%s)",
@@ -4732,7 +4759,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
switch (ret) {
case 0:
@@ -4818,7 +4845,7 @@
static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
struct nl_msg *msg,
- struct wpa_driver_ap_params *params)
+ struct unsol_bcast_probe_resp *ubpr)
{
struct nlattr *attr;
@@ -4831,15 +4858,15 @@
wpa_printf(MSG_DEBUG,
"nl80211: Unsolicited broadcast Probe Response frame interval: %u",
- params->unsol_bcast_probe_resp_interval);
+ ubpr->unsol_bcast_probe_resp_interval);
attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP);
if (!attr ||
nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
- params->unsol_bcast_probe_resp_interval) ||
- (params->unsol_bcast_probe_resp_tmpl &&
+ ubpr->unsol_bcast_probe_resp_interval) ||
+ (ubpr->unsol_bcast_probe_resp_tmpl &&
nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
- params->unsol_bcast_probe_resp_tmpl_len,
- params->unsol_bcast_probe_resp_tmpl)))
+ ubpr->unsol_bcast_probe_resp_tmpl_len,
+ ubpr->unsol_bcast_probe_resp_tmpl)))
return -1;
nla_nest_end(msg, attr);
@@ -4926,7 +4953,8 @@
#ifdef CONFIG_DRIVER_NL80211_QCA
static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv,
- const int *freqs, int num_freqs)
+ const int *freqs, int num_freqs,
+ int link_id)
{
struct nl_msg *msg;
struct nlattr *params, *freqs_list;
@@ -4944,6 +4972,10 @@
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
goto err;
+ if (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINK_ID, link_id))
+ goto err;
+
freqs_list = nla_nest_start(
msg, QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST);
if (!freqs_list)
@@ -4957,7 +4989,7 @@
nla_nest_end(msg, freqs_list);
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_ERROR,
"nl80211: Failed set AP alllowed frequency list: %d (%s)",
@@ -5094,20 +5126,18 @@
#endif /* CONFIG_MESH */
if (params->mld_ap) {
- size_t i;
-
- for (i = 0; i < bss->n_links; i++) {
- if (bss->links[i].link_id == params->mld_link_id) {
- link = &bss->links[i];
- break;
- }
- }
-
- if (i == bss->n_links) {
- wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
- params->mld_link_id);
+ if (!nl80211_link_valid(bss->valid_links,
+ params->mld_link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Link ID=%u invalid (valid: 0x%04x)",
+ params->mld_link_id, bss->valid_links);
return -EINVAL;
}
+
+ link = nl80211_get_link(bss, params->mld_link_id);
+ } else if (bss->valid_links) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLD configuration expected");
+ return -EINVAL;
}
beacon_set = params->reenable ? 0 : link->beacon_set;
@@ -5148,13 +5178,12 @@
params->mld_link_id);
if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
- params->mld_link_id) ||
- (params->freq &&
- nl80211_put_freq_params(msg, params->freq) < 0))
+ params->mld_link_id))
goto fail;
- nl80211_link_set_freq(bss, params->mld_link_id,
- params->freq->freq);
+ if (params->freq)
+ nl80211_link_set_freq(bss, params->mld_link_id,
+ params->freq->freq);
}
if (params->proberesp && params->proberesp_len) {
@@ -5216,41 +5245,35 @@
params->key_mgmt_suites);
num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
suites, ARRAY_SIZE(suites));
- if (num_suites > NL80211_MAX_NR_AKM_SUITES)
+ if ((unsigned int) num_suites > drv->capa.max_num_akms)
wpa_printf(MSG_DEBUG,
- "nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
- num_suites);
+ "nl80211: Not enough room for all AKM suites (num_suites=%d > %d)",
+ num_suites, drv->capa.max_num_akms);
else if (num_suites &&
nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
suites))
goto fail;
- if ((params->key_mgmt_suites & WPA_KEY_MGMT_PSK) &&
+ if (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suites) &&
(drv->capa.flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK) &&
- params->psk_len) {
- if (nla_put(msg, NL80211_ATTR_PMK, params->psk_len, params->psk)) {
- wpa_printf(MSG_ERROR, "nl80211: Setting PSK failed");
- goto fail;
- } else
- wpa_printf(MSG_DEBUG, "nl80211: Setting PSK for offload");
- }
+ params->psk_len &&
+ nla_put(msg, NL80211_ATTR_PMK, params->psk_len, params->psk))
+ goto fail;
if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
(drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP) &&
- params->sae_password) {
- if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
- os_strlen(params->sae_password), params->sae_password)) {
- wpa_printf(MSG_ERROR, "nl80211: Setting SAE password failed");
- goto fail;
- } else
- wpa_printf(MSG_DEBUG, "nl80211: SAE password for offload");
- }
+ params->sae_password &&
+ nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
+ os_strlen(params->sae_password), params->sae_password))
+ goto fail;
+
+ if (nl80211_put_control_port(drv, msg) < 0)
+ goto fail;
if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
(!params->pairwise_ciphers ||
params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
- (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
goto fail;
if (drv->device_ap_sme) {
@@ -5269,7 +5292,9 @@
flags |= NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT;
- if (nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags))
+ if (nl80211_attr_supported(drv,
+ NL80211_ATTR_AP_SETTINGS_FLAGS) &&
+ nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags))
goto fail;
}
@@ -5366,6 +5391,9 @@
nla_nest_end(msg, ftm);
}
+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+ goto fail;
+
#ifdef CONFIG_IEEE80211AX
if (params->he_spr_ctrl) {
struct nlattr *spr;
@@ -5400,10 +5428,8 @@
nla_nest_end(msg, spr);
}
- if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
- goto fail;
-
- if (params->freq && params->freq->he_enabled) {
+ if (params->freq && params->freq->he_enabled &&
+ nl80211_attr_supported(drv, NL80211_ATTR_HE_BSS_COLOR)) {
struct nlattr *bss_color;
bss_color = nla_nest_start(msg, NL80211_ATTR_HE_BSS_COLOR);
@@ -5425,8 +5451,8 @@
goto fail;
}
- if (params->unsol_bcast_probe_resp_interval &&
- nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
+ if (params->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, ¶ms->ubpr) < 0)
goto fail;
if (nl80211_mbssid(msg, params) < 0)
@@ -5455,10 +5481,15 @@
#ifdef CONFIG_DRIVER_NL80211_QCA
if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs)
qca_set_allowed_ap_freqs(drv, params->allowed_freqs,
- int_array_len(params->allowed_freqs));
+ int_array_len(params->allowed_freqs),
+ params->mld_ap ? params->mld_link_id :
+ NL80211_DRV_LINK_ID_NA);
#endif /* CONFIG_DRIVER_NL80211_QCA */
- ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
+ goto fail;
+ ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
ret, strerror(-ret));
@@ -5466,7 +5497,9 @@
link->beacon_set = 1;
nl80211_set_bss(bss, params->cts_protect, params->preamble,
params->short_slot_time, params->ht_opmode,
- params->isolate, params->basic_rates);
+ params->isolate, params->basic_rates,
+ params->mld_ap ? params->mld_link_id :
+ NL80211_DRV_LINK_ID_NA);
nl80211_set_multicast_to_unicast(bss,
params->multicast_to_unicast);
if (beacon_set && params->freq &&
@@ -5513,27 +5546,6 @@
}
-static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
-{
- unsigned int i;
-
- if (link_id < 0)
- return false;
-
- for (i = 0; i < bss->n_links; i++) {
- wpa_printf(MSG_DEBUG, "nl80211: %s - i=%u, link_id=%u",
- __func__, i, bss->links[i].link_id);
- if (bss->links[i].link_id == NL80211_DRV_LINK_ID_NA)
- continue;
-
- if (bss->links[i].link_id == link_id)
- return true;
- }
-
- return false;
-}
-
-
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan)
{
@@ -5554,7 +5566,7 @@
return -1;
}
- if (nl80211_link_valid(bss, freq->link_id)) {
+ if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
freq->link_id);
@@ -5564,7 +5576,7 @@
}
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret == 0) {
nl80211_link_set_freq(bss, freq->link_id, freq->freq);
return 0;
@@ -5905,7 +5917,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: %s result: %d (%s)",
@@ -5920,26 +5932,25 @@
static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_addr;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->ifindex,
+ .ndm_family = AF_BRIDGE,
+ };
+ struct nl_msg *msg;
int err;
- rn = rtnl_neigh_alloc();
- if (!rn)
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
return;
- rtnl_neigh_set_family(rn, AF_BRIDGE);
- rtnl_neigh_set_ifindex(rn, bss->ifindex);
- nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
- if (!nl_addr) {
- rtnl_neigh_put(rn);
- return;
- }
- rtnl_neigh_set_lladdr(rn, nl_addr);
+ 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 = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+ 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),
@@ -5949,9 +5960,8 @@
MACSTR, MAC2STR(addr));
}
- nl_addr_put(nl_addr);
- rtnl_neigh_put(rn);
-#endif /* CONFIG_LIBNL3_ROUTE */
+errout:
+ nlmsg_free(msg);
}
@@ -5976,7 +5986,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
" --> %d (%s)",
bss->ifname, MAC2STR(addr), ret, strerror(-ret));
@@ -6007,7 +6017,7 @@
}
msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
- if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
+ if (send_and_recv_cmd(drv, msg) == 0)
return;
wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
}
@@ -6091,7 +6101,7 @@
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
- ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, handler, arg);
msg = NULL;
if (ret) {
fail:
@@ -6238,7 +6248,8 @@
nl80211_mgmt_unsubscribe(bss, "AP teardown");
nl80211_put_wiphy_data_ap(bss);
- bss->flink->beacon_set = 0;
+ if (bss->flink)
+ bss->flink->beacon_set = 0;
}
@@ -6272,8 +6283,8 @@
os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
ext_arg.ext_data = &cookie;
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
- ack_handler_cookie, &ext_arg);
+ ret = send_and_recv(bss->drv->global, bss->drv->global->nl, msg,
+ NULL, NULL, ack_handler_cookie, &ext_arg, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port failed: ret=%d (%s)",
@@ -6440,7 +6451,7 @@
if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
goto fail;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(bss->drv, msg);
fail:
nlmsg_free(msg);
return -ENOBUFS;
@@ -6463,7 +6474,7 @@
nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
goto fail;
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(bss->drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: SET_STATION[AIRTIME_WEIGHT] failed: ret=%d (%s)",
@@ -6513,7 +6524,8 @@
int ret;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
- ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+ ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+ NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -6643,7 +6655,7 @@
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA384) {
wpa_printf(MSG_DEBUG, " * control port");
- if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+ if (nl80211_put_control_port(drv, msg))
goto fail;
}
@@ -6660,7 +6672,10 @@
if (ret < 0)
goto fail;
- ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+ if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
+ goto fail;
+ ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+ NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
@@ -6743,7 +6758,6 @@
if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
struct wpa_driver_mld_params *mld_params = ¶ms->mld_params;
struct nlattr *links, *attr;
- int i;
u8 link_id;
wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR,
@@ -6759,31 +6773,8 @@
if (!links)
return -1;
- attr = nla_nest_start(msg, 0);
- if (!attr)
- return -1;
-
- /* First add the association link ID */
- link_id = mld_params->assoc_link_id;
- if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
- mld_params->mld_links[link_id].bssid) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
- mld_params->mld_links[link_id].freq))
- return -1;
-
- os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
- mld_params->mld_links[link_id].bssid, ETH_ALEN);
-
- nla_nest_end(msg, attr);
-
- for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
- link_id++) {
- if (!(mld_params->valid_links & BIT(link_id)) ||
- link_id == mld_params->assoc_link_id)
- continue;
-
- attr = nla_nest_start(msg, i);
+ for_each_link(mld_params->valid_links, link_id) {
+ attr = nla_nest_start(msg, 0);
if (!attr)
return -1;
@@ -6793,8 +6784,11 @@
mld_params->mld_links[link_id].bssid) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
mld_params->mld_links[link_id].freq) ||
+ (mld_params->mld_links[link_id].disabled &&
+ nla_put_flag(msg,
+ NL80211_ATTR_MLO_LINK_DISABLED)) ||
(mld_params->mld_links[link_id].ies &&
- mld_params->mld_links[i].ies_len &&
+ mld_params->mld_links[link_id].ies_len &&
nla_put(msg, NL80211_ATTR_IE,
mld_params->mld_links[link_id].ies_len,
mld_params->mld_links[link_id].ies)))
@@ -6804,7 +6798,6 @@
mld_params->mld_links[link_id].bssid,
ETH_ALEN);
nla_nest_end(msg, attr);
- i++;
}
nla_nest_end(msg, links);
@@ -6891,8 +6884,20 @@
if (params->wpa_proto & WPA_PROTO_WPA)
ver |= NL80211_WPA_VERSION_1;
- if (params->wpa_proto & WPA_PROTO_RSN)
- ver |= NL80211_WPA_VERSION_2;
+ if (params->wpa_proto & WPA_PROTO_RSN) {
+#if !defined(CONFIG_DRIVER_NL80211_BRCM) && !defined(CONFIG_DRIVER_NL80211_SYNA)
+ /*
+ * 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
+#endif
+ ver |= NL80211_WPA_VERSION_2;
+ }
wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
@@ -7086,15 +7091,14 @@
return -1;
}
- if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+ if (nl80211_put_control_port(drv, msg))
return -1;
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
(params->pairwise_suite == WPA_CIPHER_NONE ||
params->pairwise_suite == WPA_CIPHER_WEP104 ||
params->pairwise_suite == WPA_CIPHER_WEP40) &&
- (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
- nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
return -1;
if (params->rrm_used) {
@@ -7244,7 +7248,10 @@
if (ret)
goto fail;
- ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
+ goto fail;
+ ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -7302,6 +7309,7 @@
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl80211_err_info err_info;
int ret = -1;
struct nl_msg *msg;
@@ -7319,11 +7327,6 @@
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
- if (wpa_key_mgmt_sae(params->key_mgmt_suite) ||
- wpa_key_mgmt_sae(params->allowed_key_mgmts))
- bss->use_nl_connect = 1;
- else
- bss->use_nl_connect = 0;
return wpa_driver_nl80211_connect(drv, params, bss);
}
@@ -7360,13 +7363,42 @@
goto fail;
}
- ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
- msg = NULL;
+ 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,
+ msg, NULL, NULL, NULL, NULL, &err_info);
+ msg = NULL;
+ } else {
+ int i;
+
+ /* Error and force TEST_FAIL checking for each link */
+ ret = -EINVAL;
+ for_each_link(params->mld_params.valid_links, i) {
+ if (TEST_FAIL_TAG("link"))
+ err_info.link_id = i;
+ }
+ }
+
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed (assoc): ret=%d (%s)",
ret, strerror(-ret));
nl80211_dump_scan(drv);
+
+ /* Mark failed link within params */
+ if (err_info.link_id >= 0) {
+ if (err_info.link_id >= MAX_NUM_MLD_LINKS ||
+ !(params->mld_params.valid_links &
+ BIT(err_info.link_id))) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid errorred link_id %d",
+ err_info.link_id);
+ goto fail;
+ }
+ params->mld_params.mld_links[err_info.link_id].error =
+ ret;
+ }
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Association request send successfully");
@@ -7391,7 +7423,7 @@
if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (!ret)
return 0;
@@ -7658,7 +7690,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
@@ -7725,7 +7757,7 @@
}
os_memset(seq, 0, 6);
- res = send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
+ res = send_and_recv_resp(drv, msg, get_key_handler, seq);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to get current TX sequence for a key (link_id=%d idx=%d): %d (%s)",
@@ -7755,7 +7787,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
@@ -7783,7 +7815,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
@@ -7792,25 +7824,37 @@
}
-static int i802_flush(void *priv)
+static int i802_flush(void *priv, int link_id)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
int res;
- wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
- bss->ifname);
+ if (link_id == NL80211_DRV_LINK_ID_NA)
+ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+ bss->ifname);
+ else
+ wpa_printf(MSG_DEBUG,
+ "nl80211: flush -> DEL_STATION %s (with link %d)",
+ bss->ifname, link_id);
/*
* XXX: FIX! this needs to flush all VLANs too
*/
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
- res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+
+ res = send_and_recv_cmd(bss->drv, msg);
if (res) {
wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
"(%s)", res, strerror(-res));
}
return res;
+fail:
+ nlmsg_free(msg);
+ return -1;
}
@@ -8136,7 +8180,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_sta_handler, data);
}
@@ -8152,8 +8196,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
- NULL, NULL);
+ return send_and_recv_resp(bss->drv, msg, get_sta_handler, data);
}
@@ -8215,10 +8258,10 @@
nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
goto fail;
- res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ res = send_and_recv_cmd(drv, msg);
wpa_printf(MSG_DEBUG,
- "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
- queue, aifs, cw_min, cw_max, burst_time, res);
+ "nl80211: link=%d: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
+ link_id, queue, aifs, cw_min, cw_max, burst_time, res);
if (res == 0)
return 0;
msg = NULL;
@@ -8250,7 +8293,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
@@ -8318,7 +8361,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
- 0, NULL, 0, 0, -1);
+ 0, NULL, 0, 0, link_id);
}
@@ -8678,7 +8721,6 @@
(params->num_bridge == 0 || !params->bridge[0]))
add_ifidx(drv, br_ifindex, drv->ifindex);
-#ifdef CONFIG_LIBNL3_ROUTE
if (bss->added_if_into_bridge || bss->already_in_bridge) {
int err;
@@ -8695,7 +8737,6 @@
goto failed;
}
}
-#endif /* CONFIG_LIBNL3_ROUTE */
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
wpa_printf(MSG_DEBUG,
@@ -8769,7 +8810,7 @@
struct wpa_driver_nl80211_data *drv;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
- if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, drv->first_bss->addr))
return 1;
}
return 0;
@@ -8920,7 +8961,6 @@
if (type == WPA_IF_AP_BSS && setup_ap) {
struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
- unsigned int i;
if (new_bss == NULL) {
if (added)
@@ -8928,10 +8968,6 @@
return -1;
}
- /* Initialize here before any failure path */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
- new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
-
if (bridge &&
i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
@@ -8956,7 +8992,7 @@
new_bss->drv = drv;
new_bss->next = drv->first_bss->next;
new_bss->flink = &new_bss->links[0];
- new_bss->n_links = 1;
+ 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;
@@ -9036,6 +9072,7 @@
tbss->next = bss->next;
/* Unsubscribe management frames */
nl80211_teardown_ap(bss);
+ nl80211_remove_links(bss);
nl80211_destroy_bss(bss);
if (!bss->added_if)
i802_set_iface_flags(bss, 0);
@@ -9050,6 +9087,7 @@
} else {
wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
nl80211_teardown_ap(bss);
+ nl80211_remove_links(bss);
if (!bss->added_if && !drv->first_bss->next)
wpa_driver_nl80211_del_beacon_all(bss);
nl80211_destroy_bss(bss);
@@ -9058,6 +9096,7 @@
if (drv->first_bss->next) {
drv->first_bss = drv->first_bss->next;
drv->ctx = drv->first_bss->ctx;
+ drv->ifindex = drv->first_bss->ifindex;
os_free(bss);
} else {
wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
@@ -9086,7 +9125,7 @@
const u8 *buf, size_t buf_len,
int save_cookie, int no_cck, int no_ack,
int offchanok, const u16 *csa_offs,
- size_t csa_offs_len)
+ size_t csa_offs_len, int link_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
@@ -9099,6 +9138,8 @@
wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+ ((link_id != NL80211_DRV_LINK_ID_NA) &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
(freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
(wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
(offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
@@ -9112,7 +9153,7 @@
goto fail;
cookie = 0;
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, cookie_handler, &cookie);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
@@ -9170,9 +9211,14 @@
bss->flink->beacon_set)
offchanok = 0;
- wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
- "freq=%u MHz wait=%d ms no_cck=%d offchanok=%d)",
- drv->ifindex, freq, wait_time, no_cck, offchanok);
+ if (!freq && is_sta_interface(drv->nlmode))
+ 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 ")",
+ drv->ifindex, freq, wait_time, no_cck, offchanok,
+ MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
buf = os_zalloc(24 + data_len);
if (buf == NULL)
@@ -9185,7 +9231,7 @@
os_memcpy(hdr->addr2, src, ETH_ALEN);
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
- if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(bss->addr, src)) {
wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
MAC2STR(src));
os_memcpy(bss->rand_addr, src, ETH_ALEN);
@@ -9224,8 +9270,9 @@
wait_time, NULL, 0, 0, -1);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
- 24 + data_len,
- 1, no_cck, 0, offchanok, NULL, 0);
+ 24 + data_len, 1, no_cck, 0,
+ offchanok, NULL, 0,
+ NL80211_DRV_LINK_ID_NA);
os_free(buf);
return ret;
@@ -9246,7 +9293,7 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -9278,6 +9325,43 @@
}
+static int nl80211_put_any_link_id(struct nl_msg *msg,
+ struct driver_sta_mlo_info *mlo,
+ int freq)
+{
+ int i;
+ int link_id = -1;
+ int any_valid_link_id = -1;
+
+ if (!mlo->valid_links)
+ return 0;
+
+ /* First try to pick a link that uses the same band */
+ for_each_link(mlo->valid_links, i) {
+ if (any_valid_link_id == -1)
+ any_valid_link_id = i;
+
+ if (is_same_band(freq, mlo->links[i].freq)) {
+ link_id = i;
+ break;
+ }
+ }
+
+ /* Use any valid link ID if no band match was found */
+ if (link_id == -1)
+ link_id = any_valid_link_id;
+
+ if (link_id == -1) {
+ wpa_printf(MSG_INFO,
+ "nl80211: No valid Link ID found for freq %u", freq);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Add Link ID %d", link_id);
+ return nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id);
+}
+
+
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
unsigned int duration)
{
@@ -9289,13 +9373,14 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
- nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+ nla_put_u32(msg, NL80211_ATTR_DURATION, duration) ||
+ nl80211_put_any_link_id(msg, &drv->sta_mlo_info, freq)) {
nlmsg_free(msg);
return -1;
}
cookie = 0;
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ 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",
@@ -9335,7 +9420,7 @@
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
@@ -9432,7 +9517,7 @@
nla_nest_end(msg, bands);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -9447,56 +9532,81 @@
}
-static void nl80211_remove_links(struct i802_bss *bss)
+static 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;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove link (ifindex=%d link_id=%u)",
+ bss->ifindex, link_id);
+
+ if (!(bss->valid_links & BIT(link_id))) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: remove link: Link not found");
+ return -1;
+ }
+
+ link = &bss->links[link_id];
+
+ wpa_driver_nl80211_del_beacon(bss, link_id);
+
+ /* First remove the link locally */
+ bss->valid_links &= ~BIT(link_id);
+ os_memset(link->addr, 0, ETH_ALEN);
+
+ /* Choose new deflink if we are removing that link */
+ if (bss->flink == link) {
+ for_each_link_default(bss->valid_links, i, 0) {
+ bss->flink = &bss->links[i];
+ break;
+ }
+ }
+
+ /* If this was the last link, reset default link */
+ if (!bss->valid_links) {
+ /* TODO: Does keeping freq/bandwidth make sense? */
+ if (bss->flink != link)
+ os_memcpy(bss->flink, link, sizeof(*link));
+
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+ }
+
+ /* Remove the link from the kernel */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed", link_id);
+ return -1;
+ }
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed. ret=%d (%s)",
+ link_id, ret, strerror(-ret));
+
+ return ret;
+}
+
+
+static void nl80211_remove_links(struct i802_bss *bss)
+{
int ret;
u8 link_id;
- while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
- struct i802_link *link = &bss->links[0];
-
- wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
- link->link_id);
-
- wpa_driver_nl80211_del_beacon(bss, link);
-
- link_id = link->link_id;
-
- /* First remove the link locally */
- if (bss->n_links == 1) {
- bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
- os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
- } else {
- struct i802_link *other = &bss->links[bss->n_links - 1];
-
- os_memcpy(link, other, sizeof(*link));
- other->link_id = NL80211_DRV_LINK_ID_NA;
- os_memset(other->addr, 0, ETH_ALEN);
-
- bss->n_links--;
- }
-
- /* Remove the link from the kernel */
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
- if (!msg ||
- nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
- nlmsg_free(msg);
- wpa_printf(MSG_ERROR,
- "nl80211: remove link (%d) failed",
- link_id);
- return;
- }
-
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
- if (ret) {
- wpa_printf(MSG_ERROR,
- "nl80211: remove link (%d) failed. ret=%d (%s)",
- link_id, ret, strerror(-ret));
- return;
- }
+ for_each_link(bss->valid_links, link_id) {
+ ret = nl80211_remove_link(bss, link_id);
+ if (ret)
+ break;
}
+
+ if (bss->flink)
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
}
@@ -9509,7 +9619,7 @@
return -1;
/* Stop beaconing */
- wpa_driver_nl80211_del_beacon(bss, bss->flink);
+ wpa_driver_nl80211_del_beacon(bss, NL80211_DRV_LINK_ID_NA);
nl80211_remove_links(bss);
@@ -9524,7 +9634,7 @@
}
-static int wpa_driver_nl80211_stop_ap(void *priv)
+static int wpa_driver_nl80211_stop_ap(void *priv, int link_id)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9532,9 +9642,17 @@
if (!is_ap_interface(drv->nlmode))
return -1;
- wpa_driver_nl80211_del_beacon_all(bss);
+ if (link_id == -1) {
+ wpa_driver_nl80211_del_beacon_all(bss);
+ return 0;
+ }
- return 0;
+ if (nl80211_link_valid(bss->valid_links, link_id)) {
+ wpa_driver_nl80211_del_beacon(bss, link_id);
+ return 0;
+ }
+
+ return -1;
}
@@ -9588,7 +9706,7 @@
}
nla_nest_end(msg, cqm);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -9626,7 +9744,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_channel_width, sig);
}
@@ -9687,10 +9805,7 @@
if (!sinfo[NL80211_SURVEY_INFO_NOISE])
return NL_SKIP;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_sig->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_sig->valid_links, i) {
if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
mlo_sig->links[i].frequency)
continue;
@@ -9710,8 +9825,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
- NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_links_noise, mlo_sig);
}
@@ -9765,8 +9879,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
- NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_links_channel_width, mlo_sig);
}
@@ -9785,10 +9898,7 @@
os_memset(mlo_si, 0, sizeof(*mlo_si));
mlo_si->valid_links = drv->sta_mlo_info.valid_links;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_si->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_si->valid_links, i) {
res = nl80211_get_link_signal(drv,
drv->sta_mlo_info.links[i].bssid,
&mlo_si->links[i].data);
@@ -9879,6 +9989,9 @@
WPA_DRIVER_FLAGS2_SEC_LTF_AP;
}
+ if (os_strstr(param, "rsn_override_in_driver=1"))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
+
return 0;
}
@@ -9887,6 +10000,13 @@
{
struct nl80211_global *global;
struct netlink_config *cfg;
+ struct utsname name;
+
+ if (uname(&name) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Kernel version: %s %s (%s; %s)",
+ name.sysname, name.release,
+ name.version, name.machine);
+ }
global = os_zalloc(sizeof(*global));
if (global == NULL)
@@ -9997,7 +10117,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(bss->drv, msg);
}
@@ -10073,7 +10193,7 @@
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
if (!msg)
return -ENOBUFS;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(bss->drv, msg);
}
@@ -10237,8 +10357,8 @@
do {
wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
- err = send_and_recv_msgs(drv, msg, survey_handler,
- survey_results, NULL, NULL);
+ err = send_and_recv_resp(drv, msg, survey_handler,
+ survey_results);
} while (err > 0);
if (err)
@@ -10278,7 +10398,7 @@
nla_nest_end(msg, replay_nested);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret == -EOPNOTSUPP) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver does not support rekey offload");
@@ -10345,7 +10465,7 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, cookie_handler, &cookie);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
MACSTR " failed: ret=%d (%s)",
@@ -10371,7 +10491,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(bss->drv, msg);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Setting PS state %s failed: %d (%s)",
@@ -10431,7 +10551,7 @@
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
@@ -10490,7 +10610,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
#else /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_ERROR,
"nl80211: Setting TX link for TDLS Discovery Response not supported");
@@ -10518,17 +10638,22 @@
nl80211_tdls_set_discovery_resp_link(drv, 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)) ||
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) ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code) ||
+ (link_id >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
nl80211_add_peer_capab(msg, peer_capab) ||
(initiator && nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
nla_put(msg, NL80211_ATTR_IE, len, buf))
goto fail;
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
fail:
nlmsg_free(msg);
@@ -10578,7 +10703,7 @@
return -ENOBUFS;
}
- res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ res = send_and_recv_cmd(drv, msg);
wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
" --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
strerror(-res));
@@ -10612,7 +10737,7 @@
return ret;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -10638,7 +10763,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
#endif /* CONFIG TDLS */
@@ -10705,6 +10830,73 @@
}
+#ifdef CONFIG_IEEE80211BE
+
+static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
+ const char *ifname, u8 link_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (type != WPA_IF_AP_BSS ||
+ !nl80211_link_valid(bss->valid_links, link_id))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Teardown AP(%s) link %d (type=%d ifname=%s links=0x%x)",
+ bss->ifname, link_id, type, ifname, bss->valid_links);
+
+ nl80211_remove_link(bss, link_id);
+
+ bss->ctx = bss->flink->ctx;
+
+ if (drv->first_bss == bss && bss->valid_links)
+ drv->ctx = bss->ctx;
+
+ if (!bss->valid_links) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: No more links remaining, so remove interface");
+ return wpa_driver_nl80211_if_remove(bss, type, ifname);
+ }
+
+ return 0;
+}
+
+
+static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int num_bss = 0;
+
+ /* 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
+ * this BSS should be remaining if the driver is not shared by anyone.
+ */
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ num_bss++;
+ if (num_bss > 1)
+ return true;
+ }
+
+ /* This is the only BSS present */
+ bss = priv;
+
+ /* If only one/no link is there no one is sharing */
+ if (bss->valid_links <= 1)
+ 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)))
+ return false;
+
+ return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
unsigned int freq,
@@ -10783,7 +10975,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
"err=%d (%s)", ret, strerror(-ret));
@@ -10811,7 +11003,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: update_dh_ie failed err=%d (%s)",
@@ -10953,9 +11145,9 @@
drv->retry_auth ? "retry_auth=1\n" : "",
drv->use_monitor ? "use_monitor=1\n" : "",
drv->ignore_next_local_disconnect ?
- "ignore_next_local_disconnect=1\n" : "",
+ "ignore_next_local_disconnect\n" : "",
drv->ignore_next_local_deauth ?
- "ignore_next_local_deauth=1\n" : "");
+ "ignore_next_local_deauth\n" : "");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -10973,10 +11165,7 @@
return pos - buf;
pos += res;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo->valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo->valid_links, i) {
res = os_snprintf(pos, end - pos,
"link_addr[%u]=" MACSTR "\n"
"link_bssid[%u]=" MACSTR "\n"
@@ -11063,8 +11252,8 @@
if (msg &&
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
- if (send_and_recv_msgs(drv, msg, nl80211_get_country,
- alpha2, NULL, NULL) == 0 &&
+ if (send_and_recv_resp(drv, msg, nl80211_get_country,
+ alpha2) == 0 &&
alpha2[0]) {
res = os_snprintf(pos, end - pos, "country=%s\n",
alpha2);
@@ -11117,7 +11306,7 @@
int i;
wpa_printf(MSG_DEBUG,
- "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x%s%s%s)",
+ "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x link_id=%d%s%s%s)",
settings->cs_count, settings->block_tx,
settings->freq_params.freq,
settings->freq_params.channel,
@@ -11126,6 +11315,7 @@
settings->freq_params.center_freq1,
settings->freq_params.center_freq2,
settings->punct_bitmap,
+ settings->link_id,
settings->freq_params.ht_enabled ? " ht" : "",
settings->freq_params.vht_enabled ? " vht" : "",
settings->freq_params.he_enabled ? " he" : "");
@@ -11199,7 +11389,9 @@
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->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 */
@@ -11226,7 +11418,14 @@
goto fail;
nla_nest_end(msg, beacon_csa);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+#ifdef CONFIG_IEEE80211AX
+ if (settings->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, &settings->ubpr) < 0)
+ goto fail;
+#endif /* CONFIG_IEEE80211AX */
+
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
ret, strerror(-ret));
@@ -11307,7 +11506,28 @@
}
nla_nest_end(msg, beacon_cca);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+ if (settings->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, &settings->ubpr) < 0) {
+ ret = -ENOBUFS;
+ goto error;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (nl80211_link_valid(bss->valid_links, settings->link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Color change request on link_id=%d",
+ settings->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ settings->link_id)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: switch_color failed err=%d (%s)",
@@ -11348,7 +11568,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
ret, strerror(-ret));
@@ -11375,7 +11595,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
ret, strerror(-ret));
@@ -11477,12 +11697,10 @@
0)
goto fail;
/* This test vendor_cmd can be used with nl80211 commands that
- * need the connect nl_sock, so use the owner-setting variant
- * of send_and_recv_msgs(). */
- ret = send_and_recv_msgs_owner(drv, msg,
- get_connect_handle(bss), 0,
- cmd_reply_handler, buf,
- NULL, NULL);
+ * 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,
+ cmd_reply_handler, buf, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
ret);
@@ -11506,8 +11724,7 @@
data_len, data)))
goto fail;
- ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, vendor_reply_handler, buf);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
ret);
@@ -11536,7 +11753,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
@@ -11571,8 +11788,7 @@
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
- ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, get_wowlan_handler, &wowlan_enabled);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
return 0;
@@ -11619,7 +11835,7 @@
nla_nest_end(msg, wowlan_triggers);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
@@ -11658,7 +11874,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -11686,7 +11902,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -11743,7 +11959,7 @@
nla_nest_end(msg, nlbssids);
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
fail:
nlmsg_free(msg);
@@ -11781,7 +11997,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -11820,7 +12036,7 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_ERROR, "nl80211: p2p set macaddr failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -11991,7 +12207,10 @@
if (nl80211_put_mesh_config(msg, ¶ms->conf) < 0)
goto fail;
- ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
+ return -1;
+ ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+ NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
@@ -12048,7 +12267,8 @@
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_msgs_connect_handle(drv, msg, bss, 0);
+ ret = send_and_recv(drv->global, 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));
@@ -12084,7 +12304,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
" failed: ret=%d (%s)",
@@ -12104,13 +12324,14 @@
const u8 *ipaddr, int prefixlen,
const u8 *addr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_ipaddr = NULL;
- struct nl_addr *nl_lladdr = NULL;
- int family, addrsize;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
int res;
if (!ipaddr || prefixlen == 0 || !addr)
@@ -12129,85 +12350,62 @@
}
if (version == 4) {
- family = AF_INET;
+ nhdr.ndm_family = AF_INET;
addrsize = 4;
} else if (version == 6) {
- family = AF_INET6;
+ nhdr.ndm_family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
}
- rn = rtnl_neigh_alloc();
- if (rn == NULL)
+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+ if (!msg)
return -ENOMEM;
- /* set the destination ip address for neigh */
- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
- if (nl_ipaddr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
- res = -ENOMEM;
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_DST, addrsize, (void *) ipaddr) ||
+ nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *) addr))
goto errout;
- }
- nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: neigh set destination addr failed");
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
goto errout;
- }
- /* set the corresponding lladdr for neigh */
- nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
- if (nl_lladdr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
- res = -ENOMEM;
- goto errout;
- }
- rtnl_neigh_set_lladdr(rn, nl_lladdr);
-
- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
- rtnl_neigh_set_state(rn, NUD_PERMANENT);
-
- res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+ res = nl_wait_for_ack(drv->rtnl_sk);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Adding bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
- if (nl_lladdr)
- nl_addr_put(nl_lladdr);
- if (nl_ipaddr)
- nl_addr_put(nl_ipaddr);
- if (rn)
- rtnl_neigh_put(rn);
+ nlmsg_free(msg);
return res;
-#else /* CONFIG_LIBNL3_ROUTE */
- return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
}
static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
const u8 *ipaddr)
{
-#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
- struct rtnl_neigh *rn;
- struct nl_addr *nl_ipaddr;
- int family, addrsize;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
int res;
if (!ipaddr)
return -EINVAL;
if (version == 4) {
- family = AF_INET;
+ nhdr.ndm_family = AF_INET;
addrsize = 4;
} else if (version == 6) {
- family = AF_INET6;
+ nhdr.ndm_family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
@@ -12225,41 +12423,28 @@
return -1;
}
- rn = rtnl_neigh_alloc();
- if (rn == NULL)
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
return -ENOMEM;
- /* set the destination ip address for neigh */
- nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
- if (nl_ipaddr == NULL) {
- wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
- res = -ENOMEM;
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0 ||
+ nla_put(msg, NDA_DST, addrsize, (void *) ipaddr))
goto errout;
- }
- res = rtnl_neigh_set_dst(rn, nl_ipaddr);
- if (res) {
- wpa_printf(MSG_DEBUG,
- "nl80211: neigh set destination addr failed");
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
goto errout;
- }
- rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
-
- res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+ res = nl_wait_for_ack(drv->rtnl_sk);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Deleting bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
- if (nl_ipaddr)
- nl_addr_put(nl_ipaddr);
- if (rn)
- rtnl_neigh_put(rn);
+ nlmsg_free(msg);
return res;
-#else /* CONFIG_LIBNL3_ROUTE */
- return -1;
-#endif /* CONFIG_LIBNL3_ROUTE */
}
@@ -12489,19 +12674,22 @@
add_acs_ch_list(msg, params->freq_list) ||
add_acs_freq_list(msg, params->freq_list) ||
(params->edmg_enabled &&
- nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED))) {
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED)) ||
+ (params->link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID,
+ params->link_id))) {
nlmsg_free(msg);
return -ENOBUFS;
}
nla_nest_end(msg, data);
wpa_printf(MSG_DEBUG,
- "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d",
+ "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d, link_id: %d",
params->hw_mode, params->ht_enabled, params->ht40_enabled,
params->vht_enabled, params->eht_enabled, params->ch_width,
- params->edmg_enabled);
+ params->edmg_enabled, params->link_id);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to invoke driver ACS function: %s",
@@ -12559,7 +12747,7 @@
}
nla_nest_end(msg, data);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver setband function failed: %s",
@@ -12761,11 +12949,10 @@
if (freq_list)
os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl));
- ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, preferred_freq_info_handler, ¶m);
if (ret) {
wpa_printf(MSG_ERROR,
- "%s: err in send_and_recv_msgs", __func__);
+ "%s: err in send_and_recv_resp", __func__);
return ret;
}
@@ -12816,10 +13003,10 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
- wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_cmd",
__func__);
return ret;
}
@@ -12872,7 +13059,7 @@
goto fail;
nla_nest_end(msg, container);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -12907,7 +13094,7 @@
return -1;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
}
@@ -12946,7 +13133,7 @@
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -13137,9 +13324,9 @@
nla_nest_end(msg, attr1);
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg,
+ ret = send_and_recv_resp(drv, msg,
nl80211_get_bss_transition_status_handler,
- info, NULL, NULL);
+ info);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -13192,7 +13379,7 @@
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -13263,7 +13450,7 @@
nla_nest_end(msg, nlpeers);
nla_nest_end(msg, attr);
- return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return send_and_recv_cmd(drv, msg);
fail:
nlmsg_free(msg);
@@ -13359,7 +13546,7 @@
}
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret)
wpa_printf(MSG_DEBUG,
"nl80211: Set secure ranging context failed: ret=%d (%s)",
@@ -13374,6 +13561,60 @@
#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
+static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
+ struct drv_acs_params *params)
+{
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int freq_list_len;
+ int ret = -1;
+
+ freq_list_len = int_array_len(params->freq_list);
+ wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
+ __func__, freq_list_len);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ BRCM_VENDOR_SCMD_ACS) ||
+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+ params->ht_enabled) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+ params->ht40_enabled) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+ params->vht_enabled) ||
+ nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) ||
+ (freq_list_len > 0 &&
+ nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(int) * freq_list_len, params->freq_list)))
+ goto fail;
+ nla_nest_end(msg, data);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
+ params->hw_mode, params->ht_enabled, params->ht40_enabled,
+ params->vht_enabled, params->ch_width);
+
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: BRCM Failed to invoke driver ACS function: %s",
+ strerror(errno));
+ }
+
+ msg = NULL;
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+
static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
{
#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM) \
@@ -13629,7 +13870,7 @@
nl80211_put_fils_connect_params(drv, params, msg))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret)
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -13672,7 +13913,7 @@
(params->bssid &&
nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -13712,7 +13953,7 @@
bss->added_if_into_bridge = 0;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret && val && nl80211_get_4addr(bss) == 1) {
wpa_printf(MSG_DEBUG,
@@ -13776,7 +14017,7 @@
nla_nest_end(msg, params);
wpa_printf(MSG_DEBUG, "nl80211: Transition Disable Policy %d\n", td_policy);
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Transition Disable setting failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -13786,12 +14027,12 @@
}
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr,
+ void *bss_ctx)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- unsigned int idx, i;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
@@ -13804,35 +14045,27 @@
return -EINVAL;
}
- if (bss->n_links >= MAX_NUM_MLD_LINKS) {
- wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
- bss->n_links);
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: invalid link_id=%u", link_id);
return -EINVAL;
}
- for (i = 0; i < bss->n_links; i++) {
- if (bss->links[i].link_id == link_id &&
- bss->links[i].beacon_set) {
- wpa_printf(MSG_DEBUG,
- "nl80211: MLD: link already set");
- return -EINVAL;
- }
+ if (bss->valid_links & BIT(link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: Link %u already set", link_id);
+ return -EINVAL;
}
- /* try using the first link entry, assuming it is not beaconing yet */
- if (bss->n_links == 1 &&
- bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+ if (!bss->valid_links) {
+ /* Becoming MLD, verify we were not beaconing */
if (bss->flink->beacon_set) {
wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
return -EINVAL;
}
-
- idx = 0;
- } else {
- idx = bss->n_links;
}
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
if (!msg ||
nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
@@ -13840,23 +14073,58 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: add link failed. ret=%d (%s)",
ret, strerror(-ret));
return ret;
}
- bss->links[idx].link_id = link_id;
- os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+ os_memcpy(bss->links[link_id].addr, addr, ETH_ALEN);
- bss->n_links = idx + 1;
+ /* The new link is the first one, make it the default */
+ if (!bss->valid_links)
+ bss->flink = &bss->links[link_id];
- wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+ bss->valid_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);
return 0;
}
+#ifdef CONFIG_IEEE80211BE
+static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
+ const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!(bss->valid_links & BIT(link_id)))
+ return -ENOLINK;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_REMOVE_LINK_STA)) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN, addr) ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_cmd(drv, msg);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: link_sta_remove -> REMOVE_LINK_STA on link_id %u from MLD STA "
+ MACSTR ", from %s --> %d (%s)",
+ link_id, MAC2STR(addr), bss->ifname, ret, strerror(-ret));
+
+ return ret;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
#ifdef CONFIG_TESTING_OPTIONS
static int testing_nl80211_register_frame(void *priv, u16 type,
@@ -13911,7 +14179,7 @@
.scan2 = driver_nl80211_scan2,
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
- .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .get_scan_results = wpa_driver_nl80211_get_scan_results,
.abort_scan = wpa_driver_nl80211_abort_scan,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
@@ -14051,6 +14319,11 @@
#endif /* CONFIG_DPP */
.get_sta_mlo_info = nl80211_get_sta_mlo_info,
.link_add = nl80211_link_add,
+#ifdef CONFIG_IEEE80211BE
+ .link_remove = driver_nl80211_link_remove,
+ .is_drv_shared = nl80211_is_drv_shared,
+ .link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
.radio_disable = testing_nl80211_radio_disable,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index d922912..d2c1ffa 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -32,6 +32,7 @@
struct nl_cb *nl_cb;
struct nl_sock *nl;
int nl80211_id;
+ unsigned int nl80211_maxattr;
int nlctrl_id;
int ioctl_sock; /* socket for ioctl() use */
struct nl_sock *nl_event;
@@ -54,7 +55,6 @@
struct i802_link {
unsigned int beacon_set:1;
- s8 link_id;
int freq;
int bandwidth;
u8 addr[ETH_ALEN];
@@ -65,7 +65,7 @@
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
- size_t n_links;
+ u16 valid_links;
struct i802_link links[MAX_NUM_MLD_LINKS];
struct i802_link *flink;
@@ -81,7 +81,6 @@
unsigned int wdev_id_set:1;
unsigned int added_if:1;
unsigned int static_ap:1;
- unsigned int use_nl_connect:1;
u8 addr[ETH_ALEN];
u8 prev_addr[ETH_ALEN];
@@ -165,8 +164,6 @@
unsigned int scan_for_auth:1;
unsigned int retry_auth:1;
unsigned int use_monitor:1;
- unsigned int ignore_next_local_disconnect:1;
- unsigned int ignore_next_local_deauth:1;
unsigned int hostapd:1;
unsigned int start_mode_sta:1;
unsigned int start_iface_up:1;
@@ -199,10 +196,14 @@
unsigned int qca_do_acs:1;
unsigned int brcm_do_acs:1;
unsigned int uses_6ghz:1;
+ unsigned int uses_s1g:1;
unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
unsigned int puncturing:1;
unsigned int qca_ap_allowed_freqs:1;
+ u32 ignore_next_local_disconnect;
+ u32 ignore_next_local_deauth;
+
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_frame_cookie;
@@ -271,18 +272,58 @@
struct nl_msg;
+struct nl80211_err_info {
+ int link_id;
+};
+
void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int flags, uint8_t cmd);
struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
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_msgs(struct wpa_driver_nl80211_data *drv, 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 nl_sock * get_connect_handle(struct i802_bss *bss);
+
+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);
+
+// This function is not used in supplicant anymore. But keeping this wrapper
+// functions for libraries outside wpa_supplicant to build (For eg: lib_driver_cmd_XX)
+static inline int
+send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+ 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)
+{
+ return send_and_recv(drv->global, drv->global->nl, msg,
+ valid_handler, valid_data,
+ ack_handler_custom, ack_data, NULL);
+}
+
+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,
+ NULL, NULL, NULL, NULL, NULL);
+}
+
+static inline int
+send_and_recv_resp(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data)
+{
+ return send_and_recv(drv->global, drv->global->nl, msg,
+ valid_handler, valid_data, NULL, NULL, NULL);
+}
+
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
@@ -330,6 +371,24 @@
void nl80211_restore_ap_mode(struct i802_bss *bss);
struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
+static inline bool nl80211_link_valid(u16 links, s8 link_id)
+{
+ if (link_id < 0 || link_id >= MAX_NUM_MLD_LINKS)
+ return false;
+
+ if (links & BIT(link_id))
+ return true;
+
+ return false;
+}
+
+
+static inline bool
+nl80211_attr_supported(struct wpa_driver_nl80211_data *drv, unsigned int attr)
+{
+ return attr <= drv->global->nl80211_maxattr;
+}
+
#ifdef ANDROID
int android_nl_socket_set_nonblocking(struct nl_sock *handle);
int android_pno_start(struct i802_bss *bss,
@@ -361,7 +420,8 @@
int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params);
int wpa_driver_nl80211_stop_sched_scan(void *priv);
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
+ const u8 *bssid);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 11a32af..ebf69dc 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -49,8 +49,7 @@
return 0;
}
- if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
- NULL, NULL) == 0)
+ if (send_and_recv_resp(drv, msg, protocol_feature_handler, &feat) == 0)
return feat;
return 0;
@@ -889,6 +888,10 @@
nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
}
+ wpa_printf(MSG_DEBUG,
+ "nl80211: EML Capability: 0x%x MLD Capability: 0x%x",
+ capa->eml_capa, capa->mld_capa_and_ops);
+
drv->num_iface_capa++;
if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
break;
@@ -1207,7 +1210,7 @@
return -1;
}
- if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
+ if (send_and_recv_resp(drv, msg, wiphy_info_handler, info))
return -1;
if (info->auth_supported)
@@ -1320,8 +1323,7 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, dfs_info_handler, &dfs_capability);
if (!ret && dfs_capability)
drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
}
@@ -1408,8 +1410,7 @@
os_memset(&info, 0, sizeof(info));
info.capa = &drv->capa;
- ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, features_info_handler, &info);
if (ret || !info.flags)
return;
@@ -1449,6 +1450,14 @@
if (check_feature(QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST,
&info))
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;
+ }
+
os_free(info.flags);
}
@@ -1476,6 +1485,7 @@
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;
@@ -1491,6 +1501,7 @@
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 |=
@@ -2168,6 +2179,9 @@
for (m = 0; m < *num_modes; m++) {
if (!modes[m].num_channels)
continue;
+
+ modes[m].is_6ghz = false;
+
if (modes[m].channels[0].freq < 2000) {
modes[m].num_channels = 0;
continue;
@@ -2179,10 +2193,14 @@
break;
}
}
- } else if (modes[m].channels[0].freq > 50000)
+ } else if (modes[m].channels[0].freq > 50000) {
modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
- else
+ } else if (is_6ghz_freq(modes[m].channels[0].freq)) {
modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+ modes[m].is_6ghz = true;
+ } else {
+ modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+ }
}
/* Remove unsupported bands */
@@ -2410,6 +2428,57 @@
}
+static void nl80211_set_6ghz_mode(struct hostapd_hw_modes *mode, int start,
+ int end, int max_bw)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->freq - 10 < start || chan->freq + 10 > end)
+ continue;
+
+ if (max_bw >= 80)
+ chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
+
+ if (max_bw >= 160)
+ chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
+
+ if (max_bw >= 320)
+ chan->flag |= HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL;
+ }
+}
+
+
+static void nl80211_reg_rule_6ghz(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (!tb[NL80211_ATTR_FREQ_RANGE_START] ||
+ !tb[NL80211_ATTR_FREQ_RANGE_END] ||
+ !tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ 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 (max_bw < 80)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (results->modes[m].num_channels == 0 ||
+ !is_6ghz_freq(results->modes[m].channels[0].freq))
+ continue;
+
+ nl80211_set_6ghz_mode(&results->modes[m], start, end, max_bw);
+ }
+}
+
+
static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
u8 *dfs_domain)
{
@@ -2528,6 +2597,13 @@
nl80211_reg_rule_vht(tb_rule, results);
}
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_6ghz(tb_rule, results);
+ }
+
return NL_SKIP;
}
@@ -2549,8 +2625,7 @@
}
}
- return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
- NULL, NULL);
+ return send_and_recv_resp(drv, msg, nl80211_get_reg, results);
}
@@ -2592,6 +2667,8 @@
if (is_6ghz_freq(chan->freq))
drv->uses_6ghz = true;
+ if (chan->freq >= 900 && chan->freq < 1000)
+ drv->uses_s1g = true;
res = os_snprintf(pos, end - pos, " %d%s%s%s",
chan->freq,
(chan->flag & HOSTAPD_CHAN_DISABLED) ?
@@ -2642,8 +2719,7 @@
return NULL;
}
- if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
- NULL, NULL) == 0) {
+ if (send_and_recv_resp(drv, msg, phy_info_handler, &result) == 0) {
struct hostapd_hw_modes *modes;
nl80211_set_regulatory_flags(drv, &result);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7f8fd60..ba6aa9c 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -21,7 +21,7 @@
#include "driver_nl80211.h"
static void
-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie);
@@ -385,9 +385,9 @@
return 0;
}
- ret = send_and_recv_msgs(drv, msg,
+ ret = send_and_recv_resp(drv, msg,
qca_drv_connect_fail_reason_code_handler,
- &reason_code, NULL, NULL);
+ &reason_code);
if (ret)
wpa_printf(MSG_DEBUG,
"nl80211: Get connect fail reason_code failed: ret=%d (%s)",
@@ -442,7 +442,7 @@
wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
" received in link reconfig event", MAC2STR(ap_mld));
if (!drv->sta_mlo_info.valid_links ||
- os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ !ether_addr_equal(drv->sta_mlo_info.ap_mld_addr, ap_mld)) {
if (drv->pending_link_reconfig_data == data) {
wpa_printf(MSG_DEBUG,
"nl80211: Drop pending link reconfig event since AP MLD not matched even after new connect/roam event");
@@ -476,10 +476,7 @@
* links when the link used for (re)association is removed.
*/
if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(drv->sta_mlo_info.valid_links & BIT(i)))
- continue;
-
+ for_each_link(drv->sta_mlo_info.valid_links, i) {
os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
ETH_ALEN);
drv->sta_mlo_info.assoc_link_id = i;
@@ -659,7 +656,8 @@
return -1;
}
- mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mle = ieee802_11_defrag(req_elems.basic_mle, req_elems.basic_mle_len,
+ true);
if (!mle) {
wpa_printf(MSG_INFO,
"nl80211: MLO: Basic Multi-Link element not found in Association Request");
@@ -670,7 +668,8 @@
&req_info);
wpabuf_free(mle);
- mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mle = ieee802_11_defrag(resp_elems.basic_mle, resp_elems.basic_mle_len,
+ true);
if (!mle) {
wpa_printf(MSG_ERROR,
"nl80211: MLO: Basic Multi-Link element not found in Association Response");
@@ -699,10 +698,7 @@
}
/* Get MLO links info for rejected links */
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
- continue;
-
+ for_each_link((mlo->req_links & ~mlo->valid_links), i) {
os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
}
@@ -809,7 +805,7 @@
wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
" received in TID to link mapping event", MAC2STR(ap_mld));
if (!drv->sta_mlo_info.valid_links ||
- os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ !ether_addr_equal(drv->sta_mlo_info.ap_mld_addr, ap_mld)) {
if (drv->pending_t2lm_data == data) {
wpa_printf(MSG_DEBUG,
"nl80211: Drop pending TID-to-link mapping event since AP MLD not matched even after new connect/roam event");
@@ -872,9 +868,7 @@
wpa_printf(MSG_DEBUG,
"nl80211: TID-to-link: Received uplink %x downlink %x",
uplink, downlink);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(drv->sta_mlo_info.valid_links & BIT(i)))
- continue;
+ for_each_link(drv->sta_mlo_info.valid_links, i) {
if (uplink & BIT(i))
event.t2l_map_info.t2lmap[i].uplink |=
BIT(tidnum);
@@ -957,9 +951,8 @@
if (drv->ignore_next_local_disconnect) {
drv->ignore_next_local_disconnect = 0;
if (!event.assoc_reject.bssid ||
- (os_memcmp(event.assoc_reject.bssid,
- drv->auth_attempt_bssid,
- ETH_ALEN) != 0)) {
+ !ether_addr_equal(event.assoc_reject.bssid,
+ drv->auth_attempt_bssid)) {
/*
* Ignore the event that came without a BSSID or
* for the old connection since this is likely
@@ -1267,15 +1260,34 @@
if (cf2)
data.ch_switch.cf2 = nla_get_u32(cf2);
- if (finished)
- bss->flink->freq = data.ch_switch.freq;
+ if (link)
+ data.ch_switch.link_id = nla_get_u8(link);
+ else
+ data.ch_switch.link_id = NL80211_DRV_LINK_ID_NA;
+
+ if (finished) {
+ if (data.ch_switch.link_id != NL80211_DRV_LINK_ID_NA) {
+ struct i802_link *mld_link;
+
+ mld_link = nl80211_get_link(bss,
+ data.ch_switch.link_id);
+ mld_link->freq = data.ch_switch.freq;
+ if (bw)
+ mld_link->bandwidth = channel_width_to_int(
+ data.ch_switch.ch_width);
+ } else {
+ bss->flink->freq = data.ch_switch.freq;
+ if (bw)
+ bss->flink->bandwidth = channel_width_to_int(
+ data.ch_switch.ch_width);
+ }
+ }
if (link && is_sta_interface(drv->nlmode)) {
- u8 link_id = nla_get_u8(link);
+ u8 link_id = data.ch_switch.link_id;
if (link_id < MAX_NUM_MLD_LINKS &&
drv->sta_mlo_info.valid_links & BIT(link_id)) {
- data.ch_switch.link_id = link_id;
drv->sta_mlo_info.links[link_id].freq =
data.ch_switch.freq;
wpa_supplicant_event(
@@ -1361,18 +1373,20 @@
event.rx_mgmt.frame_len = len;
event.rx_mgmt.ssi_signal = ssi_signal;
event.rx_mgmt.drv_priv = bss;
+ event.rx_mgmt.ctx = bss->ctx;
event.rx_mgmt.link_id = link_id;
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
}
-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
struct nlattr *cookie, const u8 *frame,
size_t len, struct nlattr *ack)
{
union wpa_event_data event;
const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
u16 fc = le_to_host16(hdr->frame_control);
u64 cookie_val = 0;
@@ -1391,7 +1405,7 @@
WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
wpa_printf(MSG_DEBUG,
"nl80211: Work around misdelivered control port TX status for EAPOL");
- nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+ nl80211_control_port_frame_tx_status(bss, frame, len, ack,
cookie);
return;
}
@@ -1427,7 +1441,7 @@
event.tx_status.ack = ack != NULL;
event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
- wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+ wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
}
@@ -1451,9 +1465,9 @@
if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
!drv->associated &&
- os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
- os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
- os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+ !ether_addr_equal(bssid, drv->auth_bssid) &&
+ !ether_addr_equal(bssid, drv->auth_attempt_bssid) &&
+ ether_addr_equal(bssid, drv->prev_bssid)) {
/*
* Avoid issues with some roaming cases where
* disconnection event for the old AP may show up after
@@ -1462,11 +1476,10 @@
* ignore_next_local_deauth as well, to avoid next local
* deauth event be wrongly ignored.
*/
- if (os_memcmp(mgmt->sa, drv->first_bss->addr,
- ETH_ALEN) == 0 ||
+ if (ether_addr_equal(mgmt->sa, drv->first_bss->addr) ||
(!is_zero_ether_addr(drv->first_bss->prev_addr) &&
- os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
- ETH_ALEN) == 0)) {
+ ether_addr_equal(mgmt->sa,
+ drv->first_bss->prev_addr))) {
wpa_printf(MSG_DEBUG,
"nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
drv->ignore_next_local_deauth = 0;
@@ -1481,8 +1494,8 @@
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
drv->connect_reassoc && drv->associated &&
- os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
- os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) {
+ ether_addr_equal(bssid, drv->prev_bssid) &&
+ !ether_addr_equal(bssid, drv->auth_attempt_bssid)) {
/*
* Avoid issues with some roaming cases where
* disconnection event for the old AP may show up after
@@ -1498,8 +1511,8 @@
}
if (drv->associated != 0 &&
- os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
- os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(bssid, drv->bssid) &&
+ !ether_addr_equal(bssid, drv->auth_bssid)) {
/*
* We have presumably received this deauth as a
* response to a clear_state_mismatch() outgoing
@@ -1521,7 +1534,7 @@
if (type == EVENT_DISASSOC) {
event.disassoc_info.locally_generated =
- !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+ ether_addr_equal(mgmt->sa, drv->first_bss->addr);
event.disassoc_info.addr = bssid;
event.disassoc_info.reason_code = reason_code;
if (frame + len > mgmt->u.disassoc.variable) {
@@ -1531,7 +1544,7 @@
}
} else {
event.deauth_info.locally_generated =
- !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+ ether_addr_equal(mgmt->sa, drv->first_bss->addr);
if (drv->ignore_deauth_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
drv->ignore_deauth_event = 0;
@@ -1614,18 +1627,17 @@
}
-static struct i802_link *
-nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
+static s8
+nl80211_get_link_id_by_freq(struct i802_bss *bss, unsigned int freq)
{
unsigned int i;
- for (i = 0; i < bss->n_links; i++) {
- if ((unsigned int) bss->links[i].freq == freq &&
- bss->links[i].link_id != -1)
- return &bss->links[i];
+ for_each_link(bss->valid_links, i) {
+ if ((unsigned int) bss->links[i].freq == freq)
+ return i;
}
- return NULL;
+ return NL80211_DRV_LINK_ID_NA;
}
@@ -1659,12 +1671,12 @@
/* Determine the MLD link either by an explicitly provided link id or
* finding a match based on the frequency. */
if (link)
- mld_link = nl80211_get_link(bss, nla_get_u8(link));
+ link_id = nla_get_u8(link);
else if (freq)
- mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
+ link_id = nl80211_get_link_id_by_freq(bss, nla_get_u32(freq));
- if (mld_link)
- link_id = mld_link->link_id;
+ if (nl80211_link_valid(bss->valid_links, link_id))
+ mld_link = nl80211_get_link(bss, link_id);
data = nla_data(frame);
len = nla_len(frame);
@@ -1701,15 +1713,14 @@
bss->ifname);
} else if (cmd != NL80211_CMD_FRAME_TX_STATUS &&
!(data[4] & 0x01) &&
- os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+ !ether_addr_equal(bss->addr, data + 4) &&
(is_zero_ether_addr(bss->rand_addr) ||
- os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
- os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+ !ether_addr_equal(bss->rand_addr, data + 4)) &&
+ !ether_addr_equal(bss->addr, data + 4 + ETH_ALEN) &&
(is_zero_ether_addr(drv->first_bss->prev_addr) ||
- os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
- ETH_ALEN) != 0) &&
+ !ether_addr_equal(bss->prev_addr, data + 4 + ETH_ALEN)) &&
(!mld_link ||
- os_memcmp(mld_link->addr, data + 4, ETH_ALEN) != 0)) {
+ !ether_addr_equal(mld_link->addr, data + 4))) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
return;
@@ -1738,7 +1749,7 @@
nla_len(frame), link_id);
break;
case NL80211_CMD_FRAME_TX_STATUS:
- mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+ mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
nla_len(frame), ack);
break;
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
@@ -1917,7 +1928,7 @@
os_memset(&data, 0, sizeof(data));
addr = nla_data(tb[NL80211_ATTR_MAC]);
- if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+ if (!bss->valid_links &&
(tb[NL80211_ATTR_MLO_LINK_ID] ||
tb[NL80211_ATTR_MLD_ADDR])) {
wpa_printf(MSG_ERROR,
@@ -2181,7 +2192,7 @@
u8 *req_ies = NULL, *resp_ies = NULL;
size_t req_ies_len = 0, resp_ies_len = 0;
- if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA &&
+ if (!bss->valid_links &&
(tb[NL80211_ATTR_MLO_LINK_ID] ||
tb[NL80211_ATTR_MLD_ADDR])) {
wpa_printf(MSG_ERROR,
@@ -2454,9 +2465,19 @@
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;
@@ -2487,10 +2508,12 @@
if (tb[NL80211_ATTR_CENTER_FREQ2])
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",
+ 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.cf1, data.dfs_event.cf2,
+ data.dfs_event.link_id);
switch (event_type) {
case NL80211_RADAR_DETECTED:
@@ -2722,8 +2745,14 @@
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP])
event.acs_selected_channels.puncture_bitmap =
nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID])
+ event.acs_selected_channels.link_id =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID]);
+ else
+ event.acs_selected_channels.link_id = -1;
+
wpa_printf(MSG_INFO,
- "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d PUNCBITMAP: 0x%x",
+ "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d PUNCBITMAP: 0x%x, LinkId: %d",
event.acs_selected_channels.pri_freq,
event.acs_selected_channels.sec_freq,
event.acs_selected_channels.ch_width,
@@ -2731,7 +2760,8 @@
event.acs_selected_channels.vht_seg1_center_ch,
event.acs_selected_channels.hw_mode,
event.acs_selected_channels.edmg_channel,
- event.acs_selected_channels.puncture_bitmap);
+ event.acs_selected_channels.puncture_bitmap,
+ event.acs_selected_channels.link_id);
/* Ignore ACS channel list check for backwards compatibility */
@@ -2826,9 +2856,19 @@
os_memset(&data, 0, sizeof(data));
data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
- wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
- data.dfs_event.freq);
+ 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);
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, link=%d",
+ data.dfs_event.freq, data.dfs_event.link_id);
/* Check HT params */
if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -2936,6 +2976,7 @@
info = &event.scan_info;
info->aborted = aborted;
info->external_scan = external_scan;
+ info->scan_cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl,
@@ -3386,7 +3427,8 @@
}
-static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq)
+static void nl80211_parse_freq_attrs(const char *title, struct nlattr *nl_freq,
+ struct frequency_attrs *attrs)
{
static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
@@ -3413,6 +3455,15 @@
tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "",
tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "",
tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : "");
+
+ attrs->freq = freq;
+ attrs->max_tx_power = max_tx_power;
+ if (tb[NL80211_FREQUENCY_ATTR_DISABLED])
+ attrs->disabled = true;
+ if (tb[NL80211_FREQUENCY_ATTR_NO_IR])
+ attrs->no_ir = true;
+ if (tb[NL80211_FREQUENCY_ATTR_RADAR])
+ attrs->radar = true;
}
@@ -3426,9 +3477,13 @@
data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
if (tb[NL80211_ATTR_FREQ_BEFORE])
- nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]);
+ nl80211_parse_freq_attrs(
+ "before", tb[NL80211_ATTR_FREQ_BEFORE],
+ &data.channel_list_changed.beacon_hint_before);
if (tb[NL80211_ATTR_FREQ_AFTER])
- nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]);
+ nl80211_parse_freq_attrs(
+ "after", tb[NL80211_ATTR_FREQ_AFTER],
+ &data.channel_list_changed.beacon_hint_after);
wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
}
@@ -3494,8 +3549,6 @@
{
const u8 *addr;
union wpa_event_data event;
- const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
- drv->sta_mlo_info.ap_mld_addr : drv->bssid;
os_memset(&event, 0, sizeof(event));
@@ -3512,13 +3565,19 @@
wpa_printf(MSG_DEBUG,
"nl80211: Port authorized for STA addr " MACSTR,
MAC2STR(addr));
- } else if (is_sta_interface(drv->nlmode) &&
- os_memcmp(addr, connected_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Ignore port authorized event for " MACSTR
- " (not the currently connected BSSID " MACSTR ")",
- MAC2STR(addr), MAC2STR(connected_addr));
- return;
+ } else if (is_sta_interface(drv->nlmode)) {
+ const u8 *connected_addr;
+
+ connected_addr = drv->sta_mlo_info.valid_links ?
+ drv->sta_mlo_info.ap_mld_addr : drv->bssid;
+ if (!ether_addr_equal(addr, connected_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event for "
+ MACSTR " (not the currently connected BSSID "
+ MACSTR ")",
+ MAC2STR(addr), MAC2STR(connected_addr));
+ return;
+ }
}
if (tb[NL80211_ATTR_TD_BITMAP]) {
@@ -3606,8 +3665,7 @@
}
-static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- struct nlattr **tb)
+static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
{
u8 *src_addr;
u16 ethertype;
@@ -3636,7 +3694,7 @@
MAC2STR(src_addr));
break;
case ETH_P_PAE:
- drv_event_eapol_rx2(drv->ctx, src_addr,
+ drv_event_eapol_rx2(bss->ctx, src_addr,
nla_data(tb[NL80211_ATTR_FRAME]),
nla_len(tb[NL80211_ATTR_FRAME]),
encrypted, link_id);
@@ -3652,10 +3710,11 @@
static void
-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data event;
if (!cookie || len < ETH_HLEN)
@@ -3674,7 +3733,7 @@
nla_get_u64(cookie) == drv->eapol_tx_cookie ?
drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
- wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+ wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
}
@@ -3700,8 +3759,11 @@
(long long unsigned int) cookie,
match ? " (match)" : "",
drv->send_frame_cookie == cookie ? " (match-saved)" : "");
- if (drv->send_frame_cookie == cookie)
+ if (drv->send_frame_cookie == cookie) {
drv->send_frame_cookie = (u64) -1;
+ if (!match)
+ goto send_event;
+ }
if (!match)
return;
@@ -3711,6 +3773,7 @@
(drv->num_send_frame_cookies - i - 1) * sizeof(u64));
drv->num_send_frame_cookies--;
+send_event:
wpa_supplicant_event(drv->ctx, EVENT_TX_WAIT_EXPIRE, NULL);
}
@@ -3728,48 +3791,62 @@
#ifdef CONFIG_IEEE80211AX
-static void nl80211_obss_color_collision(struct i802_bss *bss,
- struct nlattr *tb[])
+static void nl80211_obss_color_event(struct i802_bss *bss,
+ enum nl80211_commands cmd,
+ struct nlattr *tb[])
{
union wpa_event_data data;
-
- if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
- return;
+ enum wpa_event_type event_type;
os_memset(&data, 0, sizeof(data));
- data.bss_color_collision.bitmap =
- nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+ data.bss_color_collision.link_id = NL80211_DRV_LINK_ID_NA;
- wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
- (long long unsigned int) data.bss_color_collision.bitmap);
- wpa_supplicant_event(bss->ctx, EVENT_BSS_COLOR_COLLISION, &data);
-}
+ switch (cmd) {
+ case NL80211_CMD_OBSS_COLOR_COLLISION:
+ event_type = EVENT_BSS_COLOR_COLLISION;
+ if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
+ return;
+ data.bss_color_collision.bitmap =
+ nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BSS color collision - bitmap %08llx",
+ (long long unsigned int)
+ data.bss_color_collision.bitmap);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_STARTED:
+ event_type = EVENT_CCA_STARTED_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+ break;
+ case NL80211_CMD_COLOR_CHANGE_ABORTED:
+ event_type = EVENT_CCA_ABORTED_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+ break;
+ case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+ event_type = EVENT_CCA_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown CCA command %d", cmd);
+ return;
+ }
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ data.bss_color_collision.link_id =
+ nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-static void nl80211_color_change_announcement_started(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
+ if (!nl80211_link_valid(bss->valid_links,
+ data.bss_color_collision.link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid BSS color event link ID %d",
+ data.bss_color_collision.link_id);
+ return;
+ }
- wpa_printf(MSG_DEBUG, "nl80211: CCA started");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
-}
+ wpa_printf(MSG_DEBUG, "nl80211: BSS color event - Link ID %d",
+ data.bss_color_collision.link_id);
+ }
-
-static void nl80211_color_change_announcement_aborted(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
-
- wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
-}
-
-
-static void nl80211_color_change_announcement_completed(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
-
- wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_NOTIFY, &data);
+ wpa_supplicant_event(bss->ctx, event_type, &data);
}
#endif /* CONFIG_IEEE80211AX */
@@ -3881,6 +3958,7 @@
case NL80211_CMD_ASSOCIATE:
case NL80211_CMD_DEAUTHENTICATE:
case NL80211_CMD_DISASSOCIATE:
+ case NL80211_CMD_FRAME:
case NL80211_CMD_FRAME_TX_STATUS:
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
case NL80211_CMD_UNPROT_DISASSOCIATE:
@@ -4014,7 +4092,7 @@
case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
if (!frame)
break;
- nl80211_control_port_frame_tx_status(drv,
+ nl80211_control_port_frame_tx_status(bss,
nla_data(frame),
nla_len(frame),
tb[NL80211_ATTR_ACK],
@@ -4029,18 +4107,15 @@
break;
#ifdef CONFIG_IEEE80211AX
case NL80211_CMD_OBSS_COLOR_COLLISION:
- nl80211_obss_color_collision(bss, tb);
- break;
case NL80211_CMD_COLOR_CHANGE_STARTED:
- nl80211_color_change_announcement_started(bss);
- break;
case NL80211_CMD_COLOR_CHANGE_ABORTED:
- nl80211_color_change_announcement_aborted(bss);
- break;
case NL80211_CMD_COLOR_CHANGE_COMPLETED:
- nl80211_color_change_announcement_completed(bss);
+ nl80211_obss_color_event(bss, cmd, tb);
break;
#endif /* CONFIG_IEEE80211AX */
+ case NL80211_CMD_LINKS_REMOVED:
+ wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
+ break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
@@ -4049,6 +4124,21 @@
}
+static bool nl80211_drv_in_list(struct nl80211_global *global,
+ struct wpa_driver_nl80211_data *drv)
+{
+ struct wpa_driver_nl80211_data *tmp;
+
+ dl_list_for_each(tmp, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv == tmp)
+ return true;
+ }
+
+ return false;
+}
+
+
int process_global_event(struct nl_msg *msg, void *arg)
{
struct nl80211_global *global = arg;
@@ -4060,6 +4150,31 @@
u64 wdev_id = 0;
int wdev_id_set = 0;
int wiphy_idx_set = 0;
+ bool processed = false;
+
+ /* Event marker, all prior events have been processed */
+ if (gnlh->cmd == NL80211_CMD_GET_PROTOCOL_FEATURES) {
+ u32 seq = nlmsg_hdr(msg)->nlmsg_seq;
+
+ dl_list_for_each_safe(drv, tmp, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv->ignore_next_local_deauth > 0 &&
+ drv->ignore_next_local_deauth <= seq) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: No DEAUTHENTICATE event was ignored");
+ drv->ignore_next_local_deauth = 0;
+ }
+
+ if (drv->ignore_next_local_disconnect > 0 &&
+ drv->ignore_next_local_disconnect <= seq) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: No DISCONNECT event was ignored");
+ drv->ignore_next_local_disconnect = 0;
+ }
+ }
+
+ return NL_SKIP;
+ }
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -4084,16 +4199,28 @@
(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);
- return NL_SKIP;
+ 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;
}
}
- wpa_printf(MSG_DEBUG,
- "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)",
- gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
- ifidx, (long long unsigned int) wdev_id);
}
+ if (processed)
+ return NL_SKIP;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx wiphy %d)",
+ gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+ ifidx, (long long unsigned int) wdev_id, wiphy_idx_rx);
+
return NL_SKIP;
}
@@ -4132,7 +4259,7 @@
nl80211_external_auth(bss->drv, tb);
break;
case NL80211_CMD_CONTROL_PORT_FRAME:
- nl80211_control_port_frame(bss->drv, tb);
+ nl80211_control_port_frame(bss, tb);
break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 4907e3c..b055e68 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -82,8 +82,7 @@
os_memset(info, 0, sizeof(*info));
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info,
- NULL, NULL);
+ return send_and_recv_resp(drv, msg, get_noise_for_scan_results, info);
}
@@ -95,7 +94,7 @@
wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -126,7 +125,7 @@
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_INFO,
@@ -397,29 +396,24 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
if (drv->hostapd && is_ap_interface(drv->nlmode)) {
- enum nl80211_iftype old_mode = drv->nlmode;
-
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
*/
+ drv->ap_scan_as_station = drv->nlmode;
if (wpa_driver_nl80211_set_mode(
- bss, NL80211_IFTYPE_STATION))
- goto fail;
-
- if (wpa_driver_nl80211_scan(bss, params)) {
- wpa_driver_nl80211_set_mode(bss, old_mode);
+ bss, NL80211_IFTYPE_STATION) ||
+ wpa_driver_nl80211_scan(bss, params)) {
+ nl80211_restore_ap_mode(bss);
goto fail;
}
- /* Restore AP mode when processing scan results */
- drv->ap_scan_as_station = old_mode;
ret = 0;
} else
goto fail;
@@ -428,7 +422,9 @@
drv->scan_state = SCAN_REQUESTED;
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
- timeout = drv->uses_6ghz ? 15 : 10;
+ timeout = drv->uses_6ghz ? 20 : 10;
+ if (drv->uses_s1g)
+ timeout += 5;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver events to notify when scan is
@@ -650,7 +646,7 @@
params->sched_scan_start_delay))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
/* TODO: if we get an error here, we should fall back to normal scan */
@@ -687,7 +683,7 @@
#endif /* ANDROID */
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Sched scan stop failed: ret=%d (%s)",
@@ -727,7 +723,7 @@
static struct wpa_scan_res *
nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
- struct nl_msg *msg)
+ struct nl_msg *msg, const u8 *bssid)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -761,6 +757,9 @@
if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy))
return NULL;
+ if (bssid && bss[NL80211_BSS_BSSID] &&
+ !ether_addr_equal(bssid, nla_data(bss[NL80211_BSS_BSSID])))
+ return NULL;
if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -865,6 +864,7 @@
struct nl80211_bss_info_arg {
struct wpa_driver_nl80211_data *drv;
struct wpa_scan_results *res;
+ const u8 *bssid;
};
static int bss_info_handler(struct nl_msg *msg, void *arg)
@@ -874,7 +874,7 @@
struct wpa_scan_res **tmp;
struct wpa_scan_res *r;
- r = nl80211_parse_bss_info(_arg->drv, msg);
+ r = nl80211_parse_bss_info(_arg->drv, msg, _arg->bssid);
if (!r)
return NL_SKIP;
@@ -922,14 +922,14 @@
"nl80211: Local state (not associated) does not match with BSS state");
clear_state_mismatch(drv, r->bssid);
} else if (is_sta_interface(drv->nlmode) &&
- os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(drv->bssid, r->bssid)) {
wpa_printf(MSG_DEBUG,
"nl80211: Local state (associated with " MACSTR
") does not match with BSS state",
MAC2STR(drv->bssid));
- if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(drv->sta_mlo_info.ap_mld_addr,
+ drv->bssid)) {
clear_state_mismatch(drv, r->bssid);
if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
@@ -972,7 +972,7 @@
static struct wpa_scan_results *
-nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
{
struct nl_msg *msg;
struct wpa_scan_results *res;
@@ -992,7 +992,8 @@
arg.drv = drv;
arg.res = res;
- ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg, NULL, NULL);
+ arg.bssid = bssid;
+ ret = send_and_recv_resp(drv, msg, bss_info_handler, &arg);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1028,16 +1029,18 @@
/**
* wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ * @bssid: Return results only for the specified BSSID, %NULL for all
* Returns: Scan results on success, -1 on failure
*/
-struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
+ const u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct wpa_scan_results *res;
- res = nl80211_get_scan_results(drv);
+ res = nl80211_get_scan_results(drv, bssid);
if (res)
wpa_driver_nl80211_check_bss_status(drv, res);
return res;
@@ -1054,7 +1057,7 @@
struct nl80211_dump_scan_ctx *ctx = arg;
struct wpa_scan_res *r;
- r = nl80211_parse_bss_info(ctx->drv, msg);
+ r = nl80211_parse_bss_info(ctx->drv, msg, NULL);
if (!r)
return NL_SKIP;
wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
@@ -1076,8 +1079,7 @@
ctx.idx = 0;
msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
if (msg)
- send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx,
- NULL, NULL);
+ send_and_recv_resp(drv, msg, nl80211_dump_scan_handler, &ctx);
}
@@ -1268,10 +1270,14 @@
goto fail;
}
+ if (is_ap_interface(drv->nlmode) &&
+ params->link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID, params->link_id))
+ goto fail;
+
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie,
- NULL, NULL);
+ ret = send_and_recv_resp(drv, msg, scan_cookie_handler, &cookie);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -1334,7 +1340,7 @@
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ ret = send_and_recv_cmd(drv, msg);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 9beb6c4..23c7086 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -175,7 +175,7 @@
struct wpa_driver_roboswitch_data *drv = priv;
if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL &&
- os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0)
+ ether_addr_equal(buf, drv->own_addr))
drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
}
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index cf201fe..2c656fb 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -452,9 +452,10 @@
MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
if (is_zero_ether_addr(
(const u8 *) iwe->u.ap_addr.sa_data) ||
- os_memcmp(iwe->u.ap_addr.sa_data,
- "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
- 0) {
+ ether_addr_equal((const u8 *)
+ iwe->u.ap_addr.sa_data,
+ (const u8 *)
+ "\x44\x44\x44\x44\x44\x44")) {
os_free(drv->assoc_req_ies);
drv->assoc_req_ies = NULL;
os_free(drv->assoc_resp_ies);
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 7edb9df..29abc0c 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -161,11 +161,20 @@
ifr.ifr_ifindex = ifindex;
if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
int saved_errno = errno;
+ char in_br[IFNAMSIZ];
wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
"%s: %s", ifname, brname, strerror(errno));
errno = saved_errno;
- return -1;
+
+ /* If ioctl() returns EBUSY when adding an interface into the
+ * bridge, the interface might have already been added by an
+ * external operation, so check whether the interface is
+ * currently on the right bridge and ignore the error if it is.
+ */
+ if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
+ os_strcmp(in_br, brname) != 0)
+ return -1;
}
return 0;
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 4a09908..1d8ad62 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1771,12 +1771,13 @@
return NULL;
wpabuf_put_data(resp, identity, identity_len);
- wpabuf_free(privacy_identity);
os_free(sm->identity);
sm->identity = os_memdup(identity, identity_len);
sm->identity_len = identity_len;
+ wpabuf_free(privacy_identity);
+
return resp;
}
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index a1e7bff..fe61c83 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -255,8 +255,18 @@
cfg.new_ap_settings = &new_ap_settings;
}
- if (os_strstr(phase1, "multi_ap=1"))
- cfg.multi_ap_backhaul_sta = 1;
+ pos = os_strstr(phase1, "multi_ap=");
+ if (pos) {
+ u16 id = atoi(pos + 9);
+
+ if (id != 0) {
+ cfg.multi_ap_backhaul_sta = 1;
+ cfg.multi_ap_profile = id;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "EAP-WSC: Invalid multi_ap setting");
+ }
+ }
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
index 255d0fd..61c7299 100644
--- a/src/fst/fst_group.c
+++ b/src/fst/fst_group.c
@@ -364,8 +364,7 @@
cur_mbie, this_band_id);
if (!this_peer_addr)
continue;
- if (os_memcmp(this_peer_addr, peer_addr, ETH_ALEN) ==
- 0) {
+ if (ether_addr_equal(this_peer_addr, peer_addr)) {
os_memcpy(other_peer_addr, cur_peer_addr,
ETH_ALEN);
return other_iface;
diff --git a/src/fst/fst_iface.c b/src/fst/fst_iface.c
index 90c5fc0..96b2847 100644
--- a/src/fst/fst_iface.c
+++ b/src/fst/fst_iface.c
@@ -56,7 +56,7 @@
const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only);
for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only))
- if (os_memcmp(addr, a, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, a))
return true;
return false;
diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c
index e42a85c..49886ff 100644
--- a/src/fst/fst_session.c
+++ b/src/fst/fst_session.c
@@ -238,10 +238,8 @@
foreach_fst_session(s) {
if (s->group == g &&
- (os_memcmp(s->data.old_peer_addr, peer_addr,
- ETH_ALEN) == 0 ||
- os_memcmp(s->data.new_peer_addr, peer_addr,
- ETH_ALEN) == 0) &&
+ (ether_addr_equal(s->data.old_peer_addr, peer_addr) ||
+ ether_addr_equal(s->data.new_peer_addr, peer_addr)) &&
fst_session_is_in_progress(s))
return s;
}
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index 3f0b299..481c8ca 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -30,6 +30,9 @@
#include "eloop.h"
#include "l2_packet.h"
+#ifndef ETHER_VLAN_ENCAP_LEN
+#define ETHER_VLAN_ENCAP_LEN 4
+#endif
static const u8 pae_group_addr[ETH_ALEN] =
{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 7897bc0..5da0505 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -179,7 +179,7 @@
* authorization has been completed (in dormant state).
*/
if (l2->num_rx_br <= 1 &&
- (os_memcmp(eth->h_dest, l2->own_addr, ETH_ALEN) == 0 ||
+ (ether_addr_equal(eth->h_dest, l2->own_addr) ||
is_multicast_ether_addr(eth->h_dest))) {
wpa_printf(MSG_DEBUG,
"l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
@@ -241,7 +241,7 @@
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
__func__, MAC2STR(ll.sll_addr), (int) res);
- if (os_memcmp(ll.sll_addr, l2->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(ll.sll_addr, l2->own_addr)) {
wpa_printf(MSG_DEBUG, "%s: Drop RX of own frame", __func__);
return;
}
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 267399d..df7cb7f 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -301,6 +301,8 @@
ies) < 0) {
p2p_dbg(p2p, "Failed to start listen mode");
p2p->pending_listen_freq = 0;
+ } else {
+ p2p->pending_listen_wait_drv = true;
}
wpabuf_free(ies);
}
@@ -350,6 +352,7 @@
wpabuf_free(ies);
return -1;
}
+ p2p->pending_listen_wait_drv = true;
wpabuf_free(ies);
p2p_set_state(p2p, P2P_LISTEN_ONLY);
@@ -378,7 +381,7 @@
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(dev->info.p2p_device_addr, addr))
return dev;
}
return NULL;
@@ -396,7 +399,7 @@
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(dev->interface_addr, addr))
return dev;
}
return NULL;
@@ -486,8 +489,8 @@
* group, the information will be restored in the loop following this.
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
- ETH_ALEN) == 0) {
+ if (ether_addr_equal(dev->member_in_go_iface,
+ go_interface_addr)) {
os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
}
@@ -495,8 +498,7 @@
for (c = 0; c < info.num_clients; c++) {
struct p2p_client_info *cli = &info.client[c];
- if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
- ETH_ALEN) == 0)
+ if (ether_addr_equal(cli->p2p_device_addr, p2p->cfg->dev_addr))
continue; /* ignore our own entry */
dev = p2p_get_device(p2p, cli->p2p_device_addr);
if (dev) {
@@ -754,7 +756,7 @@
}
if (!is_zero_ether_addr(p2p->peer_filter) &&
- os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
+ !ether_addr_equal(p2p_dev_addr, p2p->peer_filter)) {
p2p_dbg(p2p, "Do not add peer filter for " MACSTR
" due to peer filter", MAC2STR(p2p_dev_addr));
p2p_parse_free(&msg);
@@ -799,7 +801,7 @@
dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY |
P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT);
- if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(addr, p2p_dev_addr))
os_memcpy(dev->interface_addr, addr, ETH_ALEN);
if (msg.ssid &&
msg.ssid[1] <= sizeof(dev->oper_ssid) &&
@@ -1030,6 +1032,7 @@
return;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
if (p2p->find_pending_full &&
(p2p->find_type == P2P_FIND_PROGRESSIVE ||
@@ -1247,6 +1250,7 @@
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
p2p->find_pending_full = 0;
p2p->find_type = type;
if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
@@ -1341,6 +1345,10 @@
void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
{
+ p2p_dbg(p2p,
+ "%s(freq=%d) pending_listen_freq=%d in_listen=%d drv_in_listen=%d",
+ __func__, freq, p2p->pending_listen_freq, p2p->in_listen,
+ p2p->drv_in_listen);
if (freq > 0 &&
((p2p->drv_in_listen == freq && p2p->in_listen) ||
p2p->pending_listen_freq == (unsigned int) freq)) {
@@ -1360,7 +1368,15 @@
p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen);
p2p->drv_in_listen = 0;
}
+ if (p2p->pending_listen_freq &&
+ p2p->pending_listen_freq != (unsigned int) freq &&
+ !p2p->drv_in_listen && p2p->pending_listen_wait_drv) {
+ p2p_dbg(p2p,
+ "Clear pending_listen_freq since the started listen did not complete before being stopped");
+ p2p->pending_listen_freq = 0;
+ }
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
}
@@ -2029,6 +2045,7 @@
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
p2p->go_neg_peer->status = P2P_SC_SUCCESS;
/*
* Set new timeout to make sure a previously set one does not expire
@@ -2049,6 +2066,7 @@
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
p2p->invite_dev_pw_id);
}
@@ -2383,7 +2401,7 @@
}
if (dst && !is_broadcast_ether_addr(dst) &&
- os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(dst, p2p->cfg->dev_addr)) {
/* Not sent to the broadcast address or our P2P Device Address
*/
p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it",
@@ -2467,7 +2485,7 @@
}
if (msg.device_id &&
- os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(msg.device_id, p2p->cfg->dev_addr)) {
/* Device ID did not match */
p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it",
MAC2STR(msg.device_id));
@@ -2556,8 +2574,7 @@
*/
if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
p2p->go_neg_peer &&
- os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
- == 0 &&
+ ether_addr_equal(addr, p2p->go_neg_peer->info.p2p_device_addr) &&
!(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
/* Received a Probe Request from GO Negotiation peer */
p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout");
@@ -2569,8 +2586,7 @@
if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
p2p->invite_peer &&
(p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) &&
- os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
- == 0) {
+ ether_addr_equal(addr, p2p->invite_peer->info.p2p_device_addr)) {
/* Received a Probe Request from Invite peer */
p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout");
eloop_cancel_timeout(p2p_invite_start, p2p, NULL);
@@ -2923,8 +2939,7 @@
return; /* No pending Group Formation */
}
- if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
- 0) {
+ if (!ether_addr_equal(mac_addr, p2p->go_neg_peer->intended_addr)) {
p2p_dbg(p2p, "Ignore WPS registration success notification for "
MACSTR " (GO Negotiation peer " MACSTR ")",
MAC2STR(mac_addr),
@@ -3333,7 +3348,7 @@
}
-static void p2p_sd_cb(struct p2p_data *p2p, int success)
+void p2p_sd_query_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d",
success);
@@ -3397,8 +3412,8 @@
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(p2p->pending_pd_devaddr,
- dev->info.p2p_device_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr))
continue;
if (!dev->req_config_methods)
continue;
@@ -3836,7 +3851,7 @@
p2p_go_neg_conf_cb(p2p, result);
break;
case P2P_PENDING_SD:
- p2p_sd_cb(p2p, success);
+ p2p_sd_query_cb(p2p, success);
break;
case P2P_PENDING_PD:
p2p_prov_disc_cb(p2p, success);
@@ -3881,6 +3896,7 @@
p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback",
p2p->pending_listen_sec, p2p->pending_listen_usec,
p2p->pending_listen_freq);
+ p2p->pending_listen_wait_drv = false;
p2p->in_listen = 1;
p2p->drv_in_listen = freq;
if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
@@ -4036,6 +4052,7 @@
p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
if (p2p->pending_listen_freq) {
p2p_dbg(p2p, "Clear pending_listen_freq for %s", __func__);
p2p->pending_listen_freq = 0;
@@ -4089,8 +4106,8 @@
int for_join = 0;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(p2p->pending_pd_devaddr,
- dev->info.p2p_device_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr))
continue;
if (dev->req_config_methods &&
(dev->flags & P2P_DEV_PD_FOR_JOIN))
@@ -4163,6 +4180,7 @@
if (p2p->drv_in_listen) {
p2p_dbg(p2p, "Driver is still in listen state - stop it");
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->pending_listen_wait_drv = false;
}
switch (p2p->state) {
@@ -4557,8 +4575,8 @@
p2p_dbg(p2p, "Received P2P Action - P2P Presence Request");
for (g = 0; g < p2p->num_groups; g++) {
- if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
- ETH_ALEN) == 0) {
+ if (ether_addr_equal(
+ da, p2p_group_get_interface_addr(p2p->groups[g]))) {
group = p2p->groups[g];
break;
}
@@ -4656,15 +4674,6 @@
p2p_ext_listen_timeout, p2p, NULL);
}
- if ((p2p->cfg->is_p2p_in_progress &&
- p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
- (p2p->pending_action_state == P2P_PENDING_PD &&
- p2p->pd_retries > 0)) {
- p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
- p2p_state_txt(p2p->state));
- return;
- }
-
if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
/*
* This should not really happen, but it looks like the Listen
@@ -4677,6 +4686,15 @@
p2p_set_state(p2p, P2P_IDLE);
}
+ if ((p2p->cfg->is_p2p_in_progress &&
+ p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
+ (p2p->pending_action_state == P2P_PENDING_PD &&
+ p2p->pd_retries > 0)) {
+ p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
if (p2p->state != P2P_IDLE) {
p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state));
return;
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
index 98805fe..ff2bdfc 100644
--- a/src/p2p/p2p_dev_disc.c
+++ b/src/p2p/p2p_dev_disc.c
@@ -223,7 +223,7 @@
go = p2p->pending_client_disc_go;
if (go == NULL ||
- os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, go->info.p2p_device_addr)) {
p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response");
return;
}
@@ -249,10 +249,10 @@
p2p_dbg(p2p, "Device Discoverability Response status %u", status);
if (p2p->go_neg_peer == NULL ||
- os_memcmp(p2p->pending_client_disc_addr,
- p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
- os_memcmp(p2p->go_neg_peer->member_in_go_dev,
- go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(p2p->pending_client_disc_addr,
+ p2p->go_neg_peer->info.p2p_device_addr) ||
+ !ether_addr_equal(p2p->go_neg_peer->member_in_go_dev,
+ go->info.p2p_device_addr)) {
p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore");
return;
}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 55db8a5..30901b3 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -859,7 +859,7 @@
goto fail;
}
- if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(msg.p2p_device_addr, sa)) {
p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR
" != dev_addr=" MACSTR,
MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index aa18af6..c036f92 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -579,7 +579,7 @@
m = group->members;
prev = NULL;
while (m) {
- if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(m->addr, addr))
break;
prev = m;
m = m->next;
@@ -785,11 +785,11 @@
if (!msg.device_id)
return 1; /* No filter on Device ID */
- if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(msg.device_id, group->p2p->cfg->dev_addr))
return 1; /* Match with our P2P Device Address */
for (m = group->members; m; m = m->next) {
- if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(msg.device_id, m->dev_addr))
return 1; /* Match with group client P2P Device Address */
}
@@ -844,7 +844,7 @@
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
- if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(dev_id, m->dev_addr))
return m;
}
@@ -872,7 +872,7 @@
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
- if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(interface_addr, m->addr))
return m;
}
@@ -1038,7 +1038,7 @@
struct p2p_group_member *m;
for (m = group->members; m; m = m->next) {
- if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(m->dev_addr, dev_addr))
return 1;
}
@@ -1051,7 +1051,7 @@
{
if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
return 0;
- if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(group_id, group->p2p->cfg->dev_addr))
return 0;
return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
group->cfg->ssid_len) == 0;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 235467e..5239ee4 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -400,6 +400,7 @@
unsigned int pending_listen_freq;
unsigned int pending_listen_sec;
unsigned int pending_listen_usec;
+ bool pending_listen_wait_drv;
u8 dev_capab;
@@ -909,6 +910,9 @@
const struct weighted_pcl *freq_list,
unsigned int num_channels,
struct p2p_channels *res, bool go);
+
+void p2p_sd_query_cb(struct p2p_data *p2p, int success);
+
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 941ec00..70a7f6f 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -33,8 +33,8 @@
for (i = 0; i < p2p->num_groups; i++) {
struct p2p_group *g = p2p->groups[i];
struct wpabuf *ie;
- if (os_memcmp(p2p_group_get_interface_addr(g),
- p2p->inv_bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(p2p_group_get_interface_addr(g),
+ p2p->inv_bssid))
continue;
ie = p2p_group_get_wfd_ie(g);
if (ie) {
@@ -127,8 +127,8 @@
for (i = 0; i < p2p->num_groups; i++) {
struct p2p_group *g = p2p->groups[i];
struct wpabuf *ie;
- if (os_memcmp(p2p_group_get_interface_addr(g),
- group_bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(p2p_group_get_interface_addr(g),
+ group_bssid))
continue;
ie = p2p_group_get_wfd_ie(g);
if (ie) {
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index f75cee8..542521e 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -544,7 +544,7 @@
if (msg->persistent_dev) {
channel_list = 1;
config_timeout = 1;
- if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(msg->persistent_dev, addr)) {
intended_addr = 1;
operating_channel = 1;
}
@@ -730,7 +730,7 @@
if (!msg.status) {
unsigned int forced_freq, pref_freq;
- if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
+ 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;
@@ -892,14 +892,14 @@
goto out;
if (p2p->p2ps_prov->adv_id != adv_id ||
- os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
+ !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 ||
- os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
+ !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;
}
@@ -1239,8 +1239,7 @@
return -1;
}
- if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
- ETH_ALEN)) {
+ if (!ether_addr_equal(p2p->p2ps_prov->session_mac, msg->session_mac)) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Session MAC");
return -1;
@@ -1252,7 +1251,7 @@
return -1;
}
- if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(p2p->p2ps_prov->adv_mac, msg->adv_mac)) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Advertisement MAC");
return -1;
@@ -1396,7 +1395,7 @@
* was sent earlier, we reset that state info here.
*/
if (p2p->user_initiated_pd &&
- os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
+ ether_addr_equal(p2p->pending_pd_devaddr, sa))
p2p_reset_pending_pd(p2p);
if (msg.wps_config_methods != req_config_methods) {
@@ -1758,8 +1757,8 @@
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcmp(p2p->pending_pd_devaddr,
- dev->info.p2p_device_addr, ETH_ALEN))
+ if (!ether_addr_equal(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr))
continue;
if (!dev->req_config_methods)
continue;
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index b9e753f..5537a6e 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -79,8 +79,7 @@
count++;
}
if (!q->for_all_peers &&
- os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
- 0)
+ ether_addr_equal(q->peer, dev->info.p2p_device_addr))
goto found;
}
@@ -289,8 +288,7 @@
if (query == NULL)
return -1;
if (p2p->state == P2P_SEARCH &&
- os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr,
- ETH_ALEN) == 0) {
+ ether_addr_equal(p2p->sd_query_no_ack, dev->info.p2p_device_addr)) {
p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR
" due to it being the first no-ACK peer in this search iteration",
MAC2STR(dev->info.p2p_device_addr));
@@ -491,12 +489,21 @@
u16 slen;
u16 update_indic;
- if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
- os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ if ((p2p->state != P2P_SD_DURING_FIND && p2p->state != P2P_SEARCH) ||
+ !p2p->sd_peer ||
+ !ether_addr_equal(sa, p2p->sd_peer->info.p2p_device_addr)) {
p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from "
MACSTR, MAC2STR(sa));
return;
}
+ if (p2p->state == P2P_SEARCH) {
+ /* It is possible for the TX status and RX response events to be
+ * reordered, so assume the request was ACKed if a response is
+ * received. */
+ p2p_dbg(p2p,
+ "GAS Initial Request had not yet received TX status - process the response anyway");
+ p2p_sd_query_cb(p2p, 1);
+ }
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_clear_timeout(p2p);
@@ -642,7 +649,7 @@
p2p_dbg(p2p, "No pending SD response fragment available");
return;
}
- if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sa, p2p->sd_resp_addr)) {
p2p_dbg(p2p, "No pending SD response fragment for " MACSTR,
MAC2STR(sa));
return;
@@ -707,7 +714,7 @@
wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
- os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, p2p->sd_peer->info.p2p_device_addr)) {
p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from "
MACSTR, MAC2STR(sa));
return;
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index f75fd8f..bc33a25 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1215,7 +1215,7 @@
/* My message id is used by other participant */
if (peer_mn > participant->mn &&
!reset_participant_mi(participant))
- wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
+ wpa_printf(MSG_DEBUG, "KaY: Could not update MI");
continue;
}
}
@@ -2560,7 +2560,7 @@
}
if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
- wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail");
+ wpa_printf(MSG_ERROR, "KaY: encode MKPDU fail");
return -1;
}
@@ -2628,9 +2628,7 @@
struct ieee802_1x_kay_peer, list) {
if (now.sec > peer->expire) {
wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
- wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
- sizeof(peer->mi));
- wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ ieee802_1x_kay_dump_peer(peer);
dl_list_for_each_safe(rxsc, pre_rxsc,
&participant->rxsc_list,
struct receive_sc, list) {
@@ -2659,10 +2657,10 @@
if (key_server_removed) {
if (!reset_participant_mi(participant))
wpa_printf(MSG_WARNING,
- "KaY: Could not update mi on key server removal");
+ "KaY: Could not update MI on key server removal");
else
wpa_printf(MSG_DEBUG,
- "KaY: Update mi on key server removal");
+ "KaY: Update MI on key server removal");
}
if (lp_changed) {
@@ -2706,9 +2704,7 @@
struct ieee802_1x_kay_peer, list) {
if (now.sec > peer->expire) {
wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
- wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
- sizeof(peer->mi));
- wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+ ieee802_1x_kay_dump_peer(peer);
dl_list_del(&peer->list);
os_free(peer);
}
@@ -3165,7 +3161,7 @@
be_to_host16(eth_hdr->ethertype));
/* the destination address shall not be an individual address */
- if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(eth_hdr->dest, pae_group_addr)) {
wpa_printf(MSG_DEBUG,
"KaY: ethernet destination address is not PAE group address");
return -1;
@@ -3413,7 +3409,7 @@
"KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
MKA_SAK_USE);
if (!reset_participant_mi(participant))
- wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
+ wpa_printf(MSG_DEBUG, "KaY: Could not update MI");
else
wpa_printf(MSG_DEBUG,
"KaY: Selected a new random MI: %s",
@@ -3630,7 +3626,7 @@
goto error;
}
- wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
+ wpa_printf(MSG_DEBUG, "KaY: SecY init MACsec done");
/* init CP */
kay->cp = ieee802_1x_cp_sm_init(kay);
@@ -3721,7 +3717,7 @@
kay->if_name, mode_txt(mode), yes_no(is_authenticator));
if (!kay || !ckn || !cak) {
- wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
+ wpa_printf(MSG_ERROR, "KaY: CKN or CAK is null");
return NULL;
}
diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c
new file mode 100644
index 0000000..e2c6681
--- /dev/null
+++ b/src/pasn/pasn_common.c
@@ -0,0 +1,232 @@
+/*
+ * PASN common processing
+ *
+ * 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/wpa_common.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "common/ieee802_11_defs.h"
+#include "pasn_common.h"
+
+
+struct pasn_data * pasn_data_init(void)
+{
+ struct pasn_data *pasn = os_zalloc(sizeof(struct pasn_data));
+
+ return pasn;
+}
+
+
+void pasn_data_deinit(struct pasn_data *pasn)
+{
+ bin_clear_free(pasn, sizeof(struct pasn_data));
+}
+
+
+void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx,
+ int (*send_mgmt)(void *ctx, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+ unsigned int wait),
+ int (*validate_custom_pmkid)(void *ctx,
+ const u8 *addr,
+ const u8 *pmkid))
+{
+ if (!pasn)
+ return;
+
+ pasn->cb_ctx = cb_ctx;
+ pasn->send_mgmt = send_mgmt;
+ pasn->validate_custom_pmkid = validate_custom_pmkid;
+}
+
+
+void pasn_enable_kdk_derivation(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return;
+ pasn->derive_kdk = true;
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+}
+
+
+void pasn_disable_kdk_derivation(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return;
+ pasn->derive_kdk = false;
+ pasn->kdk_len = 0;
+}
+
+
+void pasn_set_akmp(struct pasn_data *pasn, int akmp)
+{
+ if (!pasn)
+ return;
+ pasn->akmp = akmp;
+}
+
+
+void pasn_set_cipher(struct pasn_data *pasn, int cipher)
+{
+ if (!pasn)
+ return;
+ pasn->cipher = cipher;
+}
+
+
+void pasn_set_own_addr(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->own_addr, addr, ETH_ALEN);
+}
+
+
+void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+}
+
+
+void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr)
+{
+ if (!pasn || !addr)
+ return;
+ os_memcpy(pasn->bssid, addr, ETH_ALEN);
+}
+
+
+int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt)
+{
+ if (!pasn)
+ return -1;
+#ifdef CONFIG_SAE
+ pasn->pt = pt;
+ return 0;
+#else /* CONFIG_SAE */
+ return -1;
+#endif /* CONFIG_SAE */
+}
+
+
+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)
+ return;
+ pasn->wpa_key_mgmt = key_mgmt;
+}
+
+
+void pasn_set_rsn_pairwise(struct pasn_data *pasn, int rsn_pairwise)
+{
+ if (!pasn)
+ return;
+ pasn->rsn_pairwise = rsn_pairwise;
+}
+
+
+void pasn_set_rsnxe_caps(struct pasn_data *pasn, u16 rsnxe_capab)
+{
+ if (!pasn)
+ return;
+ pasn->rsnxe_capab = rsnxe_capab;
+}
+
+
+void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie)
+{
+ if (!pasn || !rsnxe_ie)
+ return;
+ pasn->rsnxe_ie = rsnxe_ie;
+}
+
+
+void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid)
+{
+ if (!pasn || !pmkid)
+ return;
+ os_memcpy(pasn->custom_pmkid, pmkid, PMKID_LEN);
+ pasn->custom_pmkid_valid = true;
+}
+
+
+int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ if (!pasn || !extra_ies_len || !extra_ies)
+ return -1;
+
+ if (pasn->extra_ies) {
+ os_free((u8 *) pasn->extra_ies);
+ pasn->extra_ies_len = extra_ies_len;
+ }
+
+ pasn->extra_ies = os_memdup(extra_ies, extra_ies_len);
+ if (!pasn->extra_ies) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Extra IEs memory allocation failed");
+ return -1;
+ }
+ pasn->extra_ies_len = extra_ies_len;
+ return 0;
+}
+
+
+int pasn_get_akmp(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->akmp;
+}
+
+
+int pasn_get_cipher(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->cipher;
+}
+
+
+size_t pasn_get_pmk_len(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return 0;
+ return pasn->pmk_len;
+}
+
+
+u8 * pasn_get_pmk(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return NULL;
+ return pasn->pmk;
+}
+
+
+struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn)
+{
+ if (!pasn)
+ return NULL;
+ return &pasn->ptk;
+}
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index a4850a2..36710c2 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -16,8 +16,6 @@
extern "C" {
#endif
-#ifdef CONFIG_PASN
-
enum pasn_fils_state {
PASN_FILS_STATE_NONE = 0,
PASN_FILS_STATE_PENDING_AS,
@@ -35,19 +33,46 @@
};
struct pasn_data {
+ /* External modules access below variables using setter and getter
+ * functions */
int akmp;
int cipher;
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ struct rsn_pmksa_cache *pmksa;
+ bool derive_kdk;
+ size_t kdk_len;
+ void *cb_ctx;
+
+#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;
+ bool custom_pmkid_valid;
+ u8 custom_pmkid[PMKID_LEN];
+
+ /*
+ * Extra elements to add into Authentication frames. These can be used,
+ * e.g., for Wi-Fi Aware use cases.
+ */
+ const u8 *extra_ies;
+ size_t extra_ies_len;
+
+ /* External modules do not access below variables */
u16 group;
bool secure_ltf;
int freq;
- size_t kdk_len;
u8 trans_seq;
u8 status;
- u8 own_addr[ETH_ALEN];
- u8 peer_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
size_t pmk_len;
u8 pmk[PMK_LEN_MAX];
bool using_pmksa;
@@ -63,7 +88,6 @@
#ifdef CONFIG_SAE
struct sae_data sae;
- struct sae_pt *pt;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
@@ -81,15 +105,12 @@
* differently for the PASN initiator (using RSN Supplicant
* implementation) and PASN responser (using RSN Authenticator
* implementation). Functions cannot be mixed between those cases. */
- struct rsn_pmksa_cache *pmksa;
struct rsn_pmksa_cache_entry *pmksa_entry;
struct eapol_sm *eapol;
int fast_reauth;
#ifdef CONFIG_TESTING_OPTIONS
int corrupt_mic;
#endif /* CONFIG_TESTING_OPTIONS */
- void *cb_ctx;
- u16 rsnxe_capab;
int network_id;
u8 wrapped_data_format;
@@ -97,16 +118,11 @@
/* Responder */
bool noauth; /* Whether PASN without mutual authentication is enabled */
- int wpa_key_mgmt;
- int rsn_pairwise;
- bool derive_kdk;
- const char *password;
int disable_pmksa_caching;
int *pasn_groups;
struct wpabuf *wrapped_data;
int use_anti_clogging;
const u8 *rsn_ie;
- const u8 *rsnxe_ie;
size_t rsn_ie_len;
u8 *comeback_key;
@@ -114,16 +130,6 @@
u16 comeback_idx;
u16 *comeback_pending_idx;
- bool custom_pmkid_valid;
- u8 custom_pmkid[PMKID_LEN];
-
- /**
- * Extra elements to add into Authentication frames. These can be used,
- * e.g., for Wi-Fi Aware use cases.
- */
- const u8 *extra_ies;
- size_t extra_ies_len;
-
/**
* send_mgmt - Function handler to transmit a Management frame
* @ctx: Callback context from cb_ctx
@@ -147,7 +153,6 @@
};
/* Initiator */
-
void wpa_pasn_reset(struct pasn_data *pasn);
int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
const u8 *peer_addr, const u8 *bssid,
@@ -177,7 +182,45 @@
const u8 *peer_addr,
struct rsn_pmksa_cache_entry *pmksa, u16 status);
-#endif /* CONFIG_PASN */
+struct pasn_data * pasn_data_init(void);
+void pasn_data_deinit(struct pasn_data *pasn);
+void pasn_register_callbacks(struct pasn_data *pasn, void *cb_ctx,
+ int (*send_mgmt)(void *ctx, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+ unsigned int wait),
+ int (*validate_custom_pmkid)(void *ctx,
+ const u8 *addr,
+ const u8 *pmkid));
+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_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,
+ struct rsn_pmksa_cache *pmksa);
+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);
+
+/* Responder */
+void pasn_set_password(struct pasn_data *pasn, const char *password);
+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_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);
+
+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);
#ifdef __cplusplus
}
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index 1f9a508..d273067 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -26,6 +26,14 @@
#include "pasn_common.h"
+void pasn_set_initiator_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa)
+{
+ if (pasn)
+ pasn->pmksa = pmksa;
+}
+
+
#ifdef CONFIG_SAE
static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn)
@@ -378,8 +386,8 @@
return -1;
}
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
+ true);
if (!fils_wd) {
wpa_printf(MSG_DEBUG,
@@ -741,6 +749,11 @@
pasn->rsn_ie_len = 0;
pasn->rsnxe_ie = NULL;
pasn->custom_pmkid_valid = false;
+
+ if (pasn->extra_ies) {
+ os_free((u8 *) pasn->extra_ies);
+ pasn->extra_ies = NULL;
+ }
}
@@ -1024,15 +1037,15 @@
return false;
/* Not our frame; do nothing */
- if (os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(mgmt->bssid, pasn->bssid))
return false;
- if (rx && (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->sa, pasn->peer_addr, ETH_ALEN) != 0))
+ if (rx && (!ether_addr_equal(mgmt->da, pasn->own_addr) ||
+ !ether_addr_equal(mgmt->sa, pasn->peer_addr)))
return false;
- if (!rx && (os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) != 0 ||
- os_memcmp(mgmt->da, pasn->peer_addr, ETH_ALEN) != 0))
+ if (!rx && (!ether_addr_equal(mgmt->sa, pasn->own_addr) ||
+ !ether_addr_equal(mgmt->da, pasn->peer_addr)))
return false;
/* Not PASN; do nothing */
@@ -1200,9 +1213,9 @@
}
if (pasn_params->wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len,
+ true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index 47be403..b991364 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -25,6 +25,15 @@
#include "ap/pmksa_cache_auth.h"
#include "pasn_common.h"
+
+void pasn_set_responder_pmksa(struct pasn_data *pasn,
+ struct rsn_pmksa_cache *pmksa)
+{
+ if (pasn)
+ pasn->pmksa = pmksa;
+}
+
+
#ifdef CONFIG_PASN
#ifdef CONFIG_SAE
@@ -753,9 +762,8 @@
derive_keys = true;
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len, true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -979,9 +987,9 @@
}
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems,
- WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len,
+ true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
diff --git a/src/radius/radius.c b/src/radius/radius.c
index be59a94..37aa216 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -423,25 +423,54 @@
}
+u8 * radius_msg_add_msg_auth(struct radius_msg *msg)
+{
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+
+ os_memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (!attr) {
+ wpa_printf(MSG_ERROR,
+ "WARNING: Could not add Message-Authenticator");
+ return NULL;
+ }
+
+ return (u8 *) (attr + 1);
+}
+
+
+static u8 * radius_msg_auth_pos(struct radius_msg *msg)
+{
+ u8 *pos;
+ size_t alen;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ &pos, &alen, NULL) == 0 &&
+ alen == MD5_MAC_LEN) {
+ /* Use already added Message-Authenticator attribute */
+ return pos;
+ }
+
+ /* Add a Message-Authenticator attribute */
+ return radius_msg_add_msg_auth(msg);
+}
+
+
int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
size_t secret_len)
{
if (secret) {
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg,
- RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_WARNING, "RADIUS: Could not add "
- "Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
} else
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
@@ -457,23 +486,19 @@
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
size_t secret_len, const u8 *req_authenticator)
{
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
const u8 *addr[4];
size_t len[4];
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memcpy(msg->hdr->authenticator, req_authenticator,
sizeof(msg->hdr->authenticator));
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
addr[0] = (u8 *) msg->hdr;
@@ -501,21 +526,17 @@
{
const u8 *addr[2];
size_t len[2];
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
addr[0] = wpabuf_head_u8(msg->buf);
@@ -980,6 +1001,20 @@
return 1;
}
+ if (!auth) {
+ u8 *pos;
+ size_t alen;
+
+ if (radius_msg_get_attr_ptr(msg,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ &pos, &alen, NULL) == 0) {
+ /* Check the Message-Authenticator attribute since it
+ * was included even if we are configured to not
+ * require it. */
+ auth = 1;
+ }
+ }
+
if (auth &&
radius_msg_verify_msg_auth(msg, secret, secret_len,
sent_msg->hdr->authenticator)) {
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 571c159..05fddba 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -268,6 +268,7 @@
struct radius_msg * radius_msg_new(u8 code, u8 identifier);
void radius_msg_free(struct radius_msg *msg);
void radius_msg_dump(struct radius_msg *msg);
+u8 * radius_msg_add_msg_auth(struct radius_msg *msg);
int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
size_t secret_len);
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index ee9e46d..2a7f361 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -1,18 +1,20 @@
/*
* RADIUS client
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
+#include <fcntl.h>
#include <net/if.h>
#include "common.h"
+#include "eloop.h"
+#include "crypto/tls.h"
#include "radius.h"
#include "radius_client.h"
-#include "eloop.h"
/* Defaults for RADIUS retransmit values (exponential backoff) */
@@ -169,36 +171,36 @@
struct hostapd_radius_servers *conf;
/**
- * auth_serv_sock - IPv4 socket for RADIUS authentication messages
- */
- int auth_serv_sock;
-
- /**
- * acct_serv_sock - IPv4 socket for RADIUS accounting messages
- */
- int acct_serv_sock;
-
- /**
- * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
- */
- int auth_serv_sock6;
-
- /**
- * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
- */
- int acct_serv_sock6;
-
- /**
* auth_sock - Currently used socket for RADIUS authentication server
*/
int auth_sock;
/**
+ * auth_tls - Whether current authentication connection uses TLS
+ */
+ bool auth_tls;
+
+ /**
+ * auth_tls_ready - Whether authentication TLS is ready
+ */
+ bool auth_tls_ready;
+
+ /**
* acct_sock - Currently used socket for RADIUS accounting server
*/
int acct_sock;
/**
+ * acct_tls - Whether current accounting connection uses TLS
+ */
+ bool acct_tls;
+
+ /**
+ * acct_tls_ready - Whether accounting TLS is ready
+ */
+ bool acct_tls_ready;
+
+ /**
* auth_handlers - Authentication message handlers
*/
struct radius_rx_handler *auth_handlers;
@@ -242,6 +244,12 @@
* interim_error_cb_ctx - interim_error_cb() context data
*/
void *interim_error_cb_ctx;
+
+#ifdef CONFIG_RADIUS_TLS
+ void *tls_ctx;
+ struct tls_connection *auth_tls_conn;
+ struct tls_connection *acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
};
@@ -249,7 +257,7 @@
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
- int sock, int sock6, int auth);
+ int auth);
static int radius_client_init_acct(struct radius_client_data *radius);
static int radius_client_init_auth(struct radius_client_data *radius);
static void radius_client_auth_failover(struct radius_client_data *radius);
@@ -374,9 +382,19 @@
u8 *acct_delay_time;
size_t acct_delay_time_len;
int num_servers;
+#ifdef CONFIG_RADIUS_TLS
+ struct wpabuf *out = NULL;
+ struct tls_connection *conn = NULL;
+ bool acct = false;
+#endif /* CONFIG_RADIUS_TLS */
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) {
+#ifdef CONFIG_RADIUS_TLS
+ acct = true;
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_acct_servers;
if (radius->acct_sock < 0)
radius_client_init_acct(radius);
@@ -394,6 +412,10 @@
conf->acct_server->retransmissions++;
}
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_auth_servers;
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
@@ -429,6 +451,15 @@
return 1;
}
+#ifdef CONFIG_RADIUS_TLS
+ if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
+ (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection not yet ready for TX");
+ goto not_ready;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
if (entry->msg_type == RADIUS_ACCT &&
radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
&acct_delay_time, &acct_delay_time_len,
@@ -473,11 +504,37 @@
os_get_reltime(&entry->last_attempt);
buf = radius_msg_get_buf(entry->msg);
+#ifdef CONFIG_RADIUS_TLS
+ if (conn) {
+ out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to encrypt RADIUS message (TLS)");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
+ wpabuf_len(buf), wpabuf_len(out));
+ buf = out;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
+ wpabuf_len(buf));
if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
if (radius_client_handle_send_error(radius, s, entry->msg_type)
- > 0)
+ > 0) {
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+#endif /* CONFIG_RADIUS_TLS */
return 0;
+ }
}
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+
+not_ready:
+#endif /* CONFIG_RADIUS_TLS */
entry->next_try = now + entry->next_wait;
entry->next_wait *= 2;
@@ -598,9 +655,7 @@
if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
next = conf->auth_servers;
conf->auth_server = next;
- radius_change_server(radius, next, old,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1);
+ radius_change_server(radius, next, old, 1);
}
@@ -628,9 +683,7 @@
if (next > &conf->acct_servers[conf->num_acct_servers - 1])
next = conf->acct_servers;
conf->acct_server = next;
- radius_change_server(radius, next, old,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0);
+ radius_change_server(radius, next, old, 0);
}
@@ -719,6 +772,52 @@
}
+static int radius_client_disable_pmtu_discovery(int s)
+{
+ int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+ int action = IP_PMTUDISC_DONT;
+ r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+ sizeof(action));
+ if (r == -1)
+ wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+ strerror(errno));
+#endif
+ return r;
+}
+
+
+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)
+ eloop_unregister_sock(radius->auth_sock,
+ EVENT_TYPE_WRITE);
+#endif /* CONFIG_RADIUS_TLS */
+ eloop_unregister_read_sock(radius->auth_sock);
+ close(radius->auth_sock);
+ radius->auth_sock = -1;
+ }
+}
+
+
+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)
+ eloop_unregister_sock(radius->acct_sock,
+ EVENT_TYPE_WRITE);
+#endif /* CONFIG_RADIUS_TLS */
+ eloop_unregister_read_sock(radius->acct_sock);
+ close(radius->acct_sock);
+ radius->acct_sock = -1;
+ }
+}
+
+
/**
* radius_client_send - Send a RADIUS request
* @radius: RADIUS client context from radius_client_init()
@@ -754,8 +853,18 @@
char *name;
int s, res;
struct wpabuf *buf;
+#ifdef CONFIG_RADIUS_TLS
+ struct wpabuf *out = NULL;
+ struct tls_connection *conn = NULL;
+ bool acct = false;
+#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+#ifdef CONFIG_RADIUS_TLS
+ acct = true;
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
if (conf->acct_server && radius->acct_sock < 0)
radius_client_init_acct(radius);
@@ -774,6 +883,10 @@
s = radius->acct_sock;
conf->acct_server->requests++;
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+#endif /* CONFIG_RADIUS_TLS */
if (conf->auth_server && radius->auth_sock < 0)
radius_client_init_auth(radius);
@@ -799,11 +912,42 @@
if (conf->msg_dumps)
radius_msg_dump(msg);
+#ifdef CONFIG_RADIUS_TLS
+ if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
+ (!acct && radius->auth_tls && !radius->auth_tls_ready)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection not yet ready for TX");
+ goto skip_send;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
buf = radius_msg_get_buf(msg);
+#ifdef CONFIG_RADIUS_TLS
+ if (conn) {
+ out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to encrypt RADIUS message (TLS)");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
+ wpabuf_len(buf), wpabuf_len(out));
+ buf = out;
+ }
+#endif /* CONFIG_RADIUS_TLS */
+ wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
+ wpabuf_len(buf));
res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
+#ifdef CONFIG_RADIUS_TLS
+ wpabuf_free(out);
+#endif /* CONFIG_RADIUS_TLS */
if (res < 0)
radius_client_handle_send_error(radius, s, msg_type);
+#ifdef CONFIG_RADIUS_TLS
+skip_send:
+#endif /* CONFIG_RADIUS_TLS */
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
@@ -811,6 +955,137 @@
}
+#ifdef CONFIG_RADIUS_TLS
+
+static void radius_client_close_tcp(struct radius_client_data *radius,
+ int sock, RadiusType msg_type)
+{
+ wpa_printf(MSG_DEBUG, "RADIUS: Closing TCP connection (sock %d)",
+ sock);
+ if (msg_type == RADIUS_ACCT) {
+ radius->acct_tls_ready = false;
+ radius_close_acct_socket(radius);
+ } else {
+ radius->auth_tls_ready = false;
+ radius_close_auth_socket(radius);
+ }
+}
+
+
+static void
+radius_client_process_tls_handshake(struct radius_client_data *radius,
+ int sock, RadiusType msg_type,
+ u8 *buf, size_t len)
+{
+ struct wpabuf *in, *out = NULL, *appl;
+ struct tls_connection *conn;
+ int res;
+ bool ready = false;
+
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Process %zu bytes of received TLS handshake message",
+ len);
+
+ if (msg_type == RADIUS_ACCT)
+ conn = radius->acct_tls_conn;
+ else
+ conn = radius->auth_tls_conn;
+
+ in = wpabuf_alloc_copy(buf, len);
+ if (!in)
+ return;
+
+ appl = NULL;
+ out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
+ wpabuf_free(in);
+ if (!out) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Could not generate TLS handshake data");
+ goto fail;
+ }
+
+ if (tls_connection_get_failed(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
+ goto fail;
+ }
+
+ if (tls_connection_established(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TLS connection established (sock=%d)",
+ sock);
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_ready = true;
+ else
+ radius->auth_tls_ready = true;
+ ready = true;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
+ wpabuf_len(out));
+ res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res != wpabuf_len(out)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
+ res);
+ goto fail;
+ }
+ wpabuf_free(out);
+
+ if (ready) {
+ struct radius_msg_list *entry, *prev, *tmp;
+ struct os_reltime now;
+
+ /* Send all pending message of matching type since the TLS
+ * tunnel has now been established. */
+
+ os_get_reltime(&now);
+
+ entry = radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (entry->msg_type != msg_type) {
+ prev = entry;
+ entry = entry->next;
+ continue;
+ }
+
+ if (radius_client_retransmit(radius, entry, now.sec)) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+
+ return;
+
+fail:
+ wpabuf_free(out);
+ tls_connection_deinit(radius->tls_ctx, conn);
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_conn = NULL;
+ else
+ radius->auth_tls_conn = NULL;
+ radius_client_close_tcp(radius, sock, msg_type);
+}
+
+#endif /* CONFIG_RADIUS_TLS */
+
+
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_client_data *radius = eloop_ctx;
@@ -828,12 +1103,28 @@
struct os_reltime now;
struct hostapd_radius_server *rconf;
int invalid_authenticator = 0;
+#ifdef CONFIG_RADIUS_TLS
+ struct tls_connection *conn = NULL;
+ bool tls, tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT) {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->acct_tls)
+ conn = radius->acct_tls_conn;
+ tls = radius->acct_tls;
+ tls_ready = radius->acct_tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
handlers = radius->acct_handlers;
num_handlers = radius->num_acct_handlers;
rconf = conf->acct_server;
} else {
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->auth_tls)
+ conn = radius->auth_tls_conn;
+ tls = radius->auth_tls;
+ tls_ready = radius->auth_tls_ready;
+#endif /* CONFIG_RADIUS_TLS */
handlers = radius->auth_handlers;
num_handlers = radius->num_auth_handlers;
rconf = conf->auth_server;
@@ -849,6 +1140,52 @@
wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno));
return;
}
+#ifdef CONFIG_RADIUS_TLS
+ if (tls && len == 0) {
+ wpa_printf(MSG_DEBUG, "RADIUS: No TCP data available");
+ goto close_tcp;
+ }
+
+ if (tls && !tls_ready) {
+ radius_client_process_tls_handshake(radius, sock, msg_type,
+ buf, len);
+ return;
+ }
+
+ if (conn) {
+ struct wpabuf *out, *in;
+
+ in = wpabuf_alloc_copy(buf, len);
+ if (!in)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Process %d bytes of encrypted TLS data",
+ len);
+ out = tls_connection_decrypt(radius->tls_ctx, conn, in);
+ wpabuf_free(in);
+ if (!out) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to decrypt TLS data");
+ goto close_tcp;
+ }
+ if (wpabuf_len(out) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Full message not yet received - continue waiting for additional TLS data");
+ wpabuf_free(out);
+ return;
+ }
+ if (wpabuf_len(out) > RADIUS_MAX_MSG_LEN) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Too long RADIUS message from TLS: %zu",
+ wpabuf_len(out));
+ wpabuf_free(out);
+ goto close_tcp;
+ }
+ os_memcpy(buf, wpabuf_head(out), wpabuf_len(out));
+ len = wpabuf_len(out);
+ wpabuf_free(out);
+ }
+#endif /* CONFIG_RADIUS_TLS */
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
@@ -964,9 +1301,121 @@
fail:
radius_msg_free(msg);
+ return;
+
+#ifdef CONFIG_RADIUS_TLS
+close_tcp:
+ radius_client_close_tcp(radius, sock, msg_type);
+#endif /* CONFIG_RADIUS_TLS */
}
+#ifdef CONFIG_RADIUS_TLS
+static void radius_client_write_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct radius_client_data *radius = eloop_ctx;
+ RadiusType msg_type = (uintptr_t) sock_ctx;
+ struct tls_connection *conn = NULL;
+ struct wpabuf *in, *out = NULL, *appl;
+ int res = -1;
+ struct tls_connection_params params;
+ struct hostapd_radius_server *server;
+
+ wpa_printf(MSG_DEBUG, "RADIUS: TCP connection established - start TLS handshake (sock=%d)",
+ sock);
+
+ if (msg_type == RADIUS_ACCT) {
+ eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
+ eloop_register_read_sock(sock, radius_client_receive, radius,
+ (void *) RADIUS_ACCT);
+ if (radius->acct_tls_conn) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Deinit previously used TLS connection");
+ tls_connection_deinit(radius->tls_ctx,
+ radius->acct_tls_conn);
+ radius->acct_tls_conn = NULL;
+ }
+ server = radius->conf->acct_server;
+ } else {
+ eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
+ eloop_register_read_sock(sock, radius_client_receive, radius,
+ (void *) RADIUS_AUTH);
+ if (radius->auth_tls_conn) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Deinit previously used TLS connection");
+ tls_connection_deinit(radius->tls_ctx,
+ radius->auth_tls_conn);
+ radius->auth_tls_conn = NULL;
+ }
+ server = radius->conf->auth_server;
+ }
+
+ if (!server)
+ goto fail;
+
+ conn = tls_connection_init(radius->tls_ctx);
+ if (!conn) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to initiate TLS connection");
+ goto fail;
+ }
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.ca_cert = server->ca_cert;
+ params.client_cert = server->client_cert;
+ params.private_key = server->private_key;
+ params.private_key_passwd = server->private_key_passwd;
+ params.flags = TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1;
+ if (tls_connection_set_params(radius->tls_ctx, conn, ¶ms)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Failed to set TLS connection parameters");
+ goto fail;
+ }
+
+ in = NULL;
+ appl = NULL;
+ out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
+ if (!out) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: Could not generate TLS handshake data");
+ goto fail;
+ }
+
+ if (tls_connection_get_failed(radius->tls_ctx, conn)) {
+ wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
+ wpabuf_len(out));
+ res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
+ goto fail;
+ }
+ if ((size_t) res != wpabuf_len(out)) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
+ res);
+ goto fail;
+ }
+ wpabuf_free(out);
+
+ if (msg_type == RADIUS_ACCT)
+ radius->acct_tls_conn = conn;
+ else
+ radius->auth_tls_conn = conn;
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "RADIUS: Failed to perform TLS handshake");
+ tls_connection_deinit(radius->tls_ctx, conn);
+ wpabuf_free(out);
+ radius_client_close_tcp(radius, sock, msg_type);
+}
+#endif /* CONFIG_RADIUS_TLS */
+
+
/**
* radius_client_get_id - Get an identifier for a new RADIUS message
* @radius: RADIUS client context from radius_client_init()
@@ -1071,7 +1520,7 @@
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
- int sock, int sock6, int auth)
+ int auth)
{
struct sockaddr_in serv, claddr;
#ifdef CONFIG_IPV6
@@ -1083,9 +1532,17 @@
int sel_sock;
struct radius_msg_list *entry;
struct hostapd_radius_servers *conf = radius->conf;
- struct sockaddr_in disconnect_addr = {
- .sin_family = AF_UNSPEC,
- };
+ int type = SOCK_DGRAM;
+ bool tls = nserv->tls;
+
+ if (tls) {
+#ifdef CONFIG_RADIUS_TLS
+ type = SOCK_STREAM;
+#else /* CONFIG_RADIUS_TLS */
+ wpa_printf(MSG_ERROR, "RADIUS: TLS not supported");
+ return -1;
+#endif /* CONFIG_RADIUS_TLS */
+ }
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -1144,7 +1601,9 @@
serv.sin_port = htons(nserv->port);
addr = (struct sockaddr *) &serv;
addrlen = sizeof(serv);
- sel_sock = sock;
+ sel_sock = socket(PF_INET, type, 0);
+ if (sel_sock >= 0)
+ radius_client_disable_pmtu_discovery(sel_sock);
break;
#ifdef CONFIG_IPV6
case AF_INET6:
@@ -1155,7 +1614,7 @@
serv6.sin6_port = htons(nserv->port);
addr = (struct sockaddr *) &serv6;
addrlen = sizeof(serv6);
- sel_sock = sock6;
+ sel_sock = socket(PF_INET6, type, 0);
break;
#endif /* CONFIG_IPV6 */
default:
@@ -1164,15 +1623,19 @@
if (sel_sock < 0) {
wpa_printf(MSG_INFO,
- "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
- nserv->addr.af, sock, sock6, auth);
+ "RADIUS: Failed to open server socket (af=%d auth=%d)",
+ nserv->addr.af, auth);
return -1;
}
- /* Force a reconnect by disconnecting the socket first */
- if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
- sizeof(disconnect_addr)) < 0)
- wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+#ifdef CONFIG_RADIUS_TLS
+ if (tls && fcntl(sel_sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "RADIUS: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ close(sel_sock);
+ return -1;
+ }
+#endif /* CONFIG_RADIUS_TLS */
#ifdef __linux__
if (conf->force_client_dev && conf->force_client_dev[0]) {
@@ -1214,19 +1677,29 @@
break;
#endif /* CONFIG_IPV6 */
default:
+ close(sel_sock);
return -1;
}
if (bind(sel_sock, cl_addr, claddrlen) < 0) {
wpa_printf(MSG_INFO, "bind[radius]: %s",
strerror(errno));
- return -1;
+ close(sel_sock);
+ return -2;
}
}
if (connect(sel_sock, addr, addrlen) < 0) {
- wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
- return -1;
+ if (nserv->tls && errno == EINPROGRESS) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS: TCP connection establishment in progress (sock %d)",
+ sel_sock);
+ } else {
+ wpa_printf(MSG_INFO, "connect[radius]: %s",
+ strerror(errno));
+ close(sel_sock);
+ return -2;
+ }
}
#ifndef CONFIG_NATIVE_WINDOWS
@@ -1256,10 +1729,34 @@
}
#endif /* CONFIG_NATIVE_WINDOWS */
- if (auth)
+ if (auth) {
+ radius_close_auth_socket(radius);
radius->auth_sock = sel_sock;
- else
+ } else {
+ radius_close_acct_socket(radius);
radius->acct_sock = sel_sock;
+ }
+
+ if (!tls)
+ eloop_register_read_sock(sel_sock, radius_client_receive,
+ radius,
+ auth ? (void *) RADIUS_AUTH :
+ (void *) RADIUS_ACCT);
+#ifdef CONFIG_RADIUS_TLS
+ if (tls)
+ eloop_register_sock(sel_sock, EVENT_TYPE_WRITE,
+ radius_client_write_ready, radius,
+ auth ? (void *) RADIUS_AUTH :
+ (void *) RADIUS_ACCT);
+#endif /* CONFIG_RADIUS_TLS */
+
+ if (auth) {
+ radius->auth_tls = nserv->tls;
+ radius->auth_tls_ready = false;
+ } else {
+ radius->acct_tls = nserv->tls;
+ radius->acct_tls_ready = false;
+ }
return 0;
}
@@ -1276,12 +1773,10 @@
oserv = conf->auth_server;
conf->auth_server = conf->auth_servers;
if (radius_change_server(radius, conf->auth_server, oserv,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1) < 0) {
+ 1) < 0) {
conf->auth_server = oserv;
radius_change_server(radius, oserv, conf->auth_server,
- radius->auth_serv_sock,
- radius->auth_serv_sock6, 1);
+ 1);
}
}
@@ -1290,12 +1785,10 @@
oserv = conf->acct_server;
conf->acct_server = conf->acct_servers;
if (radius_change_server(radius, conf->acct_server, oserv,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0) < 0) {
+ 0) < 0) {
conf->acct_server = oserv;
radius_change_server(radius, oserv, conf->acct_server,
- radius->acct_serv_sock,
- radius->acct_serv_sock6, 0);
+ 0);
}
}
@@ -1306,172 +1799,29 @@
}
-static int radius_client_disable_pmtu_discovery(int s)
-{
- int r = -1;
-#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
- /* Turn off Path MTU discovery on IPv4/UDP sockets. */
- int action = IP_PMTUDISC_DONT;
- r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
- sizeof(action));
- if (r == -1)
- wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
- strerror(errno));
-#endif
- return r;
-}
-
-
-static void radius_close_auth_sockets(struct radius_client_data *radius)
-{
- radius->auth_sock = -1;
-
- if (radius->auth_serv_sock >= 0) {
- eloop_unregister_read_sock(radius->auth_serv_sock);
- close(radius->auth_serv_sock);
- radius->auth_serv_sock = -1;
- }
-#ifdef CONFIG_IPV6
- if (radius->auth_serv_sock6 >= 0) {
- eloop_unregister_read_sock(radius->auth_serv_sock6);
- close(radius->auth_serv_sock6);
- radius->auth_serv_sock6 = -1;
- }
-#endif /* CONFIG_IPV6 */
-}
-
-
-static void radius_close_acct_sockets(struct radius_client_data *radius)
-{
- radius->acct_sock = -1;
-
- if (radius->acct_serv_sock >= 0) {
- eloop_unregister_read_sock(radius->acct_serv_sock);
- close(radius->acct_serv_sock);
- radius->acct_serv_sock = -1;
- }
-#ifdef CONFIG_IPV6
- if (radius->acct_serv_sock6 >= 0) {
- eloop_unregister_read_sock(radius->acct_serv_sock6);
- close(radius->acct_serv_sock6);
- radius->acct_serv_sock6 = -1;
- }
-#endif /* CONFIG_IPV6 */
-}
-
-
static int radius_client_init_auth(struct radius_client_data *radius)
{
- struct hostapd_radius_servers *conf = radius->conf;
- int ok = 0;
-
- radius_close_auth_sockets(radius);
-
- radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (radius->auth_serv_sock < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- else {
- radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
- ok++;
- }
-
-#ifdef CONFIG_IPV6
- radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
- if (radius->auth_serv_sock6 < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
- strerror(errno));
- else
- ok++;
-#endif /* CONFIG_IPV6 */
-
- if (ok == 0)
- return -1;
-
- radius_change_server(radius, conf->auth_server, NULL,
- radius->auth_serv_sock, radius->auth_serv_sock6,
- 1);
-
- if (radius->auth_serv_sock >= 0 &&
- eloop_register_read_sock(radius->auth_serv_sock,
- radius_client_receive, radius,
- (void *) RADIUS_AUTH)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
- radius_close_auth_sockets(radius);
- return -1;
- }
-
-#ifdef CONFIG_IPV6
- if (radius->auth_serv_sock6 >= 0 &&
- eloop_register_read_sock(radius->auth_serv_sock6,
- radius_client_receive, radius,
- (void *) RADIUS_AUTH)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
- radius_close_auth_sockets(radius);
- return -1;
- }
-#endif /* CONFIG_IPV6 */
-
- return 0;
+ radius_close_auth_socket(radius);
+ return radius_change_server(radius, radius->conf->auth_server, NULL, 1);
}
static int radius_client_init_acct(struct radius_client_data *radius)
{
- struct hostapd_radius_servers *conf = radius->conf;
- int ok = 0;
-
- radius_close_acct_sockets(radius);
-
- radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (radius->acct_serv_sock < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
- strerror(errno));
- else {
- radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
- ok++;
- }
-
-#ifdef CONFIG_IPV6
- radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
- if (radius->acct_serv_sock6 < 0)
- wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
- strerror(errno));
- else
- ok++;
-#endif /* CONFIG_IPV6 */
-
- if (ok == 0)
- return -1;
-
- radius_change_server(radius, conf->acct_server, NULL,
- radius->acct_serv_sock, radius->acct_serv_sock6,
- 0);
-
- if (radius->acct_serv_sock >= 0 &&
- eloop_register_read_sock(radius->acct_serv_sock,
- radius_client_receive, radius,
- (void *) RADIUS_ACCT)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
- radius_close_acct_sockets(radius);
- return -1;
- }
-
-#ifdef CONFIG_IPV6
- if (radius->acct_serv_sock6 >= 0 &&
- eloop_register_read_sock(radius->acct_serv_sock6,
- radius_client_receive, radius,
- (void *) RADIUS_ACCT)) {
- wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
- radius_close_acct_sockets(radius);
- return -1;
- }
-#endif /* CONFIG_IPV6 */
-
- return 0;
+ radius_close_acct_socket(radius);
+ return radius_change_server(radius, radius->conf->acct_server, NULL, 0);
}
+#ifdef CONFIG_RADIUS_TLS
+static void radius_tls_event_cb(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ wpa_printf(MSG_DEBUG, "RADIUS: TLS event %d", ev);
+}
+#endif /* CONFIG_RADIUS_TLS */
+
+
/**
* radius_client_init - Initialize RADIUS client
* @ctx: Callback context to be used in hostapd_logger() calls
@@ -1493,16 +1843,14 @@
radius->ctx = ctx;
radius->conf = conf;
- radius->auth_serv_sock = radius->acct_serv_sock =
- radius->auth_serv_sock6 = radius->acct_serv_sock6 =
- radius->auth_sock = radius->acct_sock = -1;
+ radius->auth_sock = radius->acct_sock = -1;
- if (conf->auth_server && radius_client_init_auth(radius)) {
+ if (conf->auth_server && radius_client_init_auth(radius) == -1) {
radius_client_deinit(radius);
return NULL;
}
- if (conf->acct_server && radius_client_init_acct(radius)) {
+ if (conf->acct_server && radius_client_init_acct(radius) == -1) {
radius_client_deinit(radius);
return NULL;
}
@@ -1512,6 +1860,22 @@
radius_retry_primary_timer, radius,
NULL);
+#ifdef CONFIG_RADIUS_TLS
+ if ((conf->auth_server && conf->auth_server->tls) ||
+ (conf->acct_server && conf->acct_server->tls)) {
+ struct tls_config tls_conf;
+
+ os_memset(&tls_conf, 0, sizeof(tls_conf));
+ tls_conf.event_cb = radius_tls_event_cb;
+ radius->tls_ctx = tls_init(&tls_conf);
+ if (!radius->tls_ctx) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_RADIUS_TLS */
+
+
return radius;
}
@@ -1525,14 +1889,21 @@
if (!radius)
return;
- radius_close_auth_sockets(radius);
- radius_close_acct_sockets(radius);
+ radius_close_auth_socket(radius);
+ radius_close_acct_socket(radius);
eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
radius_client_flush(radius, 0);
os_free(radius->auth_handlers);
os_free(radius->acct_handlers);
+#ifdef CONFIG_RADIUS_TLS
+ if (radius->tls_ctx) {
+ tls_connection_deinit(radius->tls_ctx, radius->auth_tls_conn);
+ tls_connection_deinit(radius->tls_ctx, radius->acct_tls_conn);
+ tls_deinit(radius->tls_ctx);
+ }
+#endif /* CONFIG_RADIUS_TLS */
os_free(radius);
}
@@ -1556,7 +1927,7 @@
entry = radius->msgs;
while (entry) {
if (entry->msg_type == RADIUS_AUTH &&
- os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ ether_addr_equal(entry->addr, addr)) {
hostapd_logger(radius->ctx, addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 687cd81..db40637 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -1,6 +1,6 @@
/*
* RADIUS client
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -36,6 +36,11 @@
int port;
/**
+ * tls - Whether to use RADIUS/TLS instead of RADIUS/UDP
+ */
+ bool tls;
+
+ /**
* shared_secret - Shared secret for authenticating RADIUS messages
*/
u8 *shared_secret;
@@ -45,6 +50,26 @@
*/
size_t shared_secret_len;
+ /**
+ * ca_cert - Path to trusted CA certificate(s) for RADIUS/TLS
+ */
+ char *ca_cert;
+
+ /**
+ * client_cert - Path to client certificate for RADIUS/TLS
+ */
+ char *client_cert;
+
+ /**
+ * private_key - Path to clienbt private key for RADIUS/TLS
+ */
+ char *private_key;
+
+ /**
+ * private_key_passwd - Password for the private key for RADIUS/TLS
+ */
+ char *private_key_passwd;
+
/* Dynamic (not from configuration file) MIB data */
/**
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index aaa3fc2..8d7c9b4 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -177,6 +177,11 @@
if (reply == NULL)
return NULL;
+ if (!radius_msg_add_msg_auth(reply)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
if (error) {
if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
error)) {
@@ -368,6 +373,11 @@
if (!reply)
return NULL;
+ if (!radius_msg_add_msg_auth(reply)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
if (error &&
!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
radius_msg_free(reply);
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index e02c215..fa36915 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -920,6 +920,11 @@
return NULL;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+
sess_id = htonl(sess->sess_id);
if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
!radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
@@ -1204,6 +1209,11 @@
return NULL;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
radius_msg_free(msg);
@@ -1253,6 +1263,11 @@
return -1;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return -1;
+ }
+
os_memset(&eapfail, 0, sizeof(eapfail));
eapfail.code = EAP_CODE_FAILURE;
eapfail.identifier = 0;
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index b2c4809..cb7f7e4 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -255,13 +255,13 @@
if (pmkid) {
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
} else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
- if (kck) {
+ if (kck && kck_len > 0) {
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
os_memcpy(entry->kck, kck, kck_len);
entry->kck_len = kck_len;
}
} else if (wpa_key_mgmt_suite_b(akmp)) {
- if (kck) {
+ if (kck && kck_len > 0) {
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
os_memcpy(entry->kck, kck, kck_len);
entry->kck_len = kck_len;
@@ -302,8 +302,8 @@
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
- if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0 &&
- os_memcmp(entry->spa, pos->spa, ETH_ALEN) == 0) {
+ if (ether_addr_equal(entry->aa, pos->aa) &&
+ ether_addr_equal(entry->spa, pos->spa)) {
if (pos->pmk_len == entry->pmk_len &&
os_memcmp_const(pos->pmk, entry->pmk,
entry->pmk_len) == 0 &&
@@ -489,8 +489,8 @@
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
- if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
- (!spa || os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ if ((aa == NULL || ether_addr_equal(entry->aa, aa)) &&
+ (!spa || ether_addr_equal(entry->spa, spa)) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
(!akmp || akmp == entry->akmp) &&
@@ -857,4 +857,99 @@
}
}
+#else /* IEEE8021X_EAPOL */
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, enum pmksa_free_reason reason),
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
+ void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
+ void *ctx, struct wpa_sm *sm)
+{
+ return (void *) -1;
+}
+
+
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid, const void *network_ctx, int akmp)
+{
+ return NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ return NULL;
+}
+
+
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+ return NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ return NULL;
+}
+
+
+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)
+{
+ return NULL;
+}
+
+
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid,
+ void *network_ctx, int try_opportunistic,
+ const u8 *fils_cache_id, int akmp, bool associated)
+{
+ return -1;
+}
+
+
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *pmk, size_t pmk_len, bool external_only)
+{
+}
+
+
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+}
+
+
+void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
#endif /* IEEE8021X_EAPOL */
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6ba48f7..b1203ad 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -64,8 +64,6 @@
PMKSA_EXPIRE,
};
-#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
-
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason),
@@ -105,95 +103,4 @@
struct rsn_pmksa_cache_entry *entry);
void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
-#else /* IEEE8021X_EAPOL */
-
-static inline struct rsn_pmksa_cache *
-pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx, enum pmksa_free_reason reason),
- bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx),
- void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx),
- void *ctx, struct wpa_sm *sm)
-{
- return (void *) -1;
-}
-
-static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
-{
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
- const u8 *pmkid, const void *network_ctx, int akmp)
-{
- return NULL;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get_current(struct wpa_sm *sm)
-{
- return NULL;
-}
-
-static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
- size_t len)
-{
- return -1;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
-{
- return NULL;
-}
-
-static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
- struct rsn_pmksa_cache_entry *entry)
-{
- return NULL;
-}
-
-static inline 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)
-{
- return NULL;
-}
-
-static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
-{
-}
-
-static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
- const u8 *bssid,
- void *network_ctx,
- int try_opportunistic,
- const u8 *fils_cache_id,
- int akmp, bool associated)
-{
- return -1;
-}
-
-static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
- void *network_ctx,
- const u8 *pmk, size_t pmk_len,
- bool external_only)
-{
-}
-
-static inline void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
- struct rsn_pmksa_cache_entry *entry)
-{
-}
-
-static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-}
-
-#endif /* IEEE8021X_EAPOL */
-
#endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 1a28884..026a98d 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -69,7 +69,7 @@
if (sm->preauth_eapol == NULL ||
is_zero_ether_addr(sm->preauth_bssid) ||
- os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sm->preauth_bssid, src_addr)) {
wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
"unexpected source " MACSTR " - dropped",
MAC2STR(src_addr));
@@ -333,7 +333,7 @@
struct rsn_pmksa_cache_entry *p = NULL;
p = pmksa_cache_get(sm->pmksa, candidate->bssid, sm->own_addr,
NULL, NULL, 0);
- if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+ if (!ether_addr_equal(sm->bssid, candidate->bssid) &&
(p == NULL || p->opportunistic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
"candidate " MACSTR
@@ -395,7 +395,7 @@
cand = NULL;
dl_list_for_each(pos, &sm->pmksa_candidates,
struct rsn_pmksa_candidate, list) {
- if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
+ if (ether_addr_equal(pos->bssid, bssid)) {
cand = pos;
break;
}
@@ -487,7 +487,7 @@
os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
return; /* Not for the current SSID */
- if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(bssid, sm->bssid))
return; /* Ignore current AP */
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index e6f5877..65960b7 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -161,6 +161,8 @@
int chan_switch_enabled;
int mld_link_id;
+ bool disc_resp_rcvd;
+ bool setup_req_rcvd;
};
@@ -294,7 +296,7 @@
return 0; /* No retries */
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, dest))
break;
}
@@ -791,7 +793,7 @@
/* Find the node and free from the list */
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -880,7 +882,7 @@
return -1;
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -913,7 +915,7 @@
struct wpa_tdls_peer *peer;
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -949,7 +951,7 @@
return "disabled";
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -976,7 +978,7 @@
/* Find the node and free from the list */
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, src_addr))
break;
}
@@ -1072,7 +1074,7 @@
if (existing)
*existing = 0;
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(peer->addr, addr)) {
if (existing)
*existing = 1;
return peer; /* re-use existing entry */
@@ -1564,15 +1566,14 @@
*link_id = -1;
if (!sm->mlo.valid_links) {
- if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(sm->bssid, lnkid->bssid))
return false;
} else {
int i;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if ((sm->mlo.valid_links & BIT(i)) &&
- os_memcmp(lnkid->bssid, sm->mlo.links[i].bssid,
- ETH_ALEN) == 0) {
+ for_each_link(sm->mlo.valid_links, i) {
+ if (ether_addr_equal(lnkid->bssid,
+ sm->mlo.links[i].bssid)) {
*link_id = i;
break;
}
@@ -2371,7 +2372,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, src_addr))
break;
}
if (peer == NULL) {
@@ -2460,8 +2461,8 @@
kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(sm->bssid, wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sm->bssid,
+ wpa_tdls_get_link_bssid(sm, peer->mld_link_id))) {
wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
status = WLAN_STATUS_NOT_IN_SAME_BSS;
goto error;
@@ -2668,7 +2669,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, src_addr))
break;
}
if (peer == NULL) {
@@ -2713,8 +2714,8 @@
(u8 *) kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
- if (os_memcmp(wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
- lnkid->bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(wpa_tdls_get_link_bssid(sm, peer->mld_link_id),
+ lnkid->bssid)) {
wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
goto error;
}
@@ -2868,6 +2869,12 @@
return 0;
}
+ if (sm->mlo.valid_links && !peer->disc_resp_rcvd) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: MLO STA connection - defer the setup request since Discovery Resp not yet received");
+ peer->setup_req_rcvd = true;
+ return 0;
+ }
peer->initiator = 1;
/* add the peer to the driver as a "setup in progress" peer */
@@ -2899,7 +2906,7 @@
return;
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -2937,7 +2944,7 @@
return;
}
- if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(src_addr, sm->own_addr)) {
wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
return;
}
@@ -3187,7 +3194,7 @@
const u8 *buf, size_t len)
{
struct ieee802_11_elems elems;
- struct wpa_tdls_lnkid lnkid;
+ const struct wpa_tdls_lnkid *lnkid;
struct wpa_tdls_peer *peer;
size_t min_req_len = 1 /* Dialog Token */ + 2 /* Capability */ +
sizeof(struct wpa_tdls_lnkid);
@@ -3217,12 +3224,12 @@
return -1;
}
- os_memcpy(&lnkid.bssid[0], elems.link_id, sizeof(lnkid) - 2);
+ lnkid = (const struct wpa_tdls_lnkid *) (elems.link_id - 2);
- if (!wpa_tdls_is_lnkid_bss_valid(sm, &lnkid, &link_id)) {
+ if (!wpa_tdls_is_lnkid_bss_valid(sm, lnkid, &link_id)) {
wpa_printf(MSG_DEBUG,
"TDLS: Discovery Response from different BSS "
- MACSTR, MAC2STR(lnkid.bssid));
+ MACSTR, MAC2STR(lnkid->bssid));
return -1;
}
@@ -3234,7 +3241,14 @@
peer->mld_link_id = link_id;
wpa_printf(MSG_DEBUG, "TDLS: Link identifier BSS: " MACSTR
- " , link id: %u", MAC2STR(lnkid.bssid), link_id);
+ " , link id: %u", MAC2STR(lnkid->bssid), link_id);
+
+ peer->disc_resp_rcvd = true;
+ if (peer->setup_req_rcvd) {
+ peer->setup_req_rcvd = false;
+ wpa_printf(MSG_DEBUG, "TDLS: Process the deferred TDLS start");
+ return wpa_tdls_start(sm, addr);
+ }
return 0;
}
@@ -3263,7 +3277,7 @@
}
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
@@ -3297,7 +3311,7 @@
return -1;
for (peer = sm->tdls; peer; peer = peer->next) {
- if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(peer->addr, addr))
break;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 9f49cf9..935a1aa 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -233,6 +233,12 @@
return;
}
+ if (!sm->ptk_set) {
+ wpa_printf(MSG_INFO,
+ "WPA: No PTK derived yet - cannot send EAPOL-Key Request");
+ return;
+ }
+
if (wpa_use_akm_defined(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
@@ -254,10 +260,11 @@
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
- if (sm->ptk_set)
- key_info |= WPA_KEY_INFO_SECURE;
- if (sm->ptk_set && mic_len)
+ key_info |= WPA_KEY_INFO_SECURE;
+ if (mic_len)
key_info |= WPA_KEY_INFO_MIC;
+ else
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
if (error)
key_info |= WPA_KEY_INFO_ERROR;
if (pairwise)
@@ -522,11 +529,14 @@
const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ptk *ptk)
{
- size_t mic_len, hdrlen, rlen;
+ size_t mic_len, hdrlen, rlen, extra_len = 0;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
u8 *rsn_ie_buf = NULL;
u16 key_info;
+#ifdef CONFIG_TESTING_OPTIONS
+ size_t pad_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_ie == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
@@ -550,7 +560,7 @@
return -1;
os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len,
- sm->pmk_r1_name);
+ sm->pmk_r1_name, !sm->ft_prepend_pmkid);
if (res < 0) {
os_free(rsn_ie_buf);
return -1;
@@ -574,10 +584,21 @@
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->test_eapol_m2_elems)
+ extra_len = wpabuf_len(sm->test_eapol_m2_elems);
+ if (sm->encrypt_eapol_m2) {
+ pad_len = (wpa_ie_len + extra_len) % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ extra_len += pad_len + 8;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
- NULL, hdrlen + wpa_ie_len,
+ NULL, hdrlen + wpa_ie_len + extra_len,
&rlen, (void *) &reply);
if (rbuf == NULL) {
os_free(rsn_ie_buf);
@@ -594,6 +615,10 @@
key_info |= WPA_KEY_INFO_MIC;
else
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->encrypt_eapol_m2)
+ 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)
WPA_PUT_BE16(reply->key_length, 0);
@@ -605,9 +630,48 @@
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
- WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
+ /* Key Data Length */
+ 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);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->test_eapol_m2_elems) {
+ os_memcpy(key_mic + mic_len + 2 + wpa_ie_len,
+ wpabuf_head(sm->test_eapol_m2_elems),
+ wpabuf_len(sm->test_eapol_m2_elems));
+ }
+
+ if (sm->encrypt_eapol_m2) {
+ u8 *plain;
+ size_t plain_len;
+
+ if (sm->test_eapol_m2_elems)
+ extra_len = wpabuf_len(sm->test_eapol_m2_elems);
+ else
+ extra_len = 0;
+ plain_len = wpa_ie_len + extra_len + pad_len;
+ plain = os_memdup(key_mic + mic_len + 2, plain_len);
+ if (!plain) {
+ os_free(rbuf);
+ return -1;
+ }
+ if (pad_len)
+ plain[plain_len - pad_len] = 0xdd;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+ ptk->kek, ptk->kek_len);
+ if (aes_wrap(ptk->kek, ptk->kek_len, plain_len / 8, plain,
+ key_mic + mic_len + 2)) {
+ os_free(plain);
+ os_free(rbuf);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Encrypted Key Data from AES-WRAP",
+ key_mic + mic_len + 2, plain_len + 8);
+ os_free(plain);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
@@ -737,8 +801,8 @@
int i;
unsigned int num_links = 0;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (sm->mlo.assoc_link_id != i && (sm->mlo.req_links & BIT(i)))
+ for_each_link(sm->mlo.req_links, i) {
+ if (sm->mlo.assoc_link_id != i)
num_links++;
}
@@ -751,8 +815,8 @@
int i;
u8 hdr[1 + ETH_ALEN];
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (sm->mlo.assoc_link_id == i || !(sm->mlo.req_links & BIT(i)))
+ for_each_link(sm->mlo.req_links, i) {
+ if (sm->mlo.assoc_link_id == i)
continue;
wpa_printf(MSG_DEBUG,
@@ -769,7 +833,7 @@
static bool is_valid_ap_mld_mac_kde(struct wpa_sm *sm, const u8 *mac_kde)
{
return mac_kde &&
- os_memcmp(mac_kde, sm->mlo.ap_mld_addr, ETH_ALEN) == 0;
+ ether_addr_equal(mac_kde, sm->mlo.ap_mld_addr);
}
@@ -1335,6 +1399,7 @@
const u8 *key_rsc, int wnm_sleep)
{
const u8 *gtk = gd->gtk;
+ u8 gtk_buf[32];
/* Detect possible key reinstallation */
if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len &&
@@ -1357,14 +1422,23 @@
link_id, gd->keyidx, gd->tx, gd->gtk_len);
wpa_hexdump_link(MSG_DEBUG, link_id, "RSN: RSC",
key_rsc, gd->key_rsc_len);
+ if (sm->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ os_memcpy(gtk_buf, gd->gtk, 16);
+ os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+ os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+ gtk = gtk_buf;
+ }
if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk,
gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)",
link_id, gd->alg, gd->gtk_len, gd->keyidx);
+ forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
+ forced_memzero(gtk_buf, sizeof(gtk_buf));
if (wnm_sleep) {
sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len;
@@ -1487,10 +1561,7 @@
{
u8 i;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
if (!ie->mlo_gtk[i]) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"MLO RSN: GTK not found for link ID %u", i);
@@ -1839,10 +1910,7 @@
sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
return 0;
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
if (_mlo_ieee80211w_set_keys(sm, i, ie))
return -1;
}
@@ -2155,7 +2223,10 @@
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
u8 *kde = NULL;
- size_t kde_len = 0;
+ size_t kde_len = 0, extra_len = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ size_t pad_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
if (sm->mlo.valid_links) {
u8 *pos;
@@ -2172,10 +2243,22 @@
kde_len = pos - kde;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->test_eapol_m4_elems)
+ extra_len = wpabuf_len(sm->test_eapol_m4_elems);
+ if (sm->encrypt_eapol_m4) {
+ pad_len = (kde_len + extra_len) % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ extra_len += pad_len + 8;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- hdrlen + kde_len, &rlen, (void *) &reply);
+ hdrlen + kde_len + extra_len, &rlen,
+ (void *) &reply);
if (!rbuf) {
os_free(kde);
return -1;
@@ -2190,6 +2273,10 @@
key_info |= WPA_KEY_INFO_MIC;
else
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->encrypt_eapol_m4)
+ 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)
WPA_PUT_BE16(reply->key_length, 0);
@@ -2199,12 +2286,52 @@
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
- WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data length */
+ /* Key Data length */
+ WPA_PUT_BE16(key_mic + mic_len, kde_len + extra_len);
if (kde) {
os_memcpy(key_mic + mic_len + 2, kde, kde_len); /* Key Data */
os_free(kde);
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->test_eapol_m4_elems) {
+ os_memcpy(key_mic + mic_len + 2 + kde_len,
+ wpabuf_head(sm->test_eapol_m4_elems),
+ wpabuf_len(sm->test_eapol_m4_elems));
+ }
+
+ if (sm->encrypt_eapol_m4) {
+ u8 *plain;
+ size_t plain_len;
+
+ if (sm->test_eapol_m4_elems)
+ extra_len = wpabuf_len(sm->test_eapol_m4_elems);
+ else
+ extra_len = 0;
+ plain_len = kde_len + extra_len + pad_len;
+ plain = os_memdup(key_mic + mic_len + 2, plain_len);
+ if (!plain) {
+ os_free(rbuf);
+ return -1;
+ }
+ if (pad_len)
+ plain[plain_len - pad_len] = 0xdd;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+ ptk->kek, ptk->kek_len);
+ if (aes_wrap(ptk->kek, ptk->kek_len, plain_len / 8, plain,
+ key_mic + mic_len + 2)) {
+ os_free(plain);
+ os_free(rbuf);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Encrypted Key Data from AES-WRAP",
+ key_mic + mic_len + 2, plain_len + 8);
+ os_free(plain);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key 4/4");
return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
key_mic);
@@ -2226,9 +2353,8 @@
return -1;
}
- if (os_memcmp(sm->mlo.links[link_id].bssid,
- &link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX],
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sm->mlo.links[link_id].bssid,
+ &link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX])) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: MLO Link %u MAC address (" MACSTR
") not matching association response (" MACSTR ")",
@@ -2319,7 +2445,7 @@
(unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN MLO: Invalid IGTK KDE length %lu for link ID %u",
- (unsigned long) ie->mlo_igtk_len, link_id);
+ (unsigned long) ie->mlo_igtk_len[link_id], link_id);
return -1;
}
@@ -2331,7 +2457,7 @@
(unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN MLO: Invalid BIGTK KDE length %lu for link ID %u",
- (unsigned long) ie->mlo_bigtk_len, link_id);
+ (unsigned long) ie->mlo_bigtk_len[link_id], link_id);
return -1;
}
@@ -2427,6 +2553,27 @@
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
+ if (sm->ssid_protection) {
+ if (!ie.ssid) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No SSID included in EAPOL-Key msg 3/4");
+ goto failed;
+ }
+
+ if (ie.ssid_len != sm->ssid_len ||
+ os_memcmp(ie.ssid, sm->ssid, sm->ssid_len) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: SSID mismatch in EAPOL-Key msg 3/4");
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: Received SSID",
+ ie.ssid, ie.ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: Expected SSID",
+ sm->ssid, sm->ssid_len);
+ goto failed;
+ }
+
+ wpa_sm_ssid_verified(sm);
+ }
+
if (mlo && !ie.valid_mlo_gtks) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
@@ -2810,10 +2957,7 @@
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"MLO RSN: Failed to configure MLO IGTK");
- for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
- if (!(sm->mlo.valid_links & BIT(i)))
- continue;
-
+ for_each_link(sm->mlo.valid_links, i) {
/*
* AP may send group keys for subset of the all links during
* rekey
@@ -4041,6 +4185,8 @@
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(sm->test_assoc_ie);
+ wpabuf_free(sm->test_eapol_m2_elems);
+ wpabuf_free(sm->test_eapol_m4_elems);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS_SK_PFS
crypto_ecdh_deinit(sm->fils_ecdh);
@@ -4111,7 +4257,7 @@
os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 0;
sm->renew_snonce = 1;
- if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(sm->preauth_bssid, bssid))
rsn_preauth_deinit(sm);
#ifdef CONFIG_IEEE80211R
@@ -4346,6 +4492,20 @@
}
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len)
+{
+ if (!sm)
+ return;
+
+ if (ssid) {
+ os_memcpy(sm->ssid, ssid, ssid_len);
+ sm->ssid_len = ssid_len;
+ } else {
+ sm->ssid_len = 0;
+ }
+}
+
+
int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
{
int i;
@@ -4391,12 +4551,27 @@
} else {
wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNE",
ie, len);
- sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
- if (!sm->mlo.links[i].ap_rsne) {
- sm->mlo.links[i].ap_rsne_len = 0;
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->mlo.links[i].ap_rsne = os_malloc(len - 4);
+ if (!sm->mlo.links[i].ap_rsne)
+ return -1;
+ sm->mlo.links[i].ap_rsne[0] = WLAN_EID_RSN;
+ sm->mlo.links[i].ap_rsne[1] = len - 2 - 4;
+ os_memcpy(&sm->mlo.links[i].ap_rsne[2],
+ ie + 2 + 4, len - 2 - 4);
+ sm->mlo.links[i].ap_rsne_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNE override to RSNE",
+ sm->mlo.links[i].ap_rsne,
+ sm->mlo.links[i].ap_rsne_len);
+ } else {
+ sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsne) {
+ sm->mlo.links[i].ap_rsne_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsne_len = len;
}
- sm->mlo.links[i].ap_rsne_len = len;
}
ie = mlo->links[i].ap_rsnxe;
@@ -4412,12 +4587,27 @@
} else {
wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXE", ie,
len);
- sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
- if (!sm->mlo.links[i].ap_rsnxe) {
- sm->mlo.links[i].ap_rsnxe_len = 0;
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->mlo.links[i].ap_rsnxe = os_malloc(len - 4);
+ if (!sm->mlo.links[i].ap_rsnxe)
+ return -1;
+ sm->mlo.links[i].ap_rsnxe[0] = WLAN_EID_RSNX;
+ sm->mlo.links[i].ap_rsnxe[1] = len - 2 - 4;
+ os_memcpy(&sm->mlo.links[i].ap_rsnxe[2],
+ ie + 2 + 4, len - 2 - 4);
+ sm->mlo.links[i].ap_rsnxe_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNXE override to RSNXE",
+ sm->mlo.links[i].ap_rsnxe,
+ sm->mlo.links[i].ap_rsnxe_len);
+ } else {
+ sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsnxe) {
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsnxe_len = len;
}
- sm->mlo.links[i].ap_rsnxe_len = len;
}
}
@@ -4557,6 +4747,12 @@
case WPA_PARAM_DISABLE_EAPOL_G2_TX:
sm->disable_eapol_g2_tx = value;
break;
+ case WPA_PARAM_ENCRYPT_EAPOL_M2:
+ sm->encrypt_eapol_m2 = value;
+ break;
+ case WPA_PARAM_ENCRYPT_EAPOL_M4:
+ sm->encrypt_eapol_m4 = value;
+ break;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_DPP2
case WPA_PARAM_DPP_PFS:
@@ -4566,6 +4762,12 @@
case WPA_PARAM_WMM_ENABLED:
sm->wmm_enabled = value;
break;
+ case WPA_PARAM_FT_PREPEND_PMKID:
+ sm->ft_prepend_pmkid = value;
+ break;
+ case WPA_PARAM_SSID_PROTECTION:
+ sm->ssid_protection = value;
+ break;
default:
break;
}
@@ -4837,6 +5039,14 @@
sm->assoc_rsnxe_len = len;
}
+ if (sm->ssid_protection &&
+ !ieee802_11_rsnx_capab(sm->assoc_rsnxe,
+ WLAN_RSNX_CAPAB_SSID_PROTECTION)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Disabling SSID protection based on own RSNXE update");
+ sm->ssid_protection = 0;
+ }
+
return 0;
}
@@ -4898,11 +5108,24 @@
sm->ap_rsn_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
- sm->ap_rsn_ie = os_memdup(ie, len);
- if (sm->ap_rsn_ie == NULL)
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->ap_rsn_ie = os_malloc(len - 4);
+ if (!sm->ap_rsn_ie)
+ return -1;
+ sm->ap_rsn_ie[0] = WLAN_EID_RSN;
+ sm->ap_rsn_ie[1] = len - 2 - 4;
+ os_memcpy(&sm->ap_rsn_ie[2], ie + 2 + 4, len - 2 - 4);
+ sm->ap_rsn_ie_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNE override to RSNE",
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+ } else {
+ sm->ap_rsn_ie = os_memdup(ie, len);
+ if (sm->ap_rsn_ie == NULL)
+ return -1;
- sm->ap_rsn_ie_len = len;
+ sm->ap_rsn_ie_len = len;
+ }
}
return 0;
@@ -4931,11 +5154,24 @@
sm->ap_rsnxe_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len);
- sm->ap_rsnxe = os_memdup(ie, len);
- if (!sm->ap_rsnxe)
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->ap_rsnxe = os_malloc(len - 4);
+ if (!sm->ap_rsnxe)
+ return -1;
+ sm->ap_rsnxe[0] = WLAN_EID_RSNX;
+ sm->ap_rsnxe[1] = len - 2 - 4;
+ os_memcpy(&sm->ap_rsnxe[2], ie + 2 + 4, len - 2 - 4);
+ sm->ap_rsnxe_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNXE override to RSNXE",
+ sm->ap_rsnxe, sm->ap_rsnxe_len);
+ } else {
+ sm->ap_rsnxe = os_memdup(ie, len);
+ if (!sm->ap_rsnxe)
+ return -1;
- sm->ap_rsnxe_len = len;
+ sm->ap_rsnxe_len = len;
+ }
}
return 0;
@@ -5247,6 +5483,20 @@
}
+void wpa_sm_set_test_eapol_m2_elems(struct wpa_sm *sm, struct wpabuf *buf)
+{
+ wpabuf_free(sm->test_eapol_m2_elems);
+ sm->test_eapol_m2_elems = buf;
+}
+
+
+void wpa_sm_set_test_eapol_m4_elems(struct wpa_sm *sm, struct wpabuf *buf)
+{
+ wpabuf_free(sm->test_eapol_m4_elems);
+ sm->test_eapol_m4_elems = buf;
+}
+
+
const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
{
return sm->anonce;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index d8d0a15..231e088 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -104,6 +104,7 @@
#endif /* CONFIG_PASN */
void (*notify_pmksa_cache_entry)(void *ctx,
struct rsn_pmksa_cache_entry *entry);
+ void (*ssid_verified)(void *ctx);
};
@@ -132,6 +133,10 @@
WPA_PARAM_OCI_FREQ_FT_ASSOC,
WPA_PARAM_OCI_FREQ_FILS_ASSOC,
WPA_PARAM_DISABLE_EAPOL_G2_TX,
+ WPA_PARAM_ENCRYPT_EAPOL_M2,
+ WPA_PARAM_ENCRYPT_EAPOL_M4,
+ WPA_PARAM_FT_PREPEND_PMKID,
+ WPA_PARAM_SSID_PROTECTION,
};
struct rsn_supp_config {
@@ -185,6 +190,7 @@
void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len);
void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
const char *bridge_ifname);
@@ -605,6 +611,8 @@
int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
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);
unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3d1dbc6..9a39749 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -607,7 +607,7 @@
goto fail;
}
- if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(target_ap, sm->target_ap)) {
wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
"with this Target AP - drop FT Action "
"Response");
@@ -1348,7 +1348,7 @@
size_t i;
for (i = 0; i < sm->n_pasn_r1kh; i++)
- if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(sm->pasn_r1kh[i].bssid, bssid))
return &sm->pasn_r1kh[i];
return NULL;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 5fe6182..d7e7805 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -112,6 +112,7 @@
unsigned int secure_ltf:1;
unsigned int secure_rtt:1;
unsigned int prot_range_neg:1;
+ unsigned int ssid_protection:1;
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
@@ -182,12 +183,16 @@
#ifdef CONFIG_TESTING_OPTIONS
struct wpabuf *test_assoc_ie;
+ struct wpabuf *test_eapol_m2_elems;
+ struct wpabuf *test_eapol_m4_elems;
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;
+ bool encrypt_eapol_m2;
+ bool encrypt_eapol_m4;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS
@@ -223,6 +228,7 @@
bool wmm_enabled;
bool driver_bss_selection;
+ bool ft_prepend_pmkid;
};
@@ -512,6 +518,12 @@
sm->ctx->notify_pmksa_cache_entry(sm->ctx->ctx, entry);
}
+static inline void wpa_sm_ssid_verified(struct wpa_sm *sm)
+{
+ if (sm->ctx->ssid_verified)
+ sm->ctx->ssid_verified(sm->ctx->ctx);
+}
+
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index d1510aa..515f1b0 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -33,8 +33,15 @@
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)
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_IE_VENDOR_TYPE)
+ 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]) == 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);
}
@@ -366,7 +373,7 @@
int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
{
u8 *pos = rsnxe;
- u16 capab = 0;
+ u32 capab = 0, tmp;
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
@@ -385,20 +392,27 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (sm->prot_range_neg)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (sm->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
- flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return 0; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
if (rsnxe_len < 2 + flen)
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
- *pos++ = capab & 0x00ff;
- capab >>= 8;
- if (capab)
- *pos++ = capab;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
return pos - rsnxe;
}
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 7156744..ed595bd 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -59,14 +59,6 @@
/* from tommath.h */
-#ifndef MIN
- #define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
-#ifndef MAX
- #define MAX(x,y) ((x)>(y)?(x):(y))
-#endif
-
#define OPT_CAST(x)
#ifdef __x86_64__
diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c
index 49e439d..7ea9cc7 100644
--- a/src/tls/pkcs1.c
+++ b/src/tls/pkcs1.c
@@ -322,8 +322,6 @@
return -1;
}
- os_free(decrypted);
-
if (hdr.payload + hdr.length != decrypted + decrypted_len) {
wpa_printf(MSG_INFO,
"PKCS #1: Extra data after signature - reject");
@@ -332,8 +330,12 @@
hdr.payload + hdr.length,
decrypted + decrypted_len - hdr.payload -
hdr.length);
+
+ os_free(decrypted);
return -1;
}
+ os_free(decrypted);
+
return 0;
}
diff --git a/src/utils/common.c b/src/utils/common.c
index 6acfcbd..d62dec7 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -990,6 +990,19 @@
}
+bool int_array_includes(int *arr, int val)
+{
+ int i;
+
+ for (i = 0; arr && arr[i]; i++) {
+ if (val == arr[i])
+ return true;
+ }
+
+ return false;
+}
+
+
void str_clear_free(char *str)
{
if (str) {
diff --git a/src/utils/common.h b/src/utils/common.h
index bede21e..aed93fb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -9,6 +9,7 @@
#ifndef COMMON_H
#define COMMON_H
+#include "includes.h"
#include "os.h"
#if defined(__linux__) || defined(__GLIBC__)
@@ -441,6 +442,13 @@
#define BIT(x) (1U << (x))
#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
/*
* Definitions for sparse validation
* (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
@@ -543,6 +551,11 @@
return a[0] & 0x01;
}
+static inline bool ether_addr_equal(const u8 *a, const u8 *b)
+{
+ return os_memcmp(a, b, ETH_ALEN) == 0;
+}
+
#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
#include "wpa_debug.h"
@@ -565,6 +578,7 @@
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);
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -587,6 +601,18 @@
u8 rssi_to_rcpi(int rssi);
char * get_param(const char *cmd, const char *param);
+#define for_each_link(__links, __i) \
+ for ((__i) = 0; (__i) < MAX_NUM_MLD_LINKS; (__i)++) \
+ if ((__links) & BIT(__i))
+
+/* 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) == (_def_idx)); \
+ (_i)++) \
+ if (!(_links) || (_links) & BIT(_i))
+
void forced_memzero(void *ptr, size_t len);
/*
diff --git a/src/utils/os.h b/src/utils/os.h
index 21ba5c3..1bbaea3 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -108,6 +108,26 @@
}
+static inline void os_reltime_add_ms(struct os_reltime *ts, int ms)
+{
+ ts->usec += ms * 1000;
+ while (ts->usec >= 1000000) {
+ ts->sec++;
+ ts->usec -= 1000000;
+ }
+ while (ts->usec < 0) {
+ ts->sec--;
+ ts->usec += 1000000;
+ }
+}
+
+
+static inline int os_reltime_in_ms(struct os_reltime *ts)
+{
+ return ts->sec * 1000 + ts->usec / 1000;
+}
+
+
static inline int os_reltime_initialized(struct os_reltime *t)
{
return t->sec != 0 || t->usec != 0;
@@ -667,14 +687,24 @@
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
-#define TEST_FAIL() testing_test_fail()
-int testing_test_fail(void);
-extern char wpa_trace_fail_func[256];
-extern unsigned int wpa_trace_fail_after;
-extern char wpa_trace_test_fail_func[256];
-extern unsigned int wpa_trace_test_fail_after;
+#define TEST_FAIL() testing_test_fail(NULL, false)
+#define TEST_FAIL_TAG(tag) testing_test_fail(tag, false)
+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_set_fail_pattern(bool is_alloc, char *patterns)
+{
+ return -1;
+}
+
+static inline int testing_get_fail_pattern(bool is_alloc, char *buf,
+ size_t buflen)
+{
+ return -1;
+}
#endif
#endif /* OS_H */
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index e721df2..e190645 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -566,44 +566,65 @@
#ifdef WPA_TRACE
#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
-char wpa_trace_fail_func[256] = { 0 };
-unsigned int wpa_trace_fail_after;
+struct wpa_trace_test_fail {
+ unsigned int fail_after;
+ char pattern[256];
+} wpa_trace_test_fail[5][2];
-static int testing_fail_alloc(void)
+int testing_test_fail(const char *tag, bool is_alloc)
{
+ const char *ignore_list[] = {
+ "os_malloc", "os_zalloc", "os_calloc", "os_realloc",
+ "os_realloc_array", "os_strdup", "os_memdup"
+ };
const char *func[WPA_TRACE_LEN];
- size_t i, res, len;
+ size_t i, j, res, len, idx;
char *pos, *next;
int match;
- if (!wpa_trace_fail_after)
+ is_alloc = !!is_alloc;
+
+ 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;
+ }
+ if (idx >= ARRAY_SIZE(wpa_trace_test_fail[is_alloc]))
return 0;
res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
i = 0;
- if (i < res && os_strcmp(func[i], __func__) == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_malloc") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_calloc") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_realloc") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_strdup") == 0)
- i++;
- if (i < res && os_strcmp(func[i], "os_memdup") == 0)
+
+ if (is_alloc) {
+ /* Skip our own stack frame */
i++;
- pos = wpa_trace_fail_func;
+ /* Skip allocation helpers */
+ for (j = 0; j < ARRAY_SIZE(ignore_list) && i < res; j++) {
+ if (os_strcmp(func[i], ignore_list[j]) == 0)
+ i++;
+ }
+ } else {
+ /* Not allocation, we might have a tag, if so, replace our
+ * own stack frame with the tag, otherwise skip it.
+ */
+ if (tag)
+ func[0] = tag;
+ else
+ i++;
+ }
+
+ pos = wpa_trace_test_fail[is_alloc][idx].pattern;
+
+ /* The prefixes mean:
+ * - '=': The function needs to be next in the backtrace
+ * - '?': The function is optionally present in the backtrace
+ */
match = 0;
while (i < res) {
int allow_skip = 1;
int maybe = 0;
+ bool prefix = false;
if (*pos == '=') {
allow_skip = 0;
@@ -617,7 +638,12 @@
len = next - pos;
else
len = os_strlen(pos);
- if (os_memcmp(pos, func[i], len) != 0) {
+ if (len >= 1 && pos[len - 1] == '*') {
+ prefix = true;
+ len -= 1;
+ }
+ if (os_strncmp(pos, func[i], len) != 0 ||
+ (!prefix && func[i][len] != '\0')) {
if (maybe && next) {
pos = next + 1;
continue;
@@ -638,82 +664,10 @@
if (!match)
return 0;
- wpa_trace_fail_after--;
- if (wpa_trace_fail_after == 0) {
- wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
- wpa_trace_fail_func);
- for (i = 0; i < res; i++)
- wpa_printf(MSG_INFO, "backtrace[%d] = %s",
- (int) i, func[i]);
- return 1;
- }
-
- return 0;
-}
-
-
-char wpa_trace_test_fail_func[256] = { 0 };
-unsigned int wpa_trace_test_fail_after;
-
-int testing_test_fail(void)
-{
- const char *func[WPA_TRACE_LEN];
- size_t i, res, len;
- char *pos, *next;
- int match;
-
- if (!wpa_trace_test_fail_after)
- return 0;
-
- res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
- i = 0;
- if (i < res && os_strcmp(func[i], __func__) == 0)
- i++;
-
- pos = wpa_trace_test_fail_func;
-
- match = 0;
- while (i < res) {
- int allow_skip = 1;
- int maybe = 0;
-
- if (*pos == '=') {
- allow_skip = 0;
- pos++;
- } else if (*pos == '?') {
- maybe = 1;
- pos++;
- }
- next = os_strchr(pos, ';');
- if (next)
- len = next - pos;
- else
- len = os_strlen(pos);
- if (os_memcmp(pos, func[i], len) != 0) {
- if (maybe && next) {
- pos = next + 1;
- continue;
- }
- if (allow_skip) {
- i++;
- continue;
- }
- return 0;
- }
- if (!next) {
- match = 1;
- break;
- }
- pos = next + 1;
- i++;
- }
- if (!match)
- return 0;
-
- wpa_trace_test_fail_after--;
- if (wpa_trace_test_fail_after == 0) {
+ wpa_trace_test_fail[is_alloc][idx].fail_after--;
+ if (wpa_trace_test_fail[is_alloc][idx].fail_after == 0) {
wpa_printf(MSG_INFO, "TESTING: fail at %s",
- wpa_trace_test_fail_func);
+ wpa_trace_test_fail[is_alloc][idx].pattern);
for (i = 0; i < res; i++)
wpa_printf(MSG_INFO, "backtrace[%d] = %s",
(int) i, func[i]);
@@ -723,19 +677,84 @@
return 0;
}
-#else
-static inline int testing_fail_alloc(void)
+int testing_set_fail_pattern(bool is_alloc, char *patterns)
+{
+#ifdef WPA_TRACE_BFD
+ char *token, *context = NULL;
+ size_t idx;
+
+ is_alloc = !!is_alloc;
+
+ os_memset(wpa_trace_test_fail[is_alloc], 0,
+ sizeof(wpa_trace_test_fail[is_alloc]));
+
+ idx = 0;
+ while ((token = str_token(patterns, " \n\r\t", &context)) &&
+ idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc])) {
+ wpa_trace_test_fail[is_alloc][idx].fail_after = atoi(token);
+ token = os_strchr(token, ':');
+ if (!token) {
+ os_memset(wpa_trace_test_fail[is_alloc], 0,
+ sizeof(wpa_trace_test_fail[is_alloc]));
+ return -1;
+ }
+
+ os_strlcpy(wpa_trace_test_fail[is_alloc][idx].pattern,
+ token + 1,
+ sizeof(wpa_trace_test_fail[is_alloc][0].pattern));
+ idx++;
+ }
+
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+int testing_get_fail_pattern(bool is_alloc, char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ size_t idx, ret;
+ char *pos = buf;
+ char *end = buf + buflen;
+
+ is_alloc = !!is_alloc;
+
+ for (idx = 0; idx < ARRAY_SIZE(wpa_trace_test_fail[is_alloc]); idx++) {
+ if (wpa_trace_test_fail[is_alloc][idx].pattern[0] == '\0')
+ break;
+
+ ret = os_snprintf(pos, end - pos, "%s%u:%s",
+ pos == buf ? "" : " ",
+ wpa_trace_test_fail[is_alloc][idx].fail_after,
+ wpa_trace_test_fail[is_alloc][idx].pattern);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+
+ return pos - buf;
+#else /* WPA_TRACE_BFD */
+ 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
void * os_malloc(size_t size)
{
struct os_alloc_trace *a;
- if (testing_fail_alloc())
+ if (testing_test_fail(NULL, true))
return NULL;
a = malloc(sizeof(*a) + size);
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 8f12da8..7c9a17f 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -197,7 +197,7 @@
if (abfd == NULL)
return;
- data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
+ data.pc = (uintptr_t) ((u8 *) pc - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -238,7 +238,7 @@
if (abfd == NULL)
return NULL;
- data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset);
+ data.pc = (uintptr_t) ((u8 *) pc - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
@@ -310,7 +310,7 @@
for (i = 0; i < btrace_num; i++) {
struct bfd_data data;
- data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset);
+ data.pc = (uintptr_t) ((u8 *) btrace_res[i] - start_offset);
data.found = FALSE;
bfd_map_over_sections(abfd, find_addr_sect, &data);
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 1fe3806..fd5bd93 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -146,6 +146,7 @@
}
data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta;
+ data->multi_ap_profile = cfg->multi_ap_profile;
return data;
}
@@ -336,9 +337,9 @@
pos = attr.authorized_macs;
for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
- if (os_memcmp(pos, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(pos, addr))
return 2;
- if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
+ if (ether_addr_equal(pos, bcast))
return 1;
pos += ETH_ALEN;
}
diff --git a/src/wps/wps.h b/src/wps/wps.h
index fed3e28..b99ae94 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -195,6 +195,11 @@
* enrollee
*/
int multi_ap_backhaul_sta;
+
+ /*
+ * multi_ap_profile - Get the Multi-AP Profile
+ */
+ int multi_ap_profile;
};
struct wps_data * wps_init(const struct wps_config *cfg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 819cd43..af828e5 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -715,8 +715,7 @@
wps_process_cred(&attr, &wps->cred))
return -1;
- if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
- 0) {
+ if (!ether_addr_equal(wps->cred.mac_addr, wps->wps->dev.mac_addr)) {
wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
MACSTR ") does not match with own address (" MACSTR
")", MAC2STR(wps->cred.mac_addr),
@@ -815,8 +814,7 @@
wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
"Registrar");
- if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
- 0) {
+ if (!ether_addr_equal(cred.mac_addr, wps->wps->dev.mac_addr)) {
wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
MACSTR ") does not match with own address (" MACSTR
")", MAC2STR(cred.mac_addr),
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 31d2e50..dede64b 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -62,7 +62,7 @@
struct wps_er_sta *sta;
dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
if ((addr == NULL ||
- os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
+ ether_addr_equal(sta->addr, addr)) &&
(uuid == NULL ||
os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
return sta;
@@ -106,7 +106,7 @@
(uuid == NULL ||
os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) &&
(mac_addr == NULL ||
- os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0))
+ ether_addr_equal(mac_addr, ap->mac_addr)))
return ap;
}
return NULL;
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 2cf22d4..5486e2a 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -127,6 +127,7 @@
struct wps_nfc_pw_token *nfc_pw_token;
int multi_ap_backhaul_sta;
+ int multi_ap_profile;
};
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 9587293..f49784f 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -238,7 +238,7 @@
wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
MAC2STR(addr));
for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
- if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(reg->authorized_macs[i], addr)) {
wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
"already in the list");
return; /* already in list */
@@ -259,7 +259,7 @@
wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
MAC2STR(addr));
for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
- if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(reg->authorized_macs[i], addr))
break;
}
if (i == WPS_MAX_AUTHORIZED_MACS) {
@@ -296,7 +296,7 @@
struct wps_registrar_device *dev;
for (dev = reg->devices; dev; dev = dev->next) {
- if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(dev->dev.mac_addr, addr))
return dev;
}
return NULL;
@@ -353,7 +353,7 @@
pbc = reg->pbc_sessions;
while (pbc) {
- if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(pbc->addr, addr) &&
os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
if (prev)
prev->next = pbc->next;
@@ -405,8 +405,7 @@
while (pbc) {
if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
(p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
- os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
- 0)) {
+ ether_addr_equal(reg->p2p_dev_addr, p2p_dev_addr))) {
if (prev)
prev->next = pbc->next;
else
@@ -2611,7 +2610,7 @@
if (is_zero_ether_addr(reg->p2p_dev_addr))
return 1; /* no filtering in use */
- if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(reg->p2p_dev_addr, wps->p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
"filtering for PBC: expected " MACSTR " was "
MACSTR " - indicate PBC session overlap",
@@ -2632,7 +2631,7 @@
if (is_zero_ether_addr(reg->p2p_dev_addr))
return 0; /* no specific Enrollee selected */
- if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(reg->p2p_dev_addr, wps->p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
"Enrollee match");
return 1;
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index e6ba165..8c50031 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -67,7 +67,7 @@
defaults: ["wpa_supplicant_cflags_defaults"],
srcs: [":wpa_supplicant_srcs"],
shared_libs: [
- "android.hardware.wifi.supplicant-V3-ndk",
+ "android.hardware.wifi.supplicant-V4-ndk",
"android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
@@ -91,37 +91,8 @@
],
}
-soong_config_module_type {
- name: "wpa_supplicant_cflags_cc_defaults",
- module_type: "cc_defaults",
- config_namespace: "wpa_supplicant",
- value_variables: [
- "platform_version",
- "nl80211_driver",
- ],
- properties: ["cflags"],
-}
-
-wpa_supplicant_cflags_cc_defaults {
+cc_defaults {
name: "wpa_supplicant_cflags_defaults",
- soong_config_variables: {
- // Devices that include this module should set these soong config vars appropriately.
- // For example:
- // $(call add_soong_config_namespace, wpa_supplicant)
- // $(call add_soong_config_var_value, wpa_supplicant, platform_version, $(PLATFORM_VERSION))
- // $(call add_soong_config_var_value, wpa_supplicant, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
- platform_version: {
- cflags: ["-DVERSION_STR_POSTFIX=\"-%s\""],
- conditions_default: {
- // Default value
- cflags: ["-DVERSION_STR_POSTFIX=\"-Android\""],
- },
- },
- nl80211_driver: {
- cflags: ["-D%s"],
- // Flag is optional, so no default value provided.
- },
- },
// Generated by building wpa_supplicant and printing LOCAL_CFLAGS.
cflags: [
"-DANDROID_LOG_NAME=\"wpa_supplicant\"",
@@ -215,7 +186,20 @@
"-Wno-unused-function",
"-Wno-unused-parameter",
"-Wno-unused-variable",
- ],
+ ] +
+ // Devices that include this module should set these soong config vars appropriately.
+ // For example:
+ // $(call soong_config_set, wpa_supplicant, platform_version, $(PLATFORM_VERSION))
+ // $(call soong_config_set, wpa_supplicant, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
+ select(soong_config_variable("wpa_supplicant", "platform_version"), {
+ any @ version: ["-DVERSION_STR_POSTFIX=\"-" + version + "\""],
+ default: ["-DVERSION_STR_POSTFIX=\"-Android\""],
+ }) +
+ select(soong_config_variable("wpa_supplicant", "nl80211_driver"), {
+ any @ driver: ["-D" + driver],
+ // Flag is optional, so no default value provided.
+ default: [],
+ }),
// Similar to suppressing clang compiler warnings, here we
// suppress clang-tidy warnings to reduce noises in Android build.log.
tidy_checks: [
@@ -331,6 +315,7 @@
"src/common/sae.c",
"src/common/sae_pk.c",
"src/common/wpa_common.c",
+ "src/common/ptksa_cache.c",
"src/crypto/aes-ctr.c",
"src/crypto/aes-encblock.c",
"src/crypto/aes-siv.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 98152be..f36b074 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -139,11 +139,9 @@
OBJS += src/utils/bitfield.c
OBJS += src/utils/ip_addr.c
OBJS += src/utils/crc32.c
-OBJS += wmm_ac.c
-OBJS += op_classes.c
-OBJS += rrm.c
+OBJS += src/common/ptksa_cache.c
+OBJS += src/rsn_supp/pmksa_cache.c
OBJS += twt.c
-OBJS += robust_av.c
OBJS_p = wpa_passphrase.c
OBJS_p += src/utils/common.c
OBJS_p += src/utils/wpa_debug.c
@@ -324,6 +322,12 @@
endif
endif
+ifdef CONFIG_NAN_USD
+OBJS += src/common/nan_de.c
+OBJS += nan_usd.c
+L_CFLAGS += -DCONFIG_NAN_USD
+endif
+
ifdef CONFIG_OWE
L_CFLAGS += -DCONFIG_OWE
NEED_ECC=y
@@ -352,6 +356,10 @@
CONFIG_WNM=y
endif
+ifdef CONFIG_BGSCAN_SIMPLE
+CONFIG_WNM=y
+endif
+
ifdef CONFIG_WNM
L_CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.c
@@ -373,7 +381,6 @@
ifndef CONFIG_NO_WPA
OBJS += src/rsn_supp/wpa.c
OBJS += src/rsn_supp/preauth.c
-OBJS += src/rsn_supp/pmksa_cache.c
OBJS += src/rsn_supp/wpa_ie.c
OBJS += src/common/wpa_common.c
NEED_AES=y
@@ -426,8 +433,8 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
OBJS += src/pasn/pasn_initiator.c
+OBJS += src/pasn/pasn_common.c
OBJS += pasn_supplicant.c
endif
@@ -466,6 +473,28 @@
L_CFLAGS += -DCONFIG_NO_TKIP
endif
+ifdef CONFIG_NO_RRM
+L_CFLAGS += -DCONFIG_NO_RRM
+else
+OBJS += rrm.c
+ifdef CONFIG_AP
+OBJS += src/ap/rrm.c
+endif
+OBJS += op_classes.c
+endif
+
+ifdef CONFIG_NO_WMM_AC
+L_CFLAGS += -DCONFIG_NO_WMM_AC
+else
+OBJS += wmm_ac.c
+endif
+
+ifdef CONFIG_NO_ROBUST_AV
+L_CFLAGS += -DCONFIG_NO_ROBUST_AV
+else
+OBJS += robust_av.c
+endif
+
include $(LOCAL_PATH)/src/drivers/drivers.mk
@@ -949,7 +978,6 @@
OBJS += src/ap/bss_load.c
OBJS += src/ap/eap_user_db.c
OBJS += src/ap/neighbor_db.c
-OBJS += src/ap/rrm.c
OBJS += src/ap/ieee802_11_ht.c
ifdef CONFIG_IEEE80211AC
OBJS += src/ap/ieee802_11_vht.c
@@ -1010,6 +1038,9 @@
OBJS += src/ap/gas_query_ap.c
NEED_AP_GAS_SERV=y
endif
+ifdef CONFIG_NAN_USD
+OBJS += src/ap/nan_usd_ap.c
+endif
ifdef CONFIG_INTERWORKING
NEED_AP_GAS_SERV=y
endif
@@ -1028,6 +1059,7 @@
ifdef CONFIG_TESTING_OPTIONS
L_CFLAGS += -DCONFIG_TESTING_OPTIONS
+NEED_AES_WRAP=y
endif
ifdef NEED_RSN_AUTHENTICATOR
@@ -1815,8 +1847,9 @@
PASNOBJS += src/common/ptksa_cache.c
-ifndef CONFIG_NO_WPA
PASNOBJS += src/rsn_supp/pmksa_cache.c
+
+ifndef CONFIG_NO_WPA
PASNOBJS += src/rsn_supp/wpa_ie.c
endif
@@ -1902,6 +1935,7 @@
PASNOBJS += src/pasn/pasn_initiator.c
PASNOBJS += src/pasn/pasn_responder.c
+PASNOBJS += src/pasn/pasn_common.c
########################
@@ -1968,12 +2002,12 @@
LOCAL_MODULE := wpa_supplicant
ifeq ($(WPA_SUPPLICANT_USE_AIDL), y)
-LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant-V3-ndk
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant-V4-ndk
LOCAL_SHARED_LIBRARIES += android.system.keystore2-V1-ndk
LOCAL_SHARED_LIBRARIES += libutils libbase
LOCAL_SHARED_LIBRARIES += libbinder_ndk
LOCAL_STATIC_LIBRARIES += libwpa_aidl
-LOCAL_VINTF_FRAGMENTS := aidl/android.hardware.wifi.supplicant.xml
+LOCAL_REQUIRED_MODULES += android.hardware.wifi.supplicant.xml
ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
LOCAL_INIT_RC=aidl/android.hardware.wifi.supplicant-service.rc
endif
@@ -2072,7 +2106,7 @@
aidl/sta_network.cpp \
aidl/supplicant.cpp
LOCAL_SHARED_LIBRARIES := \
- android.hardware.wifi.supplicant-V3-ndk \
+ android.hardware.wifi.supplicant-V4-ndk \
android.system.keystore2-V1-ndk \
libbinder_ndk \
libbase \
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index efcc6cd..3f4162e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,55 @@
ChangeLog for wpa_supplicant
+2024-07-20 - v2.11
+ * Wi-Fi Easy Connect
+ - add support for DPP release 3
+ - allow Configurator parameters to be provided during config exchange
+ * MACsec
+ - add support for GCM-AES-256 cipher suite
+ - remove incorrect EAP Session-Id length constraint
+ - add hardware offload support for additional drivers
+ * HE/IEEE 802.11ax/Wi-Fi 6
+ - support BSS color updates
+ - various fixes
+ * EHT/IEEE 802.11be/Wi-Fi 7
+ - add preliminary support
+ * support OpenSSL 3.0 API changes
+ * improve EAP-TLS support for TLSv1.3
+ * EAP-SIM/AKA: support IMSI privacy
+ * improve mitigation against DoS attacks when PMF is used
+ * improve 4-way handshake operations
+ - discard unencrypted EAPOL frames in additional cases
+ - use Secure=1 in message 2 during PTK rekeying
+ * OCV: do not check Frequency Segment 1 Channel Number for 160 MHz cases
+ to avoid interoperability issues
+ * support new SAE AKM suites with variable length keys
+ * support new AKM for 802.1X/EAP with SHA384
+ * improve cross-AKM roaming with driver-based SME/BSS selection
+ * PASN
+ - extend support for secure ranging
+ - allow PASN implementation to be used with external programs for
+ Wi-Fi Aware
+ * FT: Use SHA256 to derive PMKID for AKM 00-0F-AC:3 (FT-EAP)
+ - this is based on additional details being added in the IEEE 802.11
+ standard
+ - the new implementation is not backwards compatible, but PMKSA
+ caching with FT-EAP was, and still is, disabled by default
+ * support a pregenerated MAC (mac_addr=3) as an alternative mechanism
+ for using per-network random MAC addresses
+ * EAP-PEAP: require Phase 2 authentication by default (phase2_auth=1)
+ to improve security for still unfortunately common invalid
+ configurations that do not set ca_cert
+ * extend SCS support for QoS Characteristics
+ * extend MSCS support
+ * support unsynchronized service discovery (USD)
+ * add support for explicit SSID protection in 4-way handshake
+ (a mitigation for CVE-2023-52424; disabled by default for now, can be
+ enabled with ssid_protection=1)
+ - in addition, verify SSID after key setup when beacon protection is
+ used
+ * fix SAE H2E rejected groups validation to avoid downgrade attacks
+ * a large number of other fixes, cleanup, and extensions
+
2022-01-16 - v2.10
* SAE changes
- improved protection against side channel attacks
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8c417b8..8bec178 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -64,6 +64,7 @@
CFLAGS += -DCONFIG_TESTING_OPTIONS
CONFIG_WPS_TESTING=y
CONFIG_TDLS_TESTING=y
+NEED_AES_WRAP=y
endif
mkconfig:
@@ -111,10 +112,9 @@
OBJS += ../src/utils/bitfield.o
OBJS += ../src/utils/ip_addr.o
OBJS += ../src/utils/crc32.o
-OBJS += op_classes.o
-OBJS += rrm.o
+OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
OBJS += twt.o
-OBJS += robust_av.o
OBJS_p = wpa_passphrase.o
OBJS_p += ../src/utils/common.o
OBJS_p += ../src/utils/wpa_debug.o
@@ -123,7 +123,6 @@
OBJS_c += ../src/utils/wpa_debug.o
OBJS_c += ../src/utils/common.o
OBJS_c += ../src/common/cli.o
-OBJS += wmm_ac.o
ifndef CONFIG_OS
ifdef CONFIG_NATIVE_WINDOWS
@@ -192,7 +191,7 @@
endif
ifdef CONFIG_CODE_COVERAGE
-CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE
LIBS += -lgcov
LIBS_c += -lgcov
LIBS_p += -lgcov
@@ -318,6 +317,12 @@
endif
endif
+ifdef CONFIG_NAN_USD
+OBJS += ../src/common/nan_de.o
+OBJS += nan_usd.o
+CFLAGS += -DCONFIG_NAN_USD
+endif
+
ifdef CONFIG_OWE
CFLAGS += -DCONFIG_OWE
NEED_ECC=y
@@ -346,6 +351,10 @@
CONFIG_WNM=y
endif
+ifdef CONFIG_BGSCAN_SIMPLE
+CONFIG_WNM=y
+endif
+
ifdef CONFIG_WNM
CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.o
@@ -367,7 +376,6 @@
ifndef CONFIG_NO_WPA
OBJS += ../src/rsn_supp/wpa.o
OBJS += ../src/rsn_supp/preauth.o
-OBJS += ../src/rsn_supp/pmksa_cache.o
OBJS += ../src/rsn_supp/wpa_ie.o
OBJS += ../src/common/wpa_common.o
NEED_AES=y
@@ -428,8 +436,8 @@
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
OBJS += ../src/pasn/pasn_initiator.o
+OBJS += ../src/pasn/pasn_common.o
OBJS += pasn_supplicant.o
endif
@@ -477,6 +485,28 @@
CFLAGS += -DCONFIG_NO_LOAD_DYNAMIC_EAP
endif
+ifdef CONFIG_NO_RRM
+CFLAGS += -DCONFIG_NO_RRM
+else
+OBJS += rrm.o
+ifdef CONFIG_AP
+OBJS += ../src/ap/rrm.o
+endif
+OBJS += op_classes.o
+endif
+
+ifdef CONFIG_NO_WMM_AC
+CFLAGS += -DCONFIG_NO_WMM_AC
+else
+OBJS += wmm_ac.o
+endif
+
+ifdef CONFIG_NO_ROBUST_AV
+CFLAGS += -DCONFIG_NO_ROBUST_AV
+else
+OBJS += robust_av.o
+endif
+
include ../src/drivers/drivers.mak
ifdef CONFIG_AP
OBJS_d += $(DRV_BOTH_OBJS)
@@ -983,7 +1013,6 @@
OBJS += ../src/ap/bss_load.o
OBJS += ../src/ap/eap_user_db.o
OBJS += ../src/ap/neighbor_db.o
-OBJS += ../src/ap/rrm.o
OBJS += ../src/ap/ieee802_11_ht.o
ifdef CONFIG_IEEE80211AC
OBJS += ../src/ap/ieee802_11_vht.o
@@ -1044,6 +1073,9 @@
OBJS += ../src/ap/gas_query_ap.o
NEED_AP_GAS_SERV=y
endif
+ifdef CONFIG_NAN_USD
+OBJS += ../src/ap/nan_usd_ap.o
+endif
ifdef CONFIG_INTERWORKING
NEED_AP_GAS_SERV=y
endif
@@ -1058,6 +1090,7 @@
ifdef CONFIG_MBO
OBJS += mbo.o
CFLAGS += -DCONFIG_MBO
+NEED_GAS=y
endif
ifdef NEED_RSN_AUTHENTICATOR
@@ -1076,17 +1109,20 @@
ifdef CONFIG_PCSC
# PC/SC interface for smartcards (USIM, GSM SIM)
-CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+CFLAGS += -DPCSC_FUNCS
OBJS += ../src/utils/pcsc_funcs.o
ifdef CONFIG_NATIVE_WINDOWS
#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
#dynamic symbol loading that is now used in pcsc_funcs.c
#LIBS += -lwinscard
+CFLAGS += -I/usr/include/PCSC
else
ifdef CONFIG_OSX
LIBS += -framework PCSC
+CFLAGS += -I/usr/include/PCSC
else
LIBS += $(shell $(PKG_CONFIG) --libs libpcsclite)
+CFLAGS += $(shell $(PKG_CONFIG) --cflags libpcsclite)
endif
endif
endif
@@ -1152,6 +1188,10 @@
CFLAGS += -DCONFIG_TLSV12
endif
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+endif
+
ifeq ($(CONFIG_TLS), wolfssl)
ifdef TLS_FUNCS
CFLAGS += -DWOLFSSL_DER_LOAD
@@ -1890,6 +1930,9 @@
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
OBJS_t += ../src/radius/radius_client.o
OBJS_t += ../src/radius/radius.o
+ifdef CONFIG_RADIUS_TLS
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
@@ -2184,9 +2227,9 @@
endif
LIBPASNSO += ../src/common/ptksa_cache.c
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
ifndef CONFIG_NO_WPA
-LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
LIBPASNSO += ../src/rsn_supp/wpa_ie.c
endif
@@ -2281,6 +2324,7 @@
LIBPASNSO += ../src/pasn/pasn_initiator.c
LIBPASNSO += ../src/pasn/pasn_responder.c
+LIBPASNSO += ../src/pasn/pasn_common.c
libpasn.so: $(LIBPASNSO)
@$(E) " CC $@ ($^)"
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index f8da781..49e971e 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
wpa_supplicant
==============
-Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
diff --git a/wpa_supplicant/README-NAN-USD b/wpa_supplicant/README-NAN-USD
new file mode 100644
index 0000000..72c379f
--- /dev/null
+++ b/wpa_supplicant/README-NAN-USD
@@ -0,0 +1,147 @@
+Wi-Fi Aware unsynchronized service discovery (NAN USD)
+======================================================
+
+This document descibes how the unsynchronized service discovery defined
+in the Wi-Fi Aware specification v4.0 can be used with wpa_spplicant.
+
+More information about Wi-Fi Aware is available from this Wi-Fi
+Alliance web page:
+https://www.wi-fi.org/discover-wi-fi/wi-fi-aware
+
+Build config setup
+------------------
+
+The following parameters must be included in the config file used to
+compile hostapd and wpa_supplicant.
+
+wpa_supplicant build config
+---------------------------
+
+Enable NAN USD in wpa_supplicant build config file
+
+CONFIG_NAN_USD=y
+
+Control interface commands and events
+-------------------------------------
+
+Following control interface commands can be used:
+
+NAN_PUBLISH service_name=<name> [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [freq_list=<comma separate list of MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [solicited=0] [unsolicited=0] [fsd=0]
+
+If ttl=0 or the parameter is not included, only one Publish message is
+transmitted.
+
+If freq is not included, the default frequency 2437 MHz (channel 6 on
+the 2.4 GHz band) is used.
+
+If freq_list is included, publisher iterates over all the listed
+channels. A special freq_list=all value can be used to generate the
+channel list automatically based on the list of allowed 2.4 and 5 GHz
+channels.
+
+srv_proto_type values are defined in the Service Protocol Types table in
+the Wi-Fi Aware specification.
+
+This command returns the assigned publish_id value or FAIL on failure.
+
+This command maps to the Publish() method in the NAN Discovery Engine.
+
+NAN_CANCEL_PUBLISH publish_id=<id from NAN_PUBLISH>
+
+This command maps to the CancelPublish() method in the NAN Discovery
+Engine.
+
+NAN_UPDATE_PUBLISH publish_id=<id from NAN_PUBLISH> [ssi=<service specific information (hexdump)>]
+
+This command maps to the UpdatePublish() method in the NAN Discovery
+Engine.
+
+NAN_SUBSCRIBE service_name=<name> [active=1] [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>]
+
+If ttl=0 or the parameter is not included, operation is terminated once
+the first matching publisher is found.
+
+If freq is not included, the default frequency 2437 MHz (channel 6 on
+the 2.4 GHz band) is used.
+
+srv_proto_type values are defined in the Service Protocol Types table in
+the Wi-Fi Aware specification.
+
+This command returns the assigned subscribe_id value or FAIL on failure.
+
+This command maps to the Subscribe() method in the NAN Discovery Engine.
+
+NAN_CANCEL_SUBSCRIBE subscribe_id=<id from NAN_SUBSCRIBE>
+
+This command maps to the CancelSubscribe() method in the NAN Discovery Engine.
+
+NAN_TRANSMIT handle=<id from NAN_PUBLISH or NAN_SUBSCRIBE> req_instance_id=<peer's id> address=<peer's MAC address> [ssi=<service specific information (hexdump)>]
+
+This command maps to the Transmit() method in the NAN Discovery Engine.
+
+Following control interface events are used:
+
+NAN-DISCOVERY-RESULT subscribe_id=<own id> publish_id=<peer's id> address=<peer MAC address> fsd=<0/1> fsd_gas=<0/1> srv_proto_type=<type> ssi=<service specific information (hexdump)>
+
+This event maps to the DiscoveryResult() event in the NAN Discovery
+Engine.
+
+NAN-REPLIED publish_id=<own id> address=<peer MAC address> subscribe_id=<peer id> srv_proto_type=<ype> ssi=<service specific information (hexdump)>
+
+This event maps to the Replied() event in the NAN Discovery Engine.
+
+NAN-PUBLISH-TERMINATED publish_id=<own id> reason=<timeout/user-request/failure>
+
+This event maps to the PublishTerminated() event in the NAN Discovery
+Engine.
+
+NAN-SUBSCRIBE-TERMINATED subscribe_id=<own id> reason=<timeout/user-request/failure>
+
+This event maps to the SubscribeTerminate() event in the NAN Discovery
+Engine.
+
+NAN-RECEIVE id=<own id> peer_instance_id=<peer id> address=<peer MAC adress> ssi=<service specific information (hexdump)>
+
+This event maps to the Receive() event in the NAN Discovery Engine.
+
+
+Example operation
+-----------------
+
+Start Subscribe and Publish functions:
+
+dev0: NAN_SUBSCRIBE service_name=_test srv_proto_type=3 ssi=1122334455
+--> returns 7
+
+dev1: NAN_PUBLISH service_name=_test srv_proto_type=3 ssi=6677
+--> returns 5
+
+Subscriber notification of a discovery:
+
+event on dev0: <3>NAN-DISCOVERY-RESULT subscribe_id=7 publish_id=5 address=02:00:00:00:01:00 fsd=1 fsd_gas=0 srv_proto_type=3 ssi=6677
+
+Publisher notification of a Follow-up message with no ssi (to enter
+paused state to continue exchange with the subscriber):
+
+event on dev1: <3>NAN-RECEIVE id=5 peer_instance_id=7 address=02:00:00:00:00:00 ssi=
+
+Subscriber sending a Follow-up message:
+
+dev0: NAN_TRANSMIT handle=7 req_instance_id=5 address=02:00:00:00:01:00 ssi=8899
+
+Publisher receiving the Follow-up message:
+
+event on dev1: <3>NAN-RECEIVE id=5 peer_instance_id=7 address=02:00:00:00:00:00 ssi=8899
+
+Publisher sending a Follow-up message:
+
+dev1: NAN_TRANSMIT handle=5 req_instance_id=7 address=02:00:00:00:00:00 ssi=aabbccdd
+
+Subscriber receiving the Follow-up message:
+
+event on dev0: <3>NAN-RECEIVE id=7 peer_instance_id=5 address=02:00:00:00:01:00 ssi=aabbccdd
+
+Stop Subscribe and Publish functions:
+
+dev0: NAN_CANCEL_SUBSCRIBE subscribe_id=7
+dev1: NAN_CANCEL_PUBLIST publish_id=5
diff --git a/wpa_supplicant/aidl/Android.bp b/wpa_supplicant/aidl/Android.bp
index 2175462..a2ce7e8 100644
--- a/wpa_supplicant/aidl/Android.bp
+++ b/wpa_supplicant/aidl/Android.bp
@@ -36,7 +36,7 @@
],
soc_specific: true,
shared_libs: [
- "android.hardware.wifi.supplicant-V3-ndk",
+ "android.hardware.wifi.supplicant-V4-ndk",
"android.system.keystore2-V1-ndk",
"libbinder_ndk",
"libbase",
@@ -59,9 +59,10 @@
],
}
-filegroup {
+vintf_fragment {
name: "android.hardware.wifi.supplicant.xml",
- srcs: ["android.hardware.wifi.supplicant.xml"],
+ src: "android.hardware.wifi.supplicant.xml",
+ soc_specific: true,
}
soong_config_module_type {
diff --git a/wpa_supplicant/aidl/aidl_return_util.h b/wpa_supplicant/aidl/aidl_return_util.h
index 109723a..2cb8b5a 100644
--- a/wpa_supplicant/aidl/aidl_return_util.h
+++ b/wpa_supplicant/aidl/aidl_return_util.h
@@ -10,6 +10,12 @@
#define AIDL_RETURN_UTIL_H_
#include <aidl/android/hardware/wifi/supplicant/SupplicantStatusCode.h>
+#include <mutex>
+
+namespace {
+ // Mutex serializes requests when this process is called by multiple clients
+ std::mutex aidl_return_mutex;
+}
namespace aidl {
namespace android {
@@ -33,6 +39,7 @@
ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
Args&&... args)
{
+ std::lock_guard<std::mutex> guard(aidl_return_mutex);
if (obj->isValid()) {
return (obj->*work)(std::forward<Args>(args)...);
} else {
@@ -47,6 +54,7 @@
ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
ReturnT* ret_val, Args&&... args)
{
+ std::lock_guard<std::mutex> guard(aidl_return_mutex);
if (obj->isValid()) {
auto call_pair = (obj->*work)(std::forward<Args>(args)...);
*ret_val = call_pair.first;
diff --git a/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml b/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
index 37cc3af..103d37f 100644
--- a/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
+++ b/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.wifi.supplicant</name>
- <version>3</version>
+ <version>4</version>
<fqname>ISupplicant/default</fqname>
</hal>
</manifest>
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index a4c2c36..3880a1d 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -1920,6 +1920,11 @@
#ifdef EAP_TLSV1_3
mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::TLS_V1_3);
#endif
+ AidlManager *aidl_manager = AidlManager::getInstance();
+ WPA_ASSERT(aidl_manager);
+ if (aidl_manager->isAidlServiceVersionAtLeast(4) && wpas_rsn_overriding(wpa_s)) {
+ mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::RSN_OVERRIDING);
+ }
wpa_printf(MSG_DEBUG, "Driver capability mask: 0x%x", mask);
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 1589dd2..ae1943f 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -413,7 +413,7 @@
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, NULL);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
@@ -472,7 +472,7 @@
// Request the current scan results from the driver and update
// the local BSS list wpa_s->bss. This is to avoid a full scan
// while processing the connect request on newly created interface.
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, NULL);
}
// The supplicant core creates a corresponding aidl object via
// AidlManager when |wpa_supplicant_add_iface| is called.
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 4cc3808..42e2237 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -564,4 +564,19 @@
# WPA3-Personal (SAE) PK (Public Key) mode
CONFIG_SAE_PK=y
+# Disable support for Radio Measurement (IEEE 802.11k) and supported operating
+# class indication. Removing these is not recommended since they can help the
+# AP manage the network and STA steering.
+#CONFIG_NO_RRM=y
+
+# Disable support for Robust AV streaming for consumer and enterprise Wi-Fi
+# applications; IEEE Std 802.11-2020, 4.3.24; SCS, MSCS, QoS Management
+#CONFIG_NO_ROBUST_AV=y
+
+# Disable support for WMM admission control
+#CONFIG_NO_WMM_AC=y
+
+# Wi-Fi Aware unsynchronized service discovery (NAN USD)
+#CONFIG_NAN_USD=y
+
include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 1b94fe5..69a0e5e 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -440,9 +440,14 @@
}
}
- if (wpa_s->p2p_go_no_pri_sec_switch) {
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group && wpa_s->p2p_go_no_pri_sec_switch) {
conf->no_pri_sec_switch = 1;
- } else if (conf->secondary_channel) {
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
+ if (conf->secondary_channel) {
struct wpa_supplicant *iface;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
@@ -868,7 +873,8 @@
static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
- int authorized, const u8 *p2p_dev_addr, const u8 *ip)
+ int authorized, const u8 *p2p_dev_addr,
+ const u8 *ip)
{
wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr, ip);
}
@@ -1848,6 +1854,8 @@
if (ret)
return ret;
+ settings.link_id = -1;
+
return ap_switch_channel(wpa_s, &settings);
}
#endif /* CONFIG_CTRL_IFACE */
diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h
index 3df1550..4388b4e 100644
--- a/wpa_supplicant/bgscan.h
+++ b/wpa_supplicant/bgscan.h
@@ -51,7 +51,7 @@
#else /* CONFIG_BGSCAN */
static inline int bgscan_init(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, const char name)
+ struct wpa_ssid *ssid, const char *name)
{
return 0;
}
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index 9830c4a..cab4ae2 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -57,7 +57,7 @@
return 0;
for (i = 0; i < array_len; i++) {
- if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(array + i * ETH_ALEN, bssid))
return 1;
}
@@ -70,7 +70,7 @@
{
u8 *n;
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(bss->bssid, bssid))
return;
if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
return;
@@ -91,7 +91,7 @@
struct bgscan_learn_bss *bss;
dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(bss->bssid, bssid))
return bss;
}
return NULL;
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index f398b85..a90cf86 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -15,11 +15,16 @@
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "scan.h"
+#include "config.h"
+#include "wnm_sta.h"
+#include "bss.h"
#include "bgscan.h"
struct bgscan_simple_data {
struct wpa_supplicant *wpa_s;
const struct wpa_ssid *ssid;
+ unsigned int use_btm_query;
+ unsigned int scan_action_count;
int scan_interval;
int signal_threshold;
int short_scan_count; /* counter for scans using short scan interval */
@@ -30,12 +35,54 @@
};
+static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static bool bgscan_simple_btm_query(struct wpa_supplicant *wpa_s,
+ struct bgscan_simple_data *data)
+{
+ unsigned int mod;
+
+ if (!data->use_btm_query || wpa_s->conf->disable_btm ||
+ !wpa_s->current_bss ||
+ !wpa_bss_ext_capab(wpa_s->current_bss,
+ WLAN_EXT_CAPAB_BSS_TRANSITION))
+ return false;
+
+ /* Try BTM x times, scan on x + 1 */
+ data->scan_action_count++;
+ mod = data->scan_action_count % (data->use_btm_query + 1);
+ if (mod >= data->use_btm_query)
+ return false;
+
+ wpa_printf(MSG_DEBUG,
+ "bgscan simple: Send BSS transition management query %d/%d",
+ mod, data->use_btm_query);
+ if (wnm_send_bss_transition_mgmt_query(
+ wpa_s, WNM_TRANSITION_REASON_BETTER_AP_FOUND, NULL, 0)) {
+ wpa_printf(MSG_DEBUG,
+ "bgscan simple: Failed to send BSS transition management query");
+ /* Fall through and do regular scan */
+ return false;
+ }
+
+ /* Start a new timeout for the next one. We don't have scan callback to
+ * otherwise trigger future progress when using BTM path. */
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_simple_timeout, data, NULL);
+ return true;
+}
+
+
static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct bgscan_simple_data *data = eloop_ctx;
struct wpa_supplicant *wpa_s = data->wpa_s;
struct wpa_driver_scan_params params;
+ if (bgscan_simple_btm_query(wpa_s, data))
+ goto scan_ok;
+
os_memset(¶ms, 0, sizeof(params));
params.num_ssids = 1;
params.ssids[0].ssid = data->ssid->ssid;
@@ -54,6 +101,7 @@
eloop_register_timeout(data->scan_interval, 0,
bgscan_simple_timeout, data, NULL);
} else {
+ scan_ok:
if (data->scan_interval == data->short_interval) {
data->short_scan_count++;
if (data->short_scan_count >= data->max_short_scans) {
@@ -80,6 +128,8 @@
{
const char *pos;
+ data->use_btm_query = 0;
+
data->short_interval = atoi(params);
pos = os_strchr(params, ':');
@@ -95,6 +145,11 @@
}
pos++;
data->long_interval = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos) {
+ pos++;
+ data->use_btm_query = atoi(pos);
+ }
return 0;
}
@@ -134,6 +189,7 @@
data->scan_interval = data->short_interval;
data->max_short_scans = data->long_interval / data->short_interval + 1;
if (data->signal_threshold) {
+ wpa_s->signal_threshold = data->signal_threshold;
/* Poll for signal info to set initial scan interval */
struct wpa_signal_info siginfo;
if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
@@ -161,8 +217,10 @@
{
struct bgscan_simple_data *data = priv;
eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
- if (data->signal_threshold)
+ if (data->signal_threshold) {
+ data->wpa_s->signal_threshold = 0;
wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+ }
os_free(data);
}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index c1be660..cf94d4b 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -13,10 +13,12 @@
#include "common/ieee802_11_defs.h"
#include "drivers/driver.h"
#include "eap_peer/eap.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "config.h"
#include "notify.h"
#include "scan.h"
+#include "bssid_ignore.h"
#include "bss.h"
static void wpa_bss_set_hessid(struct wpa_bss *bss)
@@ -263,7 +265,7 @@
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 || os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) &&
+ if ((!bssid || ether_addr_equal(bss->bssid, bssid)) &&
bss->ssid_len == ssid_len &&
os_memcmp(bss->ssid, ssid, ssid_len) == 0)
return bss;
@@ -358,12 +360,11 @@
#ifdef CONFIG_P2P
u8 addr[ETH_ALEN];
- if (os_memcmp(bss->bssid, wpa_s->pending_join_iface_addr,
- ETH_ALEN) == 0)
+ if (ether_addr_equal(bss->bssid, wpa_s->pending_join_iface_addr))
return true;
if (!is_zero_ether_addr(wpa_s->pending_join_dev_addr) &&
p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len, addr) == 0 &&
- os_memcmp(addr, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0)
+ ether_addr_equal(addr, wpa_s->pending_join_dev_addr))
return true;
#endif /* CONFIG_P2P */
return false;
@@ -399,6 +400,11 @@
if (bss == wpa_s->ml_connect_probe_bss)
return 1;
+#ifdef CONFIG_WNM
+ if (bss == wpa_s->wnm_target_bss)
+ return 1;
+#endif /* CONFIG_WNM */
+
if (wpa_s->current_bss &&
(bss->ssid_len != wpa_s->current_bss->ssid_len ||
os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
@@ -406,18 +412,15 @@
return 0; /* SSID has changed */
if (!is_zero_ether_addr(bss->bssid) &&
- (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
- os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0))
+ (ether_addr_equal(bss->bssid, wpa_s->bssid) ||
+ ether_addr_equal(bss->bssid, wpa_s->pending_bssid)))
return 1;
if (!wpa_s->valid_links)
return 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
- if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
+ for_each_link(wpa_s->valid_links, i) {
+ if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
return 1;
}
@@ -717,7 +720,8 @@
dl_list_del(&bss->list);
#ifdef CONFIG_P2P
if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
- !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
+ !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE) &&
+ !(changes & WPA_BSS_FREQ_CHANGED_FLAG)) {
/*
* This can happen when non-P2P station interface runs a scan
* without P2P IE in the Probe Request frame. P2P GO would reply
@@ -1101,7 +1105,7 @@
if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
return NULL;
dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(bss->bssid, bssid))
return bss;
}
return NULL;
@@ -1126,7 +1130,7 @@
if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
return NULL;
dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(bss->bssid, bssid))
continue;
if (found == NULL ||
os_reltime_before(&found->last_update, &bss->last_update))
@@ -1155,7 +1159,7 @@
u8 addr[ETH_ALEN];
if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
addr) != 0 ||
- os_memcmp(addr, dev_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(addr, dev_addr))
continue;
if (!found ||
os_reltime_before(&found->last_update, &bss->last_update))
@@ -1222,18 +1226,27 @@
/**
- * wpa_bss_get_ie_nth - Fetch a specified information element from a BSS entry
+ * wpa_bss_get_ie_beacon - Fetch a specified information element from a BSS entry
* @bss: BSS table entry
* @ie: Information element identitifier (WLAN_EID_*)
- * @nth: Return the nth element of the requested type (2 returns the second)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
- * This function returns the nth matching information element in the BSS
+ * This function returns the first matching information element in the BSS
* entry.
+ *
+ * This function is like wpa_bss_get_ie(), but uses IE buffer only from Beacon
+ * frames instead of either Beacon or Probe Response frames.
*/
-const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth)
+const u8 * wpa_bss_get_ie_beacon(const struct wpa_bss *bss, u8 ie)
{
- return get_ie_nth(wpa_bss_ie_ptr(bss), bss->ie_len, ie, nth);
+ const u8 *ies;
+
+ if (bss->beacon_ie_len == 0)
+ return NULL;
+
+ ies = wpa_bss_ie_ptr(bss);
+ ies += bss->ie_len;
+ return get_ie(ies, bss->beacon_ie_len, ie);
}
@@ -1496,29 +1509,12 @@
}
-/**
- * wpa_bss_defrag_mle - Get a buffer holding a de-fragmented ML element
- * @bss: BSS table entry
- * @type: ML control type
- */
-struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type)
-{
- struct ieee802_11_elems elems;
- const u8 *pos = wpa_bss_ie_ptr(bss);
- size_t len = bss->ie_len;
-
- if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
- return NULL;
-
- return ieee802_11_defrag_mle(&elems, type);
-}
-
-
static void
wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u8 mbssid_idx,
const struct ieee80211_neighbor_ap_info *ap_info,
- size_t len, u16 *seen, u16 *missing)
+ size_t len, u16 *seen, u16 *missing,
+ struct wpa_ssid *ssid)
{
const u8 *pos, *end;
const u8 *mld_params;
@@ -1542,38 +1538,52 @@
pos += sizeof(*ap_info);
for (i = 0; i < count; i++) {
- if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
- return;
+ u8 bss_params;
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;
if (*mld_params != mbssid_idx) {
wpa_printf(MSG_DEBUG,
"MLD: Reported link not part of MLD");
} else if (!(BIT(link_id) & *seen)) {
- struct wpa_bss *neigh_bss =
- wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
+ 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);
*seen |= BIT(link_id);
wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
*mld_params, link_id);
- if (neigh_bss) {
+ 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;
- l = &bss->mld_links[bss->n_mld_links];
- l->link_id = link_id;
- os_memcpy(l->bssid, ap_info->data + 1,
- ETH_ALEN);
+ 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;
- bss->n_mld_links++;
- } else {
- *missing |= BIT(link_id);
+ l->disabled = mld_params[2] &
+ RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
}
}
@@ -1590,6 +1600,8 @@
* @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
@@ -1600,13 +1612,15 @@
int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
u8 *ap_mld_addr,
- u16 *missing_links)
+ u16 *missing_links,
+ struct wpa_ssid *ssid,
+ u8 *ap_mld_id)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf;
const struct element *elem;
u8 mbssid_idx = 0;
- u8 ml_ie_len;
+ 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;
@@ -1633,7 +1647,7 @@
return ret;
}
- mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ 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;
@@ -1641,6 +1655,44 @@
ml_ie_len = wpabuf_len(mlbuf);
+ if (ssid) {
+ struct wpa_ie_data ie;
+ const u8 *rsne;
+ size_t rsne_len;
+
+ if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s)) {
+ rsne = elems.rsne_override_2;
+ rsne_len = elems.rsne_override_2_len;
+ } else if (elems.rsne_override &&
+ wpas_rsn_overriding(wpa_s)) {
+ rsne = elems.rsne_override;
+ rsne_len = elems.rsne_override_len;
+ } else {
+ rsne = elems.rsn_ie;
+ rsne_len = elems.rsn_ie_len;
+ }
+ if (!rsne ||
+ wpa_parse_wpa_ie(rsne - 2, 2 + rsne_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
@@ -1669,15 +1721,15 @@
os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
ETH_ALEN);
- bss->n_mld_links = 0;
- l = &bss->mld_links[bss->n_mld_links];
link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
- l->link_id = link_id;
+
+ bss->mld_link_id = link_id;
+ seen = bss->valid_links = BIT(link_id);
+
+ l = &bss->mld_links[link_id];
os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
l->freq = bss->freq;
- seen = BIT(link_id);
- bss->n_mld_links++;
/*
* The AP MLD ID in the RNR corresponds to the MBSSID index, see
@@ -1720,25 +1772,27 @@
wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
ap_info, len, &seen,
- &missing);
+ &missing, ssid);
pos += ap_info_len;
len -= ap_info_len;
}
}
- wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
- bss->n_mld_links, missing);
+ wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
+ bss->valid_links, missing);
- for (i = 0; i < bss->n_mld_links; i++) {
+ for_each_link(bss->valid_links, i) {
wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
- bss->mld_links[i].link_id,
- MAC2STR(bss->mld_links[i].bssid));
+ 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);
@@ -1769,7 +1823,7 @@
if (!elems.reconf_mle || !elems.reconf_mle_len)
return 0;
- mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_RECONF);
+ mlbuf = ieee802_11_defrag(elems.reconf_mle, elems.reconf_mle_len, true);
if (!mlbuf)
return 0;
@@ -1824,3 +1878,201 @@
wpabuf_free(mlbuf);
return removed_links;
}
+
+
+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;
+}
+
+
+const u8 * wpa_bss_get_rsne(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)) {
+ 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;
+ }
+ }
+
+ 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)) {
+ 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/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 042b5d2..508129c 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -126,13 +126,17 @@
size_t beacon_ie_len;
/** MLD address of the AP */
u8 mld_addr[ETH_ALEN];
+ /** Link ID of this affiliated AP of the AP MLD */
+ u8 mld_link_id;
/** An array of MLD links */
- u8 n_mld_links;
+ u16 valid_links;
struct mld_link {
- u8 link_id;
u8 bssid[ETH_ALEN];
int freq;
+
+ /* Whether the link is valid but currently disabled */
+ bool disabled;
} mld_links[MAX_NUM_MLD_LINKS];
/* followed by ie_len octets of IEs */
@@ -171,7 +175,7 @@
struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
unsigned int idf, unsigned int idl);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
-const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth);
+const u8 * wpa_bss_get_ie_beacon(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
@@ -213,12 +217,20 @@
unsigned int age_ms,
struct os_reltime *update_time);
-struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type);
int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
u8 *ap_mld_addr,
- u16 *missing_links);
+ u16 *missing_links,
+ struct wpa_ssid *ssid,
+ u8 *ap_mld_id);
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/wpa_supplicant/bssid_ignore.c b/wpa_supplicant/bssid_ignore.c
index e378577..ab16fc5 100644
--- a/wpa_supplicant/bssid_ignore.c
+++ b/wpa_supplicant/bssid_ignore.c
@@ -37,7 +37,7 @@
e = wpa_s->bssid_ignore;
while (e) {
- if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(e->bssid, bssid))
return e;
e = e->next;
}
@@ -85,9 +85,9 @@
e->timeout_secs = 60;
else
e->timeout_secs = 10;
- wpa_printf(MSG_INFO, "BSSID " MACSTR
- " ignore list count incremented to %d, ignoring for %d seconds",
- MAC2STR(bssid), e->count, e->timeout_secs);
+ wpa_msg(wpa_s, MSG_INFO, "BSSID " MACSTR
+ " ignore list count incremented to %d, ignoring for %d seconds",
+ MAC2STR(bssid), e->count, e->timeout_secs);
return e->count;
}
@@ -100,9 +100,9 @@
e->start = now;
e->next = wpa_s->bssid_ignore;
wpa_s->bssid_ignore = e;
- wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR
- " into ignore list, ignoring for %d seconds",
- MAC2STR(bssid), e->timeout_secs);
+ wpa_msg(wpa_s, MSG_INFO, "Added BSSID " MACSTR
+ " into ignore list, ignoring for %d seconds",
+ MAC2STR(bssid), e->timeout_secs);
return e->count;
}
@@ -123,14 +123,14 @@
e = wpa_s->bssid_ignore;
while (e) {
- if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+ if (ether_addr_equal(e->bssid, bssid)) {
if (prev == NULL) {
wpa_s->bssid_ignore = e->next;
} else {
prev->next = e->next;
}
- wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
- " from ignore list", MAC2STR(bssid));
+ wpa_msg(wpa_s, MSG_INFO, "Removed BSSID " MACSTR
+ " from ignore list", MAC2STR(bssid));
os_free(e);
return 0;
}
@@ -175,8 +175,8 @@
while (e) {
prev = e;
e = e->next;
- wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
- " from ignore list (clear)", MAC2STR(prev->bssid));
+ wpa_msg(wpa_s, MSG_INFO, "Removed BSSID " MACSTR
+ " from ignore list (clear)", MAC2STR(prev->bssid));
os_free(prev);
}
}
@@ -209,9 +209,9 @@
wpa_s->bssid_ignore = e->next;
e = wpa_s->bssid_ignore;
}
- wpa_printf(MSG_INFO, "Removed BSSID " MACSTR
- " from ignore list (expired)",
- MAC2STR(to_delete->bssid));
+ wpa_msg(wpa_s, MSG_INFO, "Removed BSSID " MACSTR
+ " from ignore list (expired)",
+ MAC2STR(to_delete->bssid));
os_free(to_delete);
} else {
prev = e;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index d1d8ca3..0f98f77 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -197,9 +197,10 @@
#endif /* NO_CONFIG_WRITE */
-static int wpa_config_parse_int(const struct parse_data *data,
- struct wpa_ssid *ssid,
- int line, const char *value)
+static int wpa_config_parse_int_impl(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value,
+ bool check_range)
{
int val, *dst;
char *end;
@@ -212,31 +213,46 @@
return -1;
}
+ if (check_range && val < (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, val,
+ (long) data->param3);
+ return -1;
+ }
+
+ if (check_range && val > (long) data->param4) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, val,
+ (long) data->param4);
+ return -1;
+ }
+
if (*dst == val)
return 1;
+
*dst = val;
wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
- if (data->param3 && *dst < (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
- if (data->param4 && *dst > (long) data->param4) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param4);
- *dst = (long) data->param4;
- return -1;
- }
-
return 0;
}
+static int wpa_config_parse_int(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ return wpa_config_parse_int_impl(data, ssid, line, value, false);
+}
+
+
+static int wpa_config_parse_int_range(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ return wpa_config_parse_int_impl(data, ssid, line, value, true);
+}
+
+
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_int(const struct parse_data *data,
struct wpa_ssid *ssid)
@@ -2368,7 +2384,7 @@
u8 mac_value[ETH_ALEN];
if (hwaddr_aton(value, mac_value) == 0) {
- if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
+ if (ether_addr_equal(mac_value, ssid->mac_value))
return 1;
os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
return 0;
@@ -2457,7 +2473,14 @@
#define INTe(f, m) _INTe(f, m), NULL, NULL, 0
/* INT_RANGE: Define an integer variable with allowed value range */
-#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+#ifdef NO_CONFIG_WRITE
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, OFFSET(f), \
+ (void *) 0, (void *) (min), (void *) (max), 0
+#else /* NO_CONFIG_WRITE */
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, \
+ wpa_config_write_int, OFFSET(f), \
+ (void *) 0, (void *) (min), (void *) (max), 0
+#endif /* NO_CONFIG_WRITE */
/* FUNC: Define a configuration variable that uses a custom function for
* parsing and writing the value. */
@@ -2652,7 +2675,7 @@
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
{ INT_RANGE(disable_ht, 0, 1) },
- { INT_RANGE(disable_ht40, -1, 1) },
+ { INT_RANGE(disable_ht40, 0, 1) },
{ INT_RANGE(disable_sgi, 0, 1) },
{ INT_RANGE(disable_ldpc, 0, 1) },
{ INT_RANGE(ht40_intolerant, 0, 1) },
@@ -2725,11 +2748,15 @@
{ INT_RANGE(owe_ptk_workaround, 0, 1) },
{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
+ { INT_RANGE(multi_ap_profile, MULTI_AP_PROFILE_1,
+ MULTI_AP_PROFILE_MAX) },
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
{ 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)},
};
#undef OFFSET
@@ -4681,6 +4708,10 @@
config->driver_param = os_strdup(driver_param);
config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+#ifdef CONFIG_TESTING_OPTIONS
+ config->mld_connect_band_pref = DEFAULT_MLD_CONNECT_BAND_PREF;
+#endif /* CONFIG_TESTING_OPTIONS */
+
return config;
}
@@ -4739,9 +4770,10 @@
};
-static int wpa_global_config_parse_int(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
+static int
+wpa_global_config_parse_int_impl(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos, bool check_range)
{
int val, *dst;
char *end;
@@ -4754,31 +4786,47 @@
line, pos);
return -1;
}
+
+ if (check_range && val < (long) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, val,
+ (long) data->param2);
+ return -1;
+ }
+
+ if (check_range && val > (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, val,
+ (long) data->param3);
+ return -1;
+ }
+
same = *dst == val;
*dst = val;
wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
- if (data->param2 && *dst < (long) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param2);
- *dst = (long) data->param2;
- return -1;
- }
-
- if (data->param3 && *dst > (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
return same;
}
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ return wpa_global_config_parse_int_impl(data, config, line, pos, false);
+}
+
+
+static int
+wpa_global_config_parse_int_range(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ return wpa_global_config_parse_int_impl(data, config, line, pos, true);
+}
+
+
static int wpa_global_config_parse_str(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
@@ -5315,6 +5363,23 @@
#endif /* CONFIG_P2P */
+#ifdef CONFIG_TESTING_OPTIONS
+static int wpa_config_process_mld_connect_bssid_pref(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ if (hwaddr_aton2(pos, config->mld_connect_bssid_pref) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid mld_connect_bssid_pref '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
#ifdef OFFSET
#undef OFFSET
#endif /* OFFSET */
@@ -5325,7 +5390,8 @@
#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) _INT(f), (void *) min, (void *) max
+#define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \
+ wpa_config_get_int, OFFSET(f), (void *) min, (void *) max
#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
@@ -5525,12 +5591,22 @@
{ 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
#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 },
+ { 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 },
+ /* 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
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 09bcbc8..7ea43a6 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -48,6 +48,7 @@
#define DEFAULT_EXTENDED_KEY_ID 0
#define DEFAULT_BTM_OFFLOAD 0
#define DEFAULT_SCAN_RES_VALID_FOR_CONNECT 5
+#define DEFAULT_MLD_CONNECT_BAND_PREF MLD_CONNECT_BAND_PREF_AUTO
#include "config_ssid.h"
#include "wps/wps.h"
@@ -451,7 +452,8 @@
#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
#define CFG_CHANGED_DISABLE_BTM BIT(19)
#define CFG_CHANGED_BGSCAN BIT(20)
-#define CFG_CHANGED_DISABLE_BTM_NOTIFY BIT(21)
+#define CFG_CHANGED_FT_PREPEND_PMKID BIT(21)
+#define CFG_CHANGED_DISABLE_BTM_NOTIFY BIT(22)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -726,6 +728,14 @@
unsigned int dot11RSNAConfigSATimeout;
/**
+ * ft_prepend_pmkid - Whether to prepend PMKR1Name with PMKIDs
+ *
+ * This control whether PMKR1Name is prepended to the PMKID list
+ * insread of replacing the full list when constructing RSNE for
+ * EAPOL-Key msg 2/4 for FT cases. */
+ bool ft_prepend_pmkid;
+
+ /**
* update_config - Is wpa_supplicant allowed to update configuration
*
* This variable control whether wpa_supplicant is allow to re-write
@@ -1793,6 +1803,19 @@
*/
int wowlan_disconnect_on_deinit;
+ /**
+ * 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 rsn_overriding {
+ RSN_OVERRIDING_DISABLED = 0,
+ RSN_OVERRIDING_AUTO = 1,
+ RSN_OVERRIDING_ENABLED = 2,
+ } rsn_overriding;
+
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
/*
@@ -1806,6 +1829,20 @@
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN*/
+
+#ifdef CONFIG_TESTING_OPTIONS
+ enum {
+ MLD_CONNECT_BAND_PREF_AUTO = 0,
+ MLD_CONNECT_BAND_PREF_2GHZ = 1,
+ MLD_CONNECT_BAND_PREF_5GHZ = 2,
+ MLD_CONNECT_BAND_PREF_6GHZ = 3,
+ MLD_CONNECT_BAND_PREF_MAX = 4,
+ } mld_connect_band_pref;
+
+ u8 mld_connect_bssid_pref[ETH_ALEN];
+
+ int mld_force_single_link;
+#endif /* CONFIG_TESTING_OPTIONS */
};
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index ebad5f1..fa829eb 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -850,6 +850,7 @@
INT(owe_ptk_workaround);
INT(multi_ap_backhaul_sta);
INT(ft_eap_pmksa_caching);
+ INT(multi_ap_profile);
INT(beacon_prot);
INT(transition_disable);
INT(sae_pk);
@@ -892,6 +893,8 @@
#endif /* CONFIG_HE_OVERRIDES */
INT(disable_eht);
INT(enable_4addr_mode);
+ INT(max_idle);
+ INT(ssid_protection);
#undef STR
#undef INT
@@ -1618,6 +1621,20 @@
if (config->wowlan_disconnect_on_deinit)
fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
config->wowlan_disconnect_on_deinit);
+ if (config->rsn_overriding)
+ fprintf(f, "rsn_overriding=%d\n", config->rsn_overriding);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (config->mld_force_single_link)
+ fprintf(f, "mld_force_single_link=1\n");
+ if (config->mld_connect_band_pref != MLD_CONNECT_BAND_PREF_AUTO)
+ fprintf(f, "mld_connect_band_pref=%d\n",
+ config->mld_connect_band_pref);
+ if (!is_zero_ether_addr(config->mld_connect_bssid_pref))
+ fprintf(f, "mld_connect_bssid_pref=" MACSTR "\n",
+ 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);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 785acc3..3d1bc5e 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1187,6 +1187,11 @@
int ft_eap_pmksa_caching;
/**
+ * multi_ap_profile - Supported Multi-AP profile
+ */
+ int multi_ap_profile;
+
+ /**
* beacon_prot - Whether Beacon protection is enabled
*
* This depends on management frame protection (ieee80211w) being
@@ -1264,6 +1269,19 @@
* to use the interface in a bridge.
*/
int enable_4addr_mode;
+
+ /**
+ * max_idle - BSS max idle period to request
+ *
+ * If nonzero, request the specified number of 1000 TU (i.e., 1.024 s)
+ * as the maximum idle period for the STA during association.
+ */
+ int max_idle;
+
+ /**
+ * ssid_protection - Whether to use SSID protection in 4-way handshake
+ */
+ bool ssid_protection;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a68802e..d245531 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -22,6 +22,7 @@
#ifdef CONFIG_DPP
#include "common/dpp.h"
#endif /* CONFIG_DPP */
+#include "common/nan_de.h"
#include "common/ptksa_cache.h"
#include "crypto/tls.h"
#include "ap/hostapd.h"
@@ -58,6 +59,7 @@
#include "mesh.h"
#include "dpp_supplicant.h"
#include "sme.h"
+#include "nan_usd.h"
#ifdef __NetBSD__
#include <net/if_ether.h>
@@ -445,7 +447,7 @@
dl_list_for_each(tmp, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
- if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+ if (ether_addr_equal(bssid, tmp->bssid)) {
dso = tmp;
break;
}
@@ -739,6 +741,12 @@
wpa_s->ext_eapol_frame_io;
}
#endif /* CONFIG_AP */
+ } else if (os_strcasecmp(cmd, "encrypt_eapol_m2") == 0) {
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2,
+ !!atoi(value));
+ } else if (os_strcasecmp(cmd, "encrypt_eapol_m4") == 0) {
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4,
+ !!atoi(value));
} else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
wpa_s->extra_roc_dur = atoi(value);
} else if (os_strcasecmp(cmd, "test_failure") == 0) {
@@ -833,15 +841,19 @@
wpa_s->sae_commit_override = wpabuf_parse_bin(value);
} else if (os_strcasecmp(cmd, "driver_signal_override") == 0) {
ret = wpas_ctrl_iface_set_dso(wpa_s, value);
+#ifndef CONFIG_NO_ROBUST_AV
} else if (os_strcasecmp(cmd, "disable_scs_support") == 0) {
wpa_s->disable_scs_support = !!atoi(value);
} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
wpa_s->disable_mscs_support = !!atoi(value);
+#endif /* CONFIG_NO_ROBUST_AV */
} else if (os_strcasecmp(cmd, "disable_eapol_g2_tx") == 0) {
wpa_s->disable_eapol_g2_tx = !!atoi(value);
/* Populate value to wpa_sm if already associated. */
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX,
wpa_s->disable_eapol_g2_tx);
+ } else if (os_strcasecmp(cmd, "test_assoc_comeback_type") == 0) {
+ wpa_s->test_assoc_comeback_type = atoi(value);
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
os_free(wpa_s->dpp_config_obj_override);
@@ -890,9 +902,20 @@
wpa_config_process_global(wpa_s->conf, cmd, -1);
}
} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
- wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+ int val = atoi(value);
+
+ if (val < MBO_CELL_CAPA_AVAILABLE ||
+ val > MBO_CELL_CAPA_NOT_SUPPORTED)
+ return -1;
+
+ wpas_mbo_update_cell_capa(wpa_s, val);
} else if (os_strcasecmp(cmd, "oce") == 0) {
- wpa_s->conf->oce = atoi(value);
+ int val = atoi(value);
+
+ if (val < 0 || val > 3)
+ return -1;
+
+ wpa_s->conf->oce = val;
if (wpa_s->conf->oce) {
if ((wpa_s->conf->oce & OCE_STA) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
@@ -932,8 +955,10 @@
return -1;
wnm_set_coloc_intf_elems(wpa_s, elems);
#endif /* CONFIG_WNM */
+#ifndef CONFIG_NO_ROBUST_AV
} else if (os_strcasecmp(cmd, "enable_dscp_policy_capa") == 0) {
wpa_s->enable_dscp_policy_capa = !!atoi(value);
+#endif /* CONFIG_NO_ROBUST_AV */
} else {
value[-1] = '=';
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -1260,6 +1285,8 @@
#endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_WMM_AC
+
static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
{
char *token, *context = NULL;
@@ -1309,6 +1336,8 @@
return wpas_wmm_ac_delts(wpa_s, tsid);
}
+#endif /* CONFIG_NO_WMM_AC */
+
#ifdef CONFIG_IEEE80211R
static int wpa_supplicant_ctrl_iface_ft_ds(
@@ -2529,6 +2558,30 @@
pos += ret;
}
+#ifdef CONFIG_SME
+ if (wpa_s->sme.bss_max_idle_period) {
+ ret = os_snprintf(pos, end - pos, "bss_max_idle_period=%d\n",
+ wpa_s->sme.bss_max_idle_period);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SME */
+
+ if (wpa_s->ssid_verified) {
+ ret = os_snprintf(pos, end - pos, "ssid_verified=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (wpa_s->bigtk_set) {
+ ret = os_snprintf(pos, end - pos, "bigtk_set=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
#ifdef ANDROID
/*
* Allow using the STATUS command with default behavior, say for debug,
@@ -3066,12 +3119,12 @@
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))
@@ -3618,7 +3671,7 @@
#endif /* CONFIG_BGSCAN */
if (os_strcmp(name, "bssid") != 0 &&
- os_strcmp(name, "bssid_hint") != 0 &&
+ os_strncmp(name, "bssid_", 6) != 0 &&
os_strcmp(name, "scan_freq") != 0 &&
os_strcmp(name, "priority") != 0) {
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
@@ -3683,7 +3736,7 @@
value);
if (ret == 0 &&
(ssid->bssid_set != prev_bssid_set ||
- os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
+ !ether_addr_equal(ssid->bssid, prev_bssid)))
wpas_notify_network_bssid_set_changed(wpa_s, ssid);
if (prev_disabled != ssid->disabled &&
@@ -4859,6 +4912,15 @@
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (os_strcmp(field, "nan") == 0) {
+ res = os_snprintf(buf, buflen, "USD");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_NAN_USD */
+
#ifdef CONFIG_SAE
if (os_strcmp(field, "sae") == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
@@ -4954,7 +5016,7 @@
ie_end = ie + 2 + ie[1];
ie += 2;
if (ie_end - ie < 2)
- return -1;
+ return 0;
info = WPA_GET_LE16(ie);
ie += 2;
@@ -4966,7 +5028,7 @@
if (info & BIT(7)) {
/* Cache Identifier Included */
if (ie_end - ie < 2)
- return -1;
+ return 0;
ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n",
ie[0], ie[1]);
if (os_snprintf_error(end - pos, ret))
@@ -4978,7 +5040,7 @@
if (info & BIT(8)) {
/* HESSID Included */
if (ie_end - ie < ETH_ALEN)
- return -1;
+ return 0;
ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n",
MAC2STR(ie));
if (os_snprintf_error(end - pos, ret))
@@ -4990,7 +5052,7 @@
realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3;
if (realms) {
if (ie_end - ie < realms * 2)
- return -1;
+ return 0;
ret = os_snprintf(pos, end - pos, "fils_realms=");
if (os_snprintf_error(end - pos, ret))
return 0;
@@ -5244,7 +5306,7 @@
if (common_info_length < 1)
return 0;
- ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x\n", *ie);
+ ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x", *ie);
if (os_snprintf_error(end - pos, ret))
return 0;
pos += ret;
@@ -5252,6 +5314,11 @@
common_info_length--;
}
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
return pos - start;
}
@@ -5377,22 +5444,22 @@
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))
- return -1;
+ return 0;
pos += ret;
}
if (ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SAE_PK)) {
ret = os_snprintf(pos, end - pos, "[SAE-PK]");
if (os_snprintf_error(end - pos, ret))
- return -1;
+ return 0;
pos += ret;
}
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
@@ -5691,8 +5758,6 @@
#ifdef CONFIG_FILS
if (mask & WPA_BSS_MASK_FILS_INDICATION) {
ret = print_fils_indication(bss, pos, end);
- if (ret < 0)
- return 0;
pos += ret;
}
#endif /* CONFIG_FILS */
@@ -6289,32 +6354,6 @@
}
-static int parse_freq(int chwidth, int freq2)
-{
- if (freq2 < 0)
- return -1;
- if (freq2)
- return CONF_OPER_CHWIDTH_80P80MHZ;
-
- switch (chwidth) {
- case 0:
- case 20:
- case 40:
- return CONF_OPER_CHWIDTH_USE_HT;
- case 80:
- return CONF_OPER_CHWIDTH_80MHZ;
- case 160:
- return CONF_OPER_CHWIDTH_160MHZ;
- case 320:
- return CONF_OPER_CHWIDTH_320MHZ;
- default:
- wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
- chwidth);
- return -1;
- }
-}
-
-
static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
@@ -6408,7 +6447,7 @@
if (pos2)
chwidth = atoi(pos2 + 18);
- max_oper_chwidth = parse_freq(chwidth, freq2);
+ max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
@@ -6701,6 +6740,7 @@
char *pos;
size_t len;
struct wpabuf *query, *resp;
+ int ret;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
@@ -6714,34 +6754,28 @@
query = wpabuf_alloc(len);
if (query == NULL)
return -1;
- if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
- wpabuf_free(query);
- return -1;
- }
-
+ ret = hexstr2bin(cmd, wpabuf_put(query, len), len);
+ if (ret < 0)
+ goto err_query;
+ ret = -1;
len = os_strlen(pos);
- if (len & 1) {
- wpabuf_free(query);
- return -1;
- }
+ if (len & 1)
+ goto err_query;
len /= 2;
resp = wpabuf_alloc(len);
- if (resp == NULL) {
- wpabuf_free(query);
- return -1;
- }
- if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
- wpabuf_free(query);
- wpabuf_free(resp);
- return -1;
- }
+ if (!resp)
+ goto err_query;
+ ret = hexstr2bin(pos, wpabuf_put(resp, len), len);
+ if (ret < 0)
+ goto err_resp;
- if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
- wpabuf_free(query);
- wpabuf_free(resp);
- return -1;
- }
- return 0;
+ ret = wpas_p2p_service_add_bonjour(wpa_s, query, resp);
+
+err_resp:
+ wpabuf_free(resp);
+err_query:
+ wpabuf_free(query);
+ return ret;
}
@@ -7062,7 +7096,7 @@
if (pos)
chwidth = atoi(pos + 18);
- max_oper_chwidth = parse_freq(chwidth, freq2);
+ max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
@@ -7138,8 +7172,8 @@
return -1;
}
- return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
- vht_center_freq2, 0, ht40, vht,
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
+ vht_center_freq2, ht40, vht,
vht_chwidth, he, edmg,
NULL, 0, 0, allow_6ghz, 0,
go_bssid);
@@ -7217,7 +7251,7 @@
}
#endif /* CONFIG_ACS */
- max_oper_chwidth = parse_freq(chwidth, freq2);
+ max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
@@ -7837,7 +7871,7 @@
dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
list) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(bss->bssid, bssid) &&
bss->ssid_len > 0) {
found = 1;
break;
@@ -8004,11 +8038,11 @@
dialog_token = atoi(pos);
if (wpa_s->last_gas_resp &&
- os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+ ether_addr_equal(addr, wpa_s->last_gas_addr) &&
dialog_token == wpa_s->last_gas_dialog_token)
resp = wpa_s->last_gas_resp;
else if (wpa_s->prev_gas_resp &&
- os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+ ether_addr_equal(addr, wpa_s->prev_gas_addr) &&
dialog_token == wpa_s->prev_gas_dialog_token)
resp = wpa_s->prev_gas_resp;
else
@@ -8846,6 +8880,8 @@
wpa_s->next_ssid = NULL;
+ wnm_btm_reset(wpa_s);
+
#ifdef CONFIG_INTERWORKING
#ifdef CONFIG_HS20
hs20_cancel_fetch_osu(wpa_s);
@@ -8867,6 +8903,10 @@
wpa_s->ft_rsnxe_used = 0;
wpa_s->reject_btm_req_reason = 0;
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_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);
wpa_s->get_pref_freq_list_override = NULL;
wpabuf_free(wpa_s->sae_commit_override);
@@ -8880,9 +8920,11 @@
wpabuf_free(wpa_s->rsnxe_override_eapol);
wpa_s->rsnxe_override_eapol = NULL;
wpas_clear_driver_signal_override(wpa_s);
+#ifndef CONFIG_NO_ROBUST_AV
wpa_s->disable_scs_support = 0;
wpa_s->disable_mscs_support = 0;
wpa_s->enable_dscp_policy_capa = 0;
+#endif /* CONFIG_NO_ROBUST_AV */
wpa_s->oci_freq_override_eapol = 0;
wpa_s->oci_freq_override_saquery_req = 0;
wpa_s->oci_freq_override_saquery_resp = 0;
@@ -8891,6 +8933,7 @@
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->test_assoc_comeback_type = -1;
#ifdef CONFIG_DPP
os_free(wpa_s->dpp_config_obj_override);
wpa_s->dpp_config_obj_override = NULL;
@@ -8911,7 +8954,9 @@
wpa_s->next_scan_bssid_wildcard_ssid = 0;
os_free(wpa_s->select_network_scan_freqs);
wpa_s->select_network_scan_freqs = NULL;
+#ifndef CONFIG_NO_ROBUST_AV
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
+#endif /* CONFIG_NO_ROBUST_AV */
wpa_bss_flush(wpa_s);
if (!dl_list_empty(&wpa_s->bss)) {
@@ -8938,7 +8983,9 @@
free_bss_tmp_disallowed(wpa_s);
+#ifndef CONFIG_NO_ROBUST_AV
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
+#endif /* CONFIG_NO_ROBUST_AV */
#ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s);
@@ -8948,6 +8995,10 @@
wpas_restore_permanent_mac_addr(wpa_s);
wpa_s->conf->ignore_old_scan_res = 0;
+
+#ifdef CONFIG_NAN_USD
+ wpas_nan_usd_flush(wpa_s);
+#endif /* CONFIG_NAN_USD */
}
@@ -10134,72 +10185,6 @@
}
-static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
-{
-#ifdef WPA_TRACE_BFD
- char *pos;
-
- wpa_trace_fail_after = atoi(cmd);
- pos = os_strchr(cmd, ':');
- if (pos) {
- pos++;
- os_strlcpy(wpa_trace_fail_func, pos,
- sizeof(wpa_trace_fail_func));
- } else {
- wpa_trace_fail_after = 0;
- }
- return 0;
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
- char *buf, size_t buflen)
-{
-#ifdef WPA_TRACE_BFD
- return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
- wpa_trace_fail_func);
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
-{
-#ifdef WPA_TRACE_BFD
- char *pos;
-
- wpa_trace_test_fail_after = atoi(cmd);
- pos = os_strchr(cmd, ':');
- if (pos) {
- pos++;
- os_strlcpy(wpa_trace_test_fail_func, pos,
- sizeof(wpa_trace_test_fail_func));
- } else {
- wpa_trace_test_fail_after = 0;
- }
- return 0;
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
-static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
- char *buf, size_t buflen)
-{
-#ifdef WPA_TRACE_BFD
- return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
- wpa_trace_test_fail_func);
-#else /* WPA_TRACE_BFD */
- return -1;
-#endif /* WPA_TRACE_BFD */
-}
-
-
static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -10227,13 +10212,12 @@
}
-static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
- const char *cmd)
+static int wpas_get_hex_buf(const char *val, struct wpabuf **ret)
{
struct wpabuf *buf;
size_t len;
- len = os_strlen(cmd);
+ len = os_strlen(val);
if (len & 1)
return -1;
len /= 2;
@@ -10242,20 +10226,56 @@
buf = NULL;
} else {
buf = wpabuf_alloc(len);
- if (buf == NULL)
+ if (!buf)
return -1;
- if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+ if (hexstr2bin(val, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
}
+ *ret = buf;
+ return 0;
+}
+
+
+static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ struct wpabuf *buf;
+
+ if (wpas_get_hex_buf(cmd, &buf) < 0)
+ return -1;
wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf);
return 0;
}
+static int wpas_ctrl_test_eapol_m2_elems(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ struct wpabuf *buf;
+
+ if (wpas_get_hex_buf(cmd, &buf) < 0)
+ return -1;
+ wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, buf);
+ return 0;
+}
+
+
+static int wpas_ctrl_test_eapol_m4_elems(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ struct wpabuf *buf;
+
+ if (wpas_get_hex_buf(cmd, &buf) < 0)
+ return -1;
+ wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, buf);
+ return 0;
+}
+
+
static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
{
u8 zero[WPA_TK_MAX_LEN];
@@ -10571,6 +10591,8 @@
}
+#ifndef CONFIG_NO_RRM
+
static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -10714,6 +10736,8 @@
return ret;
}
+#endif /* CONFIG_NO_RRM */
+
static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
{
@@ -11067,6 +11091,7 @@
}
+#ifndef CONFIG_NO_ROBUST_AV
static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s,
const char *cmd)
{
@@ -11135,6 +11160,7 @@
return wpas_send_mscs_req(wpa_s);
}
+#endif /* CONFIG_NO_ROBUST_AV */
#ifdef CONFIG_PASN
@@ -11236,9 +11262,55 @@
return wpas_pasn_deauthenticate(wpa_s, wpa_s->own_addr, bssid);
}
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int wpas_ctrl_iface_pasn_driver(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ union wpa_event_data event;
+ const char *pos = cmd;
+ u8 addr[ETH_ALEN];
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (os_strncmp(pos, "auth ", 5) == 0)
+ event.pasn_auth.action = PASN_ACTION_AUTH;
+ else if (os_strncmp(pos, "del ", 4) == 0)
+ event.pasn_auth.action =
+ PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
+ else
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ 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++;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ break;
+ pos++;
+ }
+
+ wpa_supplicant_event(wpa_s, EVENT_PASN_AUTH, &event);
+ return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
#endif /* CONFIG_PASN */
+#ifndef CONFIG_NO_ROBUST_AV
+
static int set_type4_frame_classifier(const char *cmd,
struct type4_params *param)
{
@@ -11935,6 +12007,8 @@
return wpas_send_dscp_query(wpa_s, pos + 12, os_strlen(pos + 12));
}
+#endif /* CONFIG_NO_ROBUST_AV */
+
static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
@@ -11953,10 +12027,7 @@
pos = buf;
end = buf + buflen;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo_si.valid_links & BIT(i)))
- continue;
-
+ for_each_link(mlo_si.valid_links, i) {
ret = os_snprintf(pos, end - pos,
"LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
"NOISE=%d\nFREQUENCY=%u\n",
@@ -12028,10 +12099,7 @@
pos = buf;
end = buf + buflen;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
"ap_link_addr=" MACSTR
"\nsta_link_addr=" MACSTR "\n",
@@ -12069,7 +12137,7 @@
}
}
- if (mld_id < 0 || is_zero_ether_addr(bssid)) {
+ if (is_zero_ether_addr(bssid)) {
wpa_printf(MSG_DEBUG,
"MLD: Failed parsing ML probe request arguments");
return -1;
@@ -12121,6 +12189,327 @@
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_NAN_USD
+
+static int wpas_ctrl_nan_publish(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ char *token, *context = NULL;
+ int publish_id;
+ struct nan_publish_params params;
+ const char *service_name = NULL;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+ enum nan_service_protocol_type srv_proto_type = 0;
+ int *freq_list = 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;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "service_name=", 13) == 0) {
+ service_name = token + 13;
+ continue;
+ }
+
+ if (os_strncmp(token, "ttl=", 4) == 0) {
+ params.ttl = atoi(token + 4);
+ continue;
+ }
+
+ if (os_strncmp(token, "freq=", 5) == 0) {
+ params.freq = atoi(token + 5);
+ 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;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+
+ if (os_strcmp(token, "solicited=0") == 0) {
+ params.solicited = false;
+ continue;
+ }
+
+ if (os_strcmp(token, "unsolicited=0") == 0) {
+ params.unsolicited = false;
+ continue;
+ }
+
+ if (os_strcmp(token, "fsd=0") == 0) {
+ params.fsd = false;
+ 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);
+ if (publish_id > 0)
+ ret = os_snprintf(buf, buflen, "%d", publish_id);
+fail:
+ wpabuf_free(ssi);
+ os_free(freq_list);
+ return ret;
+}
+
+
+static int wpas_ctrl_nan_cancel_publish(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int publish_id = 0;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "publish_id=%i", &publish_id) == 1)
+ continue;
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_CANCEL_PUBLISH parameter: %s",
+ token);
+ return -1;
+ }
+
+ if (publish_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_CANCEL_PUBLISH publish_id");
+ return -1;
+ }
+
+ wpas_nan_usd_cancel_publish(wpa_s, publish_id);
+ return 0;
+}
+
+
+static int wpas_ctrl_nan_update_publish(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int publish_id = 0;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "publish_id=%i", &publish_id) == 1)
+ continue;
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_UPDATE_PUBLISH parameter: %s",
+ token);
+ goto fail;
+ }
+
+ if (publish_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_UPDATE_PUBLISH publish_id");
+ goto fail;
+ }
+
+ ret = wpas_nan_usd_update_publish(wpa_s, publish_id, ssi);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+
+static int wpas_ctrl_nan_subscribe(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ char *token, *context = NULL;
+ int subscribe_id;
+ struct nan_subscribe_params params;
+ const char *service_name = NULL;
+ struct wpabuf *ssi = NULL;
+ int ret = -1;
+ enum nan_service_protocol_type srv_proto_type = 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.freq = NAN_USD_DEFAULT_FREQ;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (os_strncmp(token, "service_name=", 13) == 0) {
+ service_name = token + 13;
+ continue;
+ }
+
+ if (os_strcmp(token, "active=1") == 0) {
+ params.active = true;
+ continue;
+ }
+
+ if (os_strncmp(token, "ttl=", 4) == 0) {
+ params.ttl = atoi(token + 4);
+ continue;
+ }
+
+ if (os_strncmp(token, "freq=", 5) == 0) {
+ params.freq = atoi(token + 5);
+ continue;
+ }
+
+ if (os_strncmp(token, "srv_proto_type=", 15) == 0) {
+ srv_proto_type = atoi(token + 15);
+ continue;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ 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);
+ if (subscribe_id > 0)
+ ret = os_snprintf(buf, buflen, "%d", subscribe_id);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+
+static int wpas_ctrl_nan_cancel_subscribe(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *token, *context = NULL;
+ int subscribe_id = 0;
+
+ while ((token = str_token(cmd, " ", &context))) {
+ if (sscanf(token, "subscribe_id=%i", &subscribe_id) == 1)
+ continue;
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_CANCEL_SUBSCRIBE parameter: %s",
+ token);
+ return -1;
+ }
+
+ if (subscribe_id <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_CANCEL_SUBSCRIBE subscribe_id");
+ return -1;
+ }
+
+ wpas_nan_usd_cancel_subscribe(wpa_s, subscribe_id);
+ return 0;
+}
+
+
+static int wpas_ctrl_nan_transmit(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *token, *context = NULL;
+ int handle = 0;
+ int req_instance_id = 0;
+ struct wpabuf *ssi = NULL;
+ u8 peer_addr[ETH_ALEN];
+ int ret = -1;
+
+ os_memset(peer_addr, 0, ETH_ALEN);
+
+ while ((token = str_token(cmd, " ", &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, "address=", 8) == 0) {
+ if (hwaddr_aton(token + 8, peer_addr) < 0)
+ return -1;
+ continue;
+ }
+
+ if (os_strncmp(token, "ssi=", 4) == 0) {
+ if (ssi)
+ goto fail;
+ ssi = wpabuf_parse_bin(token + 4);
+ if (!ssi)
+ goto fail;
+ continue;
+ }
+
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid NAN_TRANSMIT parameter: %s",
+ token);
+ goto fail;
+ }
+
+ if (handle <= 0) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_TRANSMIT handle");
+ goto fail;
+ }
+
+ if (is_zero_ether_addr(peer_addr)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: Invalid or missing NAN_TRANSMIT address");
+ goto fail;
+ }
+
+ ret = wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr,
+ req_instance_id);
+fail:
+ wpabuf_free(ssi);
+ return ret;
+}
+
+#endif /* CONFIG_NAN_USD */
+
+
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
@@ -12792,6 +13181,7 @@
reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
wpa_s, buf + 17, reply, reply_size);
#endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_WMM_AC
} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
@@ -12800,6 +13190,7 @@
} else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
reply_len = -1;
+#endif /* CONFIG_NO_WMM_AC */
} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
reply_size);
@@ -12886,21 +13277,27 @@
if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
- if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+ if (testing_set_fail_pattern(true, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
- reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+ reply_len = testing_get_fail_pattern(true, reply, reply_size);
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
- if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+ if (testing_set_fail_pattern(false, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
- reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
+ reply_len = testing_get_fail_pattern(false, reply, reply_size);
} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "TEST_EAPOL_M2_ELEMS ", 20) == 0) {
+ if (wpas_ctrl_test_eapol_m2_elems(wpa_s, buf + 20) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TEST_EAPOL_M4_ELEMS ", 20) == 0) {
+ if (wpas_ctrl_test_eapol_m4_elems(wpa_s, buf + 20) < 0)
+ reply_len = -1;
} else if (os_strcmp(buf, "RESET_PN") == 0) {
if (wpas_ctrl_reset_pn(wpa_s) < 0)
reply_len = -1;
@@ -12939,9 +13336,11 @@
} else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
reply_len = -1;
+#ifndef CONFIG_NO_RRM
} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20))
reply_len = -1;
+#endif /* CONFIG_NO_RRM */
} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
wpas_ctrl_iface_erp_flush(wpa_s);
} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
@@ -13114,9 +13513,26 @@
reply_len = -1;
#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */
- } else if (os_strncmp(buf, "MSCS ", 5) == 0) {
- if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
+#ifdef 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);
+ } else if (os_strncmp(buf, "NAN_CANCEL_PUBLISH ", 19) == 0) {
+ if (wpas_ctrl_nan_cancel_publish(wpa_s, buf + 19) < 0)
reply_len = -1;
+ } 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_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_TRANSMIT ", 13) == 0) {
+ if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+#endif /* 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)
@@ -13128,7 +13544,16 @@
} 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_TESTING_OPTIONS
+ } else if (os_strncmp(buf, "PASN_DRIVER ", 12) == 0) {
+ if (wpas_ctrl_iface_pasn_driver(wpa_s, buf + 12) < 0)
+ reply_len = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
+#ifndef CONFIG_NO_ROBUST_AV
+ } else if (os_strncmp(buf, "MSCS ", 5) == 0) {
+ if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
+ reply_len = -1;
} else if (os_strncmp(buf, "SCS ", 4) == 0) {
if (wpas_ctrl_iface_configure_scs(wpa_s, buf + 4))
reply_len = -1;
@@ -13138,6 +13563,7 @@
} else if (os_strncmp(buf, "DSCP_QUERY ", 11) == 0) {
if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11))
reply_len = -1;
+#endif /* CONFIG_NO_ROBUST_AV */
} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
reply_size);
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index fdf7b12..27003eb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -706,6 +706,59 @@
}
+#define UINT16_ARRAY_CHUNK_SIZE 18
+#define UINT16_ARRAY_ITEM_SIZE (sizeof(dbus_uint16_t))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_uint16_array(
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+ dbus_uint32_t count = 0;
+ dbus_uint16_t *buffer, *nbuffer;
+
+ entry->uint16array_value = NULL;
+ entry->array_type = DBUS_TYPE_UINT16;
+
+ buffer = os_calloc(UINT16_ARRAY_CHUNK_SIZE, UINT16_ARRAY_ITEM_SIZE);
+ if (!buffer)
+ return FALSE;
+
+ entry->array_len = 0;
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT16) {
+ dbus_uint16_t value;
+
+ if ((count % UINT16_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+ nbuffer = os_realloc_array(
+ buffer, count + UINT16_ARRAY_CHUNK_SIZE,
+ UINT16_ARRAY_ITEM_SIZE);
+ if (!nbuffer) {
+ os_free(buffer);
+ wpa_printf(MSG_ERROR,
+ "dbus: %s out of memory trying to retrieve the uint16 array",
+ __func__);
+ return FALSE;
+ }
+ buffer = nbuffer;
+ }
+
+ dbus_message_iter_get_basic(iter, &value);
+ buffer[count] = value;
+ entry->array_len = ++count;
+ dbus_message_iter_next(iter);
+ }
+ entry->uint16array_value = buffer;
+ wpa_hexdump_key(MSG_MSGDUMP, "dbus: uint16 array contents",
+ entry->bytearray_value, entry->array_len);
+
+ /* Zero-length arrays are valid. */
+ if (entry->array_len == 0) {
+ os_free(entry->uint16array_value);
+ entry->uint16array_value = NULL;
+ }
+
+ return TRUE;
+}
+
+
#define STR_ARRAY_CHUNK_SIZE 8
#define STR_ARRAY_ITEM_SIZE (sizeof(char *))
@@ -873,6 +926,10 @@
success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
entry);
break;
+ case DBUS_TYPE_UINT16:
+ success = _wpa_dbus_dict_entry_get_uint16_array(&iter_array,
+ entry);
+ break;
case DBUS_TYPE_STRING:
success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
array_type,
@@ -1081,6 +1138,9 @@
case DBUS_TYPE_BYTE:
os_free(entry->bytearray_value);
break;
+ case DBUS_TYPE_UINT16:
+ os_free(entry->uint16array_value);
+ break;
case DBUS_TYPE_STRING:
if (!entry->strarray_value)
break;
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index cc9e26f..1d33689 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -139,6 +139,7 @@
dbus_uint64_t uint64_value;
double double_value;
char *bytearray_value;
+ dbus_uint16_t *uint16array_value;
char **strarray_value;
struct wpabuf **binarray_value;
};
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 8fc29b3..76e42ff 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1023,6 +1023,40 @@
dbus_message_unref(msg);
}
+
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *result)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char addr[WPAS_DBUS_OBJECT_PATH_MAX], *bssid;
+
+ os_snprintf(addr, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(dst));
+ bssid = addr;
+
+ 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,
+ "ANQPQueryDone");
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bssid) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &result))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
#endif /* CONFIG_INTERWORKING */
@@ -1629,7 +1663,8 @@
wpa_s->dbus_new_path) ||
!wpa_dbus_dict_append_string(&dict_iter, "role",
client ? "client" : "GO") ||
- !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) ||
+ !wpa_dbus_dict_append_bool(&dict_iter, "persistent",
+ !!persistent) ||
!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
wpa_s->dbus_groupobj_path) ||
(ip &&
@@ -2439,6 +2474,9 @@
case WPAS_DBUS_BSS_PROP_AGE:
prop = "Age";
break;
+ case WPAS_DBUS_BSS_PROP_ANQP:
+ prop = "ANQP";
+ break;
default:
wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
__func__, property);
@@ -2977,6 +3015,11 @@
NULL,
NULL
},
+ {"ANQP", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+ wpas_dbus_getter_bss_anqp,
+ NULL,
+ NULL,
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -3716,6 +3759,13 @@
END_ARGS
}
},
+ {"ANQPGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_anqp_get,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ },
+ },
#endif /* CONFIG_INTERWORKING */
{ NULL, NULL, NULL, { END_ARGS } }
};
@@ -3973,6 +4023,11 @@
NULL,
NULL,
},
+ { "SignalChange", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+ wpas_dbus_getter_signal_change,
+ NULL,
+ NULL
+ },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -4303,7 +4358,22 @@
END_ARGS
}
},
+ {"ANQPQueryDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "addr", "s", ARG_OUT },
+ { "result", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ { "HS20TermsAndConditions", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "url", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_HS20 */
{ NULL, NULL, { END_ARGS } }
};
@@ -4555,11 +4625,6 @@
NULL,
NULL
},
- { "SignalChange", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
- wpas_dbus_getter_signal_change,
- NULL,
- NULL
- },
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -5142,3 +5207,39 @@
}
#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_HS20
+/**
+ * wpas_dbus_signal_hs20_t_c_acceptance - Signals a terms and conditions was
+ * received.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @url: URL of the terms and conditions acceptance page.
+ */
+void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
+ const char *url)
+{
+ 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,
+ "HS20TermsAndConditions");
+ if (!msg)
+ return;
+
+ if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &url,
+ DBUS_TYPE_INVALID))
+ dbus_connection_send(iface->con, msg, NULL);
+ else
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+#endif /* CONFIG_HS20 */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 5c5d855..952bb42 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -53,6 +53,7 @@
WPAS_DBUS_BSS_PROP_WPS,
WPAS_DBUS_BSS_PROP_IES,
WPAS_DBUS_BSS_PROP_AGE,
+ WPAS_DBUS_BSS_PROP_ANQP,
};
enum wpas_dbus_sta_prop {
@@ -279,6 +280,10 @@
int bh, int bss_load,
int conn_capab);
void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s);
+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);
#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
@@ -650,6 +655,18 @@
{
}
+static inline
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *result)
+{
+}
+
+static inline
+void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
+ const char *url)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6ad49a1..960b306 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1731,6 +1731,7 @@
reply = wpas_dbus_error_scan_error(
message,
"Scan request rejected");
+ goto out;
}
} else {
wpa_s->scan_req = MANUAL_SCAN_REQ;
@@ -1757,6 +1758,7 @@
false)) {
reply = wpas_dbus_error_scan_error(
message, "Scan request rejected");
+ goto out;
}
} else {
wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s",
@@ -1957,6 +1959,7 @@
#ifdef CONFIG_INTERWORKING
+
DBusMessage *
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s)
@@ -1977,6 +1980,111 @@
return reply;
}
+
+
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter, iter_dict;
+ struct wpa_dbus_dict_entry entry;
+ int ret;
+ u8 dst_addr[ETH_ALEN];
+ bool is_addr_present = false;
+ unsigned int freq = 0;
+#define MAX_ANQP_INFO_ID 100 /* Max info ID count from CLI implementation */
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+ u32 subtypes = 0;
+ u32 mbo_subtypes = 0;
+ size_t i;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (os_strcmp(entry.key, "addr") == 0 &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (hwaddr_aton(entry.str_value, dst_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: Invalid address '%s'",
+ __func__, entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "invalid address");
+ }
+
+ is_addr_present = true;
+ } else if (os_strcmp(entry.key, "freq") == 0 &&
+ entry.type == DBUS_TYPE_UINT32) {
+ freq = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_UINT16) {
+ for (i = 0; i < entry.array_len &&
+ num_id < MAX_ANQP_INFO_ID; i++) {
+ id[num_id] = entry.uint16array_value[i];
+ num_id++;
+ }
+ } else if (os_strcmp(entry.key, "hs20_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+
+ if (num <= 0 || num > 31) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message,
+ "invalid HS20 ANQP id");
+ }
+ subtypes |= BIT(num);
+ }
+ } else if (os_strcmp(entry.key, "mbo_ids") == 0 &&
+ entry.type == DBUS_TYPE_ARRAY &&
+ entry.array_type == DBUS_TYPE_BYTE) {
+ for (i = 0; i < entry.array_len; i++) {
+ int num = entry.bytearray_value[i];
+
+ if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "invalid MBO ANQP id");
+ }
+ mbo_subtypes |= BIT(num);
+ }
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ return wpas_dbus_error_invalid_args(
+ message, "unsupported parameter");
+ }
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!is_addr_present) {
+ wpa_printf(MSG_DEBUG,
+ "%s[dbus]: address not provided", __func__);
+ return wpas_dbus_error_invalid_args(message,
+ "address not provided");
+ }
+
+ ret = anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
+ mbo_subtypes);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s[dbus]: failed to send ANQP request",
+ __func__);
+ return wpas_dbus_error_unknown_error(
+ message, "error sending ANQP request");
+ }
+
+ return NULL;
+}
+
#endif /* CONFIG_INTERWORKING */
@@ -5527,7 +5635,7 @@
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");
@@ -5652,6 +5760,177 @@
/**
+ * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
+ * @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 for "ANQP" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_anqp(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ DBusMessageIter iter_dict, variant_iter;
+ 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)
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+ goto nomem;
+
+ anqp = bss->anqp;
+ if (anqp) {
+#ifdef CONFIG_INTERWORKING
+ if (anqp->capability_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "CapabilityList",
+ wpabuf_head(anqp->capability_list),
+ wpabuf_len(anqp->capability_list)))
+ goto nomem;
+ if (anqp->venue_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "VenueName",
+ wpabuf_head(anqp->venue_name),
+ wpabuf_len(anqp->venue_name)))
+ goto nomem;
+ if (anqp->network_auth_type &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "NetworkAuthType",
+ wpabuf_head(anqp->network_auth_type),
+ wpabuf_len(anqp->network_auth_type)))
+ goto nomem;
+ if (anqp->roaming_consortium &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "RoamingConsortium",
+ wpabuf_head(anqp->roaming_consortium),
+ wpabuf_len(anqp->roaming_consortium)))
+ goto nomem;
+ if (anqp->ip_addr_type_availability &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "IPAddrTypeAvailability",
+ wpabuf_head(anqp->ip_addr_type_availability),
+ wpabuf_len(anqp->ip_addr_type_availability)))
+ goto nomem;
+ if (anqp->nai_realm &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "NAIRealm",
+ wpabuf_head(anqp->nai_realm),
+ wpabuf_len(anqp->nai_realm)))
+ goto nomem;
+ if (anqp->anqp_3gpp &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "3GPP",
+ wpabuf_head(anqp->anqp_3gpp),
+ wpabuf_len(anqp->anqp_3gpp)))
+ goto nomem;
+ if (anqp->domain_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "DomainName",
+ wpabuf_head(anqp->domain_name),
+ wpabuf_len(anqp->domain_name)))
+ goto nomem;
+ if (anqp->fils_realm_info &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "FilsRealmInfo",
+ wpabuf_head(anqp->fils_realm_info),
+ wpabuf_len(anqp->fils_realm_info)))
+ goto nomem;
+
+#ifdef CONFIG_HS20
+ if (anqp->hs20_capability_list &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20CapabilityList",
+ wpabuf_head(anqp->hs20_capability_list),
+ wpabuf_len(anqp->hs20_capability_list)))
+ goto nomem;
+ if (anqp->hs20_operator_friendly_name &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OperatorFriendlyName",
+ wpabuf_head(anqp->hs20_operator_friendly_name),
+ wpabuf_len(anqp->hs20_operator_friendly_name)))
+ goto nomem;
+ if (anqp->hs20_wan_metrics &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20WanMetrics",
+ wpabuf_head(anqp->hs20_wan_metrics),
+ wpabuf_len(anqp->hs20_wan_metrics)))
+ goto nomem;
+ if (anqp->hs20_connection_capability &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20ConnectionCapability",
+ wpabuf_head(anqp->hs20_connection_capability),
+ wpabuf_len(anqp->hs20_connection_capability)))
+ goto nomem;
+ if (anqp->hs20_operating_class &&
+ !wpa_dbus_dict_append_byte_array(
+ &iter_dict, "HS20OperatingClass",
+ 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,
+ struct wpa_bss_anqp_elem, list) {
+ char title[32];
+
+ os_snprintf(title, sizeof(title), "anqp[%u]",
+ elem->infoid);
+ if (!wpa_dbus_dict_append_byte_array(
+ &iter_dict, title,
+ wpabuf_head(elem->payload),
+ wpabuf_len(elem->payload)))
+ goto nomem;
+
+ os_snprintf(title, sizeof(title),
+ "protected-anqp-info[%u]", elem->infoid);
+ if (!wpa_dbus_dict_append_bool(
+ &iter_dict, title,
+ elem->protected_response))
+ goto nomem;
+ }
+#endif /* CONFIG_INTERWORKING */
+ }
+
+ if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ goto nomem;
+
+ return TRUE;
+
+nomem:
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+}
+
+
+/**
* wpas_dbus_getter_enabled - Check whether network is enabled or disabled
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 97fa337..acd6af7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -157,6 +157,9 @@
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s);
+
DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
DECLARE_ACCESSOR(wpas_dbus_getter_state);
DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
@@ -216,6 +219,7 @@
DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index a178d87..418a8fd 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -14,6 +14,7 @@
#include "../wpa_supplicant_i.h"
#include "../wps_supplicant.h"
#include "../notify.h"
+#include "../bss.h"
#include "dbus_new_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
@@ -360,6 +361,12 @@
unsigned int group_id = 0;
struct wpa_ssid *ssid;
u8 go_bssid_buf[ETH_ALEN], *go_bssid = NULL;
+ bool allow_6ghz = false;
+ int vht = wpa_s->conf->p2p_go_vht;
+ int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+ int he = wpa_s->conf->p2p_go_he;
+ int edmg = wpa_s->conf->p2p_go_edmg;
+ int max_oper_chwidth, chwidth = 0, freq2 = 0;
dbus_message_iter_init(message, &iter);
@@ -392,6 +399,28 @@
if (hwaddr_aton(entry.str_value, go_bssid_buf))
goto inv_args_clear;
go_bssid = go_bssid_buf;
+ } else if (os_strcmp(entry.key, "ht40") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ ht40 = entry.bool_value;
+ } else if (os_strcmp(entry.key, "vht") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ vht = entry.bool_value;
+ ht40 |= vht;
+ } else if (os_strcmp(entry.key, "he") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ he = entry.bool_value;
+ } else if (os_strcmp(entry.key, "edmg") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ edmg = entry.bool_value;
+ } else if (os_strcmp(entry.key, "allow_6ghz") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ allow_6ghz = entry.bool_value;
+ } else if (os_strcmp(entry.key, "freq2") == 0 &&
+ entry.type == DBUS_TYPE_INT32) {
+ freq2 = entry.int32_value;
+ } else if (os_strcmp(entry.key, "max_oper_chwidth") == 0 &&
+ entry.type == DBUS_TYPE_INT32) {
+ chwidth = entry.int32_value;
} else {
goto inv_args_clear;
}
@@ -399,6 +428,13 @@
wpa_dbus_dict_entry_clear(&entry);
}
+ max_oper_chwidth = chwidth_freq2_to_ch_width(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ goto inv_args;
+
+ if (allow_6ghz && chwidth == 40)
+ max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
+
wpa_s = wpa_s->global->p2p_init_wpa_s;
if (!wpa_s) {
reply = wpas_dbus_error_no_p2p_mgmt_iface(message);
@@ -437,17 +473,19 @@
if (ssid == NULL || ssid->disabled != 2)
goto inv_args;
- if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
- 0, 0, 0, 0, NULL, 0, 0,
- false, retry_limit,
- go_bssid)) {
+ if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
+ freq2, ht40, vht,
+ max_oper_chwidth, he, edmg,
+ NULL, 0, 0, allow_6ghz,
+ retry_limit, go_bssid)) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
goto out;
}
- } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
- 0, 0, 0, false))
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, freq2,
+ ht40, vht, max_oper_chwidth, he, edmg,
+ allow_6ghz))
goto inv_args;
out:
@@ -2427,9 +2465,9 @@
u8 *p_bssid;
if (role == WPAS_P2P_ROLE_CLIENT) {
- if (wpa_s->current_ssid == NULL)
+ if (!wpa_s->current_bss)
return FALSE;
- p_bssid = wpa_s->current_ssid->bssid;
+ p_bssid = wpa_s->current_bss->bssid;
} else {
if (wpa_s->ap_iface == NULL)
return FALSE;
@@ -2451,9 +2489,9 @@
u8 role = wpas_get_p2p_role(wpa_s);
if (role == WPAS_P2P_ROLE_CLIENT) {
- if (wpa_s->go_params == NULL)
+ if (!wpa_s->current_bss)
return FALSE;
- op_freq = wpa_s->go_params->freq;
+ op_freq = wpa_s->current_bss->freq;
} else {
if (wpa_s->ap_iface == NULL)
return FALSE;
@@ -2740,20 +2778,20 @@
if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0)
goto error;
- query = NULL;
- resp = NULL;
} else
goto error;
+out:
os_free(service);
+ wpabuf_free(query);
+ wpabuf_free(resp);
+
return reply;
error_clear:
wpa_dbus_dict_entry_clear(&entry);
error:
- os_free(service);
- wpabuf_free(query);
- wpabuf_free(resp);
- return wpas_dbus_error_invalid_args(message, NULL);
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
}
@@ -2887,6 +2925,7 @@
if (entry.type != DBUS_TYPE_ARRAY ||
entry.array_type != DBUS_TYPE_BYTE)
goto error_clear;
+ wpabuf_free(tlv);
tlv = wpabuf_alloc_copy(entry.bytearray_value,
entry.array_len);
} else
@@ -2914,7 +2953,6 @@
if (tlv == NULL)
goto error;
ref = wpas_p2p_sd_request(wpa_s, addr, tlv);
- wpabuf_free(tlv);
}
if (ref != 0) {
@@ -2926,14 +2964,13 @@
message, "Unable to send SD request");
}
out:
+ wpabuf_free(tlv);
os_free(service);
os_free(peer_object_path);
return reply;
error_clear:
wpa_dbus_dict_entry_clear(&entry);
error:
- if (tlv)
- wpabuf_free(tlv);
reply = wpas_dbus_error_invalid_args(message, NULL);
goto out;
}
@@ -2975,6 +3012,7 @@
if (entry.type != DBUS_TYPE_ARRAY ||
entry.array_type != DBUS_TYPE_BYTE)
goto error_clear;
+ wpabuf_free(tlv);
tlv = wpabuf_alloc_copy(entry.bytearray_value,
entry.array_len);
} else
@@ -2988,8 +3026,8 @@
goto error;
wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
- wpabuf_free(tlv);
out:
+ wpabuf_free(tlv);
os_free(peer_object_path);
return reply;
error_clear:
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 7fb0669..28f1aa4 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -671,20 +671,27 @@
&interface) ||
/* Changed properties dict */
!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
- "{sv}", &dict_iter) ||
- !put_changed_properties(obj_dsc, interface, &dict_iter, 0) ||
- !dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
+ "{sv}", &dict_iter))
+ goto fail;
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) {
+ dbus_message_iter_close_container(&signal_iter, &dict_iter);
+ goto fail;
+ }
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
/* Invalidated properties array (empty) */
!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
"s", &dict_iter) ||
- !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
- wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
- __func__);
- } else {
- dbus_connection_send(con, msg, NULL);
- }
+ !dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto fail;
+ dbus_connection_send(con, msg, NULL);
+
+out:
dbus_message_unref(msg);
+ return;
+fail:
+ wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", __func__);
+ goto out;
}
@@ -702,16 +709,23 @@
dbus_message_iter_init_append(msg, &signal_iter);
if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
- "{sv}", &dict_iter) ||
- !put_changed_properties(obj_dsc, interface, &dict_iter, 1) ||
- !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
- wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
- __func__);
- } else {
- dbus_connection_send(con, msg, NULL);
+ "{sv}", &dict_iter))
+ goto fail;
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) {
+ dbus_message_iter_close_container(&signal_iter, &dict_iter);
+ goto fail;
}
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto fail;
+ dbus_connection_send(con, msg, NULL);
+
+out:
dbus_message_unref(msg);
+ return;
+fail:
+ wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", __func__);
+ goto out;
}
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index 6c721bf..a8c0d28 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
if (!iface)
return NULL;
iface->dbus_interface = os_strdup(dbus_interface);
- iface->xml = wpabuf_alloc(15000);
+ iface->xml = wpabuf_alloc(16000);
if (iface->dbus_interface == NULL || iface->xml == NULL) {
os_free(iface->dbus_interface);
wpabuf_free(iface->xml);
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1ae5a9f..c0192e0 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -677,3 +677,17 @@
# production use.
#CONFIG_PASN=y
+# Disable support for Radio Measurement (IEEE 802.11k) and supported operating
+# class indication. Removing these is not recommended since they can help the
+# AP manage the network and STA steering.
+#CONFIG_NO_RRM=y
+
+# Disable support for Robust AV streaming for consumer and enterprise Wi-Fi
+# applications; IEEE Std 802.11-2020, 4.3.24; SCS, MSCS, QoS Management
+#CONFIG_NO_ROBUST_AV=y
+
+# Disable support for WMM admission control
+#CONFIG_NO_WMM_AC=y
+
+# Wi-Fi Aware unsynchronized service discovery (NAN USD)
+#CONFIG_NAN_USD=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 4f5be0a..216224f 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -238,6 +238,12 @@
wait_time = wpa_s->dpp_resp_retry_time;
else
wait_time = 1000;
+ if (wpa_s->dpp_tx_chan_change) {
+ wpa_s->dpp_tx_chan_change = false;
+ if (wait_time > 100)
+ wait_time = 100;
+ }
+
wpa_printf(MSG_DEBUG,
"DPP: Schedule retransmission of Authentication Response frame in %u ms",
wait_time);
@@ -487,6 +493,21 @@
}
+static void wpas_dpp_neg_freq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!wpa_s->dpp_listen_on_tx_expire || !auth || !auth->neg_freq)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start listen on neg_freq %u MHz based on timeout for TX wait expiration",
+ auth->neg_freq);
+ wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+}
+
+
static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
@@ -605,7 +626,9 @@
wpa_s->dpp_auth->curr_freq,
wpa_s->dpp_auth->neg_freq);
offchannel_send_action_done(wpa_s);
- wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq);
+ wpa_s->dpp_listen_on_tx_expire = true;
+ eloop_register_timeout(0, 100000, wpas_dpp_neg_freq_timeout,
+ wpa_s, NULL);
}
if (wpa_s->dpp_auth_ok_on_ack)
@@ -1035,6 +1058,8 @@
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = lwork->freq;
wpa_drv_dpp_listen(wpa_s, true);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = false;
}
@@ -1134,11 +1159,58 @@
}
+static void wpas_dpp_tx_auth_resp(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ offchannel_send_action(wpa_s, auth->curr_freq,
+ auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg),
+ 500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_tx_auth_resp_roc_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth || !wpa_s->dpp_tx_auth_resp_on_roc_stop)
+ return;
+
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = true;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send postponed Authentication Response on remain-on-channel termination timeout");
+ wpas_dpp_tx_auth_resp(wpa_s);
+}
+
+
void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
+ wpa_printf(MSG_DEBUG, "DPP: Remain on channel cancel for %u MHz", freq);
wpas_dpp_listen_work_done(wpa_s);
+ if (wpa_s->dpp_auth && wpa_s->dpp_tx_auth_resp_on_roc_stop) {
+ eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout,
+ wpa_s, NULL);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = true;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send postponed Authentication Response on remain-on-channel termination");
+ wpas_dpp_tx_auth_resp(wpa_s);
+ return;
+ }
+
if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) {
unsigned int new_freq;
@@ -1256,17 +1328,17 @@
wpa_printf(MSG_DEBUG,
"DPP: Stop listen on %u MHz to allow response on the request %u MHz",
wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq);
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = true;
+ eloop_register_timeout(0, 100000,
+ wpas_dpp_tx_auth_resp_roc_timeout,
+ wpa_s, NULL);
wpas_dpp_listen_stop(wpa_s);
+ return;
}
+ wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+ wpa_s->dpp_tx_chan_change = false;
- wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
- MAC2STR(src), wpa_s->dpp_auth->curr_freq,
- DPP_PA_AUTHENTICATION_RESP);
- offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
- src, wpa_s->own_addr, broadcast,
- wpabuf_head(wpa_s->dpp_auth->resp_msg),
- wpabuf_len(wpa_s->dpp_auth->resp_msg),
- 500, wpas_dpp_tx_status, 0);
+ wpas_dpp_tx_auth_resp(wpa_s);
}
@@ -1275,6 +1347,15 @@
struct dpp_authentication *auth = wpa_s->dpp_auth;
int freq;
+ 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",
+ auth->neg_freq);
+ eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
+ wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+ return;
+ }
+
if (!wpa_s->dpp_gas_server || !auth) {
if (auth && auth->waiting_auth_resp &&
eloop_is_timeout_registered(wpas_dpp_drv_wait_timeout,
@@ -1800,7 +1881,7 @@
wpa_s->dpp_gas_dialog_token = -1;
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
- os_memcmp(addr, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(addr, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return;
}
@@ -1935,7 +2016,11 @@
offchannel_send_action_done(wpa_s);
wpas_dpp_listen_stop(wpa_s);
+#ifdef CONFIG_NO_RRM
+ supp_op_classes = NULL;
+#else /* CONFIG_NO_RRM */
supp_op_classes = wpas_supp_op_classes(wpa_s);
+#endif /* CONFIG_NO_RRM */
buf = dpp_build_conf_req_helper(auth, wpa_s->conf->dpp_name,
wpa_s->dpp_netrole,
wpa_s->conf->dpp_mud_url,
@@ -2016,7 +2101,7 @@
}
if (!is_zero_ether_addr(auth->peer_mac_addr) &&
- os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2071,7 +2156,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2175,7 +2260,7 @@
if (!auth || !auth->waiting_conf_result) {
if (auth &&
- os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) == 0 &&
+ ether_addr_equal(src, auth->peer_mac_addr) &&
gas_server_response_sent(wpa_s->gas_server,
auth->gas_server_ctx)) {
/* This could happen if the TX status event gets delayed
@@ -2192,7 +2277,7 @@
}
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2608,7 +2693,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2652,7 +2737,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2690,7 +2775,7 @@
wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR,
MAC2STR(src));
if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
- os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, wpa_s->dpp_intro_bssid)) {
wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
MACSTR " - drop", MAC2STR(src));
return;
@@ -3854,7 +3939,7 @@
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Notify from "
MACSTR, MAC2STR(src));
if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
- os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, wpa_s->dpp_intro_bssid)) {
wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
MACSTR " - drop", MAC2STR(src));
return;
@@ -4145,7 +4230,7 @@
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
MAC2STR(sa));
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
- os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return NULL;
}
@@ -4159,6 +4244,13 @@
* exchange. */
dpp_notify_auth_success(auth, 1);
wpa_s->dpp_auth_ok_on_ack = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_hexdump(MSG_DEBUG,
@@ -4446,7 +4538,7 @@
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 */
@@ -4805,6 +4897,8 @@
eloop_cancel_timeout(wpas_dpp_gas_initial_resp_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_gas_client_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_drv_wait_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
@@ -5616,6 +5710,7 @@
eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
wpa_s, NULL);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
@@ -5677,6 +5772,7 @@
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
return 0;
}
@@ -5692,6 +5788,8 @@
if (wpa_s->dpp_pb_bi) {
char id[20];
+ 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);
dpp_bootstrap_remove(wpa_s->dpp, id);
wpa_s->dpp_pb_bi = NULL;
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index dcf5764..d01b52b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -128,7 +128,7 @@
}
struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s);
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid);
static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
{
@@ -342,14 +342,6 @@
return -1;
}
-static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
- struct wpa_driver_ap_params *params)
-{
- if (wpa_s->driver->set_ap)
- return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
- return -1;
-}
-
static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
struct hostapd_sta_add_params *params)
{
@@ -1155,8 +1147,10 @@
{
struct secure_ranging_params params;
+ /* Configure secure ranging context only to the drivers that support it.
+ */
if (!wpa_s->driver->set_secure_ranging_ctx)
- return -1;
+ return 0;
os_memset(¶ms, 0, sizeof(params));
params.action = action;
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9641062..0c17aae 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -195,6 +195,9 @@
return;
}
+ if (!radius_msg_add_msg_auth(msg))
+ goto fail;
+
radius_msg_make_authenticator(msg);
hdr = (const struct eap_hdr *) eap;
@@ -670,6 +673,10 @@
wpa_s->eapol = NULL;
if (e->radius_conf && e->radius_conf->auth_server) {
os_free(e->radius_conf->auth_server->shared_secret);
+ os_free(e->radius_conf->auth_server->ca_cert);
+ os_free(e->radius_conf->auth_server->client_cert);
+ os_free(e->radius_conf->auth_server->private_key);
+ os_free(e->radius_conf->auth_server->private_key_passwd);
os_free(e->radius_conf->auth_server);
}
os_free(e->radius_conf);
@@ -1007,7 +1014,10 @@
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
- int port, const char *secret,
+ int port, bool tls, const char *secret,
+ const char *ca_cert, const char *client_cert,
+ const char *private_key,
+ const char *private_key_passwd,
const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
@@ -1045,8 +1055,17 @@
}
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
as->port = port;
+ as->tls = tls;
as->shared_secret = (u8 *) os_strdup(secret);
as->shared_secret_len = os_strlen(secret);
+ if (ca_cert)
+ as->ca_cert = os_strdup(ca_cert);
+ if (client_cert)
+ as->client_cert = os_strdup(client_cert);
+ if (private_key)
+ as->private_key = os_strdup(private_key);
+ if (private_key_passwd)
+ as->private_key_passwd = os_strdup(private_key_passwd);
e->radius_conf->auth_server = as;
e->radius_conf->auth_servers = as;
e->radius_conf->msg_dumps = 1;
@@ -1256,11 +1275,16 @@
{
printf("usage:\n"
"eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
- "[-s<AS secret>]\\\n"
+ "[-s<AS secret>] \\\n"
+ " [-X<RADIUS protocol> \\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
+#ifdef CONFIG_RADIUS_TLS
+ " [-j<CA cert>] [-J<client cert>] \\\n"
+ "[-k<private key] [-K<private key passwd>] \\\n"
+#endif /* CONFIG_RADIUS_TLS */
" [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1269,10 +1293,11 @@
" -c<conf> = configuration file\n"
" -a<AS IP> = IP address of the authentication server, "
"default 127.0.0.1\n"
- " -p<AS port> = UDP port of the authentication server, "
- "default 1812\n"
- " -s<AS secret> = shared secret with the authentication "
- "server, default 'radius'\n"
+ " -p<AS port> = Port of the authentication server,\n"
+ " default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n"
+ " -s<AS secret> = shared secret with the authentication server,\n"
+ " default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n"
+ " -X<RADIUS protocol> = RADIUS protocol to use: UDP (default) or TLS\n"
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
@@ -1310,8 +1335,11 @@
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
- int as_port = 1812;
- char *as_secret = "radius";
+ int as_port = -1;
+ char *as_secret = NULL;
+ char *ca_cert = NULL, *client_cert = NULL;
+ char *private_key = NULL, *private_key_passwd = NULL;
+ bool tls = false;
char *cli_addr = NULL;
char *conf = NULL;
int timeout = 30;
@@ -1334,7 +1362,8 @@
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
+ c = getopt(argc, argv,
+ "a:A:c:C:ei:j:J:k:K:M:nN:o:p:P:r:R:s:St:T:vWX:");
if (c < 0)
break;
switch (c) {
@@ -1356,6 +1385,20 @@
case 'i':
ifname = optarg;
break;
+#ifdef CONFIG_RADIUS_TLS
+ case 'j':
+ ca_cert = optarg;
+ break;
+ case 'J':
+ client_cert = optarg;
+ break;
+ case 'k':
+ private_key = optarg;
+ break;
+ case 'K':
+ private_key_passwd = optarg;
+ break;
+#endif /* CONFIG_RADIUS_TLS */
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
@@ -1406,6 +1449,16 @@
case 'W':
wait_for_monitor++;
break;
+ case 'X':
+ if (os_strcmp(optarg, "UDP") == 0) {
+ tls = false;
+ } else if (os_strcmp(optarg, "TLS") == 0) {
+ tls = true;
+ } else {
+ usage();
+ return -1;
+ }
+ break;
case 'N':
p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
@@ -1440,6 +1493,11 @@
}
}
+ if (!as_secret)
+ as_secret = tls ? "radsec" : "radius";
+ if (as_port < 0)
+ as_port = tls ? 2083 : 1812;
+
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
return scard_test(&eapol_test);
}
@@ -1489,7 +1547,8 @@
wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
}
- wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
+ wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, tls, as_secret,
+ ca_cert, client_cert, private_key, private_key_passwd,
cli_addr, ifname);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index f386119..09a2bbb 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -50,6 +50,7 @@
#include "mesh.h"
#include "mesh_mpm.h"
#include "wmm_ac.h"
+#include "nan_usd.h"
#include "dpp_supplicant.h"
#include "rsn_supp/wpa_i.h"
@@ -137,6 +138,33 @@
}
+static struct wpa_bss * __wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid,
+ size_t ssid_len)
+{
+ if (ssid && ssid_len > 0)
+ return wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+ else
+ return wpa_bss_get_bssid(wpa_s, bssid);
+}
+
+
+static struct wpa_bss * _wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid,
+ size_t ssid_len, bool try_update_scan_results)
+{
+ struct wpa_bss *bss = __wpa_supplicant_get_new_bss(wpa_s, bssid, ssid,
+ ssid_len);
+
+ if (bss || !try_update_scan_results)
+ return bss;
+
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
+
+ return __wpa_supplicant_get_new_bss(wpa_s, bssid, ssid, ssid_len);
+}
+
+
static struct wpa_bss * wpa_supplicant_get_new_bss(
struct wpa_supplicant *wpa_s, const u8 *bssid)
{
@@ -144,14 +172,23 @@
struct wpa_ssid *ssid = wpa_s->current_ssid;
u8 drv_ssid[SSID_MAX_LEN];
int res;
+ bool try_update_scan_results = true;
res = wpa_drv_get_ssid(wpa_s, drv_ssid);
- if (res > 0)
- bss = wpa_bss_get(wpa_s, bssid, drv_ssid, res);
- if (!bss && ssid && ssid->ssid_len > 0)
- bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (res > 0) {
+ bss = _wpa_supplicant_get_new_bss(wpa_s, bssid, drv_ssid, res,
+ try_update_scan_results);
+ try_update_scan_results = false;
+ }
+ if (!bss && ssid && ssid->ssid_len > 0) {
+ bss = _wpa_supplicant_get_new_bss(wpa_s, bssid, ssid->ssid,
+ ssid->ssid_len,
+ try_update_scan_results);
+ try_update_scan_results = false;
+ }
if (!bss)
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+ bss = _wpa_supplicant_get_new_bss(wpa_s, bssid, NULL, 0,
+ try_update_scan_results);
return bss;
}
@@ -162,13 +199,6 @@
{
struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
-
- /* Get the BSS from the new scan results */
- bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- }
-
if (bss)
wpa_s->current_bss = bss;
@@ -181,11 +211,6 @@
{
struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
- bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
- }
-
if (bss)
wpa_s->links[link_id].bss = bss;
}
@@ -383,8 +408,13 @@
wpa_s->key_mgmt = 0;
wpa_s->allowed_key_mgmts = 0;
+#ifndef CONFIG_NO_RRM
wpas_rrm_reset(wpa_s);
+#endif /* CONFIG_NO_RRM */
wpa_s->wnmsleep_used = 0;
+#ifdef CONFIG_WNM
+ wpa_s->wnm_mode = 0;
+#endif /* CONFIG_WNM */
wnm_clear_coloc_intf_reporting(wpa_s);
wpa_s->disable_mbo_oce = 0;
@@ -399,6 +429,13 @@
wpa_s->wps_scan_done = false;
wpas_reset_mlo_info(wpa_s);
+
+#ifdef CONFIG_SME
+ wpa_s->sme.bss_max_idle_period = 0;
+#endif /* CONFIG_SME */
+
+ wpa_s->ssid_verified = false;
+ wpa_s->bigtk_set = false;
}
@@ -634,7 +671,7 @@
(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,
@@ -759,15 +796,33 @@
return 0;
}
+ wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+
if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED &&
(!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) {
+#ifdef CONFIG_OWE
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && ssid->owe_only &&
+ !wpa_ie && !rsn_ie &&
+ wpa_s->owe_transition_select &&
+ wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) &&
+ ssid->owe_transition_bss_select_count + 1 <=
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT) {
+ ssid->owe_transition_bss_select_count++;
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip OWE open BSS (selection count %d does not exceed %d)",
+ ssid->owe_transition_bss_select_count,
+ MAX_OWE_TRANSITION_BSS_SELECT_COUNT);
+ wpa_s->owe_transition_search = 1;
+ return 0;
+ }
+#endif /* CONFIG_OWE */
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - MFP Required but network not MFP Capable");
return 0;
}
- wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
proto_match++;
@@ -1089,7 +1144,7 @@
u8 ssid_len;
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;
@@ -1132,19 +1187,18 @@
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))
+ if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL, NULL))
return true;
- if (bss->n_mld_links == 0)
+ if (!bss->valid_links)
return true;
/* Check if the current BSS is going to be removed */
removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
- if (BIT(bss->mld_links[0].link_id) & removed_links)
+ if (BIT(bss->mld_link_id) & removed_links)
return false;
return true;
@@ -1194,7 +1248,7 @@
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;
@@ -1233,7 +1287,7 @@
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))
@@ -1242,8 +1296,10 @@
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 */
@@ -1285,7 +1341,7 @@
#endif /* CONFIG_WPS */
if (ssid->bssid_set && ssid->ssid_len == 0 &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+ ether_addr_equal(bss->bssid, ssid->bssid))
check_ssid = false;
if (check_ssid &&
@@ -1297,7 +1353,7 @@
}
if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(bss->bssid, ssid->bssid)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
return false;
@@ -1466,7 +1522,7 @@
}
if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 ||
- os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(dev_addr, ssid->go_p2p_dev_addr)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no matching GO P2P Device Address in P2P element");
@@ -1600,7 +1656,7 @@
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);
@@ -1673,6 +1729,12 @@
return NULL;
}
+ if (wnm_is_bss_excluded(wpa_s, bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID excluded");
+ return NULL;
+ }
+
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))
@@ -1777,10 +1839,12 @@
break;
}
- if (selected == NULL && wpa_s->bssid_ignore &&
+ if (!selected &&
+ (wpa_s->bssid_ignore || wnm_active_bss_trans_mgmt(wpa_s)) &&
!wpa_s->countermeasures) {
wpa_dbg(wpa_s, MSG_DEBUG,
"No APs found - clear BSSID ignore list and try again");
+ wnm_btm_reset(wpa_s);
wpa_bssid_ignore_clear(wpa_s);
wpa_s->bssid_ignore_cleared = true;
} else if (selected == NULL)
@@ -1857,14 +1921,16 @@
{
int *freqs;
u16 missing_links = 0, removed_links;
+ u8 ap_mld_id;
if (!((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)))
return 0;
- /* Try to resolve any missing link information */
if (wpa_bss_parse_basic_ml_element(wpa_s, selected, NULL,
- &missing_links) || !missing_links)
+ &missing_links, ssid,
+ &ap_mld_id) ||
+ !missing_links)
return 0;
removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, selected);
@@ -1895,7 +1961,36 @@
wpa_s->manual_scan_freqs = freqs;
os_memcpy(wpa_s->ml_probe_bssid, selected->bssid, ETH_ALEN);
- wpa_s->ml_probe_mld_id = -1;
+
+ /*
+ * In case the ML probe request is intended to retrieve information from
+ * the transmitted BSS, the AP MLD ID should be included and should be
+ * set to zero.
+ * 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)
+ wpa_s->ml_probe_mld_id = -1;
+ else
+ wpa_s->ml_probe_mld_id = 0;
+
+ if (ssid && ssid->ssid_len) {
+ os_free(wpa_s->ssids_from_scan_req);
+ wpa_s->num_ssids_from_scan_req = 0;
+
+ wpa_s->ssids_from_scan_req =
+ os_zalloc(sizeof(struct wpa_ssid_value));
+ if (wpa_s->ssids_from_scan_req) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe: With direct SSID");
+
+ wpa_s->num_ssids_from_scan_req = 1;
+ wpa_s->ssids_from_scan_req[0].ssid_len = ssid->ssid_len;
+ os_memcpy(wpa_s->ssids_from_scan_req[0].ssid,
+ ssid->ssid, ssid->ssid_len);
+ }
+ }
+
wpa_s->ml_probe_links = missing_links;
wpa_s->normal_scans = 0;
@@ -1952,12 +2047,11 @@
* the selected BSSID, do not trigger new attempt.
*/
if (wpa_s->reassociate ||
- (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+ (!ether_addr_equal(selected->bssid, wpa_s->bssid) &&
((wpa_s->wpa_state != WPA_ASSOCIATING &&
wpa_s->wpa_state != WPA_AUTHENTICATING) ||
(!is_zero_ether_addr(wpa_s->pending_bssid) &&
- os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
- 0) ||
+ !ether_addr_equal(selected->bssid, wpa_s->pending_bssid)) ||
(is_zero_ether_addr(wpa_s->pending_bssid) &&
ssid != wpa_s->current_ssid)))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
@@ -2029,7 +2123,7 @@
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;
@@ -2070,11 +2164,22 @@
}
+static int wpas_evaluate_band_score(int frequency)
+{
+ if (is_6ghz_freq(frequency))
+ return 2;
+ if (IS_5GHZ(frequency))
+ return 1;
+ return 0;
+}
+
+
int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *selected)
{
int min_diff, diff;
+ int cur_band_score, sel_band_score;
int to_5ghz, to_6ghz;
int cur_level, sel_level;
unsigned int cur_est, sel_est;
@@ -2099,9 +2204,13 @@
MAC2STR(selected->bssid), selected->freq, selected->level,
selected->snr, selected->est_throughput);
+ if (wpas_ap_link_address(wpa_s, selected->bssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "MLD: associated to selected BSS");
+ return 0;
+ }
+
if (wpa_s->current_ssid->bssid_set &&
- os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
- 0) {
+ ether_addr_equal(selected->bssid, wpa_s->current_ssid->bssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
"has preferred BSSID");
return 1;
@@ -2221,9 +2330,11 @@
else if (sel_est > cur_est)
min_diff--;
- if (to_5ghz)
- min_diff -= 2;
- if (to_6ghz)
+ cur_band_score = wpas_evaluate_band_score(current_bss->freq);
+ sel_band_score = wpas_evaluate_band_score(selected->freq);
+ min_diff += (cur_band_score - sel_band_score) * 2;
+ if (wpa_s->signal_threshold && cur_level <= wpa_s->signal_threshold &&
+ sel_level > wpa_s->signal_threshold)
min_diff -= 2;
diff = sel_level - cur_level;
if (diff < min_diff) {
@@ -2256,6 +2367,7 @@
struct wpa_ssid *ssid)
{
struct wpa_bss *current_bss = NULL;
+ const u8 *bssid;
if (wpa_s->reassociate)
return 1; /* explicit request to reassociate */
@@ -2269,12 +2381,17 @@
if (wpas_driver_bss_selection(wpa_s))
return 0; /* Driver-based roaming */
+ if (wpa_s->valid_links)
+ bssid = wpa_s->links[wpa_s->mlo_assoc_link_id].bssid;
+ else
+ bssid = wpa_s->bssid;
+
if (wpa_s->current_ssid->ssid)
- current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+ current_bss = wpa_bss_get(wpa_s, bssid,
wpa_s->current_ssid->ssid,
wpa_s->current_ssid->ssid_len);
if (!current_bss)
- current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+ current_bss = wpa_bss_get_bssid(wpa_s, bssid);
if (!current_bss)
return 1; /* current BSS not seen in scan results */
@@ -2329,7 +2446,7 @@
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
- NULL, 1);
+ NULL, 1, NULL);
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap ||
wpa_s->scan_res_handler == scan_only_handler)
@@ -2370,6 +2487,8 @@
}
#endif /* CONFIG_NO_RANDOM_POOL */
+ wpa_s->last_scan_external = data && data->scan_info.external_scan;
+
if (update_only) {
ret = 1;
goto scan_work_done;
@@ -2418,15 +2537,17 @@
return 0;
}
- if (wnm_scan_process(wpa_s, 1) > 0)
+ if (wnm_scan_process(wpa_s, false) > 0)
goto scan_work_done;
if (sme_proc_obss_scan(wpa_s) > 0)
goto scan_work_done;
+#ifndef CONFIG_NO_RRM
if (own_request && data &&
wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
goto scan_work_done;
+#endif /* CONFIG_NO_RRM */
if (ml_link_probe_scan(wpa_s))
goto scan_work_done;
@@ -2762,6 +2883,9 @@
wpa_s->conf->scan_res_valid_for_connect)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
+ } else if (wpa_s->crossed_6ghz_dom) {
+ wpa_printf(MSG_DEBUG, "Fast associate: Crossed 6 GHz domain");
+ return -1;
}
return wpas_select_network_from_last_scan(wpa_s, 0, 1, false, NULL);
@@ -2844,6 +2968,8 @@
wnm_bss_keep_alive, wpa_s,
NULL);
}
+ } else {
+ wpa_s->sme.bss_max_idle_period = 0;
}
#endif /* CONFIG_SME */
}
@@ -2859,8 +2985,6 @@
}
-#ifdef CONFIG_INTERWORKING
-
static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
size_t len)
{
@@ -2893,8 +3017,6 @@
}
}
-#endif /* CONFIG_INTERWORKING */
-
static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
{
@@ -2920,25 +3042,24 @@
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems elems;
- const u8 *map_sub_elem, *pos;
- size_t len;
+ struct multi_ap_params multi_ap;
+ u16 status;
wpa_s->multi_ap_ie = 0;
if (!ies ||
ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed ||
- !elems.multi_ap || elems.multi_ap_len < 7)
+ !elems.multi_ap)
return;
- pos = elems.multi_ap + 4;
- len = elems.multi_ap_len - 4;
-
- map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
- if (!map_sub_elem || map_sub_elem[1] < 1)
+ status = check_multi_ap_ie(elems.multi_ap + 4, elems.multi_ap_len - 4,
+ &multi_ap);
+ if (status != WLAN_STATUS_SUCCESS)
return;
- wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS);
- wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] &
+ wpa_s->multi_ap_backhaul = !!(multi_ap.capability &
+ MULTI_AP_BACKHAUL_BSS);
+ wpa_s->multi_ap_fronthaul = !!(multi_ap.capability &
MULTI_AP_FRONTHAUL_BSS);
wpa_s->multi_ap_ie = 1;
}
@@ -3155,7 +3276,8 @@
if (wpa_s->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) {
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];
@@ -3262,6 +3384,16 @@
#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
+ wpa_s->ssid_verified = false;
+ wpa_s->bigtk_set = false;
+#ifdef CONFIG_SAE
+#ifdef CONFIG_SME
+ /* SAE H2E binds the SSID into PT and that verifies the SSID
+ * implicitly. */
+ if (wpa_s->sme.sae.state == SAE_ACCEPTED && wpa_s->sme.sae.h2e)
+ wpa_s->ssid_verified = true;
+#endif /* CONFIG_SME */
+#endif /* CONFIG_SAE */
bssid_known = wpa_drv_get_bssid(wpa_s, bssid) == 0;
if (data->assoc_info.req_ies)
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
@@ -3277,10 +3409,8 @@
wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_WNM */
-#ifdef CONFIG_INTERWORKING
interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
-#endif /* CONFIG_INTERWORKING */
if (wpa_s->hw_capab == CAPAB_VHT &&
get_ie(data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
@@ -3441,14 +3571,22 @@
#ifdef CONFIG_FILS
#ifdef CONFIG_SME
- if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
- wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
- (!data->assoc_info.resp_frame ||
- fils_process_assoc_resp(wpa_s->wpa,
- data->assoc_info.resp_frame,
- data->assoc_info.resp_frame_len) < 0)) {
- wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
- return -1;
+ if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+ wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) {
+ if (!data->assoc_info.resp_frame ||
+ fils_process_assoc_resp(wpa_s->wpa,
+ data->assoc_info.resp_frame,
+ data->assoc_info.resp_frame_len) <
+ 0) {
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_UNSPECIFIED);
+ return -1;
+ }
+
+ /* FILS use of an AEAD cipher include the SSID element in
+ * (Re)Association Request frame in the AAD and since the AP
+ * accepted that, the SSID was verified. */
+ wpa_s->ssid_verified = true;
}
#endif /* CONFIG_SME */
@@ -3509,6 +3647,9 @@
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
}
+ /* SSID is included in PMK-R0 derivation, so it is verified
+ * implicitly. */
+ wpa_s->ssid_verified = true;
}
p = data->assoc_info.resp_ies;
@@ -3580,16 +3721,21 @@
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done");
+ /* SSID is included in PMK-R0 derivation, so it is verified
+ * implicitly. */
+ wpa_s->ssid_verified = true;
}
wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_IEEE80211R */
+#ifndef CONFIG_NO_ROBUST_AV
if (bssid_known)
wpas_handle_assoc_resp_mscs(wpa_s, bssid,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
+#endif /* CONFIG_NO_ROBUST_AV */
/* WPA/RSN IE from Beacon/ProbeResp */
p = data->assoc_info.beacon_ies;
@@ -3618,9 +3764,29 @@
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
}
+ if (wpas_rsn_overriding(wpa_s) &&
+ p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ WPA_GET_BE32(&p[2]) == RSNE_OVERRIDE_2_IE_VENDOR_TYPE) {
+ rsn_found = 1;
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
+ }
+
+ if (!rsn_found &&
+ wpas_rsn_overriding(wpa_s) &&
+ p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ WPA_GET_BE32(&p[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE) {
+ rsn_found = 1;
+ wpa_sm_set_ap_rsn_ie(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 (wpas_rsn_overriding(wpa_s) &&
+ p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ WPA_GET_BE32(&p[2]) == RSNXE_OVERRIDE_IE_VENDOR_TYPE)
+ wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len);
+
l -= len;
p += len;
}
@@ -3634,18 +3800,29 @@
if (wpa_found || rsn_found)
wpa_s->ap_ies_from_associnfo = 1;
- if (wpa_s->assoc_freq && data->assoc_info.freq &&
- wpa_s->assoc_freq != data->assoc_info.freq) {
- wpa_printf(MSG_DEBUG, "Operating frequency changed from "
- "%u to %u MHz",
- wpa_s->assoc_freq, data->assoc_info.freq);
- wpa_supplicant_update_scan_results(wpa_s);
+ if (wpa_s->assoc_freq && data->assoc_info.freq) {
+ struct wpa_bss *bss;
+ unsigned int freq = 0;
+
+ if (bssid_known) {
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (bss)
+ freq = bss->freq;
+ }
+ if (freq != data->assoc_info.freq) {
+ wpa_printf(MSG_DEBUG,
+ "Operating frequency changed from %u to %u MHz",
+ wpa_s->assoc_freq, data->assoc_info.freq);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
+ }
}
wpa_s->assoc_freq = data->assoc_info.freq;
+#ifndef CONFIG_NO_ROBUST_AV
wpas_handle_assoc_resp_qos_mgmt(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
+#endif /* CONFIG_NO_ROBUST_AV */
return 0;
}
@@ -3663,8 +3840,10 @@
bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
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);
+ bss_rsn = wpa_bss_get_rsne(wpa_s, wpa_s->current_bss, NULL,
+ wpa_s->valid_links);
+ bss_rsnx = wpa_bss_get_rsnxe(wpa_s, wpa_s->current_bss, NULL,
+ wpa_s->valid_links);
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
bss_wpa ? 2 + bss_wpa[1] : 0) ||
@@ -3717,6 +3896,261 @@
}
+static unsigned int wpas_ml_parse_assoc(struct wpa_supplicant *wpa_s,
+ struct ieee802_11_elems *elems,
+ struct ml_sta_link_info *ml_info)
+{
+ struct wpabuf *mlbuf;
+ struct ieee80211_eht_ml *ml;
+ size_t ml_len;
+ 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);
+ u8 expected_common_info_len = 9;
+ unsigned int i = 0;
+ u16 ml_control;
+
+ if (!wpa_s->valid_links || !elems->basic_mle || !elems->basic_mle_len)
+ return 0;
+
+ mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
+ if (!mlbuf)
+ return 0;
+
+ ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+ if (ml_len < sizeof(*ml))
+ goto out;
+
+ os_memset(ml_info, 0, sizeof(*ml_info) * MAX_NUM_MLD_LINKS);
+
+ ml_control = le_to_host16(ml->ml_control);
+
+ if ((ml_control & control) != control) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid presence BM=0x%x",
+ ml_control);
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ wpa_printf(MSG_DEBUG, "MLD: EML capabilities included");
+ expected_common_info_len += 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ wpa_printf(MSG_DEBUG, "MLD: MLD capabilities included");
+ expected_common_info_len += 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected: medium sync delay info present");
+ expected_common_info_len += 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Unexpected: MLD ID present");
+ expected_common_info_len++;
+ }
+
+ if (sizeof(*ml) + expected_common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Not enough bytes for common info. ml_len=%zu",
+ ml_len);
+ goto out;
+ }
+
+ common_info = (struct eht_ml_basic_common_info *) ml->variable;
+ 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;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: address: " MACSTR,
+ MAC2STR(common_info->mld_addr));
+
+ if (!ether_addr_equal(wpa_s->ap_mld_addr, common_info->mld_addr)) {
+ wpa_printf(MSG_DEBUG, "MLD: Mismatching MLD address (expected "
+ MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
+ goto out;
+ }
+
+ pos = common_info->variable;
+
+ /* Store the information for the association link */
+ ml_info[i].link_id = *pos;
+ pos++;
+
+ /* Skip the BSS Parameters Change Count */
+ pos++;
+
+ /* Skip the Medium Synchronization Delay Information if present */
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO)
+ pos += 2;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ eml_capa = WPA_GET_LE16(pos);
+ pos += 2;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+ mld_capa = WPA_GET_LE16(pos);
+ pos += 2;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: link_id=%u, eml=0x%x, mld=0x%x",
+ ml_info[i].link_id, eml_capa, mld_capa);
+
+ i++;
+
+ 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;
+ u8 nstr_bitmap_len = 0;
+ u16 ctrl;
+ const u8 *end;
+
+ wpa_printf(MSG_DEBUG, "MLD: Subelement len=%u", sub_elem_len);
+
+ if (sub_elem_len > ml_len - 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid link info len: %u > %zu",
+ 2 + sub_elem_len, ml_len);
+ goto out;
+ }
+
+ switch (*pos) {
+ case EHT_ML_SUB_ELEM_PER_STA_PROFILE:
+ break;
+ case EHT_ML_SUB_ELEM_FRAGMENT:
+ case EHT_ML_SUB_ELEM_VENDOR:
+ wpa_printf(MSG_DEBUG,
+ "MLD: Skip subelement id=%u, len=%u",
+ *pos, sub_elem_len);
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
+ default:
+ wpa_printf(MSG_DEBUG, "MLD: Unknown subelement ID=%u",
+ *pos);
+ goto out;
+ }
+
+ end = pos + 2 + sub_elem_len;
+
+ /* Skip the subelement ID and the length */
+ pos += 2;
+ ml_len -= 2;
+
+ if (end - pos < 2)
+ goto out;
+
+ /* Get the station control field */
+ ctrl = WPA_GET_LE16(pos);
+
+ pos += 2;
+ ml_len -= 2;
+
+ if (!(ctrl & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per STA complete profile expected");
+ goto out;
+ }
+
+ if (!(ctrl & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per STA MAC address not present");
+ goto out;
+ }
+
+ if (!(ctrl & EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Per STA TSF offset not present");
+ goto out;
+ }
+
+ if (!(ctrl & EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Beacon interval not present");
+ goto out;
+ }
+
+ if (!(ctrl & EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK)) {
+ 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)
+ nstr_bitmap_len = 2;
+ else
+ nstr_bitmap_len = 1;
+ }
+
+ if (!(ctrl & EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK)) {
+ 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) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid STA info len=%u, len=%u",
+ sta_info_len, *pos);
+ goto out;
+ }
+
+ /* Get the link address */
+ wpa_printf(MSG_DEBUG,
+ "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;
+ 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",
+ sub_elem_len, sta_info_len);
+
+ sub_elem_len -= sta_info_len + 2;
+ if (sub_elem_len < 4) {
+ wpa_printf(MSG_DEBUG, "MLD: Per STA profile too short");
+ goto out;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "MLD: STA profile", pos, sub_elem_len);
+ ml_info[i].status = WPA_GET_LE16(pos + 2);
+
+ pos += sub_elem_len;
+ ml_len -= sub_elem_len;
+
+ i++;
+ }
+
+ wpabuf_free(mlbuf);
+ return i;
+out:
+ wpabuf_free(mlbuf);
+ return 0;
+}
+
+
static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s)
{
struct driver_sta_mlo_info mlo;
@@ -3736,32 +4170,25 @@
if (!mlo.valid_links)
return 0;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(mlo.valid_links & BIT(i)))
- continue;
-
- if (os_memcmp(wpa_s->links[i].addr, mlo.links[i].addr,
- ETH_ALEN) != 0 ||
- os_memcmp(wpa_s->links[i].bssid, mlo.links[i].bssid,
- ETH_ALEN) != 0) {
+ for_each_link(mlo.valid_links, i) {
+ if (!ether_addr_equal(wpa_s->links[i].addr,
+ mlo.links[i].addr) ||
+ !ether_addr_equal(wpa_s->links[i].bssid,
+ mlo.links[i].bssid)) {
match = false;
break;
}
}
if (match && wpa_s->mlo_assoc_link_id == mlo.assoc_link_id &&
- os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr,
- ETH_ALEN) == 0)
+ ether_addr_equal(wpa_s->ap_mld_addr, mlo.ap_mld_addr))
return 0;
}
wpa_s->valid_links = mlo.valid_links;
wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
wpa_s->links[i].freq = mlo.links[i].freq;
@@ -3794,27 +4221,18 @@
wpa_mlo.valid_links = drv_mlo.valid_links;
wpa_mlo.req_links = drv_mlo.req_links;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ for_each_link(drv_mlo.req_links, i) {
struct wpa_bss *bss;
- if (!(drv_mlo.req_links & BIT(i)))
- continue;
-
bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
- bss = wpa_supplicant_get_new_bss(
- wpa_s, drv_mlo.links[i].bssid);
- }
-
- if (!bss) {
wpa_dbg(wpa_s, MSG_INFO,
"Failed to get MLO link %d BSS", i);
return -1;
}
- bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ bss_rsn = wpa_bss_get_rsne(wpa_s, bss, NULL, true);
+ bss_rsnx = wpa_bss_get_rsnxe(wpa_s, bss, NULL, true);
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;
@@ -3939,7 +4357,7 @@
#endif
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
- if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(bssid, wpa_s->bssid)) {
if (os_reltime_initialized(&wpa_s->session_start)) {
os_reltime_age(&wpa_s->session_start,
&wpa_s->session_length);
@@ -4138,9 +4556,9 @@
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 200000 &&
- os_memcmp(wpa_s->pending_eapol_rx_src,
- wpa_s->valid_links ? wpa_s->ap_mld_addr : bssid,
- ETH_ALEN) == 0) {
+ ether_addr_equal(wpa_s->pending_eapol_rx_src,
+ wpa_s->valid_links ? wpa_s->ap_mld_addr :
+ bssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
"frame that was received just before "
"association notification");
@@ -4184,6 +4602,7 @@
wpas_wps_notify_assoc(wpa_s, bssid);
+#ifndef CONFIG_NO_WMM_AC
if (data) {
wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
@@ -4192,6 +4611,7 @@
if (wpa_s->reassoc_same_bss)
wmm_ac_restore_tspecs(wpa_s);
}
+#endif /* CONFIG_NO_WMM_AC */
#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
bss = wpa_bss_get_bssid(wpa_s, bssid);
@@ -4284,16 +4704,10 @@
int locally_generated)
{
const u8 *bssid;
- int authenticating;
- u8 prev_pending_bssid[ETH_ALEN];
struct wpa_bss *fast_reconnect = NULL;
struct wpa_ssid *fast_reconnect_ssid = NULL;
- struct wpa_ssid *last_ssid;
struct wpa_bss *curr = NULL;
- authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
- os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
-
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/*
* At least Host AP driver and a Prism3 card seemed to be
@@ -4323,7 +4737,7 @@
"pre-shared key may be incorrect");
if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
return; /* P2P group removed */
- wpas_auth_failed(wpa_s, "WRONG_KEY", prev_pending_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,
@@ -4374,7 +4788,7 @@
if (is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
- wpas_connection_failed(wpa_s, bssid);
+ wpas_connection_failed(wpa_s, bssid, NULL);
wpa_sm_notify_disassoc(wpa_s->wpa);
ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE);
@@ -4387,17 +4801,11 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
- last_ssid = wpa_s->current_ssid;
wpa_supplicant_mark_disassoc(wpa_s);
if (curr)
wpa_bss_remove(wpa_s, curr, "Connection to AP lost");
- if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
- sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
- wpa_s->current_ssid = last_ssid;
- }
-
if (fast_reconnect &&
!wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
!disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
@@ -4747,7 +5155,7 @@
MACSTR " TargetAP " MACSTR " status %u",
MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
- if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta_addr, wpa_s->own_addr)) {
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
" in FT Action Response", MAC2STR(sta_addr));
return;
@@ -4974,6 +5382,20 @@
}
+static void wpas_beacon_hint(struct wpa_supplicant *wpa_s, const char *title,
+ struct frequency_attrs *attrs)
+{
+ if (!attrs->freq)
+ return;
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_BEACON_HINT
+ "%s freq=%u max_tx_power=%u%s%s%s",
+ title, attrs->freq, attrs->max_tx_power,
+ attrs->disabled ? " disabled=1" : "",
+ attrs->no_ir ? " no_ir=1" : "",
+ attrs->radar ? " radar=1" : "");
+}
+
+
void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s,
struct channel_list_changed *info)
{
@@ -4995,6 +5417,13 @@
reg_init_str(info->initiator), reg_type_str(info->type),
info->alpha2[0] ? " alpha2=" : "",
info->alpha2[0] ? info->alpha2 : "");
+
+ if (info->initiator == REGDOM_BEACON_HINT) {
+ wpas_beacon_hint(ifs, "before",
+ &info->beacon_hint_before);
+ wpas_beacon_hint(ifs, "after",
+ &info->beacon_hint_after);
+ }
}
if (wpa_s->drv_priv == NULL)
@@ -5021,11 +5450,10 @@
wpa_dbg(ifs, MSG_DEBUG,
"Channel list changed - restart sched_scan");
wpas_scan_restart_sched_scan(ifs);
- } else if (ifs->scanning && !was_6ghz_enabled &&
- ifs->is_6ghz_enabled) {
- /* Look for APs in the 6 GHz band */
+ } else if (!was_6ghz_enabled && ifs->is_6ghz_enabled) {
wpa_dbg(ifs, MSG_INFO,
- "Channel list changed - trigger 6 GHz-only scan");
+ "Channel list changed: 6 GHz was enabled");
+
ifs->crossed_6ghz_dom = true;
}
}
@@ -5055,10 +5483,12 @@
" Category=%u DataLen=%d freq=%d MHz",
MAC2STR(mgmt->sa), category, (int) plen, freq);
+#ifndef CONFIG_NO_WMM_AC
if (category == WLAN_ACTION_WMM) {
wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
return;
}
+#endif /* CONFIG_NO_WMM_AC */
#ifdef CONFIG_IEEE80211R
if (category == WLAN_ACTION_FT) {
@@ -5122,7 +5552,7 @@
size_t qlen = plen - 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
MACSTR, MAC2STR(mgmt->sa));
- if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(mgmt->sa, wpa_s->bssid) &&
qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
pos[1] <= qlen - 2 && pos[1] >= 16)
wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
@@ -5130,6 +5560,7 @@
}
#endif /* CONFIG_INTERWORKING */
+#ifndef CONFIG_NO_RRM
if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
@@ -5152,6 +5583,7 @@
rssi);
return;
}
+#endif /* CONFIG_NO_RRM */
#ifdef CONFIG_FST
if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
@@ -5160,6 +5592,17 @@
}
#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;
+ }
+#endif /* CONFIG_NAN_USD */
+
#ifdef CONFIG_DPP
if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
@@ -5172,6 +5615,7 @@
}
#endif /* CONFIG_DPP */
+#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,
@@ -5192,6 +5636,7 @@
payload + 4, plen - 4);
return;
}
+#endif /* CONFIG_NO_ROBUST_AV */
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
category, payload, plen, freq);
@@ -5395,6 +5840,9 @@
union wpa_event_data *data)
{
const u8 *bssid = data->assoc_reject.bssid;
+ struct ieee802_11_elems elems;
+ struct ml_sta_link_info ml_info[MAX_NUM_MLD_LINKS];
+ const u8 *link_bssids[MAX_NUM_MLD_LINKS + 1];
#ifdef CONFIG_MBO
struct wpa_bss *reject_bss;
#endif /* CONFIG_MBO */
@@ -5450,7 +5898,7 @@
if (!bss) {
bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
if (!bss) {
- wpas_connection_failed(wpa_s, bssid);
+ wpas_connection_failed(wpa_s, bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
@@ -5485,7 +5933,7 @@
if (!bss || wpa_s->dpp_pfs_fallback) {
wpa_printf(MSG_DEBUG,
"DPP: Updated PFS policy for next try");
- wpas_connection_failed(wpa_s, bssid);
+ wpas_connection_failed(wpa_s, bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
@@ -5522,8 +5970,33 @@
}
#endif /* CONFIG_MBO */
+ /* Check for other failed links in the response */
+ os_memset(link_bssids, 0, sizeof(link_bssids));
+ if (ieee802_11_parse_elems(data->assoc_reject.resp_ies,
+ data->assoc_reject.resp_ies_len,
+ &elems, 1) != ParseFailed) {
+ unsigned int n_links, i, idx;
+
+ idx = 0;
+ n_links = wpas_ml_parse_assoc(wpa_s, &elems, ml_info);
+
+ for (i = 1; i < n_links; i++) {
+ /* The status cannot be success here.
+ * Add the link to the failed list if it is reporting
+ * an error. The only valid "non-error" status is
+ * TX_LINK_NOT_ACCEPTED as that means this link may
+ * still accept an association from us.
+ */
+ if (ml_info[i].status !=
+ WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED) {
+ link_bssids[idx] = ml_info[i].bssid;
+ idx++;
+ }
+ }
+ }
+
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
- sme_event_assoc_reject(wpa_s, data);
+ sme_event_assoc_reject(wpa_s, data, link_bssids);
return;
}
@@ -5560,7 +6033,7 @@
}
#endif /* CONFIG_FILS */
- wpas_connection_failed(wpa_s, bssid);
+ wpas_connection_failed(wpa_s, bssid, link_bssids);
wpa_supplicant_mark_disassoc(wpa_s);
}
@@ -5572,7 +6045,7 @@
int res;
if (!data || wpa_s->wpa_state != WPA_COMPLETED ||
- os_memcmp(data->sa, wpa_s->bssid, ETH_ALEN) != 0)
+ !ether_addr_equal(data->sa, wpa_s->bssid))
return;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_UNPROT_BEACON MACSTR,
MAC2STR(data->sa));
@@ -5625,13 +6098,10 @@
pos += res;
if (!info->default_map) {
- for (i = 0; i < MAX_NUM_MLD_LINKS && end > pos; i++) {
+ for_each_link(info->valid_links, i) {
char uplink_map_str[9];
char downlink_map_str[9];
- if (!(info->valid_links & BIT(i)))
- continue;
-
bitmap_to_str(info->t2lmap[i].uplink, uplink_map_str);
bitmap_to_str(info->t2lmap[i].downlink,
downlink_map_str);
@@ -5662,7 +6132,7 @@
return;
}
- if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(bssid, wpa_s->bssid)) {
os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
wpa_supplicant_update_current_bss(wpa_s, wpa_s->bssid);
wpas_notify_bssid_changed(wpa_s);
@@ -5911,6 +6381,13 @@
" type=%d stype=%d",
MAC2STR(data->tx_status.dst),
data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_WNM
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+ wnm_btm_resp_tx_status(wpa_s, data->tx_status.data,
+ data->tx_status.data_len) == 0)
+ break;
+#endif /* CONFIG_WNM */
#ifdef CONFIG_PASN
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
@@ -5947,8 +6424,8 @@
*/
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
- os_memcmp(wpa_s->p2pdev->pending_action_dst,
- data->tx_status.dst, ETH_ALEN) == 0) {
+ ether_addr_equal(wpa_s->p2pdev->pending_action_dst,
+ data->tx_status.dst)) {
offchannel_send_action_tx_status(
wpa_s->p2pdev, data->tx_status.dst,
data->tx_status.data,
@@ -6271,6 +6748,11 @@
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
@@ -6283,6 +6765,10 @@
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 */
break;
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
@@ -6321,7 +6807,7 @@
wpa_s, NULL);
os_memcpy(addr, wpa_s->own_addr, ETH_ALEN);
wpa_supplicant_update_mac_addr(wpa_s);
- if (os_memcmp(addr, wpa_s->own_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(addr, wpa_s->own_addr))
wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
else
wpa_sm_pmksa_cache_reconfig(wpa_s->wpa);
@@ -6455,8 +6941,8 @@
#endif /* CONFIG_IBSS_RSN */
break;
case EVENT_DRIVER_GTK_REKEY:
- if (os_memcmp(data->driver_gtk_rekey.bssid,
- wpa_s->bssid, ETH_ALEN))
+ if (!ether_addr_equal(data->driver_gtk_rekey.bssid,
+ wpa_s->bssid))
break;
if (!wpa_s->wpa)
break;
@@ -6618,6 +7104,9 @@
#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)
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index c301f74..7d29931 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -199,10 +199,16 @@
gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
{
struct gas_query_pending *q;
+ struct wpa_supplicant *wpa_s = gas->wpa_s;
+
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ 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;
}
return NULL;
}
@@ -243,7 +249,7 @@
wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
" result=%d query=%p dialog_token=%u dur=%d ms",
freq, MAC2STR(dst), result, query, query->dialog_token, dur);
- if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(dst, query->addr)) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
@@ -300,7 +306,7 @@
(!gas->wpa_s->conf->gas_address3 ||
(gas->wpa_s->current_ssid &&
gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
- os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)))
+ ether_addr_equal(query->addr, gas->wpa_s->bssid))))
bssid = query->addr;
else
bssid = wildcard_bssid;
@@ -668,7 +674,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(dst, q->addr) &&
dialog_token == q->dialog_token)
return 0;
}
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index eaf0803..f14e6cb 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -27,6 +27,7 @@
#include "interworking.h"
#include "hs20_supplicant.h"
#include "base64.h"
+#include "notify.h"
#define OSU_MAX_ITEMS 10
@@ -68,7 +69,6 @@
void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss = wpa_s->current_bss;
- u8 *bssid = wpa_s->bssid;
const u8 *ie;
const u8 *ext_capa;
u32 filter = 0;
@@ -79,9 +79,8 @@
|| !is_hs20_config(wpa_s)
#endif
) {
- wpa_printf(MSG_DEBUG,
- "Not configuring frame filtering - BSS " MACSTR
- " is not a Hotspot 2.0 network", MAC2STR(bssid));
+ /* Not configuring frame filtering - BSS is not a Hotspot 2.0
+ * network */
return;
}
@@ -336,7 +335,7 @@
struct icon_entry *icon;
dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
- if (os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(icon->bssid, bssid) &&
os_strcmp(icon->file_name, file_name) == 0 && icon->image)
return icon;
}
@@ -412,7 +411,7 @@
dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
list) {
- if ((!bssid || os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0) &&
+ if ((!bssid || ether_addr_equal(icon->bssid, bssid)) &&
(!file_name ||
os_strcmp(icon->file_name, file_name) == 0)) {
dl_list_del(&icon->list);
@@ -458,7 +457,7 @@
list) {
if (icon == new_icon)
continue;
- if (os_memcmp(icon->bssid, new_icon->bssid, ETH_ALEN) == 0 &&
+ 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);
@@ -479,7 +478,7 @@
dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
if (icon->dialog_token == dialog_token && !icon->image &&
- os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) {
+ ether_addr_equal(icon->bssid, sa)) {
icon->image = os_memdup(pos, slen);
if (!icon->image)
return -1;
@@ -1102,7 +1101,7 @@
prov_anqp = bss->anqp->hs20_osu_providers_list;
if (prov_anqp == NULL)
continue;
- ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ ie = wpa_bss_get_rsne(wpa_s, bss, NULL, false);
if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 &&
(data.key_mgmt & WPA_KEY_MGMT_OSEN)) {
osu_ssid2 = bss->ssid;
@@ -1354,8 +1353,7 @@
return;
}
- wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url);
- wpas_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
+ wpas_notify_hs20_t_c_acceptance(wpa_s, url);
}
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 5b31f7b..25039a0 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -30,7 +30,7 @@
struct ibss_rsn_peer *peer;
for (peer = ibss_rsn->peers; peer; peer = peer->next)
- if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, peer->addr))
break;
return peer;
}
@@ -484,8 +484,8 @@
"\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x02"
- "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) !=
- WPA_IE_OK) {
+ "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0,
+ NULL) != WPA_IE_OK) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
return -1;
}
@@ -672,7 +672,7 @@
for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
prev = peer, peer = peer->next) {
- if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(peermac, peer->addr)) {
if (prev == NULL)
ibss_rsn->peers = peer->next;
else
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index cb1165e..651907b 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1734,7 +1734,7 @@
" 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.
@@ -2467,7 +2467,7 @@
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 "
@@ -2519,7 +2519,7 @@
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.
@@ -2674,7 +2674,7 @@
continue;
if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
continue;
- if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(bss->hessid, other->hessid))
continue;
if (bss->ssid_len != other->ssid_len ||
os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
@@ -2820,7 +2820,7 @@
struct wpa_bss *bss;
int res;
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
if (!bss && !freq) {
wpa_printf(MSG_WARNING,
"ANQP: Cannot send query without BSS freq info");
@@ -3169,13 +3169,13 @@
*/
dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
if (tmp == wpa_s->interworking_gas_bss &&
- os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
+ ether_addr_equal(tmp->bssid, dst)) {
bss = tmp;
break;
}
}
if (bss == NULL)
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
pos = wpabuf_head(resp);
end = pos + wpabuf_len(resp);
@@ -3206,12 +3206,12 @@
}
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:
- wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
- MAC2STR(dst), anqp_result);
wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
}
@@ -3290,7 +3290,7 @@
u8 query_resp_len_limit = 0;
freq = wpa_s->assoc_freq;
- bss = wpa_bss_get_bssid(wpa_s, dst);
+ bss = wpa_bss_get_bssid_latest(wpa_s, dst);
if (bss)
freq = bss->freq;
if (freq <= 0)
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 31d0fce..80fbe01 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -115,7 +115,7 @@
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 */
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 486fc6a..85c1ea8 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -231,7 +231,7 @@
hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
ifmsh->conf->vht_capab,
- he_capab, NULL)) {
+ he_capab, NULL, 0)) {
wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
wpa_supplicant_mesh_deinit(wpa_s, true);
return -1;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 138c013..b2fc127 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -468,6 +468,7 @@
params.plink_state = state;
params.peer_aid = sta->peer_aid;
params.set = 1;
+ params.mld_link_id = -1;
ret = wpa_drv_sta_add(wpa_s, ¶ms);
if (ret) {
@@ -697,6 +698,7 @@
params.addr = sta->addr;
params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED;
params.set = 1;
+ params.mld_link_id = -1;
wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR,
MAC2STR(sta->addr));
@@ -816,6 +818,7 @@
params.eht_capab_len = sta->eht_capab_len;
params.flags |= WPA_STA_WMM;
params.flags_mask |= WPA_STA_AUTHENTICATED;
+ params.mld_link_id = -1;
if (conf->security == MESH_CONF_SEC_NONE) {
params.flags |= WPA_STA_AUTHORIZED;
params.flags |= WPA_STA_AUTHENTICATED;
@@ -851,7 +854,7 @@
if (ssid && ssid->no_auto_peer &&
(is_zero_ether_addr(data->mesh_required_peer) ||
- os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) {
+ !ether_addr_equal(data->mesh_required_peer, addr))) {
wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
MACSTR " because of no_auto_peer", MAC2STR(addr));
if (data->mesh_pending_auth) {
@@ -862,7 +865,7 @@
mgmt = wpabuf_head(data->mesh_pending_auth);
os_reltime_age(&data->mesh_pending_auth_time, &age);
if (age.sec < 2 &&
- os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mgmt->sa, addr)) {
wpa_printf(MSG_DEBUG,
"mesh: Process pending Authentication frame from %u.%06u seconds ago",
(unsigned int) age.sec,
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 12dcc30..ada53c7 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -142,6 +142,23 @@
}
+static int auth_for_each_sta(
+ void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx)
+{
+ struct mesh_rsn *rsn = ctx;
+ struct hostapd_data *hapd;
+ struct sta_info *sta;
+
+ hapd = rsn->wpa_s->ifmsh->bss[0];
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
+ return 1;
+ }
+ return 0;
+}
+
+
static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
enum mfp_options ieee80211w, int ocv)
{
@@ -151,6 +168,7 @@
.get_psk = auth_get_psk,
.set_key = auth_set_key,
.start_ampe = auth_start_ampe,
+ .for_each_sta = auth_for_each_sta,
};
u8 seq[6] = {};
@@ -386,7 +404,8 @@
" - try to use PMKSA caching instead of new SAE authentication",
MAC2STR(sta->addr));
wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
- sta->sae->pmkid, sta->sae->pmk);
+ sta->sae->pmkid, sta->sae->pmk,
+ &sta->sae->pmk_len);
sae_accept_sta(hapd, sta);
sta->mesh_sae_pmksa_caching = 1;
return 0;
diff --git a/wpa_supplicant/nan_usd.c b/wpa_supplicant/nan_usd.c
new file mode 100644
index 0000000..657b302
--- /dev/null
+++ b/wpa_supplicant/nan_usd.c
@@ -0,0 +1,513 @@
+/*
+ * 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/wpa_supplicant/nan_usd.h b/wpa_supplicant/nan_usd.h
new file mode 100644
index 0000000..149ac9e
--- /dev/null
+++ b/wpa_supplicant/nan_usd.h
@@ -0,0 +1,46 @@
+/*
+ * 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/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index a1746da..d53ae56 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -467,6 +467,8 @@
wpa_s->ml_connect_probe_ssid = NULL;
wpa_s->ml_connect_probe_bss = NULL;
}
+ if (wpa_s->connect_without_scan == ssid)
+ wpa_s->connect_without_scan = NULL;
#if defined(CONFIG_SME) && defined(CONFIG_SAE)
if (wpa_s->sme.ext_auth_wpa_ssid == ssid)
wpa_s->sme.ext_auth_wpa_ssid = NULL;
@@ -627,6 +629,15 @@
}
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ if (wpa_s->p2p_mgmt)
+ return;
+
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_ANQP, id);
+}
+
+
void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
{
if (wpa_s->p2p_mgmt)
@@ -935,7 +946,8 @@
const u8 *p2p_dev_addr, const u8 *ip)
{
if (authorized)
- wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr, ip);
+ wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr,
+ ip);
else
wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
}
@@ -1057,11 +1069,14 @@
const char *result,
const struct wpa_bss_anqp *anqp)
{
+ wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+ MAC2STR(bssid), result);
#ifdef CONFIG_INTERWORKING
if (!wpa_s || !bssid || !anqp)
return;
wpas_aidl_notify_anqp_query_done(wpa_s, bssid, result, anqp);
+ wpas_dbus_signal_anqp_query_done(wpa_s, bssid, result);
#endif /* CONFIG_INTERWORKING */
}
@@ -1103,15 +1118,6 @@
#endif /* CONFIG_HS20 */
}
-void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
- struct wpa_supplicant *wpa_s, const char *url) {
-#ifdef CONFIG_HS20
- if (!wpa_s || !url)
- return;
-
- wpas_aidl_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
-#endif /* CONFIG_HS20 */
-}
#ifdef CONFIG_MESH
@@ -1368,6 +1374,7 @@
wpas_dbus_signal_interworking_select_done(wpa_s);
}
+
#endif /* CONFIG_INTERWORKING */
void wpas_notify_eap_method_selected(struct wpa_supplicant *wpa_s,
@@ -1437,3 +1444,16 @@
wpas_aidl_notify_qos_policy_scs_response(wpa_s, num_scs_resp, scs_resp);
}
+
+void wpas_notify_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
+ const char *url)
+{
+#ifdef CONFIG_HS20
+ if (!wpa_s || !url)
+ return;
+
+ wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url);
+ wpas_aidl_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
+ wpas_dbus_signal_hs20_t_c_acceptance(wpa_s, url);
+#endif /* CONFIG_HS20 */
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 260f439..a584884 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -89,6 +89,8 @@
void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
unsigned int id);
void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s,
+ unsigned int id);
void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
@@ -182,8 +184,6 @@
void wpas_notify_hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s,
u8 code, u16 reauth_delay,
const char *url);
-void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
- struct wpa_supplicant *wpa_s, const char *url);
void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, bool conn_status_requested);
void wpas_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
@@ -232,5 +232,7 @@
unsigned int num_scs_resp, int **scs_resp);
void wpas_notify_mlo_info_change_reason(struct wpa_supplicant *wpa_s,
enum mlo_info_change_reason reason);
+void wpas_notify_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
+ const char *url);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index e40cf5b..9e591d7 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -23,12 +23,12 @@
{
struct wpa_supplicant *iface;
- if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(src, wpa_s->own_addr)) {
#ifdef CONFIG_P2P
if (wpa_s->p2p_mgmt && wpa_s != wpa_s->parent &&
wpa_s->parent->ap_iface &&
- os_memcmp(wpa_s->parent->own_addr,
- wpa_s->own_addr, ETH_ALEN) == 0 &&
+ ether_addr_equal(wpa_s->parent->own_addr,
+ wpa_s->own_addr) &&
wpabuf_len(wpa_s->pending_action_tx) >= 2 &&
*wpabuf_head_u8(wpa_s->pending_action_tx) !=
WLAN_ACTION_PUBLIC) {
@@ -52,7 +52,7 @@
*/
iface = wpa_s->global->ifaces;
while (iface) {
- if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(src, iface->own_addr))
break;
iface = iface->next;
}
@@ -186,7 +186,7 @@
return;
}
- if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(dst, wpa_s->pending_action_dst)) {
wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
"unknown destination address");
return;
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index b4ad3ca..ff11d20 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,12 @@
unsigned int *flags)
{
int i;
- bool is_6ghz = op_class >= 131 && op_class <= 136;
+ bool is_6ghz = is_6ghz_op_class(op_class);
for (i = 0; i < mode->num_channels; i++) {
bool chan_is_6ghz;
- chan_is_6ghz = mode->channels[i].freq >= 5935 &&
- mode->channels[i].freq <= 7115;
+ chan_is_6ghz = is_6ghz_freq(mode->channels[i].freq);
if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
break;
}
@@ -187,6 +186,69 @@
}
+static int get_center_320mhz(struct hostapd_hw_modes *mode, u8 channel,
+ const u8 *center_channels, size_t num_chan)
+{
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A || !mode->is_6ghz)
+ return 0;
+
+ for (i = 0; i < num_chan; i++) {
+ /*
+ * In 320 MHz, the bandwidth "spans" 60 channels (e.g., 65-125),
+ * so the center channel is 30 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 30 &&
+ channel <= center_channels[i] + 30)
+ return center_channels[i];
+ }
+
+ return 0;
+}
+
+
+static enum chan_allowed verify_320mhz(struct hostapd_hw_modes *mode,
+ u8 op_class, u8 channel)
+{
+ u8 center_chan;
+ unsigned int i;
+ bool no_ir = false;
+ const u8 *center_channels;
+ size_t num_chan;
+ const u8 center_channels_6ghz[] = { 31, 63, 95, 127, 159, 191 };
+
+ center_channels = center_channels_6ghz;
+ num_chan = ARRAY_SIZE(center_channels_6ghz);
+
+ center_chan = get_center_320mhz(mode, channel, center_channels,
+ num_chan);
+ if (!center_chan)
+ return NOT_ALLOWED;
+
+ /* Check all the channels are available */
+ for (i = 0; i < 16; i++) {
+ unsigned int flags;
+ u8 adj_chan = center_chan - 30 + i * 4;
+
+ if (allow_channel(mode, op_class, adj_chan, &flags) ==
+ NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (!(flags & HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL))
+ return NOT_ALLOWED;
+
+ if (flags & HOSTAPD_CHAN_NO_IR)
+ no_ir = true;
+ }
+
+ if (no_ir)
+ return NO_IR;
+
+ return ALLOWED;
+}
+
+
enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
u8 channel, u8 bw)
{
@@ -228,6 +290,13 @@
* result and use only the 80 MHz specific version.
*/
res2 = res = verify_80mhz(mode, op_class, channel);
+ } else if (bw == BW320) {
+ /*
+ * channel is a center channel and as such, not necessarily a
+ * valid 20 MHz channels. Override earlier allow_channel()
+ * result and use only the 320 MHz specific version.
+ */
+ res2= res = verify_320mhz(mode, op_class, channel);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -251,6 +320,7 @@
int z;
int freq2 = 0;
int freq5 = 0;
+ bool freq6 = false;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode,
is_6ghz_op_class(op_class->op_class));
@@ -265,7 +335,9 @@
if (f == 0)
break; /* end of list */
- if (f > 4000 && f < 6000)
+ if (is_6ghz_freq(f))
+ freq6 = true;
+ else if (f > 4000 && f < 6000)
freq5 = 1;
else if (f > 2400 && f < 2500)
freq2 = 1;
@@ -274,8 +346,11 @@
/* No frequencies specified, can use anything hardware supports.
*/
freq2 = freq5 = 1;
+ freq6 = true;
}
+ if (is_6ghz_op_class(op_class->op_class) && !freq6)
+ return 0;
if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
return 0;
if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
@@ -453,6 +528,7 @@
u8 op, current, chan;
u8 *ie_len;
size_t res;
+ bool op128 = false, op130 = false, op133 = false, op135 = false;
/*
* Determine the current operating class correct mode based on
@@ -480,8 +556,50 @@
wpabuf_put_u8(buf, current);
for (op = 0; global_op_class[op].op_class; op++) {
- if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
- wpabuf_put_u8(buf, global_op_class[op].op_class);
+ bool supp;
+ u8 op_class = global_op_class[op].op_class;
+
+ supp = wpas_op_class_supported(wpa_s, ssid,
+ &global_op_class[op]);
+ if (!supp)
+ continue;
+ switch (op_class) {
+ case 128:
+ op128 = true;
+ break;
+ case 130:
+ op130 = true;
+ break;
+ case 133:
+ op133 = true;
+ break;
+ case 135:
+ op135 = true;
+ break;
+ }
+ if (is_80plus_op_class(op_class))
+ continue;
+
+ /* Add a 1-octet operating class to the Operating Class field */
+ wpabuf_put_u8(buf, global_op_class[op].op_class);
+ }
+
+ /* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */
+ if ((op128 && op130) || (op133 && op135)) {
+ /* Operating Class Duple Sequence field */
+
+ /* Zero Delimiter */
+ wpabuf_put_u8(buf, 0);
+
+ /* Operating Class Duple List */
+ if (op128 && op130) {
+ wpabuf_put_u8(buf, 130);
+ wpabuf_put_u8(buf, 128);
+ }
+ if (op133 && op135) {
+ wpabuf_put_u8(buf, 135);
+ wpabuf_put_u8(buf, 133);
+ }
}
*ie_len = wpabuf_len(buf) - 2;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index c150863..9c20ee5 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1103,6 +1103,8 @@
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+ wpa_s->p2p_go_no_pri_sec_switch = 0;
+
return 0;
}
@@ -1187,7 +1189,7 @@
"group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 &&
- os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+ ether_addr_equal(go_dev_addr, s->bssid) &&
s->ssid_len == ssid->ssid_len &&
os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
break;
@@ -1293,8 +1295,8 @@
return;
for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
- if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
- ETH_ALEN) != 0)
+ if (!ether_addr_equal(s->p2p_client_list + i * 2 * ETH_ALEN,
+ addr))
continue;
if (i == s->num_p2p_clients - 1)
@@ -1595,8 +1597,8 @@
if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
wpa_s->pending_pd_before_join &&
- (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
- os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
+ (ether_addr_equal(dst, wpa_s->pending_join_dev_addr) ||
+ ether_addr_equal(dst, wpa_s->pending_join_iface_addr)) &&
wpa_s->p2p_fallback_to_go_neg) {
wpa_s->pending_pd_before_join = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
@@ -2937,8 +2939,8 @@
char params[20];
if (wpa_s->pending_pd_before_join &&
- (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
- os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+ (ether_addr_equal(peer, wpa_s->pending_join_dev_addr) ||
+ ether_addr_equal(peer, wpa_s->pending_join_iface_addr))) {
wpa_s->pending_pd_before_join = 0;
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation");
@@ -3232,9 +3234,8 @@
" to join an active group (SSID: %s)",
MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
- (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
- == 0 ||
- os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+ (ether_addr_equal(go_dev_addr, wpa_s->p2p_auth_invite) ||
+ ether_addr_equal(sa, wpa_s->p2p_auth_invite))) {
wpa_printf(MSG_DEBUG, "P2P: Accept previously "
"authorized invitation");
goto accept_inv;
@@ -3271,7 +3272,7 @@
}
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
- os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
+ ether_addr_equal(sa, wpa_s->p2p_auth_invite)) {
wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
"invitation to re-invoke a persistent group");
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
@@ -3280,7 +3281,7 @@
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 &&
- os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+ ether_addr_equal(s->bssid, go_dev_addr) &&
s->ssid_len == ssid_len &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
break;
@@ -3482,13 +3483,13 @@
return;
for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
- if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
- ETH_ALEN) == 0)
+ if (ether_addr_equal(ssid->p2p_client_list + i * 2 * ETH_ALEN,
+ peer))
break;
}
if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
if (ssid->mode != WPAS_MODE_P2P_GO &&
- os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
+ ether_addr_equal(ssid->bssid, peer)) {
wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
"due to invitation result", ssid->id);
wpas_notify_network_removed(wpa_s, ssid);
@@ -4185,7 +4186,7 @@
struct wpa_supplicant *wpa_s = ctx;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(wpa_s->own_addr, interface_addr))
break;
}
if (wpa_s == NULL)
@@ -4224,7 +4225,7 @@
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
continue;
- if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(wpa_s->go_dev_addr, peer_dev_addr))
return wpa_s;
}
@@ -4435,7 +4436,7 @@
while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
if (go && ssid && ssid_len &&
s->ssid_len == ssid_len &&
- os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
+ ether_addr_equal(go, s->bssid) &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
break;
@@ -4451,8 +4452,8 @@
}
for (i = 0; i < s->num_p2p_clients; i++) {
- if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
- peer, ETH_ALEN) != 0)
+ if (!ether_addr_equal(s->p2p_client_list +
+ i * 2 * ETH_ALEN, peer))
continue;
os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
@@ -4606,7 +4607,7 @@
break;
if (s && s->ssid_len == stale->ssid_len &&
- os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
+ ether_addr_equal(stale->bssid, s->bssid) &&
os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
break;
@@ -4622,9 +4623,8 @@
size_t i;
for (i = 0; i < stale->num_p2p_clients; i++) {
- if (os_memcmp(stale->p2p_client_list +
- i * ETH_ALEN,
- dev, ETH_ALEN) == 0) {
+ if (ether_addr_equal(stale->p2p_client_list +
+ i * ETH_ALEN, dev)) {
os_memmove(stale->p2p_client_list +
i * ETH_ALEN,
stale->p2p_client_list +
@@ -5474,8 +5474,8 @@
p2p_get_interface_addr(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr,
iface_addr) == 0 &&
- os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0
- && !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
+ !ether_addr_equal(iface_addr, wpa_s->pending_join_dev_addr) &&
+ !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
"address for join from " MACSTR " to " MACSTR
" based on newly discovered P2P peer entry",
@@ -5515,10 +5515,9 @@
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
dev_addr) == 0 &&
- os_memcmp(wpa_s->pending_join_dev_addr,
- wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
- os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
- ETH_ALEN) != 0) {
+ ether_addr_equal(wpa_s->pending_join_dev_addr,
+ wpa_s->pending_join_iface_addr) &&
+ !ether_addr_equal(dev_addr, wpa_s->pending_join_dev_addr)) {
wpa_printf(MSG_DEBUG,
"P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
MAC2STR(dev_addr),
@@ -7006,7 +7005,8 @@
return -1;
}
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, selected_freq, vht_center_freq2,
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, selected_freq,
+ vht_center_freq2,
ht40, vht, max_oper_chwidth, he, edmg,
NULL))
return -1;
@@ -7100,7 +7100,7 @@
* fetch time on the same radio so it reflects the actual time the last
* scan result event occurred.
*/
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, go_bssid);
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs == wpa_s)
@@ -7504,9 +7504,10 @@
}
-static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s,
+ bool force)
{
- if (!offchannel_pending_action_tx(wpa_s))
+ if (!offchannel_pending_action_tx(wpa_s) && !force)
return;
if (wpa_s->p2p_send_action_work) {
@@ -7516,6 +7517,8 @@
offchannel_send_action_done(wpa_s);
}
+ if (!offchannel_pending_action_tx(wpa_s))
+ return;
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
offchannel_clear_pending_action_tx(wpa_s);
@@ -7529,7 +7532,7 @@
u8 seek_cnt, const char **seek_string, int freq,
bool include_6ghz)
{
- wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpas_p2p_clear_pending_action_tx(wpa_s, false);
wpa_s->global->p2p_long_listen = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
@@ -7575,7 +7578,7 @@
static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
{
- wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpas_p2p_clear_pending_action_tx(wpa_s, true);
wpa_s->global->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
@@ -7620,7 +7623,7 @@
}
wpa_supplicant_cancel_sched_scan(wpa_s);
- wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpas_p2p_clear_pending_action_tx(wpa_s, false);
if (timeout == 0) {
/*
@@ -8740,13 +8743,13 @@
return s;
continue;
}
- if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(s->bssid, addr))
return s; /* peer is GO in the persistent group */
if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
continue;
for (i = 0; i < s->num_p2p_clients; i++) {
- if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
- addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(s->p2p_client_list +
+ i * 2 * ETH_ALEN, addr))
return s; /* peer is P2P client in persistent
* group */
}
@@ -8888,9 +8891,9 @@
dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry,
list) {
if ((iface_addr && !psk->p2p &&
- os_memcmp(addr, psk->addr, ETH_ALEN) == 0) ||
+ ether_addr_equal(addr, psk->addr)) ||
(!iface_addr && psk->p2p &&
- os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) {
+ ether_addr_equal(addr, psk->addr))) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove persistent group PSK list entry for "
MACSTR " p2p=%u",
@@ -9029,9 +9032,9 @@
prev = NULL;
psk = hapd->conf->ssid.wpa_psk;
while (psk) {
- if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) ||
+ if ((iface_addr && ether_addr_equal(peer, psk->addr)) ||
(!iface_addr &&
- os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) {
+ ether_addr_equal(peer, psk->p2p_dev_addr))) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for "
MACSTR " iface_addr=%d",
MAC2STR(peer), iface_addr);
@@ -9885,6 +9888,7 @@
os_memset(&csa_settings, 0, sizeof(csa_settings));
csa_settings.cs_count = P2P_GO_CSA_COUNT;
csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
+ csa_settings.link_id = -1;
csa_settings.freq_params.freq = params.freq;
csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
csa_settings.freq_params.ht_enabled = conf->ieee80211n;
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
index 312f46b..576180d 100644
--- a/wpa_supplicant/p2p_supplicant_sd.c
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -1222,12 +1222,22 @@
bsrv = os_zalloc(sizeof(*bsrv));
if (bsrv == NULL)
return -1;
- bsrv->query = query;
- bsrv->resp = resp;
+ bsrv->query = wpabuf_dup(query);
+ if (!bsrv->query)
+ goto error_bsrv;
+ bsrv->resp = wpabuf_dup(resp);
+ if (!bsrv->resp)
+ goto error_query;
dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
wpas_p2p_sd_service_update(wpa_s);
return 0;
+
+error_query:
+ wpabuf_free(bsrv->query);
+error_bsrv:
+ os_free(bsrv);
+ return -1;
}
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index edecfde..89edad4 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -166,7 +166,7 @@
bss = wpa_bss_get_bssid(wpa_s, peer_addr);
if (!bss) {
- wpa_supplicant_update_scan_results(wpa_s);
+ 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");
@@ -174,7 +174,7 @@
}
}
- 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 -1;
@@ -186,7 +186,7 @@
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;
@@ -320,7 +320,7 @@
return -1;
}
- if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(entry->own_addr, own_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: own addr " MACSTR " and PTKSA entry own addr "
MACSTR " differ",
@@ -352,7 +352,7 @@
while (wpa_s->pasn_count < pasn_params->num_peers) {
peer = &pasn_params->peer[wpa_s->pasn_count];
- if (os_memcmp(wpa_s->bssid, peer->peer_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(wpa_s->bssid, peer->peer_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: Associated peer is not expected");
peer->status = PASN_STATUS_FAILURE;
@@ -426,6 +426,7 @@
}
+#ifdef CONFIG_FILS
static void wpas_pasn_initiate_eapol(struct pasn_data *pasn,
struct wpa_ssid *ssid)
{
@@ -443,6 +444,7 @@
eapol_sm_notify_config(pasn->eapol, &ssid->eap, &eapol_conf);
}
+#endif /* CONFIG_FILS */
static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
@@ -466,19 +468,19 @@
struct wpa_ie_data rsne_data;
int ret;
- if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
+ if (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(wpa_s, peer_addr);
+ 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;
@@ -509,8 +511,10 @@
struct wpa_ssid *ssid;
struct wpa_bss *bss;
const u8 *rsne, *rsnxe;
+#ifdef CONFIG_FILS
const u8 *indic;
u16 fils_info;
+#endif /* CONFIG_FILS */
u16 capab = 0;
bool derive_kdk;
int ret;
@@ -540,13 +544,13 @@
goto fail;
}
- 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");
goto fail;
}
- rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ 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,
@@ -556,9 +560,10 @@
derive_kdk = wpa_s->conf->force_kdk_derivation;
#endif /* CONFIG_TESTING_OPTIONS */
if (derive_kdk)
- pasn->kdk_len = WPA_KDK_MAX_LEN;
+ pasn_enable_kdk_derivation(pasn);
else
- pasn->kdk_len = 0;
+ 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) &&
@@ -578,9 +583,8 @@
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->rsnxe_capab = capab;
- pasn->send_mgmt = wpas_pasn_send_mlme;
-
+ pasn_set_rsnxe_caps(pasn, capab);
+ pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
#ifdef CONFIG_SAE
@@ -590,7 +594,7 @@
"PASN: No network profile found for SAE");
goto fail;
}
- pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+ pasn_set_pt(pasn, wpas_pasn_sae_derive_pt(ssid, awork->group));
if (!pasn->pt) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
goto fail;
@@ -625,8 +629,7 @@
}
#endif /* CONFIG_FILS */
- pasn->cb_ctx = wpa_s;
- pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+ pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa));
if (wpa_key_mgmt_ft(awork->akmp)) {
#ifdef CONFIG_IEEE80211R
@@ -749,7 +752,8 @@
wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
- wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
+ wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn_get_akmp(pasn),
+ pasn_get_cipher(pasn),
pasn->status, pasn->comeback,
pasn->comeback_after);
@@ -761,8 +765,8 @@
struct pasn_data *pasn,
struct wpa_pasn_params_data *params)
{
- int akmp = pasn->akmp;
- int cipher = pasn->cipher;
+ int akmp = pasn_get_akmp(pasn);
+ int cipher = pasn_get_cipher(pasn);
u16 group = pasn->group;
u8 own_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
@@ -802,20 +806,22 @@
if (!wpa_s->pasn_auth_work)
return -2;
- pasn->cb_ctx = wpa_s;
+ pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, 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,
- pasn->cipher, dot11RSNAConfigPMKLifetime,
- &pasn->ptk,
+ pasn_get_cipher(pasn),
+ dot11RSNAConfigPMKLifetime,
+ pasn_get_ptk(pasn),
wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
- wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
+ wpa_s->pasn_params ? wpa_s : NULL,
+ pasn_get_akmp(pasn));
if (pasn->pmksa_entry)
wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
}
- forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+ forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
if (ret == -1) {
wpas_pasn_auth_stop(wpa_s);
@@ -905,7 +911,8 @@
}
wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
- pasn->cipher, pasn->akmp);
+ pasn_get_cipher(pasn),
+ pasn_get_akmp(pasn));
wpas_pasn_auth_stop(wpa_s);
wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
@@ -921,7 +928,7 @@
struct ieee80211_mgmt *deauth;
int ret;
- if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(wpa_s->bssid, peer_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: Cannot deauthenticate from current BSS");
return -1;
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index 58edd9b..658103d 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -665,17 +665,91 @@
}
+/* Element ID Extension(1) + Request Type(1) + User Priority Control(2) +
+ * Stream Timeout(4) */
+#define MSCS_DESCRIPTOR_FIXED_LEN 8
+
+static void wpas_parse_mscs_resp(struct wpa_supplicant *wpa_s,
+ u16 status, const u8 *bssid,
+ const u8 *mscs_desc_ie)
+{
+ struct robust_av_data robust_av;
+ const u8 *pos;
+
+ /* The MSCS Descriptor element is optional in the MSCS Response frame */
+ if (!mscs_desc_ie)
+ goto event_mscs_result;
+
+ if (mscs_desc_ie[1] < MSCS_DESCRIPTOR_FIXED_LEN) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Drop received frame: invalid MSCS Descriptor element length: %d",
+ mscs_desc_ie[1]);
+ return;
+ }
+
+ os_memset(&robust_av, 0, sizeof(struct robust_av_data));
+
+ /* Skip Element ID, Length, and Element ID Extension */
+ pos = &mscs_desc_ie[3];
+
+ robust_av.request_type = *pos++;
+
+ switch (robust_av.request_type) {
+ case SCS_REQ_CHANGE:
+ /*
+ * Inform the suggested set of parameters that could be accepted
+ * by the AP in response to a subsequent request by the station.
+ */
+ robust_av.up_bitmap = *pos++;
+ robust_av.up_limit = *pos++ & 0x07;
+ robust_av.stream_timeout = WPA_GET_LE32(pos);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u change up_bitmap=%u up_limit=%u stream_timeout=%u",
+ MAC2STR(bssid), status, robust_av.up_bitmap,
+ robust_av.up_limit, robust_av.stream_timeout);
+ wpa_s->mscs_setup_done = false;
+ return;
+ case SCS_REQ_ADD:
+ /*
+ * This type is used in (Re)Association Response frame MSCS
+ * Descriptor element if no change is required.
+ */
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "MSCS: Drop received frame with unknown Request Type: %u",
+ robust_av.request_type);
+ return;
+ }
+
+event_mscs_result:
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u", MAC2STR(bssid), status);
+ wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
+}
+
+
void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *buf, size_t len)
{
u8 dialog_token;
u16 status_code;
+ const u8 *mscs_desc_ie;
if (len < 3)
return;
dialog_token = *buf++;
- if (dialog_token != wpa_s->robust_av.dialog_token) {
+ len--;
+
+ /* AP sets dialog token to 0 for unsolicited response */
+ if (!dialog_token && !wpa_s->mscs_setup_done) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Drop unsolicited received frame: inactive");
+ return;
+ }
+
+ if (dialog_token && dialog_token != wpa_s->robust_av.dialog_token) {
wpa_printf(MSG_INFO,
"MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
dialog_token, wpa_s->robust_av.dialog_token);
@@ -683,9 +757,11 @@
}
status_code = WPA_GET_LE16(buf);
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
- " status_code=%u", MAC2STR(src), status_code);
- wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
+ buf += 2;
+ len -= 2;
+
+ mscs_desc_ie = get_ie_ext(buf, len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+ wpas_parse_mscs_resp(wpa_s, status_code, src, mscs_desc_ie);
}
@@ -701,21 +777,19 @@
return;
mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
- if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
+ if (!mscs_desc_ie || mscs_desc_ie[1] <= MSCS_DESCRIPTOR_FIXED_LEN)
return;
- /* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
- * request type(1) + upc(2) + stream timeout(4) =) 10.
- */
- mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
+ /* Subelements start after element header and fixed fields */
+ mscs_status = get_ie(&mscs_desc_ie[2 + MSCS_DESCRIPTOR_FIXED_LEN],
+ mscs_desc_ie[1] - MSCS_DESCRIPTOR_FIXED_LEN,
MCSC_SUBELEM_STATUS);
if (!mscs_status || mscs_status[1] < 2)
return;
status = WPA_GET_LE16(mscs_status + 2);
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
- " status_code=%u", MAC2STR(bssid), status);
- wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
+
+ wpas_parse_mscs_resp(wpa_s, status, bssid, mscs_desc_ie);
}
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 8e51717..2ec4310 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -515,6 +515,8 @@
num_primary_channels = 4;
else if (op->bw == BW160)
num_primary_channels = 8;
+ else if (op->bw == BW320)
+ num_primary_channels = 16;
else
num_primary_channels = 1;
@@ -561,6 +563,7 @@
u8 channels_80mhz_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151,
167, 183, 199, 215 };
u8 channels_160mhz_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
+ u8 channels_320mhz_6ghz[] = { 31, 63, 95, 127, 159, 191 };
const u8 *channels = NULL;
size_t num_chan = 0;
bool is_6ghz = is_6ghz_op_class(op->op_class);
@@ -579,6 +582,9 @@
channels_160mhz_5ghz;
num_chan = is_6ghz ? ARRAY_SIZE(channels_160mhz_6ghz) :
ARRAY_SIZE(channels_160mhz_5ghz);
+ } else if (op->bw == BW320) {
+ channels = channels_320mhz_6ghz;
+ num_chan = ARRAY_SIZE(channels_320mhz_6ghz);
}
return wpas_add_channels(op, mode, channels, num_chan);
@@ -772,6 +778,7 @@
static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
+ struct bitfield *ext_eids,
enum beacon_report_detail detail,
struct wpa_bss *bss, u8 *buf,
size_t buf_len, const u8 **ies_buf,
@@ -828,7 +835,9 @@
*/
while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) {
if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS ||
- (eids && bitfield_is_set(eids, ies[0]))) {
+ (eids && bitfield_is_set(eids, ies[0])) ||
+ (ext_eids && ies[0] == WLAN_EID_EXTENSION && ies[1] &&
+ bitfield_is_set(ext_eids, ies[2]))) {
u8 elen = ies[1];
if (2 + elen > buf + buf_len - pos ||
@@ -876,7 +885,8 @@
os_memcpy(buf, rep, sizeof(*rep));
- ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
+ ret = wpas_beacon_rep_add_frame_body(data->eids, data->ext_eids,
+ data->report_detail,
bss, buf + sizeof(*rep),
14 + *ie_len, ie, ie_len,
idx == 0);
@@ -932,8 +942,8 @@
struct rrm_measurement_beacon_report rep;
u8 idx = 0;
- if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
- os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(data->bssid, broadcast_ether_addr) &&
+ !ether_addr_equal(data->bssid, bss->bssid))
return 0;
if (data->ssid_len &&
@@ -1043,6 +1053,7 @@
struct beacon_rep_data *data,
u8 sid, u8 slen, const u8 *subelem)
{
+ struct bitfield *eids;
u8 report_info, i;
switch (sid) {
@@ -1096,6 +1107,7 @@
break;
case WLAN_BEACON_REQUEST_SUBELEM_REQUEST:
+ case WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST:
if (data->report_detail !=
BEACON_REPORT_DETAIL_REQUESTED_ONLY) {
wpa_printf(MSG_DEBUG,
@@ -1111,20 +1123,46 @@
return -1;
}
- if (data->eids) {
+ if (sid == WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST) {
+ if (slen < 2) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid extended request");
+ return -1;
+ }
+ if (subelem[0] != WLAN_EID_EXTENSION) {
+ wpa_printf(MSG_DEBUG,
+ "Skip unknown Requested Element ID %u in Extended Request subelement",
+ subelem[0]);
+ break;
+ }
+
+ /* Skip the Requested Element ID field */
+ subelem++;
+ slen--;
+ }
+
+ if ((sid == WLAN_BEACON_REQUEST_SUBELEM_REQUEST &&
+ data->eids) ||
+ (sid == WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST &&
+ data->ext_eids)) {
wpa_printf(MSG_DEBUG,
- "Beacon Request: Request subelement appears more than once");
+ "Beacon Request: Request sub elements appear more than once");
return -1;
}
- data->eids = bitfield_alloc(255);
- if (!data->eids) {
+ eids = bitfield_alloc(255);
+ if (!eids) {
wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap");
return -1;
}
+ if (sid == WLAN_BEACON_REQUEST_SUBELEM_REQUEST)
+ data->eids = eids;
+ else
+ data->ext_eids = eids;
+
for (i = 0; i < slen; i++)
- bitfield_set(data->eids, subelem[i]);
+ bitfield_set(eids, subelem[i]);
break;
case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL:
/* Skip - it will be processed when freqs are added */
@@ -1480,6 +1518,26 @@
}
+static bool wpas_beacon_rep_scan_match(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ u8 i;
+
+ if (!wpa_s->valid_links)
+ return ether_addr_equal(wpa_s->current_bss->bssid, bssid);
+
+ for_each_link(wpa_s->valid_links, i) {
+ if (ether_addr_equal(wpa_s->links[i].bssid, bssid))
+ return true;
+ }
+
+ wpa_printf(MSG_DEBUG, "RRM: MLD: no match for TSF BSSID=" MACSTR,
+ MAC2STR(bssid));
+
+ return false;
+}
+
+
int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res,
struct scan_info *info)
@@ -1501,8 +1559,7 @@
MAC2STR(info->scan_start_tsf_bssid),
MAC2STR(wpa_s->current_bss->bssid));
if ((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
- os_memcmp(info->scan_start_tsf_bssid, wpa_s->current_bss->bssid,
- ETH_ALEN) != 0) {
+ !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;
@@ -1517,8 +1574,8 @@
if ((wpa_s->drv_rrm_flags &
WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
- os_memcmp(scan_res->res[i]->tsf_bssid,
- wpa_s->current_bss->bssid, ETH_ALEN) != 0) {
+ !wpas_beacon_rep_scan_match(wpa_s,
+ scan_res->res[i]->tsf_bssid)) {
wpa_printf(MSG_DEBUG,
"RRM: Ignore scan result for " MACSTR
" due to mismatching TSF BSSID" MACSTR,
@@ -1587,6 +1644,7 @@
eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL);
bitfield_free(data->eids);
+ bitfield_free(data->ext_eids);
os_free(data->scan_params.freqs);
os_memset(data, 0, sizeof(*data));
}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index bab6a23..8b59e40 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -261,8 +261,10 @@
wpa_s->scan_res_handler = NULL;
}
+#ifndef CONFIG_NO_RRM
if (wpa_s->beacon_rep_data.token)
wpas_rrm_refuse_request(wpa_s);
+#endif /* CONFIG_NO_RRM */
return;
}
@@ -320,6 +322,12 @@
}
wpa_s->last_scan_all_chan = !params->freqs;
wpa_s->last_scan_non_coloc_6ghz = params->non_coloc_6ghz;
+
+ if (wpa_s->crossed_6ghz_dom) {
+ wpa_printf(MSG_DEBUG, "First scan after crossing 6 GHz domain");
+ wpa_s->crossed_6ghz_dom = false;
+ }
+
if (!ctx ||
radio_add_work(wpa_s, 0, "scan", next, wpas_trigger_scan_cb,
ctx) < 0) {
@@ -696,10 +704,7 @@
else
wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!(links & BIT(link_id)))
- continue;
-
+ for_each_link(links, link_id) {
wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
/* Subelement length includes only the control */
@@ -2253,7 +2258,6 @@
static int wpas_channel_width_tx_pwr(const u8 *ies, size_t ies_len,
enum chan_width cw)
{
-#define MIN(a, b) (a < b ? a : b)
int offset = wpas_channel_width_offset(cw);
const struct element *elem;
int max_tx_power = TX_POWER_NO_CONSTRAINT, tx_pwr = 0;
@@ -2329,7 +2333,6 @@
}
return max_tx_power;
-#undef MIN
}
@@ -2371,7 +2374,6 @@
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
{
-#define MIN(a,b) a < b ? a : b
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
struct wpa_scan_res *wa = *_wa;
@@ -2379,6 +2381,7 @@
int wpa_a, wpa_b;
int snr_a, snr_b, snr_a_full, snr_b_full;
size_t ies_len;
+ const u8 *rsne_a, *rsne_b;
/* WPA/WPA2 support preferred */
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -2422,6 +2425,32 @@
snr_b = snr_b_full = wb->level;
}
+ /* 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 */
+ rsne_a = wpa_scan_get_ie(wa, WLAN_EID_RSN);
+ rsne_b = wpa_scan_get_ie(wb, WLAN_EID_RSN);
+ if (rsne_a && rsne_b) {
+ struct wpa_ie_data data;
+ bool psk_a = false, psk_b = false, sae_a = false, sae_b = false;
+
+ if (wpa_parse_wpa_ie_rsn(rsne_a, 2 + rsne_a[1], &data) == 0) {
+ psk_a = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
+ sae_a = wpa_key_mgmt_sae(data.key_mgmt);
+ }
+ if (wpa_parse_wpa_ie_rsn(rsne_b, 2 + rsne_b[1], &data) == 0) {
+ psk_b = wpa_key_mgmt_wpa_psk_no_sae(data.key_mgmt);
+ sae_b = wpa_key_mgmt_sae(data.key_mgmt);
+ }
+
+ if (sae_a && !sae_b && psk_b &&
+ (snr_a >= GREAT_SNR || snr_a >= snr_b))
+ return -1;
+ if (sae_b && !sae_a && psk_a &&
+ (snr_b >= GREAT_SNR || snr_b >= snr_a))
+ return 1;
+ }
+
/* If SNR is close, decide by max rate or frequency band. For cases
* involving the 6 GHz band, use the throughput estimate irrespective
* of the SNR difference since the LPI/VLP rules may result in
@@ -2448,7 +2477,6 @@
if (snr_b_full == snr_a_full)
return wb->qual - wa->qual;
return snr_b_full - snr_a_full;
-#undef MIN
}
@@ -2573,8 +2601,7 @@
return 1;
for (i = 0; i < wpa_s->bssid_filter_count; i++) {
- if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
- ETH_ALEN) == 0)
+ if (ether_addr_equal(wpa_s->bssid_filter + i * ETH_ALEN, bssid))
return 1;
}
@@ -2582,8 +2609,8 @@
}
-void filter_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *res)
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *res)
{
size_t i, j;
@@ -2878,6 +2905,7 @@
* been taken into account.
*/
int adjusted_snr;
+ bool ht40 = false, vht80 = false, vht160 = false;
/* Limit based on estimated SNR */
if (rate > 1 * 2 && snr < 1)
@@ -2933,11 +2961,14 @@
}
}
+ ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
+ if (ie && ie[1] >= 2 &&
+ (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK))
+ ht40 = true;
+
if (hw_mode &&
(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
- ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
- if (ie && ie[1] >= 2 &&
- (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+ if (ht40) {
*max_cw = CHAN_WIDTH_40;
adjusted_snr = snr +
wpas_channel_width_rssi_bump(ies, ies_len,
@@ -2948,22 +2979,36 @@
}
}
+ /* Determine VHT BSS bandwidth based on IEEE Std 802.11-2020,
+ * Table 11-23 (VHT BSS bandwidth) */
+ ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
+ if (ie && ie[1] >= 3) {
+ u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK;
+ u8 seg0 = ie[3];
+ u8 seg1 = ie[4];
+
+ if (cw)
+ vht80 = true;
+ if (cw == 2 ||
+ (cw == 3 && (seg1 > 0 && abs(seg1 - seg0) == 16)))
+ vht160 = true;
+ if (cw == 1 &&
+ ((seg1 > 0 && abs(seg1 - seg0) == 8) ||
+ (seg1 > 0 && abs(seg1 - seg0) == 16)))
+ vht160 = true;
+ }
+
if (hw_mode && hw_mode->vht_capab) {
/* Use +1 to assume VHT is always faster than HT */
ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
if (ie) {
- bool vht80 = false, vht160 = false;
-
if (*max_cw == CHAN_WIDTH_UNKNOWN)
*max_cw = CHAN_WIDTH_20;
tmp = max_ht20_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
- ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
- if (ie && ie[1] >= 2 &&
- (ie[3] &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+ if (ht40) {
*max_cw = CHAN_WIDTH_40;
adjusted_snr = snr +
wpas_channel_width_rssi_bump(
@@ -2973,26 +3018,6 @@
est = tmp;
}
- /* Determine VHT BSS bandwidth based on IEEE Std
- * 802.11-2020, Table 11-23 (VHT BSs bandwidth) */
- ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
- if (ie && ie[1] >= 3) {
- u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK;
- u8 seg0 = ie[3];
- u8 seg1 = ie[4];
-
- if (cw)
- vht80 = true;
- if (cw == 2 ||
- (cw == 3 &&
- (seg1 > 0 && abs(seg1 - seg0) == 16)))
- vht160 = true;
- if (cw == 1 &&
- ((seg1 > 0 && abs(seg1 - seg0) == 8) ||
- (seg1 > 0 && abs(seg1 - seg0) == 16)))
- vht160 = true;
- }
-
if (vht80) {
*max_cw = CHAN_WIDTH_80;
adjusted_snr = snr +
@@ -3052,9 +3077,10 @@
cw = he->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
own_he->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
- if (cw &
- (IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
- HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
+ if ((cw &
+ (IS_2P4GHZ(freq) ?
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) && ht40) {
if (*max_cw == CHAN_WIDTH_UNKNOWN ||
*max_cw < CHAN_WIDTH_40)
*max_cw = CHAN_WIDTH_40;
@@ -3067,7 +3093,8 @@
}
if (!IS_2P4GHZ(freq) &&
- (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
+ (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) &&
+ (!IS_5GHZ(freq) || vht80)) {
if (*max_cw == CHAN_WIDTH_UNKNOWN ||
*max_cw < CHAN_WIDTH_80)
*max_cw = CHAN_WIDTH_80;
@@ -3081,7 +3108,8 @@
if (!IS_2P4GHZ(freq) &&
(cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
- HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) &&
+ (!IS_5GHZ(freq) || vht160)) {
if (*max_cw == CHAN_WIDTH_UNKNOWN ||
*max_cw < CHAN_WIDTH_160)
*max_cw = CHAN_WIDTH_160;
@@ -3144,6 +3172,7 @@
* @wpa_s: Pointer to wpa_supplicant data
* @info: Information about what was scanned or %NULL if not available
* @new_scan: Whether a new scan was performed
+ * @bssid: Return BSS entries only for a single BSSID, %NULL for all
* Returns: Scan results, %NULL on failure
*
* This function request the current scan results from the driver and updates
@@ -3152,13 +3181,14 @@
*/
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
- struct scan_info *info, int new_scan)
+ struct scan_info *info, int new_scan,
+ const u8 *bssid)
{
struct wpa_scan_results *scan_res;
size_t i;
int (*compar)(const void *, const void *) = wpa_scan_result_compar;
- scan_res = wpa_drv_get_scan_results2(wpa_s);
+ scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
if (scan_res == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
@@ -3216,6 +3246,7 @@
/**
* wpa_supplicant_update_scan_results - Update scan results from the driver
* @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: Update BSS entries only for a single BSSID, %NULL for all
* Returns: 0 on success, -1 on failure
*
* This function updates the BSS table within wpa_supplicant based on the
@@ -3225,10 +3256,11 @@
* needed information to complete the connection (e.g., to perform validation
* steps in 4-way handshake).
*/
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
{
struct wpa_scan_results *scan_res;
- scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
if (scan_res == NULL)
return -1;
wpa_scan_results_free(scan_res);
@@ -3325,6 +3357,7 @@
params->duration = src->duration;
params->duration_mandatory = src->duration_mandatory;
params->oce_scan = src->oce_scan;
+ params->link_id = src->link_id;
if (src->sched_scan_plans_num > 0) {
params->sched_scan_plans =
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index f1739fa..d4c06c1 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -38,9 +38,6 @@
*/
#define TX_POWER_NO_CONSTRAINT 64
-#define IS_2P4GHZ(n) (n >= 2412 && n <= 2484)
-#define IS_5GHZ(n) (n > 4000 && n < 5895)
-
int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s);
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
@@ -57,8 +54,10 @@
bool default_ies, bool next);
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
- struct scan_info *info, int new_scan);
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+ struct scan_info *info, int new_scan,
+ const u8 *bssid);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type);
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
@@ -89,8 +88,6 @@
int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
unsigned int type, u8 *mask);
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
-void filter_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *res);
void scan_snr(struct wpa_scan_res *res);
void scan_est_throughput(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index df2c68f..57c9b38 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - SME
- * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -28,6 +28,7 @@
#include "p2p_supplicant.h"
#include "notify.h"
#include "bss.h"
+#include "bssid_ignore.h"
#include "scan.h"
#include "sme.h"
#include "hs20_supplicant.h"
@@ -70,7 +71,9 @@
int group = groups[wpa_s->sme.sae_group_index];
if (group <= 0)
break;
- if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
+ if (!int_array_includes(wpa_s->sme.sae_rejected_groups,
+ group) &&
+ sae_set_group(&wpa_s->sme.sae, group) == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
wpa_s->sme.sae.group);
wpa_s->sme.sae.akmp = external ?
@@ -165,7 +168,7 @@
}
if (reuse && wpa_s->sme.sae.tmp &&
- os_memcmp(addr, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
+ ether_addr_equal(addr, wpa_s->sme.sae.tmp->bssid)) {
wpa_printf(MSG_DEBUG,
"SAE: Reuse previously generated PWE on a retry with the same AP");
use_pt = wpa_s->sme.sae.h2e;
@@ -181,14 +184,17 @@
if (!bss) {
wpa_printf(MSG_DEBUG,
"SAE: BSS not available, update scan result to get BSS");
- wpa_supplicant_update_scan_results(wpa_s);
+ wpa_supplicant_update_scan_results(wpa_s, bssid);
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
}
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];
}
@@ -242,7 +248,7 @@
wpa_s->sme.sae_rejected_groups, NULL) < 0)
goto fail;
if (!use_pt &&
- sae_prepare_commit(wpa_s->own_addr, bssid,
+ sae_prepare_commit(wpa_s->own_addr, addr,
(u8 *) password, os_strlen(password),
&wpa_s->sme.sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
@@ -378,220 +384,6 @@
}
-static void wpas_process_tbtt_info(struct wpa_supplicant *wpa_s, const u8 *data)
-{
- struct wpa_bss *neigh_bss;
- const u8 *bssid;
- u8 bss_params;
- u8 link_id;
-
- /* TBTT Information field
- * Neighbor AP TBTT Offset[1]
- * BSSID[6]
- * Short SSID[4]
- * BSS parameters[1]
- * 20 MHz PSD[1]
- * MLD Parameters[3]
- * B0..B7: AP MLD ID
- * B7..B11: Link ID
- * B12..B19: BSS Parameters Change Count
- * B20: All Updates Included
- * B21: Disabled Link Indication */
-
- bssid = data + 1;
- bss_params = data[1 + ETH_ALEN + 4];
-
- data += 13; /* MLD Parameters */
- link_id = *(data + 1) & 0xF;
-
- wpa_dbg(wpa_s, MSG_DEBUG,
- "MLD: mld ID=%u, link ID=%u, bssid=" MACSTR ", bss_params=0x%x",
- *data, link_id, MAC2STR(bssid), bss_params);
-
- if (*data) {
- wpa_printf(MSG_DEBUG, "MLD: Reported link not part of MLD");
- return;
- }
-
- neigh_bss = wpa_bss_get_bssid(wpa_s, bssid);
- if (!neigh_bss) {
- wpa_printf(MSG_DEBUG, "MLD: Neighbor not found in scan");
- return;
- }
-
- if (!((bss_params & RNR_BSS_PARAM_SAME_SSID) &&
- (bss_params & RNR_BSS_PARAM_CO_LOCATED)) &&
- !wpa_scan_res_match(wpa_s, 0, neigh_bss, wpa_s->current_ssid,
- 1, 0)) {
- wpa_printf(MSG_DEBUG,
- "MLD: Neighbor doesn't match current SSID - skip link");
- return;
- }
-
- wpa_s->valid_links |= BIT(link_id);
- os_memcpy(wpa_s->links[link_id].bssid, bssid, ETH_ALEN);
- wpa_s->links[link_id].freq = neigh_bss->freq;
-}
-
-
-static void wpas_process_rnr(struct wpa_supplicant *wpa_s, const u8 *pos,
- size_t rnr_ie_len)
-{
- while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
- const struct ieee80211_neighbor_ap_info *ap_info =
- (const struct ieee80211_neighbor_ap_info *) pos;
- /* The first TBTT Information field */
- const u8 *data = ap_info->data;
- u8 tbtt_count;
- size_t len;
- int tbtt_i;
-
- if (rnr_ie_len < sizeof(struct ieee80211_neighbor_ap_info))
- break;
-
- tbtt_count = (ap_info->tbtt_info_hdr >> 4) + 1;
- len = sizeof(struct ieee80211_neighbor_ap_info) +
- ap_info->tbtt_info_len * tbtt_count;
-
- wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
- ap_info->op_class, ap_info->channel);
-
- if (len > rnr_ie_len)
- break;
-
- if (ap_info->tbtt_info_len < 16) {
- rnr_ie_len -= len;
- pos += len;
- continue;
- }
-
- for (tbtt_i = 0; tbtt_i < tbtt_count; tbtt_i++) {
- wpas_process_tbtt_info(wpa_s, data);
- data += ap_info->tbtt_info_len;
- }
-
- rnr_ie_len -= len;
- pos += len;
- }
-}
-
-
-static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
- struct wpa_ssid *ssid)
-{
- struct wpabuf *mlbuf;
- const u8 *rnr_ie, *rsn_ie;
- struct wpa_ie_data ie;
- u8 ml_ie_len;
- const struct ieee80211_eht_ml *eht_ml;
- const struct eht_ml_basic_common_info *ml_basic_common_info;
- u8 i;
- 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 |
- BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
- bool ret = false;
- int rnr_idx;
-
- if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
- return false;
-
- mlbuf = wpa_bss_defrag_mle(bss, MULTI_LINK_CONTROL_TYPE_BASIC);
- if (!mlbuf) {
- wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No ML element");
- return false;
- }
-
- rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- if (!rsn_ie || wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &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;
- }
-
- ml_ie_len = wpabuf_len(mlbuf);
-
- /* control + common info len + MLD address + MLD link information */
- if (ml_ie_len < 2 + 1 + ETH_ALEN + 1)
- goto out;
-
- eht_ml = wpabuf_head(mlbuf);
- if ((eht_ml->ml_control & control) != control) {
- wpa_printf(MSG_DEBUG, "MLD: Unexpected ML element control=0x%x",
- eht_ml->ml_control);
- goto out;
- }
-
- ml_basic_common_info =
- (const struct eht_ml_basic_common_info *) eht_ml->variable;
-
- /* common info length should be valid (self, mld_addr, link_id) */
- if (ml_basic_common_info->len < 1 + ETH_ALEN + 1)
- goto out;
-
- /* get the MLD address and MLD link ID */
- os_memcpy(wpa_s->ap_mld_addr, ml_basic_common_info->mld_addr,
- ETH_ALEN);
- wpa_s->mlo_assoc_link_id = ml_basic_common_info->variable[0] &
- EHT_ML_LINK_ID_MSK;
-
- os_memcpy(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, bss->bssid,
- ETH_ALEN);
- wpa_s->links[wpa_s->mlo_assoc_link_id].freq = bss->freq;
-
- wpa_printf(MSG_DEBUG, "MLD: address=" MACSTR ", link ID=%u",
- MAC2STR(wpa_s->ap_mld_addr), wpa_s->mlo_assoc_link_id);
-
- wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
-
- ret = true;
-
- /* Process all Reduced Neighbor Report elements */
- for (rnr_idx = 1; ; rnr_idx++) {
- rnr_ie = wpa_bss_get_ie_nth(bss,
- WLAN_EID_REDUCED_NEIGHBOR_REPORT,
- rnr_idx);
- if (!rnr_ie) {
- if (rnr_idx == 0) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "MLD: No RNR element");
- goto out;
- }
- break;
- }
- wpas_process_rnr(wpa_s, rnr_ie + 2, rnr_ie[1]);
- }
-
- wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
-
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
- wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
- i, MAC2STR(wpa_s->links[i].bssid));
- }
-
-out:
- wpabuf_free(mlbuf);
- return ret;
-}
-
-
static void wpas_ml_handle_removed_links(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
@@ -601,22 +393,103 @@
}
-static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data,
- int ie_offset)
+#ifdef CONFIG_TESTING_OPTIONS
+static struct wpa_bss * wpas_ml_connect_pref(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ struct wpa_ssid *ssid)
+{
+ unsigned int low, high, i;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: valid_links=%d, band_pref=%u, bssid_pref=" MACSTR,
+ wpa_s->valid_links,
+ wpa_s->conf->mld_connect_band_pref,
+ MAC2STR(wpa_s->conf->mld_connect_bssid_pref));
+
+ /* Check if there are more than one link */
+ if (!(wpa_s->valid_links & (wpa_s->valid_links - 1)))
+ return bss;
+
+ if (!is_zero_ether_addr(wpa_s->conf->mld_connect_bssid_pref)) {
+ for_each_link(wpa_s->valid_links, i) {
+ if (wpa_s->mlo_assoc_link_id == i)
+ continue;
+
+ if (ether_addr_equal(
+ wpa_s->links[i].bssid,
+ wpa_s->conf->mld_connect_bssid_pref))
+ goto found;
+ }
+ }
+
+ if (wpa_s->conf->mld_connect_band_pref == MLD_CONNECT_BAND_PREF_AUTO)
+ return bss;
+
+ switch (wpa_s->conf->mld_connect_band_pref) {
+ case MLD_CONNECT_BAND_PREF_2GHZ:
+ low = 2412;
+ high = 2472;
+ break;
+ case MLD_CONNECT_BAND_PREF_5GHZ:
+ low = 5180;
+ high = 5985;
+ break;
+ case MLD_CONNECT_BAND_PREF_6GHZ:
+ low = 5955;
+ high = 7125;
+ break;
+ default:
+ return bss;
+ }
+
+ for_each_link(wpa_s->valid_links, i) {
+ if (wpa_s->mlo_assoc_link_id == i)
+ continue;
+
+ if (wpa_s->links[i].freq >= low && wpa_s->links[i].freq <= high)
+ goto found;
+ }
+
+found:
+ if (i == MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG, "MLD: No match for connect/band pref");
+ return bss;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Change BSS for connect: " MACSTR " -> " MACSTR,
+ MAC2STR(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid),
+ MAC2STR(wpa_s->links[i].bssid));
+
+ /* Get the BSS entry and do the switch */
+ if (ssid && ssid->ssid_len)
+ bss = wpa_bss_get(wpa_s, wpa_s->links[i].bssid, ssid->ssid,
+ ssid->ssid_len);
+ else
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->links[i].bssid);
+ wpa_s->mlo_assoc_link_id = i;
+
+ return bss;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data,
+ int ie_offset)
{
struct ieee802_11_elems elems;
const u8 *mld_addr;
u16 status_code = data->auth.status_code;
if (!wpa_s->valid_links)
- return;
+ return 0;
if (ieee802_11_parse_elems(data->auth.ies + ie_offset,
data->auth.ies_len - ie_offset,
&elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
- goto out;
+ return -1;
}
if (!elems.basic_mle || !elems.basic_mle_len) {
@@ -625,28 +498,53 @@
status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
- goto out;
+ return -1;
/* Accept missing Multi-Link element in failed authentication
* cases. */
- return;
+ return 0;
}
mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
if (!mld_addr)
- goto out;
+ return -1;
wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
- if (os_memcmp(wpa_s->ap_mld_addr, mld_addr, ETH_ALEN) != 0) {
+ 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));
- goto out;
+ return -1;
}
- return;
-out:
- wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state");
- wpas_reset_mlo_info(wpa_s);
+ return 0;
+}
+
+
+static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+ u8 i;
+
+ wpa_s->valid_links = 0;
+ wpa_s->mlo_assoc_link_id = bss->mld_link_id;
+
+ for_each_link(bss->valid_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;
+
+ if (bss->mld_link_id == i)
+ wpa_s->links[i].bss = bss;
+ else if (ssid && ssid->ssid_len)
+ wpa_s->links[i].bss = wpa_bss_get(wpa_s, bssid,
+ ssid->ssid,
+ ssid->ssid_len);
+ else
+ wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
+ }
}
@@ -681,11 +579,33 @@
return;
}
+ 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) {
+ 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);
+
+ 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;
+ wpas_ml_handle_removed_links(wpa_s, bss);
+ }
+
skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
wpa_s->reassoc_same_bss;
wpa_s->current_bss = bss;
- os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
params.freq = bss->freq;
@@ -694,14 +614,6 @@
params.ssid_len = bss->ssid_len;
params.p2p = ssid->p2p_group;
- if (wpas_ml_element(wpa_s, bss, ssid)) {
- wpa_printf(MSG_DEBUG, "MLD: In authentication");
- params.mld = true;
- params.mld_link_id = wpa_s->mlo_assoc_link_id;
- params.ap_mld_addr = wpa_s->ap_mld_addr;
- wpas_ml_handle_removed_links(wpa_s, bss);
- }
-
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
wpa_s->sme.prev_bssid_set = 0;
@@ -734,7 +646,7 @@
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");
@@ -774,7 +686,7 @@
#endif /* CONFIG_WEP */
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;
@@ -898,7 +810,7 @@
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);
+ omit_rsnxe = !wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
if (wpa_s->sme.assoc_req_ie_len + 5 <
sizeof(wpa_s->sme.assoc_req_ie)) {
struct rsn_mdie *mdie;
@@ -927,7 +839,7 @@
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 &
@@ -970,10 +882,12 @@
sme_auth_handle_rrm(wpa_s, bss);
+#ifndef CONFIG_NO_RRM
wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
wpa_s, ssid, bss,
wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len);
+#endif /* CONFIG_NO_RRM */
if (params.p2p)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
@@ -993,6 +907,18 @@
os_memcpy(pos, ext_capab, ext_capab_len);
}
+ if (ssid->max_idle && wpa_s->sme.assoc_req_ie_len + 5 <=
+ sizeof(wpa_s->sme.assoc_req_ie)) {
+ u8 *pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+ *pos++ = 3;
+ WPA_PUT_LE16(pos, ssid->max_idle);
+ pos += 2;
+ *pos = 0; /* Idle Options */
+ wpa_s->sme.assoc_req_ie_len += 5;
+ }
+
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->rsnxe_override_assoc &&
wpabuf_len(wpa_s->rsnxe_override_assoc) <=
@@ -1122,7 +1048,7 @@
else
resp = sme_auth_build_sae_confirm(wpa_s, 0);
if (resp == NULL) {
- wpas_connection_failed(wpa_s, bss->bssid);
+ wpas_connection_failed(wpa_s, bss->bssid, NULL);
return;
}
params.auth_data = wpabuf_head(resp);
@@ -1140,6 +1066,7 @@
old_ssid = wpa_s->current_ssid;
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);
#ifdef CONFIG_FILS
@@ -1258,7 +1185,7 @@
if (wpas_p2p_handle_frequency_conflicts(wpa_s,
params.freq,
ssid) < 0) {
- wpas_connection_failed(wpa_s, bss->bssid);
+ wpas_connection_failed(wpa_s, bss->bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
wpabuf_free(resp);
wpas_connect_work_done(wpa_s);
@@ -1281,7 +1208,7 @@
if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
"driver failed");
- wpas_connection_failed(wpa_s, bss->bssid);
+ wpas_connection_failed(wpa_s, bss->bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
wpabuf_free(resp);
wpas_connect_work_done(wpa_s);
@@ -1661,14 +1588,21 @@
static int sme_check_sae_rejected_groups(struct wpa_supplicant *wpa_s,
const struct wpabuf *groups)
{
- size_t i, count;
+ size_t i, count, len;
const u8 *pos;
if (!groups)
return 0;
pos = wpabuf_head(groups);
- count = wpabuf_len(groups) / 2;
+ len = wpabuf_len(groups);
+ if (len & 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid length of the Rejected Groups element payload: %zu",
+ len);
+ return 1;
+ }
+ count = len / 2;
for (i = 0; i < count; i++) {
int enabled;
u16 group;
@@ -1719,8 +1653,7 @@
wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
- if (os_memcmp(wpa_s->sme.ext_auth_ap_mld_addr, mld_addr, ETH_ALEN) !=
- 0) {
+ 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));
@@ -1965,6 +1898,7 @@
wpa_s->sme.sae.state = SAE_ACCEPTED;
sae_clear_temp_data(&wpa_s->sme.sae);
+ wpa_s_clear_sae_rejected(wpa_s);
if (external) {
/* Report success to driver */
@@ -1998,7 +1932,7 @@
}
if (wpa_insert_pmkid(wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len,
- wpa_s->sme.sae.pmkid) < 0)
+ wpa_s->sme.sae.pmkid, true) < 0)
return -1;
wpa_hexdump(MSG_DEBUG,
"SME: Updated Association Request IEs",
@@ -2075,9 +2009,9 @@
return;
}
- if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0 &&
+ if (!ether_addr_equal(wpa_s->pending_bssid, data->auth.peer) &&
!(wpa_s->valid_links &&
- os_memcmp(wpa_s->ap_mld_addr, data->auth.peer, ETH_ALEN) == 0)) {
+ ether_addr_equal(wpa_s->ap_mld_addr, data->auth.peer))) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
"unexpected peer " MACSTR,
MAC2STR(data->auth.peer));
@@ -2103,9 +2037,16 @@
data->auth.ies_len, 0, data->auth.peer,
&ie_offset);
if (res < 0) {
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ if (wpa_s->sme.sae_rejected_groups &&
+ ssid->disabled_until.sec) {
+ wpa_printf(MSG_DEBUG,
+ "SME: Clear SAE state with rejected groups due to continuous failures");
+ wpa_s_clear_sae_rejected(wpa_s);
+ }
}
if (res != 1)
return;
@@ -2147,7 +2088,8 @@
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
wpa_s->sme.auth_alg == data->auth.auth_type ||
wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
@@ -2196,7 +2138,8 @@
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
@@ -2220,7 +2163,8 @@
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
@@ -2234,7 +2178,8 @@
" reason=%d locally_generated=1",
MAC2STR(wpa_s->pending_bssid),
WLAN_REASON_DEAUTH_LEAVING);
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
@@ -2242,9 +2187,23 @@
#endif /* CONFIG_FILS */
/* TODO: Support additional auth_type values as well */
- if (data->auth.auth_type == WLAN_AUTH_OPEN ||
- data->auth.auth_type == WLAN_AUTH_SAE)
- wpas_sme_ml_auth(wpa_s, data, ie_offset);
+ if ((data->auth.auth_type == WLAN_AUTH_OPEN ||
+ data->auth.auth_type == WLAN_AUTH_SAE) &&
+ wpas_sme_ml_auth(wpa_s, data, ie_offset) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "MLD: Failed to parse ML Authentication frame");
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+ " reason=%d locally_generated=1",
+ MAC2STR(wpa_s->pending_bssid),
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpa_printf(MSG_DEBUG,
+ "MLD: Authentication - clearing MLD state");
+ wpas_reset_mlo_info(wpa_s);
+ return;
+ }
sme_associate(wpa_s, ssid->mode, data->auth.peer,
data->auth.auth_type);
@@ -2287,6 +2246,9 @@
os_memset(¶ms, 0, sizeof(params));
+ /* Save auth type, in case we need to retry after comeback timer. */
+ wpa_s->sme.assoc_auth_type = auth_type;
+
#ifdef CONFIG_FILS
if (auth_type == WLAN_AUTH_FILS_SK ||
auth_type == WLAN_AUTH_FILS_SK_PFS) {
@@ -2453,6 +2415,7 @@
pfs_fail:
#endif /* CONFIG_DPP2 */
+#ifndef CONFIG_NO_ROBUST_AV
wpa_s->mscs_setup_done = false;
if (wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS) &&
wpa_s->robust_av.valid_config) {
@@ -2486,15 +2449,20 @@
wpabuf_free(mscs_ie);
}
mscs_fail:
+#endif /* CONFIG_NO_ROBUST_AV */
if (ssid && ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
+ struct multi_ap_params multi_ap = { 0 };
+
+ multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+ multi_ap.profile = ssid->multi_ap_profile;
multi_ap_ie_len = add_multi_ap_ie(
wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
sizeof(wpa_s->sme.assoc_req_ie) -
wpa_s->sme.assoc_req_ie_len,
- MULTI_AP_BACKHAUL_STA);
+ &multi_ap);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
@@ -2503,6 +2471,28 @@
wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len;
}
+ if (wpas_rsn_overriding(wpa_s) &&
+ 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;
+ u32 type = 0;
+ const u8 *ie;
+
+ 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)
+ type = WPA_GET_BE32(&ie[2]);
+
+ if (type) {
+ /* Indicate support for RSN overriding */
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4;
+ WPA_PUT_BE32(pos, type);
+ wpa_s->sme.assoc_req_ie_len += 2 + 4;
+ }
+ }
+
params.bssid = bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
@@ -2674,27 +2664,53 @@
params.mld_params.mld_addr = wpa_s->ap_mld_addr;
params.mld_params.valid_links = wpa_s->valid_links;
params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
+ for_each_link(wpa_s->valid_links, i) {
params.mld_params.mld_links[i].bssid =
wpa_s->links[i].bssid;
params.mld_params.mld_links[i].freq =
wpa_s->links[i].freq;
+ params.mld_params.mld_links[i].disabled =
+ wpa_s->links[i].disabled;
- wpa_printf(MSG_DEBUG, "MLD: id=%u, freq=%d, " MACSTR,
+ wpa_printf(MSG_DEBUG,
+ "MLD: id=%u, freq=%d, disabled=%u, " MACSTR,
i, wpa_s->links[i].freq,
+ wpa_s->links[i].disabled,
MAC2STR(wpa_s->links[i].bssid));
}
}
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
+ unsigned int n_failed_links = 0;
+ int i;
+
wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
"driver failed");
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
- wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
- os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+
+ /* 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 ||
+ !params.mld_params.mld_links[i].error)
+ continue;
+
+ wpa_bssid_ignore_add(wpa_s, wpa_s->links[i].bssid);
+ n_failed_links++;
+ }
+
+ if (n_failed_links) {
+ /* Deauth and connect (possibly to the same AP MLD) */
+ wpa_drv_deauthenticate(wpa_s, wpa_s->ap_mld_addr,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_connect_work_done(wpa_s);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpas_request_connection(wpa_s);
+ } else {
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ }
return;
}
@@ -2734,20 +2750,26 @@
}
-static void sme_deauth(struct wpa_supplicant *wpa_s)
+static void sme_deauth(struct wpa_supplicant *wpa_s, const u8 **link_bssids)
{
int bssid_changed;
+ const u8 *bssid;
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
- if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+ if (wpa_s->valid_links)
+ bssid = wpa_s->ap_mld_addr;
+ else
+ bssid = wpa_s->pending_bssid;
+
+ if (wpa_drv_deauthenticate(wpa_s, bssid,
WLAN_REASON_DEAUTH_LEAVING) < 0) {
wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
"failed");
}
wpa_s->sme.prev_bssid_set = 0;
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid, link_bssids);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
@@ -2756,14 +2778,115 @@
}
-void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
+static void sme_assoc_comeback_timer(void *eloop_ctx, void *timeout_ctx)
{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->current_bss || !wpa_s->current_ssid) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "SME: Comeback timeout expired; SSID/BSSID cleared; ignoring");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "SME: Comeback timeout expired; retry associating with "
+ MACSTR "; mode=%d auth_type=%u",
+ MAC2STR(wpa_s->current_bss->bssid),
+ wpa_s->current_ssid->mode,
+ wpa_s->sme.assoc_auth_type);
+
+ /* Authentication state was completed already; just try association
+ * again. */
+ sme_associate(wpa_s, wpa_s->current_ssid->mode,
+ wpa_s->current_bss->bssid,
+ wpa_s->sme.assoc_auth_type);
+}
+
+
+static bool sme_try_assoc_comeback(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ struct ieee802_11_elems elems;
+ u32 timeout_interval;
+ unsigned long comeback_usec;
+ u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->test_assoc_comeback_type != -1)
+ type = wpa_s->test_assoc_comeback_type;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (ieee802_11_parse_elems(data->assoc_reject.resp_ies,
+ data->assoc_reject.resp_ies_len,
+ &elems, 0) == ParseFailed) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SME: Temporary assoc reject: failed to parse (Re)Association Response frame elements");
+ return false;
+ }
+
+ if (!elems.timeout_int) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SME: Temporary assoc reject: missing timeout interval IE");
+ return false;
+ }
+
+ if (elems.timeout_int[0] != type) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SME: Temporary assoc reject: missing association comeback time");
+ return false;
+ }
+
+ timeout_interval = WPA_GET_LE32(&elems.timeout_int[1]);
+ if (timeout_interval > 60000) {
+ /* This is unprotected information and there is no point in
+ * getting stuck waiting for very long duration based on it */
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "SME: Ignore overly long association comeback interval: %u TUs",
+ timeout_interval);
+ return false;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "SME: Association comeback interval: %u TUs",
+ timeout_interval);
+
+ comeback_usec = timeout_interval * 1024;
+ eloop_register_timeout(comeback_usec / 1000000, comeback_usec % 1000000,
+ sme_assoc_comeback_timer, wpa_s, NULL);
+ return true;
+}
+
+
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data,
+ const u8 **link_bssids)
+{
+ const u8 *bssid;
+
+ if (wpa_s->valid_links)
+ bssid = wpa_s->ap_mld_addr;
+ else
+ bssid = wpa_s->pending_bssid;
+
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
"status code %d", MAC2STR(wpa_s->pending_bssid),
data->assoc_reject.status_code);
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL);
+
+ /* Authentication phase has been completed at this point. Check whether
+ * the AP rejected association temporarily due to still holding a
+ * security associationis with us (MFP). If so, we must wait for the
+ * AP's association comeback timeout period before associating again. */
+ if (data->assoc_reject.status_code ==
+ WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "SME: Temporary association reject from BSS " MACSTR,
+ MAC2STR(bssid));
+ if (sme_try_assoc_comeback(wpa_s, data)) {
+ /* Break out early; comeback error is not a failure. */
+ return;
+ }
+ }
#ifdef CONFIG_SAE
if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
@@ -2776,7 +2899,7 @@
struct wpa_bss *bss = wpa_s->current_bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
- wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+ wpa_drv_deauthenticate(wpa_s, bssid,
WLAN_REASON_DEAUTH_LEAVING);
wpas_connect_work_done(wpa_s);
wpa_supplicant_mark_disassoc(wpa_s);
@@ -2821,7 +2944,7 @@
* benefit from using the previous authentication, so this could be
* optimized in the future.
*/
- sme_deauth(wpa_s);
+ sme_deauth(wpa_s, link_bssids);
}
@@ -2829,7 +2952,7 @@
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
}
@@ -2838,7 +2961,7 @@
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid, NULL);
wpa_supplicant_mark_disassoc(wpa_s);
}
@@ -2867,7 +2990,7 @@
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
- sme_deauth(wpa_s);
+ sme_deauth(wpa_s, NULL);
}
}
@@ -2877,7 +3000,7 @@
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state == WPA_ASSOCIATING) {
wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
- sme_deauth(wpa_s);
+ sme_deauth(wpa_s, NULL);
}
}
@@ -2885,35 +3008,15 @@
void sme_state_changed(struct wpa_supplicant *wpa_s)
{
/* Make sure timers are cleaned up appropriately. */
- if (wpa_s->wpa_state != WPA_ASSOCIATING)
+ if (wpa_s->wpa_state != WPA_ASSOCIATING) {
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL);
+ }
if (wpa_s->wpa_state != WPA_AUTHENTICATING)
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
}
-void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
- const u8 *prev_pending_bssid)
-{
- /*
- * mac80211-workaround to force deauth on failed auth cmd,
- * requires us to remain in authenticating state to allow the
- * second authentication attempt to be continued properly.
- */
- wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
- "to proceed after disconnection event");
- wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
- os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
-
- /*
- * Re-arm authentication timer in case auth fails for whatever reason.
- */
- eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
- eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
- NULL);
-}
-
-
void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
{
wpa_s->sme.prev_bssid_set = 0;
@@ -2941,6 +3044,7 @@
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(sme_assoc_comeback_timer, wpa_s, NULL);
}
@@ -3374,7 +3478,7 @@
ssid = wpa_s->current_ssid;
if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
return;
- if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(sa, wpa_s->bssid))
return;
if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
@@ -3479,7 +3583,7 @@
wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
- if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(sa, wpa_s->bssid))
return;
for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index c797d2e..f8fd06b 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -19,7 +19,8 @@
int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
const u8 *ies, size_t ies_len);
void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data);
+ union wpa_event_data *data,
+ const u8 **link_bssids);
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,
@@ -32,8 +33,6 @@
void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *da, const u8 *sa,
const u8 *data, size_t len);
void sme_state_changed(struct wpa_supplicant *wpa_s);
-void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
- const u8 *prev_pending_bssid);
void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
void sme_deinit(struct wpa_supplicant *wpa_s);
@@ -65,7 +64,8 @@
static inline void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
+ union wpa_event_data *data,
+ const u8 **link_bssids)
{
}
@@ -98,12 +98,6 @@
{
}
-static inline void
-sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
- const u8 *prev_pending_bssid)
-{
-}
-
static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
{
}
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
index d0fdd55..37e2ed4 100644
--- a/wpa_supplicant/wmm_ac.c
+++ b/wpa_supplicant/wmm_ac.c
@@ -678,7 +678,7 @@
}
/* make sure the params are the same */
- if (os_memcmp(req->address, sa, ETH_ALEN) != 0 ||
+ if (!ether_addr_equal(req->address, sa) ||
tsid != wmm_ac_get_tsid(&req->tspec) ||
up != wmm_ac_get_user_priority(&req->tspec) ||
dir != wmm_ac_get_direction(&req->tspec)) {
@@ -755,13 +755,13 @@
}
/* WMM AC action frame */
- if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(da, wpa_s->own_addr)) {
wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR
" is other than ours, ignoring frame", MAC2STR(da));
return;
}
- if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sa, wpa_s->bssid)) {
wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR
" different other than our bssid", MAC2STR(da));
return;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 56183ff..ea79ae6 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -418,7 +418,7 @@
}
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+void wnm_btm_reset(struct wpa_supplicant *wpa_s)
{
int i;
@@ -431,8 +431,17 @@
os_free(wpa_s->wnm_neighbor_report_elements);
wpa_s->wnm_neighbor_report_elements = NULL;
- wpabuf_free(wpa_s->coloc_intf_elems);
- wpa_s->coloc_intf_elems = NULL;
+ wpa_s->wnm_cand_valid_until.sec = 0;
+ wpa_s->wnm_cand_valid_until.usec = 0;
+
+ wpa_s->wnm_mode = 0;
+ wpa_s->wnm_dialog_token = 0;
+ wpa_s->wnm_reply = 0;
+
+#ifdef CONFIG_MBO
+ wpa_s->wnm_mbo_trans_reason_present = 0;
+ wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
}
@@ -686,9 +695,8 @@
if (reason) {
for (i = 0; i < info->num; i++) {
if (first_candidate_bssid &&
- os_memcmp(first_candidate_bssid,
- info->candidates[i].bssid, ETH_ALEN) == 0)
- {
+ ether_addr_equal(first_candidate_bssid,
+ info->candidates[i].bssid)) {
*reason = info->candidates[i].reject_reason;
break;
}
@@ -782,22 +790,11 @@
}
}
- if (bss->ssid_len != target->ssid_len ||
- os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
- /*
- * TODO: Could consider allowing transition to another
- * ESS if PMF was enabled for the association.
- */
- wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
- " (pref %d) in different ESS",
- MAC2STR(nei->bssid),
- nei->preference_present ? nei->preference :
- -1);
- continue;
- }
-
- if (wpa_s->current_ssid &&
- !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+ /*
+ * 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",
@@ -807,14 +804,6 @@
continue;
}
- if (wpa_is_bss_tmp_disallowed(wpa_s, target)) {
- wpa_printf(MSG_DEBUG,
- "MBO: Candidate BSS " MACSTR
- " retry delay is not over yet",
- MAC2STR(nei->bssid));
- 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)",
@@ -1046,8 +1035,8 @@
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
-static void wnm_send_bss_transition_mgmt_resp(
- struct wpa_supplicant *wpa_s, u8 dialog_token,
+static int wnm_send_bss_transition_mgmt_resp(
+ struct wpa_supplicant *wpa_s,
enum bss_trans_mgmt_status_code status,
enum mbo_transition_reject_reason reason,
u8 delay, const u8 *target_bssid)
@@ -1055,21 +1044,24 @@
struct wpabuf *buf;
int res;
+ wpa_s->wnm_reply = 0;
+
wpa_printf(MSG_DEBUG,
"WNM: Send BSS Transition Management Response to " MACSTR
" dialog_token=%u status=%u reason=%u delay=%d",
- MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
+ MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
+ reason, delay);
if (!wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Current BSS not known - drop response");
- return;
+ return -1;
}
buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
if (!buf) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for BTM response");
- return;
+ return -1;
}
wpa_s->bss_tm_status = status;
@@ -1077,7 +1069,7 @@
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
- wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
wpabuf_put_u8(buf, status);
wpabuf_put_u8(buf, delay);
if (target_bssid) {
@@ -1091,7 +1083,7 @@
wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
}
- if (status == WNM_BSS_TM_ACCEPT)
+ if (status == WNM_BSS_TM_ACCEPT && target_bssid)
wnm_add_cand_list(wpa_s, &buf);
#ifdef CONFIG_MBO
@@ -1107,7 +1099,7 @@
wpabuf_free(buf);
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for MBO IE");
- return;
+ return -1;
}
wpabuf_put_data(buf, mbo, ret);
@@ -1124,6 +1116,8 @@
}
wpabuf_free(buf);
+
+ return res;
}
@@ -1141,19 +1135,24 @@
/* Send the BSS Management Response - Accept */
if (wpa_s->wnm_reply) {
- wpa_s->wnm_reply = 0;
+ wpa_s->wnm_target_bss = bss;
wpa_printf(MSG_DEBUG,
"WNM: Sending successful BSS Transition Management Response");
- wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
- MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
- bss->bssid);
+
+ /* This function will be called again from the TX handler to
+ * start the actual reassociation after this response has been
+ * delivered to the current AP. */
+ if (wnm_send_bss_transition_mgmt_resp(
+ wpa_s, WNM_BSS_TM_ACCEPT,
+ MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+ bss->bssid) >= 0)
+ return;
}
if (bss == wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Already associated with the preferred candidate");
- wnm_deallocate_memory(wpa_s);
+ wnm_btm_reset(wpa_s);
return;
}
@@ -1169,11 +1168,10 @@
*/
if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
wpa_s->bss_trans_mgmt_in_progress = true;
- wnm_deallocate_memory(wpa_s);
}
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
{
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -1181,27 +1179,51 @@
enum mbo_transition_reject_reason reason =
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
- if (!wpa_s->wnm_neighbor_report_elements)
+ if (!wpa_s->wnm_dialog_token)
return 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Process scan results for BSS Transition Management");
- if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+ if (!pre_scan_check &&
+ os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
+ os_reltime_before(&wpa_s->wnm_cand_valid_until,
&wpa_s->scan_trigger_time)) {
wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
- wnm_deallocate_memory(wpa_s);
- return 0;
- }
-
- if (!wpa_s->current_bss ||
- os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
- ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
- return 0;
+ goto send_bss_resp_fail;
}
/* Compare the Neighbor Report and scan results */
bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
+
+ /*
+ * If this is a pre-scan check, returning 0 will trigger a scan and
+ * another call. In that case, reject "bad" candidates in the hope of
+ * finding a better candidate after scanning.
+ *
+ * Use a simple heuristic to check whether the selection is reasonable
+ * or a scan is a good idea. For that, we need to have found a
+ * candidate BSS (which might be the current one), it is up-to-date,
+ * and we don't want to immediately roam back again.
+ */
+ if (pre_scan_check) {
+ struct os_reltime age;
+
+ if (!bss)
+ return 0;
+
+ os_reltime_age(&bss->last_update, &age);
+ if (age.sec >= 10)
+ 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))
+ return 0;
+#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;
@@ -1213,18 +1235,13 @@
return 1;
send_bss_resp_fail:
- if (!reply_on_fail)
- return 0;
-
/* Send reject response for all the failures */
- if (wpa_s->wnm_reply) {
- wpa_s->wnm_reply = 0;
- wnm_send_bss_transition_mgmt_resp(wpa_s,
- wpa_s->wnm_dialog_token,
- status, reason, 0, NULL);
- }
- wnm_deallocate_memory(wpa_s);
+ if (wpa_s->wnm_reply)
+ wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
+ 0, NULL);
+
+ wnm_btm_reset(wpa_s);
return 0;
}
@@ -1331,6 +1348,10 @@
struct neighbor_report *nei;
nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+ if (nei->preference_present && nei->preference == 0)
+ continue;
+
if (nei->freq <= 0) {
wpa_printf(MSG_DEBUG,
"WNM: Unknown neighbor operating frequency for "
@@ -1355,79 +1376,6 @@
}
-static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
-{
- struct wpa_scan_results *scan_res;
- struct wpa_bss *bss;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- u8 i, found = 0;
- size_t j;
-
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: Fetch current scan results from the driver for checking transition candidates");
- scan_res = wpa_drv_get_scan_results2(wpa_s);
- if (!scan_res) {
- wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
- return 0;
- }
-
- if (scan_res->fetch_time.sec == 0)
- os_get_reltime(&scan_res->fetch_time);
-
- filter_scan_res(wpa_s, scan_res);
-
- 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)
- continue;
-
- for (j = 0; j < scan_res->num; j++) {
- struct wpa_scan_res *res;
- const u8 *ssid_ie;
-
- res = scan_res->res[j];
- if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
- res->age > WNM_SCAN_RESULT_AGE * 1000)
- continue;
- bss = wpa_s->current_bss;
- ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
- if (bss && ssid_ie && ssid_ie[1] &&
- (bss->ssid_len != ssid_ie[1] ||
- os_memcmp(bss->ssid, ssid_ie + 2,
- bss->ssid_len) != 0))
- continue; /* Skip entries for other ESSs */
-
- /* Potential candidate found */
- found = 1;
- scan_snr(res);
- scan_est_throughput(wpa_s, res);
- wpa_bss_update_scan_res(wpa_s, res,
- &scan_res->fetch_time);
- }
- }
-
- wpa_scan_results_free(scan_res);
- if (!found) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: No transition candidate matches existing scan results");
- return 0;
- }
-
- bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
- if (!bss) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WNM: Comparison of scan results against transition candidates did not find matches");
- return 0;
- }
-
- /* Associate to the network */
- wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
- return 1;
-}
-
-
static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
const u8 *pos, const u8 *end,
int reply)
@@ -1437,6 +1385,7 @@
#ifdef CONFIG_MBO
const u8 *vendor;
#endif /* CONFIG_MBO */
+ bool disassoc_imminent;
if (wpa_s->disable_mbo_oce || wpa_s->conf->disable_btm)
return;
@@ -1445,8 +1394,6 @@
return;
#ifdef CONFIG_MBO
- wpa_s->wnm_mbo_trans_reason_present = 0;
- wpa_s->wnm_mbo_transition_reason = 0;
wpa_s->wnm_mbo_cell_pref_present = 0;
wpa_s->wnm_mbo_cell_preference = 0;
wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
@@ -1458,9 +1405,12 @@
else
beacon_int = 100; /* best guess */
+ wnm_btm_reset(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_link_removal = false;
valid_int = pos[4];
wpa_s->wnm_reply = reply;
@@ -1476,8 +1426,7 @@
"WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
wpa_s->reject_btm_req_reason);
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token,
- wpa_s->reject_btm_req_reason,
+ wpa_s, wpa_s->reject_btm_req_reason,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
return;
}
@@ -1532,7 +1481,45 @@
return;
}
- if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+ 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.
+ *
+ * TODO: We should check if the AP has more than one link.
+ * TODO: We should pass the RX link and use that
+ */
+ if (disassoc_imminent && wpa_s->valid_links &&
+ (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
+ (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
+ /* If we still have a link, then just accept the request */
+ if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
+ wpa_printf(MSG_INFO,
+ "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
+ disassoc_imminent = false;
+
+ wnm_send_bss_transition_mgmt_resp(
+ wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
+
+ return;
+ }
+
+ /* 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->links[wpa_s->mlo_assoc_link_id].bssid,
+ ETH_ALEN);
+ } else {
+ os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ?
+ wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN);
+ }
+
+ 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 &&
@@ -1546,7 +1533,6 @@
unsigned int valid_ms;
wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
- wnm_deallocate_memory(wpa_s);
wpa_s->wnm_neighbor_report_elements = os_calloc(
WNM_MAX_NEIGHBOR_REPORT,
sizeof(struct neighbor_report));
@@ -1572,8 +1558,7 @@
wnm_parse_neighbor_report(wpa_s, pos, len, rep);
if ((wpa_s->wnm_mode &
WNM_BSS_TM_REQ_DISASSOC_IMMINENT) &&
- os_memcmp(rep->bssid, wpa_s->bssid,
- ETH_ALEN) == 0)
+ ether_addr_equal(rep->bssid, wpa_s->bssid))
rep->disassoc_imminent = 1;
wpa_s->wnm_num_neighbor_report++;
@@ -1595,8 +1580,7 @@
wpa_printf(MSG_DEBUG,
"WNM: Candidate list included bit is set, but no candidates found");
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token,
- WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+ wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
NULL);
return;
@@ -1606,8 +1590,7 @@
wpa_printf(MSG_DEBUG,
"WNM: Configuration prevents roaming (BSSID set)");
wnm_send_bss_transition_mgmt_resp(
- wpa_s, wpa_s->wnm_dialog_token,
- WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+ wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
NULL);
return;
@@ -1624,35 +1607,21 @@
wpa_s->wnm_cand_valid_until.sec +=
wpa_s->wnm_cand_valid_until.usec / 1000000;
wpa_s->wnm_cand_valid_until.usec %= 1000000;
- os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
/*
- * Fetch the latest scan results from the kernel and check for
- * candidates based on those results first. This can help in
- * finding more up-to-date information should the driver has
- * done some internal scanning operations after the last scan
- * result update in wpa_supplicant.
- */
- if (wnm_fetch_scan_results(wpa_s) > 0)
+ * 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;
-
- /*
- * Try to use previously received scan results, if they are
- * recent enough to use for a connection.
- */
- if (wpa_s->last_scan_res_used > 0) {
- struct os_reltime now;
-
- os_get_reltime(&now);
- if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
- wpa_printf(MSG_DEBUG,
- "WNM: Try to use recent scan results");
- if (wnm_scan_process(wpa_s, 0) > 0)
- return;
- wpa_printf(MSG_DEBUG,
- "WNM: No match in previous scan results - try a new scan");
- }
- }
+ 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) {
@@ -1666,19 +1635,54 @@
wpa_supplicant_req_scan(wpa_s, 0, 0);
} else if (reply) {
enum bss_trans_mgmt_status_code status;
- if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
+
+ if ((wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) ||
+ wpa_s->wnm_link_removal)
status = WNM_BSS_TM_ACCEPT;
else {
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, wpa_s->wnm_dialog_token, status,
+ wpa_s, status,
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
}
}
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t data_len)
+{
+ const struct ieee80211_mgmt *frame =
+ (const struct ieee80211_mgmt *) data;
+
+ if (data_len <
+ IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
+ frame->u.action.category != WLAN_ACTION_WNM ||
+ frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
+ frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
+ return -1;
+
+ /*
+ * If disassoc imminent bit was set in the request, the response may
+ * indicate accept even if no candidate was found, so bail out here.
+ */
+ if (!wpa_s->wnm_target_bss) {
+ wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
+ return 0;
+ }
+
+ if (!wpa_s->current_ssid)
+ return 0;
+
+ wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
+ 0);
+
+ wpa_s->wnm_target_bss = NULL;
+ return 0;
+}
+
+
#define BTM_QUERY_MIN_SIZE 4
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
@@ -1890,7 +1894,9 @@
pos, end - pos);
if (wpa_s->wpa_state != WPA_COMPLETED ||
- os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ (!ether_addr_equal(sa, wpa_s->bssid) &&
+ (!wpa_s->valid_links ||
+ !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
"from our AP - ignore it");
return;
@@ -1934,7 +1940,9 @@
return; /* only nonzero values are used for request */
if (wpa_s->wpa_state != WPA_COMPLETED ||
- os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ (!ether_addr_equal(sa, wpa_s->bssid) &&
+ (!wpa_s->valid_links ||
+ !ether_addr_equal(sa, wpa_s->ap_mld_addr)))) {
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Collocated Interference Request frame not from current AP - ignore it");
return;
@@ -1964,7 +1972,9 @@
wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
act, MAC2STR(mgmt->sa));
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
- os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ (!ether_addr_equal(mgmt->sa, wpa_s->bssid) &&
+ (!wpa_s->valid_links ||
+ !ether_addr_equal(mgmt->sa, wpa_s->ap_mld_addr)))) {
wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
"frame");
return;
@@ -2025,14 +2035,14 @@
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
struct wpabuf *elems)
{
- wpabuf_free(wpa_s->coloc_intf_elems);
if (elems && wpabuf_len(elems) == 0) {
wpabuf_free(elems);
elems = NULL;
}
- wpa_s->coloc_intf_elems = elems;
- if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+ /* NOTE: The elements are not stored as they are only send out once */
+
+ if (wpa_s->conf->coloc_intf_reporting && elems &&
wpa_s->coloc_intf_dialog_token &&
(wpa_s->coloc_intf_auto_report == 1 ||
wpa_s->coloc_intf_auto_report == 3)) {
@@ -2041,15 +2051,38 @@
*/
wnm_send_coloc_intf_report(wpa_s,
wpa_s->coloc_intf_dialog_token,
- wpa_s->coloc_intf_elems);
+ elems);
}
+
+ wpabuf_free(elems);
}
void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
{
-#ifdef CONFIG_WNM
wpa_s->coloc_intf_dialog_token = 0;
wpa_s->coloc_intf_auto_report = 0;
-#endif /* CONFIG_WNM */
+}
+
+
+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;
+
+ /*
+ * 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;
+ }
+
+ return false;
}
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index e4957e4..235a838 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -65,18 +65,28 @@
const char *btm_candidates,
int cand_list);
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
const struct wpabuf *elems);
void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
struct wpabuf *elems);
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t data_len);
#ifdef CONFIG_WNM
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check);
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);
+
+void wnm_btm_reset(struct wpa_supplicant *wpa_s);
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+ return !!wpa_s->wnm_dialog_token;
+}
+
#else /* CONFIG_WNM */
static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
@@ -89,6 +99,21 @@
{
}
+static inline bool
+wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return false;
+}
+
+static inline void wnm_btm_reset(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+ return false;
+}
+
#endif /* CONFIG_WNM */
#endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 65078ed..af00e79 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/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-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi> and contributors";
#define VENDOR_ELEM_FRAME_ID \
" 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
@@ -2847,6 +2847,8 @@
}
+#ifndef CONFIG_NO_WMM_AC
+
static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -2867,6 +2869,8 @@
return wpa_ctrl_command(ctrl, "WMM_AC_STATUS");
}
+#endif /* CONFIG_NO_WMM_AC */
+
static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc,
char *argv[])
@@ -3889,6 +3893,7 @@
{ "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL,
cli_cmd_flag_none,
"<addr> = TDLS link status with <addr>" },
+#ifndef CONFIG_NO_WMM_AC
{ "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL,
cli_cmd_flag_none,
"<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] "
@@ -3900,6 +3905,7 @@
{ "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL,
cli_cmd_flag_none,
"= show status for Wireless Multi-Media Admission-Control" },
+#endif /* CONFIG_NO_WMM_AC */
{ "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL,
cli_cmd_flag_none,
"<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] "
@@ -4374,6 +4380,8 @@
}
} else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH_STARTED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, AP_EVENT_ENABLED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, AP_EVENT_DISABLED)) {
@@ -5123,7 +5131,7 @@
eloop_register_signal_terminate(wpa_cli_terminate, NULL);
- if (ctrl_ifname == NULL)
+ if (!ctrl_ifname && !global)
ctrl_ifname = wpa_cli_get_default_ifname();
if (reconnect && action_file && ctrl_ifname) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 31a9af6..88f3f2a 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -187,7 +187,10 @@
int val;
size_t i;
- res = iface->driver->get_scan_results2(iface->drv_priv);
+ if (iface->driver->get_scan_results)
+ res = iface->driver->get_scan_results(iface->drv_priv, NULL);
+ else
+ res = iface->driver->get_scan_results2(iface->drv_priv);
if (res == NULL)
goto fail;
@@ -231,7 +234,7 @@
if (iface->drv_priv == NULL)
return;
- if (iface->driver->get_scan_results2)
+ if (iface->driver->get_scan_results || iface->driver->get_scan_results2)
wpa_priv_get_scan_results2(iface, from, fromlen);
else
sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index d4401ff..15a859f 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -65,6 +65,7 @@
#include "wpas_kay.h"
#include "mesh.h"
#include "dpp_supplicant.h"
+#include "nan_usd.h"
#ifdef CONFIG_MESH
#include "ap/ap_config.h"
#include "ap/hostapd.h"
@@ -73,7 +74,7 @@
const char *const wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi> and contributors";
const char *const wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
@@ -125,6 +126,7 @@
static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_verify_ssid_beacon(void *eloop_ctx, void *timeout_ctx);
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
@@ -444,6 +446,7 @@
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
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);
pmksa_cache_clear_current(wpa_s->wpa);
os_memset(&mlo, 0, sizeof(mlo));
@@ -584,7 +587,9 @@
wpa_tdls_deinit(wpa_s->wpa);
#endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_WMM_AC
wmm_ac_clear_saved_tspecs(wpa_s);
+#endif /* CONFIG_NO_WMM_AC */
pmksa_candidate_free(wpa_s->wpa);
ptksa_cache_deinit(wpa_s->ptksa);
wpa_s->ptksa = NULL;
@@ -609,6 +614,7 @@
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);
wpas_wps_deinit(wpa_s);
@@ -675,9 +681,7 @@
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
-#ifdef CONFIG_WNM
- wnm_deallocate_memory(wpa_s);
-#endif /* CONFIG_WNM */
+ wnm_btm_reset(wpa_s);
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
@@ -701,7 +705,9 @@
wpa_s->vendor_elem[i] = NULL;
}
+#ifndef CONFIG_NO_WMM_AC
wmm_ac_notify_disassoc(wpa_s);
+#endif /* CONFIG_NO_WMM_AC */
wpa_s->sched_scan_plans_num = 0;
os_free(wpa_s->sched_scan_plans);
@@ -717,7 +723,9 @@
wpabuf_free(wpa_s->lci);
wpa_s->lci = NULL;
+#ifndef CONFIG_NO_RRM
wpas_clear_beacon_rep_data(wpa_s);
+#endif /* CONFIG_NO_RRM */
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
@@ -746,11 +754,17 @@
wpa_s->dpp = NULL;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ wpas_nan_usd_deinit(wpa_s);
+#endif /* CONFIG_NAN_USD */
+
#ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s);
#endif /* CONFIG_PASN */
+#ifndef CONFIG_NO_ROBUST_AV
wpas_scs_deinit(wpa_s);
wpas_dscp_deinit(wpa_s);
+#endif /* CONFIG_NO_ROBUST_AV */
#ifdef CONFIG_OWE
os_free(wpa_s->owe_trans_scan_freq);
@@ -881,7 +895,7 @@
struct wpa_scan_results *scan_res;
wpa_s->bgscan_ssid = wpa_s->current_ssid;
scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
- 0);
+ 0, NULL);
if (scan_res) {
bgscan_notify_scan(wpa_s, scan_res);
wpa_scan_results_free(scan_res);
@@ -917,6 +931,91 @@
}
+static void wpas_verify_ssid_beacon(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_bss *bss;
+ const u8 *ssid;
+ size_t ssid_len;
+
+ if (!wpa_s->current_ssid || !wpa_s->current_bss)
+ return;
+
+ ssid = wpa_s->current_bss->ssid;
+ ssid_len = wpa_s->current_bss->ssid_len;
+
+ if (wpa_s->current_ssid->ssid_len &&
+ (wpa_s->current_ssid->ssid_len != ssid_len ||
+ os_memcmp(wpa_s->current_ssid->ssid, ssid, ssid_len) != 0))
+ return;
+
+ if (wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+ !wpa_s->bigtk_set || wpa_s->ssid_verified)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "SSID not yet verified; check if the driver has received a verified Beacon frame");
+ if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
+ return;
+
+ bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->bssid);
+ if (!bss)
+ return;
+ wpa_printf(MSG_DEBUG, "The current beacon time stamp: 0x%llx",
+ (long long unsigned int) bss->tsf);
+ if (bss->tsf > wpa_s->first_beacon_tsf) {
+ const u8 *ie;
+
+ wpa_printf(MSG_DEBUG,
+ "Verified Beacon frame has been received");
+ wpa_s->beacons_checked++;
+
+ ie = wpa_bss_get_ie_beacon(bss, WLAN_EID_SSID);
+ if (ie && ie[1] == ssid_len &&
+ os_memcmp(&ie[2], ssid, ssid_len) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "SSID verified based on a Beacon frame and beacon protection");
+ wpa_s->ssid_verified = true;
+ return;
+ }
+
+ /* TODO: Multiple BSSID element */
+ }
+
+ if (wpa_s->beacons_checked < 16) {
+ eloop_register_timeout(wpa_s->next_beacon_check, 0,
+ wpas_verify_ssid_beacon, wpa_s, NULL);
+ wpa_s->next_beacon_check++;
+ }
+}
+
+
+static void wpas_verify_ssid_beacon_prot(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ wpa_printf(MSG_DEBUG,
+ "SSID not yet verified; try to verify using beacon protection");
+ /* Fetch the current scan result which is likely based on not yet
+ * verified payload since the current BIGTK was just received. Any
+ * newer update in the future with a larger timestamp value is an
+ * indication that a verified Beacon frame has been received. */
+ if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
+ return;
+
+ bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->bssid);
+ if (!bss)
+ return;
+ wpa_printf(MSG_DEBUG, "The initial beacon time stamp: 0x%llx",
+ (long long unsigned int) bss->tsf);
+ wpa_s->first_beacon_tsf = bss->tsf;
+ wpa_s->beacons_checked = 0;
+ wpa_s->next_beacon_check = 1;
+ eloop_cancel_timeout(wpas_verify_ssid_beacon, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_verify_ssid_beacon, wpa_s, NULL);
+}
+
+
/**
* wpa_supplicant_set_state - Set current connection state
* @wpa_s: Pointer to wpa_supplicant data
@@ -1068,8 +1167,14 @@
if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
wpa_supplicant_start_autoscan(wpa_s);
+ if (state == WPA_COMPLETED || state == WPA_INTERFACE_DISABLED ||
+ state == WPA_INACTIVE)
+ wnm_btm_reset(wpa_s);
+
+#ifndef CONFIG_NO_WMM_AC
if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
wmm_ac_notify_disassoc(wpa_s);
+#endif /* CONFIG_NO_WMM_AC */
if (wpa_s->wpa_state != old_state) {
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
@@ -1087,6 +1192,10 @@
if (wpa_s->wpa_state == WPA_COMPLETED)
wpas_dpp_connected(wpa_s);
#endif /* CONFIG_DPP2 */
+
+ if (wpa_s->wpa_state == WPA_COMPLETED &&
+ wpa_s->bigtk_set && !wpa_s->ssid_verified)
+ wpas_verify_ssid_beacon_prot(wpa_s);
}
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
if (update_fils_connect_params)
@@ -1596,8 +1705,8 @@
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_rsn = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
+ bss_rsnx = wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
} else {
bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL;
@@ -2009,9 +2118,25 @@
(wpa_s->connection_ht || wpa_s->connection_vht ||
wpa_s->connection_he || wpa_s->connection_eht);
if (!wmm && bss)
- wmm = wpa_bss_get_vendor_ie(bss, WMM_IE_VENDOR_TYPE);
+ wmm = !!wpa_bss_get_vendor_ie(bss, WMM_IE_VENDOR_TYPE);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_WMM_ENABLED, wmm);
+ if (ssid->ssid_protection && proto == WPA_PROTO_RSN) {
+ bool ssid_prot;
+
+ /* Enable SSID protection 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. */
+ ssid_prot = ieee802_11_rsnx_capab(
+ bss_rsnx, WLAN_RSNX_CAPAB_SSID_PROTECTION);
+ if (!skip_default_rsne)
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION,
+ proto == WPA_PROTO_RSN && ssid_prot);
+ } else {
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SSID_PROTECTION, false);
+ }
+
if (!skip_default_rsne) {
if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
wpa_ie_len)) {
@@ -2111,7 +2236,9 @@
static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx,
struct wpa_bss *bss)
{
+#ifndef CONFIG_NO_ROBUST_AV
bool scs = true, mscs = true;
+#endif /* CONFIG_NO_ROBUST_AV */
*pos = 0x00;
@@ -2127,7 +2254,9 @@
case 2: /* Bits 16-23 */
#ifdef CONFIG_WNM
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
- if (!wpa_s->disable_mbo_oce && !wpa_s->conf->disable_btm)
+ if ((wpas_driver_bss_selection(wpa_s) ||
+ !wpa_s->disable_mbo_oce) &&
+ !wpa_s->conf->disable_btm)
*pos |= 0x08; /* Bit 19 - BSS Transition */
#endif /* CONFIG_WNM */
break;
@@ -2156,6 +2285,7 @@
#endif /* CONFIG_MBO */
break;
case 6: /* Bits 48-55 */
+#ifndef CONFIG_NO_ROBUST_AV
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->disable_scs_support)
scs = false;
@@ -2169,6 +2299,7 @@
}
if (scs)
*pos |= 0x40; /* Bit 54 - SCS */
+#endif /* CONFIG_NO_ROBUST_AV */
break;
case 7: /* Bits 56-63 */
break;
@@ -2185,6 +2316,7 @@
#endif /* CONFIG_FILS */
break;
case 10: /* Bits 80-87 */
+#ifndef CONFIG_NO_ROBUST_AV
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->disable_mscs_support)
mscs = false;
@@ -2198,6 +2330,7 @@
}
if (mscs)
*pos |= 0x20; /* Bit 85 - Mirrored SCS */
+#endif /* CONFIG_NO_ROBUST_AV */
break;
}
}
@@ -2315,8 +2448,7 @@
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 (os_memcmp(wpa_s->own_addr, ssid->mac_value,
- ETH_ALEN) == 0)
+ if (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) &&
@@ -2402,6 +2534,7 @@
password = ssid->passphrase;
if (!password ||
+ !wpa_key_mgmt_sae(ssid->key_mgmt) ||
(conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
!wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
!force &&
@@ -2422,7 +2555,7 @@
}
-static void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s)
+void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s)
{
#if defined(CONFIG_SAE) && defined(CONFIG_SME)
os_free(wpa_s->sme.sae_rejected_groups);
@@ -2475,6 +2608,7 @@
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
+ bool clear_rejected = true;
struct wpa_connect_work *cwork;
enum wpas_mac_addr_style rand_style;
@@ -2495,7 +2629,13 @@
wpa_s->eapol_failed = 0;
wpa_s->multi_ap_ie = 0;
+#ifndef CONFIG_NO_WMM_AC
wmm_ac_clear_saved_tspecs(wpa_s);
+#endif /* CONFIG_NO_WMM_AC */
+#ifdef CONFIG_WNM
+ wpa_s->wnm_mode = 0;
+ wpa_s->wnm_target_bss = NULL;
+#endif /* CONFIG_WNM */
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
#ifdef CONFIG_TESTING_OPTIONS
@@ -2506,16 +2646,19 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
wpa_s->reassoc_same_ess = 1;
if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+#ifndef CONFIG_NO_WMM_AC
wmm_ac_save_tspecs(wpa_s);
+#endif /* CONFIG_NO_WMM_AC */
wpa_s->reassoc_same_bss = 1;
+ clear_rejected = false;
} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {
os_get_reltime(&wpa_s->roam_start);
}
- } else {
-#ifdef CONFIG_SAE
- wpa_s_clear_sae_rejected(wpa_s);
-#endif /* CONFIG_SAE */
}
+
+ if (clear_rejected)
+ wpa_s_clear_sae_rejected(wpa_s);
+
#ifdef CONFIG_SAE
wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
#endif /* CONFIG_SAE */
@@ -2890,7 +3033,8 @@
if (obss_scan) {
struct wpa_scan_results *scan_res;
- scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0,
+ NULL);
if (scan_res == NULL) {
/* Back to HT20 */
freq->sec_channel_offset = 0;
@@ -3054,7 +3198,7 @@
freq->sec_channel_offset,
chwidth, seg0, seg1, vht_caps,
&mode->he_capab[ieee80211_mode],
- &mode->eht_capab[ieee80211_mode]) != 0)
+ &mode->eht_capab[ieee80211_mode], 0) != 0)
return false;
*freq = vht_freq;
@@ -3072,9 +3216,9 @@
int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
enum hostapd_hw_mode hw_mode;
struct hostapd_hw_modes *mode = NULL;
- int i, obss_scan = 1;
+ int obss_scan = 1;
u8 channel;
- bool is_6ghz;
+ bool is_6ghz, is_24ghz;
freq->freq = ssid->frequency;
@@ -3091,18 +3235,15 @@
}
hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
- for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
- if (wpa_s->hw.modes[i].mode == hw_mode &&
- hw_mode_get_channel(&wpa_s->hw.modes[i], freq->freq,
- NULL) != NULL) {
- mode = &wpa_s->hw.modes[i];
- break;
- }
- }
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ hw_mode, is_6ghz_freq(ssid->frequency));
if (!mode)
return;
+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B;
+
is_6ghz = is_6ghz_freq(freq->freq);
freq->ht_enabled = 0;
@@ -3114,7 +3255,7 @@
freq->ht_enabled = ibss_mesh_can_use_ht(wpa_s, ssid, mode);
if (freq->ht_enabled)
freq->vht_enabled = ibss_mesh_can_use_vht(wpa_s, ssid, mode);
- if (freq->vht_enabled || is_6ghz)
+ if (freq->vht_enabled || (freq->ht_enabled && is_24ghz) || is_6ghz)
freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
ieee80211_mode);
freq->channel = channel;
@@ -3256,8 +3397,10 @@
size_t wfa_ie_len, buf_len;
os_memset(wfa_capa, 0, sizeof(wfa_capa));
+#ifndef CONFIG_NO_ROBUST_AV
if (wpa_s->enable_dscp_policy_capa)
wfa_capa[0] |= WFA_CAPA_QM_DSCP_POLICY;
+#endif /* CONFIG_NO_ROBUST_AV */
if (wpa_is_non_eht_scs_traffic_desc_supported(bss))
wfa_capa[0] |= WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC;
@@ -3335,7 +3478,7 @@
}
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;
@@ -3521,12 +3664,14 @@
os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
#endif /* CONFIG_P2P */
+#ifndef CONFIG_NO_RRM
if (bss) {
wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss,
wpa_ie + wpa_ie_len,
max_wpa_ie_len -
wpa_ie_len);
}
+#endif /* CONFIG_NO_RRM */
/*
* Workaround: Add Extended Capabilities element only if the AP
@@ -3558,6 +3703,17 @@
}
}
+ if (ssid->max_idle && wpa_ie_len + 5 <= max_wpa_ie_len) {
+ u8 *pos = wpa_ie;
+
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+ *pos++ = 3;
+ WPA_PUT_LE16(pos, ssid->max_idle);
+ pos += 2;
+ *pos = 0; /* Idle Options */
+ wpa_ie_len += 5;
+ }
+
#ifdef CONFIG_HS20
if (is_hs20_network(wpa_s, ssid, bss)
#ifndef ANDROID /* Android does not use the native HS 2.0 config */
@@ -3753,6 +3909,7 @@
wpa_ie_len += wpa_s->rsnxe_len;
}
+#ifndef CONFIG_NO_ROBUST_AV
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->disable_mscs_support)
goto mscs_end;
@@ -3787,16 +3944,21 @@
wpabuf_free(mscs_ie);
}
mscs_end:
+#endif /* CONFIG_NO_ROBUST_AV */
wpa_ie_len = wpas_populate_wfa_capa(wpa_s, bss, wpa_ie, wpa_ie_len,
max_wpa_ie_len);
if (ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
+ struct multi_ap_params multi_ap = { 0 };
+
+ multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+ multi_ap.profile = ssid->multi_ap_profile;
multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len,
- MULTI_AP_BACKHAUL_STA);
+ &multi_ap);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
@@ -3806,6 +3968,57 @@
wpa_ie_len += multi_ap_ie_len;
}
+ if (!wpas_driver_bss_selection(wpa_s) &&
+ wpas_rsn_overriding(wpa_s) &&
+ wpas_ap_supports_rsn_overriding(wpa_s, bss) &&
+ wpa_ie_len + 2 + 4 <= max_wpa_ie_len) {
+ u8 *pos = wpa_ie + wpa_ie_len;
+ u32 type = 0;
+ const u8 *ie;
+
+ ie = wpa_bss_get_rsne(wpa_s, bss, ssid, wpa_s->valid_links);
+ if (ie && ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie[1] >= 4)
+ type = WPA_GET_BE32(&ie[2]);
+
+ if (type) {
+ /* Indicate support for RSN overriding */
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4;
+ WPA_PUT_BE32(pos, type);
+ pos += 4;
+ wpa_hexdump(MSG_MSGDUMP, "RSNE Override", wpa_ie,
+ pos - wpa_ie);
+ wpa_ie_len += 2 + 4;
+ }
+ }
+
+ if (wpas_driver_bss_selection(wpa_s) &&
+ wpas_rsn_overriding(wpa_s)) {
+ if (wpa_ie_len + 2 + 4 <= max_wpa_ie_len) {
+ u8 *pos = wpa_ie + wpa_ie_len;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4;
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
+ wpa_hexdump(MSG_MSGDUMP, "RSNE Override", wpa_ie,
+ pos - wpa_ie);
+ wpa_ie_len += 2 + 4;
+ }
+
+ if (wpa_ie_len + 2 + 4 <= max_wpa_ie_len) {
+ u8 *pos = wpa_ie + wpa_ie_len;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4;
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ pos += 4;
+ wpa_hexdump(MSG_MSGDUMP, "RSNE Override 2",
+ wpa_ie, pos - wpa_ie);
+ wpa_ie_len += 2 + 4;
+ }
+ }
+
params->wpa_ie = wpa_ie;
params->wpa_ie_len = wpa_ie_len;
params->auth_alg = algs;
@@ -4030,6 +4243,7 @@
#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) {
@@ -4071,7 +4285,9 @@
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
wpa_s->rsnxe_len = 0;
+#ifndef CONFIG_NO_ROBUST_AV
wpa_s->mscs_setup_done = false;
+#endif /* CONFIG_NO_ROBUST_AV */
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL);
if (!wpa_ie) {
@@ -4379,7 +4595,7 @@
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 &&
@@ -4466,7 +4682,8 @@
* can stop right here; the association will not
* succeed.
*/
- wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid,
+ NULL);
wpa_s->assoc_status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpas_notify_assoc_status_code(wpa_s, wpa_s->pending_bssid, 0, NULL, 0);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -4538,6 +4755,8 @@
}
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+ if (bss)
+ wpa_sm_set_ssid(wpa_s->wpa, bss->ssid, bss->ssid_len);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
@@ -4562,8 +4781,10 @@
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
+#ifndef CONFIG_NO_ROBUST_AV
wpas_scs_deinit(wpa_s);
wpas_dscp_deinit(wpa_s);
+#endif /* CONFIG_NO_ROBUST_AV */
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
}
@@ -4584,14 +4805,18 @@
int zero_addr = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
- " pending_bssid=" MACSTR " reason=%d (%s) state=%s",
+ " pending_bssid=" MACSTR
+ " reason=%d (%s) state=%s valid_links=0x%x ap_mld_addr=" MACSTR,
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
reason_code, reason2str(reason_code),
- wpa_supplicant_state_txt(wpa_s->wpa_state));
+ wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_s->valid_links,
+ MAC2STR(wpa_s->ap_mld_addr));
- if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
- (wpa_s->wpa_state == WPA_AUTHENTICATING ||
- wpa_s->wpa_state == WPA_ASSOCIATING))
+ if (wpa_s->valid_links && !is_zero_ether_addr(wpa_s->ap_mld_addr))
+ addr = wpa_s->ap_mld_addr;
+ else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+ wpa_s->wpa_state == WPA_ASSOCIATING))
addr = wpa_s->pending_bssid;
else if (!is_zero_ether_addr(wpa_s->bssid))
addr = wpa_s->bssid;
@@ -4909,6 +5134,7 @@
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)
@@ -4954,6 +5180,18 @@
(ssid->mode == WPAS_MODE_MESH ||
ssid->mode == WPAS_MODE_AP) ? ssid : NULL;
+ if (ssid->scan_ssid &&
+ (wpa_s->no_suitable_network || wpa_s->last_scan_external)) {
+ wpa_printf(MSG_DEBUG,
+ "Request a new scan for hidden network");
+ request_new_scan = true;
+ } else if ((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.
@@ -4973,7 +5211,7 @@
wpa_s_setup_sae_pt(wpa_s->conf, 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);
@@ -5343,14 +5581,14 @@
os_memcmp(ssid, entry->ssid, ssid_len) == 0)) ||
wired) &&
(!entry->bssid_set ||
- os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ 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 ||
- os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ ether_addr_equal(bssid, entry->bssid)))
return entry;
#endif /* CONFIG_WPS */
@@ -5360,13 +5598,13 @@
owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
entry->ssid_len)) &&
(!entry->bssid_set ||
- os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+ ether_addr_equal(bssid, entry->bssid)))
return entry;
#endif /* CONFIG_OWE */
if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
entry->ssid_len == 0 &&
- os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+ ether_addr_equal(bssid, entry->bssid))
return entry;
entry = entry->next;
@@ -5494,7 +5732,7 @@
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
- os_memcmp(src_addr, connected_addr, ETH_ALEN) != 0)) {
+ !ether_addr_equal(src_addr, connected_addr))) {
/*
* There is possible race condition between receiving the
* association event and the EAPOL frame since they are coming
@@ -5524,7 +5762,7 @@
}
wpa_s->last_eapol_matches_bssid =
- os_memcmp(src_addr, connected_addr, ETH_ALEN) == 0;
+ ether_addr_equal(src_addr, connected_addr);
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
@@ -5681,7 +5919,7 @@
fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
#endif /* CONFIG_FST */
- if (os_memcmp(prev_mac_addr, wpa_s->own_addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(prev_mac_addr, wpa_s->own_addr))
wpas_notify_mac_address_changed(wpa_s);
return 0;
@@ -5698,7 +5936,7 @@
return;
eth = (const struct l2_ethhdr *) buf;
- if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
+ if (!ether_addr_equal(eth->h_dest, wpa_s->own_addr) &&
!(eth->h_dest[0] & 0x01)) {
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
" (bridge - not for this interface - ignore)",
@@ -5873,8 +6111,11 @@
dl_list_init(&wpa_s->fils_hlp_req);
#ifdef CONFIG_TESTING_OPTIONS
dl_list_init(&wpa_s->drv_signal_override);
+ wpa_s->test_assoc_comeback_type = -1;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifndef CONFIG_NO_ROBUST_AV
dl_list_init(&wpa_s->active_scs_ids);
+#endif /* CONFIG_NO_ROBUST_AV */
wpa_s->ml_probe_mld_id = -1;
return wpa_s;
@@ -6387,7 +6628,7 @@
{
struct wpa_supplicant *wpa_s = ctx;
- if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(wpa_s->bssid, da)) {
wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR,
__func__, MAC2STR(wpa_s->bssid), MAC2STR(da));
return -1;
@@ -6403,7 +6644,7 @@
{
struct wpa_supplicant *wpa_s = ctx;
- WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+ WPA_ASSERT(ether_addr_equal(wpa_s->bssid, addr));
return wpa_s->received_mb_ies;
}
@@ -6414,7 +6655,7 @@
struct wpa_supplicant *wpa_s = ctx;
struct mb_ies_info info;
- WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+ WPA_ASSERT(ether_addr_equal(wpa_s->bssid, addr));
if (!mb_ies_info_by_ies(&info, buf, size)) {
wpabuf_free(wpa_s->received_mb_ies);
@@ -7203,6 +7444,9 @@
return -1;
}
+ 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,
@@ -7232,6 +7476,7 @@
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;
@@ -7251,7 +7496,9 @@
wpa_s->extended_capa_len = capa.extended_capa_len;
wpa_s->num_multichan_concurrent =
capa.num_multichan_concurrent;
+#ifndef CONFIG_NO_WMM_AC
wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+#endif /* CONFIG_NO_WMM_AC */
wpa_s->max_num_akms = capa.max_num_akms;
if (capa.mac_addr_rand_scan_supported)
@@ -7339,6 +7586,11 @@
return -1;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (wpas_nan_usd_init(wpa_s) < 0)
+ return -1;
+#endif /* CONFIG_NAN_USD */
+
if (wpa_supplicant_init_eapol(wpa_s) < 0)
return -1;
wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
@@ -7408,7 +7660,9 @@
if (wpas_init_ext_pw(wpa_s) < 0)
return -1;
+#ifndef CONFIG_NO_RRM
wpas_rrm_reset(wpa_s);
+#endif /* CONFIG_NO_RRM */
wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
@@ -8173,6 +8427,10 @@
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM)
wpa_supplicant_set_default_scan_ies(wpa_s);
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_FT_PREPEND_PMKID)
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_PREPEND_PMKID,
+ wpa_s->conf->ft_prepend_pmkid);
+
#ifdef CONFIG_BGSCAN
/*
* We default to global bgscan parameters only when per-network bgscan
@@ -8241,7 +8499,8 @@
}
-void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 **link_bssids)
{
int timeout;
int count;
@@ -8276,6 +8535,12 @@
return;
}
+ /* Also mark links as failed */
+ while (link_bssids && *link_bssids) {
+ wpa_bssid_ignore_add(wpa_s, *link_bssids);
+ link_bssids++;
+ }
+
/*
* Add the failed BSSID into the ignore list and speed up next scan
* attempt if there could be other APs that could accept association.
@@ -8425,6 +8690,28 @@
}
+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)
+{
+ if (wpa_s->conf->rsn_overriding == RSN_OVERRIDING_DISABLED)
+ return false;
+
+ if (wpa_s->conf->rsn_overriding == 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) || defined (CONFIG_CTRL_IFACE_AIDL)
int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
@@ -8692,9 +8979,16 @@
int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
{
if (wpa_s->current_ssid == NULL ||
- wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
- os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+ wpa_s->wpa_state < WPA_4WAY_HANDSHAKE)
return 0;
+ if (wpa_s->valid_links) {
+ if (!ether_addr_equal(addr, wpa_s->ap_mld_addr) &&
+ !wpas_ap_link_address(wpa_s, addr))
+ return 0;
+ } else {
+ if (!ether_addr_equal(addr, wpa_s->bssid))
+ return 0;
+ }
return wpa_sm_pmf_enabled(wpa_s->wpa);
}
@@ -8819,8 +9113,8 @@
return 0;
for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
- if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
- bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
+ bssid))
return 1;
}
@@ -9097,8 +9391,7 @@
if (modes[i].mode != mode ||
!modes[i].num_channels || !modes[i].channels)
continue;
- if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) ||
- (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq)))
+ if (is_6ghz == modes[i].is_6ghz)
return &modes[i];
}
@@ -9130,7 +9423,7 @@
dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
- if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(bssid, bss->bssid))
return bss;
}
@@ -9213,7 +9506,7 @@
dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
- if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) {
+ if (ether_addr_equal(bss->bssid, tmp->bssid)) {
disallowed = tmp;
break;
}
@@ -9311,8 +9604,7 @@
dl_list_for_each(dso, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
- if (os_memcmp(wpa_s->bssid, dso->bssid,
- ETH_ALEN) != 0)
+ if (!ether_addr_equal(wpa_s->bssid, dso->bssid))
continue;
wpa_printf(MSG_DEBUG,
"Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d",
@@ -9338,17 +9630,21 @@
struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s)
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_scan_results *scan_res;
#ifdef CONFIG_TESTING_OPTIONS
size_t idx;
#endif /* CONFIG_TESTING_OPTIONS */
- if (!wpa_s->driver->get_scan_results2)
+ if (wpa_s->driver->get_scan_results)
+ scan_res = wpa_s->driver->get_scan_results(wpa_s->drv_priv,
+ bssid);
+ else if (wpa_s->driver->get_scan_results2)
+ scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+ else
return NULL;
- scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
#ifdef CONFIG_TESTING_OPTIONS
for (idx = 0; scan_res && idx < scan_res->num; idx++) {
@@ -9357,7 +9653,7 @@
dl_list_for_each(dso, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
- if (os_memcmp(res->bssid, dso->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(res->bssid, dso->bssid))
continue;
wpa_printf(MSG_DEBUG,
"Override driver scan signal level %d->%d for "
@@ -9379,18 +9675,15 @@
}
-static bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr)
+bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr)
{
int i;
if (!wpa_s->valid_links)
return false;
- for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- if (!(wpa_s->valid_links & BIT(i)))
- continue;
-
- if (os_memcmp(wpa_s->links[i].bssid, addr, ETH_ALEN) == 0)
+ for_each_link(wpa_s->valid_links, i) {
+ if (ether_addr_equal(wpa_s->links[i].bssid, addr))
return true;
}
@@ -9439,3 +9732,58 @@
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 (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+ 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 (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(wpa_s->valid_links & BIT(i)))
+ continue;
+ 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;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index f6e4f83..abbe7d7 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -878,6 +878,15 @@
# 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.
+# 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
@@ -982,9 +991,11 @@
# parameter uses following format: "<bgscan module name>:<module parameters>"
# Following bgscan modules are available:
# simple - Periodic background scans based on signal strength
+# send_btm_query > 0 means do this many BTM queries before attempting a scan.
# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
-# <long interval>"
+# <long interval>[:<send_btm_query>]"
# bgscan="simple:30:-45:300"
+# bgscan="simple:30:-45:300:3"
# learn - Learn channels used by the network and try to avoid bgscans on other
# channels (experimental)
# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
@@ -1715,6 +1726,12 @@
# support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be
# added to a bridge to allow forwarding frames over this backhaul link.
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
@@ -1763,6 +1780,11 @@
# In STA mode it defines the EDMG channel for connection (if supported by AP).
#edmg_channel=9
+# BSS max idle period to request
+# If nonzero, request the specified number of 1000 TU (i.e., 1.024 s)
+# as the maximum idle period for the STA during association.
+#max_idle=600
+
# Example blocks:
# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant/Android.bp b/wpa_supplicant/wpa_supplicant/Android.bp
index 3f76ae8..6623454 100644
--- a/wpa_supplicant/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/wpa_supplicant/Android.bp
@@ -23,24 +23,13 @@
],
}
-soong_config_module_type_import {
- from: "frameworks/opt/net/wifi/libwifi_hal/Android.bp",
- module_types: ["wifi_cc_defaults"],
-}
-
-wifi_cc_defaults {
+cc_defaults {
name: "lib_vendor_wpa_supplicant",
- soong_config_variables: {
- board_wlan_device: {
- emulator: {
- static_libs: ["lib_driver_cmd_simulated_cf_bp"],
- },
- // TODO(b/295186835): Convert lib_driver_cmd_* to soong
- conditions_default: {
- static_libs: ["lib_driver_cmd_fallback"],
- },
- },
- },
+ static_libs: select(soong_config_variable("wifi", "board_wlan_device"), {
+ "emulator": ["lib_driver_cmd_simulated_cf_bp"],
+ // TODO(b/295186835): Convert lib_driver_cmd_* to soong
+ default: ["lib_driver_cmd_fallback"],
+ }),
}
cc_binary {
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 0890110..245ac93 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1,6 +1,6 @@
/*
* wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -494,6 +494,7 @@
u8 bssid[ETH_ALEN];
enum beacon_report_detail report_detail;
struct bitfield *eids;
+ struct bitfield *ext_eids;
};
@@ -678,6 +679,13 @@
};
+struct ml_sta_link_info {
+ u8 link_id;
+ u8 bssid[ETH_ALEN];
+ u16 status;
+};
+
+
/**
* struct wpa_supplicant - Internal data for wpa_supplicant interface
*
@@ -740,11 +748,12 @@
u8 ap_mld_addr[ETH_ALEN];
u8 mlo_assoc_link_id;
u16 valid_links; /* bitmap of valid MLO link IDs */
- struct ml_sta_link_info {
+ struct {
u8 addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
unsigned int freq;
struct wpa_bss *bss;
+ bool disabled;
} links[MAX_NUM_MLD_LINKS];
u8 *last_con_fail_realm;
size_t last_con_fail_realm_len;
@@ -813,6 +822,7 @@
size_t last_scan_res_used;
size_t last_scan_res_size;
struct os_reltime last_scan;
+ bool last_scan_external;
const struct wpa_driver_ops *driver;
int interface_removed; /* whether the network interface has been
@@ -928,6 +938,7 @@
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;
@@ -1046,6 +1057,7 @@
bool ext_ml_auth;
int *sae_rejected_groups;
#endif /* CONFIG_SAE */
+ u16 assoc_auth_type;
} sme;
#endif /* CONFIG_SME */
@@ -1207,6 +1219,7 @@
struct wpa_ssid *bgscan_ssid;
const struct bgscan_ops *bgscan;
void *bgscan_priv;
+ int signal_threshold;
const struct autoscan_ops *autoscan;
struct wpa_driver_scan_params *autoscan_params;
@@ -1315,14 +1328,15 @@
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;
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
- u8 wnm_cand_from_bss[ETH_ALEN];
+ struct wpa_bss *wnm_target_bss;
enum bss_trans_mgmt_status_code bss_tm_status;
bool bss_trans_mgmt_in_progress;
- struct wpabuf *coloc_intf_elems;
u8 coloc_intf_dialog_token;
u8 coloc_intf_auto_report;
u8 coloc_intf_timeout;
@@ -1381,6 +1395,7 @@
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
unsigned int disable_eapol_g2_tx;
+ int test_assoc_comeback_type;
#endif /* CONFIG_TESTING_OPTIONS */
struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1487,6 +1502,9 @@
int dpp_netrole;
int dpp_auth_ok_on_ack;
int dpp_in_response_listen;
+ bool dpp_tx_auth_resp_on_roc_stop;
+ bool dpp_tx_chan_change;
+ bool dpp_listen_on_tx_expire;
int dpp_gas_client;
int dpp_gas_server;
int dpp_gas_dialog_token;
@@ -1567,8 +1585,24 @@
unsigned int multi_ap_ie:1;
unsigned int multi_ap_backhaul:1;
unsigned int multi_ap_fronthaul:1;
+
+#ifndef CONFIG_NO_ROBUST_AV
struct robust_av_data robust_av;
bool mscs_setup_done;
+ struct scs_robust_av_data scs_robust_av_req;
+ u8 scs_dialog_token;
+ struct dl_list active_scs_ids;
+ bool ongoing_scs_req;
+ u8 dscp_req_dialog_token;
+ u8 dscp_query_dialog_token;
+ unsigned int enable_dscp_policy_capa:1;
+ unsigned int connection_dscp:1;
+ unsigned int wait_for_dscp_req:1;
+#ifdef CONFIG_TESTING_OPTIONS
+ unsigned int disable_scs_support:1;
+ unsigned int disable_mscs_support:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_NO_ROBUST_AV */
bool wps_scan_done; /* Set upon receiving scan results event */
bool supp_pbc_active; /* Set for interface when PBC is triggered */
@@ -1580,19 +1614,7 @@
unsigned int pasn_count;
struct pasn_auth *pasn_params;
#endif /* CONFIG_PASN */
- struct scs_robust_av_data scs_robust_av_req;
- u8 scs_dialog_token;
-#ifdef CONFIG_TESTING_OPTIONS
- unsigned int disable_scs_support:1;
- unsigned int disable_mscs_support:1;
-#endif /* CONFIG_TESTING_OPTIONS */
- struct dl_list active_scs_ids;
- bool ongoing_scs_req;
- u8 dscp_req_dialog_token;
- u8 dscp_query_dialog_token;
- unsigned int enable_dscp_policy_capa:1;
- unsigned int connection_dscp:1;
- unsigned int wait_for_dscp_req:1;
+
bool is_6ghz_enabled;
bool crossed_6ghz_dom;
bool last_scan_all_chan;
@@ -1609,6 +1631,18 @@
* owe_transition_search == 1 */
int *owe_trans_scan_freq;
#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_NAN_USD
+ struct nan_de *nan_de;
+ struct wpa_radio_work *nan_usd_listen_work;
+ struct wpa_radio_work *nan_usd_tx_work;
+#endif /* CONFIG_NAN_USD */
+
+ bool ssid_verified;
+ bool bigtk_set;
+ u64 first_beacon_tsf;
+ unsigned int beacons_checked;
+ unsigned int next_beacon_check;
};
@@ -1715,10 +1749,12 @@
enum frame_encryption encrypted);
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
-void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 **link_bssids);
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);
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);
@@ -1905,6 +1941,7 @@
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,
bool force);
+void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s);
bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
@@ -2015,5 +2052,10 @@
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);
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 9804b91..de216d2 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -418,7 +418,7 @@
const u8 *ie;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
- if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(bss->bssid, wpa_s->bssid))
continue;
if (ssid == NULL ||
((bss->ssid_len == ssid->ssid_len &&
@@ -441,11 +441,11 @@
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
- ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
+ ie = wpa_bss_get_rsne(wpa_s, curr, ssid, false);
if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
- ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX);
+ ie = wpa_bss_get_rsnxe(wpa_s, curr, ssid, false);
if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
} else {
@@ -465,7 +465,7 @@
/* No WPA/RSN IE found in the cached scan results. Try to get updated
* scan results from the driver. */
- if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+ if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
return -1;
return wpa_get_beacon_ie(wpa_s);
@@ -550,6 +550,8 @@
enum key_flag key_flag)
{
struct wpa_supplicant *wpa_s = _wpa_s;
+ int ret;
+
if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
/* Clear the MIC error counter when setting a new PTK. */
wpa_s->mic_errors_seen = 0;
@@ -572,8 +574,14 @@
wpa_s->last_tk_len = key_len;
}
#endif /* CONFIG_TESTING_OPTIONS */
- return wpa_drv_set_key(wpa_s, link_id, alg, addr, key_idx, set_tx, seq,
- seq_len, key, key_len, key_flag);
+
+ ret = wpa_drv_set_key(wpa_s, link_id, alg, addr, key_idx, set_tx, seq,
+ seq_len, key, key_len, key_flag);
+ if (ret == 0 && (key_idx == 6 || key_idx == 7) &&
+ alg != WPA_ALG_NONE && key_len > 0)
+ wpa_s->bigtk_set = true;
+
+ return ret;
}
@@ -1470,6 +1478,15 @@
}
+static void wpa_supplicant_ssid_verified(void *_wpa_s)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+
+ wpa_s->ssid_verified = true;
+ wpa_msg(wpa_s, MSG_INFO, "RSN: SSID matched expected value");
+}
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
@@ -1536,6 +1553,7 @@
ctx->set_ltf_keyseed = wpa_supplicant_set_ltf_keyseed;
#endif /* CONFIG_PASN */
ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry;
+ ctx->ssid_verified = wpa_supplicant_ssid_verified;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index ce5398c..9e7a57c 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -17,9 +17,12 @@
static int wpas_bssid_ignore_module_tests(void)
{
struct wpa_supplicant wpa_s;
+ struct wpa_global global;
int ret = -1;
os_memset(&wpa_s, 0, sizeof(wpa_s));
+ os_memset(&global, 0, sizeof(global));
+ wpa_s.global = &global;
wpa_bssid_ignore_clear(&wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index cd94b64..f3a8c9c 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -226,7 +226,7 @@
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)
@@ -295,8 +295,7 @@
if (ssid->bssid_set || new_ssid->bssid_set) {
if (ssid->bssid_set != new_ssid->bssid_set)
continue;
- if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) !=
- 0)
+ if (!ether_addr_equal(ssid->bssid, new_ssid->bssid))
continue;
}
@@ -1062,7 +1061,7 @@
*/
#ifndef CONFIG_P2P
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
- if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
+ if (!ether_addr_equal(bssid, bss->bssid))
continue;
os_free(ssid->ssid);
@@ -1209,14 +1208,19 @@
}
}
#endif /* CONFIG_P2P */
- os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
- multi_ap_backhaul_sta ? " multi_ap=1" : "");
+ if (multi_ap_backhaul_sta)
+ os_snprintf(phase1, sizeof(phase1), "pbc=1 multi_ap=%d",
+ multi_ap_backhaul_sta);
+ else
+ os_snprintf(phase1, sizeof(phase1), "pbc=1");
if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
return -1;
if (wpa_s->wps_fragment_size)
ssid->eap.fragment_size = wpa_s->wps_fragment_size;
- if (multi_ap_backhaul_sta)
+ if (multi_ap_backhaul_sta) {
ssid->multi_ap_backhaul_sta = 1;
+ ssid->multi_ap_profile = multi_ap_backhaul_sta;
+ }
wpa_s->supp_pbc_active = true;
wpa_s->wps_overlap = false;
wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
@@ -1812,7 +1816,7 @@
}
if (!ret && ssid->bssid_set &&
- os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
+ ether_addr_equal(ssid->bssid, bss->bssid)) {
/* allow wildcard SSID due to hardcoded BSSID match */
ret = 1;
}
@@ -1851,11 +1855,11 @@
const u8 *sel_uuid)
{
if (!ap->pbc_active ||
- os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+ ether_addr_equal(selected->bssid, ap->bssid))
return false;
if (!is_zero_ether_addr(ssid->bssid) &&
- os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ !ether_addr_equal(ap->bssid, ssid->bssid)) {
wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
" in active PBC mode due to local BSSID limitation",
MAC2STR(ap->bssid));
@@ -2943,7 +2947,7 @@
for (i = 0; i < wpa_s->num_wps_ap; i++) {
struct wps_ap_info *ap = &wpa_s->wps_ap[i];
- if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
+ if (ether_addr_equal(ap->bssid, bssid))
return ap;
}