Revert "Revert "[wpa_supplicant] cumilative patch from commit 3a..."

Revert submission 28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR

Reason for revert: Fixed the regression issue (ag/28389573)

Reverted changes: /q/submissionid:28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR

Bug: 329004037
Test: Turn ON/OFF SoftAp multiple times

Change-Id: Ibfff2a847be5678f1a6d77e28506a05936812a91
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index eb4224b..baf3ad1 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -139,11 +139,7 @@
 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 += 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 +320,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 +354,10 @@
 CONFIG_WNM=y
 endif
 
+ifdef CONFIG_BGSCAN_SIMPLE
+CONFIG_WNM=y
+endif
+
 ifdef CONFIG_WNM
 L_CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.c
@@ -466,6 +472,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 +977,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 +1037,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 +1058,7 @@
 
 ifdef CONFIG_TESTING_OPTIONS
 L_CFLAGS += -DCONFIG_TESTING_OPTIONS
+NEED_AES_WRAP=y
 endif
 
 ifdef NEED_RSN_AUTHENTICATOR
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8c417b8..93fc338 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,7 @@
 OBJS += ../src/utils/bitfield.o
 OBJS += ../src/utils/ip_addr.o
 OBJS += ../src/utils/crc32.o
-OBJS += op_classes.o
-OBJS += rrm.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 +121,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 +189,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 +315,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 +349,10 @@
 CONFIG_WNM=y
 endif
 
+ifdef CONFIG_BGSCAN_SIMPLE
+CONFIG_WNM=y
+endif
+
 ifdef CONFIG_WNM
 CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.o
@@ -477,6 +484,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 +1012,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 +1072,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 +1089,7 @@
 ifdef CONFIG_MBO
 OBJS += mbo.o
 CFLAGS += -DCONFIG_MBO
+NEED_GAS=y
 endif
 
 ifdef NEED_RSN_AUTHENTICATOR
@@ -1076,17 +1108,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
diff --git a/wpa_supplicant/README-NAN-USD b/wpa_supplicant/README-NAN-USD
new file mode 100644
index 0000000..5dfe6ee
--- /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=<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/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..000914a 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -1848,6 +1848,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(&params, 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..22b694c 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;
@@ -406,8 +407,8 @@
 		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)
@@ -417,7 +418,7 @@
 		if (!(wpa_s->valid_links & BIT(i)))
 			continue;
 
-		if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
+		if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
 			return 1;
 	}
 
@@ -717,7 +718,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 +1103,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 +1128,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 +1157,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,22 +1224,6 @@
 
 
 /**
- * wpa_bss_get_ie_nth - 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
- * entry.
- */
-const u8 * wpa_bss_get_ie_nth(const struct wpa_bss *bss, u8 ie, int nth)
-{
-	return get_ie_nth(wpa_bss_ie_ptr(bss), bss->ie_len, ie, nth);
-}
-
-
-/**
  * wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
  * @bss: BSS table entry
  * @ext: Information element extension identifier (WLAN_EID_EXT_*)
@@ -1496,29 +1482,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,12 +1511,15 @@
 	pos += sizeof(*ap_info);
 
 	for (i = 0; i < count; i++) {
+		u8 bss_params;
+
 		if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
 			return;
 
 		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;
@@ -1557,23 +1529,30 @@
 				   "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);
+				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);
+				os_memcpy(l->bssid, pos + 1, ETH_ALEN);
 				l->freq = neigh_bss->freq;
+				l->disabled = mld_params[2] &
+					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
 				bss->n_mld_links++;
-			} else {
-				*missing |= BIT(link_id);
 			}
 		}
 
@@ -1590,6 +1569,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 +1581,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 +1616,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 +1624,32 @@
 
 	ml_ie_len = wpabuf_len(mlbuf);
 
+	if (ssid) {
+		struct wpa_ie_data ie;
+
+		if (!elems.rsn_ie ||
+		    wpa_parse_wpa_ie(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
+				     &ie)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RSN element");
+			goto out;
+		}
+
+		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) ||
+		    wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"MLD: No management frame protection");
+			goto out;
+		}
+
+		ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
+				 WPA_KEY_MGMT_PSK_SHA256);
+		if (!(ie.key_mgmt & ssid->key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"MLD: No valid key management");
+			goto out;
+		}
+	}
+
 	/*
 	 * for ext ID + 2 control + common info len + MLD address +
 	 * link info
@@ -1720,7 +1729,7 @@
 
 			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;
@@ -1739,6 +1748,9 @@
 	if (missing_links)
 		*missing_links = missing;
 
+	if (ap_mld_id)
+		*ap_mld_id = mbssid_idx;
+
 	ret = 0;
 out:
 	wpabuf_free(mlbuf);
@@ -1769,7 +1781,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;
 
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 042b5d2..c06c20a 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -133,6 +133,9 @@
 		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 +174,6 @@
 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_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,11 +215,12 @@
 			   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);
 
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..8c0db68 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2368,7 +2368,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;
@@ -4681,6 +4681,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;
 }
 
@@ -5315,6 +5319,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 */
@@ -5531,6 +5552,15 @@
 	{ 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..56d5c61 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
@@ -1806,6 +1816,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..71a64b1 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1618,6 +1618,18 @@
 	if (config->wowlan_disconnect_on_deinit)
 		fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
 			config->wowlan_disconnect_on_deinit);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (config->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/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a68802e..500f4d1 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);
@@ -932,8 +944,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 +1274,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 +1325,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(
@@ -3618,7 +3636,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 +3701,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 +4877,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 +4981,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 +4993,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 +5005,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 +5017,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 +5271,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 +5279,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;
 }
 
@@ -5386,13 +5418,13 @@
 		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 +5723,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 +6319,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 +6412,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;
 
@@ -7062,7 +7066,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 +7142,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 +7221,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 +7841,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 +8008,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
@@ -8867,6 +8871,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 +8888,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 +8901,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 +8922,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 +8951,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 +8963,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 +10153,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 +10180,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 +10194,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 +10559,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 +10704,8 @@
 	return ret;
 }
 
+#endif /* CONFIG_NO_RRM */
+
 
 static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
 {
@@ -11067,6 +11059,7 @@
 }
 
 
+#ifndef CONFIG_NO_ROBUST_AV
 static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s,
 					  const char *cmd)
 {
@@ -11135,6 +11128,7 @@
 
 	return wpas_send_mscs_req(wpa_s);
 }
+#endif /* CONFIG_NO_ROBUST_AV */
 
 
 #ifdef CONFIG_PASN
@@ -11236,9 +11230,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 +11975,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)
@@ -12121,6 +12163,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(&params, 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, &params);
+	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(&params, 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,
+					      &params);
+	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 +13155,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 +13164,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 +13251,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 +13310,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 +13487,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 +13518,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 +13537,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_new.c b/wpa_supplicant/dbus/dbus_new.c
index 8fc29b3..00b38ed 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -4304,6 +4304,14 @@
 	  }
 	},
 #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 } }
 };
 
@@ -5142,3 +5150,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..b653f10 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -279,6 +279,8 @@
 					    int bh, int bss_load,
 					    int conn_capab);
 void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
+					  const char *url);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -650,6 +652,12 @@
 {
 }
 
+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_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index a178d87..16b2caa 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;
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..801e698 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -1800,7 +1800,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 +1935,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 +2020,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 +2075,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 +2179,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 +2196,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 +2612,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 +2656,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 +2694,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 +3858,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 +4149,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 +4163,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,
@@ -5692,6 +5703,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..9a4c235 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1155,8 +1155,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(&params, 0, sizeof(params));
 	params.action = action;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index f386119..bc45579 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"
 
@@ -383,8 +384,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;
 
@@ -759,15 +765,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++;
 
@@ -1136,7 +1160,7 @@
 {
 	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)
@@ -1285,7 +1309,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 +1321,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 +1490,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");
@@ -1673,6 +1697,14 @@
 		return NULL;
 	}
 
+#ifdef CONFIG_WNM
+	if (wnm_is_bss_excluded(wpa_s, bss)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID excluded");
+		return NULL;
+	}
+#endif /* CONFIG_WNM */
+
 	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))
@@ -1857,14 +1889,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 +1929,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 +2015,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)) {
@@ -2070,11 +2132,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;
@@ -2100,8 +2173,7 @@
 		selected->snr, selected->est_throughput);
 
 	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 +2293,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 +2330,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 +2344,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 */
@@ -2424,9 +2504,11 @@
 	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;
@@ -3586,10 +3668,12 @@
 			     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;
@@ -3644,8 +3728,10 @@
 
 	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;
 }
@@ -3717,6 +3803,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;
@@ -3740,18 +4081,17 @@
 			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) {
+			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;
 	}
 
@@ -3939,7 +4279,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 +4478,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 +4524,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 +4533,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 +4626,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 +4659,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 +4710,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 +4723,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 +5077,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 +5304,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 +5339,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)
@@ -5055,10 +5406,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 +5475,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 +5483,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 +5506,7 @@
 							 rssi);
 		return;
 	}
+#endif /* CONFIG_NO_RRM */
 
 #ifdef CONFIG_FST
 	if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
@@ -5160,6 +5515,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 +5538,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 +5559,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 +5763,8 @@
 				    union wpa_event_data *data)
 {
 	const u8 *bssid = data->assoc_reject.bssid;
+	struct ieee802_11_elems elems;
+	const u8 *link_bssids[MAX_NUM_MLD_LINKS];
 #ifdef CONFIG_MBO
 	struct wpa_bss *reject_bss;
 #endif /* CONFIG_MBO */
@@ -5450,7 +5820,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 +5855,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 +5892,34 @@
 	}
 #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) {
+		struct ml_sta_link_info ml_info[MAX_NUM_MLD_LINKS];
+		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 +5956,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 +5968,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));
@@ -5662,7 +6058,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);
@@ -5947,8 +6343,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 +6667,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 +6684,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 +6726,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 +6860,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 +7023,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..c68167f 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;
@@ -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..554268a 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;
 }
@@ -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..498cc98 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -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)
@@ -3169,7 +3169,7 @@
 	 */
 	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;
 		}
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, &params);
 	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..6f162f2 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -1103,15 +1103,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
 
@@ -1437,3 +1428,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..d4656ad 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -182,8 +182,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 +230,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/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index c150863..3c121c8 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1187,7 +1187,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 +1293,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 +1595,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 +2937,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 +3232,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 +3270,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 +3279,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 +3481,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 +4184,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 +4223,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 +4434,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 +4450,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 +4605,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 +4621,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 +5472,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 +5513,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),
@@ -7504,9 +7501,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 +7514,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 +7529,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 +7575,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 +7620,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 +8740,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 +8888,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 +9029,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 +9885,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/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index edecfde..2e65cf0 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -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,13 +468,13 @@
 	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;
@@ -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;
@@ -921,7 +925,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..7ce854b 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -772,6 +772,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 +829,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 +879,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 +936,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 +1047,7 @@
 					     struct beacon_rep_data *data,
 					     u8 sid, u8 slen, const u8 *subelem)
 {
+	struct bitfield *eids;
 	u8 report_info, i;
 
 	switch (sid) {
@@ -1096,6 +1101,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 +1117,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 +1512,29 @@
 }
 
 
+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 (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		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 +1556,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 +1571,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 +1641,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..6e6f05d 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;
 	}
@@ -2253,7 +2255,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 +2330,6 @@
 	}
 
 	return max_tx_power;
-#undef MIN
 }
 
 
@@ -2371,7 +2371,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 +2378,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 +2422,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 +2474,6 @@
 	if (snr_b_full == snr_a_full)
 		return wb->qual - wa->qual;
 	return snr_b_full - snr_a_full;
-#undef MIN
 }
 
 
@@ -2573,8 +2598,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;
 	}
 
@@ -2878,6 +2902,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 +2958,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 +2976,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 +3015,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 +3074,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 +3090,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 +3105,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;
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index f1739fa..8402e74 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,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index df2c68f..b8f7c65 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"
@@ -165,7 +166,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;
@@ -242,7 +243,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 +379,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,16 +388,98 @@
 }
 
 
-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)
+{
+	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 (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(wpa_s->valid_links & BIT(i)))
+				continue;
+
+			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 (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		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 */
+	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,
@@ -628,7 +497,7 @@
 			goto out;
 		/* 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);
@@ -637,16 +506,39 @@
 
 	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;
+	return 0;
 out:
 	wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state");
 	wpas_reset_mlo_info(wpa_s);
+	return -1;
+}
+
+
+static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss)
+{
+	int i;
+
+	wpa_s->valid_links = 0;
+
+	for (i = 0; i < bss->n_mld_links; i++) {
+		u8 link_id = bss->mld_links[i].link_id;
+		const u8 *bssid = bss->mld_links[i].bssid;
+
+		if (i == 0)
+			wpa_s->mlo_assoc_link_id = link_id;
+		wpa_s->valid_links |= BIT(link_id);
+		os_memcpy(wpa_s->links[link_id].bssid, bssid, ETH_ALEN);
+		wpa_s->links[link_id].freq = bss->mld_links[i].freq;
+		wpa_s->links[link_id].bss = wpa_bss_get_bssid(wpa_s, bssid);
+		wpa_s->links[link_id].disabled = bss->mld_links[i].disabled;
+	}
 }
 
 
@@ -681,11 +573,33 @@
 		return;
 	}
 
+	os_memset(&params, 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->n_mld_links) {
+		wpa_printf(MSG_DEBUG, "MLD: In authentication");
+		wpas_sme_set_mlo_links(wpa_s, bss);
+
+#ifdef CONFIG_TESTING_OPTIONS
+		bss = wpas_ml_connect_pref(wpa_s, bss);
+
+		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(&params, 0, sizeof(params));
 	wpa_s->reassociate = 0;
 
 	params.freq = bss->freq;
@@ -694,14 +608,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;
@@ -970,10 +876,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);
@@ -1122,7 +1030,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);
@@ -1258,7 +1166,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 +1189,7 @@
 	if (wpa_drv_authenticate(wpa_s, &params) < 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);
@@ -1719,8 +1627,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));
@@ -1998,7 +1905,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 +1982,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,7 +2010,8 @@
 				   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);
 
 		}
@@ -2147,7 +2055,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 +2105,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 +2130,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 +2145,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 +2154,19 @@
 #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_mark_disassoc(wpa_s);
+		return;
+	}
 
 	sme_associate(wpa_s, ssid->mode, data->auth.peer,
 		      data->auth.auth_type);
@@ -2287,6 +2209,9 @@
 
 	os_memset(&params, 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 +2378,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,6 +2412,7 @@
 		wpabuf_free(mscs_ie);
 	}
 mscs_fail:
+#endif /* CONFIG_NO_ROBUST_AV */
 
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
@@ -2682,19 +2609,48 @@
 				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, &params) < 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 +2690,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 +2718,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 +2839,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 +2884,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 +2892,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 +2901,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 +2930,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 +2940,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 +2948,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 +2984,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 +3418,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 +3523,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..775e75b 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -686,9 +686,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;
 			}
@@ -1091,7 +1090,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 && !wpa_s->wnm_link_removal)
 		wnm_add_cand_list(wpa_s, &buf);
 
 #ifdef CONFIG_MBO
@@ -1194,8 +1193,8 @@
 	}
 
 	if (!wpa_s->current_bss ||
-	    os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
-		      ETH_ALEN) != 0) {
+	    !ether_addr_equal(wpa_s->wnm_cand_from_bss,
+			      wpa_s->current_bss->bssid)) {
 		wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
 		return 0;
 	}
@@ -1388,7 +1387,7 @@
 			const u8 *ssid_ie;
 
 			res = scan_res->res[j];
-			if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
+			if (!ether_addr_equal(nei->bssid, res->bssid) ||
 			    res->age > WNM_SCAN_RESULT_AGE * 1000)
 				continue;
 			bss = wpa_s->current_bss;
@@ -1437,6 +1436,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;
@@ -1461,6 +1461,7 @@
 	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;
 
@@ -1532,7 +1533,26 @@
 		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.
+	 */
+	if (disassoc_imminent &&
+	    (wpa_s->valid_links & (wpa_s->valid_links - 1)) != 0 &&
+	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
+	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
+		wpa_printf(MSG_INFO,
+			   "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
+		disassoc_imminent = false;
+		wpa_s->wnm_link_removal = true;
+	}
+
+	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 &&
@@ -1572,8 +1592,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++;
@@ -1666,7 +1685,9 @@
 		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");
@@ -1890,7 +1911,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 +1957,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 +1989,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;
@@ -2048,8 +2075,43 @@
 
 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)
+{
+	unsigned int i;
+
+	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->current_bss &&
+	    ether_addr_equal(wpa_s->current_bss->bssid, bss->bssid)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WNM: Disassociation imminent: current BSS");
+		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 (ether_addr_equal(wpa_s->links[i].bssid, bss->bssid)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"WNM: MLD: Disassociation imminent: current link");
+			return true;
+		}
+	}
+
+	return false;
 }
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index e4957e4..2a473db 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -70,6 +70,7 @@
 			       const struct wpabuf *elems);
 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
 			      struct wpabuf *elems);
+bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 
 
 #ifdef CONFIG_WNM
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 65078ed..b1334e2 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -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=] "
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index d4401ff..a851024 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"
@@ -584,7 +585,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;
@@ -701,7 +704,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 +722,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 +753,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);
@@ -1068,8 +1081,10 @@
 	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 		wpa_supplicant_start_autoscan(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);
@@ -2009,7 +2024,7 @@
 		(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 (!skip_default_rsne) {
@@ -2111,7 +2126,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 +2144,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 +2175,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 +2189,7 @@
 		}
 		if (scs)
 			*pos |= 0x40; /* Bit 54 - SCS */
+#endif /* CONFIG_NO_ROBUST_AV */
 		break;
 	case 7: /* Bits 56-63 */
 		break;
@@ -2185,6 +2206,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 +2220,7 @@
 		}
 		if (mscs)
 			*pos |= 0x20; /* Bit 85 - Mirrored SCS */
+#endif /* CONFIG_NO_ROBUST_AV */
 		break;
 	}
 }
@@ -2315,8 +2338,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) &&
@@ -2495,7 +2517,12 @@
 
 	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;
+#endif /* CONFIG_WNM */
 	wpa_s->reassoc_same_bss = 0;
 	wpa_s->reassoc_same_ess = 0;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -2506,7 +2533,9 @@
 		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;
 		} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {
 			os_get_reltime(&wpa_s->roam_start);
@@ -3074,7 +3103,7 @@
 	struct hostapd_hw_modes *mode = NULL;
 	int i, obss_scan = 1;
 	u8 channel;
-	bool is_6ghz;
+	bool is_6ghz, is_24ghz;
 
 	freq->freq = ssid->frequency;
 
@@ -3103,6 +3132,9 @@
 	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 +3146,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 +3288,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;
@@ -3521,12 +3555,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
@@ -3753,6 +3789,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,6 +3824,7 @@
 		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);
@@ -4071,7 +4109,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, &params, NULL);
 	if (!wpa_ie) {
@@ -4466,7 +4506,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);
@@ -4562,8 +4603,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);
 }
 
@@ -5343,14 +5386,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 +5403,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 +5537,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 +5567,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 +5724,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 +5741,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 +5916,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 +6433,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 +6449,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 +6460,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 +7249,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,
@@ -7251,7 +7300,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 +7390,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 +7464,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 +8231,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 +8303,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 +8339,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.
@@ -8692,9 +8761,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 +8895,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;
 	}
 
@@ -9130,7 +9206,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 +9289,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 +9387,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",
@@ -9357,7 +9432,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,7 +9454,7 @@
 }
 
 
-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;
 
@@ -9390,7 +9465,7 @@
 		if (!(wpa_s->valid_links & BIT(i)))
 			continue;
 
-		if (os_memcmp(wpa_s->links[i].bssid, addr, ETH_ALEN) == 0)
+		if (ether_addr_equal(wpa_s->links[i].bssid, addr))
 			return true;
 	}
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index f6e4f83..d689441 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -982,9 +982,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>:
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 0890110..3a5f61c 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;
@@ -1046,6 +1055,7 @@
 		bool ext_ml_auth;
 		int *sae_rejected_groups;
 #endif /* CONFIG_SAE */
+		u16 assoc_auth_type;
 	} sme;
 #endif /* CONFIG_SME */
 
@@ -1207,6 +1217,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,6 +1326,7 @@
 	u8 wnm_reply;
 	u8 wnm_num_neighbor_report;
 	u8 wnm_mode;
+	bool wnm_link_removal;
 	u16 wnm_dissoc_timer;
 	u8 wnm_bss_termination_duration[12];
 	struct neighbor_report *wnm_neighbor_report_elements;
@@ -1381,6 +1393,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;
@@ -1567,8 +1580,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 +1609,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 +1626,12 @@
 	 * 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 */
 };
 
 
@@ -1715,7 +1738,8 @@
 			     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);
@@ -2015,5 +2039,6 @@
 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);
 
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 9804b91..111680c 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 &&
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..81e11e7 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -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);
@@ -1812,7 +1811,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 +1850,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 +2942,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;
 	}