[wpa_supplicant] Cumulative patch from commit 61c6e7c62

Bug: 245752074
Test: connect/disconnect to WPA2, WPA3 networks
Test: SoftAp & p2p connection
Test: Regression test(b/247129565)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source

61c6e7c62 nl80211: Parsing of MLO connection info from roam+auth event
3d842d910 MLD STA: Add support for parsing MLO KDEs
e1105bab8 Add QCA vendor interface support for Spatial Reuse (SR) feature
1f39f85b7 Fix compiler warning on shift overflow in QCA vendor definitions
c5b950b6a Fix compilation error due to use of bitwise '&' with boolean operands
7bdd3f2aa P2P: Allow P2P CSA from 5 GHz to 2.4 GHz band
a7684a21c Update hw mode after ACS selects the channel
768537921 P2P: Set operating class along with operating channel width
ee7eec518 P2P: Fix the issue in setting optimized listen channel
01944c095 Fix RNR BSSID setting for own interfaces
3a7fe1e21 dbus: Omit FT key mgmt capabilities without CONFIG_IEEE80211R
d2caf6ef5 dbus: Fix property DebugShowKeys and DebugTimestamp
a17f9a2d4 Add usage print for -q flag
f77c0f914 ACS: Include frequency in info messages
0c7b3814c Use a less generic name for IEEE802.11 CRC-32 routine
7ed17eee3 ACS: Don't select indoor channel on outdoor operation
1f795df7a wpa_supplicant man page missing -I flag
ef2d2e81a Add a new QCA vendor attribute to support flexible TWT
1b6f3b585 MLD STA: Indicate per link channel switch
b7f98d92d MLD STA: Add per-link MLO signal poll
28b2256da MLD STA: Add MLO_STATUS control interface command
8dd5b9a9e nl80211: Send bssid info as parameter to nl80211_get_link_signal()
6ca98040a MLD STA: Indicate AP MLD address in STATUS command
22ba81cfe MLD STA: Indicate AP MLD address in CTRL-EVENT-CONNECTED
db99e7341 Add AP MLD address into BSS command output
4bd316524 bss: Parse and store MLD address of the AP from Multi-Link element
cc29cadf2 Helper function to get MLD address from Basic Multi-Link element
9a8bd7be0 scan: Helper function to get ML IE of specified type from scan result IEs
e21128614 nl80211: Use AP MLD address to set supplicant port as authorized
7784964cb MLD STA: Fetch MLO connection info into core wpa_supplicant
e2147f917 nl80211: Indicate MLO connection info in driver status
c7741009f nl80211: Parse MLO connection info in NL80211_CMD_CONNECT event
bd499f0e6 Add a helper function to get ML IE of specified type from IEs buffer
36645f839 EHT: Add Multi-Link element field definitions
62612dfc1 P2P: Use only PSC for P2P group in the 6 GHz band
7974d8053 Configure RRM elements to the driver in the driver-AP-SME case
d1a7626ff SAE: Use correct SSID profile for SAE auth retries during external auth
096feac19 SAE: Include the new -EXT-KEY AKMs in external auth case
76793cbbc SAE: Make sure H2E PT is derived for external auth SSID profile
122cdd592 Enable TWT responder AP role only if IEEE 802.11ax/HE is enabled
ed442e8dc Add p2p_optimize_listen_chan=1 to default Android template
5b76c3654 Extend attributes of QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO
897e09085 QCA vendor attribute to configure periodic sounding
0cc6f985d wlantest: Recognize additional not-Robust Action categories
cd392151c Validate MAC Address KDE length in the parser
6f8af5974 Fix expiration logic for the first PTKSA cache entry
85e28a79b PASN: Set secure ranging context to driver after association
9b62b61c6 PASN: Configure secure ranging context to the driver in AP mode
de3b91a17 nl80211: Define vendor interface functions to offload PASN authentication
edd89d6db PASN: Set keys from PTKSA cache for a valid PTKSA entry
e2c3cdf2c nl80211: Register PASN Authentication frames for SME-in-driver
58a96187e nl80211: Allow PASN Authentication frames with random (foreign) MAC address
24929543b PASN: Deauthenticate on PTKSA cache entry expiration
74d894a2e PASN: Offload handshake for multiple peers from the driver
06317f5e3 PASN: Add driver operation to set secure ranging context and PASN response
2edebc6b6 PASN: Add interface to handle PASN request from the driver
9330bf318 PASN: Add function to compute LTF keyseed from KDK
9391f7100 Add own MAC address in PASN supplicant start and deauthentication
580bd04cf Add own MAC address used for key derivation to PTKSA cache
d0d585c48 Store secure ranging driver capabilities in WPA state machine
96a604128 Use separate PASN capabilities for AP and STA modes
909fa448e EAPOL: Update PMK length in EAPOL callback to support longer keys
c80dc6940 OpenSSL: Include rsa.h for all OpenSSL versions
723eb4f38 P2P: Fix a typo in a comment about removing 6 GHz channels
e9627f8c3 P2P: Skip 6 GHz band directly if 6 GHz P2P is disabled
03f7f633a Fix wrong AKM priority for FILS
1f9a988f1 DPP3: Do not initiate PKEX for PB if no configuration is available
aa75aa1dc Add QCA vendor interface to get SAR capabilities to userspace
5de45546d Add support to send multi AKM connect request when driver's SME in use
0ce1545dc nl80211: Determine maximum number of supported AKMs
48c620829 Update PSK after cross AKM roaming to WPA-PSK when driver's SME in use
7e97c619a Sync with wireless-next.git include/uapi/linux/nl80211.h
54706957e DPP: Fix DPP_RELAY_ADD_CONTROLLER command parsing
44b26d82b nl80211: Silence a compiler warning on printf in 32-bit builds
4ae14deee DPP3: Use chirping channel list in PB discovery
c58be1d8f DPP: Channel list generation for presence announcement to helper funcion
820211245 OpenSSL: Fix HPKE in some corner cases
57968faea DPP: Do not discard network introduction frames in test mode
d72302c6b DPP: Do not use 6 GHz channels for push button
89de431f2 DPP: Add config response status value to DPP-CONF-SENT
80d5e264c Enhance QCA vendor roam event to indicate MLO links after reassociation
662249306 Update copyright notices for the QCA vendor definitions
1d08b238c nl80211: Allow more time for the initial scan with 6 GHz
faf9c04cb Remove a host of unnecessary OPENSSL_IS_BORINGSSL ifdefs
b9cd5a82f Always process pending QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH data
ef4cd8e33 QoS: Use common classifier_mask for ipv4/ipv6
93be02592 Add fixed FDD mode to qca_btc_chain_mode QCA vendor attribute
5565fbee2 DPP: Check Enrollee supported curves when building Config Response
4cfb484e9 DPP: Allow dpp_controller_start without arguments in CLIs
c97000933 Fix ifdef condition for imsi_privacy_cert
f70db167a SAE: Derive a variable length PMK with the new AKM suites
91010e6f6 SAE: Indicate AKM suite selector in commit for new AKM suites
e81ec0962 SAE: Use H2E unconditionally with the new AKM suites
f8eed2e8b SAE: Store PMK length and AKM in SAE data
9dc4e9d13 SAE: EAPOL-Key and key/MIC length information for the new AKM suites
a32ef3cfb SAE: Driver capability flags for the new SAE AKM suites
91df8c9c6 SAE: Internal WPA_KEY_MGMT_* defines for extended key AKMs
5c8a714b1 SAE: Use wpa_key_mgmt_sae() helper
5456b0f26 Define new RSN AKM suite selector values
def33101c DPP: Clear push button announcement state on wpa_supplicant FLUSH
d22dfe918 DPP: Event message for indicating when Relay would need a Controller
bfe3cfc38 DPP: Allow Relay connections to Controllers to be added and removed
808834b18 Add a comparison function for hostapd_ip_addr
f7763880b DPP: Advertise Configurator connectivity on Relay automatically
ca682f80a DPP: Dynamic Controller initiated connection on Relay
d2388bcca DPP: Strict validation of PKEX peer bootstrapping key during auth
a7b8cef8b DPP3: Fix push button boostrapping key passing through PKEX
69d7c8e6b DPP: Add peer=id entry for PKEX-over-TCP case
1ff9251a8 DPP3: Push button Configurator in wpa_supplicant
e9137950f DPP: Recognize own PKEX Exchange Request if it ends up being received
692956446 DPP: Note PKEX code/identifier deletion in debug log
ae4a3a6f6 DPP: Add DPP-CONF-REQ-RX event for Controller
fb2937b85 DPP: Allow Controller to initiate PKEX through Relay
15af83cf1 DPP: Delete PKEX code and identifier on success completion of PKEX
479e412a6 DPP3: Default value for dpp_connector_privacy
148de3e0d DPP3: Private Peer Introduction protocol
786ea402b HPKE base mode with single-shot API
f0273bc81 OpenSSL: Remove a forgotten debug print
68209ddbe DPP: Allow 3rd party information to be added into config object
0e2217c95 DPP: Allow 3rd party information to be added into config request obj
3d82fbe05 Add QCA vendor subcommand and attributes for SCS rule configuration
16b62ddfa QCA vendor attribute for DBAM configuration
451ede2c3 DPP: Allow AP/Relay to be configured to listed for new TCP connections
7bbe85987 DPP3: Allow external configuration to be specified on AP for PB
8db786a43 DPP3: Testing functionality for push button announcements
37bccfcab DPP3: Push button bootstrap mechanism
a0054fe7c Add AP and STA specific P802.11az security capabilities (vendor command)
159e63613 QCA vendor command for CoAP offload processing
3b7bb17f6 Add QCA vendor attribute for TIM beacon statistics

Change-Id: Ic5faae10839f317cc70a4df7a3f2047812ffd34c
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index bee4fe7..16d6c90 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -176,9 +176,8 @@
 		conf, conf->channel + conf->secondary_channel * 2);
 	hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_USE_HT);
 	ieee80211_freq_to_channel_ext(ssid->frequency, 0,
-		   conf->vht_oper_chwidth,
-		   &conf->op_class,
-		   &conf->channel);
+				      conf->vht_oper_chwidth,
+				      &conf->op_class, &conf->channel);
 }
 
 
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index eb97a61..a3da86c 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -379,6 +379,8 @@
 
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
+	int i;
+
 	if (bss == wpa_s->current_bss)
 		return 1;
 
@@ -388,9 +390,23 @@
 		       bss->ssid_len) != 0))
 		return 0; /* SSID has changed */
 
-	return !is_zero_ether_addr(bss->bssid) &&
-		(os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
+	if (!is_zero_ether_addr(bss->bssid) &&
+	    (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+	     os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0))
+		return 1;
+
+	if (!wpa_s->valid_links)
+		return 0;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
 }
 
 
@@ -441,7 +457,11 @@
 				    struct os_reltime *fetch_time)
 {
 	struct wpa_bss *bss;
-	char extra[50];
+	char extra[100];
+	const u8 *ml_ie;
+	char *pos, *end;
+	int ret = 0;
+	const u8 *mld_addr;
 
 	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
 	if (bss == NULL)
@@ -456,6 +476,14 @@
 	os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
 	wpa_bss_set_hessid(bss);
 
+	os_memset(bss->mld_addr, 0, ETH_ALEN);
+	ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (ml_ie) {
+		mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
+		if (mld_addr)
+			os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
+	}
+
 	if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
 	    wpa_bss_remove_oldest(wpa_s) != 0) {
 		wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
@@ -467,11 +495,21 @@
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
 	wpa_s->num_bss++;
+
+	extra[0] = '\0';
+	pos = extra;
+	end = pos + sizeof(extra);
 	if (!is_zero_ether_addr(bss->hessid))
-		os_snprintf(extra, sizeof(extra), " HESSID " MACSTR,
-			    MAC2STR(bss->hessid));
-	else
-		extra[0] = '\0';
+		ret = os_snprintf(pos, end - pos, " HESSID " MACSTR,
+				  MAC2STR(bss->hessid));
+
+	if (!is_zero_ether_addr(bss->mld_addr) &&
+	    !os_snprintf_error(end - pos, ret)) {
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, " MLD ADDR " MACSTR,
+				  MAC2STR(bss->mld_addr));
+	}
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
 		" SSID '%s' freq %d%s",
 		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
@@ -708,8 +746,19 @@
 		}
 		dl_list_add(prev, &bss->list_id);
 	}
-	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+	if (changes & WPA_BSS_IES_CHANGED_FLAG) {
+		const u8 *ml_ie, *mld_addr;
+
 		wpa_bss_set_hessid(bss);
+		os_memset(bss->mld_addr, 0, ETH_ALEN);
+		ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
+		if (ml_ie) {
+			mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
+							  ml_ie[1] - 1);
+			if (mld_addr)
+				os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
+		}
+	}
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 
 	notify_bss_changes(wpa_s, changes, bss);
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 146aaee..b68fc87 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -122,6 +122,8 @@
 	size_t ie_len;
 	/** Length of the following Beacon IE field in octets */
 	size_t beacon_ie_len;
+	/** MLD address of the AP */
+	u8 mld_addr[ETH_ALEN];
 	/* followed by ie_len octets of IEs */
 	/* followed by beacon_ie_len octets of IEs */
 	u8 ies[];
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index aa775ca..065b6fb 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -804,8 +804,12 @@
 #ifdef CONFIG_SAE
 		else if (os_strcmp(start, "SAE") == 0)
 			val |= WPA_KEY_MGMT_SAE;
+		else if (os_strcmp(start, "SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_SAE_EXT_KEY;
 		else if (os_strcmp(start, "FT-SAE") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE;
+		else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_HS20
 		else if (os_strcmp(start, "OSEN") == 0)
@@ -1004,6 +1008,16 @@
 		pos += ret;
 	}
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sSAE-EXT-KEY",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
 	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
 		ret = os_snprintf(pos, end - pos, "%sFT-SAE",
 				  pos == buf ? "" : " ");
@@ -1013,6 +1027,16 @@
 		}
 		pos += ret;
 	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sFT-SAE-EXT-KEY",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_HS20
@@ -2630,6 +2654,7 @@
 	{ STR_LEN(dpp_csign) },
 	{ STR_LEN(dpp_pp_key) },
 	{ INT_RANGE(dpp_pfs, 0, 2) },
+	{ INT_RANGE(dpp_connector_privacy, 0, 1) },
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(owe_group, 0, 65535) },
 	{ INT_RANGE(owe_only, 0, 1) },
@@ -2970,6 +2995,8 @@
 #endif /* CONFIG_MBO */
 	os_free(config->dpp_name);
 	os_free(config->dpp_mud_url);
+	os_free(config->dpp_extra_conf_req_name);
+	os_free(config->dpp_extra_conf_req_value);
 
 	os_free(config);
 }
@@ -5302,6 +5329,9 @@
 	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
 	{ STR(dpp_name), 0 },
 	{ STR(dpp_mud_url), 0 },
+	{ STR(dpp_extra_conf_req_name), 0 },
+	{ STR(dpp_extra_conf_req_value), 0 },
+	{ INT_RANGE(dpp_connector_privacy_default, 0, 1), 0 },
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
 	{ INT_RANGE(bss_no_flush_when_down, 0, 1), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index f33618c..a81e9eb 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1627,6 +1627,25 @@
 	char *dpp_mud_url;
 
 	/**
+	 * dpp_extra_conf_req_name - JSON node name of additional data for
+	 * Enrollee's DPP Configuration Request
+	 */
+	char *dpp_extra_conf_req_name;
+
+	/**
+	 * dpp_extra_conf_req_value - JSON node data of additional data for
+	 * Enrollee's DPP Configuration Request
+	 */
+	char *dpp_extra_conf_req_value;
+
+	/* dpp_connector_privacy_default - Default valur for Connector privacy
+	 *
+	 * This value is used as the default for the dpp_connector_privacy
+	 * network parameter for all new networks provisioned using DPP.
+	 */
+	int dpp_connector_privacy_default;
+
+	/**
 	 * coloc_intf_reporting - Colocated interference reporting
 	 *
 	 * dot11CoLocIntfReportingActivated
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 3b9f60b..260df8f 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -833,6 +833,7 @@
 	STR(dpp_csign);
 	STR(dpp_pp_key);
 	INT(dpp_pfs);
+	INT(dpp_connector_privacy);
 #endif /* CONFIG_DPP */
 	INT(owe_group);
 	INT(owe_only);
@@ -1543,6 +1544,19 @@
 	if (config->dpp_config_processing)
 		fprintf(f, "dpp_config_processing=%d\n",
 			config->dpp_config_processing);
+	if (config->dpp_name)
+		fprintf(f, "dpp_name=%s\n", config->dpp_name);
+	if (config->dpp_mud_url)
+		fprintf(f, "dpp_mud_url=%s\n", config->dpp_mud_url);
+	if (config->dpp_extra_conf_req_name)
+		fprintf(f, "dpp_extra_conf_req_name=%s\n",
+			config->dpp_extra_conf_req_name);
+	if (config->dpp_extra_conf_req_value)
+		fprintf(f, "dpp_extra_conf_req_value=%s\n",
+			config->dpp_extra_conf_req_value);
+	if (config->dpp_connector_privacy_default)
+		fprintf(f, "dpp_connector_privacy_default=%d\n",
+			config->dpp_connector_privacy_default);
 	if (config->coloc_intf_reporting)
 		fprintf(f, "coloc_intf_reporting=%d\n",
 			config->coloc_intf_reporting);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index f1f6cc4..adc1a06 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1064,6 +1064,14 @@
 	int dpp_pfs_fallback;
 
 	/**
+	 * dpp_connector_privacy - Network introduction type
+	 * 0: unprotected variant from DPP R1
+	 * 1: privacy protecting (station Connector encrypted) variant from
+	 *    DPP R3
+	 */
+	int dpp_connector_privacy;
+
+	/**
 	 * owe_group - OWE DH Group
 	 *
 	 * 0 = use default (19) first and then try all supported groups one by
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 8cc02a6..7118cab 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2385,6 +2385,14 @@
 		return pos - buf;
 	pos += ret;
 
+	if (wpa_s->valid_links) {
+		ret = os_snprintf(pos, end - pos, "ap_mld_addr=" MACSTR "\n",
+				  MAC2STR(wpa_s->ap_mld_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 #ifdef CONFIG_HS20
 	if (wpa_s->current_bss &&
 	    (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
@@ -2539,12 +2547,21 @@
 					  wpa_s->current_ssid->ssid_len) : "");
 		if (wpa_s->wpa_state == WPA_COMPLETED) {
 			struct wpa_ssid *ssid = wpa_s->current_ssid;
+			char mld_addr[50];
+
+			mld_addr[0] = '\0';
+			if (wpa_s->valid_links)
+				os_snprintf(mld_addr, sizeof(mld_addr),
+					    " ap_mld_addr=" MACSTR,
+					    MAC2STR(wpa_s->ap_mld_addr));
+
 			wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
 				     "- connection to " MACSTR
-				     " completed %s [id=%d id_str=%s]",
+				     " completed %s [id=%d id_str=%s]%s",
 				     MAC2STR(wpa_s->bssid), "(auth)",
 				     ssid ? ssid->id : -1,
-				     ssid && ssid->id_str ? ssid->id_str : "");
+				     ssid && ssid->id_str ? ssid->id_str : "",
+				     mld_addr);
 		}
 	}
 #endif /* ANDROID */
@@ -2818,6 +2835,13 @@
 			return pos;
 		pos += ret;
 	}
+	if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sSAE-EXT-KEY",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
 #ifdef CONFIG_IEEE80211R
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
@@ -2840,6 +2864,13 @@
 			return pos;
 		pos += ret;
 	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sFT/SAE-EXT-KEY",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
 #endif /* CONFIG_IEEE80211R */
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
@@ -3238,7 +3269,8 @@
 		return -1;
 	}
 	if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
-	    ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE_EXT_KEY) {
 		wpa_printf(MSG_ERROR,
 			   "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
 		return -1;
@@ -4320,6 +4352,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, " FT-SAE-EXT-KEY");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_SHA384
 	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) {
@@ -4337,6 +4375,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, " SAE-EXT-KEY");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_SHA256
 	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) {
@@ -5399,6 +5443,15 @@
 	}
 #endif /* CONFIG_FILS */
 
+	if (!is_zero_ether_addr(bss->mld_addr)) {
+		ret = os_snprintf(pos, end - pos,
+				  "ap_mld_addr=" MACSTR "\n",
+				  MAC2STR(bss->mld_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -8443,6 +8496,19 @@
 	wpas_dpp_chirp_stop(wpa_s);
 	wpa_s->dpp_pfs_fallback = 0;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	{
+		int i;
+
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			struct dpp_pb_info *info;
+
+			info = &wpa_s->dpp_pb[i];
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+		}
+	}
+#endif /* CONFIG_DPP3 */
 #ifdef CONFIG_TESTING_OPTIONS
 	os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN);
 	os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN);
@@ -10832,6 +10898,8 @@
 #ifdef CONFIG_SAE
 		} else if (os_strcmp(token, "akmp=SAE") == 0) {
 			akmp = WPA_KEY_MGMT_SAE;
+		} else if (os_strcmp(token, "akmp=SAE-EXT-KEY") == 0) {
+			akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 		} else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) {
@@ -10874,8 +10942,8 @@
 		goto out;
 	}
 
-	ret = wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id,
-				   comeback, comeback_len);
+	ret = wpas_pasn_auth_start(wpa_s, wpa_s->own_addr, bssid, akmp, cipher,
+				   group, id, comeback, comeback_len);
 out:
 	os_free(comeback);
 	return ret;
@@ -10893,7 +10961,7 @@
 		return -1;
 	}
 
-	return wpas_pasn_deauthenticate(wpa_s, bssid);
+	return wpas_pasn_deauthenticate(wpa_s, wpa_s->own_addr, bssid);
 }
 
 #endif /* CONFIG_PASN */
@@ -11474,6 +11542,117 @@
 }
 
 
+static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
+					   char *buf, size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+	struct wpa_mlo_signal_info mlo_si;
+
+	if (!wpa_s->valid_links)
+		return -1;
+
+	ret = wpa_drv_mlo_signal_poll(wpa_s, &mlo_si);
+	if (ret)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_si.valid_links & BIT(i)))
+			continue;
+
+		ret = os_snprintf(pos, end - pos,
+				  "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%d\n"
+				  "NOISE=%d\nFREQUENCY=%u\n",
+				  i, mlo_si.links[i].current_signal,
+				  mlo_si.links[i].current_txrate / 1000,
+				  mlo_si.links[i].current_noise,
+				  mlo_si.links[i].frequency);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+
+		if (mlo_si.links[i].chanwidth != CHAN_WIDTH_UNKNOWN) {
+			ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
+					  channel_width_to_string(
+						  mlo_si.links[i].chanwidth));
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].center_frq1 > 0) {
+			ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
+					  mlo_si.links[i].center_frq1);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].center_frq2 > 0) {
+			ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
+					  mlo_si.links[i].center_frq2);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].avg_signal) {
+			ret = os_snprintf(pos, end - pos,
+					  "AVG_RSSI=%d\n",
+					  mlo_si.links[i].avg_signal);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].avg_beacon_signal) {
+			ret = os_snprintf(pos, end - pos,
+					  "AVG_BEACON_RSSI=%d\n",
+					  mlo_si.links[i].avg_beacon_signal);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
+				      char *buf, size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+
+	if (!wpa_s->valid_links)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
+				  "ap_link_addr=" MACSTR
+				  "\nsta_link_addr=" MACSTR "\n",
+				  i, wpa_s->links[i].freq,
+				  MAC2STR(wpa_s->links[i].bssid),
+				  MAC2STR(wpa_s->links[i].addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -12455,6 +12634,14 @@
 		if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0)
 			reply_len = -1;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	} else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) {
+		if (wpas_dpp_push_button(wpa_s, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) {
+		if (wpas_dpp_push_button(wpa_s, buf + 15) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 	} else if (os_strncmp(buf, "MSCS ", 5) == 0) {
 		if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
@@ -12480,6 +12667,12 @@
 	} else if (os_strncmp(buf, "DSCP_QUERY ", 11) == 0) {
 		if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
+		reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
+						       reply_size);
+	} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
+		reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
+							    reply_size);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 0b1002b..26f7738 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -908,8 +908,10 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
+	dbus_bool_t b = wpa_debug_timestamp ? TRUE : FALSE;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
-						&wpa_debug_timestamp, error);
+						&b, error);
 
 }
 
@@ -927,8 +929,10 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
+	dbus_bool_t b = wpa_debug_show_keys ? TRUE : FALSE;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
-						&wpa_debug_show_keys, error);
+						&b, error);
 
 }
 
@@ -3131,12 +3135,16 @@
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-eap") ||
-			    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
-			     !wpa_dbus_dict_string_array_add_element(
-				     &iter_array, "wpa-ft-eap")))
+				    &iter_array, "wpa-eap"))
 				goto nomem;
 
+#ifdef CONFIG_IEEE80211R
+			if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-eap"))
+				goto nomem;
+#endif /* CONFIG_IEEE80211R */
+
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "wpa-eap-sha256"))
@@ -3146,12 +3154,16 @@
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-psk") ||
-			    ((capa.key_mgmt &
+				    &iter_array, "wpa-psk"))
+				goto nomem;
+
+#ifdef CONFIG_IEEE80211R
+			if ((capa.key_mgmt &
 			      WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
 			     !wpa_dbus_dict_string_array_add_element(
-				     &iter_array, "wpa-ft-psk")))
+				     &iter_array, "wpa-ft-psk"))
 				goto nomem;
+#endif /* CONFIG_IEEE80211R */
 
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 			if (!wpa_dbus_dict_string_array_add_element(
@@ -5095,7 +5107,7 @@
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
 	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
-	const char *key_mgmt[16]; /* max 16 key managements may be supported */
+	const char *key_mgmt[18]; /* max 18 key managements may be supported */
 	int n;
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -5145,8 +5157,12 @@
 #ifdef CONFIG_SAE
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_SAE)
 		key_mgmt[n++] = "sae";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+		key_mgmt[n++] = "sae-ext-key";
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE)
 		key_mgmt[n++] = "ft-sae";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		key_mgmt[n++] = "ft-sae-ext-key";
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_OWE
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_OWE)
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 02012d1..9a7d601 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -2,7 +2,7 @@
 
 <refentry>
   <refentryinfo>
-    <date>07 August 2019</date>
+    <date>13 September 2022</date>
   </refentryinfo>
 
   <refmeta>
@@ -22,6 +22,7 @@
       <arg>-D<replaceable>driver</replaceable></arg>
       <arg>-P<replaceable>PID_file</replaceable></arg>
       <arg>-f<replaceable>output file</replaceable></arg>
+      <arg>-I<replaceable>additional config file</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
   <refsect1>
@@ -341,6 +342,13 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-I filename</term>
+	<listitem>
+          <para>Path to additional configuration file.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-d</term>
 	<listitem>
 	  <para>Increase debugging verbosity (<option>-dd</option> even
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 60342cd..55dcd70 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -17,6 +17,7 @@
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/gas_server.h"
+#include "crypto/random.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "wpa_supplicant_i.h"
@@ -907,6 +908,9 @@
 	if (tcp)
 		return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port,
 				    wpa_s->conf->dpp_name, DPP_NETROLE_STA,
+				    wpa_s->conf->dpp_mud_url,
+				    wpa_s->conf->dpp_extra_conf_req_name,
+				    wpa_s->conf->dpp_extra_conf_req_value,
 				    wpa_s, wpa_s, wpas_dpp_process_conf_obj,
 				    wpas_dpp_tcp_msg_sent);
 #endif /* CONFIG_DPP2 */
@@ -1152,6 +1156,21 @@
 		return;
 	}
 
+	if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+				"No matching peer bootstrapping key found for PKEX - ignore message");
+			return;
+		}
+
+		if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+				"Mismatching peer PKEX bootstrapping key - ignore message");
+			return;
+		}
+	}
+
 	if (wpa_s->dpp_auth) {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
 			"Already in DPP authentication exchange - ignore new one");
@@ -1277,6 +1296,9 @@
 		ssid->dpp_connector = os_strdup(conf->connector);
 		if (!ssid->dpp_connector)
 			goto fail;
+
+		ssid->dpp_connector_privacy =
+			wpa_s->conf->dpp_connector_privacy_default;
 	}
 
 	if (conf->c_sign_key) {
@@ -1616,6 +1638,14 @@
 			conf->server_name);
 #endif /* CONFIG_DPP2 */
 
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "success");
+		wpa_s->dpp_pb_result_indicated = true;
+	}
+
+#endif /* CONFIG_DPP3 */
+
 	return wpas_dpp_process_config(wpa_s, auth, conf);
 }
 
@@ -1842,7 +1872,9 @@
 	buf = dpp_build_conf_req_helper(auth, wpa_s->conf->dpp_name,
 					wpa_s->dpp_netrole,
 					wpa_s->conf->dpp_mud_url,
-					supp_op_classes);
+					supp_op_classes,
+					wpa_s->conf->dpp_extra_conf_req_name,
+					wpa_s->conf->dpp_extra_conf_req_value);
 	os_free(supp_op_classes);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
@@ -2029,6 +2061,42 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static bool wpas_dpp_pb_active(struct wpa_supplicant *wpa_s)
+{
+	return (wpa_s->dpp_pb_time.sec || wpa_s->dpp_pb_time.usec) &&
+		wpa_s->dpp_pb_configurator;
+}
+
+
+static void wpas_dpp_remove_pb_hash(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	if (!wpa_s->dpp_pb_bi)
+		return;
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		struct dpp_pb_info *info = &wpa_s->dpp_pb[i];
+
+		if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+			continue;
+		if (os_memcmp(info->hash, wpa_s->dpp_pb_resp_hash,
+			      SHA256_MAC_LEN) == 0) {
+			/* Allow a new push button session to be established
+			 * immediately without the successfully completed
+			 * session triggering session overlap. */
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+		}
+	}
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src,
 				    const u8 *hdr, const u8 *buf, size_t len)
 {
@@ -2069,7 +2137,8 @@
 		int freq;
 
 		wpa_msg(wpa_s, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+			DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+			auth->conf_resp_status);
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
 		wpas_notify_dpp_config_accepted(wpa_s);
 		eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout,
@@ -2090,7 +2159,8 @@
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 	if (status == DPP_STATUS_OK) {
-		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+			auth->conf_resp_status);
 		wpas_notify_dpp_config_sent(wpa_s);
 	}
 	else {
@@ -2100,6 +2170,20 @@
 	dpp_auth_deinit(auth);
 	wpa_s->dpp_auth = NULL;
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
+		if (status == DPP_STATUS_OK)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"no-configuration-available");
+		wpa_s->dpp_pb_result_indicated = true;
+		if (status == DPP_STATUS_OK)
+			wpas_dpp_remove_pb_hash(wpa_s);
+		wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2716,19 +2800,38 @@
 }
 
 
+static void wpas_dpp_pkex_clear_code(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp_pkex_code && !wpa_s->dpp_pkex_identifier)
+		return;
+
+	/* Delete PKEX code and identifier on successful completion of
+	 * PKEX. We are not supposed to reuse these without being
+	 * explicitly requested to perform PKEX again. */
+	wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = NULL;
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+
+}
+
+
 #ifdef CONFIG_DPP2
 static int wpas_dpp_pkex_done(void *ctx, void *conn,
 			      struct dpp_bootstrap_info *peer_bi)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	const char *cmd = wpa_s->dpp_pkex_auth_cmd;
+	char cmd[500];
 	const char *pos;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	struct dpp_bootstrap_info *own_bi = NULL;
 	struct dpp_authentication *auth;
 
-	if (!cmd)
-		cmd = "";
+	wpas_dpp_pkex_clear_code(wpa_s);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
 	wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
 		   cmd);
 
@@ -2776,7 +2879,11 @@
 	}
 
 	return dpp_tcp_auth(wpa_s->dpp, conn, auth, wpa_s->conf->dpp_name,
-			    DPP_NETROLE_STA, wpas_dpp_process_conf_obj,
+			    DPP_NETROLE_STA,
+			    wpa_s->conf->dpp_mud_url,
+			    wpa_s->conf->dpp_extra_conf_req_name,
+			    wpa_s->conf->dpp_extra_conf_req_value,
+			    wpas_dpp_process_conf_obj,
 			    wpas_dpp_tcp_msg_sent);
 }
 #endif /* CONFIG_DPP2 */
@@ -2797,7 +2904,8 @@
 	wpa_s->dpp_pkex = NULL;
 	pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr,
 			     wpa_s->dpp_pkex_identifier,
-			     wpa_s->dpp_pkex_code, v2);
+			     wpa_s->dpp_pkex_code, wpa_s->dpp_pkex_code_len,
+			     v2);
 	if (!pkex)
 		return -1;
 	pkex->forced_ver = ver != PKEX_VER_AUTO;
@@ -2956,6 +3064,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP2
+	if (dpp_controller_is_own_pkex_req(wpa_s->dpp, buf, len)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX Exchange Request is from local Controller - ignore request");
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (wpa_s->dpp_pkex) {
 		/* TODO: Support parallel operations */
 		wpa_printf(MSG_DEBUG,
@@ -2967,6 +3083,7 @@
 						   wpa_s->own_addr, src,
 						   wpa_s->dpp_pkex_identifier,
 						   wpa_s->dpp_pkex_code,
+						   wpa_s->dpp_pkex_code_len,
 						   buf, len, v2);
 	if (!wpa_s->dpp_pkex) {
 		wpa_printf(MSG_DEBUG,
@@ -2974,6 +3091,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_announcement) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Started PB PKEX (no more PB announcements)");
+		wpabuf_free(wpa_s->dpp_pb_announcement);
+		wpa_s->dpp_pb_announcement = NULL;
+	}
+#endif /* CONFIG_DPP3 */
 	wpa_s->dpp_pkex_wait_auth_req = false;
 	msg = wpa_s->dpp_pkex->exchange_resp;
 	wait_time = wpa_s->max_remain_on_chan;
@@ -3038,10 +3163,35 @@
 {
 	struct dpp_bootstrap_info *bi;
 
+	wpas_dpp_pkex_clear_code(wpa_s);
 	bi = dpp_pkex_finish(wpa_s->dpp, wpa_s->dpp_pkex, peer, freq);
 	if (!bi)
 		return NULL;
+
 	wpa_s->dpp_pkex = NULL;
+
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && !wpa_s->dpp_pb_configurator &&
+	    os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_init_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement response hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement response",
+			    wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpas_dpp_push_button_stop(wpa_s);
+		return NULL;
+	}
+#endif /* CONFIG_DPP3 */
+
 	return bi;
 }
 
@@ -3123,6 +3273,28 @@
 	if (!bi)
 		return;
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_configurator &&
+	    os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_resp_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+			    wpa_s->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
 		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
@@ -3138,6 +3310,588 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static void wpas_dpp_pb_pkex_init(struct wpa_supplicant *wpa_s,
+				  unsigned int freq, const u8 *src,
+				  const u8 *r_hash)
+{
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	size_t len;
+
+	if (wpa_s->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending previously generated PKEX Exchange Request to "
+			   MACSTR, MAC2STR(src));
+		msg = wpa_s->dpp_pkex->exchange_req;
+		wait_time = wpa_s->max_remain_on_chan;
+		if (wait_time > 2000)
+			wait_time = 2000;
+		offchannel_send_action(wpa_s, freq, src,
+				       wpa_s->own_addr, broadcast,
+				       wpabuf_head(msg), wpabuf_len(msg),
+				       wait_time, wpas_dpp_tx_pkex_status, 0);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+		   MACSTR, MAC2STR(src));
+
+	if (!wpa_s->dpp_pb_cmd) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No configuration to provision as push button Configurator");
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi;
+	os_memcpy(wpa_s->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+	pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr,
+			     "PBPKEX", (const char *) wpa_s->dpp_pb_c_nonce,
+			     wpa_s->dpp_pb_bi->curve->nonce_len,
+			     true);
+	if (!pkex) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+	pkex->freq = freq;
+
+	wpa_s->dpp_pkex = pkex;
+	msg = wpa_s->dpp_pkex->exchange_req;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_REQ);
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, pkex->freq, src,
+			       wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	pkex->exch_req_wait_time = 2000;
+	pkex->exch_req_tries = 1;
+
+	/* Use the externally provided configuration */
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	len = 30 + os_strlen(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pkex_auth_cmd = os_malloc(len);
+	if (wpa_s->dpp_pkex_auth_cmd)
+		os_snprintf(wpa_s->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    wpa_s->dpp_pkex_bi->id, wpa_s->dpp_pb_cmd);
+	else
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static void
+wpas_dpp_rx_pb_presence_announcement(struct wpa_supplicant *wpa_s,
+				     const u8 *src, const u8 *hdr,
+				     const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *r_hash;
+	u16 r_hash_len;
+	unsigned int i;
+	bool found = false;
+	struct dpp_pb_info *info, *tmp;
+	struct os_reltime now, age;
+	struct wpabuf *msg;
+
+	os_get_reltime(&now);
+	wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+		   MACSTR, MAC2STR(src));
+
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		info = &wpa_s->dpp_pb[i];
+		if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+		    os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Active push button Enrollee already known");
+		found = true;
+		info->rx_time = now;
+	}
+
+	if (!found) {
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &wpa_s->dpp_pb[i];
+			if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+				continue;
+
+			if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+				wpa_hexdump(MSG_DEBUG,
+					    "DPP: Push button Enrollee hash expired",
+					    tmp->hash, SHA256_MAC_LEN);
+				tmp->rx_time.sec = 0;
+				tmp->rx_time.usec = 0;
+				continue;
+			}
+
+			wpa_hexdump(MSG_DEBUG,
+				    "DPP: Push button session overlap with hash",
+				    tmp->hash, SHA256_MAC_LEN);
+			if (!wpa_s->dpp_pb_result_indicated &&
+			    wpas_dpp_pb_active(wpa_s)) {
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_PB_RESULT "session-overlap");
+				wpa_s->dpp_pb_result_indicated = true;
+			}
+			wpas_dpp_push_button_stop(wpa_s);
+			return;
+		}
+
+		/* Replace the oldest entry */
+		info = &wpa_s->dpp_pb[0];
+		for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &wpa_s->dpp_pb[i];
+			if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+				info = tmp;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+		os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+		info->rx_time = now;
+	}
+
+	if (!wpas_dpp_pb_active(wpa_s)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Discard message since own push button has not been pressed");
+		return;
+	}
+
+	if (wpa_s->dpp_pb_announce_time.sec == 0 &&
+	    wpa_s->dpp_pb_announce_time.usec == 0) {
+		/* Start a wait before allowing PKEX to be initiated */
+		wpa_s->dpp_pb_announce_time = now;
+	}
+
+	if (!wpa_s->dpp_pb_bi) {
+		int res;
+
+		res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex");
+		if (res < 0)
+			return;
+		wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res);
+		if (!wpa_s->dpp_pb_bi)
+			return;
+
+		if (random_get_bytes(wpa_s->dpp_pb_c_nonce,
+				     wpa_s->dpp_pb_bi->curve->nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate C-nonce");
+			wpas_dpp_push_button_stop(wpa_s);
+			return;
+		}
+	}
+
+	/* Skip the response if one was sent within last 50 ms since the
+	 * Enrollee is going to send out at least three announcement messages.
+	 */
+	os_reltime_sub(&now, &wpa_s->dpp_pb_last_resp, &age);
+	if (age.sec == 0 && age.usec < 50000) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+		return;
+	}
+
+	msg = dpp_build_pb_announcement_resp(
+		wpa_s->dpp_pb_bi, r_hash, wpa_s->dpp_pb_c_nonce,
+		wpa_s->dpp_pb_bi->curve->nonce_len);
+	if (!msg) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send Push Button Presence Announcement Response to "
+		   MACSTR, MAC2STR(src));
+	wpa_s->dpp_pb_last_resp = now;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       0, NULL, 0);
+	wpabuf_free(msg);
+
+	if (os_reltime_expired(&now, &wpa_s->dpp_pb_announce_time, 15))
+		wpas_dpp_pb_pkex_init(wpa_s, freq, src, r_hash);
+}
+
+
+static void
+wpas_dpp_rx_pb_presence_announcement_resp(struct wpa_supplicant *wpa_s,
+					  const u8 *src, const u8 *hdr,
+					  const u8 *buf, size_t len,
+					  unsigned int freq)
+{
+	const u8 *i_hash, *r_hash, *c_nonce;
+	u16 i_hash_len, r_hash_len, c_nonce_len;
+	bool overlap = false;
+
+	if (!wpa_s->dpp_pb_announcement || !wpa_s->dpp_pb_bi ||
+	    wpa_s->dpp_pb_configurator) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Not in active push button Enrollee mode - discard Push Button Presence Announcement Response from "
+			   MACSTR, MAC2STR(src));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Push Button Presence Announcement Response from "
+		   MACSTR, MAC2STR(src));
+
+	i_hash = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+			      &i_hash_len);
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	c_nonce = dpp_get_attr(buf, len, DPP_ATTR_CONFIGURATOR_NONCE,
+			       &c_nonce_len);
+	if (!i_hash || i_hash_len != SHA256_MAC_LEN ||
+	    !r_hash || r_hash_len != SHA256_MAC_LEN ||
+	    !c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_hash, i_hash_len);
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator Nonce",
+		    c_nonce, c_nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ &&
+	    os_memcmp(r_hash, wpa_s->dpp_pb_bi->pubkey_hash_chirp,
+		      SHA256_MAC_LEN - 1) == 0)
+		goto skip_hash_check;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (os_memcmp(r_hash, wpa_s->dpp_pb_bi->pubkey_hash_chirp,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unexpected push button Responder hash - abort");
+		overlap = true;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+skip_hash_check:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_s->dpp_pb_resp_freq &&
+	    os_memcmp(i_hash, wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Push button session overlap detected - abort");
+		overlap = true;
+	}
+
+	if (overlap) {
+		if (!wpa_s->dpp_pb_result_indicated) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"session-overlap");
+			wpa_s->dpp_pb_result_indicated = true;
+		}
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (!wpa_s->dpp_pb_resp_freq) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+			"discovered push button AP/Configurator " MACSTR,
+			MAC2STR(src));
+		wpa_s->dpp_pb_resp_freq = freq;
+		os_memcpy(wpa_s->dpp_pb_init_hash, i_hash, SHA256_MAC_LEN);
+		os_memcpy(wpa_s->dpp_pb_c_nonce, c_nonce, c_nonce_len);
+		wpa_s->dpp_pb_c_nonce_len = c_nonce_len;
+		/* Stop announcement iterations after at least one more full
+		 * round and one extra round for postponed session overlap
+		 * detection. */
+		wpa_s->dpp_pb_stop_iter = 3;
+	}
+}
+
+
+static void
+wpas_dpp_tx_priv_intro_status(struct wpa_supplicant *wpa_s,
+			      unsigned int freq, const u8 *dst,
+			      const u8 *src, const u8 *bssid,
+			      const u8 *data, size_t data_len,
+			      enum offchannel_send_action_result result)
+{
+	const char *res_txt;
+
+	res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		 "FAILED");
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (DPP Private Peer Introduction Update)",
+		   freq, MAC2STR(dst), res_txt);
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+		" freq=%u result=%s", MAC2STR(dst), freq, res_txt);
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " version=%u",
+		MAC2STR(src), wpa_s->dpp_intro_peer_version);
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Try connection again after successful network introduction");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+}
+
+
+static int
+wpas_dpp_send_private_peer_intro_update(struct wpa_supplicant *wpa_s,
+					struct dpp_introduction *intro,
+					struct wpa_ssid *ssid,
+					const u8 *dst, unsigned int freq)
+{
+	struct wpabuf *pt, *msg, *enc_ct;
+	size_t len;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+	const u8 *aad;
+	size_t aad_len;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "HPKE(kem_id=%u kdf_id=%u aead_id=%u)",
+		   intro->kem_id, intro->kdf_id, intro->aead_id);
+
+	/* Plaintext for HPKE */
+	len = 5 + 4 + os_strlen(ssid->dpp_connector);
+	pt = wpabuf_alloc(len);
+	if (!pt)
+		return -1;
+
+	/* Protocol Version */
+	conn_ver = dpp_get_connector_version(ssid->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+	wpabuf_put_le16(pt, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(pt, 1);
+	wpabuf_put_u8(pt, ver);
+
+	/* Connector */
+	wpabuf_put_le16(pt, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(pt, os_strlen(ssid->dpp_connector));
+	wpabuf_put_str(pt, ssid->dpp_connector);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Plaintext for HPKE", pt);
+
+	/* HPKE(pt) using AP's public key (from its Connector) */
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, 0);
+	if (!msg) {
+		wpabuf_free(pt);
+		return -1;
+	}
+	aad = wpabuf_head_u8(msg) + 2; /* from the OUI field (inclusive) */
+	aad_len = DPP_HDR_LEN; /* to the DPP Frame Type field (inclusive) */
+	wpa_hexdump(MSG_MSGDUMP, "DPP: AAD for HPKE", aad, aad_len);
+
+	enc_ct = hpke_base_seal(intro->kem_id, intro->kdf_id, intro->aead_id,
+				intro->peer_key, NULL, 0, aad, aad_len,
+				wpabuf_head(pt), wpabuf_len(pt));
+	wpabuf_free(pt);
+	wpabuf_free(msg);
+	if (!enc_ct) {
+		wpa_printf(MSG_INFO, "DPP: HPKE Seal(Connector) failed");
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE enc|ct", enc_ct);
+
+	/* HPKE(pt) to generate payload for Wrapped Data */
+	len = 5 + 4 + wpabuf_len(enc_ct);
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, len);
+	if (!msg) {
+		wpabuf_free(enc_ct);
+		return -1;
+	}
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	/* Wrapped Data */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(enc_ct));
+	wpabuf_put_buf(msg, enc_ct);
+	wpabuf_free(enc_ct);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Update", msg);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(dst), freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
+	offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_priv_intro_status, 0);
+	wpabuf_free(msg);
+
+	return 0;
+}
+
+
+static void
+wpas_dpp_rx_priv_peer_intro_notify(struct wpa_supplicant *wpa_s,
+				   const u8 *src, const u8 *hdr,
+				   const u8 *buf, size_t len,
+				   unsigned int freq)
+{
+	struct wpa_ssid *ssid;
+	const u8 *connector, *trans_id, *version;
+	u16 connector_len, trans_id_len, version_len;
+	u8 peer_version = 1;
+	struct dpp_introduction intro;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_time now;
+	struct os_reltime rnow;
+	os_time_t expiry;
+	unsigned int seconds;
+	enum dpp_status_error res;
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Notify from "
+		   MACSTR, MAC2STR(src));
+	if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
+	    os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
+			   MACSTR " - drop", MAC2STR(src));
+		return;
+	}
+	offchannel_send_action_done(wpa_s);
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == wpa_s->dpp_intro_network)
+			break;
+	}
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Profile not found for network introduction");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_transaction_id", MAC2STR(src));
+		goto fail;
+	}
+	if (trans_id[0] != TRANSACTION_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore frame with unexpected Transaction ID %u",
+			   trans_id[0]);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=transaction_id_mismatch", MAC2STR(src));
+		goto fail;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_connector", MAC2STR(src));
+		goto fail;
+	}
+
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include valid Version");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_version", MAC2STR(src));
+		goto fail;
+	}
+
+	res = dpp_peer_intro(&intro, ssid->dpp_connector,
+			     ssid->dpp_netaccesskey,
+			     ssid->dpp_netaccesskey_len,
+			     ssid->dpp_csign,
+			     ssid->dpp_csign_len,
+			     connector, connector_len, &expiry);
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=peer_connector_validation_failed", MAC2STR(src));
+		wpas_dpp_send_conn_status_result(wpa_s, res);
+		goto fail;
+	}
+
+	peer_version = version[0];
+	if (intro.peer_version && intro.peer_version >= 2 &&
+	    peer_version != intro.peer_version) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+			   intro.peer_version, peer_version);
+		wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_NO_MATCH);
+		goto fail;
+	}
+	wpa_s->dpp_intro_peer_version = peer_version;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry)
+		goto fail;
+	entry->dpp_pfs = peer_version >= 2;
+	os_memcpy(entry->aa, src, ETH_ALEN);
+	os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
+	entry->pmk_len = intro.pmk_len;
+	entry->akmp = WPA_KEY_MGMT_DPP;
+	if (expiry) {
+		os_get_time(&now);
+		seconds = expiry - now.sec;
+	} else {
+		seconds = 86400 * 7;
+	}
+
+	if (wpas_dpp_send_private_peer_intro_update(wpa_s, &intro, ssid, src,
+						    freq) < 0) {
+		os_free(entry);
+		goto fail;
+	}
+
+	os_get_reltime(&rnow);
+	entry->expiration = rnow.sec + seconds;
+	entry->reauth_time = rnow.sec + seconds;
+	entry->network_ctx = ssid;
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+
+	/* Association will be initiated from TX status handler for the Private
+	 * Peer Intro Update: wpas_dpp_tx_priv_intro_status() */
+
+fail:
+	dpp_peer_intro_deinit(&intro);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
 			const u8 *buf, size_t len, unsigned int freq)
 {
@@ -3150,13 +3904,6 @@
 		return;
 	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
 		return;
-#ifdef CONFIG_TESTING_OPTIONS
-	if (wpa_s->dpp_discard_public_action) {
-		wpa_printf(MSG_DEBUG,
-			   "TESTING: Discard received DPP Public Action frame");
-		return;
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
 	hdr = buf;
 	buf += 4;
 	len -= 4;
@@ -3168,6 +3915,15 @@
 		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
 		   MACSTR " freq=%u",
 		   crypto_suite, type, MAC2STR(src), freq);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->dpp_discard_public_action &&
+	    type != DPP_PA_PEER_DISCOVERY_RESP &&
+	    type != DPP_PA_PRIV_PEER_INTRO_NOTIFY) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Discard received DPP Public Action frame");
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (crypto_suite != 1) {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
 			   crypto_suite);
@@ -3247,6 +4003,20 @@
 		wpas_dpp_rx_reconfig_auth_conf(wpa_s, src, hdr, buf, len, freq);
 		break;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+		wpas_dpp_rx_pb_presence_announcement(wpa_s, src, hdr,
+						     buf, len, freq);
+		break;
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP:
+		wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr,
+							  buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_NOTIFY:
+		wpas_dpp_rx_priv_peer_intro_notify(wpa_s, src, hdr,
+						   buf, len, freq);
+		break;
+#endif /* CONFIG_DPP3 */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignored unsupported frame subtype %d", type);
@@ -3450,7 +4220,8 @@
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 	if (ok) {
-		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+			auth->conf_resp_status);
 		wpas_notify_dpp_config_sent(wpa_s);
 	}
 	else {
@@ -3460,6 +4231,20 @@
 	dpp_auth_deinit(wpa_s->dpp_auth);
 	wpa_s->dpp_auth = NULL;
 	wpabuf_free(resp);
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
+		if (ok)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"could-not-connect");
+		wpa_s->dpp_pb_result_indicated = true;
+		if (ok)
+			wpas_dpp_remove_pb_hash(wpa_s);
+		wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -3510,6 +4295,63 @@
 }
 
 
+#ifdef CONFIG_DPP3
+static int wpas_dpp_start_private_peer_intro(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid,
+					     struct wpa_bss *bss)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	size_t len;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+
+	len = 5 + 5;
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_QUERY, len);
+	if (!msg)
+		return -1;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	conn_ver = dpp_get_connector_version(ssid->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, ver);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Query", msg);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(bss->bssid), bss->freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
+	offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_introduction_status, 0);
+	wpabuf_free(msg);
+
+	/* Request this connection attempt to terminate - new one will be
+	 * started when network introduction protocol completes */
+	os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
+	wpa_s->dpp_intro_network = ssid;
+	return 1;
+}
+#endif /* CONFIG_DPP3 */
+
+
 int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			   struct wpa_bss *bss)
 {
@@ -3549,11 +4391,18 @@
 	}
 
 	wpa_printf(MSG_DEBUG,
-		   "DPP: Starting network introduction protocol to derive PMKSA for "
-		   MACSTR, MAC2STR(bss->bssid));
+		   "DPP: Starting %snetwork introduction protocol to derive PMKSA for "
+		   MACSTR,
+		   ssid->dpp_connector_privacy ? "private " : "",
+		   MAC2STR(bss->bssid));
 	if (wpa_s->wpa_state == WPA_SCANNING)
 		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 
+#ifdef CONFIG_DPP3
+	if (ssid->dpp_connector_privacy)
+		return wpas_dpp_start_private_peer_intro(wpa_s, ssid, bss);
+#endif /* CONFIG_DPP3 */
+
 	len = 5 + 4 + os_strlen(ssid->dpp_connector);
 #ifdef CONFIG_DPP2
 	len += 5;
@@ -3743,6 +4592,7 @@
 	wpa_s->dpp_pkex_code = os_strdup(pos + 6);
 	if (!wpa_s->dpp_pkex_code)
 		return -1;
+	wpa_s->dpp_pkex_code_len = os_strlen(wpa_s->dpp_pkex_code);
 
 	pos = os_strstr(cmd, " ver=");
 	if (pos) {
@@ -3791,7 +4641,7 @@
 			return -1;
 	}
 
-	if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
+	if ((id_val != 0 && id_val != 1))
 		return -1;
 
 	/* TODO: Support multiple PKEX entries */
@@ -3820,6 +4670,9 @@
 	wpa_s->dpp_pkex_wait_auth_req = false;
 	if (wpa_s->dpp_gas_client && wpa_s->dpp_gas_dialog_token >= 0)
 		gas_query_stop(wpa_s->gas, wpa_s->dpp_gas_dialog_token);
+#ifdef CONFIG_DPP3
+	wpas_dpp_push_button_stop(wpa_s);
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -4094,29 +4947,20 @@
 }
 
 
-static void wpas_dpp_chirp_scan_res_handler(struct wpa_supplicant *wpa_s,
-					    struct wpa_scan_results *scan_res)
+static int * wpas_dpp_presence_ann_channels(struct wpa_supplicant *wpa_s,
+					    struct dpp_bootstrap_info *bi)
 {
-	struct dpp_bootstrap_info *bi = wpa_s->dpp_chirp_bi;
 	unsigned int i;
 	struct hostapd_hw_modes *mode;
 	int c;
 	struct wpa_bss *bss;
 	bool chan6 = wpa_s->hw.modes == NULL;
-
-	if (!bi && !wpa_s->dpp_reconfig_ssid)
-		return;
-
-	wpa_s->dpp_chirp_scan_done = 1;
-
-	os_free(wpa_s->dpp_chirp_freqs);
-	wpa_s->dpp_chirp_freqs = NULL;
+	int *freqs = NULL;
 
 	/* Channels from own bootstrapping info */
 	if (bi) {
 		for (i = 0; i < bi->num_freq; i++)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs,
-					     bi->freq[i]);
+			int_array_add_unique(&freqs, bi->freq[i]);
 	}
 
 	/* Preferred chirping channels */
@@ -4134,7 +4978,7 @@
 		}
 	}
 	if (chan6)
-		int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
+		int_array_add_unique(&freqs, 2437);
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
 			HOSTAPD_MODE_IEEE80211A, false);
@@ -4153,9 +4997,9 @@
 				chan149 = 1;
 		}
 		if (chan149)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 5745);
+			int_array_add_unique(&freqs, 5745);
 		else if (chan44)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 5220);
+			int_array_add_unique(&freqs, 5220);
 	}
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
@@ -4168,7 +5012,7 @@
 					   HOSTAPD_CHAN_RADAR)) ||
 			    chan->freq != 60480)
 				continue;
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 60480);
+			int_array_add_unique(&freqs, 60480);
 			break;
 		}
 	}
@@ -4177,10 +5021,26 @@
 	 * Connectivity element */
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_get_vendor_ie(bss, DPP_CC_IE_VENDOR_TYPE))
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs,
-					     bss->freq);
+			int_array_add_unique(&freqs, bss->freq);
 	}
 
+	return freqs;
+}
+
+
+static void wpas_dpp_chirp_scan_res_handler(struct wpa_supplicant *wpa_s,
+					    struct wpa_scan_results *scan_res)
+{
+	struct dpp_bootstrap_info *bi = wpa_s->dpp_chirp_bi;
+
+	if (!bi && !wpa_s->dpp_reconfig_ssid)
+		return;
+
+	wpa_s->dpp_chirp_scan_done = 1;
+
+	os_free(wpa_s->dpp_chirp_freqs);
+	wpa_s->dpp_chirp_freqs = wpas_dpp_presence_ann_channels(wpa_s, bi);
+
 	if (!wpa_s->dpp_chirp_freqs ||
 	    eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL) < 0)
 		wpas_dpp_chirp_stop(wpa_s);
@@ -4478,3 +5338,314 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+#define DPP_PB_ANNOUNCE_PER_CHAN 3
+
+static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq);
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpas_dpp_pb_tx_status(struct wpa_supplicant *wpa_s,
+				  unsigned int freq, const u8 *dst,
+				  const u8 *src, const u8 *bssid,
+				  const u8 *data, size_t data_len,
+				  enum offchannel_send_action_result result)
+{
+	if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to send push button announcement on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 0, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Push button announcement on %d MHz sent",
+		   freq);
+	if (wpa_s->dpp_pb_discovery_done) {
+		wpa_s->dpp_pb_announce_count = 0;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Wait for push button announcement response and PKEX on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 500000, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	} else if (wpa_s->dpp_pb_announce_count >= DPP_PB_ANNOUNCE_PER_CHAN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Wait for push button announcement response on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 50000, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (wpas_dpp_pb_announce(wpa_s, freq) < 0)
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq)
+{
+	struct wpabuf *msg;
+	int type;
+
+	msg = wpa_s->dpp_pb_announcement;
+	if (!msg)
+		return -1;
+
+	wpa_s->dpp_pb_announce_count++;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send push button announcement %d/%d (%d MHz)",
+		   wpa_s->dpp_pb_announce_count, DPP_PB_ANNOUNCE_PER_CHAN,
+		   freq);
+
+	type = DPP_PA_PB_PRESENCE_ANNOUNCEMENT;
+	if (wpa_s->dpp_pb_announce_count == 1)
+		wpa_msg(wpa_s, MSG_INFO,
+			DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+			MAC2STR(broadcast), freq, type);
+	if (offchannel_send_action(
+		    wpa_s, freq, broadcast, wpa_s->own_addr, broadcast,
+		    wpabuf_head(msg), wpabuf_len(msg),
+		    1000, wpas_dpp_pb_tx_status, 0) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct os_reltime now;
+	int freq;
+
+	if (!wpa_s->dpp_pb_freqs)
+		return;
+
+	os_get_reltime(&now);
+	offchannel_send_action_done(wpa_s);
+
+	if (os_reltime_expired(&now, &wpa_s->dpp_pb_time, 100)) {
+		wpa_printf(MSG_DEBUG, "DPP: Push button wait time expired");
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (wpa_s->dpp_pb_freq_idx >= int_array_len(wpa_s->dpp_pb_freqs)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Completed push button announcement round");
+		wpa_s->dpp_pb_freq_idx = 0;
+		if (wpa_s->dpp_pb_stop_iter > 0) {
+			wpa_s->dpp_pb_stop_iter--;
+
+			if (wpa_s->dpp_pb_stop_iter == 1) {
+				wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+					"wait for AP/Configurator to allow PKEX to be initiated");
+				if (eloop_register_timeout(10, 0,
+							   wpas_dpp_pb_next,
+							   wpa_s, NULL) < 0) {
+					wpas_dpp_push_button_stop(wpa_s);
+					return;
+				}
+				return;
+			}
+
+			if (wpa_s->dpp_pb_stop_iter == 0) {
+				wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+					"start push button PKEX responder on the discovered channel (%d MHz)",
+					wpa_s->dpp_pb_resp_freq);
+				wpa_s->dpp_pb_discovery_done = true;
+
+				wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi;
+
+				os_free(wpa_s->dpp_pkex_code);
+				wpa_s->dpp_pkex_code = os_memdup(
+					wpa_s->dpp_pb_c_nonce,
+					wpa_s->dpp_pb_c_nonce_len);
+				wpa_s->dpp_pkex_code_len =
+					wpa_s->dpp_pb_c_nonce_len;
+
+				os_free(wpa_s->dpp_pkex_identifier);
+				wpa_s->dpp_pkex_identifier =
+					os_strdup("PBPKEX");
+
+				if (!wpa_s->dpp_pkex_code ||
+				    !wpa_s->dpp_pkex_identifier) {
+					wpas_dpp_push_button_stop(wpa_s);
+					return;
+				}
+
+				wpa_s->dpp_pkex_ver = PKEX_VER_ONLY_2;
+
+				os_free(wpa_s->dpp_pkex_auth_cmd);
+				wpa_s->dpp_pkex_auth_cmd = NULL;
+			}
+		}
+	}
+
+	if (wpa_s->dpp_pb_discovery_done)
+		freq = wpa_s->dpp_pb_resp_freq;
+	else
+		freq = wpa_s->dpp_pb_freqs[wpa_s->dpp_pb_freq_idx++];
+	wpa_s->dpp_pb_announce_count = 0;
+	if (!wpa_s->dpp_pb_announcement) {
+		wpa_printf(MSG_DEBUG, "DPP: Push button announcements stopped");
+		return;
+	}
+	if (wpas_dpp_pb_announce(wpa_s, freq) < 0) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+}
+
+
+static void wpas_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Active push button Configurator mode expired");
+	wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static int wpas_dpp_push_button_configurator(struct wpa_supplicant *wpa_s,
+					     const char *cmd)
+{
+	wpa_s->dpp_pb_configurator = true;
+	wpa_s->dpp_pb_announce_time.sec = 0;
+	wpa_s->dpp_pb_announce_time.usec = 0;
+	str_clear_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+	if (cmd) {
+		wpa_s->dpp_pb_cmd = os_strdup(cmd);
+		if (!wpa_s->dpp_pb_cmd)
+			return -1;
+	}
+	eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
+			       wpa_s, NULL);
+
+	return 0;
+}
+
+
+static void wpas_dpp_pb_scan_res_handler(struct wpa_supplicant *wpa_s,
+					 struct wpa_scan_results *scan_res)
+{
+	if (!wpa_s->dpp_pb_time.sec && !wpa_s->dpp_pb_time.usec)
+		return;
+
+	os_free(wpa_s->dpp_pb_freqs);
+	wpa_s->dpp_pb_freqs = wpas_dpp_presence_ann_channels(wpa_s, NULL);
+
+	wpa_printf(MSG_DEBUG, "DPP: Scan completed for PB discovery");
+	if (!wpa_s->dpp_pb_freqs ||
+	    eloop_register_timeout(0, 0, wpas_dpp_pb_next, wpa_s, NULL) < 0)
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int res;
+
+	if (!wpa_s->dpp)
+		return -1;
+	wpas_dpp_push_button_stop(wpa_s);
+	wpas_dpp_stop(wpa_s);
+	wpas_dpp_chirp_stop(wpa_s);
+
+	os_get_reltime(&wpa_s->dpp_pb_time);
+
+	if (cmd &&
+	    (os_strstr(cmd, " role=configurator") ||
+	     os_strstr(cmd, " conf=")))
+		return wpas_dpp_push_button_configurator(wpa_s, cmd);
+
+	wpa_s->dpp_pb_configurator = false;
+
+	wpa_s->dpp_pb_freq_idx = 0;
+
+	res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex");
+	if (res < 0)
+		return -1;
+	wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res);
+	if (!wpa_s->dpp_pb_bi)
+		return -1;
+
+	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_netrole = DPP_NETROLE_STA;
+	wpa_s->dpp_qr_mutual = 0;
+	wpa_s->dpp_pb_announcement =
+		dpp_build_pb_announcement(wpa_s->dpp_pb_bi);
+	if (!wpa_s->dpp_pb_announcement)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Scan to create channel list for PB discovery");
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+	return 0;
+}
+
+
+void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp)
+		return;
+	os_free(wpa_s->dpp_pb_freqs);
+	wpa_s->dpp_pb_freqs = NULL;
+	wpabuf_free(wpa_s->dpp_pb_announcement);
+	wpa_s->dpp_pb_announcement = NULL;
+	if (wpa_s->dpp_pb_bi) {
+		char id[20];
+
+		os_snprintf(id, sizeof(id), "%u", wpa_s->dpp_pb_bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpa_s->dpp_pb_bi = NULL;
+		if (!wpa_s->dpp_pb_result_indicated) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
+			wpa_s->dpp_pb_result_indicated = true;
+		}
+	}
+
+	wpa_s->dpp_pb_resp_freq = 0;
+	wpa_s->dpp_pb_stop_iter = 0;
+	wpa_s->dpp_pb_discovery_done = false;
+	os_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+
+	eloop_cancel_timeout(wpas_dpp_pb_next, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_push_button_expire, wpa_s, NULL);
+	if (wpas_dpp_pb_active(wpa_s)) {
+		wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+		if (!wpa_s->dpp_pb_result_indicated)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
+	}
+	wpa_s->dpp_pb_time.sec = 0;
+	wpa_s->dpp_pb_time.usec = 0;
+	dpp_pkex_free(wpa_s->dpp_pkex);
+	wpa_s->dpp_pkex = NULL;
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = NULL;
+
+	wpa_s->dpp_pb_result_indicated = false;
+
+	str_clear_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+
+	if (wpa_s->scan_res_handler == wpas_dpp_pb_scan_res_handler) {
+		wpas_abort_ongoing_scan(wpa_s);
+		wpa_s->scan_res_handler = NULL;
+	}
+}
+
+#endif /* CONFIG_DPP3 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index 2b03a54..e2bdd9d 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -44,5 +44,7 @@
 int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_conf_set(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s);
 
 #endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index b0af1cd..6be117c 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -523,6 +523,14 @@
 int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
 			struct wpa_signal_info *si);
 
+static inline int wpa_drv_mlo_signal_poll(struct wpa_supplicant *wpa_s,
+					  struct wpa_mlo_signal_info *mlo_si)
+{
+	if (wpa_s->driver->mlo_signal_poll)
+		return wpa_s->driver->mlo_signal_poll(wpa_s->drv_priv, mlo_si);
+	return -1;
+}
+
 static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s,
 				       struct wpa_channel_info *ci)
 {
@@ -1117,4 +1125,49 @@
 	return wpa_s->driver->dpp_listen(wpa_s->drv_priv, enable);
 }
 
+static inline int wpa_drv_send_pasn_resp(struct wpa_supplicant *wpa_s,
+					 struct pasn_auth *params)
+{
+	if (!wpa_s->driver->send_pasn_resp)
+		return -1;
+	return wpa_s->driver->send_pasn_resp(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_set_secure_ranging_ctx(struct wpa_supplicant *wpa_s,
+						 const u8 *own_addr,
+						 const u8 *peer_addr,
+						 u32 cipher, u8 tk_len,
+						 const u8 *tk,
+						 u8 ltf_keyseed_len,
+						 const u8 *ltf_keyseed,
+						 u32 action)
+{
+	struct secure_ranging_params params;
+
+	if (!wpa_s->driver->set_secure_ranging_ctx)
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.action = action;
+	params.own_addr = own_addr;
+	params.peer_addr = peer_addr;
+	params.cipher = cipher;
+	params.tk_len = tk_len;
+	params.tk = tk;
+	params.ltf_keyseed_len = ltf_keyseed_len;
+	params.ltf_keyseed = ltf_keyseed;
+
+	return wpa_s->driver->set_secure_ranging_ctx(wpa_s->drv_priv, &params);
+}
+
+static inline int
+wpas_drv_get_sta_mlo_info(struct wpa_supplicant *wpa_s,
+			  struct driver_sta_mlo_info *mlo_info)
+{
+	if (!wpa_s->driver->get_sta_mlo_info)
+		return 0;
+
+	return wpa_s->driver->get_sta_mlo_info(wpa_s->drv_priv, mlo_info);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 1ec7d5e..43c9344 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -168,6 +168,21 @@
 }
 
 
+static void wpa_supplicant_update_link_bss(struct wpa_supplicant *wpa_s,
+					   u8 link_id, const u8 *bssid)
+{
+	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+	}
+
+	if (bss)
+		wpa_s->links[link_id].bss = bss;
+}
+
+
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid, *old_ssid;
@@ -286,6 +301,19 @@
 }
 
 
+static void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	if (!wpa_s->valid_links)
+		return;
+
+	wpa_s->valid_links = 0;
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+		wpa_s->links[i].bss = NULL;
+}
+
+
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 {
 	int bssid_changed;
@@ -338,6 +366,7 @@
 	wpa_s->current_ssid = NULL;
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	wpa_s->key_mgmt = 0;
+	wpa_s->allowed_key_mgmts = 0;
 
 	wpas_rrm_reset(wpa_s);
 	wpa_s->wnmsleep_used = 0;
@@ -352,6 +381,8 @@
 
 	if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
 		wpa_s->enabled_4addr_mode = 0;
+
+	wpas_reset_mlo_info(wpa_s);
 }
 
 
@@ -2801,6 +2832,28 @@
 		return -1;
 	}
 
+	/*
+	 * Update PMK in wpa_sm and the driver if roamed to WPA/WPA2 PSK from a
+	 * different AKM.
+	 */
+	if (wpa_s->key_mgmt != ie.key_mgmt &&
+	    wpa_key_mgmt_wpa_psk_no_sae(ie.key_mgmt)) {
+		if (!ssid->psk_set) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"No PSK available for association");
+			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+			return -1;
+		}
+
+		wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL);
+		if (wpa_s->conf->key_mgmt_offload &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD) &&
+		    wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, ssid->psk,
+				    PMK_LEN, KEY_FLAG_PMK))
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"WPA: Cannot set PMK for key management offload");
+	}
+
 	wpa_s->key_mgmt = ie.key_mgmt;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT %s and proto %d",
@@ -3391,6 +3444,60 @@
 }
 
 
+static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	struct driver_sta_mlo_info mlo;
+	int i;
+
+	mlo.valid_links = 0;
+	if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info");
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return -1;
+	}
+
+	if (wpa_s->valid_links == mlo.valid_links) {
+		bool match = true;
+
+		if (!mlo.valid_links)
+			return 0;
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo.valid_links & BIT(i)))
+				continue;
+
+			if (os_memcmp(wpa_s->links[i].addr, mlo.links[i].addr,
+				      ETH_ALEN) != 0 ||
+			    os_memcmp(wpa_s->links[i].bssid, mlo.links[i].bssid,
+				      ETH_ALEN) != 0) {
+				match = false;
+				break;
+			}
+		}
+
+		if (match &&
+		    os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr,
+			      ETH_ALEN) == 0)
+			return 0;
+	}
+
+	wpa_s->valid_links = mlo.valid_links;
+	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;
+
+		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;
+		wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid);
+	}
+
+	return 0;
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				       union wpa_event_data *data)
 {
@@ -3450,6 +3557,13 @@
 		return;
 	}
 
+	if (wpa_drv_get_mlo_info(wpa_s) < 0) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO connection info");
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return;
+	}
+
 	if (ft_completed &&
 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION)) {
 		wpa_msg(wpa_s, MSG_INFO, "Attempt to roam to " MACSTR,
@@ -5408,6 +5522,39 @@
 		break;
 #endif /* CONFIG_AP */
 
+	case EVENT_LINK_CH_SWITCH_STARTED:
+	case EVENT_LINK_CH_SWITCH:
+		if (!data || !wpa_s->current_ssid ||
+		    !(wpa_s->valid_links & BIT(data->ch_switch.link_id)))
+			break;
+
+		wpa_msg(wpa_s, MSG_INFO,
+			"%sfreq=%d link_id=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+			event == EVENT_LINK_CH_SWITCH ?
+			WPA_EVENT_LINK_CHANNEL_SWITCH :
+			WPA_EVENT_LINK_CHANNEL_SWITCH_STARTED,
+			data->ch_switch.freq,
+			data->ch_switch.link_id,
+			data->ch_switch.ht_enabled,
+			data->ch_switch.ch_offset,
+			channel_width_to_string(data->ch_switch.ch_width),
+			data->ch_switch.cf1,
+			data->ch_switch.cf2);
+		if (event == EVENT_LINK_CH_SWITCH_STARTED)
+			break;
+
+		wpa_s->links[data->ch_switch.link_id].freq =
+			data->ch_switch.freq;
+		if (wpa_s->links[data->ch_switch.link_id].bss &&
+		    wpa_s->links[data->ch_switch.link_id].bss->freq !=
+		    data->ch_switch.freq) {
+			wpa_s->links[data->ch_switch.link_id].bss->freq =
+				data->ch_switch.freq;
+			notify_bss_changes(
+				wpa_s, WPA_BSS_FREQ_CHANGED_FLAG,
+				wpa_s->links[data->ch_switch.link_id].bss);
+		}
+		break;
 	case EVENT_CH_SWITCH_STARTED:
 	case EVENT_CH_SWITCH:
 		if (!data || !wpa_s->current_ssid)
@@ -5942,6 +6089,11 @@
 		sme_external_auth_trigger(wpa_s, data);
 #endif /* CONFIG_SAE */
 		break;
+#ifdef CONFIG_PASN
+	case EVENT_PASN_AUTH:
+		wpas_pasn_auth_trigger(wpa_s, &data->pasn_auth);
+		break;
+#endif /* CONFIG_PASN */
 	case EVENT_PORT_AUTHORIZED:
 		wpa_supplicant_event_port_authorized(wpa_s);
 		break;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 8e31824..a544c94 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -424,6 +424,10 @@
 		wpa_s->last_ssid = NULL;
 	if (wpa_s->current_ssid == ssid)
 		wpa_s->current_ssid = NULL;
+#if defined(CONFIG_SME) && defined(CONFIG_SAE)
+	if (wpa_s->sme.ext_auth_wpa_ssid == ssid)
+		wpa_s->sme.ext_auth_wpa_ssid = NULL;
+#endif /* CONFIG_SME && CONFIG_SAE */
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index ce20970..0bfb962 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3833,6 +3833,10 @@
 	int flag = 0;
 	enum chan_allowed res, res2;
 
+	if (is_6ghz_op_class(op_class) && !is_6ghz_psc_frequency(
+			p2p_channel_to_freq(op_class, channel)))
+		return NOT_ALLOWED;
+
 	res2 = res = has_channel(wpa_s->global, mode, op_class, channel, &flag);
 	if (bw == BW40MINUS) {
 		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
@@ -9770,10 +9774,13 @@
 
 	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode &&
 	    (wpa_s->ap_iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A ||
+	     is_6ghz_freq(wpa_s->ap_iface->freq) ||
 	     conf->hw_mode != HOSTAPD_MODE_IEEE80211G)) {
 		wpa_dbg(wpa_s, MSG_INFO,
-			"P2P CSA: CSA from Hardware mode %d to %d is not supported",
-			wpa_s->ap_iface->current_mode->mode, conf->hw_mode);
+			"P2P CSA: CSA from hardware mode %d%s to %d is not supported",
+			wpa_s->ap_iface->current_mode->mode,
+			is_6ghz_freq(wpa_s->ap_iface->freq) ? " (6 GHz)" : "",
+			conf->hw_mode);
 		ret = -1;
 		goto out;
 	}
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index dc21b6a..2e6d9a7 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -23,11 +23,13 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "bss.h"
+#include "scan.h"
 #include "config.h"
 
 static const int dot11RSNAConfigPMKLifetime = 43200;
 
 struct wpa_pasn_auth_work {
+	u8 own_addr[ETH_ALEN];
 	u8 bssid[ETH_ALEN];
 	int akmp;
 	int cipher;
@@ -52,6 +54,8 @@
 	wpa_printf(MSG_DEBUG, "PASN: Auth work timeout - stopping auth");
 
 	wpas_pasn_auth_stop(wpa_s);
+
+	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
 }
 
 
@@ -111,7 +115,7 @@
 	}
 
 	ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
-				    wpa_s->own_addr, pasn->bssid,
+				    pasn->own_addr, pasn->bssid,
 				    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
@@ -286,6 +290,281 @@
 #endif /* CONFIG_SAE */
 
 
+static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s,
+					 struct pasn_peer *peer)
+{
+	int ret;
+	const u8 *rsne, *rsnxe;
+	struct wpa_bss *bss;
+	struct wpa_ie_data rsne_data;
+	int sel, key_mgmt, pairwise_cipher;
+	int network_id = 0, group = 19;
+	struct wpa_ssid *ssid = NULL;
+	size_t ssid_str_len = 0;
+	const u8 *ssid_str = NULL;
+	const u8 *bssid = peer->peer_addr;
+
+	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+		bss = wpa_bss_get_bssid(wpa_s, bssid);
+		if (!bss) {
+			wpa_printf(MSG_DEBUG, "PASN: BSS not found");
+			return -1;
+		}
+	}
+
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne) {
+		wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
+		return -1;
+	}
+
+	ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data");
+		return -1;
+	}
+
+	rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+	ssid_str_len = bss->ssid_len;
+	ssid_str = bss->ssid;
+
+	/* Get the network configuration based on the obtained SSID */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!wpas_network_disabled(wpa_s, ssid) &&
+		    ssid_str_len == ssid->ssid_len &&
+		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0)
+			break;
+	}
+
+	if (ssid)
+		network_id = ssid->id;
+
+	sel = rsne_data.pairwise_cipher;
+	if (ssid && ssid->pairwise_cipher)
+		sel &= ssid->pairwise_cipher;
+
+	wpa_printf(MSG_DEBUG, "PASN: peer pairwise 0x%x, select 0x%x",
+		   rsne_data.pairwise_cipher, sel);
+
+	pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
+	if (pairwise_cipher < 0) {
+		wpa_msg(wpa_s, MSG_WARNING,
+			"PASN: Failed to select pairwise cipher");
+		return -1;
+	}
+
+	sel = rsne_data.key_mgmt;
+	if (ssid && ssid->key_mgmt)
+		sel &= ssid->key_mgmt;
+
+	wpa_printf(MSG_DEBUG, "PASN: peer AKMP 0x%x, select 0x%x",
+		   rsne_data.key_mgmt, sel);
+#ifdef CONFIG_SAE
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) || !ssid)
+		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211R
+	if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME |
+				  WPA_DRIVER_FLAGS_UPDATE_FT_IES)))
+		sel &= ~WPA_KEY_MGMT_FT;
+#endif /* CONFIG_IEEE80211R */
+	if (0) {
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SHA384
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
+		key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/802.1X-SHA384");
+		if (ssid && !ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Disable PMKSA caching for FT/802.1X connection");
+			pmksa_cache_clear_current(wpa_s->wpa);
+		}
+#endif /* CONFIG_SHA384 */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+	} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) &&
+		   (ieee802_11_rsnx_capab(rsnxe,
+					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
+		   (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+		key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)");
+	} else if ((sel & WPA_KEY_MGMT_SAE) &&
+		   (ieee802_11_rsnx_capab(rsnxe,
+					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
+		   (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) == 0)) {
+		key_mgmt = WPA_KEY_MGMT_SAE;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE");
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+		key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+		key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
+		key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/802.1X");
+		if (ssid && !ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Disable PMKSA caching for FT/802.1X connection");
+			pmksa_cache_clear_current(wpa_s->wpa);
+		}
+	} else if (sel & WPA_KEY_MGMT_FT_PSK) {
+		key_mgmt = WPA_KEY_MGMT_FT_PSK;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/PSK");
+#endif /* CONFIG_IEEE80211R */
+	} else if (sel & WPA_KEY_MGMT_PASN) {
+		key_mgmt = WPA_KEY_MGMT_PASN;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT PASN");
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: invalid AKMP");
+		return -1;
+	}
+
+	peer->akmp = key_mgmt;
+	peer->cipher = pairwise_cipher;
+	peer->network_id = network_id;
+	peer->group = group;
+	return 0;
+}
+
+
+static int wpas_pasn_set_keys_from_cache(struct wpa_supplicant *wpa_s,
+					 const u8 *own_addr, const u8 *bssid,
+					 int cipher, int akmp)
+{
+	struct ptksa_cache_entry *entry;
+
+	entry = ptksa_cache_get(wpa_s->ptksa, bssid, cipher);
+	if (!entry) {
+		wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+			   " not present in PTKSA cache", MAC2STR(bssid));
+		return -1;
+	}
+
+	if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: own addr " MACSTR " and PTKSA entry own addr "
+			   MACSTR " differ",
+			   MAC2STR(own_addr), MAC2STR(entry->own_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+		   MAC2STR(bssid));
+	wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, cipher,
+				       entry->ptk.tk_len,
+				       entry->ptk.tk,
+				       entry->ptk.ltf_keyseed_len,
+				       entry->ptk.ltf_keyseed, 0);
+	return 0;
+}
+
+
+static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s,
+					  struct pasn_auth *pasn_params)
+{
+	struct pasn_peer *peer;
+	u8 comeback_len = 0;
+	const u8 *comeback = NULL;
+
+	if (!pasn_params)
+		return;
+
+	while (wpa_s->pasn_count < pasn_params->num_peers) {
+		peer = &pasn_params->peer[wpa_s->pasn_count];
+
+		if (os_memcmp(wpa_s->bssid, peer->peer_addr, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Associated peer is not expected");
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
+		}
+
+		if (wpas_pasn_set_keys_from_cache(wpa_s, peer->own_addr,
+						  peer->peer_addr,
+						  peer->cipher,
+						  peer->akmp) == 0) {
+			peer->status = PASN_STATUS_SUCCESS;
+			wpa_s->pasn_count++;
+			continue;
+		}
+
+		if (wpas_pasn_get_params_from_bss(wpa_s, peer)) {
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
+		}
+
+		if (wpas_pasn_auth_start(wpa_s, peer->own_addr,
+					 peer->peer_addr, peer->akmp,
+					 peer->cipher, peer->group,
+					 peer->network_id,
+					 comeback, comeback_len)) {
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "PASN: Sent PASN auth start for " MACSTR,
+			   MAC2STR(peer->peer_addr));
+		return;
+	}
+
+	if (wpa_s->pasn_count == pasn_params->num_peers) {
+		wpa_drv_send_pasn_resp(wpa_s, pasn_params);
+		wpa_printf(MSG_DEBUG, "PASN: Response sent");
+		os_free(wpa_s->pasn_params);
+		wpa_s->pasn_params = NULL;
+	}
+}
+
+
+void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status)
+{
+	if (!wpa_s->pasn_params)
+		return;
+
+	wpa_s->pasn_params->peer[wpa_s->pasn_count].status = status;
+	wpa_s->pasn_count++;
+	wpas_pasn_configure_next_peer(wpa_s, wpa_s->pasn_params);
+}
+
+
+static void wpas_pasn_delete_peers(struct wpa_supplicant *wpa_s,
+				   struct pasn_auth *pasn_params)
+{
+	struct pasn_peer *peer;
+	unsigned int i;
+
+	if (!pasn_params)
+		return;
+
+	for (i = 0; i < pasn_params->num_peers; i++) {
+		peer = &pasn_params->peer[i];
+		wpas_pasn_deauthenticate(wpa_s, peer->own_addr,
+					 peer->peer_addr);
+	}
+}
+
+
 #ifdef CONFIG_FILS
 
 static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
@@ -685,7 +964,7 @@
 	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
 
 	wpa_pasn_build_auth_header(buf, pasn->bssid,
-				   wpa_s->own_addr, pasn->bssid,
+				   pasn->own_addr, pasn->bssid,
 				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
 	pmkid = NULL;
@@ -732,11 +1011,11 @@
 	/* Add own RNSXE */
 	capab = 0;
 	capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
 	wpa_pasn_add_rsnxe(buf, capab);
 
@@ -788,7 +1067,7 @@
 	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
 
 	wpa_pasn_build_auth_header(buf, pasn->bssid,
-				   wpa_s->own_addr, pasn->bssid,
+				   pasn->own_addr, pasn->bssid,
 				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
 	wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
@@ -816,7 +1095,7 @@
 	data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
 
 	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
-		       wpa_s->own_addr, pasn->bssid,
+		       pasn->own_addr, pasn->bssid,
 		       pasn->hash, mic_len * 2, data, data_len, mic);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
@@ -990,9 +1269,9 @@
 }
 
 
-static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			   int akmp, int cipher, u16 group, int freq,
-			   const u8 *beacon_rsne, u8 beacon_rsne_len,
+static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *own_addr,
+			   const u8 *bssid, int akmp, int cipher, u16 group,
+			   int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
 			   const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
 			   int network_id, struct wpabuf *comeback)
 {
@@ -1080,7 +1359,7 @@
 	pasn->group = group;
 	pasn->freq = freq;
 
-	derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
+	derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
 		ieee802_11_rsnx_capab(beacon_rsnxe,
 				      WLAN_RSNX_CAPAB_SECURE_LTF);
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1093,6 +1372,13 @@
 		pasn->kdk_len = 0;
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
 
+	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+	    ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+		pasn->secure_ltf = true;
+	else
+		pasn->secure_ltf = false;
+
+	os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
 	os_memcpy(pasn->bssid, bssid, ETH_ALEN);
 
 	wpa_printf(MSG_DEBUG,
@@ -1207,8 +1493,9 @@
 
 	rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
 
-	ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
-			      awork->group, bss->freq, rsne, *(rsne + 1) + 2,
+	ret = wpas_pasn_start(wpa_s, awork->own_addr, awork->bssid, awork->akmp,
+			      awork->cipher, awork->group, bss->freq,
+			      rsne, *(rsne + 1) + 2,
 			      rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
 			      awork->network_id, awork->comeback);
 	if (ret) {
@@ -1230,7 +1517,8 @@
 }
 
 
-int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
+			 const u8 *own_addr, const u8 *bssid,
 			 int akmp, int cipher, u16 group, int network_id,
 			 const u8 *comeback, size_t comeback_len)
 {
@@ -1272,6 +1560,7 @@
 	if (!awork)
 		return -1;
 
+	os_memcpy(awork->own_addr, own_addr, ETH_ALEN);
 	os_memcpy(awork->bssid, bssid, ETH_ALEN);
 	awork->akmp = akmp;
 	awork->cipher = cipher;
@@ -1321,19 +1610,29 @@
 	int akmp = pasn->akmp;
 	int cipher = pasn->cipher;
 	u16 group = pasn->group;
+	u8 own_addr[ETH_ALEN];
 	u8 bssid[ETH_ALEN];
 	int network_id = pasn->ssid ? pasn->ssid->id : 0;
 
 	wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
+	os_memcpy(own_addr, pasn->own_addr, ETH_ALEN);
 	os_memcpy(bssid, pasn->bssid, ETH_ALEN);
 	wpas_pasn_reset(wpa_s);
 
-	return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
+	return wpas_pasn_auth_start(wpa_s, own_addr, bssid, akmp, cipher, group,
 				    network_id,
 				    params->comeback, params->comeback_len);
 }
 
 
+static void wpas_pasn_deauth_cb(struct ptksa_cache_entry *entry)
+{
+	struct wpa_supplicant *wpa_s = entry->ctx;
+
+	wpas_pasn_deauthenticate(wpa_s, entry->own_addr, entry->addr);
+}
+
+
 int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 		      const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -1358,7 +1657,7 @@
 		return -2;
 
 	/* Not our frame; do nothing */
-	if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) != 0 ||
+	if (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
 	    os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
 	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
 		return -2;
@@ -1382,9 +1681,7 @@
 	    status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Authentication rejected - status=%u", status);
-		pasn->status = status;
-		wpas_pasn_auth_stop(wpa_s);
-		return -1;
+		goto fail;
 	}
 
 	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
@@ -1518,7 +1815,7 @@
 	}
 
 	ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
-			      wpa_s->own_addr, pasn->bssid,
+			      pasn->own_addr, pasn->bssid,
 			      wpabuf_head(secret), wpabuf_len(secret),
 			      &pasn->ptk, pasn->akmp, pasn->cipher,
 			      pasn->kdk_len);
@@ -1527,6 +1824,15 @@
 		goto fail;
 	}
 
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed to derive LTF keyseed");
+			goto fail;
+		}
+	}
+
 	wpabuf_free(wrapped_data);
 	wrapped_data = NULL;
 	wpabuf_free(secret);
@@ -1534,7 +1840,7 @@
 
 	/* Verify the MIC */
 	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
-		       pasn->bssid, wpa_s->own_addr,
+		       pasn->bssid, pasn->own_addr,
 		       wpabuf_head(pasn->beacon_rsne_rsnxe),
 		       wpabuf_len(pasn->beacon_rsne_rsnxe),
 		       (u8 *) &mgmt->u.auth,
@@ -1567,8 +1873,10 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
 
-	ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher,
-			dot11RSNAConfigPMKLifetime, &pasn->ptk);
+	ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->bssid,
+			pasn->cipher, dot11RSNAConfigPMKLifetime, &pasn->ptk,
+			wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
+			wpa_s->pasn_params ? wpa_s : NULL);
 
 	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
 
@@ -1590,10 +1898,65 @@
 		pasn->status = status;
 
 	wpas_pasn_auth_stop(wpa_s);
+
+	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
 	return -1;
 }
 
 
+void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s,
+			    struct pasn_auth *pasn_auth)
+{
+	struct pasn_peer *src, *dst;
+	unsigned int i, num_peers = pasn_auth->num_peers;
+
+	if (wpa_s->pasn_params) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth_trigger: Already in progress");
+		return;
+	}
+
+	if (!num_peers || num_peers > WPAS_MAX_PASN_PEERS) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth trigger: Invalid number of peers");
+		return;
+	}
+
+	wpa_s->pasn_params = os_zalloc(sizeof(struct pasn_auth));
+	if (!wpa_s->pasn_params) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth trigger: Failed to allocate a buffer");
+		return;
+	}
+
+	wpa_s->pasn_count = 0;
+	wpa_s->pasn_params->num_peers = num_peers;
+
+	for (i = 0; i < num_peers; i++) {
+		dst = &wpa_s->pasn_params->peer[i];
+		src = &pasn_auth->peer[i];
+		os_memcpy(dst->own_addr, wpa_s->own_addr, ETH_ALEN);
+		os_memcpy(dst->peer_addr, src->peer_addr, ETH_ALEN);
+		dst->ltf_keyseed_required = src->ltf_keyseed_required;
+		dst->status = PASN_STATUS_SUCCESS;
+
+		if (!is_zero_ether_addr(src->own_addr)) {
+			os_memcpy(dst->own_addr, src->own_addr, ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "PASN: Own (source) MAC addr: "
+				   MACSTR, MAC2STR(dst->own_addr));
+		}
+	}
+
+	if (pasn_auth->action == PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT) {
+		wpas_pasn_delete_peers(wpa_s, wpa_s->pasn_params);
+		os_free(wpa_s->pasn_params);
+		wpa_s->pasn_params = NULL;
+	} else if (pasn_auth->action == PASN_ACTION_AUTH) {
+		wpas_pasn_configure_next_peer(wpa_s, wpa_s->pasn_params);
+	}
+}
+
+
 int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
 			     const u8 *data, size_t data_len, u8 acked)
 
@@ -1621,7 +1984,7 @@
 
 	/* Not our frame; do nothing */
 	if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
-	    os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) ||
+	    os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) ||
 	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
 		return -1;
 
@@ -1653,14 +2016,25 @@
 		 * Either frame was not ACKed or it was ACKed but the trans_seq
 		 * != 1, i.e., not expecting an RX frame, so we are done.
 		 */
+		if (!wpa_s->pasn_params) {
+			wpas_pasn_auth_stop(wpa_s);
+			return 0;
+		}
+
+		wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr,
+					      pasn->bssid, pasn->cipher,
+					      pasn->akmp);
 		wpas_pasn_auth_stop(wpa_s);
+
+		wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
 	}
 
 	return 0;
 }
 
 
-int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
+			     const u8 *bssid)
 {
 	struct wpa_bss *bss;
 	struct wpabuf *buf;
@@ -1673,6 +2047,9 @@
 		return -1;
 	}
 
+	wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, bssid, 0, 0, NULL, 0,
+				       NULL, 1);
+
 	wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
 		   MACSTR, MAC2STR(bssid));
 	ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
@@ -1696,7 +2073,7 @@
 					     (WLAN_FC_STYPE_DEAUTH << 4));
 
 	os_memcpy(deauth->da, bssid, ETH_ALEN);
-	os_memcpy(deauth->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(deauth->sa, own_addr, ETH_ALEN);
 	os_memcpy(deauth->bssid, bssid, ETH_ALEN);
 	deauth->u.deauth.reason_code =
 		host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index f269fb6..a561891 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -706,14 +706,15 @@
 
 
 static int write_ipv4_info(char *pos, int total_len,
-			   const struct ipv4_params *v4)
+			   const struct ipv4_params *v4,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v4->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 source address");
@@ -728,7 +729,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 destination address");
@@ -743,7 +744,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -752,7 +753,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -761,7 +762,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -775,14 +776,15 @@
 
 
 static int write_ipv6_info(char *pos, int total_len,
-			   const struct ipv6_params *v6)
+			   const struct ipv6_params *v6,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET6_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v6->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 source addr");
@@ -797,7 +799,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 destination addr");
@@ -812,7 +814,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -821,7 +823,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -830,7 +832,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d",
 				  v6->next_header);
 		if (os_snprintf_error(rem_len, res))
@@ -861,7 +863,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v4.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v4.src_ip,
 			  &frame_classifier[3], 4);
 	}
@@ -874,14 +876,14 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v4.dst_ip,
 			  &frame_classifier[7], 4);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v4.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v4.src_port =
 			WPA_GET_BE16(&frame_classifier[11]);
 	}
@@ -894,7 +896,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v4.dst_port =
 			WPA_GET_BE16(&frame_classifier[13]);
 	}
@@ -903,7 +905,7 @@
 
 	/* Classifier Mask - bit 6 = Protocol */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v4.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v4.protocol = frame_classifier[16];
 	}
 
@@ -928,7 +930,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v6.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v6.src_ip,
 			  &frame_classifier[3], 16);
 	}
@@ -940,14 +942,14 @@
 				   "QM: IPv6: Both domain name and destination IP address not expected");
 			return -1;
 		}
-		type4_param->ip_params.v6.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v6.dst_ip,
 			  &frame_classifier[19], 16);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v6.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v6.src_port =
 				WPA_GET_BE16(&frame_classifier[35]);
 	}
@@ -960,7 +962,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v6.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v6.dst_port =
 				WPA_GET_BE16(&frame_classifier[37]);
 	}
@@ -969,7 +971,7 @@
 
 	/* Classifier Mask - bit 6 = Next Header */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v6.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v6.next_header = frame_classifier[40];
 	}
 
@@ -1075,9 +1077,11 @@
 		}
 
 		if (type4->ip_version == IPV4)
-			res = write_ipv4_info(pos, len, &type4->ip_params.v4);
+			res = write_ipv4_info(pos, len, &type4->ip_params.v4,
+					      type4->classifier_mask);
 		else
-			res = write_ipv6_info(pos, len, &type4->ip_params.v6);
+			res = write_ipv6_info(pos, len, &type4->ip_params.v6,
+					      type4->classifier_mask);
 
 		if (res <= 0) {
 			wpa_printf(MSG_ERROR,
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index f9e927a..ee90aac 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1371,8 +1371,6 @@
 	    (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) &&
 	    !is_p2p_allow_6ghz(wpa_s->global->p2p) &&
 	    is_6ghz_supported(wpa_s)) {
-		int i;
-
 		/* Exclude 6 GHz channels from the full scan for P2P connection
 		 * since the 6 GHz band is disabled for P2P uses. */
 		wpa_printf(MSG_DEBUG,
@@ -1932,6 +1930,18 @@
 }
 
 
+const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type)
+{
+	size_t ie_len = res->ie_len;
+
+	/* Use the Beacon frame IEs if res->ie_len is not available */
+	if (!ie_len)
+		ie_len = res->beacon_ie_len;
+
+	return get_ml_ie((const u8 *) (res + 1), ie_len, type);
+}
+
+
 /**
  * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
  * @res: Scan result entry
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index d1780eb..f826d91 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -51,6 +51,7 @@
 				struct scan_info *info, int new_scan);
 int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
 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,
 				  u32 vendor_type);
 const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 80cb6bb..a847e2f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -72,6 +72,7 @@
 		if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
 				wpa_s->sme.sae.group);
+			wpa_s->sme.sae.akmp = wpa_s->key_mgmt;
 			return 0;
 		}
 		wpa_s->sme.sae_group_index++;
@@ -153,6 +154,9 @@
 
 	if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
 		use_pt = 1;
+	if (wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt) &&
+	    wpa_s->conf->sae_pwe != 3)
+		use_pt = 1;
 #ifdef CONFIG_SAE_PK
 	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
 	    ssid->sae_pk != SAE_PK_MODE_DISABLED &&
@@ -174,7 +178,8 @@
 	if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
 		use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
 
-		if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
+		if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id ||
+		     wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
 		    wpa_s->conf->sae_pwe != 3 &&
 		    !use_pt) {
 			wpa_printf(MSG_DEBUG,
@@ -749,9 +754,9 @@
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
 	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
 				    NULL,
-				    wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ?
-				    WPA_KEY_MGMT_FT_SAE :
-				    WPA_KEY_MGMT_SAE) == 0) {
+				    wpa_key_mgmt_sae(wpa_s->key_mgmt) ?
+				    wpa_s->key_mgmt :
+				    (int) WPA_KEY_MGMT_SAE) == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
@@ -1112,6 +1117,7 @@
 {
 	struct external_auth params;
 
+	wpa_s->sme.ext_auth_wpa_ssid = NULL;
 	os_memset(&params, 0, sizeof(params));
 	params.status = status;
 	params.ssid = wpa_s->sme.ext_auth_ssid;
@@ -1130,13 +1136,18 @@
 	size_t ssid_str_len = data->external_auth.ssid_len;
 	const u8 *ssid_str = data->external_auth.ssid;
 
+	wpa_s->sme.ext_auth_wpa_ssid = NULL;
 	/* Get the SSID conf from the ssid string obtained */
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 		if (!wpas_network_disabled(wpa_s, ssid) &&
 		    ssid_str_len == ssid->ssid_len &&
 		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
-		    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
+		    wpa_key_mgmt_sae(ssid->key_mgmt)) {
+			/* Make sure PT is derived */
+			wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+			wpa_s->sme.ext_auth_wpa_ssid = ssid;
 			break;
+		}
 	}
 	if (!ssid ||
 	    sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
@@ -1263,7 +1274,8 @@
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+	    ((external && wpa_s->sme.ext_auth_wpa_ssid) ||
+	     (!external && wpa_s->current_bss && wpa_s->current_ssid))) {
 		int default_groups[] = { 19, 20, 21, 0 };
 		u16 group;
 		const u8 *token_pos;
@@ -1325,14 +1337,15 @@
 		else
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
 		return 0;
 	}
 
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+	    ((external && wpa_s->sme.ext_auth_wpa_ssid) ||
+	     (!external && wpa_s->current_bss && wpa_s->current_ssid))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
 		int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
 				     wpa_s->sme.sae.group);
@@ -1346,7 +1359,7 @@
 		else
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
 		return 0;
 	}
 
@@ -1378,8 +1391,9 @@
 		groups = wpa_s->conf->sae_groups;
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
-		if ((!external && wpa_s->current_bss == NULL) ||
-		    wpa_s->current_ssid == NULL)
+		if ((external && !wpa_s->sme.ext_auth_wpa_ssid) ||
+		    (!external &&
+		     (!wpa_s->current_bss || !wpa_s->current_ssid)))
 			return -1;
 		if (wpa_s->sme.sae.state != SAE_COMMITTED) {
 			wpa_printf(MSG_DEBUG,
@@ -1466,7 +1480,7 @@
 {
 	wpa_printf(MSG_DEBUG,
 		   "SME: SAE completed - setting PMK for 4-way handshake");
-	wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+	wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, wpa_s->sme.sae.pmk_len,
 		       wpa_s->sme.sae.pmkid, bssid);
 	if (wpa_s->conf->sae_pmkid_in_assoc) {
 		/* Update the own RSNE contents now that we have set the PMK
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 0e2315d..197efe0 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -413,6 +413,18 @@
 }
 
 
+static int wpa_cli_cmd_mlo_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "MLO_STATUS");
+}
+
+
+static int wpa_cli_cmd_mlo_signal_poll(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "MLO_SIGNAL_POLL");
+}
+
+
 static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -3134,7 +3146,7 @@
 static int wpa_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
 					    char *argv[])
 {
-	return wpa_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
+	return wpa_cli_cmd(ctrl, "DPP_CONTROLLER_START", 0, argc, argv);
 }
 
 
@@ -3159,6 +3171,15 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+static int wpa_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PUSH_BUTTON", 0, argc, argv);
+}
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 
 
@@ -3994,6 +4015,11 @@
 	  cli_cmd_flag_none,
 	  "= stop DPP chirp" },
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	{ "dpp_push_button", wpa_cli_cmd_dpp_push_button, NULL,
+	  cli_cmd_flag_none,
+	  "= press DPP push button" },
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 	{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
 	  "= list all BSS entries (scan results)" },
@@ -4023,6 +4049,12 @@
 	{ "dscp_query", wpa_cli_cmd_dscp_query, NULL,
 	  cli_cmd_flag_none,
 	  "wildcard/domain_name=<string> = Send DSCP Query" },
+	{ "mlo_status", wpa_cli_cmd_mlo_status, NULL,
+	  cli_cmd_flag_none,
+	  "= get MLO status" },
+	{ "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL,
+	  cli_cmd_flag_none,
+	  "= get mlo signal parameters" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index c63f3b2..be9f6e4 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -986,6 +986,13 @@
 	if (state == WPA_COMPLETED && wpa_s->new_connection) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		int fils_hlp_sent = 0;
+		char mld_addr[50];
+
+		mld_addr[0] = '\0';
+		if (wpa_s->valid_links)
+			os_snprintf(mld_addr, sizeof(mld_addr),
+				    " ap_mld_addr=" MACSTR,
+				    MAC2STR(wpa_s->ap_mld_addr));
 
 #ifdef CONFIG_SME
 		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
@@ -998,11 +1005,11 @@
 
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-			MACSTR " completed [id=%d id_str=%s%s]",
+			MACSTR " completed [id=%d id_str=%s%s]%s",
 			MAC2STR(wpa_s->bssid),
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "",
-			fils_hlp_sent ? " FILS_HLP_SENT" : "");
+			fils_hlp_sent ? " FILS_HLP_SENT" : "", mld_addr);
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_s->consecutive_conn_failures = 0;
@@ -1123,6 +1130,7 @@
 	wpa_s->group_cipher = 0;
 	wpa_s->mgmt_group_cipher = 0;
 	wpa_s->key_mgmt = 0;
+	wpa_s->allowed_key_mgmts = 0;
 	if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
 		wpa_supplicant_set_state(wpa_s, new_state);
 
@@ -1343,6 +1351,111 @@
 }
 
 
+static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+	int akm_count = wpa_s->max_num_akms;
+	u8 capab = 0;
+
+	if (akm_count < 2)
+		return;
+
+	akm_count--;
+	wpa_s->allowed_key_mgmts = 0;
+	switch (wpa_s->key_mgmt) {
+	case WPA_KEY_MGMT_PSK:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	case WPA_KEY_MGMT_PSK_SHA256:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		break;
+	case WPA_KEY_MGMT_SAE:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	default:
+		return;
+	}
+
+	if (wpa_s->conf->sae_pwe)
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+	if (ssid->sae_pk)
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+
+	if (!((wpa_s->allowed_key_mgmts &
+	       (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab))
+		return;
+
+	if (!wpa_s->rsnxe_len) {
+		wpa_s->rsnxe_len = 3;
+		wpa_s->rsnxe[0] = WLAN_EID_RSNX;
+		wpa_s->rsnxe[1] = 1;
+		wpa_s->rsnxe[2] = 0;
+	}
+
+	wpa_s->rsnxe[2] |= capab;
+}
+
+
 /**
  * wpa_supplicant_set_suites - Set authentication and encryption parameters
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1543,7 +1656,8 @@
 	sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
-		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
+		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_IEEE80211R
 	if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME |
@@ -1588,13 +1702,15 @@
 	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
-	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
-		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
 #endif /* CONFIG_IEEE80211R */
 	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+#ifdef CONFIG_IEEE80211R
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
 	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
@@ -1619,6 +1735,13 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_SAE
+	} else if (sel & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RSN: using KEY_MGMT FT/SAE (ext key)");
+	} else if (sel & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE (ext key)");
 	} else if (sel & WPA_KEY_MGMT_FT_SAE) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
@@ -1683,7 +1806,9 @@
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
 	sae_pwe = wpa_s->conf->sae_pwe;
-	if (ssid->sae_password_id && sae_pwe != 3)
+	if ((ssid->sae_password_id ||
+	     wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
+	    sae_pwe != 3)
 		sae_pwe = 1;
 	if (bss && is_6ghz_freq(bss->freq)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hash-to-element mode for 6GHz BSS.");
@@ -1906,6 +2031,10 @@
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0);
 	}
 
+	if (wpa_key_mgmt_cross_akm(wpa_s->key_mgmt) &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		wpas_update_allowed_key_mgmt(wpa_s, ssid);
+
 #ifdef CONFIG_DRIVER_NL80211_BRCM
 	if ((wpa_s->key_mgmt & WPA_KEY_MGMT_CROSS_AKM_ROAM) &&
 		IS_CROSS_AKM_ROAM_KEY_MGMT(ssid->key_mgmt) &&
@@ -2160,7 +2289,7 @@
 }
 
 
-static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid)
+void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid)
 {
 #ifdef CONFIG_SAE
 	int *groups = conf->sae_groups;
@@ -2176,6 +2305,7 @@
 
 	if (!password ||
 	    (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+	     !wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
 	     !sae_pk_valid_password(password)) ||
 	    conf->sae_pwe == 3) {
 		/* PT derivation not needed */
@@ -3114,7 +3244,7 @@
 #endif /* CONFIG_FILS */
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_SAE
-	if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
+	if (wpa_key_mgmt_sae(wpa_s->key_mgmt))
 		algs = WPA_AUTH_ALG_SAE;
 #endif /* CONFIG_SAE */
 
@@ -3920,6 +4050,7 @@
 	params.group_suite = cipher_group;
 	params.mgmt_group_suite = cipher_group_mgmt;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
+	params.allowed_key_mgmts = wpa_s->allowed_key_mgmts;
 	params.wpa_proto = wpa_s->wpa_proto;
 	wpa_s->auth_alg = params.auth_alg;
 	params.mode = ssid->mode;
@@ -3938,13 +4069,15 @@
 #endif /* CONFIG_WEP */
 
 	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
-	    (
 #ifdef CONFIG_DRIVER_NL80211_BRCM
-	     (params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
+	     ((params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
+	      (params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK))) {
 #else
-	     params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+	     (params.allowed_key_mgmts &
+	      (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK)))) {
 #endif /* CONFIG_DRIVER_NL80211_BRCM */
-	     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
 		params.passphrase = ssid->passphrase;
 		if (ssid->psk_set)
 			params.psk = ssid->psk;
@@ -3968,14 +4101,14 @@
 		else
 			params.req_key_mgmt_offload = 1;
 
-		if ((
 #ifdef CONFIG_DRIVER_NL80211_BRCM
-		     (params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
-#else
-		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+		if (((params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
 		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 		     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+#else
+		if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) ||
+		     wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) &&
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
 		    ssid->psk_set)
 			params.psk = ssid->psk;
 	}
@@ -6800,6 +6933,7 @@
 		wpa_s->num_multichan_concurrent =
 			capa.num_multichan_concurrent;
 		wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+		wpa_s->max_num_akms = capa.max_num_akms;
 
 		if (capa.mac_addr_rand_scan_supported)
 			wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
@@ -6814,6 +6948,9 @@
 		    wpa_s->extended_capa[2] & 0x40)
 			wpa_s->multi_bss_support = 1;
 	}
+#ifdef CONFIG_PASN
+	wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2);
+#endif /* CONFIG_PASN */
 	if (wpa_s->max_remain_on_chan == 0)
 		wpa_s->max_remain_on_chan = 1000;
 
@@ -8089,6 +8226,7 @@
 	    !ssid->mem_only_psk)
 		return 1;
 
+#ifdef IEEE8021X_EAPOL
 #ifdef CRYPTO_RSA_OAEP_SHA256
 	if (ssid->eap.imsi_privacy_cert) {
 		struct crypto_rsa_key *key;
@@ -8106,6 +8244,7 @@
 		}
 	}
 #endif /* CRYPTO_RSA_OAEP_SHA256 */
+#endif /* IEEE8021X_EAPOL */
 
 	return 0;
 }
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index fd54fef..8262eef 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1493,6 +1493,12 @@
 # 2: do not allow PFS to be used
 #dpp_pfs=0
 
+# DPP Network introduction type
+# 0: unprotected variant from DPP R1 (default)
+# 1: privacy protecting (station Connector encrypted) variant from
+#    DPP R3
+#dpp_connector_privacy=0
+
 # Whether beacon protection is enabled
 # This depends on management frame protection (ieee80211w) being enabled and
 # beacon protection support indication from the driver.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 047e88d..faec32b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -555,12 +555,14 @@
 	int akmp;
 	int cipher;
 	u16 group;
+	bool secure_ltf;
 	int freq;
 	size_t kdk_len;
 
 	u8 trans_seq;
 	u8 status;
 
+	u8 own_addr[ETH_ALEN];
 	u8 bssid[ETH_ALEN];
 	size_t pmk_len;
 	u8 pmk[PMK_LEN_MAX];
@@ -607,7 +609,6 @@
 	u16 dst_port;
 	u8 dscp;
 	u8 protocol;
-	u8 param_mask;
 };
 
 
@@ -619,7 +620,6 @@
 	u8 dscp;
 	u8 next_header;
 	u8 flow_label[3];
-	u8 param_mask;
 };
 
 
@@ -756,6 +756,14 @@
 	struct wpa_bss *current_bss;
 	int ap_ies_from_associnfo;
 	unsigned int assoc_freq;
+	u8 ap_mld_addr[ETH_ALEN];
+	u8 valid_links; /* bitmap of valid MLO link IDs */
+	struct {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		unsigned int freq;
+		struct wpa_bss *bss;
+	} links[MAX_NUM_MLD_LINKS];
 	u8 *last_con_fail_realm;
 	size_t last_con_fail_realm_len;
 
@@ -766,6 +774,11 @@
 	int key_mgmt;
 	int wpa_proto;
 	int mgmt_group_cipher;
+	/*
+	 * Allowed key management suites for roaming/initial connection
+	 * when the driver's SME is in use.
+	 */
+	int allowed_key_mgmts;
 
 	void *drv_priv; /* private data used by driver_ops */
 	void *global_drv_priv;
@@ -951,6 +964,7 @@
 	unsigned int max_match_sets;
 	unsigned int max_remain_on_chan;
 	unsigned int max_stations;
+	unsigned int max_num_akms;
 
 	int pending_mic_error_report;
 	int pending_mic_error_pairwise;
@@ -1037,6 +1051,7 @@
 		unsigned int sae_pmksa_caching:1;
 		u16 seq_num;
 		u8 ext_auth_bssid[ETH_ALEN];
+		struct wpa_ssid *ext_auth_wpa_ssid;
 		u8 ext_auth_ssid[SSID_MAX_LEN];
 		size_t ext_auth_ssid_len;
 		int *sae_rejected_groups;
@@ -1486,9 +1501,11 @@
 	int dpp_gas_dialog_token;
 	u8 dpp_intro_bssid[ETH_ALEN];
 	void *dpp_intro_network;
+	u8 dpp_intro_peer_version;
 	struct dpp_pkex *dpp_pkex;
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
+	size_t dpp_pkex_code_len;
 	char *dpp_pkex_identifier;
 	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
@@ -1519,6 +1536,27 @@
 	int dpp_reconfig_ssid_id;
 	struct dpp_reconfig_id *dpp_reconfig_id;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	struct os_reltime dpp_pb_time;
+	bool dpp_pb_configurator;
+	int *dpp_pb_freqs;
+	unsigned int dpp_pb_freq_idx;
+	unsigned int dpp_pb_announce_count;
+	struct wpabuf *dpp_pb_announcement;
+	struct dpp_bootstrap_info *dpp_pb_bi;
+	unsigned int dpp_pb_resp_freq;
+	u8 dpp_pb_init_hash[SHA256_MAC_LEN];
+	int dpp_pb_stop_iter;
+	bool dpp_pb_discovery_done;
+	u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+	size_t dpp_pb_c_nonce_len;
+	bool dpp_pb_result_indicated;
+	struct os_reltime dpp_pb_announce_time;
+	struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+	u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+	struct os_reltime dpp_pb_last_resp;
+	char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
 	char *dpp_discovery_override;
@@ -1544,6 +1582,8 @@
 #ifdef CONFIG_PASN
 	struct wpas_pasn pasn;
 	struct wpa_radio_work *pasn_auth_work;
+	unsigned int pasn_count;
+	struct pasn_auth *pasn_params;
 #endif /* CONFIG_PASN */
 	struct scs_robust_av_data scs_robust_av_req;
 	u8 scs_dialog_token;
@@ -1655,7 +1695,7 @@
 int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid);
 void wpa_supplicant_terminate_proc(struct wpa_global *global);
-void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *own_addr,
 			     const u8 *buf, size_t len,
 			     enum frame_encryption encrypted);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
@@ -1842,6 +1882,7 @@
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
+void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid);
 
 int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
 
@@ -1927,7 +1968,7 @@
 int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
 			 size_t domain_name_length);
 
-int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *own_addr,
 			 const u8 *bssid, int akmp, int cipher,
 			 u16 group, int network_id,
 			 const u8 *comeback, size_t comeback_len);
@@ -1938,6 +1979,10 @@
 		      const struct ieee80211_mgmt *mgmt, size_t len);
 int disabled_freq(struct wpa_supplicant *wpa_s, int freq);
 
-int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
+			     const u8 *bssid);
+void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s,
+			    struct pasn_auth *pasn_auth);
+void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status);
 
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 90af6eb..e13ea8f 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1343,7 +1343,7 @@
 #ifdef CONFIG_SAE
 	if ((bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) &&
 	    wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
-	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 	    (ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
 	     (ssid->group_cipher & WPA_CIPHER_TKIP))) {
 		wpa_printf(MSG_DEBUG,
@@ -1358,7 +1358,7 @@
 	    wpa_s->sme.sae.state == SAE_ACCEPTED &&
 	    wpa_s->sme.sae.pk &&
 #endif /* CONFIG_SME */
-	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 	    (ssid->sae_pk != SAE_PK_MODE_ONLY ||
 	     ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
 	     (ssid->group_cipher & WPA_CIPHER_TKIP))) {
@@ -1418,12 +1418,28 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	ptksa_cache_add(wpa_s->ptksa, addr, cipher, life_time, ptk);
+	ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, addr, cipher, life_time,
+			ptk, NULL, NULL);
 }
 
 #endif /* CONFIG_NO_WPA */
 
 
+#ifdef CONFIG_PASN
+static int wpa_supplicant_set_ltf_keyseed(void *_wpa_s, const u8 *own_addr,
+					  const u8 *peer_addr,
+					  size_t ltf_keyseed_len,
+					  const u8 *ltf_keyseed)
+{
+	struct wpa_supplicant *wpa_s = _wpa_s;
+
+	return wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, 0, 0,
+					      NULL, ltf_keyseed_len,
+					      ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -1486,6 +1502,9 @@
 	ctx->channel_info = wpa_supplicant_channel_info;
 	ctx->transition_disable = wpa_supplicant_transition_disable;
 	ctx->store_ptk = wpa_supplicant_store_ptk;
+#ifdef CONFIG_PASN
+	ctx->set_ltf_keyseed = wpa_supplicant_set_ltf_keyseed;
+#endif /* CONFIG_PASN */
 
 	wpa_s->wpa = wpa_sm_init(ctx);
 	if (wpa_s->wpa == NULL) {