[wpa_supplicant] cumilative patch from commit 4b755c967

Bug: 329004037
Test: Connect to open, WPA2, WPA3 and OWE
Test: Establish P2P connection
Test: Basic SoftAp tests
Test: Ran above tests on Pixel6
Test: Regression test (b/329003970)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source

4b755c967 build: De-duplicate _DIRS before calling mkdir
9a4423645 hostapd: Only attempt to set QoS map if supported by the driver
dec6fccf1 Support qos_map_set without CONFIG_INTERWORKING
8634e7343 mesh: Allow processing authentication frames in blocked state
a210fdb1c nl80211: Rewrite neigh code to not depend on libnl3-route
3ef057901 ndisc_snoop: Call dl_list_del() before freeing IPv6 addresses
e1cd3fe3c Cancel channel_list_update_timeout() in hostapd_cleanup_iface_partial()
47d7f3169 nl80211: Update drv->ifindex on removing the first BSS
1be706e86 hostapd: Add RRM link measurement request support
92fdb49b2 AP MLD: Set DTIM information properly in per-STA profile
36bd75dfd hostapd: Fix channel switch to a DFS channel
f4b84ecaf AP MLD: Track radar detection in offloaded DFS case
aaf879ef2 AP MLD: Do not update other links' RNR element if not enabled yet
32261721e nl80211: AP MLD: Parse link ID to determine the BSS for radar event
216cfd708 AP MLD: Fix missing check for legacy client case
d5e6f7998 AP MLD: Request Handle OBSS scan for a specific link
c9ad16870 AP MLD: Allow scan processing link to match the request
9b682e72d AP MLD: Find the link that is waiting for scan events
147f83692 PASN: Add set and get API for PASN data context
ab37a5731 Replace PMKSA cache inline stubs with wrapper function stubs
ba55088a7 Replace PTKSA cache inline stubs with wrapper function stubs
1f230a497 MBSSID: Include Extended Capabilities element in non-TX BSSID profile
37c00c3c5 AP MLD: Provide link addresses for non-AP MLDs in control interface
b818a1be1 Add a QCA vendor attribute to set avoid frequencies per netdev
9ac0e785c Revert "nl80211: Skip interface down/up when setting MAC address"
94506e8ed Use the latest updated BSS entry for sending ANQP requests
a9bc6e89d OpenSSL: Fix a memory leak in CMAC
4bc61b657 AP MLD: Remove restriction of having to disable the first link BSS last
a6d92da9a AP MLD: Support removal of link station from AP
1f88b3daf nl80211: Add callback function for removing link STAs
19e50f862 Export hostapd_sta_is_link_sta()
df34c2ced AP MLD: De-initialize/disable link BSS properly
63982fd09 nl80211: Print the MLD capabilities in debug
9fdbaf2ed AP MLD: Fix advertisement of MLD capabilities
7a0501d20 AP MLD: Refresh beacons for other links when one gets disabled/enabled
d2b62b3fe AP MLD: Support link removal before removing interface
55c30e8ab nl80211: Remove AP MLD links while removing the interface
a576180cd nl80211: Use per-BSS command for remove link
b162886fd nl80211: Re-factor nl80211_remove_links() function
b810426ea nl80211: Remove redundant put_freq call in set_ap() for AP MLD
420065733 nl80211: Fix set_ap() to add frequency without CONFIG_IEEE80211AX
f2f0dd354 nl80211: Cache hostapd_data context in per link BSS struct for AP MLD
60e1dca1e AP MLD: Clean up MLD when not required any further
fac34688a AP MLD: Assign link ID during BSS creation
b19aa9c42 AP MLD: Use MLD struct for MLD level information
2f0e5303e AP MLD: Add a separate MLD level structure
259b43a31 hostapd: MLO: Avoid use of mld_id as user configuration
69d53b8b6 nl80211: Fix potential NULL pointer dereference in set_ap()
666e954ca Remove unused wpa_drv_set_ap()
9be122d2e nl80211: Fix AP MLD frequency update on channel switch
9144f876a nl80211: Fix sending NL80211_CMD_DEL_BEACON command to wrong interface
ec4b755b0 wpa_cli: Don't select interface when using global socket
c24453dd9 Add a vendor attribute per MLO link ratemask bitmap configuration
77f39ed23 Document vendor command ratemask bitmap for EHT case
58017de69 Add QCA vendor sub-command and attribute for spectral scan completion
8f9da72d2 Add QCA vendor attribute indicating the spectral transport mode
5b4a78b1f Optimize internal BSS table updates based on a specific BSSID
8d0bd7f9c Update BSS entry on roaming only for actual BSS frequency change
024d4bca1 Multi-AP: WPS support for different Multi-AP profiles
69d086298 Multi-AP: Add support for VLAN related information
210c2b4bd Multi-AP: Add hostapd config option to disallow certain profiles
9a1512532 Multi-AP: Reject non-Multi-AP STA association on backhaul-only BSS
420afbdbd Multi-AP: Allow supported profile to be configured
c3e528653 Multi-AP: Parse Profile subelement
003411242 Multi-AP: Generation of Multi-AP Profile subelement
364cb7c94 Multi-AP: Parse the Multi-AP element using a shared helper function
0e2ca2e4e Multi-AP: Use proper length for remaining buffer for the element
61e46f860 Multi-AP: Move IE parameters into a struct for extensibility
2ae1e6f18 DBus: Add ANQP fields to BSS properties
2ea04435e DBus: Signal ANQP query done
d71c83851 DBus: Add a method to get ANQP fields
5eb409c4b DBus: Add dict helper for uint16 arrays
a438e5293 OpenSSL: Fix a memory leak on hpke_labeled_expand() error path
b35b1036f OpenSSL: Fix a memory leak on openssl_evp_pkey_ec_prime_len() error path
35df7ee09 DPP: Emit a DPP PB_STATUS event when push button starts
69dd408fb EHT: More accurate no-second-channel-offset checks when puncturing
131ee5926 EHT: Support punct_bitmap overriding in HE element generation
c96c3adc3 Move punct_update_legacy_bw() into src/common
9f43c1e26 Provide punct_bitmap to hostapd_set_freq_params()
47dad1ed1 EHT: Move puncturing bitmap determination into a helper function
010d8d10e EHT: Use eht_oper_puncturing_override when constructing VHT elements
c00abc69f Handle 6 GHz channels in Supported Operating Classes with freq_list
9e90486bc 2-octet operating classes in Support Operating Classes element
5dabc1018 Extend support for the 6 GHz operating class 137 (320 MHz)
e74d95e0a nl80211: Process 6 GHz regulatory rules to accurate channel flags
59951ebf0 Use a helper function to free neighbor DB entries
96f0af07e Clear all neighbor entry items explicitly
6f285fbaf Update own report in nr_db if SSID is changed
b653420a2 AP MLD: Set link address only when non-AP MLD is not added to driver
16abdac80 Fix INTERFACES command buffer size to allow more data
b483ceafc hostapd: Dump VHT/HE/EHT full capabilities in STA command output
040ba112a Use os_snprintf_error() more consistently in STA output generation
31bbc9391 Do not change out-of-range configuration parameters
fd24ed949 Fix valid range for disable_ht40
b1a880f38 Enforce valid range check for SET mbo_cell_capa and oce
e5b7e5b90 wpa_supplicant: Fix ignoring boundary 0 in config parser
10122e951 P2P: Fix pri/sec channel switch skipping for GO
e508c070c WNM: Keep BTM information until connection completes
17a2aa822 WNM: Follow BTM procedure if the last link is dropped
770f3cb30 WNM: Remove dialog_token parameter
c7cafef8a WNM: Set wnm_reply to 0 when sending it
261f7f9e5 WNM: Do not store coloc_intf_elems
033adbf83 WNM: Drop explicit wpa_is_bss_tmp_disallowed() check
8b51310f4 WNM: Drop explicit SSID check
939cd294b WNM: Drop check for current_ssid
436f07d02 WNM: Define a stub wnm_is_bss_excluded if WNM is disabled
e164943f4 WNM: Wait for BTM response TX status before roaming
40ef706e5 WNM: Don't scan frequencies of neighbors that should be ignored
20ed289a7 WNM: Clean up old scan data processing
4750a4f62 DPP: Wait for TX wait expiration on moving to neg_freq for Auth Resp
963dbad7d nl80211: Indicate EVENT_TX_WAIT_EXPIRE on match-saved
451d29952 DPP: Wait for ROC cancelled event on Auth Resp TX on another channel
45fffac0f BSS: Switch struct wpa_bss to use valid_links bitmask
e90f6678f nl80211: Remnove unused struct i802_link ctx
0d4288a00 nl80211: Use valid_links bitmask for bss->links array
9ed51186e Use a single define MAX_NUM_MLD_LINKS for the maximum number of links
11f26fed6 Use for_each_link() where possible
dbdf7ef67 Use for_each_link() in most cases
c9f8fe066 common: Introduce for_each_link() macro
6cb421c1f nl80211: Fix link indexing in nl80211_connect_common()
408a399aa nl80211: Explicitly differentiate between 5 GHz and 6 GHz modes
dbcf9ff15 P2P: Notify the IP address of the connected P2P Client
b18d95759 P2P: Disable pri/sec channel switch for GO with forced frequency
0e4aa28da hostapd_cli: Indentation cleanup
a01972a06 Add "stop_ap" command for hostapd_cli
8cdb0d3f2 AP MLD: Stop AP per link
d084ef36b AP MLD: Clean up disassoc handling for non-AP MLD link validity check
7ceafb6e9 AP MLD: Handle disassociation notification with SME offload to driver
95a825bc4 RADIUS: Preliminary support RADIUS/TLS as an alternative to RADIUS/UDP
87f33c26b RADIUS: Simplify IPv4/IPv6 socket handling in client
971b78147 RADIUS: Simplify radius_change_server() parameters
3386e1327 l2_packet_freebsd: Fix macOS build
86c242171 TDLS: Defer the start request until the discovery response RX for MLO
352ad5f1a Apply CHAN_SWITCH in all BSS for MBSSID case
87120a5b6 Add QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER command
fe82a61ef Add QCA vendor attribute for BTM support configuration

Change-Id: I11d4eeb2e4ab44630e3359a2886361b3fccd7ead
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index e6ba165..347ff39 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -331,6 +331,7 @@
         "src/common/sae.c",
         "src/common/sae_pk.c",
         "src/common/wpa_common.c",
+        "src/common/ptksa_cache.c",
         "src/crypto/aes-ctr.c",
         "src/crypto/aes-encblock.c",
         "src/crypto/aes-siv.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index bdb19f9..952d1be 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -139,6 +139,8 @@
 OBJS += src/utils/bitfield.c
 OBJS += src/utils/ip_addr.c
 OBJS += src/utils/crc32.c
+OBJS += src/common/ptksa_cache.c
+OBJS += src/rsn_supp/pmksa_cache.c
 OBJS += twt.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
@@ -379,7 +381,6 @@
 ifndef CONFIG_NO_WPA
 OBJS += src/rsn_supp/wpa.c
 OBJS += src/rsn_supp/preauth.c
-OBJS += src/rsn_supp/pmksa_cache.c
 OBJS += src/rsn_supp/wpa_ie.c
 OBJS += src/common/wpa_common.c
 NEED_AES=y
@@ -432,8 +433,8 @@
 NEED_HMAC_SHA384_KDF=y
 NEED_SHA256=y
 NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
 OBJS += src/pasn/pasn_initiator.c
+OBJS += src/pasn/pasn_common.c
 OBJS += pasn_supplicant.c
 endif
 
@@ -1846,8 +1847,9 @@
 
 PASNOBJS += src/common/ptksa_cache.c
 
-ifndef CONFIG_NO_WPA
 PASNOBJS += src/rsn_supp/pmksa_cache.c
+
+ifndef CONFIG_NO_WPA
 PASNOBJS += src/rsn_supp/wpa_ie.c
 endif
 
@@ -1933,6 +1935,7 @@
 
 PASNOBJS += src/pasn/pasn_initiator.c
 PASNOBJS += src/pasn/pasn_responder.c
+PASNOBJS += src/pasn/pasn_common.c
 
 ########################
 
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 93fc338..8bec178 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -112,6 +112,8 @@
 OBJS += ../src/utils/bitfield.o
 OBJS += ../src/utils/ip_addr.o
 OBJS += ../src/utils/crc32.o
+OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
 OBJS += twt.o
 OBJS_p = wpa_passphrase.o
 OBJS_p += ../src/utils/common.o
@@ -374,7 +376,6 @@
 ifndef CONFIG_NO_WPA
 OBJS += ../src/rsn_supp/wpa.o
 OBJS += ../src/rsn_supp/preauth.o
-OBJS += ../src/rsn_supp/pmksa_cache.o
 OBJS += ../src/rsn_supp/wpa_ie.o
 OBJS += ../src/common/wpa_common.o
 NEED_AES=y
@@ -435,8 +436,8 @@
 NEED_HMAC_SHA384_KDF=y
 NEED_SHA256=y
 NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
 OBJS += ../src/pasn/pasn_initiator.o
+OBJS += ../src/pasn/pasn_common.o
 OBJS += pasn_supplicant.o
 endif
 
@@ -1187,6 +1188,10 @@
 CFLAGS += -DCONFIG_TLSV12
 endif
 
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+endif
+
 ifeq ($(CONFIG_TLS), wolfssl)
 ifdef TLS_FUNCS
 CFLAGS += -DWOLFSSL_DER_LOAD
@@ -1925,6 +1930,9 @@
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
 OBJS_t += ../src/radius/radius_client.o
 OBJS_t += ../src/radius/radius.o
+ifdef CONFIG_RADIUS_TLS
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
 OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
 
 OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
@@ -2219,9 +2227,9 @@
 endif
 
 LIBPASNSO += ../src/common/ptksa_cache.c
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
 
 ifndef CONFIG_NO_WPA
-LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
 LIBPASNSO += ../src/rsn_supp/wpa_ie.c
 endif
 
@@ -2316,6 +2324,7 @@
 
 LIBPASNSO += ../src/pasn/pasn_initiator.c
 LIBPASNSO += ../src/pasn/pasn_responder.c
+LIBPASNSO += ../src/pasn/pasn_common.c
 
 libpasn.so: $(LIBPASNSO)
 	@$(E) "  CC  $@ ($^)"
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 1589dd2..ae1943f 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -413,7 +413,7 @@
 		// Request the current scan results from the driver and update
 		// the local BSS list wpa_s->bss. This is to avoid a full scan
 		// while processing the connect request on newly created interface.
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
 	}
 	// The supplicant core creates a corresponding aidl object via
 	// AidlManager when |wpa_supplicant_add_iface| is called.
@@ -472,7 +472,7 @@
 		// Request the current scan results from the driver and update
 		// the local BSS list wpa_s->bss. This is to avoid a full scan
 		// while processing the connect request on newly created interface.
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
 	}
 	// The supplicant core creates a corresponding aidl object via
 	// AidlManager when |wpa_supplicant_add_iface| is called.
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 000914a..69a0e5e 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -440,9 +440,14 @@
 		}
 	}
 
-	if (wpa_s->p2p_go_no_pri_sec_switch) {
+#ifdef CONFIG_P2P
+	if (ssid->p2p_group && wpa_s->p2p_go_no_pri_sec_switch) {
 		conf->no_pri_sec_switch = 1;
-	} else if (conf->secondary_channel) {
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
+	if (conf->secondary_channel) {
 		struct wpa_supplicant *iface;
 
 		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
@@ -868,7 +873,8 @@
 
 
 static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
-				 int authorized, const u8 *p2p_dev_addr, const u8 *ip)
+				 int authorized, const u8 *p2p_dev_addr,
+				 const u8 *ip)
 {
 	wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr, ip);
 }
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 22b694c..2890353 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -400,6 +400,11 @@
 	if (bss == wpa_s->ml_connect_probe_bss)
 		return 1;
 
+#ifdef CONFIG_WNM
+	if (bss == wpa_s->wnm_target_bss)
+		return 1;
+#endif /* CONFIG_WNM */
+
 	if (wpa_s->current_bss &&
 	    (bss->ssid_len != wpa_s->current_bss->ssid_len ||
 	     os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
@@ -414,10 +419,7 @@
 	if (!wpa_s->valid_links)
 		return 0;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
 			return 1;
 	}
@@ -1513,9 +1515,6 @@
 	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;
 
@@ -1523,6 +1522,8 @@
 		mld_params = pos + mld_params_offset;
 
 		link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			return;
 
 		if (*mld_params != mbssid_idx) {
 			wpa_printf(MSG_DEBUG,
@@ -1546,13 +1547,12 @@
 					   wpa_s, neigh_bss->bssid)) {
 				struct mld_link *l;
 
-				l = &bss->mld_links[bss->n_mld_links];
-				l->link_id = link_id;
+				bss->valid_links |= BIT(link_id);
+				l = &bss->mld_links[link_id];
 				os_memcpy(l->bssid, pos + 1, ETH_ALEN);
 				l->freq = neigh_bss->freq;
 				l->disabled = mld_params[2] &
 					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
-				bss->n_mld_links++;
 			}
 		}
 
@@ -1678,15 +1678,15 @@
 		os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
 			  ETH_ALEN);
 
-	bss->n_mld_links = 0;
-	l = &bss->mld_links[bss->n_mld_links];
 	link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
-	l->link_id = link_id;
+
+	bss->mld_link_id = link_id;
+	seen = bss->valid_links = BIT(link_id);
+
+	l = &bss->mld_links[link_id];
 	os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
 	l->freq = bss->freq;
 
-	seen = BIT(link_id);
-	bss->n_mld_links++;
 
 	/*
 	 * The AP MLD ID in the RNR corresponds to the MBSSID index, see
@@ -1736,13 +1736,12 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
-		   bss->n_mld_links, missing);
+	wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
+		   bss->valid_links, missing);
 
-	for (i = 0; i < bss->n_mld_links; i++) {
+	for_each_link(bss->valid_links, i) {
 		wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
-			   bss->mld_links[i].link_id,
-			   MAC2STR(bss->mld_links[i].bssid));
+			   i, MAC2STR(bss->mld_links[i].bssid));
 	}
 
 	if (missing_links)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index c06c20a..cc04963 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -126,11 +126,12 @@
 	size_t beacon_ie_len;
 	/** MLD address of the AP */
 	u8 mld_addr[ETH_ALEN];
+	/** Link ID of this affiliated AP of the AP MLD */
+	u8 mld_link_id;
 
 	/** An array of MLD links */
-	u8 n_mld_links;
+	u16 valid_links;
 	struct mld_link {
-		u8 link_id;
 		u8 bssid[ETH_ALEN];
 		int freq;
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8c0db68..c949bab 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -197,9 +197,10 @@
 #endif /* NO_CONFIG_WRITE */
 
 
-static int wpa_config_parse_int(const struct parse_data *data,
-				struct wpa_ssid *ssid,
-				int line, const char *value)
+static int wpa_config_parse_int_impl(const struct parse_data *data,
+				     struct wpa_ssid *ssid,
+				     int line, const char *value,
+				     bool check_range)
 {
 	int val, *dst;
 	char *end;
@@ -212,31 +213,46 @@
 		return -1;
 	}
 
+	if (check_range && val < (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, val,
+			   (long) data->param3);
+		return -1;
+	}
+
+	if (check_range && val > (long) data->param4) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, val,
+			   (long) data->param4);
+		return -1;
+	}
+
 	if (*dst == val)
 		return 1;
+
 	*dst = val;
 	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
-	if (data->param3 && *dst < (long) data->param3) {
-		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
-			   "min_value=%ld)", line, data->name, *dst,
-			   (long) data->param3);
-		*dst = (long) data->param3;
-		return -1;
-	}
-
-	if (data->param4 && *dst > (long) data->param4) {
-		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
-			   "max_value=%ld)", line, data->name, *dst,
-			   (long) data->param4);
-		*dst = (long) data->param4;
-		return -1;
-	}
-
 	return 0;
 }
 
 
+static int wpa_config_parse_int(const struct parse_data *data,
+				struct wpa_ssid *ssid,
+				int line, const char *value)
+{
+	return wpa_config_parse_int_impl(data, ssid, line, value, false);
+}
+
+
+static int wpa_config_parse_int_range(const struct parse_data *data,
+				      struct wpa_ssid *ssid,
+				      int line, const char *value)
+{
+	return wpa_config_parse_int_impl(data, ssid, line, value, true);
+}
+
+
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_int(const struct parse_data *data,
 				   struct wpa_ssid *ssid)
@@ -2457,7 +2473,14 @@
 #define INTe(f, m) _INTe(f, m), NULL, NULL, 0
 
 /* INT_RANGE: Define an integer variable with allowed value range */
-#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+#ifdef NO_CONFIG_WRITE
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, OFFSET(f), \
+	(void *) 0, (void *) (min), (void *) (max), 0
+#else /* NO_CONFIG_WRITE */
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, \
+	wpa_config_write_int, OFFSET(f),	       \
+	(void *) 0, (void *) (min), (void *) (max), 0
+#endif /* NO_CONFIG_WRITE */
 
 /* FUNC: Define a configuration variable that uses a custom function for
  * parsing and writing the value. */
@@ -2652,7 +2675,7 @@
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HT_OVERRIDES
 	{ INT_RANGE(disable_ht, 0, 1) },
-	{ INT_RANGE(disable_ht40, -1, 1) },
+	{ INT_RANGE(disable_ht40, 0, 1) },
 	{ INT_RANGE(disable_sgi, 0, 1) },
 	{ INT_RANGE(disable_ldpc, 0, 1) },
 	{ INT_RANGE(ht40_intolerant, 0, 1) },
@@ -2725,6 +2748,8 @@
 	{ INT_RANGE(owe_ptk_workaround, 0, 1) },
 	{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
 	{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
+	{ INT_RANGE(multi_ap_profile, MULTI_AP_PROFILE_1,
+		    MULTI_AP_PROFILE_MAX) },
 	{ INT_RANGE(beacon_prot, 0, 1) },
 	{ INT_RANGE(transition_disable, 0, 255) },
 	{ INT_RANGE(sae_pk, 0, 2) },
@@ -4743,9 +4768,10 @@
 };
 
 
-static int wpa_global_config_parse_int(const struct global_parse_data *data,
-				       struct wpa_config *config, int line,
-				       const char *pos)
+static int
+wpa_global_config_parse_int_impl(const struct global_parse_data *data,
+				 struct wpa_config *config, int line,
+				 const char *pos, bool check_range)
 {
 	int val, *dst;
 	char *end;
@@ -4758,31 +4784,47 @@
 			   line, pos);
 		return -1;
 	}
+
+	if (check_range && val < (long) data->param2) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, val,
+			   (long) data->param2);
+		return -1;
+	}
+
+	if (check_range && val > (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, val,
+			   (long) data->param3);
+		return -1;
+	}
+
 	same = *dst == val;
 	*dst = val;
 
 	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
 
-	if (data->param2 && *dst < (long) data->param2) {
-		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
-			   "min_value=%ld)", line, data->name, *dst,
-			   (long) data->param2);
-		*dst = (long) data->param2;
-		return -1;
-	}
-
-	if (data->param3 && *dst > (long) data->param3) {
-		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
-			   "max_value=%ld)", line, data->name, *dst,
-			   (long) data->param3);
-		*dst = (long) data->param3;
-		return -1;
-	}
-
 	return same;
 }
 
 
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+				       struct wpa_config *config, int line,
+				       const char *pos)
+{
+	return wpa_global_config_parse_int_impl(data, config, line, pos, false);
+}
+
+
+static int
+wpa_global_config_parse_int_range(const struct global_parse_data *data,
+				  struct wpa_config *config, int line,
+				  const char *pos)
+{
+	return wpa_global_config_parse_int_impl(data, config, line, pos, true);
+}
+
+
 static int wpa_global_config_parse_str(const struct global_parse_data *data,
 				       struct wpa_config *config, int line,
 				       const char *pos)
@@ -5346,7 +5388,8 @@
 #define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
 #define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
 #define INT(f) _INT(f), NULL, NULL
-#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \
+	wpa_config_get_int, OFFSET(f), (void *) min, (void *) max
 #define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 71a64b1..414ed22 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -850,6 +850,7 @@
 	INT(owe_ptk_workaround);
 	INT(multi_ap_backhaul_sta);
 	INT(ft_eap_pmksa_caching);
+	INT(multi_ap_profile);
 	INT(beacon_prot);
 	INT(transition_disable);
 	INT(sae_pk);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 785acc3..2043a80 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1187,6 +1187,11 @@
 	int ft_eap_pmksa_caching;
 
 	/**
+	 * multi_ap_profile - Supported Multi-AP profile
+	 */
+	int multi_ap_profile;
+
+	/**
 	 * beacon_prot - Whether Beacon protection is enabled
 	 *
 	 * This depends on management frame protection (ieee80211w) being
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 500f4d1..d0fda4c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -902,9 +902,20 @@
 			wpa_config_process_global(wpa_s->conf, cmd, -1);
 		}
 	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
-		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+		int val = atoi(value);
+
+		if (val < MBO_CELL_CAPA_AVAILABLE ||
+		    val > MBO_CELL_CAPA_NOT_SUPPORTED)
+			return -1;
+
+		wpas_mbo_update_cell_capa(wpa_s, val);
 	} else if (os_strcasecmp(cmd, "oce") == 0) {
-		wpa_s->conf->oce = atoi(value);
+		int val = atoi(value);
+
+		if (val < 0 || val > 3)
+			return -1;
+
+		wpa_s->conf->oce = val;
 		if (wpa_s->conf->oce) {
 			if ((wpa_s->conf->oce & OCE_STA) &&
 			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
@@ -8850,6 +8861,8 @@
 
 	wpa_s->next_ssid = NULL;
 
+	wnm_btm_reset(wpa_s);
+
 #ifdef CONFIG_INTERWORKING
 #ifdef CONFIG_HS20
 	hs20_cancel_fetch_osu(wpa_s);
@@ -11995,10 +12008,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(mlo_si.valid_links & BIT(i)))
-			continue;
-
+	for_each_link(mlo_si.valid_links, i) {
 		ret = os_snprintf(pos, end - pos,
 				  "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
 				  "NOISE=%d\nFREQUENCY=%u\n",
@@ -12070,10 +12080,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
 				  "ap_link_addr=" MACSTR
 				  "\nsta_link_addr=" MACSTR "\n",
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index fdf7b12..27003eb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -706,6 +706,59 @@
 }
 
 
+#define UINT16_ARRAY_CHUNK_SIZE 18
+#define UINT16_ARRAY_ITEM_SIZE (sizeof(dbus_uint16_t))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_uint16_array(
+	DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+	dbus_uint32_t count = 0;
+	dbus_uint16_t *buffer, *nbuffer;
+
+	entry->uint16array_value = NULL;
+	entry->array_type = DBUS_TYPE_UINT16;
+
+	buffer = os_calloc(UINT16_ARRAY_CHUNK_SIZE, UINT16_ARRAY_ITEM_SIZE);
+	if (!buffer)
+		return FALSE;
+
+	entry->array_len = 0;
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT16) {
+		dbus_uint16_t value;
+
+		if ((count % UINT16_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+			nbuffer = os_realloc_array(
+				buffer, count + UINT16_ARRAY_CHUNK_SIZE,
+				UINT16_ARRAY_ITEM_SIZE);
+			if (!nbuffer) {
+				os_free(buffer);
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the uint16 array",
+					   __func__);
+				return FALSE;
+			}
+			buffer = nbuffer;
+		}
+
+		dbus_message_iter_get_basic(iter, &value);
+		buffer[count] = value;
+		entry->array_len = ++count;
+		dbus_message_iter_next(iter);
+	}
+	entry->uint16array_value = buffer;
+	wpa_hexdump_key(MSG_MSGDUMP, "dbus: uint16 array contents",
+			entry->bytearray_value, entry->array_len);
+
+	/* Zero-length arrays are valid. */
+	if (entry->array_len == 0) {
+		os_free(entry->uint16array_value);
+		entry->uint16array_value = NULL;
+	}
+
+	return TRUE;
+}
+
+
 #define STR_ARRAY_CHUNK_SIZE 8
 #define STR_ARRAY_ITEM_SIZE (sizeof(char *))
 
@@ -873,6 +926,10 @@
 		success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
 							      entry);
 		break;
+	case DBUS_TYPE_UINT16:
+		success = _wpa_dbus_dict_entry_get_uint16_array(&iter_array,
+								entry);
+		break;
 	case DBUS_TYPE_STRING:
 		success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
 								array_type,
@@ -1081,6 +1138,9 @@
 		case DBUS_TYPE_BYTE:
 			os_free(entry->bytearray_value);
 			break;
+		case DBUS_TYPE_UINT16:
+			os_free(entry->uint16array_value);
+			break;
 		case DBUS_TYPE_STRING:
 			if (!entry->strarray_value)
 				break;
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index cc9e26f..1d33689 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -139,6 +139,7 @@
 		dbus_uint64_t uint64_value;
 		double double_value;
 		char *bytearray_value;
+		dbus_uint16_t *uint16array_value;
 		char **strarray_value;
 		struct wpabuf **binarray_value;
 	};
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 00b38ed..8bd6a9a 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1023,6 +1023,40 @@
 	dbus_message_unref(msg);
 }
 
+
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char addr[WPAS_DBUS_OBJECT_PATH_MAX], *bssid;
+
+	os_snprintf(addr, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(dst));
+	bssid = addr;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "ANQPQueryDone");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bssid) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &result))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -2439,6 +2473,9 @@
 	case WPAS_DBUS_BSS_PROP_AGE:
 		prop = "Age";
 		break;
+	case WPAS_DBUS_BSS_PROP_ANQP:
+		prop = "ANQP";
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -2977,6 +3014,11 @@
 	  NULL,
 	  NULL
 	},
+	{"ANQP", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_anqp,
+	  NULL,
+	  NULL,
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -3716,6 +3758,13 @@
 		  END_ARGS
 	  }
 	},
+	{"ANQPGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_anqp_get,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  },
+	},
 #endif /* CONFIG_INTERWORKING */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
@@ -4303,6 +4352,13 @@
 		  END_ARGS
 	  }
 	},
+	{"ANQPQueryDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "addr", "s", ARG_OUT },
+		  { "result", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	{ "HS20TermsAndConditions", WPAS_DBUS_NEW_IFACE_INTERFACE,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index b653f10..952bb42 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -53,6 +53,7 @@
 	WPAS_DBUS_BSS_PROP_WPS,
 	WPAS_DBUS_BSS_PROP_IES,
 	WPAS_DBUS_BSS_PROP_AGE,
+	WPAS_DBUS_BSS_PROP_ANQP,
 };
 
 enum wpas_dbus_sta_prop {
@@ -279,6 +280,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_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result);
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url);
 
@@ -653,6 +656,12 @@
 }
 
 static inline
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result)
+{
+}
+
+static inline
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url)
 {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6ad49a1..3897d98 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1957,6 +1957,7 @@
 
 
 #ifdef CONFIG_INTERWORKING
+
 DBusMessage *
 wpas_dbus_handler_interworking_select(DBusMessage *message,
 				      struct wpa_supplicant *wpa_s)
@@ -1977,6 +1978,111 @@
 
 	return reply;
 }
+
+
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	int ret;
+	u8 dst_addr[ETH_ALEN];
+	bool is_addr_present = false;
+	unsigned int freq = 0;
+#define MAX_ANQP_INFO_ID 100 /* Max info ID count from CLI implementation */
+	u16 id[MAX_ANQP_INFO_ID];
+	size_t num_id = 0;
+	u32 subtypes = 0;
+	u32 mbo_subtypes = 0;
+	size_t i;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			return wpas_dbus_error_invalid_args(message, NULL);
+
+		if (os_strcmp(entry.key, "addr") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, dst_addr)) {
+				wpa_printf(MSG_DEBUG,
+					   "%s[dbus]: Invalid address '%s'",
+					   __func__, entry.str_value);
+				wpa_dbus_dict_entry_clear(&entry);
+				return wpas_dbus_error_invalid_args(
+					message, "invalid address");
+			}
+
+			is_addr_present = true;
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			for (i = 0; i < entry.array_len &&
+				     num_id < MAX_ANQP_INFO_ID; i++) {
+				id[num_id] = entry.uint16array_value[i];
+				num_id++;
+			}
+		} else if (os_strcmp(entry.key, "hs20_ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			for (i = 0; i < entry.array_len; i++) {
+				int num = entry.bytearray_value[i];
+
+				if (num <= 0 || num > 31) {
+					wpa_dbus_dict_entry_clear(&entry);
+					return wpas_dbus_error_invalid_args(
+						message,
+						"invalid HS20 ANQP id");
+				}
+				subtypes |= BIT(num);
+			}
+		} else if (os_strcmp(entry.key, "mbo_ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			for (i = 0; i < entry.array_len; i++) {
+				int num = entry.bytearray_value[i];
+
+				if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) {
+					wpa_dbus_dict_entry_clear(&entry);
+					return wpas_dbus_error_invalid_args(
+						message, "invalid MBO ANQP id");
+				}
+				mbo_subtypes |= BIT(num);
+			}
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			return wpas_dbus_error_invalid_args(
+				message, "unsupported parameter");
+		}
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (!is_addr_present) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: address not provided", __func__);
+		return wpas_dbus_error_invalid_args(message,
+						    "address not provided");
+	}
+
+	ret = anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
+			    mbo_subtypes);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s[dbus]: failed to send ANQP request",
+			   __func__);
+		return wpas_dbus_error_unknown_error(
+			message, "error sending ANQP request");
+	}
+
+	return NULL;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -5652,6 +5758,177 @@
 
 
 /**
+ * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ANQP" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_anqp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	DBusMessageIter iter_dict, variant_iter;
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *bss;
+	struct wpa_bss_anqp *anqp;
+	struct wpa_bss_anqp_elem *elem;
+
+	bss = get_bss_helper(args, error, __func__);
+	if (!bss)
+		return FALSE;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+	anqp = bss->anqp;
+	if (anqp) {
+#ifdef CONFIG_INTERWORKING
+		if (anqp->capability_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "CapabilityList",
+			    wpabuf_head(anqp->capability_list),
+			    wpabuf_len(anqp->capability_list)))
+			goto nomem;
+		if (anqp->venue_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "VenueName",
+			    wpabuf_head(anqp->venue_name),
+			    wpabuf_len(anqp->venue_name)))
+			goto nomem;
+		if (anqp->network_auth_type &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "NetworkAuthType",
+			    wpabuf_head(anqp->network_auth_type),
+			    wpabuf_len(anqp->network_auth_type)))
+			goto nomem;
+		if (anqp->roaming_consortium &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "RoamingConsortium",
+			    wpabuf_head(anqp->roaming_consortium),
+			    wpabuf_len(anqp->roaming_consortium)))
+			goto nomem;
+		if (anqp->ip_addr_type_availability &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "IPAddrTypeAvailability",
+			    wpabuf_head(anqp->ip_addr_type_availability),
+			    wpabuf_len(anqp->ip_addr_type_availability)))
+			goto nomem;
+		if (anqp->nai_realm &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "NAIRealm",
+			    wpabuf_head(anqp->nai_realm),
+			    wpabuf_len(anqp->nai_realm)))
+			goto nomem;
+		if (anqp->anqp_3gpp &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "3GPP",
+			    wpabuf_head(anqp->anqp_3gpp),
+			    wpabuf_len(anqp->anqp_3gpp)))
+			goto nomem;
+		if (anqp->domain_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "DomainName",
+			    wpabuf_head(anqp->domain_name),
+			    wpabuf_len(anqp->domain_name)))
+			goto nomem;
+		if (anqp->fils_realm_info &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "FilsRealmInfo",
+			    wpabuf_head(anqp->fils_realm_info),
+			    wpabuf_len(anqp->fils_realm_info)))
+			goto nomem;
+
+#ifdef CONFIG_HS20
+		if (anqp->hs20_capability_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20CapabilityList",
+			    wpabuf_head(anqp->hs20_capability_list),
+			    wpabuf_len(anqp->hs20_capability_list)))
+			goto nomem;
+		if (anqp->hs20_operator_friendly_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatorFriendlyName",
+			    wpabuf_head(anqp->hs20_operator_friendly_name),
+			    wpabuf_len(anqp->hs20_operator_friendly_name)))
+			goto nomem;
+		if (anqp->hs20_wan_metrics &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20WanMetrics",
+			    wpabuf_head(anqp->hs20_wan_metrics),
+			    wpabuf_len(anqp->hs20_wan_metrics)))
+			goto nomem;
+		if (anqp->hs20_connection_capability &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20ConnectionCapability",
+			    wpabuf_head(anqp->hs20_connection_capability),
+			    wpabuf_len(anqp->hs20_connection_capability)))
+			goto nomem;
+		if (anqp->hs20_operating_class &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatingClass",
+			    wpabuf_head(anqp->hs20_operating_class),
+			    wpabuf_len(anqp->hs20_operating_class)))
+			goto nomem;
+		if (anqp->hs20_osu_providers_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OSUProvidersList",
+			    wpabuf_head(anqp->hs20_osu_providers_list),
+			    wpabuf_len(anqp->hs20_osu_providers_list)))
+			goto nomem;
+		if (anqp->hs20_operator_icon_metadata &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatorIconMetadata",
+			    wpabuf_head(anqp->hs20_operator_icon_metadata),
+			    wpabuf_len(anqp->hs20_operator_icon_metadata)))
+			goto nomem;
+		if (anqp->hs20_osu_providers_nai_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OSUProvidersNAIList",
+			    wpabuf_head(anqp->hs20_osu_providers_nai_list),
+			    wpabuf_len(anqp->hs20_osu_providers_nai_list)))
+			goto nomem;
+#endif /* CONFIG_HS20 */
+
+		dl_list_for_each(elem, &anqp->anqp_elems,
+				 struct wpa_bss_anqp_elem, list) {
+			char title[32];
+
+			os_snprintf(title, sizeof(title), "anqp[%u]",
+				    elem->infoid);
+			if (!wpa_dbus_dict_append_byte_array(
+				    &iter_dict, title,
+				    wpabuf_head(elem->payload),
+				    wpabuf_len(elem->payload)))
+				goto nomem;
+
+			os_snprintf(title, sizeof(title),
+				    "protected-anqp-info[%u]", elem->infoid);
+			if (!wpa_dbus_dict_append_bool(
+				    &iter_dict, title,
+				    elem->protected_response))
+				goto nomem;
+		}
+#endif /* CONFIG_INTERWORKING */
+	}
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+/**
  * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 97fa337..acd6af7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -157,6 +157,9 @@
 wpas_dbus_handler_interworking_select(DBusMessage *message,
 				      struct wpa_supplicant *wpa_s);
 
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
 DECLARE_ACCESSOR(wpas_dbus_getter_state);
 DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
@@ -216,6 +219,7 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
 DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 801e698..e223450 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -238,6 +238,12 @@
 		wait_time = wpa_s->dpp_resp_retry_time;
 	else
 		wait_time = 1000;
+	if (wpa_s->dpp_tx_chan_change) {
+		wpa_s->dpp_tx_chan_change = false;
+		if (wait_time > 100)
+			wait_time = 100;
+	}
+
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Schedule retransmission of Authentication Response frame in %u ms",
 		wait_time);
@@ -487,6 +493,21 @@
 }
 
 
+static void wpas_dpp_neg_freq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!wpa_s->dpp_listen_on_tx_expire || !auth || !auth->neg_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start listen on neg_freq %u MHz based on timeout for TX wait expiration",
+		   auth->neg_freq);
+	wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+}
+
+
 static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
 			       unsigned int freq, const u8 *dst,
 			       const u8 *src, const u8 *bssid,
@@ -605,7 +626,9 @@
 			   wpa_s->dpp_auth->curr_freq,
 			   wpa_s->dpp_auth->neg_freq);
 		offchannel_send_action_done(wpa_s);
-		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq);
+		wpa_s->dpp_listen_on_tx_expire = true;
+		eloop_register_timeout(0, 100000, wpas_dpp_neg_freq_timeout,
+				       wpa_s, NULL);
 	}
 
 	if (wpa_s->dpp_auth_ok_on_ack)
@@ -1035,6 +1058,8 @@
 	wpa_s->off_channel_freq = 0;
 	wpa_s->roc_waiting_drv_freq = lwork->freq;
 	wpa_drv_dpp_listen(wpa_s, true);
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = false;
 }
 
 
@@ -1134,11 +1159,58 @@
 }
 
 
+static void wpas_dpp_tx_auth_resp(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+			       wpabuf_head(auth->resp_msg),
+			       wpabuf_len(auth->resp_msg),
+			       500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_tx_auth_resp_roc_timeout(void *eloop_ctx,
+					      void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !wpa_s->dpp_tx_auth_resp_on_roc_stop)
+		return;
+
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = true;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send postponed Authentication Response on remain-on-channel termination timeout");
+	wpas_dpp_tx_auth_resp(wpa_s);
+}
+
+
 void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					  unsigned int freq)
 {
+	wpa_printf(MSG_DEBUG, "DPP: Remain on channel cancel for %u MHz", freq);
 	wpas_dpp_listen_work_done(wpa_s);
 
+	if (wpa_s->dpp_auth && wpa_s->dpp_tx_auth_resp_on_roc_stop) {
+		eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout,
+				     wpa_s, NULL);
+		wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+		wpa_s->dpp_tx_chan_change = true;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Send postponed Authentication Response on remain-on-channel termination");
+		wpas_dpp_tx_auth_resp(wpa_s);
+		return;
+	}
+
 	if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) {
 		unsigned int new_freq;
 
@@ -1256,17 +1328,17 @@
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Stop listen on %u MHz to allow response on the request %u MHz",
 			   wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq);
+		wpa_s->dpp_tx_auth_resp_on_roc_stop = true;
+		eloop_register_timeout(0, 100000,
+				       wpas_dpp_tx_auth_resp_roc_timeout,
+				       wpa_s, NULL);
 		wpas_dpp_listen_stop(wpa_s);
+		return;
 	}
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = false;
 
-	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
-		MAC2STR(src), wpa_s->dpp_auth->curr_freq,
-		DPP_PA_AUTHENTICATION_RESP);
-	offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
-			       src, wpa_s->own_addr, broadcast,
-			       wpabuf_head(wpa_s->dpp_auth->resp_msg),
-			       wpabuf_len(wpa_s->dpp_auth->resp_msg),
-			       500, wpas_dpp_tx_status, 0);
+	wpas_dpp_tx_auth_resp(wpa_s);
 }
 
 
@@ -1275,6 +1347,15 @@
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	int freq;
 
+	if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel",
+			   auth->neg_freq);
+		eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
+		wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+		return;
+	}
+
 	if (!wpa_s->dpp_gas_server || !auth) {
 		if (auth && auth->waiting_auth_resp &&
 		    eloop_is_timeout_registered(wpas_dpp_drv_wait_timeout,
@@ -4816,6 +4897,8 @@
 	eloop_cancel_timeout(wpas_dpp_gas_initial_resp_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_gas_client_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_drv_wait_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
@@ -5627,6 +5710,7 @@
 	eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
 			       wpa_s, NULL);
 
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
 	return 0;
 }
 
@@ -5688,6 +5772,7 @@
 	wpa_s->scan_req = MANUAL_SCAN_REQ;
 	wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
 	return 0;
 }
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 9a4c235..d01b52b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -128,7 +128,7 @@
 }
 
 struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s);
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid);
 
 static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
 {
@@ -342,14 +342,6 @@
 	return -1;
 }
 
-static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
-				 struct wpa_driver_ap_params *params)
-{
-	if (wpa_s->driver->set_ap)
-		return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
-	return -1;
-}
-
 static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
 				  struct hostapd_sta_add_params *params)
 {
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9641062..95953de 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -670,6 +670,10 @@
 	wpa_s->eapol = NULL;
 	if (e->radius_conf && e->radius_conf->auth_server) {
 		os_free(e->radius_conf->auth_server->shared_secret);
+		os_free(e->radius_conf->auth_server->ca_cert);
+		os_free(e->radius_conf->auth_server->client_cert);
+		os_free(e->radius_conf->auth_server->private_key);
+		os_free(e->radius_conf->auth_server->private_key_passwd);
 		os_free(e->radius_conf->auth_server);
 	}
 	os_free(e->radius_conf);
@@ -1007,7 +1011,10 @@
 
 static void wpa_init_conf(struct eapol_test_data *e,
 			  struct wpa_supplicant *wpa_s, const char *authsrv,
-			  int port, const char *secret,
+			  int port, bool tls, const char *secret,
+			  const char *ca_cert, const char *client_cert,
+			  const char *private_key,
+			  const char *private_key_passwd,
 			  const char *cli_addr, const char *ifname)
 {
 	struct hostapd_radius_server *as;
@@ -1045,8 +1052,17 @@
 	}
 #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
 	as->port = port;
+	as->tls = tls;
 	as->shared_secret = (u8 *) os_strdup(secret);
 	as->shared_secret_len = os_strlen(secret);
+	if (ca_cert)
+		as->ca_cert = os_strdup(ca_cert);
+	if (client_cert)
+		as->client_cert = os_strdup(client_cert);
+	if (private_key)
+		as->private_key = os_strdup(private_key);
+	if (private_key_passwd)
+		as->private_key_passwd = os_strdup(private_key_passwd);
 	e->radius_conf->auth_server = as;
 	e->radius_conf->auth_servers = as;
 	e->radius_conf->msg_dumps = 1;
@@ -1256,11 +1272,16 @@
 {
 	printf("usage:\n"
 	       "eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
-	       "[-s<AS secret>]\\\n"
+	       "[-s<AS secret>] \\\n"
+	       "           [-X<RADIUS protocol> \\\n"
 	       "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
 	       "           [-M<client MAC address>] [-o<server cert file] \\\n"
 	       "           [-N<attr spec>] [-R<PC/SC reader>] "
 	       "[-P<PC/SC PIN>] \\\n"
+#ifdef CONFIG_RADIUS_TLS
+	       "           [-j<CA cert>] [-J<client cert>] \\\n"
+	       "[-k<private key] [-K<private key passwd>] \\\n"
+#endif /* CONFIG_RADIUS_TLS */
 	       "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
 	       "eapol_test scard\n"
 	       "eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1269,10 +1290,11 @@
 	       "  -c<conf> = configuration file\n"
 	       "  -a<AS IP> = IP address of the authentication server, "
 	       "default 127.0.0.1\n"
-	       "  -p<AS port> = UDP port of the authentication server, "
-	       "default 1812\n"
-	       "  -s<AS secret> = shared secret with the authentication "
-	       "server, default 'radius'\n"
+	       "  -p<AS port> = Port of the authentication server,\n"
+	       "                default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n"
+	       "  -s<AS secret> = shared secret with the authentication server,\n"
+	       "                  default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n"
+	       "  -X<RADIUS protocol> = RADIUS protocol to use: UDP (default) or TLS\n"
 	       "  -A<client IP> = IP address of the client, default: select "
 	       "automatically\n"
 	       "  -r<count> = number of re-authentications\n"
@@ -1310,8 +1332,11 @@
 	struct wpa_supplicant wpa_s;
 	int c, ret = 1, wait_for_monitor = 0, save_config = 0;
 	char *as_addr = "127.0.0.1";
-	int as_port = 1812;
-	char *as_secret = "radius";
+	int as_port = -1;
+	char *as_secret = NULL;
+	char *ca_cert = NULL, *client_cert = NULL;
+	char *private_key = NULL, *private_key_passwd = NULL;
+	bool tls = false;
 	char *cli_addr = NULL;
 	char *conf = NULL;
 	int timeout = 30;
@@ -1334,7 +1359,8 @@
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
+		c = getopt(argc, argv,
+			   "a:A:c:C:ei:j:J:k:K:M:nN:o:p:P:r:R:s:St:T:vWX:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1356,6 +1382,20 @@
 		case 'i':
 			ifname = optarg;
 			break;
+#ifdef CONFIG_RADIUS_TLS
+		case 'j':
+			ca_cert = optarg;
+			break;
+		case 'J':
+			client_cert = optarg;
+			break;
+		case 'k':
+			private_key = optarg;
+			break;
+		case 'K':
+			private_key_passwd = optarg;
+			break;
+#endif /* CONFIG_RADIUS_TLS */
 		case 'M':
 			if (hwaddr_aton(optarg, eapol_test.own_addr)) {
 				usage();
@@ -1406,6 +1446,16 @@
 		case 'W':
 			wait_for_monitor++;
 			break;
+		case 'X':
+			if (os_strcmp(optarg, "UDP") == 0) {
+				tls = false;
+			} else if (os_strcmp(optarg, "TLS") == 0) {
+				tls = true;
+			} else {
+				usage();
+				return -1;
+			}
+			break;
 		case 'N':
 			p1 = os_zalloc(sizeof(*p1));
 			if (p1 == NULL)
@@ -1440,6 +1490,11 @@
 		}
 	}
 
+	if (!as_secret)
+		as_secret = tls ? "radsec" : "radius";
+	if (as_port < 0)
+		as_port = tls ? 2083 : 1812;
+
 	if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
 		return scard_test(&eapol_test);
 	}
@@ -1489,7 +1544,8 @@
 		wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
 	}
 
-	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
+	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, tls, as_secret,
+		      ca_cert, client_cert, private_key, private_key_passwd,
 		      cli_addr, ifname);
 	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
 	if (wpa_s.ctrl_iface == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index bc45579..ff18543 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -164,7 +164,7 @@
 	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 
 		/* Get the BSS from the new scan results */
 		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
@@ -183,7 +183,7 @@
 	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 	}
 
@@ -1156,19 +1156,18 @@
 
 
 static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
-
 {
 	u16 removed_links;
 
 	if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL, NULL))
 		return true;
 
-	if (bss->n_mld_links == 0)
+	if (!bss->valid_links)
 		return true;
 
 	/* Check if the current BSS is going to be removed */
 	removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
-	if (BIT(bss->mld_links[0].link_id) & removed_links)
+	if (BIT(bss->mld_link_id) & removed_links)
 		return false;
 
 	return true;
@@ -1697,13 +1696,11 @@
 		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,
@@ -1809,10 +1806,12 @@
 				break;
 		}
 
-		if (selected == NULL && wpa_s->bssid_ignore &&
+		if (!selected &&
+		    (wpa_s->bssid_ignore || wnm_active_bss_trans_mgmt(wpa_s)) &&
 		    !wpa_s->countermeasures) {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"No APs found - clear BSSID ignore list and try again");
+			wnm_btm_reset(wpa_s);
 			wpa_bssid_ignore_clear(wpa_s);
 			wpa_s->bssid_ignore_cleared = true;
 		} else if (selected == NULL)
@@ -2409,7 +2408,7 @@
 
 	scan_res = wpa_supplicant_get_scan_results(wpa_s,
 						   data ? &data->scan_info :
-						   NULL, 1);
+						   NULL, 1, NULL);
 	if (scan_res == NULL) {
 		if (wpa_s->conf->ap_scan == 2 || ap ||
 		    wpa_s->scan_res_handler == scan_only_handler)
@@ -2498,7 +2497,7 @@
 		return 0;
 	}
 
-	if (wnm_scan_process(wpa_s, 1) > 0)
+	if (wnm_scan_process(wpa_s, false) > 0)
 		goto scan_work_done;
 
 	if (sme_proc_obss_scan(wpa_s) > 0)
@@ -2941,8 +2940,6 @@
 }
 
 
-#ifdef CONFIG_INTERWORKING
-
 static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
 			    size_t len)
 {
@@ -2975,8 +2972,6 @@
 	}
 }
 
-#endif /* CONFIG_INTERWORKING */
-
 
 static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
 {
@@ -3002,25 +2997,24 @@
 					const u8 *ies, size_t ies_len)
 {
 	struct ieee802_11_elems elems;
-	const u8 *map_sub_elem, *pos;
-	size_t len;
+	struct multi_ap_params multi_ap;
+	u16 status;
 
 	wpa_s->multi_ap_ie = 0;
 
 	if (!ies ||
 	    ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed ||
-	    !elems.multi_ap || elems.multi_ap_len < 7)
+	    !elems.multi_ap)
 		return;
 
-	pos = elems.multi_ap + 4;
-	len = elems.multi_ap_len - 4;
-
-	map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
-	if (!map_sub_elem || map_sub_elem[1] < 1)
+	status = check_multi_ap_ie(elems.multi_ap + 4, elems.multi_ap_len - 4,
+				   &multi_ap);
+	if (status != WLAN_STATUS_SUCCESS)
 		return;
 
-	wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS);
-	wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] &
+	wpa_s->multi_ap_backhaul = !!(multi_ap.capability &
+				      MULTI_AP_BACKHAUL_BSS);
+	wpa_s->multi_ap_fronthaul = !!(multi_ap.capability &
 				       MULTI_AP_FRONTHAUL_BSS);
 	wpa_s->multi_ap_ie = 1;
 }
@@ -3359,10 +3353,8 @@
 		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 				       data->assoc_info.resp_ies_len);
 #endif /* CONFIG_WNM */
-#ifdef CONFIG_INTERWORKING
 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 						data->assoc_info.resp_ies_len);
-#endif /* CONFIG_INTERWORKING */
 		if (wpa_s->hw_capab == CAPAB_VHT &&
 		    get_ie(data->assoc_info.resp_ies,
 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
@@ -3718,12 +3710,21 @@
 	if (wpa_found || rsn_found)
 		wpa_s->ap_ies_from_associnfo = 1;
 
-	if (wpa_s->assoc_freq && data->assoc_info.freq &&
-	    wpa_s->assoc_freq != data->assoc_info.freq) {
-		wpa_printf(MSG_DEBUG, "Operating frequency changed from "
-			   "%u to %u MHz",
-			   wpa_s->assoc_freq, data->assoc_info.freq);
-		wpa_supplicant_update_scan_results(wpa_s);
+	if (wpa_s->assoc_freq && data->assoc_info.freq) {
+		struct wpa_bss *bss;
+		unsigned int freq = 0;
+
+		if (bssid_known) {
+			bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+			if (bss)
+				freq = bss->freq;
+		}
+		if (freq != data->assoc_info.freq) {
+			wpa_printf(MSG_DEBUG,
+				   "Operating frequency changed from %u to %u MHz",
+				   wpa_s->assoc_freq, data->assoc_info.freq);
+			wpa_supplicant_update_scan_results(wpa_s, bssid);
+		}
 	}
 
 	wpa_s->assoc_freq = data->assoc_info.freq;
@@ -4077,10 +4078,7 @@
 		if (!mlo.valid_links)
 			return 0;
 
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(mlo.valid_links & BIT(i)))
-				continue;
-
+		for_each_link(mlo.valid_links, i) {
 			if (!ether_addr_equal(wpa_s->links[i].addr,
 					      mlo.links[i].addr) ||
 			    !ether_addr_equal(wpa_s->links[i].bssid,
@@ -4098,10 +4096,7 @@
 	wpa_s->valid_links = mlo.valid_links;
 	wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
 	os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
 		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
 		wpa_s->links[i].freq = mlo.links[i].freq;
@@ -4134,15 +4129,13 @@
 	wpa_mlo.valid_links = drv_mlo.valid_links;
 	wpa_mlo.req_links = drv_mlo.req_links;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+	for_each_link(drv_mlo.req_links, i) {
 		struct wpa_bss *bss;
 
-		if (!(drv_mlo.req_links & BIT(i)))
-			continue;
-
 		bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
 		if (!bss) {
-			wpa_supplicant_update_scan_results(wpa_s);
+			wpa_supplicant_update_scan_results(
+				wpa_s, drv_mlo.links[i].bssid);
 			bss = wpa_supplicant_get_new_bss(
 				wpa_s, drv_mlo.links[i].bssid);
 		}
@@ -6021,13 +6014,10 @@
 	pos += res;
 
 	if (!info->default_map) {
-		for (i = 0; i < MAX_NUM_MLD_LINKS && end > pos; i++) {
+		for_each_link(info->valid_links, i) {
 			char uplink_map_str[9];
 			char downlink_map_str[9];
 
-			if (!(info->valid_links & BIT(i)))
-				continue;
-
 			bitmap_to_str(info->t2lmap[i].uplink, uplink_map_str);
 			bitmap_to_str(info->t2lmap[i].downlink,
 				      downlink_map_str);
@@ -6307,6 +6297,13 @@
 			" type=%d stype=%d",
 			MAC2STR(data->tx_status.dst),
 			data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_WNM
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+		    wnm_btm_resp_tx_status(wpa_s, data->tx_status.data,
+					   data->tx_status.data_len) == 0)
+			break;
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 498cc98..5676f38 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -2820,7 +2820,7 @@
 	struct wpa_bss *bss;
 	int res;
 
-	bss = wpa_bss_get_bssid(wpa_s, dst);
+	bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 	if (!bss && !freq) {
 		wpa_printf(MSG_WARNING,
 			   "ANQP: Cannot send query without BSS freq info");
@@ -3175,7 +3175,7 @@
 		}
 	}
 	if (bss == NULL)
-		bss = wpa_bss_get_bssid(wpa_s, dst);
+		bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 
 	pos = wpabuf_head(resp);
 	end = pos + wpabuf_len(resp);
@@ -3206,12 +3206,12 @@
 	}
 
 out_parse_done:
+	if (bss)
+		wpas_notify_bss_anqp_changed(wpa_s, bss->id);
 #ifdef CONFIG_HS20
 	hs20_notify_parse_done(wpa_s);
 #endif /* CONFIG_HS20 */
 out:
-	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
-		MAC2STR(dst), anqp_result);
 	wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
 }
 
@@ -3290,7 +3290,7 @@
 	u8 query_resp_len_limit = 0;
 
 	freq = wpa_s->assoc_freq;
-	bss = wpa_bss_get_bssid(wpa_s, dst);
+	bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 	if (bss)
 		freq = bss->freq;
 	if (freq <= 0)
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 486fc6a..85c1ea8 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -231,7 +231,7 @@
 		    hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
 		    hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
 		    ifmsh->conf->vht_capab,
-		    he_capab, NULL)) {
+		    he_capab, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
 		wpa_supplicant_mesh_deinit(wpa_s, true);
 		return -1;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 6f162f2..c930651 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -627,6 +627,15 @@
 }
 
 
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_ANQP, id);
+}
+
+
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
 {
 	if (wpa_s->p2p_mgmt)
@@ -935,7 +944,8 @@
 				const u8 *p2p_dev_addr, const u8 *ip)
 {
 	if (authorized)
-		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr, ip);
+		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr,
+					      ip);
 	else
 		wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
 }
@@ -1057,11 +1067,14 @@
 				 const char *result,
 				 const struct wpa_bss_anqp *anqp)
 {
+	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+		MAC2STR(bssid), result);
 #ifdef CONFIG_INTERWORKING
 	if (!wpa_s || !bssid || !anqp)
 		return;
 
 	wpas_aidl_notify_anqp_query_done(wpa_s, bssid, result, anqp);
+	wpas_dbus_signal_anqp_query_done(wpa_s, bssid, result);
 #endif /* CONFIG_INTERWORKING */
 }
 
@@ -1359,6 +1372,7 @@
 	wpas_dbus_signal_interworking_select_done(wpa_s);
 }
 
+
 #endif /* CONFIG_INTERWORKING */
 
 void wpas_notify_eap_method_selected(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index d4656ad..a584884 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -89,6 +89,8 @@
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id);
 void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id);
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
 
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index b4ad3ca..ff11d20 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,12 @@
 				       unsigned int *flags)
 {
 	int i;
-	bool is_6ghz = op_class >= 131 && op_class <= 136;
+	bool is_6ghz = is_6ghz_op_class(op_class);
 
 	for (i = 0; i < mode->num_channels; i++) {
 		bool chan_is_6ghz;
 
-		chan_is_6ghz = mode->channels[i].freq >= 5935 &&
-			mode->channels[i].freq <= 7115;
+		chan_is_6ghz = is_6ghz_freq(mode->channels[i].freq);
 		if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
 			break;
 	}
@@ -187,6 +186,69 @@
 }
 
 
+static int get_center_320mhz(struct hostapd_hw_modes *mode, u8 channel,
+			     const u8 *center_channels, size_t num_chan)
+{
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A || !mode->is_6ghz)
+		return 0;
+
+	for (i = 0; i < num_chan; i++) {
+		/*
+		* In 320 MHz, the bandwidth "spans" 60 channels (e.g., 65-125),
+		* so the center channel is 30 channels away from the start/end.
+		*/
+		if (channel >= center_channels[i] - 30 &&
+		    channel <= center_channels[i] + 30)
+			return center_channels[i];
+	}
+
+	return 0;
+}
+
+
+static enum chan_allowed verify_320mhz(struct hostapd_hw_modes *mode,
+				       u8 op_class, u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+	bool no_ir = false;
+	const u8 *center_channels;
+	size_t num_chan;
+	const u8 center_channels_6ghz[] = { 31, 63, 95, 127, 159, 191 };
+
+	center_channels = center_channels_6ghz;
+	num_chan = ARRAY_SIZE(center_channels_6ghz);
+
+	center_chan = get_center_320mhz(mode, channel, center_channels,
+					num_chan);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* Check all the channels are available */
+	for (i = 0; i < 16; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 30 + i * 4;
+
+		if (allow_channel(mode, op_class, adj_chan, &flags) ==
+		    NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if (!(flags & HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL))
+			return NOT_ALLOWED;
+
+		if (flags & HOSTAPD_CHAN_NO_IR)
+			no_ir = true;
+	}
+
+	if (no_ir)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
 enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
 				 u8 channel, u8 bw)
 {
@@ -228,6 +290,13 @@
 		 * result and use only the 80 MHz specific version.
 		 */
 		res2 = res = verify_80mhz(mode, op_class, channel);
+	} else if (bw == BW320) {
+		/*
+		 * channel is a center channel and as such, not necessarily a
+		 * valid 20 MHz channels. Override earlier allow_channel()
+		 * result and use only the 320 MHz specific version.
+		 */
+		res2= res = verify_320mhz(mode, op_class, channel);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -251,6 +320,7 @@
 	int z;
 	int freq2 = 0;
 	int freq5 = 0;
+	bool freq6 = false;
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode,
 			is_6ghz_op_class(op_class->op_class));
@@ -265,7 +335,9 @@
 
 			if (f == 0)
 				break; /* end of list */
-			if (f > 4000 && f < 6000)
+			if (is_6ghz_freq(f))
+				freq6 = true;
+			else if (f > 4000 && f < 6000)
 				freq5 = 1;
 			else if (f > 2400 && f < 2500)
 				freq2 = 1;
@@ -274,8 +346,11 @@
 		/* No frequencies specified, can use anything hardware supports.
 		 */
 		freq2 = freq5 = 1;
+		freq6 = true;
 	}
 
+	if (is_6ghz_op_class(op_class->op_class) && !freq6)
+		return 0;
 	if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
 		return 0;
 	if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
@@ -453,6 +528,7 @@
 	u8 op, current, chan;
 	u8 *ie_len;
 	size_t res;
+	bool op128 = false, op130 = false, op133 = false, op135 = false;
 
 	/*
 	 * Determine the current operating class correct mode based on
@@ -480,8 +556,50 @@
 	wpabuf_put_u8(buf, current);
 
 	for (op = 0; global_op_class[op].op_class; op++) {
-		if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
-			wpabuf_put_u8(buf, global_op_class[op].op_class);
+		bool supp;
+		u8 op_class = global_op_class[op].op_class;
+
+		supp = wpas_op_class_supported(wpa_s, ssid,
+					       &global_op_class[op]);
+		if (!supp)
+			continue;
+		switch (op_class) {
+		case 128:
+			op128 = true;
+			break;
+		case 130:
+			op130 = true;
+			break;
+		case 133:
+			op133 = true;
+			break;
+		case 135:
+			op135 = true;
+			break;
+		}
+		if (is_80plus_op_class(op_class))
+			continue;
+
+		/* Add a 1-octet operating class to the Operating Class field */
+		wpabuf_put_u8(buf, global_op_class[op].op_class);
+	}
+
+	/* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */
+	if ((op128 && op130) || (op133 && op135)) {
+		/* Operating Class Duple Sequence field */
+
+		/* Zero Delimiter */
+		wpabuf_put_u8(buf, 0);
+
+		/* Operating Class Duple List */
+		if (op128 && op130) {
+			wpabuf_put_u8(buf, 130);
+			wpabuf_put_u8(buf, 128);
+		}
+		if (op133 && op135) {
+			wpabuf_put_u8(buf, 135);
+			wpabuf_put_u8(buf, 133);
+		}
 	}
 
 	*ie_len = wpabuf_len(buf) - 2;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 3c121c8..9c20ee5 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1103,6 +1103,8 @@
 
 	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 
+	wpa_s->p2p_go_no_pri_sec_switch = 0;
+
 	return 0;
 }
 
@@ -7003,7 +7005,8 @@
 			return -1;
 	}
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, selected_freq, vht_center_freq2,
+	if (wpas_p2p_init_go_params(wpa_s, &params, selected_freq,
+				    vht_center_freq2,
 				    ht40, vht, max_oper_chwidth, he, edmg,
 				    NULL))
 		return -1;
@@ -7097,7 +7100,7 @@
 	 * fetch time on the same radio so it reflects the actual time the last
 	 * scan result event occurred.
 	 */
-	wpa_supplicant_update_scan_results(wpa_s);
+	wpa_supplicant_update_scan_results(wpa_s, go_bssid);
 	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 			 radio_list) {
 		if (ifs == wpa_s)
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2e65cf0..1bb38f7 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -166,7 +166,7 @@
 
 	bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, peer_addr);
 		bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 		if (!bss) {
 			wpa_printf(MSG_DEBUG, "PASN: BSS not found");
@@ -560,9 +560,10 @@
 		derive_kdk = wpa_s->conf->force_kdk_derivation;
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (derive_kdk)
-		pasn->kdk_len = WPA_KDK_MAX_LEN;
+		pasn_enable_kdk_derivation(pasn);
 	else
-		pasn->kdk_len = 0;
+		pasn_disable_kdk_derivation(pasn);
+
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
 
 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
@@ -582,9 +583,8 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
-	pasn->rsnxe_capab = capab;
-	pasn->send_mgmt = wpas_pasn_send_mlme;
-
+	pasn_set_rsnxe_caps(pasn, capab);
+	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
 
 #ifdef CONFIG_SAE
@@ -594,7 +594,7 @@
 				   "PASN: No network profile found for SAE");
 			goto fail;
 		}
-		pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+		pasn_set_pt(pasn, wpas_pasn_sae_derive_pt(ssid, awork->group));
 		if (!pasn->pt) {
 			wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
 			goto fail;
@@ -629,8 +629,7 @@
 	}
 #endif /* CONFIG_FILS */
 
-	pasn->cb_ctx = wpa_s;
-	pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+	pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa));
 
 	if (wpa_key_mgmt_ft(awork->akmp)) {
 #ifdef CONFIG_IEEE80211R
@@ -753,7 +752,8 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
 
-	wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
+	wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn_get_akmp(pasn),
+			      pasn_get_cipher(pasn),
 			      pasn->status, pasn->comeback,
 			      pasn->comeback_after);
 
@@ -765,8 +765,8 @@
 				     struct pasn_data *pasn,
 				     struct wpa_pasn_params_data *params)
 {
-	int akmp = pasn->akmp;
-	int cipher = pasn->cipher;
+	int akmp = pasn_get_akmp(pasn);
+	int cipher = pasn_get_cipher(pasn);
 	u16 group = pasn->group;
 	u8 own_addr[ETH_ALEN];
 	u8 peer_addr[ETH_ALEN];
@@ -806,20 +806,22 @@
 	if (!wpa_s->pasn_auth_work)
 		return -2;
 
-	pasn->cb_ctx = wpa_s;
+	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
 	if (ret == 0) {
 		ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
-				pasn->cipher, dot11RSNAConfigPMKLifetime,
-				&pasn->ptk,
+				pasn_get_cipher(pasn),
+				dot11RSNAConfigPMKLifetime,
+				pasn_get_ptk(pasn),
 				wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
-				wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
+				wpa_s->pasn_params ? wpa_s : NULL,
+				pasn_get_akmp(pasn));
 
 		if (pasn->pmksa_entry)
 			wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
 	}
 
-	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+	forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
 
 	if (ret == -1) {
 		wpas_pasn_auth_stop(wpa_s);
@@ -909,7 +911,8 @@
 	}
 
 	wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
-				      pasn->cipher, pasn->akmp);
+				      pasn_get_cipher(pasn),
+				      pasn_get_akmp(pasn));
 	wpas_pasn_auth_stop(wpa_s);
 	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
 
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 7ce854b..2ec4310 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -515,6 +515,8 @@
 		num_primary_channels = 4;
 	else if (op->bw == BW160)
 		num_primary_channels = 8;
+	else if (op->bw == BW320)
+		num_primary_channels = 16;
 	else
 		num_primary_channels = 1;
 
@@ -561,6 +563,7 @@
 	u8 channels_80mhz_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151,
 				     167, 183, 199, 215 };
 	u8 channels_160mhz_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
+	u8 channels_320mhz_6ghz[] = { 31, 63, 95, 127, 159, 191 };
 	const u8 *channels = NULL;
 	size_t num_chan = 0;
 	bool is_6ghz = is_6ghz_op_class(op->op_class);
@@ -579,6 +582,9 @@
 			channels_160mhz_5ghz;
 		num_chan =  is_6ghz ? ARRAY_SIZE(channels_160mhz_6ghz) :
 			ARRAY_SIZE(channels_160mhz_5ghz);
+	} else if (op->bw == BW320) {
+		channels = channels_320mhz_6ghz;
+		num_chan = ARRAY_SIZE(channels_320mhz_6ghz);
 	}
 
 	return wpas_add_channels(op, mode, channels, num_chan);
@@ -1520,10 +1526,7 @@
 	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;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (ether_addr_equal(wpa_s->links[i].bssid, bssid))
 			return true;
 	}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 6e6f05d..ccd694b 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -698,10 +698,7 @@
 	else
 		wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
 
-	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-		if (!(links & BIT(link_id)))
-			continue;
-
+	for_each_link(links, link_id) {
 		wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
 
 		/* Subelement length includes only the control */
@@ -2606,8 +2603,8 @@
 }
 
 
-void filter_scan_res(struct wpa_supplicant *wpa_s,
-		     struct wpa_scan_results *res)
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+			    struct wpa_scan_results *res)
 {
 	size_t i, j;
 
@@ -3169,6 +3166,7 @@
  * @wpa_s: Pointer to wpa_supplicant data
  * @info: Information about what was scanned or %NULL if not available
  * @new_scan: Whether a new scan was performed
+ * @bssid: Return BSS entries only for a single BSSID, %NULL for all
  * Returns: Scan results, %NULL on failure
  *
  * This function request the current scan results from the driver and updates
@@ -3177,13 +3175,14 @@
  */
 struct wpa_scan_results *
 wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
-				struct scan_info *info, int new_scan)
+				struct scan_info *info, int new_scan,
+				const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
 	size_t i;
 	int (*compar)(const void *, const void *) = wpa_scan_result_compar;
 
-	scan_res = wpa_drv_get_scan_results2(wpa_s);
+	scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
 	if (scan_res == NULL) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
 		return NULL;
@@ -3241,6 +3240,7 @@
 /**
  * wpa_supplicant_update_scan_results - Update scan results from the driver
  * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: Update BSS entries only for a single BSSID, %NULL for all
  * Returns: 0 on success, -1 on failure
  *
  * This function updates the BSS table within wpa_supplicant based on the
@@ -3250,10 +3250,11 @@
  * needed information to complete the connection (e.g., to perform validation
  * steps in 4-way handshake).
  */
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+				       const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
-	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
 	if (scan_res == NULL)
 		return -1;
 	wpa_scan_results_free(scan_res);
@@ -3350,6 +3351,7 @@
 	params->duration = src->duration;
 	params->duration_mandatory = src->duration_mandatory;
 	params->oce_scan = src->oce_scan;
+	params->link_id = src->link_id;
 
 	if (src->sched_scan_plans_num > 0) {
 		params->sched_scan_plans =
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 8402e74..d4c06c1 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -54,8 +54,10 @@
 				bool default_ies, bool next);
 struct wpa_scan_results *
 wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
-				struct scan_info *info, int new_scan);
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+				struct scan_info *info, int new_scan,
+				const u8 *bssid);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+				       const u8 *bssid);
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
 const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type);
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
@@ -86,8 +88,6 @@
 int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
 				     unsigned int type, u8 *mask);
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
-void filter_scan_res(struct wpa_supplicant *wpa_s,
-		     struct wpa_scan_results *res);
 void scan_snr(struct wpa_scan_res *res);
 void scan_est_throughput(struct wpa_supplicant *wpa_s,
 			 struct wpa_scan_res *res);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index b8f7c65..be0bc0d 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -182,7 +182,7 @@
 	if (!bss) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: BSS not available, update scan result to get BSS");
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
 	}
 	if (bss) {
@@ -405,10 +405,7 @@
 		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;
-
+		for_each_link(wpa_s->valid_links, i) {
 			if (wpa_s->mlo_assoc_link_id == i)
 				continue;
 
@@ -439,10 +436,7 @@
 		return bss;
 	}
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (wpa_s->mlo_assoc_link_id == i)
 			continue;
 
@@ -523,21 +517,23 @@
 static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
 				   struct wpa_bss *bss)
 {
-	int i;
+	u8 i;
 
 	wpa_s->valid_links = 0;
+	wpa_s->mlo_assoc_link_id = bss->mld_link_id;
 
-	for (i = 0; i < bss->n_mld_links; i++) {
-		u8 link_id = bss->mld_links[i].link_id;
+	for_each_link(bss->valid_links, i) {
 		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;
+		wpa_s->valid_links |= BIT(i);
+		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+		wpa_s->links[i].freq = bss->mld_links[i].freq;
+		wpa_s->links[i].disabled = bss->mld_links[i].disabled;
+
+		if (bss->mld_link_id == i)
+			wpa_s->links[i].bss = bss;
+		else
+			wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
 	}
 }
 
@@ -578,7 +574,7 @@
 	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) {
+	    bss->valid_links) {
 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
 		wpas_sme_set_mlo_links(wpa_s, bss);
 
@@ -2416,12 +2412,16 @@
 
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
+		struct multi_ap_params multi_ap = { 0 };
+
+		multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+		multi_ap.profile = ssid->multi_ap_profile;
 
 		multi_ap_ie_len = add_multi_ap_ie(
 			wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
 			sizeof(wpa_s->sme.assoc_req_ie) -
 			wpa_s->sme.assoc_req_ie_len,
-			MULTI_AP_BACKHAUL_STA);
+			&multi_ap);
 		if (multi_ap_ie_len == 0) {
 			wpa_printf(MSG_ERROR,
 				   "Multi-AP: Failed to build Multi-AP IE");
@@ -2601,10 +2601,7 @@
 		params.mld_params.mld_addr = wpa_s->ap_mld_addr;
 		params.mld_params.valid_links = wpa_s->valid_links;
 		params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(wpa_s->valid_links & BIT(i)))
-				continue;
-
+		for_each_link(wpa_s->valid_links, i) {
 			params.mld_params.mld_links[i].bssid =
 				wpa_s->links[i].bssid;
 			params.mld_params.mld_links[i].freq =
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 775e75b..ea79ae6 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -418,7 +418,7 @@
 }
 
 
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+void wnm_btm_reset(struct wpa_supplicant *wpa_s)
 {
 	int i;
 
@@ -431,8 +431,17 @@
 	os_free(wpa_s->wnm_neighbor_report_elements);
 	wpa_s->wnm_neighbor_report_elements = NULL;
 
-	wpabuf_free(wpa_s->coloc_intf_elems);
-	wpa_s->coloc_intf_elems = NULL;
+	wpa_s->wnm_cand_valid_until.sec = 0;
+	wpa_s->wnm_cand_valid_until.usec = 0;
+
+	wpa_s->wnm_mode = 0;
+	wpa_s->wnm_dialog_token = 0;
+	wpa_s->wnm_reply = 0;
+
+#ifdef CONFIG_MBO
+	wpa_s->wnm_mbo_trans_reason_present = 0;
+	wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
 }
 
 
@@ -781,22 +790,11 @@
 			}
 		}
 
-		if (bss->ssid_len != target->ssid_len ||
-		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
-			/*
-			 * TODO: Could consider allowing transition to another
-			 * ESS if PMF was enabled for the association.
-			 */
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) in different ESS",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		if (wpa_s->current_ssid &&
-		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+		/*
+		 * TODO: Could consider allowing transition to another ESS if
+		 * PMF was enabled for the association.
+		 */
+		if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
 					1, 0)) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not match the current network profile",
@@ -806,14 +804,6 @@
 			continue;
 		}
 
-		if (wpa_is_bss_tmp_disallowed(wpa_s, target)) {
-			wpa_printf(MSG_DEBUG,
-				   "MBO: Candidate BSS " MACSTR
-				   " retry delay is not over yet",
-				   MAC2STR(nei->bssid));
-			continue;
-		}
-
 		if (target->level < bss->level && target->level < -80) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not have sufficient signal level (%d)",
@@ -1045,8 +1035,8 @@
 
 #define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
 
-static void wnm_send_bss_transition_mgmt_resp(
-	struct wpa_supplicant *wpa_s, u8 dialog_token,
+static int wnm_send_bss_transition_mgmt_resp(
+	struct wpa_supplicant *wpa_s,
 	enum bss_trans_mgmt_status_code status,
 	enum mbo_transition_reject_reason reason,
 	u8 delay, const u8 *target_bssid)
@@ -1054,21 +1044,24 @@
 	struct wpabuf *buf;
 	int res;
 
+	wpa_s->wnm_reply = 0;
+
 	wpa_printf(MSG_DEBUG,
 		   "WNM: Send BSS Transition Management Response to " MACSTR
 		   " dialog_token=%u status=%u reason=%u delay=%d",
-		   MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
+		   MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
+		   reason, delay);
 	if (!wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Current BSS not known - drop response");
-		return;
+		return -1;
 	}
 
 	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Failed to allocate memory for BTM response");
-		return;
+		return -1;
 	}
 
 	wpa_s->bss_tm_status = status;
@@ -1076,7 +1069,7 @@
 
 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
-	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
 	wpabuf_put_u8(buf, status);
 	wpabuf_put_u8(buf, delay);
 	if (target_bssid) {
@@ -1090,7 +1083,7 @@
 		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
 	}
 
-	if (status == WNM_BSS_TM_ACCEPT && !wpa_s->wnm_link_removal)
+	if (status == WNM_BSS_TM_ACCEPT && target_bssid)
 		wnm_add_cand_list(wpa_s, &buf);
 
 #ifdef CONFIG_MBO
@@ -1106,7 +1099,7 @@
 				wpabuf_free(buf);
 				wpa_printf(MSG_DEBUG,
 					   "WNM: Failed to allocate memory for MBO IE");
-				return;
+				return -1;
 			}
 
 			wpabuf_put_data(buf, mbo, ret);
@@ -1123,6 +1116,8 @@
 	}
 
 	wpabuf_free(buf);
+
+	return res;
 }
 
 
@@ -1140,19 +1135,24 @@
 
 	/* Send the BSS Management Response - Accept */
 	if (wpa_s->wnm_reply) {
-		wpa_s->wnm_reply = 0;
+		wpa_s->wnm_target_bss = bss;
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Sending successful BSS Transition Management Response");
-		wnm_send_bss_transition_mgmt_resp(
-			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
-			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
-			bss->bssid);
+
+		/* This function will be called again from the TX handler to
+		 * start the actual reassociation after this response has been
+		 * delivered to the current AP. */
+		if (wnm_send_bss_transition_mgmt_resp(
+			    wpa_s, WNM_BSS_TM_ACCEPT,
+			    MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+			    bss->bssid) >= 0)
+			return;
 	}
 
 	if (bss == wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Already associated with the preferred candidate");
-		wnm_deallocate_memory(wpa_s);
+		wnm_btm_reset(wpa_s);
 		return;
 	}
 
@@ -1168,11 +1168,10 @@
 	 */
 	if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
 		wpa_s->bss_trans_mgmt_in_progress = true;
-	wnm_deallocate_memory(wpa_s);
 }
 
 
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
 {
 	struct wpa_bss *bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -1180,27 +1179,51 @@
 	enum mbo_transition_reject_reason reason =
 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
 
-	if (!wpa_s->wnm_neighbor_report_elements)
+	if (!wpa_s->wnm_dialog_token)
 		return 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG,
 		"WNM: Process scan results for BSS Transition Management");
-	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+	if (!pre_scan_check &&
+	    os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
+	    os_reltime_before(&wpa_s->wnm_cand_valid_until,
 			      &wpa_s->scan_trigger_time)) {
 		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
-		wnm_deallocate_memory(wpa_s);
-		return 0;
-	}
-
-	if (!wpa_s->current_bss ||
-	    !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;
+		goto send_bss_resp_fail;
 	}
 
 	/* Compare the Neighbor Report and scan results */
 	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
+
+	/*
+	 * If this is a pre-scan check, returning 0 will trigger a scan and
+	 * another call. In that case, reject "bad" candidates in the hope of
+	 * finding a better candidate after scanning.
+	 *
+	 * Use a simple heuristic to check whether the selection is reasonable
+	 * or a scan is a good idea. For that, we need to have found a
+	 * candidate BSS (which might be the current one), it is up-to-date,
+	 * and we don't want to immediately roam back again.
+	 */
+	if (pre_scan_check) {
+		struct os_reltime age;
+
+		if (!bss)
+			return 0;
+
+		os_reltime_age(&bss->last_update, &age);
+		if (age.sec >= 10)
+			return 0;
+
+#ifndef CONFIG_NO_ROAMING
+		if (wpa_s->current_bss && bss != wpa_s->current_bss &&
+		    wpa_supplicant_need_to_roam_within_ess(wpa_s,
+							   wpa_s->current_bss,
+							   bss))
+			return 0;
+#endif /* CONFIG_NO_ROAMING */
+	}
+
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -1212,18 +1235,13 @@
 	return 1;
 
 send_bss_resp_fail:
-	if (!reply_on_fail)
-		return 0;
-
 	/* Send reject response for all the failures */
 
-	if (wpa_s->wnm_reply) {
-		wpa_s->wnm_reply = 0;
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  status, reason, 0, NULL);
-	}
-	wnm_deallocate_memory(wpa_s);
+	if (wpa_s->wnm_reply)
+		wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
+						  0, NULL);
+
+	wnm_btm_reset(wpa_s);
 
 	return 0;
 }
@@ -1330,6 +1348,10 @@
 		struct neighbor_report *nei;
 
 		nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+		if (nei->preference_present && nei->preference == 0)
+			continue;
+
 		if (nei->freq <= 0) {
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Unknown neighbor operating frequency for "
@@ -1354,79 +1376,6 @@
 }
 
 
-static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_scan_results *scan_res;
-	struct wpa_bss *bss;
-	struct wpa_ssid *ssid = wpa_s->current_ssid;
-	u8 i, found = 0;
-	size_t j;
-
-	wpa_dbg(wpa_s, MSG_DEBUG,
-		"WNM: Fetch current scan results from the driver for checking transition candidates");
-	scan_res = wpa_drv_get_scan_results2(wpa_s);
-	if (!scan_res) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
-		return 0;
-	}
-
-	if (scan_res->fetch_time.sec == 0)
-		os_get_reltime(&scan_res->fetch_time);
-
-	filter_scan_res(wpa_s, scan_res);
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-		struct neighbor_report *nei;
-
-		nei = &wpa_s->wnm_neighbor_report_elements[i];
-		if (nei->preference_present && nei->preference == 0)
-			continue;
-
-		for (j = 0; j < scan_res->num; j++) {
-			struct wpa_scan_res *res;
-			const u8 *ssid_ie;
-
-			res = scan_res->res[j];
-			if (!ether_addr_equal(nei->bssid, res->bssid) ||
-			    res->age > WNM_SCAN_RESULT_AGE * 1000)
-				continue;
-			bss = wpa_s->current_bss;
-			ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
-			if (bss && ssid_ie && ssid_ie[1] &&
-			    (bss->ssid_len != ssid_ie[1] ||
-			     os_memcmp(bss->ssid, ssid_ie + 2,
-				       bss->ssid_len) != 0))
-				continue; /* Skip entries for other ESSs */
-
-			/* Potential candidate found */
-			found = 1;
-			scan_snr(res);
-			scan_est_throughput(wpa_s, res);
-			wpa_bss_update_scan_res(wpa_s, res,
-						&scan_res->fetch_time);
-		}
-	}
-
-	wpa_scan_results_free(scan_res);
-	if (!found) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"WNM: No transition candidate matches existing scan results");
-		return 0;
-	}
-
-	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
-	if (!bss) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"WNM: Comparison of scan results against transition candidates did not find matches");
-		return 0;
-	}
-
-	/* Associate to the network */
-	wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
-	return 1;
-}
-
-
 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 					     const u8 *pos, const u8 *end,
 					     int reply)
@@ -1445,8 +1394,6 @@
 		return;
 
 #ifdef CONFIG_MBO
-	wpa_s->wnm_mbo_trans_reason_present = 0;
-	wpa_s->wnm_mbo_transition_reason = 0;
 	wpa_s->wnm_mbo_cell_pref_present = 0;
 	wpa_s->wnm_mbo_cell_preference = 0;
 	wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
@@ -1458,6 +1405,8 @@
 	else
 		beacon_int = 100; /* best guess */
 
+	wnm_btm_reset(wpa_s);
+
 	wpa_s->wnm_dialog_token = pos[0];
 	wpa_s->wnm_mode = pos[1];
 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
@@ -1477,8 +1426,7 @@
 			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
 			   wpa_s->reject_btm_req_reason);
 		wnm_send_bss_transition_mgmt_resp(
-			wpa_s, wpa_s->wnm_dialog_token,
-			wpa_s->reject_btm_req_reason,
+			wpa_s, wpa_s->reject_btm_req_reason,
 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 		return;
 	}
@@ -1541,15 +1489,34 @@
 	 * set to 1, and the BSS Termination Included field is set to 1, only
 	 * one of the links is removed and the other links remain associated.
 	 * Ignore the Disassociation Imminent field in such a case.
+	 *
+	 * TODO: We should check if the AP has more than one link.
+	 * TODO: We should pass the RX link and use that
 	 */
-	if (disassoc_imminent &&
-	    (wpa_s->valid_links & (wpa_s->valid_links - 1)) != 0 &&
+	if (disassoc_imminent && wpa_s->valid_links &&
 	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
 	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
-		wpa_printf(MSG_INFO,
-			   "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
-		disassoc_imminent = false;
+		/* If we still have a link, then just accept the request */
+		if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
+			wpa_printf(MSG_INFO,
+				   "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
+			disassoc_imminent = false;
+
+			wnm_send_bss_transition_mgmt_resp(
+				wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
+
+			return;
+		}
+
+		/* The last link is being removed (which must be the assoc link)
+		 */
 		wpa_s->wnm_link_removal = true;
+		os_memcpy(wpa_s->wnm_dissoc_addr,
+			  wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
+			  ETH_ALEN);
+	} else {
+		os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ?
+			  wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN);
 	}
 
 	if (disassoc_imminent) {
@@ -1566,7 +1533,6 @@
 		unsigned int valid_ms;
 
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
-		wnm_deallocate_memory(wpa_s);
 		wpa_s->wnm_neighbor_report_elements = os_calloc(
 			WNM_MAX_NEIGHBOR_REPORT,
 			sizeof(struct neighbor_report));
@@ -1614,8 +1580,7 @@
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Candidate list included bit is set, but no candidates found");
 			wnm_send_bss_transition_mgmt_resp(
-				wpa_s, wpa_s->wnm_dialog_token,
-				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+				wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
 				NULL);
 			return;
@@ -1625,8 +1590,7 @@
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Configuration prevents roaming (BSSID set)");
 			wnm_send_bss_transition_mgmt_resp(
-				wpa_s, wpa_s->wnm_dialog_token,
-				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+				wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
 				NULL);
 			return;
@@ -1643,35 +1607,21 @@
 		wpa_s->wnm_cand_valid_until.sec +=
 			wpa_s->wnm_cand_valid_until.usec / 1000000;
 		wpa_s->wnm_cand_valid_until.usec %= 1000000;
-		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
 
 		/*
-		 * Fetch the latest scan results from the kernel and check for
-		 * candidates based on those results first. This can help in
-		 * finding more up-to-date information should the driver has
-		 * done some internal scanning operations after the last scan
-		 * result update in wpa_supplicant.
-		 */
-		if (wnm_fetch_scan_results(wpa_s) > 0)
+		* Try fetching the latest scan results from the kernel.
+		* This can help in finding more up-to-date information should
+		* the driver have done some internal scanning operations after
+		* the last scan result update in wpa_supplicant.
+		*
+		* It is not a new scan, this does not update the last_scan
+		* timestamp nor will it expire old BSSs.
+		*/
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
+		if (wnm_scan_process(wpa_s, true) > 0)
 			return;
-
-		/*
-		 * Try to use previously received scan results, if they are
-		 * recent enough to use for a connection.
-		 */
-		if (wpa_s->last_scan_res_used > 0) {
-			struct os_reltime now;
-
-			os_get_reltime(&now);
-			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
-				wpa_printf(MSG_DEBUG,
-					   "WNM: Try to use recent scan results");
-				if (wnm_scan_process(wpa_s, 0) > 0)
-					return;
-				wpa_printf(MSG_DEBUG,
-					   "WNM: No match in previous scan results - try a new scan");
-			}
-		}
+		wpa_printf(MSG_DEBUG,
+			   "WNM: No valid match in previous scan results - try a new scan");
 
 		wnm_set_scan_freqs(wpa_s);
 		if (wpa_s->wnm_num_neighbor_report == 1) {
@@ -1694,12 +1644,45 @@
 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 		}
 		wnm_send_bss_transition_mgmt_resp(
-			wpa_s, wpa_s->wnm_dialog_token, status,
+			wpa_s, status,
 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 	}
 }
 
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+			   size_t data_len)
+{
+	const struct ieee80211_mgmt *frame =
+		(const struct ieee80211_mgmt *) data;
+
+	if (data_len <
+	    IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
+	    frame->u.action.category != WLAN_ACTION_WNM ||
+	    frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
+	    frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
+		return -1;
+
+	/*
+	 * If disassoc imminent bit was set in the request, the response may
+	 * indicate accept even if no candidate was found, so bail out here.
+	 */
+	if (!wpa_s->wnm_target_bss) {
+		wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
+		return 0;
+	}
+
+	if (!wpa_s->current_ssid)
+		return 0;
+
+	wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
+			   0);
+
+	wpa_s->wnm_target_bss = NULL;
+	return 0;
+}
+
+
 #define BTM_QUERY_MIN_SIZE	4
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
@@ -2052,14 +2035,14 @@
 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
 			      struct wpabuf *elems)
 {
-	wpabuf_free(wpa_s->coloc_intf_elems);
 	if (elems && wpabuf_len(elems) == 0) {
 		wpabuf_free(elems);
 		elems = NULL;
 	}
-	wpa_s->coloc_intf_elems = elems;
 
-	if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+	/* NOTE: The elements are not stored as they are only send out once */
+
+	if (wpa_s->conf->coloc_intf_reporting && elems &&
 	    wpa_s->coloc_intf_dialog_token &&
 	    (wpa_s->coloc_intf_auto_report == 1 ||
 	     wpa_s->coloc_intf_auto_report == 3)) {
@@ -2068,8 +2051,10 @@
 		 */
 		wnm_send_coloc_intf_report(wpa_s,
 					   wpa_s->coloc_intf_dialog_token,
-					   wpa_s->coloc_intf_elems);
+					   elems);
 	}
+
+	wpabuf_free(elems);
 }
 
 
@@ -2082,8 +2067,6 @@
 
 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;
 
@@ -2091,26 +2074,14 @@
 	 * 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");
+	if (wpa_s->wnm_link_removal ||
+	    !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) ||
+	    is_zero_ether_addr(bss->mld_addr)) {
+		if (ether_addr_equal(bss->bssid, wpa_s->wnm_dissoc_addr))
 			return true;
-		}
+	} else {
+		if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_dissoc_addr))
+			return true;
 	}
 
 	return false;
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 2a473db..235a838 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -65,19 +65,28 @@
 				       const char *btm_candidates,
 				       int cand_list);
 
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
 			       const struct wpabuf *elems);
 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
 			      struct wpabuf *elems);
-bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+			   size_t data_len);
 
 #ifdef CONFIG_WNM
 
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check);
 void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s);
 
+bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+
+void wnm_btm_reset(struct wpa_supplicant *wpa_s);
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+	return !!wpa_s->wnm_dialog_token;
+}
+
 #else /* CONFIG_WNM */
 
 static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
@@ -90,6 +99,21 @@
 {
 }
 
+static inline bool
+wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	return false;
+}
+
+static inline void wnm_btm_reset(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+	return false;
+}
+
 #endif /* CONFIG_WNM */
 
 #endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index b1334e2..60f8562 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -5129,7 +5129,7 @@
 
 	eloop_register_signal_terminate(wpa_cli_terminate, NULL);
 
-	if (ctrl_ifname == NULL)
+	if (!ctrl_ifname && !global)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
 	if (reconnect && action_file && ctrl_ifname) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 31a9af6..88f3f2a 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -187,7 +187,10 @@
 	int val;
 	size_t i;
 
-	res = iface->driver->get_scan_results2(iface->drv_priv);
+	if (iface->driver->get_scan_results)
+		res = iface->driver->get_scan_results(iface->drv_priv, NULL);
+	else
+		res = iface->driver->get_scan_results2(iface->drv_priv);
 	if (res == NULL)
 		goto fail;
 
@@ -231,7 +234,7 @@
 	if (iface->drv_priv == NULL)
 		return;
 
-	if (iface->driver->get_scan_results2)
+	if (iface->driver->get_scan_results || iface->driver->get_scan_results2)
 		wpa_priv_get_scan_results2(iface, from, fromlen);
 	else
 		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a851024..3fde0a8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -678,9 +678,7 @@
 	wpa_s->disallow_aps_ssid = NULL;
 
 	wnm_bss_keep_alive_deinit(wpa_s);
-#ifdef CONFIG_WNM
-	wnm_deallocate_memory(wpa_s);
-#endif /* CONFIG_WNM */
+	wnm_btm_reset(wpa_s);
 
 	ext_password_deinit(wpa_s->ext_pw);
 	wpa_s->ext_pw = NULL;
@@ -894,7 +892,7 @@
 			struct wpa_scan_results *scan_res;
 			wpa_s->bgscan_ssid = wpa_s->current_ssid;
 			scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
-								   0);
+								   0, NULL);
 			if (scan_res) {
 				bgscan_notify_scan(wpa_s, scan_res);
 				wpa_scan_results_free(scan_res);
@@ -1081,6 +1079,10 @@
 	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 		wpa_supplicant_start_autoscan(wpa_s);
 
+	if (state == WPA_COMPLETED || state == WPA_INTERFACE_DISABLED ||
+	    state == WPA_INACTIVE)
+		wnm_btm_reset(wpa_s);
+
 #ifndef CONFIG_NO_WMM_AC
 	if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
 		wmm_ac_notify_disassoc(wpa_s);
@@ -2522,6 +2524,7 @@
 #endif /* CONFIG_NO_WMM_AC */
 #ifdef CONFIG_WNM
 	wpa_s->wnm_mode = 0;
+	wpa_s->wnm_target_bss = NULL;
 #endif /* CONFIG_WNM */
 	wpa_s->reassoc_same_bss = 0;
 	wpa_s->reassoc_same_ess = 0;
@@ -2919,7 +2922,8 @@
 	if (obss_scan) {
 		struct wpa_scan_results *scan_res;
 
-		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0,
+							   NULL);
 		if (scan_res == NULL) {
 			/* Back to HT20 */
 			freq->sec_channel_offset = 0;
@@ -3083,7 +3087,7 @@
 				    freq->sec_channel_offset,
 				    chwidth, seg0, seg1, vht_caps,
 				    &mode->he_capab[ieee80211_mode],
-				    &mode->eht_capab[ieee80211_mode]) != 0)
+				    &mode->eht_capab[ieee80211_mode], 0) != 0)
 		return false;
 
 	*freq = vht_freq;
@@ -3831,10 +3835,14 @@
 
 	if (ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
+		struct multi_ap_params multi_ap = { 0 };
+
+		multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+		multi_ap.profile = ssid->multi_ap_profile;
 
 		multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len,
 						  max_wpa_ie_len - wpa_ie_len,
-						  MULTI_AP_BACKHAUL_STA);
+						  &multi_ap);
 		if (multi_ap_ie_len == 0) {
 			wpa_printf(MSG_ERROR,
 				   "Multi-AP: Failed to build Multi-AP IE");
@@ -9173,8 +9181,7 @@
 		if (modes[i].mode != mode ||
 		    !modes[i].num_channels || !modes[i].channels)
 			continue;
-		if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) ||
-		    (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq)))
+		if (is_6ghz == modes[i].is_6ghz)
 			return &modes[i];
 	}
 
@@ -9413,17 +9420,21 @@
 
 
 struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s)
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
 #ifdef CONFIG_TESTING_OPTIONS
 	size_t idx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	if (!wpa_s->driver->get_scan_results2)
+	if (wpa_s->driver->get_scan_results)
+		scan_res = wpa_s->driver->get_scan_results(wpa_s->drv_priv,
+							   bssid);
+	else if (wpa_s->driver->get_scan_results2)
+		scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+	else
 		return NULL;
 
-	scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
 
 #ifdef CONFIG_TESTING_OPTIONS
 	for (idx = 0; scan_res && idx < scan_res->num; idx++) {
@@ -9461,10 +9472,7 @@
 	if (!wpa_s->valid_links)
 		return false;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		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 d689441..89c5d03 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1717,6 +1717,12 @@
 # support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be
 # added to a bridge to allow forwarding frames over this backhaul link.
 
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 3a5f61c..0fea5cc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1327,14 +1327,14 @@
 	u8 wnm_num_neighbor_report;
 	u8 wnm_mode;
 	bool wnm_link_removal;
+	u8 wnm_dissoc_addr[ETH_ALEN];
 	u16 wnm_dissoc_timer;
 	u8 wnm_bss_termination_duration[12];
 	struct neighbor_report *wnm_neighbor_report_elements;
 	struct os_reltime wnm_cand_valid_until;
-	u8 wnm_cand_from_bss[ETH_ALEN];
+	struct wpa_bss *wnm_target_bss;
 	enum bss_trans_mgmt_status_code bss_tm_status;
 	bool bss_trans_mgmt_in_progress;
-	struct wpabuf *coloc_intf_elems;
 	u8 coloc_intf_dialog_token;
 	u8 coloc_intf_auto_report;
 	u8 coloc_intf_timeout;
@@ -1500,6 +1500,9 @@
 	int dpp_netrole;
 	int dpp_auth_ok_on_ack;
 	int dpp_in_response_listen;
+	bool dpp_tx_auth_resp_on_roc_stop;
+	bool dpp_tx_chan_change;
+	bool dpp_listen_on_tx_expire;
 	int dpp_gas_client;
 	int dpp_gas_server;
 	int dpp_gas_dialog_token;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 111680c..2fea640 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -465,7 +465,7 @@
 
 	/* No WPA/RSN IE found in the cached scan results. Try to get updated
 	 * scan results from the driver. */
-	if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+	if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
 		return -1;
 
 	return wpa_get_beacon_ie(wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 81e11e7..f103237 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1208,14 +1208,19 @@
 		}
 	}
 #endif /* CONFIG_P2P */
-	os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
-		    multi_ap_backhaul_sta ? " multi_ap=1" : "");
+	if (multi_ap_backhaul_sta)
+		os_snprintf(phase1, sizeof(phase1), "pbc=1 multi_ap=%d",
+			    multi_ap_backhaul_sta);
+	else
+		os_snprintf(phase1, sizeof(phase1), "pbc=1");
 	if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
 		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
-	if (multi_ap_backhaul_sta)
+	if (multi_ap_backhaul_sta) {
 		ssid->multi_ap_backhaul_sta = 1;
+		ssid->multi_ap_profile = multi_ap_backhaul_sta;
+	}
 	wpa_s->supp_pbc_active = true;
 	wpa_s->wps_overlap = false;
 	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);