cumilative patch from commit 795075444

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

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source

795075444 EAP-TEAP: Add a compatibility mode for FreeRADIUS
50af5c1c9 EAP-TEAP: Fix S-IMCK derivation based on RFC 7170bis
9e04dac3c WNM: A more explicit check for a connection
b9e798339 MBO: Get rid of the is_first flag in neighbor report array
1f4a2e825 WNM: Use standard BSS selection and enable abridged bit handling
f3a8e4284 WNM: Fix pre-scan rejection heuristic for BTM handling
42fe546e0 tests: get_bss_transition_status() driver op
53f9d6f69 WNM: Move driver MBO transition rejection into wnm_is_bss_excluded()
977edbdd9 EAP-TEAP: Remove deprecated PAC support
2d74d34d3 Clear the pending EAPOL RX on disconnection
84c904093 WPS: Fix a race condition on WPS_CANCEL handling
1888b7efd Use BSS-specific ACL configuration when setting up driver-based ACL
ce7b3c2ca MBSSID: Allow BSS Index and maximum number of BSSs to be configured
2fe7a2b80 Do not add extra IEs to scan request if they do not fit driver limit
66442d1bc nl80211: Fetch maximum length of extra IE(s) for Probe Request frames
df0b6fa90 Introduce DUMP_BEACON command
7b529aad9 EAP-TEAP: Don't complain about missing PAC when teap_provisioning=0
edd85280f Check last scan SSIDs before triggering new scan for hidden network
470e32c20 Update link to prplMesh repository
9d80d5d71 Handle CONFIG_NO_WPA for wpa_sm_has_ptk_installed()
3fa38e55c mka: Accept MKPDU sent to any multicast address
ee9cd7d74 OWE: Fix SSID comparison in transition mode case
093a01422 OWE: Accept only BSS entries with an actual SSID for ROAM command
38ccd0677 nl80211: Use wpa_msg() to report netlink errors
614d42a50 nl80211: Make drv pointer available to send_and_recv()
13a4d7b09 hostapd: Respect obss_interval on 40 MHz intolerant disconnect
f8b898244 P2P2: Use global interface for USD
75b25ffbe nl80211: Allow PASN Authentication frames on P2P device interface
abedecd14 P2P2: Fix frequency selection for auto GO join
546812862 P2P2: Make sure password is set when using non opportunistic methods
af7d1a012 P2P2: Report R2 information in P2P-DEVICE-FOUND event
53d15ddc5 NAN: Remove excessive debug print about the next timer
0c496d3c5 wpa_cli: Support NAN USD commands
06c5265f7 NAN: Fix A3 for unicast P2P2 USD
50c563785 NAN: Fix A3 for multicast P2P2 USD
c330b5820 Send CTRL-EVENT-SIGNAL-CHANGE message to control interfaces only
d17a85ea1 hostapd: hostapd_cleanup_iface_partial: Fix hw_features use after free
089e360cc Android: Wrap a P2P-specific chunk in CONFIG_P2P
44a0560f1 EAPOL: Fix PMK setting for driver-based FT-SHA384
d8772c195 DPP: Handle EVENT_TX_WAIT_EXPIRE path for push button
6b5e7c003 P2P2: Remove redundant enable_pairing_verification flag
a931a4782 Fix using invalid memory during driver deinit
f55b9c405 Update STA authorized flag for AP SME in driver cases for open network
65338c581 Fix CONFIG_NO_STDOUT_DEBUG=y build
fbc67eae4 Fix sae_pk_gen compilation
430bc89b9 Include base64 for hostapd CONFIG_SAE_PK builds
c9a3b3e75 ext_password_file: Do not use wpa_config_get_line()
0d3db4fe5 Fix nan_usd linker error
550513ee5 Comment out WPA related code additions with CONFIG_NO_WPA
631fbbcfc Use consistent ifdef CONFIG_NO_WPA blocks
6857b0c41 Add empty inline functions for CONFIG_NO_WPA
24df236ca Fix CONFIG_NO_WPA compile/link errors related to RSNXE
1f88e0463 Include HMAC-SHA384/512 KDF for SAE if SHA384/512 is included
4e8686cd1 hlr_auc_gw: Add TERMINATE command
b39a9cae8 AP MLD: Do not try to set hapd->mld multiple times
baeedceb0 hostapd: Fix wrong puncturing bitmap in Bandwidth Indication subelement
5a0572a49 hostapd: Move punct_bitmap into hostapd_freq_params
982c634ac hostapd: Fix length of Bandwidth Indication subelement
290a793b0 Avoid EAPOL trigger in reassoc path for AP with 4-way handshake offload
782b0c2ba AP MLD: Fix a crash in hostapd_driver_init()
4f05eead3 BSS: MLD: Parse all TBTT entries after an invalid link
26273daef BSS: MLD: Limit TBTT parsing to correct length
8e490da87 P2P: Consult driver capabilities before setting HE bit in GO's conf
ca0bb5cc9 mesh: Use the correct 6 GHz operating class 137 for 320 MHz bandwidth
01677c47f AP: Support disconnect with MLD
45f626113 P2P: Check P2P 6 GHz capability to start P2P GO
3e5d2dbea Add QCA vendor attributes to configure antenna selection
7c52ff140 P2P2: Allow P2P2 client to connect to GO with preconfigured credentials
305cd9049 P2P2: Handle join case without pending GO interface address
2552de375 EHT: Fix HE center frequency for EHT 320 MHz with puncturing
888c5b212 nl80211: Update channels unavailability for 320 MHz
338e79b6f AP MLD: Fix radar event processing
711e837fd EHT: Update legacy bandwidth when puncturing is set in 320 MHz
4bcf145ca EHT: Update legacy bandwidth for 320 MHz in Wide Bandwidth subelement
e3fe940ac nl80211: Use nl80211_bss_msg() helper wherever BSS is accessible
00c2c20d7 hostapd: Maintain single wpa_driver_nl80211_data (drv) object across interfaces
54eeaa075 Add a vendor attribute to disable DFS owner capability
7ff2ee8cd Remove empty line between vendor attribute documentation and definition
e833c5af3 Add new QCA vendor attributes for TWT session updatability
3f046c7ce QCA vendor attribute to configure operating type for monitor mode
c0e8b16a3 AP MLD: Fix max number of simultaneous links in MLE during CAC
acc35945a AP: Fix dangling pointer access during 6 GHz NO_IR channel list update
525f7d94c Force a global operating class to be used with Wi-Fi Agile Multiband
63d77c899 SAE: Reject association for no PMKID match only for PMKSA caching
04f4caad5 DPP: Deinit Configurator process if Config Requst is not received
93f623f74 AP MLD: Fix crash during config reload from non-ML to ML
5e6119234 AP: Handle (Re)Association Response frame if rsn_override_omit_rsnxe is set
9ec982cb4 AP MLD: Show maximum number of simultaneous links info for non-AP MLDs
4cbbcd3b6 AP MLD: Show AP MLD Type Indication in STATUS command
4c3b32932 AP MLD: Show puncture bitmap in STATUS command
64ac39354 P2P: Avoid infinite loop with radio_remove_works(p2p-listen)
ae5ef60ed AP MLD: Remove common elements from per STA profile
7855b6d60 AP MLD: Send EML capabilities of an ML station to the driver
e0aac2cd3 AP MLD: Add NULL check for mld pointer during MLD link removal
a158fecc9 AP MLD: Remove unnecessary wpa_group get and put for ML cases
6637b44a9 AP MLD: Remove unnecessary outer for loop in authorizing ML STA
475f50d71 P2P2: Allow op class and channel override for Invitation Response
ebffcffd7 P2P2: Set P2P mode (R1 vs. R2) in the driver
61a36a226 P2P2: P2P connection compatibility mode with RSN overriding
007d3f01b RSNO: Allow RSN overriding to be enabled for a specific network
89455cc07 Add new QCA vendor attributes for TWT setup parameters
b74e8c1b1 Add new QCA vendor TWT capability values
374b73d4d Add a new QCA vendor attribute for TWT session suspendability
25d29d65a P2P: Clear wpa_s->p2p2 for NFC cases
59c608bd8 wlantest: Use AP's RSNXOE for capabilities when RSNO is used
0c147e622 NAN USD: Do not start pause state for P2P2 on Subscribe message RX
cd8b3ad8d NAN USD: Use different group address for P2P2
0806c21db P2P2: Allow PASN-PTK to be fetched for testing purposes
bd010a7ec P2P2: Allow P2P-PMK to be extracted for testing purposes
4ebca723f P2P2: Allow device address change when reinvoking a persistent group
df37010ad P2P2: Command to remove all P2P2 identity keys
7f6fbba23 P2P2: Store device identity key in wpa_supplicant configuration
1525e84d4 Helper functions for fetching PMK and PMKID
d0e2570a7 P2P2: Store WPA3 connection credentials in the configuration
95c195014 P2P2: Fix memory leak in awork deinit case for PASN authentication
29912be14 hostapd: Pass link ID for non-link agnostic Action frames
65d865e62 AP: Avoid double free of key data buffer if AES unwrap fails
422c5dc91 AP: NULL pointer check for bssid in hostapd_mgmt_tx_cb()
37061ef86 nl80211: NULL pointer check for msg in i802_flush()
9fdbc5901 dbus: NAN USD: Actually use freq_list parameter in NANPublish
5920e2b97 Automatic generation of supported WFA generational capabilities on STA
d4cd22ac9 Convert wpa_s->hw_capab into a bitmap and add HE and EHT
8f5ae8801 Wi-Fi Generational Capabilities Indication transmission on STA
c97b35bd9 Wi-Fi Generational Capabilities Indication reception on AP
a0ba416e7 Definitions for Wi-Fi Alliance generational capabilities indication
74792088b AP: Update the list of Action frame categories that are not robust
e9fc4eec3 nl80211: Register to receive Vendor Specific Protected action frames
2c46ebfd2 Control interface command to generate new random MAC address
eea566cfa NAN: Do not expire USD services based on last TX/RX message
77ac40275 NAN: Wait on the channel with publisher is in pauseState
3d6360fb5 NAN: Limit pauseState to be within service lifetime
2817554a3 NAN: Print a debug entry on TX wait time expiration
296141fe9 NAN: Make DE aware of maximum driver supported listen time
b752dfc1d NAN: Do not unpause publisher on fixed Follow-up message timeout
4ea2c336d P2P2: Add wpa_supplicant configuration parameters for P2P2
5f507ffcc dbus: Increment introspection buffer size
e9daa3fca P2P2: Enable TWT and Channel Usage support by default
b62bef8c2 Channel Usage, peer-to-peer TWT and TWT requester support
41d09f844 P2P2: Configuration of channel switch request for testing purposes
c2f90ef35 Avoid undefined behavior in RSNXE capability bit checker
c009ac474 P2P2: Validate DIRA and configure PMK
417c67468 P2P2: Add device identity block to wpa_supplicant configuration
72e154dbd P2P2: Update P2P Device Address when changing netdev address
a9c33fa0d P2P: Print dst/src/bssid in Action frame TX debug message
ea4e0116d SAE: Do not allow password identifier to be used without H2E
afd120d03 SAE: Send Commit message with unknown-password-id from Nothing state
90a3b4a91 SAE: Do not use the wpa_passphrase if SAE password identifier was used
d3b5887f3 Add new QCA vendor attributes for TWT statistics
6742284dc Add new QCA vendor TWT status values
999ef0499 Reserve QCA vendor sub command id 255
75e767666 Add QCA vendor interface for additional TWT Setup command types
9ff75554a dbus: Notify P2P2 bootstrapping request and completed events
319c4b4e9 P2P2: Configure PMK/PMKID to the driver on the GO
f35c8a9ff P2P2: Enable PASN on a P2P GO
ae36944a9 Control interface command to flush NAN publish and subscribe sessions
e5c8774e4 P2P2: Fix pairing verification without encrypted elements
103314d55 P2P2: Determine PASN KEK derivation based on peer capabilities
be8f31acb PASN: Mark PMK and PMKID const in functions adding PMKSA entries
e5edc8a6f P2P2: Remember password from connect command to starting after scan
a01652bd5 P2P2: Start client for join without WPS
bfd799126 P2P2: Support for GO to allow a client to join the group
a2f634be8 P2P2: Provisioning step on GO when a client joins
ba5bf4a34 P2P2: Fix a typo in function documentation
564c969a5 P2P2: Fix peer entry generation based on USD
07ee3e8b0 Remove STA entries if association is not completed in 60 seconds
28ebad01e SAE: More robust password identifier checks for AP mode
8a3351055 SAE: Reject unexpected password identifier in commit message parser
18e8a525b SAE: Avoid duplicated debug entries for IEs in SAE commit messages
161327f91 hostapd: Fix clearing up settings for color switch
914f3fe8d P2P2: Do not add WPS IE to join-a-group scan
b41138a13 P2P2: Use PASN for joining a group
ebdc3b53b P2P2: Allow group to be added for P2P2 as autonomous GO
d91466a4f P2P2: Add PMKSA entry on successful group formation
b80b37910 P2P2: Fix to check if sae_password is present
96e48a05a P2P2: Invitation using pairing verification
4efc0bd02 P2P2: Do not override peer_addr from BSSID in pairing verification
eed859cdf P2P2: Indicate P2P2 group in GO parameters
050618c88 P2P2: Set up PMKSA for pairing verification
29ad7ffe7 P2P2: Export p2p_build_inviation_req()
e6a96dbce P2P2: Do not add WSC IE for P2P2 Invitation Request
ff02a87c1 P2P2: Fetch PMK and PMKID for invitation using pairing verification
431053e39 P2P2: Stop invitation process before sending out Invitation Request
71b443a98 P2P2: Indication on whether P2P2 is used with P2P_INVITE
7ba52f6a4 P2P2: Parameter setting for testing purpose
9d0aad0fa P2P2: Enable GCMP-256 as a pairwise cipher
c55d1f9e8 P2P2: Clone P2P2 and bootstrapping state to group interfaces
cf30af7c2 P2P2: Start P2P Client appropriately for P2P2 group
339f39e10 P2P2: Add P2P2 IE for groups using P2P2
fa4b6e37b P2P2: Start GO with suitable parameters for P2P2
240e1297d P2P2: Indicate SAE password and PMK from pairing with GO negotiation
6efcc17cb P2P: Clear GO negotiation results from stack after use
84a4e0004 P2P2: Select PMKSA based on P2P Device Address and PMKID match
048c30cb4 P2P2: Function callbacks for PASN
394beb560 P2P2: Add a SAE password in PASN Encrypted Data element
626a73a6a P2P2: Parse and store peer's SAE password
3207ad1ca P2P2: Parser function for PASN Encrypted Data element and DevIK
b787d1621 FILS: Verify RSNXE when processing (Re)Association Response frame
d1337b159 RSNO: Omit RSNXE in (Re)Association Response frame like in Beacon frame
a041777ff FT: Omit RSNXE from Reassociation Response frame only with FT protocol
ae6d2a5c0 AP: Use helper functions in ap_sta_disconnect()
dc9616f58 AP: Clean up MLD changes that modified skipping DMG deauthentication
6aef223ce Avoid memcmp() with NULL pointer even if for zero length
5b4c8bdd2 wpa_supplicant: 320 MHz bandwidth support for mesh
5834062c2 AP MLD: Allow link ID to be specified for Action frame TX operations
bfc89d757 nl80211: Handle radar event properly during MLO
2657e97c5 nl80211: Send link ID when starting CAC for radar detection
00daadff9 hostapd: Fix clearing old BSS during config reload
8035c11df Remove unused arguments in ieee802_11_parse_link_assoc_req()
b83859c78 scan: Pass correct link ID in all cases
9bc75ef93 Add a QCA vendor event to indicate status of the idle shutdown
a9e562b29 Update documentation of the QCA vendor ACS channel list attributes
23e8a42ca wlantest: Fix BIP replay protection check
ea2ff4b3f FT: Do not omit RSNXE from FT initial mobility domain association
ec198cfae SAE: Allow network profile sae_pwe to be configured
2e061a909 P2P2: PASN Authentication frame TX status handling
c449b2e09 P2P2: Initiate PASN on bootstrapping completion
e147d24a0 P2P2: Add support for GO Negotiation wrapped in PASN auth frame
9304e899f PASN: Store PASN authentication frames 1 and 2
8cef9b1b1 PASN: Extend maximum buffer length in 3rd auth frame
e15242565 PASN: Routines for generating and processing encrypted data
0ff802a51 P2P2: Set DevIK expiration time to 24 hours
5fb90cf3f SAE: Use sae_pwe in network profile for STA mode
f46eb8d3f SAE: Extend Basic MLE Recognition to external auth case
4d6ad78d0 Extend EAPOL frames processing workaround for reassociation to same AP
e09358447 STA: Update driver roaming policy on connection completion
8a257f9a5 Add QCA vendor command to fetch offload scan data from firmware
7cd1f3c0a Add TEST_RSNXE_DATA for RSNXE testing of AP functionality
bb1316baa Allow forced enabling of EAPOL-Key msg 2/4 key info bits for testing
236d25e75 QCA vendor interface to exclude 6 GHz non-PSC channels as primary channel in ACS
eb529b0b5 Add QCA vendor status for TWT termination due to multiple MLO links activated
9e22afa48 Fix STA's SSID protection capability when AP SME is offloaded to driver
85cd98976 dbus: Methods for NAN USD
dcf58aec8 dbus: Signals for NAN USD
d2408e303 dbus: Dict helpers for fetching integers of any type
fd1a149d9 NAN: Fix UpdatePublish offload to driver
b3bd49f3c NAN: Handle A3 copying internally to simplify control interface
fbbc9cb9e NAN: Update A3 for USD to use NAN Network ID or NAN Cluster ID in A3
e0496580a hostapd: Add drv_send_action variant for forcing A3
83f9dcbb3 NAN: Process received NAN SDFs with NAN Network ID in A3 on AP
ccba6921d SAE: Recognize Basic MLE in Authentication frames even without H2E
c97168f58 FT: Discard EAPOL-Start frames when FT was used for association
f54359915 nl80211: Remove nl_msg free on send failure for NAN USD commands
61960e6c6 P2P2: Add alternative PASN RX handler
7d13410a8 SAE: Mark the groups argument to sae_derive_pt() const
9edd8b441 nl80211: Fix conditional checks of nlmsg attributes for NAN publish

Change-Id: If300ab0f4e93d221e7e576a08e11825cb0fd0bf9
Signed-off-by: Sunil Ravi <sunilravi@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 680c572..5674962 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -276,6 +276,7 @@
 ifdef CONFIG_SAE_PK
 L_CFLAGS += -DCONFIG_SAE_PK
 NEED_AES_SIV=y
+NEED_BASE64=y
 OBJS += src/common/sae_pk.c
 endif
 NEED_ECC=y
@@ -956,6 +957,17 @@
 endif
 endif
 
+ifdef CONFIG_SAE
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
 L_CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), gnutls)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 489922c..4c31ba9 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -299,6 +299,7 @@
 ifdef CONFIG_SAE_PK
 CFLAGS += -DCONFIG_SAE_PK
 NEED_AES_SIV=y
+NEED_BASE64=y
 OBJS += ../src/common/sae_pk.o
 endif
 NEED_ECC=y
@@ -1069,6 +1070,17 @@
 endif
 endif
 
+ifdef CONFIG_SAE
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
@@ -1369,6 +1381,8 @@
 SOBJS += ../src/common/sae_pk.o
 SOBJS += ../src/common/dragonfly.o
 SOBJS += $(AESOBJS)
+SOBJS += ../src/crypto/sha384.o
+SOBJS += ../src/crypto/sha512.o
 SOBJS += ../src/crypto/sha256-prf.o
 SOBJS += ../src/crypto/sha384-prf.o
 SOBJS += ../src/crypto/sha512-prf.o
@@ -1376,6 +1390,10 @@
 SOBJS += ../src/crypto/sha256-kdf.o
 SOBJS += ../src/crypto/sha384-kdf.o
 SOBJS += ../src/crypto/sha512-kdf.o
+SOBJS += ../src/common/wpa_common.o
+SOBJS += ../src/crypto/random.o
+SOBJS += ../src/crypto/sha1-prf.o
+SOBJS += ../src/utils/eloop.o
 
 _OBJS_VAR := NOBJS
 include ../src/objs.mk
diff --git a/hostapd/README-MULTI-AP b/hostapd/README-MULTI-AP
index ccee69e..6190cac 100644
--- a/hostapd/README-MULTI-AP
+++ b/hostapd/README-MULTI-AP
@@ -155,6 +155,6 @@
 ----------
 
 [1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
-[2] https://github.com/prplfoundation/prplMesh
+[2] https://gitlab.com/prpl-foundation/prplmesh/prplMesh
 [3] https://www.wi-fi.org/file/multi-ap-specification-v10
     (requires registration)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 9470cae..f4a6aeb 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2756,8 +2756,6 @@
 			return 1;
 		}
 		bss->eap_teap_auth = val;
-	} else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
-		bss->eap_teap_pac_no_inner = atoi(pos);
 	} else if (os_strcmp(buf, "eap_teap_separate_result") == 0) {
 		bss->eap_teap_separate_result = atoi(pos);
 	} else if (os_strcmp(buf, "eap_teap_id") == 0) {
@@ -3964,6 +3962,10 @@
 			return 1;
 		}
 		conf->mbssid = mbssid;
+	} else if (os_strcmp(buf, "mbssid_index") == 0) {
+		bss->mbssid_index = atoi(pos);
+	} else if (os_strcmp(buf, "mbssid_max") == 0) {
+		conf->mbssid_max = atoi(pos);
 #endif /* CONFIG_IEEE80211AX */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
@@ -5132,6 +5134,10 @@
 		if (val < 0 || val > 1)
 			return 1;
 		bss->ssid_protection = val;
+	} else if (os_strcmp(buf, "channel_usage") == 0) {
+		conf->channel_usage = atoi(pos);
+	} else if (os_strcmp(buf, "peer_to_peer_twt") == 0) {
+		conf->peer_to_peer_twt = atoi(pos);
 #ifdef CONFIG_IEEE80211BE
 	} else if (os_strcmp(buf, "ieee80211be") == 0) {
 		conf->ieee80211be = atoi(pos);
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index ea19ba7..dd445d8 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2707,7 +2707,7 @@
 	}
 
 	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
-					     settings.punct_bitmap);
+					     settings.freq_params.punct_bitmap);
 	if (ret) {
 		wpa_printf(MSG_INFO,
 			   "chanswitch: invalid frequency settings provided");
@@ -3210,6 +3210,7 @@
 
 
 #ifdef NEED_AP_MLME
+
 static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
 					     char *buf, size_t buflen)
 {
@@ -3243,6 +3244,35 @@
 
 	return pos - buf;
 }
+
+
+static int hostapd_ctrl_iface_dump_beacon(struct hostapd_data *hapd,
+					  char *buf, size_t buflen)
+{
+	struct beacon_data beacon;
+	char *pos, *end;
+	int ret;
+
+	if (hostapd_build_beacon_data(hapd, &beacon) < 0)
+		return -1;
+
+	if (2 * (beacon.head_len + beacon.tail_len) > buflen)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	ret = wpa_snprintf_hex(pos, end - pos, beacon.head, beacon.head_len);
+	pos += ret;
+
+	ret = wpa_snprintf_hex(pos, end - pos, beacon.tail, beacon.tail_len);
+	pos += ret;
+
+	free_beacon_data(&beacon);
+
+	return pos - buf;
+}
+
 #endif /* NEED_AP_MLME */
 
 
@@ -4026,7 +4056,7 @@
 	}
 
 	ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr,
-				    req_instance_id);
+				       req_instance_id);
 fail:
 	wpabuf_free(ssi);
 	return ret;
@@ -4347,6 +4377,9 @@
 	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
 		reply_len = hostapd_ctrl_iface_track_sta_list(
 			hapd, reply, reply_size);
+	} else if (os_strcmp(buf, "DUMP_BEACON") == 0) {
+		reply_len = hostapd_ctrl_iface_dump_beacon(hapd, reply,
+							   reply_size);
 #endif /* NEED_AP_MLME */
 	} else if (os_strcmp(buf, "PMKSA") == 0) {
 		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 5caa779..291acf9 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -66,6 +66,7 @@
 static int sqn_changes = 0;
 static int ind_len = 5;
 static int stdout_debug = 1;
+static bool stop = false;
 
 /* GSM triplets */
 struct gsm_triplet {
@@ -877,6 +878,12 @@
 	if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
 		return aka_auts(cmd + 9, resp, resp_len);
 
+	if (strncmp(cmd, "TERMINATE", 9) == 0) {
+		stop = true;
+		resp[0] = '\0';
+		return 0;
+	}
+
 	printf("Unknown request: %s\n", cmd);
 	return -1;
 }
@@ -931,10 +938,13 @@
 	struct gsm_triplet *g, *gprev;
 	struct milenage_parameters *m, *prev;
 
-	if (update_milenage && milenage_file && sqn_changes)
+	if (update_milenage && milenage_file && sqn_changes) {
+		sqn_changes = 0;
 		update_milenage_file(milenage_file);
+	}
 
 	g = gsm_db;
+	gsm_db = NULL;
 	while (g) {
 		gprev = g;
 		g = g->next;
@@ -942,16 +952,21 @@
 	}
 
 	m = milenage_db;
+	milenage_db = NULL;
 	while (m) {
 		prev = m;
 		m = m->next;
 		os_free(prev);
 	}
 
-	if (serv_sock >= 0)
+	if (serv_sock >= 0) {
 		close(serv_sock);
-	if (socket_path)
+		serv_sock = -1;
+	}
+	if (socket_path) {
 		unlink(socket_path);
+		socket_path = NULL;
+	}
 
 #ifdef CONFIG_SQLITE
 	if (sqlite_db) {
@@ -965,6 +980,7 @@
 static void handle_term(int sig)
 {
 	printf("Signal %d - terminate\n", sig);
+	cleanup();
 	exit(0);
 }
 
@@ -973,7 +989,7 @@
 {
 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 	       "database/authenticator\n"
-	       "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
+	       "Copyright (c) 2005-2024, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
 	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
@@ -1081,8 +1097,9 @@
 		signal(SIGTERM, handle_term);
 		signal(SIGINT, handle_term);
 
-		for (;;)
+		while (!stop)
 			process(serv_sock);
+		cleanup();
 	} else {
 		char buf[1000];
 		socket_path = NULL;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 93524cf..b1e8ac5 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1030,6 +1030,18 @@
 # Valid range: 0..20 TUs; default is 0 (disabled)
 #unsol_bcast_probe_resp_interval=0
 
+#channel_usage: Whether Channel Usage procedures is supported by AP.
+# 0 = Channel Usage support is disabled (default)
+# 1 = Channel Usage support is enabled
+#channel_usage=0
+
+#peer_to_peer_twt: Indicates an HE AP supports negotiating a peer-to-peer
+# TWT schedule that is requested by a non-AP STA to establish a
+# channel-usage-aidable BSS or an off-channel TDLS direct link.
+# 0 = Does not support Peer-to-peer TWT (default)
+# 1 = Supports Peer-to-peer TWT
+#peer_to_peer_twt=0
+
 ##### IEEE 802.11be related configuration #####################################
 
 #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
@@ -3411,6 +3423,18 @@
 # 2 = Enhanced multiple BSSID advertisement enabled.
 #mbssid=0
 #
+# Maximum number of BSSs that can be added into a Multiple BSSID set
+# This is a radio level parameter. If not set (or 0), the maximum is determined
+# automatically based on the configured BSSs which may limit dynamic addition
+# of new BSSs.
+#mbssid_max=0
+#
+# Multiple BSSID Index override
+# This is a BSS level parameter. If not set (or 0), the BSSID index is
+# determined automatically based on the configured BSSs which may limit dynamic
+# addition of new BSSs.
+#mbssid_index=0
+#
 # The transmitting interface should be added with the 'interface' option while
 # the non-transmitting interfaces should be added using the 'bss' option.
 # Security configuration should be added separately per interface, if required.
diff --git a/hostapd/main.c b/hostapd/main.c
index 50b9f04..5769fa0 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -162,6 +162,7 @@
 	struct wpa_driver_capa capa;
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_data *h_hapd = NULL;
+	void *shared_hapd = NULL;
 #endif /* CONFIG_IEEE80211BE */
 
 	if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
@@ -170,8 +171,11 @@
 	}
 
 #ifdef CONFIG_IEEE80211BE
-	if (conf->mld_ap)
+	if (conf->mld_ap) {
+		if (!hapd->mld)
+			hostapd_bss_setup_multi_link(hapd, iface->interfaces);
 		h_hapd = hostapd_mld_get_first_bss(hapd);
+	}
 
 	if (h_hapd) {
 		hapd->drv_priv = h_hapd->drv_priv;
@@ -255,6 +259,42 @@
 
 	params.own_addr = hapd->own_addr;
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->driver->can_share_drv &&
+	    hapd->driver->can_share_drv(hapd, &params, &shared_hapd)) {
+		char force_ifname[IFNAMSIZ];
+		const u8 *addr = params.bssid;
+		u8 if_addr[ETH_ALEN];
+
+		if (!shared_hapd) {
+			wpa_printf(MSG_ERROR, "Failed to get the shared drv");
+			os_free(params.bridge);
+			return -1;
+		}
+
+		/* Share an already initialized driver interface instance
+		 * using an AP mode BSS in it instead of adding a new driver
+		 * interface instance for the same driver. */
+		if (hostapd_if_add(shared_hapd, WPA_IF_AP_BSS,
+				   params.ifname, addr, hapd,
+				   &hapd->drv_priv, force_ifname, if_addr,
+				   params.num_bridge && params.bridge[0] ?
+				   params.bridge[0] : NULL,
+				   0)) {
+			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+				   MACSTR ")", MAC2STR(hapd->own_addr));
+			os_free(params.bridge);
+			return -1;
+		}
+		os_free(params.bridge);
+
+		hapd->interface_added = 1;
+		os_memcpy(params.own_addr, addr ? addr : if_addr, ETH_ALEN);
+
+		goto pre_setup_mld;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
 	os_free(params.bridge);
 	if (hapd->drv_priv == NULL) {
@@ -265,6 +305,7 @@
 	}
 
 #ifdef CONFIG_IEEE80211BE
+pre_setup_mld:
 	/*
 	 * This is the first interface added to the AP MLD, so have the
 	 * interface hardware address be the MLD address, while the link address
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 55f3b64..d33ba9d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -451,7 +451,6 @@
 	int pac_key_lifetime;
 	int pac_key_refresh_time;
 	int eap_teap_auth;
-	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	int eap_teap_id;
 	int eap_teap_method_sequence;
@@ -995,6 +994,7 @@
 	bool mld_indicate_disabled;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_IEEE80211BE */
+	int mbssid_index;
 };
 
 /**
@@ -1248,9 +1248,13 @@
 		MBSSID_ENABLED = 1,
 		ENHANCED_MBSSID_ENABLED = 2,
 	} mbssid;
+	unsigned int mbssid_max;
 
 	/* Whether to enable TWT responder in HT and VHT modes */
 	bool ht_vht_twt_responder;
+
+	bool channel_usage;
+	bool peer_to_peer_twt;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 92dbc16..65e83f4 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -116,9 +116,11 @@
 		goto fail;
 #endif /* CONFIG_FILS */
 
-	pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
-	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
-		goto fail;
+	if (!hapd->conf->rsn_override_omit_rsnxe) {
+		pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
+		if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+			goto fail;
+	}
 
 	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
 	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
@@ -472,7 +474,8 @@
 		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
-		    int set, const u8 *link_addr, bool mld_link_sta)
+		    int set, const u8 *link_addr, bool mld_link_sta,
+		    u16 eml_cap)
 {
 	struct hostapd_sta_add_params params;
 
@@ -512,6 +515,9 @@
 		params.mld_link_id = hapd->mld_link_id;
 		params.mld_link_addr = link_addr;
 		params.mld_link_sta = mld_link_sta;
+		/* Copy EML capabilities of ML STA */
+		if (link_addr)
+			params.eml_cap = eml_cap;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -781,6 +787,12 @@
 int hostapd_driver_scan(struct hostapd_data *hapd,
 			struct wpa_driver_scan_params *params)
 {
+	params->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		params->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (hapd->driver && hapd->driver->scan2)
 		return hapd->driver->scan2(hapd->drv_priv, params);
 	return -1;
@@ -916,9 +928,50 @@
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+static bool hostapd_is_action_frame_link_agnostic(u8 category, u8 sub_category)
+{
+	/* As per IEEE P802.11be/D7.0, 35.3.14 (MLD individually addressed
+	 * Management frame delivery), between an AP MLD and a non-AP MLD, the
+	 * following individually addressed MMPDUs shall be intended for an MLD.
+	 */
+	switch (category) {
+	case WLAN_ACTION_BLOCK_ACK:
+	case WLAN_ACTION_FT:
+	case WLAN_ACTION_SA_QUERY:
+	case WLAN_ACTION_WNM:
+		switch (sub_category) {
+		case WNM_BSS_TRANS_MGMT_REQ:
+		case WNM_BSS_TRANS_MGMT_RESP:
+		case WNM_SLEEP_MODE_REQ:
+		case WNM_SLEEP_MODE_RESP:
+			return true;
+		default:
+			return false;
+		}
+	case WLAN_ACTION_ROBUST_AV_STREAMING:
+		switch (sub_category) {
+		case ROBUST_AV_SCS_REQ:
+		case ROBUST_AV_SCS_RESP:
+		case ROBUST_AV_MSCS_REQ:
+		case ROBUST_AV_MSCS_RESP:
+			return true;
+		default:
+			return false;
+		}
+	/* TODO: Handle EHT/EPCS related action frames once the support is
+	 * added. */
+	default:
+		return false;
+	}
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 				unsigned int wait, const u8 *dst,
-				const u8 *data, size_t len, bool addr3_ap)
+				const u8 *data, size_t len, bool addr3_ap,
+				const u8 *forced_a3)
 {
 	const u8 *own_addr = hapd->own_addr;
 	const u8 *bssid;
@@ -926,12 +979,15 @@
 		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 	};
 	struct sta_info *sta;
+	int link_id = -1;
 
 	if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
 		return 0;
 	bssid = hapd->own_addr;
-	if (!addr3_ap && !is_multicast_ether_addr(dst) &&
-	    len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+	if (forced_a3) {
+		bssid = forced_a3;
+	} else if (!addr3_ap && !is_multicast_ether_addr(dst) &&
+		   len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
 		/*
 		 * Public Action frames to a STA that is not a member of the BSS
 		 * shall use wildcard BSSID value.
@@ -956,11 +1012,15 @@
 			own_addr = hapd->mld->mld_addr;
 			bssid = own_addr;
 		}
+
+		if (!hostapd_is_action_frame_link_agnostic(data[0], data[1]))
+			link_id = hapd->mld_link_id;
 #endif /* CONFIG_IEEE80211BE */
 	}
 
 	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
-					 own_addr, bssid, data, len, 0);
+					 own_addr, bssid, data, len, 0,
+					 link_id);
 }
 
 
@@ -968,7 +1028,8 @@
 			    unsigned int wait, const u8 *dst, const u8 *data,
 			    size_t len)
 {
-	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false);
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+				    NULL);
 }
 
 
@@ -977,7 +1038,19 @@
 				     unsigned int wait, const u8 *dst,
 				     const u8 *data, size_t len)
 {
-	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true,
+				    NULL);
+}
+
+
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+					 unsigned int freq,
+					 unsigned int wait, const u8 *dst,
+					 const u8 *a3,
+					 const u8 *data, size_t len)
+{
+	return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false,
+				    a3);
 }
 
 
@@ -1018,6 +1091,12 @@
 	}
 	data.radar_background = radar_background;
 
+	data.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		data.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
 	if (!res) {
 		if (radar_background)
@@ -1261,3 +1340,27 @@
 
 	return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws);
 }
+
+
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+			  struct wpa_pmkid_params *params)
+{
+	if (!hapd->driver || !hapd->driver->add_pmkid || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->add_pmkid(hapd->drv_priv, params);
+}
+
+
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+		      size_t pmk_len, const u8 *pmkid, int akmp)
+{
+	struct wpa_pmkid_params params;
+
+	os_memset(&params, 0, sizeof(params));
+	params.bssid = bssid;
+	params.pmkid = pmkid;
+	params.pmk = pmk;
+	params.pmk_len = pmk_len;
+
+	return hostapd_drv_add_pmkid(hapd, &params);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 6b7f02a..cbb8044 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -49,7 +49,8 @@
 		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
-		    int set, const u8 *link_addr, bool mld_link_sta);
+		    int set, const u8 *link_addr, bool mld_link_sta,
+		    u16 eml_cap);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 			     size_t elem_len);
@@ -116,6 +117,11 @@
 				     unsigned int freq,
 				     unsigned int wait, const u8 *dst,
 				     const u8 *data, size_t len);
+int hostapd_drv_send_action_forced_addr3(struct hostapd_data *hapd,
+					 unsigned int freq,
+					 unsigned int wait, const u8 *dst,
+					 const u8 *a3,
+					 const u8 *data, size_t len);
 static inline void
 hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
 {
@@ -482,4 +488,9 @@
 hostapd_get_multi_hw_info(struct hostapd_data *hapd,
 			  unsigned int *num_multi_hws);
 
+int hostapd_drv_add_pmkid(struct hostapd_data *hapd,
+			  struct wpa_pmkid_params *params);
+int hostapd_add_pmkid(struct hostapd_data *hapd, const u8 *bssid, const u8 *pmk,
+		      size_t pmk_len, const u8 *pmkid, int akmp);;
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 837b690..630cef6 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -224,7 +224,6 @@
 	cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
 	cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
 	cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
-	cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
 	cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
 	cfg->eap_teap_id = hapd->conf->eap_teap_id;
 	cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 2e3d904..542768d 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -262,6 +262,7 @@
 {
 	u8 *pos = eid;
 	u8 *end = eid + max_len;
+	bool force_global;
 
 	if (!hapd->iconf->ieee80211d || max_len < 6 ||
 	    hapd->iface->current_mode == NULL)
@@ -272,11 +273,23 @@
 	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
 	pos += 3;
 
-	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+	/* The 6 GHz band uses global operating classes */
+	force_global = is_6ghz_op_class(hapd->iconf->op_class);
+
+#ifdef CONFIG_MBO
+	/* Wi-Fi Agile Muiltiband AP is required to use a global operating
+	 * class. */
+	if (hapd->conf->mbo_enabled)
+		force_global = true;
+#endif /* CONFIG_MBO */
+
+	if (force_global) {
 		/* Force the third octet of the country string to indicate
 		 * Global Operating Class (Table E-4) */
 		eid[4] = 0x04;
+	}
 
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
 		/* Operating Triplet field */
 		/* Operating Extension Identifier (>= 201 to indicate this is
 		 * not a Subband Triplet field) */
@@ -2909,7 +2922,15 @@
 					is_identical_vendor_ies = true;
 					num_own_elem_vendor_ies++;
 				}
-				continue;
+
+				/* Update the parsed EIDs bitmap */
+				if (is_ext)
+					parsed_ext_eid_bmap[own_eid / 8] |=
+						BIT(own_eid % 8);
+				else
+					parsed_eid_bmap[own_eid / 8] |=
+						BIT(own_eid % 8);
+				break;
 			}
 
 			/* No need to include this non-matching Vendor Specific
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index b93a5d2..4a51e63 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -475,6 +475,10 @@
 
 #ifdef CONFIG_IEEE80211BE
 	if (sta->mld_info.mld_sta) {
+		u16 mld_sta_capa = sta->mld_info.common_info.mld_capa;
+		u8 max_simul_links = mld_sta_capa &
+			EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
+
 		for (i = 0; i < MAX_NUM_MLD_LINKS; ++i) {
 			if (!sta->mld_info.links[i].valid)
 				continue;
@@ -485,6 +489,11 @@
 			if (!os_snprintf_error(buflen - len, ret))
 				len += ret;
 		}
+
+		ret = os_snprintf(buf + len, buflen - len,
+				  "max_simul_links=%d\n", max_simul_links);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -917,6 +926,15 @@
 			len += ret;
 		}
 
+		if (hapd->iconf->punct_bitmap) {
+			ret = os_snprintf(buf + len, buflen - len,
+					  "punct_bitmap=0x%x\n",
+					  hapd->iconf->punct_bitmap);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+		}
+
 		if (hapd->conf->mld_ap) {
 			struct hostapd_data *link_bss;
 
@@ -951,6 +969,15 @@
 					return len;
 				len += ret;
 			}
+
+			ret = os_snprintf(buf + len, buflen - len,
+					  "ap_mld_type=%s\n",
+					  (hapd->iface->mld_mld_capa &
+					   EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK)
+					  ? "NSTR" : "STR");
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
 		}
 	}
 #endif /* CONFIG_IEEE80211BE */
@@ -1127,20 +1154,11 @@
 		} \
 	} while (0)
 
-#define SET_CSA_SETTING_EXT(str) \
-	do { \
-		const char *pos2 = os_strstr(pos, " " #str "="); \
-		if (pos2) { \
-			pos2 += sizeof(" " #str "=") - 1; \
-			settings->str = atoi(pos2); \
-		} \
-	} while (0)
-
 	SET_CSA_SETTING(center_freq1);
 	SET_CSA_SETTING(center_freq2);
 	SET_CSA_SETTING(bandwidth);
 	SET_CSA_SETTING(sec_channel_offset);
-	SET_CSA_SETTING_EXT(punct_bitmap);
+	SET_CSA_SETTING(punct_bitmap);
 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
@@ -1148,7 +1166,6 @@
 		settings->freq_params.eht_enabled;
 	settings->block_tx = !!os_strstr(pos, " blocktx");
 #undef SET_CSA_SETTING
-#undef SET_CSA_SETTING_EXT
 
 	return 0;
 }
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index d1bffa8..3dc4639 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1382,6 +1382,21 @@
 }
 
 
+static void hostapd_gas_req_wait(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Timeout while waiting for Config Request");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
@@ -1400,6 +1415,9 @@
 
 	if (!hapd->dpp_auth->configurator)
 		hostapd_dpp_start_gas_client(hapd);
+	else
+		eloop_register_timeout(10, 0, hostapd_gas_req_wait,
+				       hapd, NULL);
 }
 
 
@@ -3079,6 +3097,7 @@
 	struct wpabuf *resp;
 
 	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
 	    !ether_addr_equal(sa, auth->peer_mac_addr)) {
 #ifdef CONFIG_DPP2
@@ -3359,6 +3378,7 @@
 #ifdef CONFIG_DPP3
 	hostapd_dpp_push_button_stop(hapd);
 #endif /* CONFIG_DPP3 */
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 }
 
 
@@ -3512,6 +3532,7 @@
 	eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
 			     hapd, NULL);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 05adc41..82a922e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,6 +18,7 @@
 #include "common/dpp.h"
 #include "common/sae.h"
 #include "common/hw_features_common.h"
+#include "common/nan_de.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -677,6 +678,13 @@
 			goto fail;
 		}
 #endif /* CONFIG_SAE */
+
+		wpa_auth_set_ssid_protection(
+			sta->wpa_sm,
+			hapd->conf->ssid_protection &&
+			ieee802_11_rsnx_capab_len(
+				elems.rsnxe, elems.rsnxe_len,
+				WLAN_RSNX_CAPAB_SSID_PROTECTION));
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -950,6 +958,10 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (elems.wfa_capab)
+		hostapd_wfa_capab(hapd, sta, elems.wfa_capab,
+				  elems.wfa_capab + elems.wfa_capab_len);
+
 	return 0;
 
 fail:
@@ -1787,8 +1799,8 @@
 		pos = mgmt->u.action.u.vs_public_action.variable;
 		end = drv_mgmt->frame + drv_mgmt->frame_len;
 		pos++;
-		hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
-				       pos, end - pos);
+		hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+				       drv_mgmt->freq, pos, end - pos);
 		return;
 	}
 #endif /* CONFIG_NAN_USD */
@@ -1855,6 +1867,11 @@
 	if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
 	    bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
 		return HAPD_BROADCAST;
+#ifdef CONFIG_NAN_USD
+	if (nan_de_is_nan_network_id(bssid))
+		return HAPD_BROADCAST; /* Process NAN Network ID like broadcast
+					*/
+#endif /* CONFIG_NAN_USD */
 
 	for (i = 0; i < iface->num_bss; i++) {
 		struct hostapd_data *hapd;
@@ -1988,18 +2005,19 @@
 {
 	struct ieee80211_hdr *hdr;
 	struct hostapd_data *orig_hapd, *tmp_hapd;
+	const u8 *bssid;
 
 	orig_hapd = hapd;
 
 	hdr = (struct ieee80211_hdr *) buf;
 	hapd = switch_link_hapd(hapd, link_id);
-	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
+	bssid = get_hdr_bssid(hdr, len);
+	tmp_hapd = get_hapd_bssid(hapd->iface, bssid, link_id);
 	if (tmp_hapd) {
 		hapd = tmp_hapd;
 #ifdef CONFIG_IEEE80211BE
-	} else if (hapd->conf->mld_ap &&
-		   ether_addr_equal(hapd->mld->mld_addr,
-				    get_hdr_bssid(hdr, len))) {
+	} else if (hapd->conf->mld_ap && bssid &&
+		   ether_addr_equal(hapd->mld->mld_addr, bssid)) {
 		/* AP MLD address match - use hapd pointer as-is */
 #endif /* CONFIG_IEEE80211BE */
 	} else {
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7d92489..4bc6b3a 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -242,6 +242,10 @@
 		if (os_strcmp(newconf->bss[i]->iface,
 			      oldconf->bss[i]->iface) != 0)
 			return 1;
+#ifdef CONFIG_IEEE80211BE
+		if (newconf->bss[i]->mld_ap != oldconf->bss[i]->mld_ap)
+			return 1;
+#endif /* CONFIG_IEEE80211BE */
 	}
 
 	return 0;
@@ -302,7 +306,6 @@
 				   "Failed to enable interface on config reload");
 		return res;
 	}
-	iface->conf = newconf;
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
@@ -330,6 +333,7 @@
 		hostapd_reload_bss(hapd);
 	}
 
+	iface->conf = newconf;
 	hostapd_config_free(oldconf);
 
 
@@ -521,7 +525,10 @@
 
 	authsrv_deinit(hapd);
 
-	if (hapd->interface_added) {
+	/* For single drv, first bss would have interface_added flag set.
+	 * Don't remove interface now. Driver deinit part will take care
+	 */
+	if (hapd->interface_added && hapd->iface->bss[0] != hapd) {
 		hapd->interface_added = 0;
 		if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
 			wpa_printf(MSG_WARNING,
@@ -633,7 +640,7 @@
 	}
 
 	/* Put all freeing logic above this */
-	if (!hapd->mld->num_links)
+	if (!hapd->mld || !hapd->mld->num_links)
 		return;
 
 	/* If not started, not yet linked to the MLD. However, the first
@@ -703,6 +710,7 @@
 		acs_cleanup(iface);
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
+	iface->num_hw_features = 0;
 	iface->current_mode = NULL;
 	os_free(iface->current_rates);
 	iface->current_rates = NULL;
@@ -1794,26 +1802,26 @@
 
 int hostapd_set_acl(struct hostapd_data *hapd)
 {
-	struct hostapd_config *conf = hapd->iconf;
+	struct hostapd_bss_config *conf = hapd->conf;
 	int err = 0;
 	u8 accept_acl;
 
 	if (hapd->iface->drv_max_acl_mac_addrs == 0)
 		return 0;
 
-	if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+	if (conf->macaddr_acl == DENY_UNLESS_ACCEPTED) {
 		accept_acl = 1;
-		err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
-					   conf->bss[0]->num_accept_mac,
+		err = hostapd_set_acl_list(hapd, conf->accept_mac,
+					   conf->num_accept_mac,
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set accept acl");
 			return -1;
 		}
-	} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+	} else if (conf->macaddr_acl == ACCEPT_UNLESS_DENIED) {
 		accept_acl = 0;
-		err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
-					   conf->bss[0]->num_deny_mac,
+		err = hostapd_set_acl_list(hapd, conf->deny_mac,
+					   conf->num_deny_mac,
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set deny acl");
@@ -1906,18 +1914,30 @@
 static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
 					      void *ctx)
 {
+	struct hostapd_data *hapd = iface->bss[0];
 	bool all_no_ir, is_6ghz;
 	int i, j;
 	struct hostapd_hw_modes *mode = NULL;
+	struct hostapd_hw_modes *hw_features;
+	u16 num_hw_features, flags;
+	u8 dfs_domain;
 
-	if (hostapd_get_hw_features(iface))
-		return 0;
+	if (hostapd_drv_none(hapd))
+		return -1;
+
+	hw_features = hostapd_get_hw_feature_data(hapd, &num_hw_features,
+						  &flags, &dfs_domain);
+	if (!hw_features) {
+		wpa_printf(MSG_DEBUG,
+			   "Could not fetching hardware channel list");
+		return -1;
+	}
 
 	all_no_ir = true;
 	is_6ghz = false;
 
-	for (i = 0; i < iface->num_hw_features; i++) {
-		mode = &iface->hw_features[i];
+	for (i = 0; i < num_hw_features; i++) {
+		mode = &hw_features[i];
 
 		if (mode->mode == iface->conf->hw_mode) {
 			if (iface->freq > 0 &&
@@ -1939,26 +1959,25 @@
 	}
 
 	if (!mode || !is_6ghz)
-		return 0;
-	iface->current_mode = mode;
+		goto free_hw_features;
 
 	if (iface->state == HAPD_IFACE_ENABLED) {
 		if (!all_no_ir) {
 			struct hostapd_channel_data *chan;
 
-			chan = hw_get_channel_freq(iface->current_mode->mode,
+			chan = hw_get_channel_freq(mode->mode,
 						   iface->freq, NULL,
-						   iface->hw_features,
-						   iface->num_hw_features);
+						   hw_features,
+						   num_hw_features);
 
 			if (!chan) {
 				wpa_printf(MSG_ERROR,
 					   "NO_IR: Could not derive chan from freq");
-				return 0;
+				goto free_hw_features;
 			}
 
 			if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
-				return 0;
+				goto free_hw_features;
 			wpa_printf(MSG_DEBUG,
 				   "NO_IR: The current channel has NO_IR flag now, stop AP.");
 		} else {
@@ -1975,20 +1994,20 @@
 		if (all_no_ir) {
 			wpa_printf(MSG_DEBUG,
 				   "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
-			return 0;
+			goto free_hw_features;
 		}
 
 		if (!iface->conf->acs) {
 			struct hostapd_channel_data *chan;
 
-			chan = hw_get_channel_freq(iface->current_mode->mode,
+			chan = hw_get_channel_freq(mode->mode,
 						   iface->freq, NULL,
-						   iface->hw_features,
-						   iface->num_hw_features);
+						   hw_features,
+						   num_hw_features);
 			if (!chan) {
 				wpa_printf(MSG_ERROR,
 					   "NO_IR: Could not derive chan from freq");
-				return 0;
+				goto free_hw_features;
 			}
 
 			/* If the last operating channel is NO_IR, trigger ACS.
@@ -1999,13 +2018,15 @@
 				if (acs_init(iface) != HOSTAPD_CHAN_ACS)
 					wpa_printf(MSG_ERROR,
 						   "NO_IR: Could not start ACS");
-				return 0;
+				goto free_hw_features;
 			}
 		}
 
 		setup_interface2(iface);
 	}
 
+free_hw_features:
+	hostapd_free_hw_features(hw_features, num_hw_features);
 	return 0;
 }
 
@@ -3080,14 +3101,17 @@
 #endif /* CONFIG_IEEE80211BE */
 
 
-static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
-					 struct hapd_interfaces *interfaces)
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+				  struct hapd_interfaces *interfaces)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_mld *mld, **all_mld;
 	struct hostapd_bss_config *conf;
 	size_t i;
 
+	if (hapd->mld)
+		return;
+
 	conf = hapd->conf;
 
 	if (!hapd->iconf || !hapd->iconf->ieee80211be || !conf->mld_ap ||
@@ -3445,8 +3469,7 @@
 	 * still being used by some other BSS before de-initiallizing. */
 	if (!iface->bss[0]->conf->mld_ap) {
 		driver->hapd_deinit(drv_priv);
-	} else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
-		   driver->is_drv_shared &&
+	} else if (driver->is_drv_shared &&
 		   !driver->is_drv_shared(drv_priv,
 					  iface->bss[0]->mld_link_id)) {
 		driver->hapd_deinit(drv_priv);
@@ -3611,8 +3634,6 @@
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
 	size_t j;
-	const struct wpa_driver_ops *driver;
-	void *drv_priv;
 
 	if (hapd_iface == NULL)
 		return -1;
@@ -3627,8 +3648,6 @@
 	}
 
 	wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
-	driver = hapd_iface->bss[0]->driver;
-	drv_priv = hapd_iface->bss[0]->drv_priv;
 
 	hapd_iface->driver_ap_teardown =
 		!!(hapd_iface->drv_flags &
@@ -3647,7 +3666,8 @@
 		hostapd_free_hapd_data(hapd);
 	}
 
-	hostapd_deinit_driver(driver, drv_priv, hapd_iface);
+	hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+			      hapd_iface->bss[0]->drv_priv, hapd_iface);
 
 	/* From hostapd_cleanup_iface: These were initialized in
 	 * hostapd_setup_interface and hostapd_setup_interface_complete
@@ -4068,6 +4088,7 @@
 #endif /* CONFIG_IEEE80211BE */
 
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	ap_sta_clear_assoc_timeout(hapd, sta);
 	sta->post_csa_sa_query = 0;
 
 #ifdef CONFIG_P2P
@@ -4085,7 +4106,13 @@
 	 * IEEE 802.1X/WPA code will start accounting after the station has
 	 * been authorized. */
 	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
-		ap_sta_set_authorized(hapd, sta, 1);
+		if (ap_sta_set_authorized(hapd, sta, 1)) {
+			/* Update driver authorized flag for the STA to cover
+			 * the case where AP SME is in the driver and there is
+			 * no separate event for handling TX status event for
+			 * the (Re)Association Response frame. */
+			hostapd_set_sta_flags(hapd, sta);
+		}
 		os_get_reltime(&sta->connected_time);
 		accounting_sta_start(hapd, sta);
 	}
@@ -4200,8 +4227,8 @@
 }
 
 
-static int hostapd_build_beacon_data(struct hostapd_data *hapd,
-				     struct beacon_data *beacon)
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+			      struct beacon_data *beacon)
 {
 	struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
 	struct wpa_driver_ap_params params;
@@ -4382,6 +4409,10 @@
 	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
 	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
 
+#ifdef CONFIG_IEEE80211BE
+	conf->punct_bitmap = params->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
 	/* TODO: maybe call here hostapd_config_check here? */
 
 	return 0;
@@ -4394,9 +4425,6 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
-#ifdef CONFIG_IEEE80211BE
-	u16 old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
@@ -4445,16 +4473,9 @@
 	if (ret)
 		return ret;
 
-#ifdef CONFIG_IEEE80211BE
-	old_punct_bitmap = iface->conf->punct_bitmap;
-	iface->conf->punct_bitmap = settings->punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
 
 	/* change back the configuration */
-#ifdef CONFIG_IEEE80211BE
-	iface->conf->punct_bitmap = old_punct_bitmap;
-#endif /* CONFIG_IEEE80211BE */
 	hostapd_change_config_freq(iface->bss[0], iface->conf,
 				   &old_freq, NULL);
 
@@ -4768,6 +4789,7 @@
 		struct cca_settings settings;
 		int ret;
 
+		os_memset(&settings, 0, sizeof(settings));
 		hostapd_cleanup_cca_params(bss);
 		bss->cca_color = r;
 		bss->cca_count = 10;
@@ -5042,6 +5064,28 @@
 		link_bss->drv_priv = NULL;
 }
 
+
+/* Return the number of currently active links, not counting the calling link
+ * (i.e., a value that is suitable to be used as-is in fields that use encoding
+ * of the value minus 1). */
+u8 hostapd_get_active_links(struct hostapd_data *hapd)
+{
+	struct hostapd_data *link_bss;
+	u8 active_links = 0;
+
+	if (!hapd || !hapd->conf->mld_ap)
+		return 0;
+
+	for_each_mld_link(link_bss, hapd) {
+		if (link_bss == hapd || !link_bss->started)
+			continue;
+
+		active_links++;
+	}
+
+	return active_links;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 5d91d85..846535a 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -768,6 +768,8 @@
 struct hostapd_iface *
 hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
 			   const char *config_fname, int debug);
+void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
+				  struct hapd_interfaces *interfaces);
 void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 			   int reassoc);
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -859,8 +861,11 @@
 u8 hostapd_get_mld_id(struct hostapd_data *hapd);
 int hostapd_mld_add_link(struct hostapd_data *hapd);
 int hostapd_mld_remove_link(struct hostapd_data *hapd);
+u8 hostapd_get_active_links(struct hostapd_data *hapd);
 struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
 
+int hostapd_build_beacon_data(struct hostapd_data *hapd,
+			      struct beacon_data *beacon);
 void free_beacon_data(struct beacon_data *beacon);
 int hostapd_fill_cca_settings(struct hostapd_data *hapd,
 			      struct cca_settings *settings);
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 02d6759..cef3817 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -528,12 +528,6 @@
 	else
 		ieee80211n_scan_channels_5g(iface, &params);
 
-	params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
-	if (iface->bss[0]->conf->mld_ap)
-		params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
-
 	ret = hostapd_driver_scan(iface->bss[0], &params);
 	iface->num_ht40_scan_tries++;
 	os_free(params.freqs);
@@ -585,11 +579,6 @@
 	else
 		ieee80211n_scan_channels_5g(iface, &params);
 
-	params.link_id = -1;
-#ifdef CONFIG_IEEE80211BE
-	if (iface->bss[0]->conf->mld_ap)
-		params.link_id = iface->bss[0]->mld_link_id;
-#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_driver_scan(iface->bss[0], &params);
 	os_free(params.freqs);
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index d4552f2..a9ed6eb 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -26,6 +26,7 @@
 #include "common/wpa_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/ptksa_cache.h"
+#include "common/nan_de.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -576,12 +577,12 @@
 			pk = pw->pk;
 		break;
 	}
-	if (!password) {
+	if (!password && !rx_id) {
 		password = hapd->conf->ssid.wpa_passphrase;
 		pt = hapd->conf->ssid.pt;
 	}
 
-	if (!password && sta) {
+	if (!password && sta && !rx_id) {
 		for (psk = sta->psk; psk; psk = psk->next) {
 			if (psk->is_passphrase) {
 				password = psk->passphrase;
@@ -620,7 +621,8 @@
 #endif /* CONFIG_IEEE80211BE */
 
 	if (sta->sae->tmp) {
-		rx_id = sta->sae->tmp->pw_id;
+		rx_id = sta->sae->tmp->parsed_pw_id ?
+			sta->sae->tmp->parsed_pw_id : sta->sae->tmp->pw_id;
 		use_pt = sta->sae->h2e;
 #ifdef CONFIG_SAE_PK
 		os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
@@ -712,7 +714,8 @@
 	u16 status;
 
 	data = auth_build_sae_commit(hapd, sta, update, status_code);
-	if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+	if (!data && sta->sae->tmp &&
+	    (sta->sae->tmp->pw_id || sta->sae->tmp->parsed_pw_id))
 		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	if (data == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1001,7 +1004,9 @@
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
-			if (sta->sae->tmp) {
+			struct sae_temporary_data *tmp = sta->sae->tmp;
+
+			if (tmp) {
 				sta->sae->h2e =
 					(status_code ==
 					 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
@@ -1011,8 +1016,21 @@
 			}
 			ret = auth_sae_send_commit(hapd, sta,
 						   !allow_reuse, status_code);
+			if (ret == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER)
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+					MACSTR, MAC2STR(sta->addr));
 			if (ret)
 				return ret;
+
+			if (tmp && tmp->parsed_pw_id && !tmp->pw_id) {
+				tmp->pw_id = tmp->parsed_pw_id;
+				tmp->parsed_pw_id = NULL;
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Known Password Identifier bound to this STA: '%s'",
+					   tmp->pw_id);
+			}
+
 			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
 
 			if (sae_process_commit(sta->sae) < 0)
@@ -1374,6 +1392,8 @@
 			resp = -1;
 			goto remove_sta;
 		}
+		if (!hostapd_sae_pw_id_in_use(hapd->conf))
+			sta->sae->no_pw_id = 1;
 		sae_set_state(sta, SAE_NOTHING, "Init");
 		sta->sae->sync = 0;
 	}
@@ -1512,6 +1532,8 @@
 			sae_clear_retransmit_timer(hapd, sta);
 			sae_set_state(sta, SAE_NOTHING,
 				      "Unknown Password Identifier");
+			if (sta->sae->state == SAE_NOTHING)
+				goto reply;
 			goto remove_sta;
 		}
 
@@ -1634,14 +1656,6 @@
 				data ? wpabuf_head(data) : (u8 *) "",
 				data ? wpabuf_len(data) : 0, "auth-sae");
 		sae_sme_send_external_auth_status(hapd, sta, resp);
-		if (sta->sae && sta->sae->tmp && sta->sae->tmp->pw_id &&
-		    resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER &&
-		    auth_transaction == 1) {
-			wpa_printf(MSG_DEBUG,
-				   "SAE: Clear stored password identifier since this SAE commit was not accepted");
-			os_free(sta->sae->tmp->pw_id);
-			sta->sae->tmp->pw_id = NULL;
-		}
 	}
 
 remove_sta:
@@ -2506,6 +2520,8 @@
 	fils->erp_resp = erp_resp;
 	ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
 				    WLAN_STATUS_SUCCESS);
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 	fils->erp_resp = NULL;
 
 	if (ret) {
@@ -2819,6 +2835,32 @@
 			     const struct ieee80211_mgmt *mgmt, size_t len,
 			     u16 trans_seq, u16 status)
 {
+	int ret;
+#ifdef CONFIG_P2P
+	struct ieee802_11_elems elems;
+
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "PASN: Too short Management frame");
+		return;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return;
+	}
+
+	if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    (P2P_ENABLED | P2P_GROUP_OWNER) &&
+	    hapd->p2p && elems.p2p2_ie && elems.p2p2_ie_len) {
+		p2p_pasn_auth_rx(hapd->p2p, mgmt, len, hapd->iface->freq);
+		return;
+	}
+#endif /* CONFIG_P2P */
+
 	if (hapd->conf->wpa != WPA_PROTO_RSN) {
 		wpa_printf(MSG_INFO, "PASN: RSN is not configured");
 		return;
@@ -2850,8 +2892,11 @@
 		hapd_initialize_pasn(hapd, sta);
 
 		hapd_pasn_update_params(hapd, sta, mgmt, len);
-		if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
-				       sta->addr, mgmt, len, false) < 0)
+		ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr,
+					 mgmt, len, false);
+		wpabuf_free(sta->pasn->frame);
+		sta->pasn->frame = NULL;
+		if (ret < 0)
 			ap_free_sta(hapd, sta);
 	} else if (trans_seq == 3) {
 		if (!sta->pasn) {
@@ -3369,7 +3414,10 @@
 	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
 		return 0;
 
-	num_bss_nontx = hapd->iface->num_bss - 1;
+	if (hapd->iface->conf->mbssid_max > 0)
+		num_bss_nontx = hapd->iface->conf->mbssid_max - 1;
+	else
+		num_bss_nontx = hapd->iface->conf->num_bss - 1;
 	while (num_bss_nontx > 0) {
 		max_bssid_ind++;
 		num_bss_nontx >>= 1;
@@ -4441,6 +4489,10 @@
 				hapd->conf->max_acceptable_idle_period;
 	}
 
+	if (elems->wfa_capab)
+		hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
+				  elems->wfa_capab + elems->wfa_capab_len);
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -4565,9 +4617,8 @@
 	if (!mlbuf)
 		goto out;
 
-	if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
-					    hapd->mld_link_id, true) ==
-	    ParseFailed) {
+	if (ieee802_11_parse_link_assoc_req(&elems, mlbuf, hapd->mld_link_id,
+					    true) == ParseFailed) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: link: Failed to parse association request Multi-Link element");
 		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -4752,6 +4803,7 @@
 	int set = 1;
 	const u8 *mld_link_addr = NULL;
 	bool mld_link_sta = false;
+	u16 eml_cap = 0;
 
 #ifdef CONFIG_IEEE80211BE
 	if (ap_sta_is_mld(hapd, sta)) {
@@ -4762,6 +4814,7 @@
 
 		if (hapd->mld_link_id != sta->mld_assoc_link_id)
 			set = 0;
+		eml_cap = sta->mld_info.common_info.eml_capa;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -4842,7 +4895,7 @@
 			    sta->he_6ghz_capab,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
-			    set, mld_link_addr, mld_link_sta)) {
+			    set, mld_link_addr, mld_link_sta, eml_cap)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
 			       "Could not %s STA to kernel driver",
@@ -5606,7 +5659,12 @@
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
-	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#ifdef CONFIG_IEEE80211R_AP
+	if (reassoc && sta->auth_alg == WLAN_AUTH_FT)
+		omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+#endif /* CONFIG_IEEE80211R_AP */
+	if (hapd->conf->rsn_override_omit_rsnxe)
+		omit_rsnxe = 1;
 
 	if (hostapd_get_aid(hapd, sta) < 0) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -5998,10 +6056,51 @@
 }
 
 
+static int hostapd_action_vs(struct hostapd_data *hapd,
+			     struct sta_info *sta,
+			     const struct ieee80211_mgmt *mgmt, size_t len,
+			     unsigned int freq, bool protected)
+{
+	const u8 *pos, *end;
+	u32 oui_type;
+
+	pos = &mgmt->u.action.category;
+	end = ((const u8 *) mgmt) + len;
+
+	if (end - pos < 1 + 4)
+		return -1;
+	pos++;
+
+	oui_type = WPA_GET_BE32(pos);
+	pos += 4;
+
+	switch (oui_type) {
+	case WFA_CAPAB_VENDOR_TYPE:
+		hostapd_wfa_capab(hapd, sta, pos, end);
+		return 0;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "Ignore unknown Vendor Specific Action frame OUI/type %08x%s",
+			   oui_type, protected ? " (protected)" : "");
+		break;
+	}
+
+	return -1;
+}
+
+
 static int robust_action_frame(u8 category)
 {
 	return category != WLAN_ACTION_PUBLIC &&
-		category != WLAN_ACTION_HT;
+		category != WLAN_ACTION_HT &&
+		category != WLAN_ACTION_UNPROTECTED_WNM &&
+		category != WLAN_ACTION_SELF_PROTECTED &&
+		category != WLAN_ACTION_UNPROTECTED_DMG &&
+		category != WLAN_ACTION_VHT &&
+		category != WLAN_ACTION_UNPROTECTED_S1G &&
+		category != WLAN_ACTION_HE &&
+		category != WLAN_ACTION_EHT &&
+		category != WLAN_ACTION_VENDOR_SPECIFIC;
 }
 
 
@@ -6148,8 +6247,8 @@
 			pos = mgmt->u.action.u.vs_public_action.variable;
 			end = ((const u8 *) mgmt) + len;
 			pos++;
-			hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
-					       pos, end - pos);
+			hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, mgmt->bssid,
+					       freq, pos, end - pos);
 			return 1;
 		}
 #endif /* CONFIG_NAN_USD */
@@ -6170,6 +6269,14 @@
 						   (u8 *) mgmt, len, freq) == 0)
 				return 1;
 		}
+		if (sta &&
+		    hostapd_action_vs(hapd, sta, mgmt, len, freq, false) == 0)
+			return 1;
+		break;
+	case WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED:
+		if (sta &&
+		    hostapd_action_vs(hapd, sta, mgmt, len, freq, true) == 0)
+			return 1;
 		break;
 #ifndef CONFIG_NO_RRM
 	case WLAN_ACTION_RADIO_MEASUREMENT:
@@ -6263,6 +6370,8 @@
 #ifdef CONFIG_NAN_USD
 	static const u8 nan_network_id[ETH_ALEN] =
 		{ 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
+	static const u8 p2p_network_id[ETH_ALEN] =
+		{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
 #endif /* CONFIG_NAN_USD */
 
 	if (len < 24)
@@ -6295,6 +6404,10 @@
 	}
 
 	if (!is_broadcast_ether_addr(mgmt->bssid) &&
+#ifdef CONFIG_NAN_USD
+	    !nan_de_is_nan_network_id(mgmt->bssid) &&
+	    !nan_de_is_p2p_network_id(mgmt->bssid) &&
+#endif /* CONFIG_NAN_USD */
 #ifdef CONFIG_P2P
 	    /* Invitation responses can be sent with the peer MAC as BSSID */
 	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
@@ -6332,6 +6445,7 @@
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_NAN_USD
 	    !ether_addr_equal(mgmt->da, nan_network_id) &&
+	    !ether_addr_equal(mgmt->da, p2p_network_id) &&
 #endif /* CONFIG_NAN_USD */
 	    !ether_addr_equal(mgmt->da, hapd->own_addr)) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -7254,32 +7368,29 @@
 {
 	u8 bw;
 
-	/* bandwidth: 0: 40, 1: 80, 160, 80+80, 4: 320 as per
-	 * IEEE P802.11-REVme/D4.0, 9.4.2.159 and Table 9-314. */
+	/* bandwidth: 0: 40, 1: 80, 160, 80+80, 4 to 255 reserved as per
+	 * IEEE P802.11-REVme/D7.0, 9.4.2.159 and Table 9-316.
+	 */
 	switch (hapd->cs_freq_params.bandwidth) {
-	case 40:
-		bw = 0;
-		break;
-	case 80:
-		bw = 1;
-		break;
-	case 160:
-		bw = 1;
-		break;
 	case 320:
-		bw = 4;
-		break;
-	default:
-		/* not valid VHT bandwidth or not in CSA */
-		return eid;
-	}
+		/* As per IEEE P802.11be/D7.0, 35.15.3,
+		 * For EHT BSS operating channel width wider than 160 MHz,
+		 * the announced BSS bandwidth in the Wide Bandwidth
+		 * Channel Switch element is less than the BSS bandwidth
+		 * in the Bandwidth Indication element
+		 */
 
-	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
-	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
-	*eid++ = bw; /* New Channel Width */
-	if (hapd->cs_freq_params.bandwidth == 160) {
+		/* Modifying the center frequency to 160 MHz */
+		if (hapd->cs_freq_params.channel < chan1)
+			chan1 -= 16;
+		else
+			chan1 += 16;
+
+		/* fallthrough */
+	case 160:
 		/* Update the CCFS0 and CCFS1 values in the element based on
-		 * IEEE P802.11-REVme/D4.0, Table 9-314 */
+		 * IEEE P802.11-REVme/D7.0, Table 9-316
+		 */
 
 		/* CCFS1 - The channel center frequency index of the 160 MHz
 		 * channel. */
@@ -7291,7 +7402,23 @@
 			chan1 -= 8;
 		else
 			chan1 += 8;
+
+		bw = 1;
+		break;
+	case 80:
+		bw = 1;
+		break;
+	case 40:
+		bw = 0;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
 	}
+
+	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
 	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
 	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
 
@@ -7305,9 +7432,9 @@
 static u8 * hostapd_eid_bw_indication(struct hostapd_data *hapd, u8 *eid,
 				      u8 chan1, u8 chan2)
 {
-	u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
+	u16 punct_bitmap = hapd->cs_freq_params.punct_bitmap;
 	struct ieee80211_bw_ind_element *bw_ind_elem;
-	size_t elen = 3;
+	size_t elen = 4;
 
 	if (hapd->cs_freq_params.bandwidth <= 160 && !punct_bitmap)
 		return eid;
@@ -8221,11 +8348,13 @@
 	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
 		struct hostapd_data *bss = hapd->iface->bss[i];
 		struct hostapd_bss_config *conf;
+		struct hostapd_bss_config *tx_conf = tx_bss->conf;
 		u8 *eid_len_pos, *nontx_bss_start = eid;
 		const u8 *auth, *rsn = NULL, *rsnx = NULL;
 		u8 ie_count = 0, non_inherit_ie[3];
 		size_t auth_len = 0;
 		u16 capab_info;
+		u8 mbssindex = i;
 
 		if (!bss || !bss->conf || !bss->started ||
 		    mbssid_known_bss(i, known_bss, known_bss_len))
@@ -8246,10 +8375,14 @@
 		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
 		eid += conf->ssid.ssid_len;
 
+		if (conf->mbssid_index &&
+		    conf->mbssid_index > tx_conf->mbssid_index)
+			mbssindex = conf->mbssid_index - tx_conf->mbssid_index;
+
 		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
 		if (frame_type == WLAN_FC_STYPE_BEACON) {
 			*eid++ = 3;
-			*eid++ = i; /* BSSID Index */
+			*eid++ = mbssindex; /* BSSID Index */
 			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
 			    (conf->dtim_period % elem_count))
 				conf->dtim_period = elem_count;
@@ -8265,7 +8398,7 @@
 			/* Probe Request frame does not include DTIM Period and
 			 * DTIM Count fields. */
 			*eid++ = 1;
-			*eid++ = i; /* BSSID Index */
+			*eid++ = mbssindex; /* BSSID Index */
 		}
 
 		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index abf48ab..2bcc29e 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -130,6 +130,8 @@
 int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
 				 enum ieee80211_op_mode mode);
 bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd);
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *pos, const u8 *end);
 u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index aea69ab..e778041 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -503,7 +503,7 @@
 
 	mld_cap = hapd->iface->mld_mld_capa;
 	max_simul_links = mld_cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK;
-	active_links = hapd->mld->num_links - 1;
+	active_links = hostapd_get_active_links(hapd);
 
 	if (active_links > max_simul_links) {
 		wpa_printf(MSG_ERROR,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index a2deda6..cd9f8bc 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -221,7 +221,7 @@
 
 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
 		enum oper_chan_width oper_chwidth =
-			hostapd_get_oper_chwidth(hapd->iconf);
+			hapd->iconf->he_oper_chwidth;
 		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
 		u8 control;
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 4c39e40..9bc0461 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -457,6 +457,7 @@
 	iface->num_sta_ht40_intolerant--;
 
 	if (iface->num_sta_ht40_intolerant == 0 &&
+	    iface->conf->obss_interval &&
 	    (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
 		unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 3dd3a6a..5e67216 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -476,6 +476,14 @@
 			*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
 #endif /* CONFIG_SAE_PK */
 		break;
+	case 12: /* Bits 96-103 */
+		if (hapd->iconf->peer_to_peer_twt)
+			*pos |= 0x10; /* Bit 100 - Peer to Peer TWT */
+		break;
+	case 13: /* Bits 104-111 */
+		if (hapd->iconf->channel_usage)
+			*pos |= 0x01; /* Bit 104 - Channel Usage support */
+		break;
 	}
 }
 
@@ -1226,3 +1234,47 @@
 		(hapd->iface->drv_flags2 &
 		 WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER);
 }
+
+
+static void hostapd_wfa_gen_capab(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  const u8 *capab, size_t len)
+{
+	char *hex;
+	size_t buflen;
+
+	wpa_printf(MSG_DEBUG,
+		   "WFA: Received indication of generational capabilities from "
+		   MACSTR, MAC2STR(sta->addr));
+	wpa_hexdump(MSG_DEBUG, "WFA: Generational Capabilities", capab, len);
+
+	buflen = 2 * len + 1;
+	hex = os_zalloc(buflen);
+	if (!hex)
+		return;
+	wpa_snprintf_hex(hex, buflen, capab, len);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WFA_GEN_CAPAB_RX MACSTR " %s",
+		MAC2STR(sta->addr), hex);
+	os_free(hex);
+}
+
+
+void hostapd_wfa_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *pos, const u8 *end)
+{
+	u8 capab_len;
+	const u8 *gen_capa;
+
+	if (end - pos < 1)
+		return;
+	capab_len = *pos++;
+	if (capab_len > end - pos)
+		return;
+	pos += capab_len; /* skip the Capabilities field */
+
+	/* Wi-Fi Alliance Capabilities attributes use a header that is similar
+	 * to the one used in Information Elements. */
+	gen_capa = get_ie(pos, end - pos, WFA_CAPA_ATTR_GENERATIONAL_CAPAB);
+	if (gen_capa)
+		hostapd_wfa_gen_capab(hapd, sta, gen_capa + 2, gen_capa[1]);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f4103ac..e8d21ff 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -149,7 +149,7 @@
 					     bool authorized)
 {
 #ifdef CONFIG_IEEE80211BE
-	unsigned int i, link_id;
+	unsigned int i;
 
 	if (!hostapd_is_mld_ap(hapd))
 		return;
@@ -161,32 +161,30 @@
 	if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
 		return;
 
-	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-		struct mld_link_info *link = &sta->mld_info.links[link_id];
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		struct sta_info *tmp_sta;
+		struct mld_link_info *link;
+		struct hostapd_data *tmp_hapd =
+			hapd->iface->interfaces->iface[i]->bss[0];
 
+		if (!hostapd_is_ml_partner(hapd, tmp_hapd))
+			continue;
+
+		link = &sta->mld_info.links[tmp_hapd->mld_link_id];
 		if (!link->valid)
 			continue;
 
-		for (i = 0; i < hapd->iface->interfaces->count; i++) {
-			struct sta_info *tmp_sta;
-			struct hostapd_data *tmp_hapd =
-				hapd->iface->interfaces->iface[i]->bss[0];
-
-			if (!hostapd_is_ml_partner(hapd, tmp_hapd))
+		for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+		     tmp_sta = tmp_sta->next) {
+			if (tmp_sta == sta ||
+			    tmp_sta->mld_assoc_link_id !=
+			    sta->mld_assoc_link_id ||
+			    tmp_sta->aid != sta->aid)
 				continue;
 
-			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
-			     tmp_sta = tmp_sta->next) {
-				if (tmp_sta == sta ||
-				    tmp_sta->mld_assoc_link_id !=
-				    sta->mld_assoc_link_id ||
-				    tmp_sta->aid != sta->aid)
-					continue;
-
-				ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
-							  authorized, true);
-				break;
-			}
+			ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+						  authorized, true);
+			break;
 		}
 	}
 #endif /* CONFIG_IEEE80211BE */
@@ -1252,6 +1250,27 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "received EAPOL-Start from STA");
+#ifdef CONFIG_IEEE80211R_AP
+		if (hapd->conf->wpa && sta->wpa_sm &&
+		    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+		     sta->auth_alg == WLAN_AUTH_FT)) {
+			/* When FT is used, reauthentication to generate a new
+			 * PMK-R0 would be complicated since the current AP
+			 * might not be the one with which the currently used
+			 * PMK-R0 was generated. IEEE Std 802.11-2020, 13.4.2
+			 * (FT initial mobility domain association in an RSN)
+			 * mandates STA to perform a new FT initial mobility
+			 * domain association whenever its Supplicant would
+			 * trigger sending of an EAPOL-Start frame. As such,
+			 * this EAPOL-Start frame should not have been sent.
+			 * Discard it to avoid unexpected behavior. */
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "discard unexpected EAPOL-Start from STA that uses FT");
+			break;
+		}
+#endif /* CONFIG_IEEE80211R_AP */
 		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
 		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 		if (pmksa) {
diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
index 570abfc..4623f67 100644
--- a/src/ap/nan_usd_ap.c
+++ b/src/ap/nan_usd_ap.c
@@ -29,8 +29,10 @@
 		   wpabuf_len(buf));
 
 	/* TODO: Force use of OFDM */
-	return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
-				       wpabuf_head(buf), wpabuf_len(buf));
+	return hostapd_drv_send_action_forced_addr3(hapd, hapd->iface->freq, 0,
+						    dst, bssid,
+						    wpabuf_head(buf),
+						    wpabuf_len(buf));
 }
 
 
@@ -158,7 +160,7 @@
 	cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
 	cb.receive = hostapd_nan_de_receive;
 
-	hapd->nan_de = nan_de_init(hapd->own_addr, false, true, &cb);
+	hapd->nan_de = nan_de_init(hapd->own_addr, false, true, 0, &cb);
 	if (!hapd->nan_de)
 		return -1;
 	return 0;
@@ -173,11 +175,12 @@
 
 
 void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
-			    unsigned int freq, const u8 *buf, size_t len)
+			    const u8 *a3, unsigned int freq,
+			    const u8 *buf, size_t len)
 {
 	if (!hapd->nan_de)
 		return;
-	nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+	nan_de_rx_sdf(hapd->nan_de, src, a3, freq, buf, len);
 }
 
 
@@ -258,7 +261,8 @@
 int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
 			     const struct wpabuf *ssi,
 			     const struct wpabuf *elems,
-			     const u8 *peer_addr, u8 req_instance_id)
+			     const u8 *peer_addr,
+			     u8 req_instance_id)
 {
 	if (!hapd->nan_de)
 		return -1;
diff --git a/src/ap/nan_usd_ap.h b/src/ap/nan_usd_ap.h
index 0571643..b7e8b76 100644
--- a/src/ap/nan_usd_ap.h
+++ b/src/ap/nan_usd_ap.h
@@ -16,6 +16,7 @@
 int hostapd_nan_usd_init(struct hostapd_data *hapd);
 void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
 void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+			    const u8 *a3,
 			    unsigned int freq, const u8 *buf, size_t len);
 void hostapd_nan_usd_flush(struct hostapd_data *hapd);
 int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index aa7e156..9d49569 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -334,6 +334,7 @@
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	ap_sta_clear_assoc_timeout(hapd, sta);
 	sae_clear_retransmit_timer(hapd, sta);
 
 	ieee802_1x_free_station(hapd, sta);
@@ -791,6 +792,25 @@
 }
 
 
+static void ap_sta_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->flags & WLAN_STA_ASSOC)
+		return;
+
+	wpa_printf(MSG_DEBUG, "STA " MACSTR
+		   " did not complete association in time - remove it",
+		   MAC2STR(sta->addr));
+	if (sta->flags & WLAN_STA_AUTH)
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else
+		ap_free_sta(hapd, sta);
+}
+
+
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta;
@@ -856,6 +876,9 @@
 				      &sta->probe_ie_taxonomy);
 #endif /* CONFIG_TAXONOMY */
 
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		eloop_register_timeout(60, 0, ap_sta_assoc_timeout, hapd, sta);
+
 	return sta;
 }
 
@@ -953,6 +976,18 @@
 }
 
 
+static void ap_sta_disassociate_common(struct hostapd_data *hapd,
+				       struct sta_info *sta, u16 reason)
+{
+	sta->disassoc_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
 static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
 				       struct sta_info *sta, u16 reason)
 {
@@ -971,13 +1006,7 @@
 	}
 
 	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
-
-	sta->disassoc_reason = reason;
-	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
-	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
-	eloop_register_timeout(hapd->iface->drv_flags &
-			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
-			       ap_sta_disassoc_cb_timeout, hapd, sta);
+	ap_sta_disassociate_common(hapd, sta, reason);
 }
 
 
@@ -993,25 +1022,9 @@
 }
 
 
-static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+static void ap_sta_deauthenticate_common(struct hostapd_data *hapd,
 					 struct sta_info *sta, u16 reason)
 {
-	if (hapd->iface->current_mode &&
-	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
-		/* Deauthentication is not used in DMG/IEEE 802.11ad;
-		 * disassociate the STA instead. */
-		ap_sta_disassociate(hapd, sta, reason);
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
-		   hapd->conf->iface, MAC2STR(sta->addr));
-
-	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
-
-	sta->timeout_next = STA_REMOVE;
-	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
-
 	sta->deauth_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -1021,9 +1034,46 @@
 }
 
 
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+					 struct sta_info *sta, u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+
+	sta->timeout_next = STA_REMOVE;
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	ap_sta_deauthenticate_common(hapd, sta, reason);
+}
+
+
+static void ap_sta_handle_disconnect(struct hostapd_data *hapd,
+				     struct sta_info *sta, u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: disconnect STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	sta->timeout_next = STA_REMOVE;
+
+	sta->deauth_reason = reason;
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	ap_sta_deauthenticate_common(hapd, sta, reason);
+}
+
+
+enum ap_sta_disconnect_op {
+	AP_STA_DEAUTHENTICATE,
+	AP_STA_DISASSOCIATE,
+	AP_STA_DISCONNECT
+};
+
 static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
 				 struct sta_info *sta, u16 reason,
-				 bool disassoc)
+				 enum ap_sta_disconnect_op op)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_data *assoc_hapd, *tmp_hapd;
@@ -1072,25 +1122,30 @@
 				    tmp_sta->aid != assoc_sta->aid)
 					continue;
 
-				if (disassoc)
+				if (op == AP_STA_DISASSOCIATE)
 					ap_sta_handle_disassociate(tmp_hapd,
 								   tmp_sta,
 								   reason);
-				else
+				else if (op == AP_STA_DEAUTHENTICATE)
 					ap_sta_handle_deauthenticate(tmp_hapd,
 								     tmp_sta,
 								     reason);
-
+				else
+					ap_sta_handle_disconnect(tmp_hapd,
+								 tmp_sta,
+								 reason);
 				break;
 			}
 		}
 	}
 
 	/* Disconnect the station on which the association was performed. */
-	if (disassoc)
+	if (op == AP_STA_DISASSOCIATE)
 		ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
-	else
+	else if (op == AP_STA_DEAUTHENTICATE)
 		ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+	else
+		ap_sta_handle_disconnect(assoc_hapd, assoc_sta, reason);
 
 	return true;
 #else /* CONFIG_IEEE80211BE */
@@ -1102,7 +1157,7 @@
 void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 			 u16 reason)
 {
-	if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISASSOCIATE))
 		return;
 
 	ap_sta_handle_disassociate(hapd, sta, reason);
@@ -1112,7 +1167,15 @@
 void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 reason)
 {
-	if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		ap_sta_disassociate(hapd, sta, reason);
+		return;
+	}
+
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DEAUTHENTICATE))
 		return;
 
 	ap_sta_handle_deauthenticate(hapd, sta, reason);
@@ -1596,12 +1659,13 @@
 }
 
 
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+bool ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
 	if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
-		return;
+		return false;
 	ap_sta_set_authorized_event(hapd, sta, authorized);
+	return true;
 }
 
 
@@ -1625,41 +1689,19 @@
 
 	if (sta == NULL)
 		return;
-	sta->deauth_reason = reason;
-	ap_sta_set_authorized(hapd, sta, 0);
-	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-	hostapd_set_sta_flags(hapd, sta);
-	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
-	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
-	wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
-		   "for " MACSTR " (%d seconds - "
-		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
-		   hapd->conf->iface, __func__, MAC2STR(sta->addr),
-		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
-	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
-			       ap_handle_timer, hapd, sta);
-	sta->timeout_next = STA_REMOVE;
 
 	if (hapd->iface->current_mode &&
 	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
 		/* Deauthentication is not used in DMG/IEEE 802.11ad;
 		 * disassociate the STA instead. */
-		sta->disassoc_reason = reason;
-		sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
-		eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
-		eloop_register_timeout(hapd->iface->drv_flags &
-				       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
-				       2 : 0, 0, ap_sta_disassoc_cb_timeout,
-				       hapd, sta);
+		ap_sta_disassociate_common(hapd, sta, reason);
 		return;
 	}
 
-	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
-	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
-	eloop_register_timeout(hapd->iface->drv_flags &
-			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
-			       ap_sta_deauth_cb_timeout, hapd, sta);
+	if (ap_sta_ml_disconnect(hapd, sta, reason, AP_STA_DISCONNECT))
+		return;
+
+	ap_sta_handle_disconnect(hapd, sta, reason);
 }
 
 
@@ -1712,6 +1754,13 @@
 }
 
 
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+				struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_sta_assoc_timeout, hapd, sta);
+}
+
+
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
 {
 	int res;
@@ -1826,6 +1875,7 @@
 {
 	const u8 *mld_link_addr = NULL;
 	bool mld_link_sta = false;
+	u16 eml_cap = 0;
 
 	/*
 	 * If a station that is already associated to the AP, is trying to
@@ -1841,6 +1891,7 @@
 
 		mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
 		mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+		eml_cap = sta->mld_info.common_info.eml_capa;
 
 		/*
 		 * In case the AP is affiliated with an AP MLD, we need to
@@ -1859,7 +1910,7 @@
 			    sta->supported_rates_len,
 			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
 			    sta->flags, 0, 0, 0, 0,
-			    mld_link_addr, mld_link_sta)) {
+			    mld_link_addr, mld_link_sta, eml_cap)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_NOTICE,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 5b01c1e..d22e86d 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -386,7 +386,7 @@
 				int authorized);
 void ap_sta_set_authorized_event(struct hostapd_data *hapd,
 				 struct sta_info *sta, int authorized);
-void ap_sta_set_authorized(struct hostapd_data *hapd,
+bool ap_sta_set_authorized(struct hostapd_data *hapd,
 			   struct sta_info *sta, int authorized);
 static inline int ap_sta_is_authorized(struct sta_info *sta)
 {
@@ -397,6 +397,8 @@
 void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
 				      struct sta_info *sta);
+void ap_sta_clear_assoc_timeout(struct hostapd_data *hapd,
+				struct sta_info *sta);
 
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
 void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3af3404..5531aae 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -133,11 +133,8 @@
 		return;
 
 	for_each_sm_auth(sm, link_id) {
-		if (link_id == release_link_id) {
-			wpa_group_put(sm->mld_links[link_id].wpa_auth,
-				      sm->mld_links[link_id].wpa_auth->group);
+		if (link_id == release_link_id)
 			sm->mld_links[link_id].wpa_auth = NULL;
-		}
 	}
 }
 
@@ -368,6 +365,25 @@
 #endif /* CONFIG_MESH */
 
 
+static inline int wpa_auth_get_drv_flags(struct wpa_authenticator *wpa_auth,
+					 u64 *drv_flags, u64 *drv_flags2)
+{
+	if (!wpa_auth->cb->get_drv_flags)
+		return -1;
+	return wpa_auth->cb->get_drv_flags(wpa_auth->cb_ctx, drv_flags,
+					   drv_flags2);
+}
+
+
+static bool wpa_auth_4way_handshake_offload(struct wpa_authenticator *wpa_auth)
+{
+	u64 drv_flags = 0, drv_flags2 = 0;
+
+	return wpa_auth_get_drv_flags(wpa_auth, &drv_flags, &drv_flags2) == 0 &&
+		(drv_flags2 &  WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK);
+}
+
+
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
 			  void *cb_ctx)
@@ -1003,7 +1019,13 @@
 	if (wpa_sm_step(sm) == 1)
 		return 1; /* should not really happen */
 	sm->Init = false;
-	sm->AuthenticationRequest = true;
+
+	if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Skip EAPOL for 4-way handshake offload case");
+	else
+		sm->AuthenticationRequest = true;
+
 	return wpa_sm_step(sm);
 }
 
@@ -1049,11 +1071,8 @@
 	os_free(sm->rsnxe);
 	os_free(sm->rsn_selection);
 #ifdef CONFIG_IEEE80211BE
-	for_each_sm_auth(sm, link_id) {
-		wpa_group_put(sm->mld_links[link_id].wpa_auth,
-			      sm->mld_links[link_id].wpa_auth->group);
+	for_each_sm_auth(sm, link_id)
 		sm->mld_links[link_id].wpa_auth = NULL;
-	}
 #endif /* CONFIG_IEEE80211BE */
 	wpa_group_put(sm->wpa_auth, sm->group);
 #ifdef CONFIG_DPP2
@@ -2361,7 +2380,12 @@
 			if (wpa_sm_step(sm) == 1)
 				return 1; /* should not really happen */
 			sm->Init = false;
-			sm->AuthenticationRequest = true;
+
+			if (wpa_auth_4way_handshake_offload(sm->wpa_auth))
+				wpa_printf(MSG_DEBUG,
+					   "Skip EAPOL for 4-way handshake offload case");
+			else
+				sm->AuthenticationRequest = true;
 			break;
 		}
 
@@ -3820,7 +3844,6 @@
 		key_data_buf_len = key_data_length;
 		if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
 			       key_data, key_data_buf)) {
-			bin_clear_free(key_data_buf, key_data_buf_len);
 			wpa_printf(MSG_INFO,
 				   "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
 			goto out;
@@ -6613,6 +6636,26 @@
 }
 
 
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+			   const u8 **pmkid)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	pmksa = wpa_auth_pmksa_get(wpa_auth, sta_addr, NULL);
+	if (!pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR,
+			   MAC2STR(sta_addr));
+		return -1;
+	}
+
+	*pmk = pmksa->pmk;
+	*pmk_len = pmksa->pmk_len;
+	*pmkid = pmksa->pmkid;
+	return 0;
+}
+
+
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -7467,11 +7510,8 @@
 			ctx.wpa_auth = NULL;
 			wpa_auth_for_each_auth(sm->wpa_auth,
 					       wpa_get_link_sta_auth, &ctx);
-			if (ctx.wpa_auth) {
+			if (ctx.wpa_auth)
 				sm_link->wpa_auth = ctx.wpa_auth;
-				wpa_group_get(sm_link->wpa_auth,
-					      sm_link->wpa_auth->group);
-			}
 		} else {
 			sm_link->wpa_auth = sm->wpa_auth;
 		}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0b692ad..d4ef49c 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -531,6 +531,9 @@
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
+int wpa_auth_pmksa_get_pmk(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, const u8 **pmk, size_t *pmk_len,
+			   const u8 **pmkid);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
 				 const u8 *sta_addr, const u8 *pmkid);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 43d9c1d..ce7f90a 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1247,6 +1247,10 @@
 			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
 		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
 						 &data.pmkid[i * PMKID_LEN]);
+		if (!sm->pmksa && !is_zero_ether_addr(sm->p2p_dev_addr))
+			sm->pmksa = pmksa_cache_auth_get(
+				wpa_auth->pmksa, sm->p2p_dev_addr,
+				&data.pmkid[i * PMKID_LEN]);
 		if (sm->pmksa) {
 			pmkid = sm->pmksa->pmkid;
 			break;
@@ -1297,7 +1301,21 @@
 				!!(drv_flags2 &
 				   WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP);
 
-		if (!ap_sae_offload && data.num_pmkid && !sm->pmksa) {
+		/* Authenticator needs to have a PMKSA corresponding to a
+		 * PMKID (if present) included by the STA in (Re)Association
+		 * Request frame if PMKSA caching is attempted to be used. In
+		 * case of SAE, this follows Open System authentication. IEEE
+		 * Std 802.11 mandates the AP to reject (re)association trying
+		 * to use PMKSA caching for SAE authentication. While the
+		 * PMKID (if any) in the RSNE in (Re)Association Request frame
+		 * following SAE authentication (i.e., in the case of no PMKSA
+		 * caching) is not really supposed to include an unknown PMKID,
+		 * the standard does not require the AP to reject association.
+		 * The PMKSA that was just derived using SAE authentication
+		 * can be used regardless of which PMKID(s) are indicated in the
+		 * (Re)Association Request frame. */
+		if (!ap_sae_offload && data.num_pmkid && !sm->pmksa &&
+		    sm->auth_alg == WLAN_AUTH_OPEN) {
 			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					 "No PMKSA cache entry found for SAE");
 			return WPA_INVALID_PMKID;
diff --git a/src/common/defs.h b/src/common/defs.h
index 754c4e4..467051f 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -537,6 +537,12 @@
 	SAE_PWE_NOT_SET = 4,
 };
 
+enum wpa_p2p_mode {
+	WPA_P2P_MODE_WFD_R1	= 0,
+	WPA_P2P_MODE_WFD_R2	= 1,
+	WPA_P2P_MODE_WFD_PCC	= 2,
+};
+
 #define USEC_80211_TU 1024
 
 #define USEC_TO_TU(m) ((m) / USEC_80211_TU)
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index bffb440..78a68aa 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -432,6 +432,27 @@
 }
 
 
+static void punct_update_legacy_bw_320(u16 bitmap, u8 pri,
+				       enum oper_chan_width *width, u8 *seg0)
+{
+	if (pri < *seg0) {
+		*seg0 -= 16;
+		if (bitmap & 0x00FF) {
+			*width = 1;
+			punct_update_legacy_bw_160(bitmap & 0xFF, pri, width,
+						   seg0);
+		}
+	} else {
+		*seg0 += 16;
+		if (bitmap & 0xFF00) {
+			*width = 1;
+			punct_update_legacy_bw_160((bitmap & 0xFF00) >> 8,
+						   pri, width, seg0);
+		}
+	}
+}
+
+
 void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
 			    u8 *seg0, u8 *seg1)
 {
@@ -446,7 +467,10 @@
 		punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
 	}
 
-	/* TODO: 320 MHz */
+	if (*width == CONF_OPER_CHWIDTH_320MHZ && (bitmap & 0xFFFF)) {
+		*width = CONF_OPER_CHWIDTH_160MHZ;
+		punct_update_legacy_bw_320(bitmap & 0xFFFF, pri, width, seg0);
+	}
 }
 
 
@@ -481,6 +505,7 @@
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
+	data->punct_bitmap = punct_bitmap;
 	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 		data->bandwidth = 80;
 	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index c9b2d37..1c36be5 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -140,6 +140,10 @@
 			elems->sae_pk = pos + 4;
 			elems->sae_pk_len = elen - 4;
 			break;
+		case WFA_CAPA_OUI_TYPE:
+			elems->wfa_capab = pos + 4;
+			elems->wfa_capab_len = elen - 4;
+			break;
 		case WFA_RSNE_OVERRIDE_OUI_TYPE:
 			elems->rsne_override = pos;
 			elems->rsne_override_len = elen;
@@ -148,6 +152,10 @@
 			elems->rsne_override_2 = pos;
 			elems->rsne_override_2_len = elen;
 			break;
+		case WFA_RSNXE_OVERRIDE_OUI_TYPE:
+			elems->rsnxe_override = pos;
+			elems->rsnxe_override_len = elen;
+			break;
 		case WFA_RSN_SELECTION_OUI_TYPE:
 			if (elen < 4 + 1) {
 				wpa_printf(MSG_DEBUG,
@@ -993,14 +1001,14 @@
 }
 
 
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
-					 struct ieee802_11_elems *elems,
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 					 struct wpabuf *mlbuf,
 					 u8 link_id, bool show_errors)
 {
 	const struct ieee80211_eht_ml *ml;
 	const u8 *pos;
 	ParseRes res = ParseFailed;
+	size_t len;
 
 	pos = wpabuf_head(mlbuf);
 	len = wpabuf_len(mlbuf);
@@ -3152,7 +3160,7 @@
 	if (flen > 4)
 		flen = 4;
 	for (i = 0; i < flen; i++)
-		capabs |= rsnxe[i] << (8 * i);
+		capabs |= (u32) rsnxe[i] << (8 * i);
 
 	return !!(capabs & BIT(capab));
 }
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 62090ce..009073c 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -120,7 +120,9 @@
 	const u8 *mbssid;
 	const u8 *rsne_override;
 	const u8 *rsne_override_2;
+	const u8 *rsnxe_override;
 	const u8 *rsn_selection;
+	const u8 *wfa_capab;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -188,7 +190,9 @@
 	u8 mbssid_len;
 	size_t rsne_override_len;
 	size_t rsne_override_2_len;
+	size_t rsnxe_override_len;
 	size_t rsn_selection_len;
+	u8 wfa_capab_len;
 
 	struct mb_ies_info mb_ies;
 
@@ -210,8 +214,7 @@
 				const u8 *ids, size_t num);
 void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
 				    const u8 *ids, size_t num);
-ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
-					 struct ieee802_11_elems *elems,
+ParseRes ieee802_11_parse_link_assoc_req(struct ieee802_11_elems *elems,
 					 struct wpabuf *mlbuf,
 					 u8 link_id, bool show_errors);
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 7ce7591..c662e0a 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -619,6 +619,7 @@
 #define WLAN_RSNX_CAPAB_SECURE_RTT 9
 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
 #define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_KEK_IN_PASN 18
 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
 
 /* Multiple BSSID element subelements */
@@ -2865,6 +2866,7 @@
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL    0x0020
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE    0x0040
 #define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK  0x0060
+#define EHT_ML_MLD_CAPA_AP_MLD_TYPE_IND_MASK          0x0080
 #define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK         0x0f80
 #define EHT_ML_MLD_CAPA_AAR_SUPP                      0x1000
 
@@ -3156,6 +3158,15 @@
 #endif /* (CONFIG_DRIVER_NL80211_BRCM && !WIFI_BRCM_OPEN_SOURCE_MULTI_AKM) ||
 	* CONFIG_DRIVER_NL80211_SYNA */
 
+/* Wi-Fi Alliance Capabilities attributes */
+enum wfa_capa_attr_id {
+	WFA_CAPA_ATTR_GENERATIONAL_CAPAB = 1,
+	WFA_CAPA_ATTR_VENDOR_SPECIFIC = 221,
+};
+
+/* Wi-Fi Alliance Capabilities frame */
+#define WFA_CAPAB_VENDOR_TYPE 0x506f9a1b
+
 struct ieee80211_neighbor_ap_info {
 	u8 tbtt_info_hdr;
 	u8 tbtt_info_len;
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index acde4f3..2c1d0c4 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -18,14 +18,18 @@
 
 static const u8 nan_network_id[ETH_ALEN] =
 { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
-static const u8 wildcard_bssid[ETH_ALEN] =
-{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 enum nan_de_service_type {
 	NAN_DE_PUBLISH,
 	NAN_DE_SUBSCRIBE,
 };
 
+static const u8 p2p_network_id[ETH_ALEN] =
+{ 0x51, 0x6f, 0x9a, 0x02, 0x00, 0x00 };
+
+static const u8 wildcard_bssid[ETH_ALEN] =
+{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
 struct nan_de_service {
 	int id;
 	enum nan_de_service_type type;
@@ -40,11 +44,12 @@
 	struct os_reltime end_time;
 	struct os_reltime last_multicast;
 	struct os_reltime first_discovered;
-	struct os_reltime last_followup;
 	bool needs_fsd;
 	unsigned int freq;
 	unsigned int default_freq;
 	int *freq_list;
+	u8 a3[ETH_ALEN];
+	bool a3_set;
 
 	/* pauseState information for Publish function */
 	struct os_reltime pause_state_end;
@@ -65,6 +70,7 @@
 	u8 nmi[ETH_ALEN];
 	bool offload;
 	bool ap;
+	unsigned int max_listen;
 	struct nan_callbacks cb;
 
 	struct nan_de_service *service[NAN_DE_MAX_SERVICE];
@@ -79,7 +85,20 @@
 };
 
 
+bool nan_de_is_nan_network_id(const u8 *addr)
+{
+	return ether_addr_equal(addr, nan_network_id);
+}
+
+
+bool nan_de_is_p2p_network_id(const u8 *addr)
+{
+	return ether_addr_equal(addr, p2p_network_id);
+}
+
+
 struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+			    unsigned int max_listen,
 			    const struct nan_callbacks *cb)
 {
 	struct nan_de *de;
@@ -91,6 +110,7 @@
 	os_memcpy(de->nmi, nmi, ETH_ALEN);
 	de->offload = offload;
 	de->ap = ap;
+	de->max_listen = max_listen ? max_listen : 1000;
 	os_memcpy(&de->cb, cb, sizeof(*cb));
 
 	return de;
@@ -152,6 +172,9 @@
 	wpa_printf(MSG_DEBUG, "NAN: Start pauseState");
 	os_get_reltime(&srv->pause_state_end);
 	srv->pause_state_end.sec += 60;
+	if (os_reltime_initialized(&srv->end_time) &&
+	    os_reltime_before(&srv->end_time, &srv->pause_state_end))
+		srv->pause_state_end = srv->end_time;
 	os_memcpy(srv->sel_peer_addr, peer_addr, ETH_ALEN);
 	srv->sel_peer_id = peer_id;
 }
@@ -206,7 +229,7 @@
 static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
 			  unsigned int wait_time,
 			  enum nan_service_control_type type,
-			  const u8 *dst, u8 req_instance_id,
+			  const u8 *dst, const u8 *a3, u8 req_instance_id,
 			  const struct wpabuf *ssi)
 {
 	struct wpabuf *buf;
@@ -268,10 +291,7 @@
 		wpabuf_put_buf(buf, srv->elems);
 	}
 
-	/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
-	 * but there is no synchronization in USD as as such, no NAN Cluster
-	 * either. Use Wildcard BSSID instead. */
-	nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, wildcard_bssid, buf);
+	nan_de_tx(de, srv->freq, wait_time, dst, de->nmi, a3, buf);
 	wpabuf_free(buf);
 }
 
@@ -335,6 +355,8 @@
 {
 	enum nan_service_control_type type;
 	unsigned int wait_time = 100;
+	const u8 *network_id;
+	const u8 *bssid;
 
 	if (srv->type == NAN_DE_PUBLISH) {
 		int ms;
@@ -352,7 +374,15 @@
 		return;
 	}
 
-	nan_de_tx_sdf(de, srv, wait_time, type, nan_network_id,
+	if (srv->is_p2p) {
+		network_id = p2p_network_id;
+		bssid = wildcard_bssid;
+	} else {
+		network_id = nan_network_id;
+		bssid = nan_network_id;
+	}
+
+	nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid,
 		      req_instance_id, srv->ssi);
 	os_get_reltime(&srv->last_multicast);
 }
@@ -398,11 +428,6 @@
 			return false;
 		if (!srv->publish.fsd)
 			return true;
-		if (os_reltime_initialized(&srv->last_followup) &&
-		    !os_reltime_expired(now, &srv->last_followup, 1))
-			return false;
-		if (os_reltime_expired(now, &srv->last_multicast, 1))
-			return true;
 	}
 
 	if (srv->type == NAN_DE_SUBSCRIBE) {
@@ -412,11 +437,6 @@
 			return false;
 		if (!srv->needs_fsd)
 			return true;
-		if (os_reltime_initialized(&srv->last_followup) &&
-		    !os_reltime_expired(now, &srv->last_followup, 1))
-			return false;
-		if (os_reltime_expired(now, &srv->first_discovered, 1))
-			return true;
 	}
 
 	return false;
@@ -481,6 +501,16 @@
 			next = tmp;
 	}
 
+	if (srv->type == NAN_DE_PUBLISH &&
+	    srv->publish.fsd &&
+	    os_reltime_initialized(&srv->pause_state_end)) {
+		os_reltime_sub(&srv->pause_state_end, now, &diff);
+		tmp = os_reltime_in_ms(&diff);
+		if (next == -1 || tmp < next)
+			next = tmp;
+		return next;
+	}
+
 	tmp = nan_de_next_multicast(de, srv, now);
 	if (tmp >= 0 && (next == -1 || tmp < next))
 		next = tmp;
@@ -583,16 +613,37 @@
 
 		if (srv->type == NAN_DE_PUBLISH &&
 		    os_reltime_initialized(&srv->pause_state_end) &&
-		    (os_reltime_before(&srv->pause_state_end, &now) ||
-		     (srv->publish.fsd &&
-		      os_reltime_initialized(&srv->last_followup) &&
-		      os_reltime_expired(&now, &srv->last_followup, 1))))
+		    (os_reltime_before(&srv->pause_state_end, &now)))
 			nan_de_unpause_state(srv);
 
 		srv_next = nan_de_srv_time_to_next(de, srv, &now);
 		if (srv_next >= 0 && (next == -1 || srv_next < next))
 			next = srv_next;
 
+		if (srv->type == NAN_DE_PUBLISH &&
+		    srv->publish.fsd &&
+		    os_reltime_initialized(&srv->pause_state_end) &&
+		    de->tx_wait_end_freq == 0 &&
+		    de->listen_freq == 0 && de->ext_listen_freq == 0) {
+			struct os_reltime diff;
+			int duration;
+
+			os_reltime_sub(&srv->pause_state_end, &now, &diff);
+			duration = os_reltime_in_ms(&diff);
+			if (duration < 0)
+				continue;
+			if ((unsigned int) duration > de->max_listen)
+				duration = de->max_listen;
+			if (de->cb.listen(de->cb.ctx, srv->freq, duration) ==
+			    0) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Publisher in pauseState - started listen on %u MHz",
+					   srv->freq);
+				de->listen_freq = srv->freq;
+				return;
+			}
+		}
+
 		if (srv_next == 0 && !started && !de->offload &&
 		    de->listen_freq == 0 && de->ext_listen_freq == 0 &&
 		    de->tx_wait_end_freq == 0 &&
@@ -629,7 +680,7 @@
 
 	if (next == 0)
 		next = 1;
-	wpa_printf(MSG_DEBUG, "NAN: Next timer in %u ms", next);
+
 	eloop_register_timeout(next / 1000, (next % 1000) * 1000, nan_de_timer,
 			       de, NULL);
 }
@@ -679,6 +730,10 @@
 
 void nan_de_tx_wait_ended(struct nan_de *de)
 {
+	if (de->tx_wait_end_freq)
+		wpa_printf(MSG_DEBUG,
+			   "NAN: TX wait for response ended (freq=%u)",
+			   de->tx_wait_end_freq);
 	de->tx_wait_end_freq = 0;
 	nan_de_run_timer(de);
 }
@@ -806,7 +861,7 @@
 
 
 static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
-			      const u8 *peer_addr, u8 instance_id,
+			      const u8 *peer_addr, const u8 *a3, u8 instance_id,
 			      u8 req_instance_id, u16 sdea_control,
 			      enum nan_service_protocol_type srv_proto_type,
 			      const u8 *ssi, size_t ssi_len)
@@ -877,7 +932,8 @@
 
 
 static void nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
-				const u8 *peer_addr, u8 instance_id,
+				const u8 *peer_addr, const u8 *a3,
+				u8 instance_id,
 				const u8 *matching_filter,
 				size_t matching_filter_len,
 				enum nan_service_protocol_type srv_proto_type,
@@ -887,6 +943,7 @@
 	size_t len = 0, sda_len, sdea_len;
 	u8 ctrl = 0;
 	u16 sdea_ctrl = 0;
+	const u8 *network_id;
 
 	/* Publish function processing of a receive Subscribe message */
 
@@ -963,15 +1020,23 @@
 		wpabuf_put_buf(buf, srv->elems);
 	}
 
-	/* Wi-Fi Aware specification v4.0 uses NAN Cluster ID as A3 for USD,
-	 * but there is no synchronization in USD as as such, no NAN Cluster
-	 * either. Use Wildcard BSSID instead. */
+	if (srv->is_p2p)
+		network_id = p2p_network_id;
+	else
+		network_id = nan_network_id;
+
+	if (srv->publish.solicited_multicast || !a3)
+		a3 = network_id;
+	else if (srv->is_p2p)
+		a3 = de->nmi;
+
 	nan_de_tx(de, srv->freq, 100,
-		  srv->publish.solicited_multicast ? nan_network_id : peer_addr,
-		  de->nmi, wildcard_bssid, buf);
+		  srv->publish.solicited_multicast ? network_id : peer_addr,
+		  de->nmi, a3, buf);
 	wpabuf_free(buf);
 
-	nan_de_pause_state(srv, peer_addr, instance_id);
+	if (!srv->is_p2p)
+		nan_de_pause_state(srv, peer_addr, instance_id);
 
 offload:
 	if (!srv->publish.disable_events && de->cb.replied)
@@ -981,8 +1046,8 @@
 
 
 static void nan_de_rx_follow_up(struct nan_de *de, struct nan_de_service *srv,
-				const u8 *peer_addr, u8 instance_id,
-				const u8 *ssi, size_t ssi_len)
+				const u8 *peer_addr, const u8 *a3,
+				u8 instance_id, const u8 *ssi, size_t ssi_len)
 {
 	/* Follow-up function processing of a receive Follow-up message for a
 	 * Subscribe or Publish instance */
@@ -997,18 +1062,19 @@
 		return;
 	}
 
-	os_get_reltime(&srv->last_followup);
-
 	if (srv->type == NAN_DE_PUBLISH && !ssi)
 		nan_de_pause_state(srv, peer_addr, instance_id);
 
+	os_memcpy(srv->a3, a3, ETH_ALEN);
+	srv->a3_set = true;
+
 	if (de->cb.receive)
 		de->cb.receive(de->cb.ctx, srv->id, instance_id, ssi, ssi_len,
 			       peer_addr);
 }
 
 
-static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr,
+static void nan_de_rx_sda(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
 			  unsigned int freq, const u8 *buf, size_t len,
 			  const u8 *sda, size_t sda_len)
 {
@@ -1135,20 +1201,20 @@
 
 		switch (type) {
 		case NAN_SRV_CTRL_PUBLISH:
-			nan_de_rx_publish(de, srv, peer_addr, instance_id,
+			nan_de_rx_publish(de, srv, peer_addr, a3, instance_id,
 					  req_instance_id,
 					  sdea_control, srv_proto_type,
 					  ssi, ssi_len);
 			break;
 		case NAN_SRV_CTRL_SUBSCRIBE:
-			nan_de_rx_subscribe(de, srv, peer_addr, instance_id,
+			nan_de_rx_subscribe(de, srv, peer_addr, a3, instance_id,
 					    matching_filter,
 					    matching_filter_len,
 					    srv_proto_type,
 					    ssi, ssi_len);
 			break;
 		case NAN_SRV_CTRL_FOLLOW_UP:
-			nan_de_rx_follow_up(de, srv, peer_addr, instance_id,
+			nan_de_rx_follow_up(de, srv, peer_addr, a3, instance_id,
 					    ssi, ssi_len);
 			break;
 		}
@@ -1156,8 +1222,8 @@
 }
 
 
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
-		   const u8 *buf, size_t len)
+void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+		   unsigned int freq, const u8 *buf, size_t len)
 {
 	const u8 *sda;
 	u16 sda_len;
@@ -1179,7 +1245,7 @@
 		sda++;
 		sda_len = WPA_GET_LE16(sda);
 		sda += 2;
-		nan_de_rx_sda(de, peer_addr, freq, buf, len, sda, sda_len);
+		nan_de_rx_sda(de, peer_addr, a3, freq, buf, len, sda, sda_len);
 	}
 }
 
@@ -1442,6 +1508,8 @@
 		    const u8 *peer_addr, u8 req_instance_id)
 {
 	struct nan_de_service *srv;
+	const u8 *a3;
+	const u8 *network_id;
 
 	if (handle < 1 || handle > NAN_DE_MAX_SERVICE)
 		return -1;
@@ -1450,9 +1518,17 @@
 	if (!srv)
 		return -1;
 
-	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
-		      peer_addr, req_instance_id, ssi);
+	if (srv->is_p2p)
+		network_id = p2p_network_id;
+	else
+		network_id = nan_network_id;
 
-	os_get_reltime(&srv->last_followup);
+	if (srv->a3_set)
+		a3 = srv->a3;
+	else
+		a3 = network_id;
+	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
+		      peer_addr, a3, req_instance_id, ssi);
+
 	return 0;
 }
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
index f369a57..9c1df31 100644
--- a/src/common/nan_de.h
+++ b/src/common/nan_de.h
@@ -59,7 +59,10 @@
 				      unsigned int freq);
 };
 
+bool nan_de_is_nan_network_id(const u8 *addr);
+bool nan_de_is_p2p_network_id(const u8 *addr);
 struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
+			    unsigned int max_listen,
 			    const struct nan_callbacks *cb);
 void nan_de_flush(struct nan_de *de);
 void nan_de_deinit(struct nan_de *de);
@@ -70,8 +73,8 @@
 void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst);
 void nan_de_tx_wait_ended(struct nan_de *de);
 
-void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, unsigned int freq,
-		   const u8 *buf, size_t len);
+void nan_de_rx_sdf(struct nan_de *de, const u8 *peer_addr, const u8 *a3,
+		   unsigned int freq, const u8 *buf, size_t len);
 const u8 * nan_de_get_service_id(struct nan_de *de, int id);
 
 struct nan_publish_params {
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index ddf1966..6c80589 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1298,6 +1298,33 @@
  *
  *	The attributes used with this command are defined in
  *	enum qca_wlan_vendor_attr_chan_usage_req.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT: Vendor subcommand that can be
+ *	used to fetch the current snapshot of scan data stored by firmware
+ *	during the offload scans such as PNO (Preferred Network Offload), RTT,
+ *	and roaming scans when the Apps or host is in suspended state. This scan
+ *	data comprises of only limited information of the scanned BSSs due to
+ *	memory limits of the firmware. The BSS information stored in the
+ *	firmware may not be pushed to the kernel (cfg80211) scan cache after
+ *	Apps or host coming out from suspended state if full Beacon or Probe
+ *	Response frame information is not available.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_fw_scan_report.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN: If there are no active Wi-Fi
+ *	interfaces for a certain duration, the host driver might trigger idle
+ *	shutdown. The host driver rejects the user space commands between start
+ *	and completion of the idle shutdown. If a command is rejected, user
+ *	space can use this event to determine when to retry the specific
+ *	command.
+ *
+ *	This is a wiphy specific vendor event and it indicates user space that
+ *	the host driver has reached the idle timer and has started or completed
+ *	idle shutdown procedure.
+ *
+ *	The attributes used with this event are defined in
+ *	enum qca_wlan_vendor_attr_idle_shutdown.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1534,6 +1561,9 @@
 	QCA_NL80211_VENDOR_SUBCMD_CONNECT_EXT = 250,
 	QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE = 251,
 	QCA_NL80211_VENDOR_SUBCMD_CHAN_USAGE_REQ = 252,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT = 253,
+	QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN = 254,
+	/* 255 - reserved for QCA */
 };
 
 /* Compatibility defines for previously used subcmd names.
@@ -2015,7 +2045,9 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC.
  * Used with command to configure channel list using an array of
- * channel numbers (u8).
+ * channel numbers (u8). This represents the list of allowed channels for
+ * the primary and non-primary channel operation. Channels which are not present
+ * in the specified list shouldn't be used as a primary or non-primary channel.
  * Note: If both the driver and user-space application supports the 6 GHz band,
  * the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas
  * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional.
@@ -2047,7 +2079,10 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC.
  * Used with command to configure the channel list using an array of channel
- * center frequencies in MHz (u32).
+ * center frequencies in MHz (u32). This represents the list of allowed
+ * frequencies for the primary and non-primary channel operation. Frequencies
+ * which are not present in the specified list shouldn't be used as a primary or
+ * non-primary channel.
  * Note: If both the driver and user-space application supports the 6 GHz band,
  * the driver first parses the frequency list and if it fails to get a frequency
  * list, parses the channel list specified using
@@ -2110,6 +2145,15 @@
  * Used with command to configure ACS operation for a specific link affiliated
  * to an AP MLD.
  *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY: Optional flag
+ * attribute. Used with command to indicate whether the driver is allowed to use
+ * a 6 GHz non-PSC channel as a primary channel. If this flag is indicated the
+ * driver shall not use 6 GHz non-PSC channels as a primary channel even if
+ * %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST includes 6 GHz non-PSC channels.
+ * However, the driver is still allowed to use 6 GHz non-PSC channels specified
+ * in %QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST as non-primary channels. User space is
+ * allowed to specify this flag only when the driver indicates support for
+ * %QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC.
  */
 enum qca_wlan_vendor_attr_acs_offload {
 	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -2134,6 +2178,7 @@
 	QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19,
 	QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME = 20,
 	QCA_WLAN_VENDOR_ATTR_ACS_LINK_ID = 21,
+	QCA_WLAN_VENDOR_ATTR_ACS_EXCLUDE_6GHZ_NON_PSC_PRIMARY = 22,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -2271,6 +2316,10 @@
  * @QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD: Flag indicates that the driver
  *	supports Unsynchronized Service Discovery to be offloaded to it.
  *
+ * @QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC: Flag indicates that the driver
+ *	supports preferring 6 GHz PSC channel as a primary channel in ACS
+ *	result.
+ *
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -2301,6 +2350,7 @@
 	QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER = 24,
 	QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA = 25,
 	QCA_WLAN_VENDOR_FEATURE_NAN_USD_OFFLOAD = 26,
+	QCA_WLAN_VENDOR_FEATURE_ACS_PREFER_6GHZ_PSC = 27,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -3754,6 +3804,51 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_P2P_GO_BEACON_INTERVAL = 122,
 
+	/* 8-bit unsigned value. Disable DFS owner capability
+	 * 1: disable DFS owner capability in the driver.
+	 * 0: reset DFS owner capability to the default DFS owner capability of
+	 * the driver.
+	 *
+	 * If DFS owner capability is disabled, the driver will not start AP
+	 * mode operations on DFS channels, and all the features depending on
+	 * DFS owner functionality will not be supported.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DFS_OWNER_DISABLE = 123,
+
+	/* 16-bit unsigned value. For probing RSSI on other antennas, this
+	 * attribute specifies the number of WLAN probes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_WLAN = 124,
+
+	/* 16-bit unsigned value. For probing RSSI on other antennas, this
+	 * attribute specifies the number of BT probes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_COUNT_BT = 125,
+
+	/* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+	 * threshold. The firmware will start to probe RSSI on other antenna
+	 * if WLAN RSSI is lower than the threshold.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_WLAN_RSSI_THRESHOLD = 126,
+
+	/* 16-bit unsigned value. This attribute specifies the BT RSSI
+	 * threshold. The firmware will start to probe RSSI on other antenna
+	 * if BT RSSI is lower than the threshold.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_BT_RSSI_THRESHOLD = 127,
+
+	/* 16-bit unsigned value. This attribute specifies the WLAN RSSI
+	 * difference. The firmware will select a better antenna if WLAN RSSI
+	 * difference is larger than the value.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_WLAN_RSSI_DIFF = 128,
+
+	/* 16-bit unsigned value. This attribute specifies the BT RSSI
+	 * difference. The firmware will select a better antenna if WLAN RSSI
+	 * difference larger than the value.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SWITCH_BT_RSSI_DIFF = 129,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -10783,6 +10878,42 @@
 };
 
 /**
+ * enum qca_wlan_twt_session_suspendable: The values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE: TWT session cannot be suspended.
+ * @QCA_WLAN_TWT_SESSION_SUSPENDABLE: TWT session can be suspended.
+ */
+enum qca_wlan_twt_session_suspendable {
+	QCA_WLAN_TWT_SESSION_NOT_SUSPENDABLE = 0,
+	QCA_WLAN_TWT_SESSION_SUSPENDABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_updatable: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_UPDATABLE: TWT session cannot be updated.
+ * @QCA_WLAN_TWT_SESSION_UPDATABLE: TWT session can be updated.
+ */
+enum qca_wlan_twt_session_updatable {
+	QCA_WLAN_TWT_SESSION_NOT_UPDATABLE = 0,
+	QCA_WLAN_TWT_SESSION_UPDATABLE = 1,
+};
+
+/**
+ * enum qca_wlan_twt_session_implicit: Define the values used with
+ * %QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT.
+ *
+ * @QCA_WLAN_TWT_SESSION_NOT_IMPLICIT: TWT session cannot be implicit.
+ * @QCA_WLAN_TWT_SESSION_IMPLICIT: TWT session can be implicit.
+ */
+enum qca_wlan_twt_session_implicit {
+	QCA_WLAN_TWT_SESSION_NOT_IMPLICIT = 0,
+	QCA_WLAN_TWT_SESSION_IMPLICIT = 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
  * TWT (Target Wake Time) setup request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
@@ -10974,9 +11105,10 @@
  * The Broadcast TWT Recommendation subfield contains a value that indicates
  * recommendations on the types of frames that are transmitted by TWT
  * scheduled STAs and scheduling AP during the broadcast TWT SP.
- * The allowed values are 0 - 3.
+ * The allowed values are 0 - 4.
  * This parameter is used for
  * 1. TWT SET Request
+ * 2. R-TWT SET Request
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8)
  * This attribute is used to configure Broadcast TWT Persistence.
@@ -11011,6 +11143,38 @@
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD: Attribute used for padding for 64-bit
  * alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated can be
+ * suspended.
+ * Refers the enum qca_wlan_twt_session_suspendable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure downlink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP: Optional (u32)
+ * This attribute is used to configure uplink TIDs for R-TWT scheduling.
+ * This attribute only applicable when requesting R-TWT schedules.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE: Optional (u8)
+ * This attribute indicates whether the parameters of the TWT session being
+ * negotiated (like wake interval, wake duration, etc.) can be updated after
+ * session setup.
+ * Refers the enum qca_wlan_twt_session_updatable.
+ * This parameter is used for
+ * 1. TWT SET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT: Optional (u8)
+ * This attribute indicates whether the TWT session being negotiated is
+ * an implicit TWT, where the requesting STA calculates the start time of the
+ * next TWT service period, or an explicit TWT, where the responding STA
+ * calculates the start time of the next TWT service period.
+ * Refers the enum qca_wlan_twt_session_implicit.
+ * This parameter is used for
+ * 1. TWT SET Response
  */
 enum qca_wlan_vendor_attr_twt_setup {
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -11048,6 +11212,11 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26,
 
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD = 27,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_SUSPENDABLE = 28,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_DOWNLINK_TID_BITMAP = 29,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RTWT_UPLINK_TID_BITMAP = 30,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_UPDATABLE = 31,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_IMPLICIT = 32,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
@@ -11103,6 +11272,16 @@
  * required bit in its capabilities.
  * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED: The peer has cleared
  * the TWT required bit(1->0) in its capabilities.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE: FW terminated
+ * the TWT session due to more than one MLO link becoming active. Used on the
+ * TWT_TERMINATE notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED: TWT session already in
+ * resumed state. Used on the TWT_RESUME notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED: Requested TWT operation is
+ * rejected by the peer. Used on the TWT_SET notification from the
+ * driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT: Requested TWT operation has timed out.
+ * Used on the TWT_SET, TWT_TERMINATE notification from the driver/firmware.
  */
 enum qca_wlan_vendor_twt_status {
 	QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
@@ -11130,6 +11309,10 @@
 	QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE = 22,
 	QCA_WLAN_VENDOR_TWT_STATUS_TWT_REQUIRED = 23,
 	QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED = 24,
+	QCA_WLAN_VENDOR_TWT_STATUS_MULTIPLE_LINKS_ACTIVE_TERMINATE = 25,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_ALREADY_RESUMED = 26,
+	QCA_WLAN_VENDOR_TWT_STATUS_PEER_REJECTED = 27,
+	QCA_WLAN_VENDOR_TWT_STATUS_TIMEOUT = 28,
 };
 
 /**
@@ -11312,6 +11495,16 @@
  * Status of the TWT GET STATISTICS request.
  * This contains status values in enum qca_wlan_vendor_twt_status
  * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US: Optional (u32)
+ * Average of duration of the early terminated TWT service periods
+ * in micro seconds.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT: Optional (u32)
+ * Number of early terminated TWT service periods observed over
+ * QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
+ * Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
  */
 enum qca_wlan_vendor_attr_twt_stats {
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0,
@@ -11327,6 +11520,8 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10,
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11,
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12,
+	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_EOSP_DUR_US = 13,
+	QCA_WLAN_VENDOR_ATTR_TWT_STATS_EOSP_COUNT = 14,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST,
@@ -11384,12 +11579,28 @@
  * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16).
  * Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in
  * enum qca_wlan_twt_capa.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL: (u32).
+ * Minimum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL: (u32).
+ * Maximum tolerance limit of wake interval parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION: (u32).
+ * Minimum tolerance limit of wake duration parameter in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION: (u32).
+ * Maximum tolerance limit of wake duration parameter in microseconds.
  */
 enum qca_wlan_vendor_attr_twt_capability {
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2,
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_INTVL = 4,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_INTVL = 5,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MIN_WAKE_DURATION = 6,
+	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX_WAKE_DURATION = 7,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST,
@@ -11458,11 +11669,31 @@
  * which the STA may accept.
  * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
  * alternate parameters than the requested ones.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING: TWT responding STA suggests TWT
+ * group parameters that are different from the suggested or demanded TWT
+ * parameters of the TWT requesting STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT: TWT responding STA or TWT scheduling
+ * AP accepts the TWT request with the TWT parameters indicated in the TWT
+ * element transmitted by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT: TWT responding STA or TWT
+ * scheduling AP suggests TWT parameters that are different from those suggested
+ * by the TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT: TWT responding STA or TWT scheduling
+ * dictates TWT parameters that are different from those suggested by the
+ * TWT requesting STA or TWT scheduled STA.
+ * @QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT: A TWT responding STA or TWT scheduling
+ * AP rejects setup or terminates an existing broadcast TWT, or a TWT scheduled
+ * STA terminates its membership in a broadcast TWT.
  */
 enum qca_wlan_vendor_twt_setup_req_type {
 	QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
 	QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
 	QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
+	QCA_WLAN_VENDOR_TWT_SETUP_TWT_GROUPING = 4,
+	QCA_WLAN_VENDOR_TWT_SETUP_ACCEPT_TWT = 5,
+	QCA_WLAN_VENDOR_TWT_SETUP_ALTERNATE_TWT = 6,
+	QCA_WLAN_VENDOR_TWT_SETUP_DICTATE_TWT = 7,
+	QCA_WLAN_VENDOR_TWT_SETUP_REJECT_TWT = 8,
 };
 
 /**
@@ -14496,6 +14727,22 @@
 	QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME = BIT(1),
 };
 
+/*
+ * enum qca_wlan_vendor_monitor_operating_type: Attributes used by vendor
+ * attribute %QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC: Local packet capture.
+ * Capture frames sent and received by the current client interface from the
+ * BSS.
+ *
+ * @QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC: Operating channel capture.
+ * Capture all frames on the current operating channel of client interface.
+ */
+enum qca_wlan_vendor_monitor_operating_type {
+	QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_LPC = 0,
+	QCA_WLAN_VENDOR_MONITOR_OPERATING_TYPE_OCC = 1,
+};
+
 /**
  * enum qca_wlan_vendor_attr_set_monitor_mode - Used by the
  * vendor command QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE to set the
@@ -14532,6 +14779,12 @@
  * Represents the interval in milliseconds only for the connected Beacon frames,
  * expecting the connected BSS's Beacon frames to be sent on the monitor
  * interface at this specific interval.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE: u32 attribute.
+ * Represents the monitor operating type (u32). These operating types are
+ * defined in enum qca_wlan_vendor_monitor_operating_type.
+ * If this attribute is not included, default operating type LPC ("local
+ * packet capture") used.
  */
 enum qca_wlan_vendor_attr_set_monitor_mode {
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_INVALID = 0,
@@ -14542,6 +14795,7 @@
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE = 5,
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE = 6,
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL = 7,
+	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_OPERATING_TYPE = 8,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST,
@@ -18380,4 +18634,161 @@
 	QCA_WLAN_VENDOR_ATTR_CHAN_USAGE_REQ_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_fw_scan_bss_flags - Flags for
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HT_OPS: This indicates HT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.56) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_VHT_OPS: This indicates VHT Operation element
+ * (IEEE Std 802.11-2020, 9.4.2.158) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_HE_OPS: This indicates HE Operation element
+ * (IEEE Std 802.11ax-2021, 9.4.2.249) is present in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_EHT_OPS: This indicates EHT Operation element
+ * (IEEE P802.11be/D7.0, 9.4.2.321) is present in the Beacon or Probe Response
+ * frame of the BSS.
+ *
+ * @QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER: This indicates Fine Timing Measurement
+ * Responder bit is set to 1 in the Extended Capabilities field of the Extended
+ * Capabilities element (IEEE Std 802.11-2020, 9.4.2.26) in the Beacon or Probe
+ * Response frame of the BSS.
+ *
+ * @NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS: Number of assigned feature bits.
+ */
+enum qca_wlan_fw_scan_bss_flags {
+	QCA_WLAN_FW_SCAN_BSS_HT_OPS = 0,
+	QCA_WLAN_FW_SCAN_BSS_VHT_OPS = 1,
+	QCA_WLAN_FW_SCAN_BSS_HE_OPS = 2,
+	QCA_WLAN_FW_SCAN_BSS_EHT_OPS = 3,
+	QCA_WLAN_FW_SCAN_BSS_FTM_RESPONDER = 4,
+
+	NUM_QCA_WLAN_FW_SCAN_BSS_FLAGS /* keep last */
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_bss: Attributes used inside
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO: Required (u32). Indicates how many
+ * milliseconds ago from %QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP this BSS
+ * was last scanned (i.e., Beacon or Probe Response frame received).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID: Required (6-byte MAC address). BSSID
+ * of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID: Required (binary attribute,
+ * 0..32 octets). SSID of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI: Required (s8). RSSI of the last
+ * received Beacon or Probe Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY: Required (CPU byte order, u16).
+ * The Capability Information field from the last received Beacon or Probe
+ * Response frame.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS: BSS capability flags contained in a
+ * byte array. The flags are identified by their bit index (see &enum
+ * qca_wlan_fw_scan_bss_flags) with the first byte being the least significant
+ * one and the last one being the most significant one. This information will be
+ * populated from the last received Beacon or Probe Response frame. This is a
+ * mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ: Required (u32). Indicates
+ * primary 20 MHz channel center frequency in MHz of the BSS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH: Required (u8). Indicates
+ * channel width of the BSS. This uses values defined in
+ * enum nl80211_chan_width.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1: Required (u32). Indicates the
+ * center frequency (MHz) of the first segment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2: Optional (u32). Indicates the
+ * center frequency (MHz) of the second segment. Used only for
+ * %NL80211_CHAN_WIDTH_80P80 value in
+ * %QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH.
+ */
+enum qca_wlan_vendor_attr_fw_scan_bss {
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MS_AGO = 1,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_BSSID = 2,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_SSID = 3,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_RSSI = 4,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CAPABILITY = 5,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_FLAGS = 6,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_PRIMARY_FREQ = 7,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CHAN_WIDTH = 8,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ1 = 9,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_CENTER_FREQ2 = 10,
+
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_MAX =
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_BSS_AFTER_LAST - 1,
+};
+
+/* enum qca_wlan_vendor_attr_fw_scan_report: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_FW_SCAN_REPORT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP: 64-bit unsigned value to
+ * indicate the timestamp when this report is generated, timestamp in
+ * microseconds from system boot. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST: Nested attribute of u32
+ * attributes. This indicates the list of frequencies that were scanned. This is
+ * an optional attribute. If this is not specified, all frequencies allowed in
+ * the current regulatory domain were scanned.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST: Nested attribute.
+ * This indicates information of the scanned BSSs by the firmware. This is an
+ * optional attribute.
+ *
+ * The attributes defined in enum qca_wlan_vendor_attr_fw_scan_bss are nested
+ * in this attribute.
+ */
+enum qca_wlan_vendor_attr_fw_scan_report {
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_TIMESTAMP = 1,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_FREQ_LIST = 2,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_BSS_LIST = 3,
+
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_MAX =
+	QCA_WLAN_VENDOR_ATTR_FW_SCAN_REPORT_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_idle_shutdown_status: Represents idle shutdown status.
+ *
+ * @QCA_WLAN_IDLE_SHUTDOWN_STARTED: Indicates idle shutdown is started in the
+ * host driver.
+ * @QCA_WLAN_IDLE_SHUTDOWN_COMPLETED: Indicates idle shutdown is completed in
+ * the host driver.
+ */
+enum qca_wlan_idle_shutdown_status {
+	QCA_WLAN_IDLE_SHUTDOWN_STARTED = 0,
+	QCA_WLAN_IDLE_SHUTDOWN_COMPLETED = 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_idle_shutdown: Attributes used by vendor event
+ * %QCA_NL80211_VENDOR_SUBCMD_IDLE_SHUTDOWN.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS: Required u8 attribute. Indicates
+ * the status of the idle shutdown from one of the values in enum
+ * qca_wlan_idle_shutdown_status.
+ */
+enum qca_wlan_vendor_attr_idle_shutdown {
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_STATUS = 1,
+
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_MAX =
+	QCA_WLAN_VENDOR_ATTR_IDLE_SHUTDOWN_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index a65da61..ce282db 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -114,6 +114,7 @@
 	wpabuf_free(tmp->own_rejected_groups);
 	wpabuf_free(tmp->peer_rejected_groups);
 	os_free(tmp->pw_id);
+	os_free(tmp->parsed_pw_id);
 	bin_clear_free(tmp, sizeof(*tmp));
 	sae->tmp = NULL;
 }
@@ -121,12 +122,16 @@
 
 void sae_clear_data(struct sae_data *sae)
 {
+	unsigned int no_pw_id;
+
 	if (sae == NULL)
 		return;
 	sae_clear_temp_data(sae);
 	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
 	crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0);
+	no_pw_id = sae->no_pw_id;
 	os_memset(sae, 0, sizeof(*sae));
+	sae->no_pw_id = no_pw_id;
 }
 
 
@@ -1093,12 +1098,13 @@
 }
 
 
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+			      const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier)
 {
 	struct sae_pt *pt = NULL, *last = NULL, *tmp;
-	int default_groups[] = { 19, 0 };
+	const int default_groups[] = { 19, 0 };
 	int i;
 
 	if (!groups)
@@ -1877,8 +1883,6 @@
 				      const u8 *pos, const u8 *end,
 				      const u8 **token, size_t *token_len)
 {
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    pos, end - pos);
 	if (!sae_is_token_container_elem(pos, end))
 		return;
 	*token = pos + 3;
@@ -2045,14 +2049,12 @@
 }
 
 
-static int sae_parse_password_identifier(struct sae_data *sae,
+static int sae_parse_password_identifier(struct sae_data *sae, bool h2e,
 					 const u8 **pos, const u8 *end)
 {
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_password_id_elem(*pos, end)) {
 		if (sae->tmp->pw_id) {
 			wpa_printf(MSG_DEBUG,
@@ -2060,8 +2062,8 @@
 				   sae->tmp->pw_id);
 			return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 		}
-		os_free(sae->tmp->pw_id);
-		sae->tmp->pw_id = NULL;
+		os_free(sae->tmp->parsed_pw_id);
+		sae->tmp->parsed_pw_id = NULL;
 		return WLAN_STATUS_SUCCESS; /* No Password Identifier */
 	}
 
@@ -2073,6 +2075,18 @@
 	epos++; /* skip ext ID */
 	len--;
 
+	if (!h2e) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Password Identifier included, but H2E is not used");
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+	}
+
+	if (sae->no_pw_id) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Password Identifier included, but none has been enabled");
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+	}
+
 	if (sae->tmp->pw_id &&
 	    (len != os_strlen(sae->tmp->pw_id) ||
 	     os_memcmp(sae->tmp->pw_id, epos, len) != 0)) {
@@ -2082,14 +2096,14 @@
 		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	}
 
-	os_free(sae->tmp->pw_id);
-	sae->tmp->pw_id = os_malloc(len + 1);
-	if (!sae->tmp->pw_id)
+	os_free(sae->tmp->parsed_pw_id);
+	sae->tmp->parsed_pw_id = os_malloc(len + 1);
+	if (!sae->tmp->parsed_pw_id)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	os_memcpy(sae->tmp->pw_id, epos, len);
-	sae->tmp->pw_id[len] = '\0';
+	os_memcpy(sae->tmp->parsed_pw_id, epos, len);
+	sae->tmp->parsed_pw_id[len] = '\0';
 	wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
-			  sae->tmp->pw_id, len);
+			  sae->tmp->parsed_pw_id, len);
 	*pos = epos + len;
 	return WLAN_STATUS_SUCCESS;
 }
@@ -2101,8 +2115,6 @@
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_rejected_groups_elem(*pos, end)) {
 		wpabuf_free(sae->tmp->peer_rejected_groups);
 		sae->tmp->peer_rejected_groups = NULL;
@@ -2141,8 +2153,6 @@
 	const u8 *epos;
 	u8 len;
 
-	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
-		    *pos, end - *pos);
 	if (!sae_is_akm_suite_selector_elem(*pos, end))
 		return WLAN_STATUS_SUCCESS;
 
@@ -2195,8 +2205,13 @@
 	if (ie_offset)
 		*ie_offset = pos - data;
 
+	if (end > pos)
+		wpa_hexdump(MSG_DEBUG,
+			    "SAE: Possible elements at the end of the frame",
+			    pos, end - pos);
+
 	/* Optional Password Identifier element */
-	res = sae_parse_password_identifier(sae, &pos, end);
+	res = sae_parse_password_identifier(sae, h2e, &pos, end);
 	if (res != WLAN_STATUS_SUCCESS)
 		return res;
 
diff --git a/src/common/sae.h b/src/common/sae.h
index a353aa8..8f74353 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -59,6 +59,7 @@
 	struct crypto_bignum *order_buf;
 	struct wpabuf *anti_clogging_token;
 	char *pw_id;
+	char *parsed_pw_id;
 	int vlan_id;
 	u8 bssid[ETH_ALEN];
 	struct wpabuf *own_rejected_groups;
@@ -120,6 +121,7 @@
 	u16 rc; /* protocol instance variable: Rc (received send-confirm) */
 	unsigned int h2e:1;
 	unsigned int pk:1;
+	unsigned int no_pw_id:1;
 	struct sae_temporary_data *tmp;
 };
 
@@ -146,7 +148,8 @@
 const char * sae_state_txt(enum sae_state state);
 size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
 size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
-struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
+struct sae_pt * sae_derive_pt(const int *groups,
+			      const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier);
 struct crypto_ec_point *
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index a8c7c41..9c96269 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -582,6 +582,7 @@
 	ptk->kek2_len = 0;
 	ptk->kck2_len = 0;
 
+	ptk->ptk_len = ptk_len;
 	os_memset(tmp, 0, sizeof(tmp));
 	os_memset(data, 0, data_len);
 	return 0;
@@ -1560,6 +1561,7 @@
 				ptk->kdk, ptk->kdk_len);
 	}
 
+	ptk->ptk_len = ptk_len;
 	forced_memzero(tmp, sizeof(tmp));
 	ret = 0;
 err:
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index e608d3c..9f1a539 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -268,6 +268,7 @@
 	size_t kck2_len;
 	size_t kek2_len;
 	size_t kdk_len;
+	size_t ptk_len;
 	size_t ltf_keyseed_len;
 	int installed; /* 1 if key has already been installed to driver */
 };
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 2ea8ab3..40628e8 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -456,6 +456,11 @@
 /* Event triggered for received management frame */
 #define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED "
 
+/* Event triggerred on AP receiving Wi-Fi Alliance Generational Capabilities
+ * indication.
+ * Parameters: <STA addr> <Generational Capabilities Indication body> */
+#define WFA_GEN_CAPAB_RX "WFA-GEN-CAPAB "
+
 #ifndef BIT
 #define BIT(x) (1U << (x))
 #endif
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 765ea59..9ce5ec0 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -892,6 +892,14 @@
 	bool eht_enabled;
 
 	/**
+	 * punct_bitmap - Preamble puncturing bitmap
+	 * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+	 * channel with the lowest frequency. A bit set to 1 indicates that the
+	 * subchannel is punctured, otherwise active.
+	 */
+	u16 punct_bitmap;
+
+	/**
 	 * link_id: If >=0 indicates the link of the AP MLD to configure
 	 */
 	int link_id;
@@ -2492,6 +2500,10 @@
 	unsigned int mbssid_max_interfaces;
 	/* Maximum profile periodicity for enhanced MBSSID advertisement */
 	unsigned int ema_max_periodicity;
+
+	/* Maximum number of bytes of extra IE(s) that can be added to Probe
+	 * Request frames */
+	size_t max_probe_req_ie_len;
 };
 
 
@@ -2598,6 +2610,7 @@
 	bool mld_link_sta;
 	s8 mld_link_id;
 	const u8 *mld_link_addr;
+	u16 eml_cap;
 };
 
 struct mac_address {
@@ -2772,7 +2785,6 @@
  * @beacon_after: Next beacon/probe resp/asooc resp info
  * @counter_offset_beacon: Offset to the count field in beacon's tail
  * @counter_offset_presp: Offset to the count field in probe resp.
- * @punct_bitmap - Preamble puncturing bitmap
  * @link_id: Link ID to determine the link for MLD; -1 for non-MLD
  * @ubpr: Unsolicited broadcast Probe Response frame data
  */
@@ -2787,7 +2799,6 @@
 	u16 counter_offset_beacon[2];
 	u16 counter_offset_presp[2];
 
-	u16 punct_bitmap;
 	int link_id;
 
 	struct unsol_bcast_probe_resp ubpr;
@@ -3503,12 +3514,15 @@
 	 * e.g., wpa_supplicant_event()
 	 * @ifname: interface name, e.g., wlan0
 	 * @global_priv: private driver global data from global_init()
+	 * @p2p_mode: P2P mode for a GO (not applicable for other interface
+	 *	types)
 	 * Returns: Pointer to private data, %NULL on failure
 	 *
 	 * This function can be used instead of init() if the driver wrapper
 	 * uses global data.
 	 */
-	void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+	void * (*init2)(void *ctx, const char *ifname, void *global_priv,
+			enum wpa_p2p_mode p2p_mode);
 
 	/**
 	 * get_interfaces - Get information about available interfaces
@@ -4044,7 +4058,10 @@
 	 * @bssid: BSSID (Address 3)
 	 * @data: Frame body
 	 * @data_len: data length in octets
-	 @ @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * @link_id: Link ID of the specified link; -1 for non-MLO cases and for
+	 *	frames that target the MLD instead of a specific link in MLO
+	 *	cases
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This command can be used to request the driver to transmit an action
@@ -4065,7 +4082,8 @@
 	 */
 	int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
 			   const u8 *dst, const u8 *src, const u8 *bssid,
-			   const u8 *data, size_t data_len, int no_cck);
+			   const u8 *data, size_t data_len, int no_cck,
+			   int link_id);
 
 	/**
 	 * send_action_cancel_wait - Cancel action frame TX wait
@@ -5333,6 +5351,25 @@
 	 */
 	int (*nan_cancel_subscribe)(void *priv, int subscribe_id);
 
+	/**
+	 * can_share_drv - Check whether driver interface can be shared
+	 * @ctx: Pointer to hostapd context
+	 * @params: Configuration for the driver wrapper
+	 * @hapd: Pointer for overwriting the hapd context or %NULL
+	 * 	if can't find a shared drv
+	 *
+	 * Checks whether the driver interface with same phy name is
+	 * already present under the global driver which can be shared
+	 * instead of creating a new driver interface instance. If present,
+	 * @hapd will be overwritten with the hapd pointer which this shared
+	 * drv's first BSS is using. This will help the caller to later call
+	 * if_add().
+	 *
+	 * Returns: true if it can be shared or else false.
+	 */
+	bool (*can_share_drv)(void *ctx, struct wpa_init_params *params,
+			      void **hapd);
+
 #ifdef CONFIG_TESTING_OPTIONS
 	int (*register_frame)(void *priv, u16 type,
 			      const u8 *match, size_t match_len,
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 82d8a01..0979fc5 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -998,7 +998,8 @@
 }
 
 static void *
-bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params,
+	 enum wpa_p2p_mode p2p_mode)
 {
 	struct bsd_driver_data *drv;
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5890ac6..95e678f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -167,7 +167,8 @@
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
 				   const u8 *set_addr, int first,
-				   const char *driver_params);
+				   const char *driver_params,
+				   enum wpa_p2p_mode p2p_mode);
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len,
@@ -351,6 +352,7 @@
 	int err;
 	struct nl_msg *orig_msg;
 	struct nl80211_err_info *err_info;
+	struct wpa_driver_nl80211_data *drv;
 };
 
 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
@@ -387,8 +389,12 @@
 	if (tb[NLMSGERR_ATTR_MSG]) {
 		len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
 			      nla_len(tb[NLMSGERR_ATTR_MSG]));
-		wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
-			   len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+		if (err_args->drv)
+			wpa_msg(err_args->drv->ctx, MSG_ERROR, "nl80211: kernel reports: %*s",
+				len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+		else
+			wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+				   len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
 	}
 
 	if (!err_args->err_info)
@@ -493,13 +499,14 @@
 }
 
 
-int send_and_recv(struct nl80211_global *global,
-		  struct nl_sock *nl_handle, struct nl_msg *msg,
-		  int (*valid_handler)(struct nl_msg *, void *),
-		  void *valid_data,
-		  int (*ack_handler_custom)(struct nl_msg *, void *),
-		  void *ack_data,
-		  struct nl80211_err_info *err_info)
+int send_and_recv_glb(struct nl80211_global *global,
+		      struct wpa_driver_nl80211_data *drv, /* may be NULL */
+		      struct nl_sock *nl_handle, struct nl_msg *msg,
+		      int (*valid_handler)(struct nl_msg *, void *),
+		      void *valid_data,
+		      int (*ack_handler_custom)(struct nl_msg *, void *),
+		      void *ack_data,
+		      struct nl80211_err_info *err_info)
 {
 	struct nl_cb *cb, *s_nl_cb;
 	struct nl80211_ack_err_args err;
@@ -541,6 +548,7 @@
 	err.err = 1;
 	err.orig_msg = msg;
 	err.err_info = err_info;
+	err.drv = drv;
 
 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err.err);
@@ -656,8 +664,8 @@
 		return -1;
 	}
 
-	ret = send_and_recv(global, global->nl, msg, family_handler, &res,
-			    NULL, NULL, NULL);
+	ret = send_and_recv_glb(global, NULL, global->nl, msg, family_handler,
+				&res, NULL, NULL, NULL);
 	if (ret == 0)
 		ret = res.id;
 	return ret;
@@ -852,7 +860,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+	ret = send_and_recv(drv, w->nl_beacons, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
@@ -1109,7 +1117,7 @@
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
 		struct nl_msg *msg;
 
-		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+		msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
 		if (send_and_recv_resp(drv, msg, get_mlo_info,
 				       &drv->sta_mlo_info))
 			return -1;
@@ -1220,7 +1228,8 @@
 		nl80211_check_global(drv->global);
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL,
+						       WPA_P2P_MODE_WFD_R1) < 0)
 			return -1;
 		return 1;
 	}
@@ -1841,7 +1850,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_INTERFACE);
 	return send_and_recv_resp(drv, msg, get_channel_info, ci);
 }
 
@@ -2290,7 +2299,8 @@
 static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
 					  void *global_priv, int hostapd,
 					  const u8 *set_addr,
-					  const char *driver_params)
+					  const char *driver_params,
+					  enum wpa_p2p_mode p2p_mode)
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
@@ -2353,7 +2363,8 @@
 	if (nl80211_init_bss(bss))
 		goto failed;
 
-	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params,
+					       p2p_mode))
 		goto failed;
 
 	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
@@ -2416,10 +2427,11 @@
  * Returns: Pointer to private data, %NULL on failure
  */
 static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
-				      void *global_priv)
+				      void *global_priv,
+				      enum wpa_p2p_mode p2p_mode)
 {
 	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
-					   NULL);
+					   NULL, p2p_mode);
 }
 
 
@@ -2447,7 +2459,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+	ret = send_and_recv(drv, nl_handle, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
@@ -2727,7 +2739,7 @@
 	int ret;
 
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
-	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+	ret = send_and_recv(bss->drv, bss->nl_mgmt, msg, NULL, NULL,
 			    NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
@@ -2774,6 +2786,9 @@
 	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
 		ret = -1;
 #endif /* CONFIG_FST */
+	/* Vendor-specific Protected */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x7e", 1) < 0)
+		ret = -1;
 	/* Vendor-specific */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
 		ret = -1;
@@ -2976,10 +2991,67 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_set_p2p_mode(void *priv, enum wpa_p2p_mode mode)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret;
+	enum qca_wlan_vendor_p2p_mode drv_mode;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set P2P mode: %d", mode);
+
+	switch (mode) {
+	case WPA_P2P_MODE_WFD_R1:
+		drv_mode = QCA_P2P_MODE_WFD_R1;
+		break;
+	case WPA_P2P_MODE_WFD_R2:
+		drv_mode = QCA_P2P_MODE_WFD_R2;
+		break;
+	case WPA_P2P_MODE_WFD_PCC:
+		drv_mode = QCA_P2P_MODE_WFD_PCC;
+		break;
+	default:
+		return -1;
+	}
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_P2P_MODE))
+		goto fail;
+
+	container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!container)
+		goto fail;
+
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SET_P2P_MODE_CONFIG, drv_mode))
+		goto fail;
+
+	nla_nest_end(msg, container);
+
+	ret = send_and_recv_cmd(drv, msg);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set P2P Mode: ret=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
 				   const u8 *set_addr, int first,
-				   const char *driver_params)
+				   const char *driver_params,
+				   enum wpa_p2p_mode p2p_mode)
 {
 	struct i802_bss *bss = drv->first_bss;
 	int send_rfkill_event = 0;
@@ -3038,6 +3110,11 @@
 
 	wpa_driver_nl80211_drv_init_rfkill(drv);
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (nlmode == NL80211_IFTYPE_P2P_GO)
+		nl80211_set_p2p_mode(bss, p2p_mode);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 	if (!rfkill_is_blocked(drv->rfkill)) {
 		int ret = i802_set_iface_flags(bss, 1);
 		if (ret) {
@@ -3860,7 +3937,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, bss->nl_connect, msg,
+	ret = send_and_recv(drv, bss->nl_connect, msg,
 			    NULL, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -4062,7 +4139,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
 		   drv->ifindex);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_AUTHENTICATE);
 	if (!msg)
 		goto fail;
 
@@ -4395,7 +4472,8 @@
 			encrypt = 0;
 	}
 
-	if (is_sta_interface(drv->nlmode) &&
+	if ((is_sta_interface(drv->nlmode) ||
+	     drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
 	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
 		if (freq == 0 &&
@@ -4567,7 +4645,7 @@
 		nla_total_size(acl_nla_sz);
 	nlmsg_sz = nlmsg_total_size(nla_sz);
 	if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
-					      drv->ifindex, 0,
+					      bss->ifindex, 0,
 					      NL80211_CMD_SET_MAC_ACL)) ||
 	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
 			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
@@ -4621,7 +4699,7 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MESH_CONFIG);
 	if (!msg)
 		return -1;
 
@@ -5506,7 +5584,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -5577,7 +5655,7 @@
 		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
 		   freq->center_freq1, freq->center_freq2);
 
-	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
 			      NL80211_CMD_SET_WIPHY);
 	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
 		nlmsg_free(msg);
@@ -5763,6 +5841,15 @@
 				goto fail;
 		}
 
+		/* Set EML capabilities of ML STA */
+		if (params->mld_link_addr && params->eml_cap) {
+			wpa_printf(MSG_DEBUG, "  * eml_cap =%u",
+				   params->eml_cap);
+			if (nla_put_u16(msg, NL80211_ATTR_EML_CAPABILITY,
+					params->eml_cap))
+				goto fail;
+		}
+
 		if (is_ap_interface(drv->nlmode) &&
 		    nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
 			       params->support_p2p_ps ?
@@ -6353,7 +6440,7 @@
 
 	os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
 	ext_arg.ext_data = &cookie;
-	ret = send_and_recv(bss->drv->global, bss->drv->global->nl, msg,
+	ret = send_and_recv(bss->drv, bss->drv->global->nl, msg,
 			    NULL, NULL, ack_handler_cookie, &ext_arg, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -6594,7 +6681,7 @@
 	int ret;
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
-	ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+	ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL,
 			    NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
@@ -6744,7 +6831,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, drv->first_bss->nl_connect, msg, NULL,
+	ret = send_and_recv(drv, drv->first_bss->nl_connect, msg, NULL,
 			    NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -7298,7 +7385,7 @@
 
 	nl80211_connect_ext(drv, params);
 	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONNECT);
 	if (!msg)
 		return -1;
 
@@ -7375,7 +7462,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		goto fail;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -7460,7 +7547,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
 		   drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ASSOCIATE);
 	if (!msg)
 		return -1;
 
@@ -7491,7 +7578,7 @@
 	if (!TEST_FAIL_TAG("assoc")) {
 		if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 			goto fail;
-		ret = send_and_recv(drv->global, drv->first_bss->nl_connect,
+		ret = send_and_recv(drv, drv->first_bss->nl_connect,
 				    msg, NULL, NULL, NULL, NULL, &err_info);
 		msg = NULL;
 	} else {
@@ -7906,7 +7993,7 @@
 	else
 		val = rts;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
 	    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -7934,7 +8021,7 @@
 	else
 		val = frag;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY)) ||
 	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -7967,6 +8054,8 @@
 	 * XXX: FIX! this needs to flush all VLANs too
 	 */
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+	if (!msg)
+		goto fail;
 	if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
 	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
 		goto fail;
@@ -8796,7 +8885,8 @@
 
 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
 					  params->global_priv, 1,
-					  params->bssid, params->driver_params);
+					  params->bssid, params->driver_params,
+					  WPA_P2P_MODE_WFD_R1);
 	if (bss == NULL)
 		return NULL;
 
@@ -9330,7 +9420,7 @@
 					  const u8 *dst, const u8 *src,
 					  const u8 *bssid,
 					  const u8 *data, size_t data_len,
-					  int no_cck)
+					  int no_cck, int link_id)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ret = -1;
@@ -9347,9 +9437,9 @@
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: Send Action frame (ifindex=%d, freq=%u MHz wait=%d ms no_cck=%d offchanok=%d dst="
-		   MACSTR " src=" MACSTR " bssid=" MACSTR ")",
+		   MACSTR " src=" MACSTR " bssid=" MACSTR ", link_id=%d)",
 		   drv->ifindex, freq, wait_time, no_cck, offchanok,
-		   MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
+		   MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), link_id);
 
 	buf = os_zalloc(24 + data_len);
 	if (buf == NULL)
@@ -9398,12 +9488,12 @@
 	     !drv->use_monitor))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, offchanok,
-						   wait_time, NULL, 0, 0, -1);
+						   wait_time, NULL, 0, 0,
+						   link_id);
 	else
 		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
 					     24 + data_len, 1, no_cck, 0,
-					     offchanok, NULL, 0,
-					     NL80211_DRV_LINK_ID_NA);
+					     offchanok, NULL, 0, link_id);
 
 	os_free(buf);
 	return ret;
@@ -10486,7 +10576,7 @@
 
 	dl_list_init(&survey_results->survey_list);
 
-	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	msg = nl80211_bss_msg(bss, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
 	if (!msg)
 		return -ENOBUFS;
 
@@ -10683,15 +10773,29 @@
 		return -1;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_RADAR_DETECT)) ||
 	    nl80211_put_freq_params(msg, freq) < 0) {
 		nlmsg_free(msg);
 		return -1;
 	}
 
+	if (nl80211_link_valid(bss->valid_links, freq->link_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Radar detection (CAC) on link_id=%d",
+			   freq->link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret == 0)
+	if (ret == 0) {
+		nl80211_link_set_freq(bss, freq->link_id, freq->freq);
 		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
 		   "%d (%s)", ret, strerror(-ret));
 	return -1;
@@ -10779,7 +10883,7 @@
 	if (link_id < 0 && drv->sta_mlo_info.valid_links)
 		link_id = drv->sta_mlo_info.assoc_link_id;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_MGMT)) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
@@ -10834,7 +10938,7 @@
 		return -EINVAL;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_TDLS_OPER)) ||
 	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
 		nlmsg_free(msg);
@@ -11099,11 +11203,12 @@
 				      const u8 *dst, const u8 *src,
 				      const u8 *bssid,
 				      const u8 *data, size_t data_len,
-				      int no_cck)
+				      int no_cck, int link_id)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
-					      bssid, data, data_len, no_cck);
+					      bssid, data, data_len, no_cck,
+					      link_id);
 }
 
 
@@ -11124,7 +11229,7 @@
 	u16 mdid = WPA_GET_LE16(md);
 
 	wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_FT_IES)) ||
 	    nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
 	    nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
 		nlmsg_free(msg);
@@ -11373,7 +11478,8 @@
 				  "capa.max_sched_scan_plan_interval=%u\n"
 				  "capa.max_sched_scan_plan_iterations=%u\n"
 				  "capa.mbssid_max_interfaces=%u\n"
-				  "capa.ema_max_periodicity=%u\n",
+				  "capa.ema_max_periodicity=%u\n"
+				  "capa.max_probe_req_ie_len=%zu\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
@@ -11398,7 +11504,8 @@
 				  drv->capa.max_sched_scan_plan_interval,
 				  drv->capa.max_sched_scan_plan_iterations,
 				  drv->capa.mbssid_max_interfaces,
-				  drv->capa.ema_max_periodicity);
+				  drv->capa.ema_max_periodicity,
+				  drv->capa.max_probe_req_ie_len);
 		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
@@ -11470,7 +11577,7 @@
 		   settings->freq_params.bandwidth,
 		   settings->freq_params.center_freq1,
 		   settings->freq_params.center_freq2,
-		   settings->punct_bitmap,
+		   settings->freq_params.punct_bitmap,
 		   settings->link_id,
 		   settings->freq_params.ht_enabled ? " ht" : "",
 		   settings->freq_params.vht_enabled ? " vht" : "",
@@ -11543,9 +11650,9 @@
 	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
 	    (settings->block_tx &&
 	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
-	    (settings->punct_bitmap &&
+	    (settings->freq_params.punct_bitmap &&
 	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
-			 settings->punct_bitmap)) ||
+			 settings->freq_params.punct_bitmap)) ||
 	    (settings->link_id != NL80211_DRV_LINK_ID_NA &&
 	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
 		goto error;
@@ -11855,7 +11962,7 @@
 		/* This test vendor_cmd can be used with nl80211 commands that
 		 * need the connect nl_sock, so use the variant that takes in
 		 * bss->nl_connect as the handle. */
-		ret = send_and_recv(drv->global, bss->nl_connect, msg,
+		ret = send_and_recv(drv, bss->nl_connect, msg,
 				    cmd_reply_handler, buf, NULL, NULL, NULL);
 		if (ret)
 			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
@@ -11942,7 +12049,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_WOWLAN);
 
 	ret = send_and_recv_resp(drv, msg, get_wowlan_handler, &wowlan_enabled);
 	if (ret) {
@@ -12015,7 +12122,7 @@
 		return -1;
 	}
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
@@ -12046,7 +12153,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
@@ -12081,7 +12188,7 @@
 	if (!drv->roam_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
@@ -12135,7 +12242,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
@@ -12319,7 +12426,7 @@
 	int ret = -1;
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_JOIN_MESH);
 	if (!msg ||
 	    nl80211_put_freq_params(msg, &params->freq) ||
 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
@@ -12365,7 +12472,7 @@
 
 	if (nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER))
 		return -1;
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -12422,8 +12529,8 @@
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
-	ret = send_and_recv(drv->global, bss->nl_connect, msg, NULL, NULL, NULL,
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_LEAVE_MESH);
+	ret = send_and_recv(drv, bss->nl_connect, msg, NULL, NULL, NULL,
 			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
@@ -12452,7 +12559,7 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_MESH_LINK);
 	if (!msg ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
 	    nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
@@ -12889,7 +12996,7 @@
 		   "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
 		   qca_band_mask, qca_band_value);
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
@@ -13086,7 +13193,7 @@
 	param.num = *num;
 	param.freq_list = freq_list;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13140,7 +13247,7 @@
 		   "nl80211: Set P2P probable operating freq %u for ifindex %d",
 		   freq, bss->ifindex);
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
@@ -13190,7 +13297,7 @@
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
@@ -13242,7 +13349,7 @@
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
@@ -13273,7 +13380,7 @@
 	else
 		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
@@ -13410,7 +13517,7 @@
 
 
 static struct wpa_bss_candidate_info *
-nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+qca_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -13443,7 +13550,7 @@
 	 */
 	info->num = params->n_candidates;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
@@ -13517,7 +13624,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
@@ -13565,7 +13672,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG,
 		"nl80211: PASN authentication response for %d entries",
 		params->num_peers);
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13648,7 +13755,7 @@
 		"nl80211: Secure ranging context for " MACSTR,
 		MAC2STR(params->peer_addr));
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13727,7 +13834,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD flush");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13743,12 +13850,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD flush");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13774,7 +13879,7 @@
 		   params->freq, params->ttl);
 	wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13807,16 +13912,15 @@
 			params->freq) ||
 	    add_freq_list(msg, QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST,
 			  params->freq_list))
+		goto fail;
 	nla_nest_end(msg, attr);
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13834,7 +13938,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel publish");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13854,12 +13958,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD cancel publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13879,7 +13981,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD update publish: id=%d",
 		   publish_id);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13900,12 +14002,10 @@
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD update publish");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13931,7 +14031,7 @@
 		   params->freq, params->ttl);
 	wpa_hexdump_buf(MSG_MSGDUMP, "nl80211: USD elements", elems);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -13969,12 +14069,10 @@
 
 	nla_nest_end(msg, container);
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD subscribe");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -13992,7 +14090,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: NAN USD cancel subscribe");
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR);
 	if (!msg ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
@@ -14010,12 +14108,10 @@
 	nla_nest_end(msg, container);
 
 	ret = send_and_recv_cmd(drv, msg);
-	if (ret) {
+	if (ret)
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Failed to send NAN USD cancel subscribe");
-		goto fail;
-	}
-	return 0;
+	return ret;
 
 fail:
 	nlmsg_free(msg);
@@ -14103,6 +14199,60 @@
 }
 
 
+#ifdef CONFIG_MBO
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+	/* This only exists for testing purposes, disable unless requested */
+	if (TEST_FAIL_TAG("simulate")) {
+		struct wpa_bss_candidate_info *info;
+		int i;
+
+		info = os_zalloc(sizeof(*info));
+		if (!info)
+			return NULL;
+
+		info->candidates = os_calloc(params->n_candidates,
+					     sizeof(*info->candidates));
+		if (!info->candidates) {
+			os_free(info);
+			return NULL;
+		}
+
+		info->num = params->n_candidates;
+		for (i = 0; i < params->n_candidates; i++) {
+			char bssid_str[ETH_ALEN * 3];
+
+			os_memcpy(info->candidates[i].bssid,
+				  &params->bssid[i * ETH_ALEN], ETH_ALEN);
+
+			os_snprintf(bssid_str, sizeof(bssid_str), MACSTR,
+				    MAC2STR(info->candidates[i].bssid));
+
+			if (TEST_FAIL_TAG(bssid_str)) {
+				info->candidates[i].is_accept = 0;
+				info->candidates[i].reject_reason =
+					MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+			} else {
+				info->candidates[i].is_accept = 1;
+				info->candidates[i].reject_reason = 0;
+			}
+		}
+
+		return info;
+	}
+#endif /* defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) */
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	return qca_get_bss_transition_status(priv, params);
+#else /* CONFIG_DRIVER_NL80211_QCA */
+	return NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+#endif /* CONFIG_MBO */
+
+
 static int nl80211_write_to_file(const char *name, unsigned int val)
 {
 	int fd, len;
@@ -14308,7 +14458,7 @@
 	}
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
 	if (!msg)
 		goto fail;
 
@@ -14368,7 +14518,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG,
 		"nl80211: External auth status: %u", params->status);
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_EXTERNAL_AUTH);
 	if (!msg ||
 	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
 	    (params->ssid && params->ssid_len &&
@@ -14565,6 +14715,7 @@
 
 
 #ifdef CONFIG_IEEE80211BE
+
 static int wpa_driver_nl80211_link_sta_remove(void *priv, u8 link_id,
 					      const u8 *addr)
 {
@@ -14591,6 +14742,124 @@
 
 	return ret;
 }
+
+
+static int wpa_driver_get_wiphy_name_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_driver_nl80211_data *drv = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_WIPHY_NAME])
+		return NL_SKIP;
+
+	os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+		   sizeof(drv->phyname));
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_get_phyname(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	u32 feat, nl_flags;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		nl_flags = NLM_F_DUMP;
+
+	if (!(msg = nl80211_cmd_msg(drv->first_bss, nl_flags,
+				    NL80211_CMD_GET_WIPHY)) ||
+	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (send_and_recv_resp(drv, msg, wpa_driver_get_wiphy_name_handler,
+			       drv))
+		return -1;
+
+	return 0;
+}
+
+
+static bool
+wpa_driver_nl80211_name_match(struct wpa_driver_nl80211_data *drv,
+			      struct wpa_driver_nl80211_data **match_drv)
+{
+	struct wpa_driver_nl80211_data *drv2;
+
+	dl_list_for_each(drv2, &drv->global->interfaces,
+			 struct wpa_driver_nl80211_data, list) {
+		if (os_strcmp(drv2->phyname, drv->phyname) == 0) {
+			if (match_drv)
+				*match_drv = drv2;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
+static bool wpa_driver_nl80211_can_share_drv(void *ctx,
+					     struct wpa_init_params *params,
+					     void **hapd)
+{
+	struct wpa_driver_nl80211_data *drv, *match_drv = NULL;
+	struct i802_bss *bss;
+	bool ret = false;
+
+	if (!params->global_priv)
+		return false;
+
+	/* Create a temporary drv to read the phyname */
+	drv = os_zalloc(sizeof(*drv));
+	if (!drv)
+		return false;
+	drv->global = params->global_priv;
+	drv->ctx = ctx;
+
+	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+	if (!drv->first_bss) {
+		os_free(drv);
+		return false;
+	}
+
+	bss = drv->first_bss;
+	bss->drv = drv;
+	bss->ctx = ctx;
+
+	os_strlcpy(bss->ifname, params->ifname, sizeof(bss->ifname));
+
+	if (nl80211_init_bss(bss))
+		goto free_all;
+
+	drv->ifindex = if_nametoindex(bss->ifname);
+	bss->ifindex = drv->ifindex;
+
+	if (wpa_driver_get_phyname(drv) ||
+	    !wpa_driver_nl80211_name_match(drv, &match_drv) ||
+	    !match_drv)
+		goto free_all;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Driver for phy %s already exist",
+		   match_drv->phyname);
+
+	*hapd = match_drv->first_bss->ctx;
+	ret = true;
+
+free_all:
+	nl80211_destroy_bss(bss);
+	os_free(bss);
+	os_free(drv);
+	return ret;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -14775,7 +15044,6 @@
 	.set_default_scan_ies = nl80211_set_default_scan_ies,
 	.set_tdls_mode = nl80211_set_tdls_mode,
 #ifdef CONFIG_MBO
-	.get_bss_transition_status = nl80211_get_bss_transition_status,
 	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
 #endif /* CONFIG_MBO */
 	.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
@@ -14794,6 +15062,9 @@
 #endif /* CONFIG_NAN_USD */
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.do_acs = nl80211_do_acs,
+#ifdef CONFIG_MBO
+	.get_bss_transition_status = nl80211_get_bss_transition_status,
+#endif /* CONFIG_MBO */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
 	.get_ext_capab = nl80211_get_ext_capab,
 	.get_mld_capab = nl80211_get_mld_capab,
@@ -14809,6 +15080,7 @@
 	.link_remove = driver_nl80211_link_remove,
 	.is_drv_shared = nl80211_is_drv_shared,
 	.link_sta_remove = wpa_driver_nl80211_link_sta_remove,
+	.can_share_drv = wpa_driver_nl80211_can_share_drv,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index bf1bf4e..da74030 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -284,13 +284,28 @@
 				uint8_t cmd);
 struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
 
-int send_and_recv(struct nl80211_global *global,
-		  struct nl_sock *nl_handle, struct nl_msg *msg,
-		  int (*valid_handler)(struct nl_msg *, void *),
-		  void *valid_data,
-		  int (*ack_handler_custom)(struct nl_msg *, void *),
-		  void *ack_data,
-		  struct nl80211_err_info *err_info);
+int send_and_recv_glb(struct nl80211_global *global,
+		      struct wpa_driver_nl80211_data *drv, /* may be NULL */
+		      struct nl_sock *nl_handle, struct nl_msg *msg,
+		      int (*valid_handler)(struct nl_msg *, void *),
+		      void *valid_data,
+		      int (*ack_handler_custom)(struct nl_msg *, void *),
+		      void *ack_data,
+		      struct nl80211_err_info *err_info);
+
+static inline int
+send_and_recv(struct wpa_driver_nl80211_data *drv,
+	      struct nl_sock *nl_handle, struct nl_msg *msg,
+	      int (*valid_handler)(struct nl_msg *, void *),
+	      void *valid_data,
+	      int (*ack_handler_custom)(struct nl_msg *, void *),
+	      void *ack_data,
+	      struct nl80211_err_info *err_info)
+{
+	return send_and_recv_glb(drv->global, drv, nl_handle, msg,
+				 valid_handler, valid_data,
+				 ack_handler_custom, ack_data, err_info);
+}
 
 // This function is not used in supplicant anymore. But keeping this wrapper
 // functions for libraries outside wpa_supplicant to build (For eg: lib_driver_cmd_XX)
@@ -302,7 +317,7 @@
 		   int (*ack_handler_custom)(struct nl_msg *, void *),
 		   void *ack_data)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     valid_handler, valid_data,
 			     ack_handler_custom, ack_data, NULL);
 }
@@ -311,7 +326,7 @@
 send_and_recv_cmd(struct wpa_driver_nl80211_data *drv,
 		  struct nl_msg *msg)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     NULL, NULL, NULL, NULL, NULL);
 }
 
@@ -321,7 +336,7 @@
 		   int (*valid_handler)(struct nl_msg *, void *),
 		   void *valid_data)
 {
-	return send_and_recv(drv->global, drv->global->nl, msg,
+	return send_and_recv(drv, drv->global->nl, msg,
 			     valid_handler, valid_data, NULL, NULL, NULL);
 }
 
@@ -432,5 +447,6 @@
 int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
 struct hostapd_multi_hw_info *
 nl80211_get_multi_hw_info(struct i802_bss *bss, unsigned int *num_multi_hws);
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv);
 
 #endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 58fb71d..1aaeae9 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -35,7 +35,7 @@
 }
 
 
-static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
 {
 	u32 feat = 0;
 	struct nl_msg *msg;
@@ -958,6 +958,10 @@
 		capa->max_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
 
+	if (tb[NL80211_ATTR_MAX_SCAN_IE_LEN])
+		capa->max_probe_req_ie_len =
+			nla_get_u16(tb[NL80211_ATTR_MAX_SCAN_IE_LEN]);
+
 	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
 		capa->max_sched_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
@@ -1204,6 +1208,10 @@
 	info->capa = &drv->capa;
 	info->drv = drv;
 
+	/* Default to large buffer of extra IE(s) to maintain previous behavior
+	 * if the driver does not support reporting an accurate limit. */
+	info->capa->max_probe_req_ie_len = 1500;
+
 	feat = get_nl80211_protocol_features(drv);
 	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
 		flags = NLM_F_DUMP;
@@ -1735,6 +1743,8 @@
 		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
 	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
 		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_320MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_320;
 
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
 		enum nl80211_dfs_state state =
@@ -1836,6 +1846,8 @@
 		[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
 		[NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_320MHZ] = { .type = NLA_FLAG },
+
 	};
 	int new_channels = 0;
 	struct hostapd_channel_data *channel;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index d301701..f297e40 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -2510,29 +2510,62 @@
 }
 
 
-static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
-				struct nlattr **tb)
+static void nl80211_process_radar_event(struct i802_bss *bss,
+					union wpa_event_data *data,
+					enum nl80211_radar_event event_type)
 {
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
+		   data->dfs_event.freq, data->dfs_event.ht_enabled,
+		   data->dfs_event.chan_offset, data->dfs_event.chan_width,
+		   data->dfs_event.cf1, data->dfs_event.cf2,
+		   data->dfs_event.link_id);
+
+	switch (event_type) {
+	case NL80211_RADAR_DETECTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_RADAR_DETECTED, data);
+		break;
+	case NL80211_RADAR_CAC_FINISHED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_FINISHED, data);
+		break;
+	case NL80211_RADAR_CAC_ABORTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_ABORTED, data);
+		break;
+	case NL80211_RADAR_NOP_FINISHED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_NOP_FINISHED, data);
+		break;
+	case NL80211_RADAR_PRE_CAC_EXPIRED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+				     data);
+		break;
+	case NL80211_RADAR_CAC_STARTED:
+		wpa_supplicant_event(bss->ctx, EVENT_DFS_CAC_STARTED, data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown radar event %d received",
+			   event_type);
+		break;
+	}
+}
+
+
+static void nl80211_radar_event(struct i802_bss *bss, struct nlattr **tb)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	union wpa_event_data data;
 	enum nl80211_radar_event event_type;
+	struct i802_bss *bss_iter;
+	int i;
+	bool hit = false;
 
 	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
 		return;
 
 	os_memset(&data, 0, sizeof(data));
-	data.dfs_event.link_id = NL80211_DRV_LINK_ID_NA;
 	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
 	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
 
-	if (tb[NL80211_ATTR_MLO_LINK_ID]) {
-		data.dfs_event.link_id =
-			nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-	} else if (data.dfs_event.freq) {
-		data.dfs_event.link_id =
-			nl80211_get_link_id_by_freq(drv->first_bss,
-						    data.dfs_event.freq);
-	}
-
 	/* Check HT params */
 	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
 		data.dfs_event.ht_enabled = 1;
@@ -2564,37 +2597,62 @@
 		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz, link_id=%d",
-		   data.dfs_event.freq, data.dfs_event.ht_enabled,
-		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
-		   data.dfs_event.cf1, data.dfs_event.cf2,
-		   data.dfs_event.link_id);
+		   "nl80211: Checking suitable BSS for the DFS event");
 
-	switch (event_type) {
-	case NL80211_RADAR_DETECTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
-		break;
-	case NL80211_RADAR_CAC_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
-		break;
-	case NL80211_RADAR_CAC_ABORTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
-		break;
-	case NL80211_RADAR_NOP_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
-		break;
-	case NL80211_RADAR_PRE_CAC_EXPIRED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
-				     &data);
-		break;
-	case NL80211_RADAR_CAC_STARTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
-			   "received", event_type);
-		break;
+	/* It is possible to have the event without ifidx and wdev_id, e.g.,
+	 * with NL80211_RADAR_NOP_FINISHED and NL80211_RADAR_PRE_CAC_EXPIRED.
+	 * Hence need to check on all BSSs. */
+	for (bss_iter = drv->first_bss; bss_iter; bss_iter = bss_iter->next) {
+		/* Find a link match based on the frequency. If
+		 * NL80211_DRV_LINK_ID_NA is returned, either a match was not
+		 * found or the BSS could be operating as a non-MLO. */
+		data.dfs_event.link_id = nl80211_get_link_id_by_freq(
+			bss_iter, data.dfs_event.freq);
+		/* If a link match is found, exit the loop after the handler is
+		 * called */
+		if (data.dfs_event.link_id != NL80211_DRV_LINK_ID_NA)
+			return nl80211_process_radar_event(bss_iter, &data,
+							   event_type);
+		if (data.dfs_event.link_id == NL80211_DRV_LINK_ID_NA) {
+			/* For non-MLO operation, frequency should still match
+			 */
+			if (!bss_iter->valid_links &&
+			    bss_iter->links[0].freq == data.dfs_event.freq)
+				return nl80211_process_radar_event(
+					bss_iter, &data, event_type);
+		}
+
+		/* For event like NL80211_RADAR_NOP_FINISHED, frequency
+		 * information will not match exactly the link frequency. Hence,
+		 * if the link frequency is 5 GHz, pass the event to it.
+		 */
+		for_each_link_default(bss_iter->valid_links, i, 0) {
+			if (bss_iter->links[i].freq < 5180 ||
+			    bss_iter->links[i].freq > 5900)
+				continue;
+
+			data.dfs_event.link_id = bss_iter->valid_links ?
+				i : NL80211_DRV_LINK_ID_NA;
+
+			/* Cannot just return after one match since if split
+			 * hardware is participating in MLO, possibly the event
+			 * is for 5 GHz upper band and this iteration has picked
+			 * a 5 GHz low band link, but 5 GHz freq check will be
+			 * true for both. Hence, iterate on all possible links.
+			 * The handler should take care whether the event is
+			 * actually for it. */
+			nl80211_process_radar_event(bss_iter, &data,
+						    event_type);
+
+			hit = true;
+		}
 	}
+
+	if (hit)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on unknown freq on %s",
+		   bss->ifname);
 }
 
 
@@ -4119,7 +4177,7 @@
 		mlme_event_ft_event(drv, tb);
 		break;
 	case NL80211_CMD_RADAR_DETECT:
-		nl80211_radar_event(drv, tb);
+		nl80211_radar_event(bss, tb);
 		break;
 	case NL80211_CMD_STOP_AP:
 		nl80211_stop_ap(bss, tb);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index d0ed7ad..f0313c1 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -238,6 +238,11 @@
 	if (params->extra_ies) {
 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
 			    params->extra_ies, params->extra_ies_len);
+		if (params->extra_ies_len > drv->capa.max_probe_req_ie_len)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Extra IEs for scan do not fit driver limit (%zu > %zu) - this is likely to fail",
+				   params->extra_ies_len,
+				   drv->capa.max_probe_req_ie_len);
 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
 			    params->extra_ies))
 			goto fail;
@@ -739,7 +744,7 @@
 		return android_pno_stop(bss);
 #endif /* ANDROID */
 
-	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_STOP_SCHED_SCAN);
 	ret = send_and_recv_cmd(drv, msg);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -1205,7 +1210,7 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
 	drv->scan_for_auth = 0;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
@@ -1380,7 +1385,7 @@
 	if (!drv->set_wifi_conf_vendor_cmd_avail)
 		return -1;
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_VENDOR)) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
 	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
 			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
diff --git a/src/eap_common/eap_teap_common.c b/src/eap_common/eap_teap_common.c
index ffb9a62..aeb0e69 100644
--- a/src/eap_common/eap_teap_common.c
+++ b/src/eap_common/eap_teap_common.c
@@ -118,32 +118,7 @@
 }
 
 
-int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk, u8 *cmk)
-{
-	u8 imsk[32], imck[EAP_TEAP_IMCK_LEN];
-	int res;
-
-	/* FIX: The Basic-Password-Auth (i.e., no inner EAP) case is
-	 * not fully defined in RFC 7170, so this CMK derivation may
-	 * need to be changed if a fixed definition is eventually
-	 * published. For now, derive CMK[0] based on S-IMCK[0] and
-	 * IMSK of 32 octets of zeros. */
-	os_memset(imsk, 0, 32);
-	res = eap_teap_tls_prf(tls_cs, s_imck_msk, EAP_TEAP_SIMCK_LEN,
-			       "Inner Methods Compound Keys",
-			       imsk, 32, imck, sizeof(imck));
-	if (res < 0)
-		return -1;
-	os_memcpy(cmk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN);
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: CMK[no-inner-EAP]",
-			cmk, EAP_TEAP_CMK_LEN);
-	forced_memzero(imck, sizeof(imck));
-	return 0;
-}
-
-
-int eap_teap_derive_imck(u16 tls_cs,
-			 const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck,
 			 const u8 *msk, size_t msk_len,
 			 const u8 *emsk, size_t emsk_len,
 			 u8 *s_imck_msk, u8 *cmk_msk,
@@ -186,7 +161,7 @@
 				imsk, 32);
 
 		res = eap_teap_tls_prf(tls_cs,
-				       prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN,
+				       prev_s_imck, EAP_TEAP_SIMCK_LEN,
 				       "Inner Methods Compound Keys",
 				       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
 		forced_memzero(imsk, sizeof(imsk));
@@ -216,7 +191,7 @@
 		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Zero IMSK", imsk, 32);
 	}
 
-	res = eap_teap_tls_prf(tls_cs, prev_s_imck_msk, EAP_TEAP_SIMCK_LEN,
+	res = eap_teap_tls_prf(tls_cs, prev_s_imck, EAP_TEAP_SIMCK_LEN,
 			       "Inner Methods Compound Keys",
 			       imsk, 32, imck, EAP_TEAP_IMCK_LEN);
 	forced_memzero(imsk, sizeof(imsk));
@@ -342,10 +317,6 @@
 	if (res < 0)
 		return res;
 
-	/* FIX: RFC 7170 does not describe how to handle truncation of the
-	 * Compound MAC or if the fields are supposed to be of variable length
-	 * based on the negotiated TLS cipher suite (they are defined as having
-	 * fixed size of 20 octets in the TLV description) */
 	if (mac_len > sizeof(tmp))
 		mac_len = sizeof(tmp);
 	os_memcpy(mac, tmp, mac_len);
@@ -704,41 +675,3 @@
 	wpabuf_put_be16(buf, id);
 	return buf;
 }
-
-
-int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type)
-{
-	/* RFC 7170, Section 3.8.3: MUST provide mutual authentication,
-	 * provide key generation, and be resistant to dictionary attack.
-	 * Section 3.8 also mentions requirement for using EMSK Compound MAC. */
-	return vendor == EAP_VENDOR_IETF &&
-		(type == EAP_TYPE_PWD || type == EAP_TYPE_EKE);
-}
-
-
-int eap_teap_allowed_anon_prov_cipher_suite(u16 cs)
-{
-	/* RFC 7170, Section 3.8.3: anonymous ciphersuites MAY be supported as
-	 * long as the TLS pre-master secret is generated form contribution from
-	 * both peers. Accept the recommended TLS_DH_anon_WITH_AES_128_CBC_SHA
-	 * cipher suite and other ciphersuites that use DH in some form, have
-	 * SHA-1 or stronger MAC function, and use reasonable strong cipher. */
-	static const u16 ok_cs[] = {
-		/* DH-anon */
-		0x0034, 0x003a, 0x006c, 0x006d, 0x00a6, 0x00a7,
-		/* DHE-RSA */
-		0x0033, 0x0039, 0x0067, 0x006b, 0x009e, 0x009f,
-		/* ECDH-anon */
-		0xc018, 0xc019,
-		/* ECDH-RSA */
-		0xc003, 0xc00f, 0xc029, 0xc02a, 0xc031, 0xc032,
-		/* ECDH-ECDSA */
-		0xc004, 0xc005, 0xc025, 0xc026, 0xc02d, 0xc02e,
-		/* ECDHE-RSA */
-		0xc013, 0xc014, 0xc027, 0xc028, 0xc02f, 0xc030,
-		/* ECDHE-ECDSA */
-		0xc009, 0xc00a, 0xc023, 0xc024, 0xc02b, 0xc02c,
-	};
-
-	return tls_cipher_suite_match(ok_cs, ARRAY_SIZE(ok_cs), cs);
-}
diff --git a/src/eap_common/eap_teap_common.h b/src/eap_common/eap_teap_common.h
index 3a25879..cbf1315 100644
--- a/src/eap_common/eap_teap_common.h
+++ b/src/eap_common/eap_teap_common.h
@@ -205,10 +205,7 @@
 struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf);
 int eap_teap_derive_eap_msk(u16 tls_cs, const u8 *simck, u8 *msk);
 int eap_teap_derive_eap_emsk(u16 tls_cs, const u8 *simck, u8 *emsk);
-int eap_teap_derive_cmk_basic_pw_auth(u16 tls_cs, const u8 *s_imck_msk,
-				      u8 *cmk);
-int eap_teap_derive_imck(u16 tls_cs,
-			 const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk,
+int eap_teap_derive_imck(u16 tls_cs, const u8 *prev_s_imck,
 			 const u8 *msk, size_t msk_len,
 			 const u8 *emsk, size_t emsk_len,
 			 u8 *s_imck_msk, u8 *cmk_msk,
@@ -224,7 +221,5 @@
 struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error);
 struct wpabuf * eap_teap_tlv_identity_type(enum teap_identity_types id);
 enum eap_type;
-int eap_teap_allowed_anon_prov_phase2_method(int vendor, enum eap_type type);
-int eap_teap_allowed_anon_prov_cipher_suite(u16 cs);
 
 #endif /* EAP_TEAP_H */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 1454447..43e100a 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -724,8 +724,6 @@
 		EXT_CERT_CHECK_GOOD,
 		EXT_CERT_CHECK_BAD,
 	} pending_ext_cert_check;
-
-	int teap_anon_dh;
 };
 
 
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index ced7b16..8ce7cb7 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TEAP (RFC 7170)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,11 +14,6 @@
 #include "eap_i.h"
 #include "eap_tls_common.h"
 #include "eap_config.h"
-#include "eap_teap_pac.h"
-
-#ifdef EAP_TEAP_DYNAMIC
-#include "eap_teap_pac.c"
-#endif /* EAP_TEAP_DYNAMIC */
 
 
 static void eap_teap_deinit(struct eap_sm *sm, void *priv);
@@ -43,13 +38,6 @@
 	struct eap_method_type *phase2_types;
 	size_t num_phase2_types;
 	int resuming; /* starting a resumed session */
-#define EAP_TEAP_PROV_UNAUTH 1
-#define EAP_TEAP_PROV_AUTH 2
-	int provisioning_allowed; /* Allowed PAC provisioning modes */
-	int provisioning; /* doing PAC provisioning (not the normal auth) */
-	int anon_provisioning; /* doing anonymous (unauthenticated)
-				* provisioning */
-	int session_ticket_used;
 	int test_outer_tlvs;
 
 	u8 key_data[EAP_TEAP_KEY_LEN];
@@ -58,96 +46,34 @@
 	u8 emsk[EAP_EMSK_LEN];
 	int success;
 
-	struct eap_teap_pac *pac;
-	struct eap_teap_pac *current_pac;
-	size_t max_pac_list_len;
-	int use_pac_binary_format;
-
+	u8 simck[EAP_TEAP_SIMCK_LEN];
 	u8 simck_msk[EAP_TEAP_SIMCK_LEN];
 	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
 	int simck_idx;
-	int cmk_emsk_available;
+	bool cmk_emsk_available;
 
 	struct wpabuf *pending_phase2_req;
 	struct wpabuf *pending_resp;
 	struct wpabuf *server_outer_tlvs;
 	struct wpabuf *peer_outer_tlvs;
+
+	enum teap_compat {
+		TEAP_DEFAULT,
+		TEAP_FREERADIUS,
+	} teap_compat;
 };
 
 
-static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
-				      const u8 *client_random,
-				      const u8 *server_random,
-				      u8 *master_secret)
-{
-	struct eap_teap_data *data = ctx;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
-
-	if (!master_secret) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: SessionTicket failed - fall back to full TLS handshake");
-		data->session_ticket_used = 0;
-		if (data->provisioning_allowed) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Try to provision a new PAC-Key");
-			data->provisioning = 1;
-			data->current_pac = NULL;
-		}
-		return 0;
-	}
-
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len);
-
-	if (!data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC-Key available for using SessionTicket");
-		data->session_ticket_used = 0;
-		return 0;
-	}
-
-	/* EAP-TEAP uses PAC-Key as the TLS master_secret */
-	os_memcpy(master_secret, data->current_pac->pac_key,
-		  EAP_TEAP_PAC_KEY_LEN);
-
-	data->session_ticket_used = 1;
-
-	return 1;
-}
-
-
 static void eap_teap_parse_phase1(struct eap_teap_data *data,
 				  const char *phase1)
 {
-	const char *pos;
-
-	pos = os_strstr(phase1, "teap_provisioning=");
-	if (pos) {
-		data->provisioning_allowed = atoi(pos + 18);
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Automatic PAC provisioning mode: %d",
-			   data->provisioning_allowed);
-	}
-
-	pos = os_strstr(phase1, "teap_max_pac_list_len=");
-	if (pos) {
-		data->max_pac_list_len = atoi(pos + 22);
-		if (data->max_pac_list_len == 0)
-			data->max_pac_list_len = 1;
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Maximum PAC list length: %lu",
-			   (unsigned long) data->max_pac_list_len);
-	}
-
-	if (os_strstr(phase1, "teap_pac_format=binary")) {
-		data->use_pac_binary_format = 1;
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Using binary format for PAC list");
-	}
-
 #ifdef CONFIG_TESTING_OPTIONS
 	if (os_strstr(phase1, "teap_test_outer_tlvs=1"))
 		data->test_outer_tlvs = 1;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+	if (os_strstr(phase1, "teap_compat=freeradius"))
+		data->teap_compat = TEAP_FREERADIUS;
 }
 
 
@@ -163,21 +89,10 @@
 	if (!data)
 		return NULL;
 	data->teap_version = EAP_TEAP_VERSION;
-	data->max_pac_list_len = 10;
 
 	if (config->phase1)
 		eap_teap_parse_phase1(data, config->phase1);
 
-	if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) &&
-	    !config->cert.ca_cert && !config->cert.ca_path) {
-		/* Prevent PAC provisioning without mutual authentication
-		 * (either by validating server certificate or by suitable
-		 * inner EAP method). */
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path");
-		data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH;
-	}
-
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
 					   &data->num_phase2_types, 0) < 0) {
@@ -188,44 +103,12 @@
 	data->phase2_type.vendor = EAP_VENDOR_IETF;
 	data->phase2_type.method = EAP_TYPE_NONE;
 
-	config->teap_anon_dh = !!(data->provisioning_allowed &
-				  EAP_TEAP_PROV_UNAUTH);
 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL");
 		eap_teap_deinit(sm, data);
 		return NULL;
 	}
 
-	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
-						 eap_teap_session_ticket_cb,
-						 data) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Failed to set SessionTicket callback");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (!config->pac_file) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (data->use_pac_binary_format &&
-	    eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-
-	if (!data->use_pac_binary_format &&
-	    eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
-		eap_teap_deinit(sm, data);
-		return NULL;
-	}
-	eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
-
 	return data;
 }
 
@@ -244,6 +127,7 @@
 	data->server_outer_tlvs = NULL;
 	wpabuf_free(data->peer_outer_tlvs);
 	data->peer_outer_tlvs = NULL;
+	forced_memzero(data->simck, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
 }
@@ -252,7 +136,6 @@
 static void eap_teap_deinit(struct eap_sm *sm, void *priv)
 {
 	struct eap_teap_data *data = priv;
-	struct eap_teap_pac *pac, *prev;
 
 	if (!data)
 		return;
@@ -262,25 +145,20 @@
 	os_free(data->phase2_types);
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
 
-	pac = data->pac;
-	prev = NULL;
-	while (pac) {
-		prev = pac;
-		pac = pac->next;
-		eap_teap_free_pac(prev);
-	}
-
 	os_free(data);
 }
 
 
 static int eap_teap_derive_msk(struct eap_teap_data *data)
 {
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk,
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Derive MSK/EMSK (n=%d)",
+		   data->simck_idx);
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: S-IMCK[n]", data->simck,
+		    EAP_TEAP_SIMCK_LEN);
+
+	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck,
 				    data->key_data) < 0 ||
-	    eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
+	    eap_teap_derive_eap_emsk(data->tls_cs, data->simck,
 				     data->emsk) < 0)
 		return -1;
 	data->success = 1;
@@ -296,13 +174,14 @@
 	/* RFC 7170, Section 5.1 */
 	res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
 					TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
-					data->simck_msk, EAP_TEAP_SIMCK_LEN);
+					data->simck, EAP_TEAP_SIMCK_LEN);
 	if (res)
 		return res;
 	wpa_hexdump_key(MSG_DEBUG,
 			"EAP-TEAP: session_key_seed (S-IMCK[0])",
-			data->simck_msk, EAP_TEAP_SIMCK_LEN);
-	os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+			data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN);
 	data->simck_idx = 0;
 	return 0;
 }
@@ -339,17 +218,6 @@
 {
 	size_t i;
 
-	/* TODO: TNC with anonymous provisioning; need to require both
-	 * completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */
-
-	if (data->anon_provisioning &&
-	    !eap_teap_allowed_anon_prov_phase2_method(vendor, type)) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: EAP type %u:%u not allowed during unauthenticated provisioning",
-			   vendor, type);
-		return -1;
-	}
-
 #ifdef EAP_TNC
 	if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_TNC) {
 		data->phase2_type.vendor = EAP_VENDOR_IETF;
@@ -518,28 +386,6 @@
 }
 
 
-static struct wpabuf * eap_teap_tlv_pac_ack(void)
-{
-	struct wpabuf *buf;
-	struct teap_tlv_result *res;
-	struct teap_tlv_pac_ack *ack;
-
-	buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
-	if (!buf)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)");
-	ack = wpabuf_put(buf, sizeof(*ack));
-	ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY);
-	ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr));
-	ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
-	ack->pac_len = host_to_be16(2);
-	ack->result = host_to_be16(TEAP_STATUS_SUCCESS);
-
-	return buf;
-}
-
-
 static struct wpabuf * eap_teap_add_identity_type(struct eap_sm *sm,
 						  struct wpabuf *msg)
 {
@@ -693,18 +539,21 @@
 				     sizeof(struct teap_tlv_hdr));
 	rbind->version = EAP_TEAP_VERSION;
 	rbind->received_version = data->received_version;
-	/* FIX: RFC 7170 is not clear on which Flags value to use when
-	 * Crypto-Binding TLV is used with Basic-Password-Auth */
-	flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
-		TEAP_CRYPTO_BINDING_MSK_CMAC;
 	subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+	if (cmk_emsk)
+		flags = TEAP_CRYPTO_BINDING_EMSK_CMAC;
+	else if (cmk_msk)
+		flags = TEAP_CRYPTO_BINDING_MSK_CMAC;
+	else
+		return -1;
 	rbind->subtype = (flags << 4) | subtype;
 	os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce));
 	inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
 	os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
 	os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
 
-	if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
+	if (cmk_msk &&
+	    eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
 				  data->peer_outer_tlvs, cmk_msk,
 				  rbind->msk_compound_mac) < 0)
 		return -1;
@@ -740,9 +589,7 @@
 		   data->simck_idx + 1);
 
 	if (!data->phase2_method)
-		return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							 data->simck_msk,
-							 cmk_msk);
+		goto out; /* no MSK derived in Basic-Password-Auth */
 
 	if (!data->phase2_method || !data->phase2_priv) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
@@ -773,17 +620,34 @@
 						     &emsk_len);
 	}
 
-	res = eap_teap_derive_imck(data->tls_cs,
-				   data->simck_msk, data->simck_emsk,
-				   msk, msk_len, emsk, emsk_len,
-				   data->simck_msk, cmk_msk,
-				   data->simck_emsk, cmk_emsk);
+out:
+	if (data->teap_compat == TEAP_FREERADIUS) {
+		u8 tmp_simck[EAP_TEAP_SIMCK_LEN];
+		u8 tmp_cmk[EAP_TEAP_CMK_LEN];
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: FreeRADIUS compatibility: use S-IMCK_MSK[j-1] and S-IMCK_EMSK[j-1] based on MSK/EMSK derivations instead of a single selected S-IMCK[j-1]");
+		res = eap_teap_derive_imck(data->tls_cs, data->simck_msk,
+					   msk, msk_len, emsk, emsk_len,
+					   data->simck_msk, cmk_msk,
+					   tmp_simck, tmp_cmk);
+		if (emsk)
+			res = eap_teap_derive_imck(data->tls_cs,
+						   data->simck_emsk,
+						   msk, msk_len, emsk, emsk_len,
+						   tmp_simck, tmp_cmk,
+						   data->simck_emsk, cmk_emsk);
+	} else {
+		res = eap_teap_derive_imck(data->tls_cs, data->simck,
+					   msk, msk_len, emsk, emsk_len,
+					   data->simck_msk, cmk_msk,
+					   data->simck_emsk, cmk_emsk);
+	}
 	bin_clear_free(msk, msk_len);
 	bin_clear_free(emsk, emsk_len);
 	if (res == 0) {
 		data->simck_idx++;
-		if (emsk)
-			data->cmk_emsk_available = 1;
+		data->cmk_emsk_available = emsk != NULL;
 	}
 	return res;
 }
@@ -825,10 +689,12 @@
 	u8 *pos;
 	u8 cmk_msk[EAP_TEAP_CMK_LEN];
 	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+	const u8 *cmk_msk_ptr = NULL;
 	const u8 *cmk_emsk_ptr = NULL;
 	int res;
 	size_t len;
 	u8 flags;
+	bool server_msk, server_emsk;
 
 	if (eap_teap_validate_crypto_binding(data, cb) < 0 ||
 	    eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0)
@@ -836,9 +702,12 @@
 
 	/* Validate received MSK/EMSK Compound MAC */
 	flags = cb->subtype >> 4;
+	server_msk = flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+		flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC;
+	server_emsk = flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+		flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC;
 
-	if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
-	    flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+	if (server_msk) {
 		u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
 
 		if (eap_teap_compound_mac(data->tls_cs, cb,
@@ -860,9 +729,7 @@
 		}
 	}
 
-	if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
-	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
-	    data->cmk_emsk_available) {
+	if (server_emsk && data->cmk_emsk_available) {
 		u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
 
 		if (eap_teap_compound_mac(data->tls_cs, cb,
@@ -882,8 +749,6 @@
 				   "EAP-TEAP: EMSK Compound MAC did not match");
 			return NULL;
 		}
-
-		cmk_emsk_ptr = cmk_emsk;
 	}
 
 	if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
@@ -898,6 +763,20 @@
 	 * crypto binding to allow server to complete authentication.
 	 */
 
+	if (server_emsk && data->cmk_emsk_available) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+		cmk_emsk_ptr = cmk_emsk;
+	} else if (server_msk) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_MSK");
+		os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+		cmk_msk_ptr = cmk_msk;
+	} else {
+		return NULL;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]",
+			data->simck, EAP_TEAP_SIMCK_LEN);
+
 	len = sizeof(struct teap_tlv_crypto_binding);
 	resp = wpabuf_alloc(len);
 	if (!resp)
@@ -920,7 +799,7 @@
 	pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding));
 	if (eap_teap_write_crypto_binding(
 		    data, (struct teap_tlv_crypto_binding *) pos,
-		    cb, cmk_msk, cmk_emsk_ptr) < 0) {
+		    cb, cmk_msk_ptr, cmk_emsk_ptr) < 0) {
 		wpabuf_free(resp);
 		return NULL;
 	}
@@ -929,231 +808,6 @@
 }
 
 
-static void eap_teap_parse_pac_tlv(struct eap_teap_pac *entry, int type,
-				   u8 *pos, size_t len, int *pac_key_found)
-{
-	switch (type & 0x7fff) {
-	case PAC_TYPE_PAC_KEY:
-		wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: PAC-Key", pos, len);
-		if (len != EAP_TEAP_PAC_KEY_LEN) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Invalid PAC-Key length %lu",
-				   (unsigned long) len);
-			break;
-		}
-		*pac_key_found = 1;
-		os_memcpy(entry->pac_key, pos, len);
-		break;
-	case PAC_TYPE_PAC_OPAQUE:
-		wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pos, len);
-		entry->pac_opaque = pos;
-		entry->pac_opaque_len = len;
-		break;
-	case PAC_TYPE_PAC_INFO:
-		wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len);
-		entry->pac_info = pos;
-		entry->pac_info_len = len;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d",
-			   type);
-		break;
-	}
-}
-
-
-static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry,
-				    u8 *pac, size_t pac_len)
-{
-	struct pac_attr_hdr *hdr;
-	u8 *pos;
-	size_t left, len;
-	int type, pac_key_found = 0;
-
-	pos = pac;
-	left = pac_len;
-
-	while (left > sizeof(*hdr)) {
-		hdr = (struct pac_attr_hdr *) pos;
-		type = be_to_host16(hdr->type);
-		len = be_to_host16(hdr->len);
-		pos += sizeof(*hdr);
-		left -= sizeof(*hdr);
-		if (len > left) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)",
-				   type, (unsigned long) len,
-				   (unsigned long) left);
-			return -1;
-		}
-
-		eap_teap_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
-
-		pos += len;
-		left -= len;
-	}
-
-	if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC TLV does not include all the required fields");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_parse_pac_info(struct eap_teap_pac *entry, int type,
-				   u8 *pos, size_t len)
-{
-	u16 pac_type;
-	u32 lifetime;
-	struct os_time now;
-
-	switch (type & 0x7fff) {
-	case PAC_TYPE_CRED_LIFETIME:
-		if (len != 4) {
-			wpa_hexdump(MSG_DEBUG,
-				    "EAP-TEAP: PAC-Info - Invalid CRED_LIFETIME length - ignored",
-				    pos, len);
-			return 0;
-		}
-
-		/*
-		 * This is not currently saved separately in PAC files since
-		 * the server can automatically initiate PAC update when
-		 * needed. Anyway, the information is available from PAC-Info
-		 * dump if it is needed for something in the future.
-		 */
-		lifetime = WPA_GET_BE32(pos);
-		os_get_time(&now);
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Info - CRED_LIFETIME %d (%d days)",
-			   lifetime, (lifetime - (u32) now.sec) / 86400);
-		break;
-	case PAC_TYPE_A_ID:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID",
-				  pos, len);
-		entry->a_id = pos;
-		entry->a_id_len = len;
-		break;
-	case PAC_TYPE_I_ID:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - I-ID",
-				  pos, len);
-		entry->i_id = pos;
-		entry->i_id_len = len;
-		break;
-	case PAC_TYPE_A_ID_INFO:
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID-Info",
-				  pos, len);
-		entry->a_id_info = pos;
-		entry->a_id_info_len = len;
-		break;
-	case PAC_TYPE_PAC_TYPE:
-		/* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */
-		if (len != 2) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Invalid PAC-Type length %lu (expected 2)",
-				   (unsigned long) len);
-			wpa_hexdump_ascii(MSG_DEBUG,
-					  "EAP-TEAP: PAC-Info - PAC-Type",
-					  pos, len);
-			return -1;
-		}
-		pac_type = WPA_GET_BE16(pos);
-		if (pac_type != PAC_TYPE_TUNNEL_PAC) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Unsupported PAC Type %d",
-				   pac_type);
-			return -1;
-		}
-
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d",
-			   pac_type);
-		entry->pac_type = pac_type;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Ignored unknown PAC-Info type %d", type);
-		break;
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_process_pac_info(struct eap_teap_pac *entry)
-{
-	struct pac_attr_hdr *hdr;
-	u8 *pos;
-	size_t left, len;
-	int type;
-
-	/* RFC 7170, Section 4.2.12.4 */
-
-	/* PAC-Type defaults to Tunnel PAC (Type 1) */
-	entry->pac_type = PAC_TYPE_TUNNEL_PAC;
-
-	pos = entry->pac_info;
-	left = entry->pac_info_len;
-	while (left > sizeof(*hdr)) {
-		hdr = (struct pac_attr_hdr *) pos;
-		type = be_to_host16(hdr->type);
-		len = be_to_host16(hdr->len);
-		pos += sizeof(*hdr);
-		left -= sizeof(*hdr);
-		if (len > left) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)",
-				   type, (unsigned long) len,
-				   (unsigned long) left);
-			return -1;
-		}
-
-		if (eap_teap_parse_pac_info(entry, type, pos, len) < 0)
-			return -1;
-
-		pos += len;
-		left -= len;
-	}
-
-	if (!entry->a_id || !entry->a_id_info) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Info does not include all the required fields");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm,
-					    struct eap_teap_data *data,
-					    struct eap_method_ret *ret,
-					    u8 *pac, size_t pac_len)
-{
-	struct eap_peer_config *config = eap_get_config(sm);
-	struct eap_teap_pac entry;
-
-	os_memset(&entry, 0, sizeof(entry));
-	if (eap_teap_process_pac_tlv(&entry, pac, pac_len) ||
-	    eap_teap_process_pac_info(&entry))
-		return NULL;
-
-	eap_teap_add_pac(&data->pac, &data->current_pac, &entry);
-	eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
-	if (data->use_pac_binary_format)
-		eap_teap_save_pac_bin(sm, data->pac, config->pac_file);
-	else
-		eap_teap_save_pac(sm, data->pac, config->pac_file);
-
-	wpa_printf(MSG_DEBUG,
-		   "EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully",
-		   data->provisioning ? "peer" : "server");
-	return eap_teap_tlv_pac_ack();
-}
-
-
 static int eap_teap_parse_decrypted(struct wpabuf *decrypted,
 				    struct eap_teap_tlv_parse *tlv,
 				    struct wpabuf **resp)
@@ -1208,38 +862,6 @@
 }
 
 
-static struct wpabuf * eap_teap_pac_request(void)
-{
-	struct wpabuf *req;
-	struct teap_tlv_request_action *act;
-	struct teap_tlv_hdr *pac;
-	struct teap_attr_pac_type *type;
-
-	req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type));
-	if (!req)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)");
-	act = wpabuf_put(req, sizeof(*act));
-	act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION);
-	act->length = host_to_be16(2);
-	act->status = TEAP_STATUS_SUCCESS;
-	act->action = TEAP_REQUEST_ACTION_PROCESS_TLV;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)");
-	pac = wpabuf_put(req, sizeof(*pac));
-	pac->tlv_type = host_to_be16(TEAP_TLV_PAC);
-	pac->length = host_to_be16(sizeof(*type));
-
-	type = wpabuf_put(req, sizeof(*type));
-	type->type = host_to_be16(PAC_TYPE_PAC_TYPE);
-	type->length = host_to_be16(2);
-	type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC);
-
-	return req;
-}
-
-
 static int eap_teap_process_decrypted(struct eap_sm *sm,
 				      struct eap_teap_data *data,
 				      struct eap_method_ret *ret,
@@ -1387,58 +1009,17 @@
 		}
 	}
 
-	if (data->result_success_done && data->session_ticket_used &&
+	if (data->result_success_done &&
+	    tls_connection_get_own_cert_used(data->ssl.conn) &&
 	    eap_teap_derive_msk(data) == 0) {
 		/* Assume the server might accept authentication without going
 		 * through inner authentication. */
 		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC used - server may decide to skip inner authentication");
-		ret->methodState = METHOD_MAY_CONT;
-		ret->decision = DECISION_COND_SUCC;
-	} else if (data->result_success_done &&
-		   tls_connection_get_own_cert_used(data->ssl.conn) &&
-		   eap_teap_derive_msk(data) == 0) {
-		/* Assume the server might accept authentication without going
-		 * through inner authentication. */
-		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Client certificate used - server may decide to skip inner authentication");
 		ret->methodState = METHOD_MAY_CONT;
 		ret->decision = DECISION_COND_SUCC;
 	}
 
-	if (tlv.pac) {
-		if (tlv.result == TEAP_STATUS_SUCCESS) {
-			tmp = eap_teap_process_pac(sm, data, ret,
-						   tlv.pac, tlv.pac_len);
-			resp = wpabuf_concat(resp, tmp);
-		} else {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV without Result TLV acknowledging success");
-			failed = 1;
-			error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
-		}
-	}
-
-	if (!data->current_pac && data->provisioning && !failed && !tlv.pac &&
-	    tlv.crypto_binding &&
-	    (!data->anon_provisioning ||
-	     (data->phase2_success && data->phase2_method &&
-	      data->phase2_method->vendor == 0 &&
-	      eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) &&
-	      eap_teap_allowed_anon_prov_phase2_method(
-		      data->phase2_method->vendor,
-		      data->phase2_method->method))) &&
-	    (tlv.iresult == TEAP_STATUS_SUCCESS ||
-	     tlv.result == TEAP_STATUS_SUCCESS)) {
-		/*
-		 * Need to request Tunnel PAC when using authenticated
-		 * provisioning.
-		 */
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC");
-		tmp = eap_teap_pac_request();
-		resp = wpabuf_concat(resp, tmp);
-	}
-
 done:
 	if (failed) {
 		tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
@@ -1470,8 +1051,7 @@
 		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Authentication completed successfully");
 		ret->methodState = METHOD_MAY_CONT;
-		data->on_tx_completion = data->provisioning ?
-			METHOD_MAY_CONT : METHOD_DONE;
+		data->on_tx_completion = METHOD_DONE;
 		ret->decision = DECISION_UNCOND_SUCC;
 	}
 
@@ -1562,76 +1142,11 @@
 }
 
 
-static void eap_teap_select_pac(struct eap_teap_data *data,
-				const u8 *a_id, size_t a_id_len)
-{
-	if (!a_id)
-		return;
-	data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len,
-					     PAC_TYPE_TUNNEL_PAC);
-	if (data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC found for this A-ID (PAC-Type %d)",
-			   data->current_pac->pac_type);
-		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info",
-				  data->current_pac->a_id_info,
-				  data->current_pac->a_id_info_len);
-	}
-}
-
-
-static int eap_teap_use_pac_opaque(struct eap_sm *sm,
-				   struct eap_teap_data *data,
-				   struct eap_teap_pac *pac)
-{
-	u8 *tlv;
-	size_t tlv_len, olen;
-	struct teap_tlv_hdr *ehdr;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension");
-	olen = pac->pac_opaque_len;
-	tlv_len = sizeof(*ehdr) + olen;
-	tlv = os_malloc(tlv_len);
-	if (tlv) {
-		ehdr = (struct teap_tlv_hdr *) tlv;
-		ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
-		ehdr->length = host_to_be16(olen);
-		os_memcpy(ehdr + 1, pac->pac_opaque, olen);
-	}
-	if (!tlv ||
-	    tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
-					    TLS_EXT_PAC_OPAQUE,
-					    tlv, tlv_len) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to add PAC-Opaque TLS extension");
-		os_free(tlv);
-		return -1;
-	}
-	os_free(tlv);
-
-	return 0;
-}
-
-
-static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm,
-					 struct eap_teap_data *data)
-{
-	if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
-					    TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to remove PAC-Opaque TLS extension");
-		return -1;
-	}
-	return 0;
-}
-
-
 static int eap_teap_process_start(struct eap_sm *sm,
 				  struct eap_teap_data *data, u8 flags,
 				  const u8 *pos, size_t left)
 {
 	const u8 *a_id = NULL;
-	size_t a_id_len = 0;
 
 	/* TODO: Support (mostly theoretical) case of TEAP/Start request being
 	 * fragmented */
@@ -1725,7 +1240,6 @@
 					return -1;
 				}
 				a_id = outer_pos;
-				a_id_len = tlv_len;
 			} else {
 				wpa_printf(MSG_DEBUG,
 					   "EAP-TEAP: Ignore unknown Outer TLV (Type %u)",
@@ -1740,28 +1254,6 @@
 		return -1;
 	}
 
-	eap_teap_select_pac(data, a_id, a_id_len);
-
-	if (data->resuming && data->current_pac) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello");
-		if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
-			return -1;
-	} else if (data->current_pac) {
-		/*
-		 * PAC found for the A-ID and we are not resuming an old
-		 * session, so add PAC-Opaque extension to ClientHello.
-		 */
-		if (eap_teap_use_pac_opaque(sm, data, data->current_pac) < 0)
-			return -1;
-	} else if (data->provisioning_allowed) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC found - starting provisioning");
-		if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
-			return -1;
-		data->provisioning = 1;
-	}
-
 	return 0;
 }
 
@@ -1939,8 +1431,6 @@
 		}
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
-			char cipher[80];
-
 			wpa_printf(MSG_DEBUG,
 				   "EAP-TEAP: TLS done, proceed to Phase 2");
 			data->tls_cs =
@@ -1949,19 +1439,6 @@
 				   "EAP-TEAP: TLS cipher suite 0x%04x",
 				   data->tls_cs);
 
-			if (data->provisioning &&
-			    (!(data->provisioning_allowed &
-			       EAP_TEAP_PROV_AUTH) ||
-			     tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
-					    cipher, sizeof(cipher)) < 0 ||
-			     os_strstr(cipher, "ADH-") ||
-			     os_strstr(cipher, "anon"))) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Using anonymous (unauthenticated) provisioning");
-				data->anon_provisioning = 1;
-			} else {
-				data->anon_provisioning = 0;
-			}
 			data->resuming = 0;
 			if (eap_teap_derive_key_auth(sm, data) < 0) {
 				wpa_printf(MSG_DEBUG,
@@ -2037,8 +1514,6 @@
 	data->iresult_verified = 0;
 	data->done_on_tx_completion = 0;
 	data->resuming = 1;
-	data->provisioning = 0;
-	data->anon_provisioning = 0;
 	data->simck_idx = 0;
 	return priv;
 }
diff --git a/src/eap_peer/eap_teap_pac.c b/src/eap_peer/eap_teap_pac.c
deleted file mode 100644
index 34a2743..0000000
--- a/src/eap_peer/eap_teap_pac.c
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * EAP peer method: EAP-TEAP PAC file processing
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eap_config.h"
-#include "eap_i.h"
-#include "eap_teap_pac.h"
-
-/* TODO: encrypt PAC-Key in the PAC file */
-
-
-/* Text data format */
-static const char *pac_file_hdr =
-	"wpa_supplicant EAP-TEAP PAC file - version 1";
-
-/*
- * Binary data format
- * 4-octet magic value: 6A E4 92 1C
- * 2-octet version (big endian)
- * <version specific data>
- *
- * version=0:
- * Sequence of PAC entries:
- *   2-octet PAC-Type (big endian)
- *   32-octet PAC-Key
- *   2-octet PAC-Opaque length (big endian)
- *   <variable len> PAC-Opaque data (length bytes)
- *   2-octet PAC-Info length (big endian)
- *   <variable len> PAC-Info data (length bytes)
- */
-
-#define EAP_TEAP_PAC_BINARY_MAGIC 0x6ae4921c
-#define EAP_TEAP_PAC_BINARY_FORMAT_VERSION 0
-
-
-/**
- * eap_teap_free_pac - Free PAC data
- * @pac: Pointer to the PAC entry
- *
- * Note that the PAC entry must not be in a list since this function does not
- * remove the list links.
- */
-void eap_teap_free_pac(struct eap_teap_pac *pac)
-{
-	os_free(pac->pac_opaque);
-	os_free(pac->pac_info);
-	os_free(pac->a_id);
-	os_free(pac->i_id);
-	os_free(pac->a_id_info);
-	os_free(pac);
-}
-
-
-/**
- * eap_teap_get_pac - Get a PAC entry based on A-ID
- * @pac_root: Pointer to root of the PAC list
- * @a_id: A-ID to search for
- * @a_id_len: Length of A-ID
- * @pac_type: PAC-Type to search for
- * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
- */
-struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
-				       const u8 *a_id, size_t a_id_len,
-				       u16 pac_type)
-{
-	struct eap_teap_pac *pac = pac_root;
-
-	while (pac) {
-		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
-		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
-			return pac;
-		}
-		pac = pac->next;
-	}
-	return NULL;
-}
-
-
-static void eap_teap_remove_pac(struct eap_teap_pac **pac_root,
-				struct eap_teap_pac **pac_current,
-				const u8 *a_id, size_t a_id_len, u16 pac_type)
-{
-	struct eap_teap_pac *pac, *prev;
-
-	pac = *pac_root;
-	prev = NULL;
-
-	while (pac) {
-		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
-		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
-			if (!prev)
-				*pac_root = pac->next;
-			else
-				prev->next = pac->next;
-			if (*pac_current == pac)
-				*pac_current = NULL;
-			eap_teap_free_pac(pac);
-			break;
-		}
-		prev = pac;
-		pac = pac->next;
-	}
-}
-
-
-static int eap_teap_copy_buf(u8 **dst, size_t *dst_len,
-			     const u8 *src, size_t src_len)
-{
-	if (src) {
-		*dst = os_memdup(src, src_len);
-		if (!(*dst))
-			return -1;
-		*dst_len = src_len;
-	}
-	return 0;
-}
-
-
-/**
- * eap_teap_add_pac - Add a copy of a PAC entry to a list
- * @pac_root: Pointer to PAC list root pointer
- * @pac_current: Pointer to the current PAC pointer
- * @entry: New entry to clone and add to the list
- * Returns: 0 on success, -1 on failure
- *
- * This function makes a clone of the given PAC entry and adds this copied
- * entry to the list (pac_root). If an old entry for the same A-ID is found,
- * it will be removed from the PAC list and in this case, pac_current entry
- * is set to %NULL if it was the removed entry.
- */
-int eap_teap_add_pac(struct eap_teap_pac **pac_root,
-		     struct eap_teap_pac **pac_current,
-		     struct eap_teap_pac *entry)
-{
-	struct eap_teap_pac *pac;
-
-	if (!entry || !entry->a_id)
-		return -1;
-
-	/* Remove a possible old entry for the matching A-ID. */
-	eap_teap_remove_pac(pac_root, pac_current,
-			    entry->a_id, entry->a_id_len, entry->pac_type);
-
-	/* Allocate a new entry and add it to the list of PACs. */
-	pac = os_zalloc(sizeof(*pac));
-	if (!pac)
-		return -1;
-
-	pac->pac_type = entry->pac_type;
-	os_memcpy(pac->pac_key, entry->pac_key, EAP_TEAP_PAC_KEY_LEN);
-	if (eap_teap_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
-			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
-	    eap_teap_copy_buf(&pac->pac_info, &pac->pac_info_len,
-			      entry->pac_info, entry->pac_info_len) < 0 ||
-	    eap_teap_copy_buf(&pac->a_id, &pac->a_id_len,
-			      entry->a_id, entry->a_id_len) < 0 ||
-	    eap_teap_copy_buf(&pac->i_id, &pac->i_id_len,
-			      entry->i_id, entry->i_id_len) < 0 ||
-	    eap_teap_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
-			      entry->a_id_info, entry->a_id_info_len) < 0) {
-		eap_teap_free_pac(pac);
-		return -1;
-	}
-
-	pac->next = *pac_root;
-	*pac_root = pac;
-
-	return 0;
-}
-
-
-struct eap_teap_read_ctx {
-	FILE *f;
-	const char *pos;
-	const char *end;
-	int line;
-	char *buf;
-	size_t buf_len;
-};
-
-static int eap_teap_read_line(struct eap_teap_read_ctx *rc, char **value)
-{
-	char *pos;
-
-	rc->line++;
-	if (rc->f) {
-		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
-			return -1;
-	} else {
-		const char *l_end;
-		size_t len;
-
-		if (rc->pos >= rc->end)
-			return -1;
-		l_end = rc->pos;
-		while (l_end < rc->end && *l_end != '\n')
-			l_end++;
-		len = l_end - rc->pos;
-		if (len >= rc->buf_len)
-			len = rc->buf_len - 1;
-		os_memcpy(rc->buf, rc->pos, len);
-		rc->buf[len] = '\0';
-		rc->pos = l_end + 1;
-	}
-
-	rc->buf[rc->buf_len - 1] = '\0';
-	pos = rc->buf;
-	while (*pos != '\0') {
-		if (*pos == '\n' || *pos == '\r') {
-			*pos = '\0';
-			break;
-		}
-		pos++;
-	}
-
-	pos = os_strchr(rc->buf, '=');
-	if (pos)
-		*pos++ = '\0';
-	*value = pos;
-
-	return 0;
-}
-
-
-static u8 * eap_teap_parse_hex(const char *value, size_t *len)
-{
-	int hlen;
-	u8 *buf;
-
-	if (!value)
-		return NULL;
-	hlen = os_strlen(value);
-	if (hlen & 1)
-		return NULL;
-	*len = hlen / 2;
-	buf = os_malloc(*len);
-	if (!buf)
-		return NULL;
-	if (hexstr2bin(value, buf, *len)) {
-		os_free(buf);
-		return NULL;
-	}
-	return buf;
-}
-
-
-static int eap_teap_init_pac_data(struct eap_sm *sm, const char *pac_file,
-				  struct eap_teap_read_ctx *rc)
-{
-	os_memset(rc, 0, sizeof(*rc));
-
-	rc->buf_len = 2048;
-	rc->buf = os_malloc(rc->buf_len);
-	if (!rc->buf)
-		return -1;
-
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		const struct wpa_config_blob *blob;
-
-		blob = eap_get_config_blob(sm, pac_file + 7);
-		if (!blob) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned",
-				   pac_file + 7);
-			os_free(rc->buf);
-			return -1;
-		}
-		rc->pos = (char *) blob->data;
-		rc->end = (char *) blob->data + blob->len;
-	} else {
-		rc->f = fopen(pac_file, "rb");
-		if (!rc->f) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
-				   pac_file);
-			os_free(rc->buf);
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
-static void eap_teap_deinit_pac_data(struct eap_teap_read_ctx *rc)
-{
-	os_free(rc->buf);
-	if (rc->f)
-		fclose(rc->f);
-}
-
-
-static const char * eap_teap_parse_start(struct eap_teap_pac **pac)
-{
-	if (*pac)
-		return "START line without END";
-
-	*pac = os_zalloc(sizeof(struct eap_teap_pac));
-	if (!(*pac))
-		return "No memory for PAC entry";
-	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_end(struct eap_teap_pac **pac_root,
-				       struct eap_teap_pac **pac)
-{
-	if (!(*pac))
-		return "END line without START";
-	if (*pac_root) {
-		struct eap_teap_pac *end = *pac_root;
-
-		while (end->next)
-			end = end->next;
-		end->next = *pac;
-	} else
-		*pac_root = *pac;
-
-	*pac = NULL;
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_type(struct eap_teap_pac *pac,
-					    char *pos)
-{
-	if (!pos)
-		return "Cannot parse pac type";
-	pac->pac_type = atoi(pos);
-	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC)
-		return "Unrecognized PAC-Type";
-
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_key(struct eap_teap_pac *pac, char *pos)
-{
-	u8 *key;
-	size_t key_len;
-
-	key = eap_teap_parse_hex(pos, &key_len);
-	if (!key || key_len != EAP_TEAP_PAC_KEY_LEN) {
-		os_free(key);
-		return "Invalid PAC-Key";
-	}
-
-	os_memcpy(pac->pac_key, key, EAP_TEAP_PAC_KEY_LEN);
-	os_free(key);
-
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_pac_opaque(struct eap_teap_pac *pac,
-					      char *pos)
-{
-	os_free(pac->pac_opaque);
-	pac->pac_opaque = eap_teap_parse_hex(pos, &pac->pac_opaque_len);
-	if (!pac->pac_opaque)
-		return "Invalid PAC-Opaque";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_a_id(struct eap_teap_pac *pac, char *pos)
-{
-	os_free(pac->a_id);
-	pac->a_id = eap_teap_parse_hex(pos, &pac->a_id_len);
-	if (!pac->a_id)
-		return "Invalid A-ID";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_i_id(struct eap_teap_pac *pac, char *pos)
-{
-	os_free(pac->i_id);
-	pac->i_id = eap_teap_parse_hex(pos, &pac->i_id_len);
-	if (!pac->i_id)
-		return "Invalid I-ID";
-	return NULL;
-}
-
-
-static const char * eap_teap_parse_a_id_info(struct eap_teap_pac *pac,
-					     char *pos)
-{
-	os_free(pac->a_id_info);
-	pac->a_id_info = eap_teap_parse_hex(pos, &pac->a_id_info_len);
-	if (!pac->a_id_info)
-		return "Invalid A-ID-Info";
-	return NULL;
-}
-
-
-/**
- * eap_teap_load_pac - Load PAC entries (text format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Pointer to root of the PAC list (to be filled)
- * @pac_file: Name of the PAC file/blob to load
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-		      const char *pac_file)
-{
-	struct eap_teap_read_ctx rc;
-	struct eap_teap_pac *pac = NULL;
-	int count = 0;
-	char *pos;
-	const char *err = NULL;
-
-	if (!pac_file)
-		return -1;
-
-	if (eap_teap_init_pac_data(sm, pac_file, &rc) < 0)
-		return 0;
-
-	if (eap_teap_read_line(&rc, &pos) < 0) {
-		/* empty file - assume it is fine to overwrite */
-		eap_teap_deinit_pac_data(&rc);
-		return 0;
-	}
-	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
-		err = "Unrecognized header line";
-
-	while (!err && eap_teap_read_line(&rc, &pos) == 0) {
-		if (os_strcmp(rc.buf, "START") == 0)
-			err = eap_teap_parse_start(&pac);
-		else if (os_strcmp(rc.buf, "END") == 0) {
-			err = eap_teap_parse_end(pac_root, &pac);
-			count++;
-		} else if (!pac)
-			err = "Unexpected line outside START/END block";
-		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
-			err = eap_teap_parse_pac_type(pac, pos);
-		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
-			err = eap_teap_parse_pac_key(pac, pos);
-		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
-			err = eap_teap_parse_pac_opaque(pac, pos);
-		else if (os_strcmp(rc.buf, "A-ID") == 0)
-			err = eap_teap_parse_a_id(pac, pos);
-		else if (os_strcmp(rc.buf, "I-ID") == 0)
-			err = eap_teap_parse_i_id(pac, pos);
-		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
-			err = eap_teap_parse_a_id_info(pac, pos);
-	}
-
-	if (pac) {
-		if (!err)
-			err = "PAC block not terminated with END";
-		eap_teap_free_pac(pac);
-	}
-
-	eap_teap_deinit_pac_data(&rc);
-
-	if (err) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: %s in '%s:%d'",
-			   err, pac_file, rc.line);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %d PAC entries from '%s'",
-		   count, pac_file);
-
-	return 0;
-}
-
-
-static void eap_teap_write(char **buf, char **pos, size_t *buf_len,
-			   const char *field, const u8 *data,
-			   size_t len, int txt)
-{
-	size_t i, need;
-	int ret;
-	char *end;
-
-	if (!data || !buf || !(*buf) || !pos || !(*pos) || *pos < *buf)
-		return;
-
-	need = os_strlen(field) + len * 2 + 30;
-	if (txt)
-		need += os_strlen(field) + len + 20;
-
-	if (*pos - *buf + need > *buf_len) {
-		char *nbuf = os_realloc(*buf, *buf_len + need);
-
-		if (!nbuf) {
-			os_free(*buf);
-			*buf = NULL;
-			return;
-		}
-		*pos = nbuf + (*pos - *buf);
-		*buf = nbuf;
-		*buf_len += need;
-	}
-	end = *buf + *buf_len;
-
-	ret = os_snprintf(*pos, end - *pos, "%s=", field);
-	if (os_snprintf_error(end - *pos, ret))
-		return;
-	*pos += ret;
-	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
-	ret = os_snprintf(*pos, end - *pos, "\n");
-	if (os_snprintf_error(end - *pos, ret))
-		return;
-	*pos += ret;
-
-	if (txt) {
-		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
-		if (os_snprintf_error(end - *pos, ret))
-			return;
-		*pos += ret;
-		for (i = 0; i < len; i++) {
-			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
-			if (os_snprintf_error(end - *pos, ret))
-				return;
-			*pos += ret;
-		}
-		ret = os_snprintf(*pos, end - *pos, "\n");
-		if (os_snprintf_error(end - *pos, ret))
-			return;
-		*pos += ret;
-	}
-}
-
-
-static int eap_teap_write_pac(struct eap_sm *sm, const char *pac_file,
-			      char *buf, size_t len)
-{
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		struct wpa_config_blob *blob;
-
-		blob = os_zalloc(sizeof(*blob));
-		if (!blob)
-			return -1;
-		blob->data = (u8 *) buf;
-		blob->len = len;
-		buf = NULL;
-		blob->name = os_strdup(pac_file + 7);
-		if (!blob->name) {
-			os_free(blob);
-			return -1;
-		}
-		eap_set_config_blob(sm, blob);
-	} else {
-		FILE *f;
-
-		f = fopen(pac_file, "wb");
-		if (!f) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Failed to open PAC file '%s' for writing",
-				   pac_file);
-			return -1;
-		}
-		if (fwrite(buf, 1, len, f) != len) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: Failed to write all PACs into '%s'",
-				   pac_file);
-			fclose(f);
-			return -1;
-		}
-		os_free(buf);
-		fclose(f);
-	}
-
-	return 0;
-}
-
-
-static int eap_teap_add_pac_data(struct eap_teap_pac *pac, char **buf,
-				 char **pos, size_t *buf_len)
-{
-	int ret;
-
-	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
-			  "START\nPAC-Type=%d\n", pac->pac_type);
-	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
-		return -1;
-
-	*pos += ret;
-	eap_teap_write(buf, pos, buf_len, "PAC-Key",
-		       pac->pac_key, EAP_TEAP_PAC_KEY_LEN, 0);
-	eap_teap_write(buf, pos, buf_len, "PAC-Opaque",
-		       pac->pac_opaque, pac->pac_opaque_len, 0);
-	eap_teap_write(buf, pos, buf_len, "PAC-Info",
-		       pac->pac_info, pac->pac_info_len, 0);
-	eap_teap_write(buf, pos, buf_len, "A-ID",
-		       pac->a_id, pac->a_id_len, 0);
-	eap_teap_write(buf, pos, buf_len, "I-ID",
-		       pac->i_id, pac->i_id_len, 1);
-	eap_teap_write(buf, pos, buf_len, "A-ID-Info",
-		       pac->a_id_info, pac->a_id_info_len, 1);
-	if (!(*buf)) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: No memory for PAC data");
-		return -1;
-	}
-	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
-	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
-		return -1;
-	*pos += ret;
-
-	return 0;
-}
-
-
-/**
- * eap_teap_save_pac - Save PAC entries (text format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Root of the PAC list
- * @pac_file: Name of the PAC file/blob
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-		      const char *pac_file)
-{
-	struct eap_teap_pac *pac;
-	int ret, count = 0;
-	char *buf, *pos;
-	size_t buf_len;
-
-	if (!pac_file)
-		return -1;
-
-	buf_len = 1024;
-	pos = buf = os_malloc(buf_len);
-	if (!buf)
-		return -1;
-
-	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
-	if (os_snprintf_error(buf + buf_len - pos, ret)) {
-		os_free(buf);
-		return -1;
-	}
-	pos += ret;
-
-	pac = pac_root;
-	while (pac) {
-		if (eap_teap_add_pac_data(pac, &buf, &pos, &buf_len)) {
-			os_free(buf);
-			return -1;
-		}
-		count++;
-		pac = pac->next;
-	}
-
-	if (eap_teap_write_pac(sm, pac_file, buf, pos - buf)) {
-		os_free(buf);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %d PAC entries into '%s'",
-		   count, pac_file);
-
-	return 0;
-}
-
-
-/**
- * eap_teap_pac_list_truncate - Truncate a PAC list to the given length
- * @pac_root: Root of the PAC list
- * @max_len: Maximum length of the list (>= 1)
- * Returns: Number of PAC entries removed
- */
-size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
-				  size_t max_len)
-{
-	struct eap_teap_pac *pac, *prev;
-	size_t count;
-
-	pac = pac_root;
-	prev = NULL;
-	count = 0;
-
-	while (pac) {
-		count++;
-		if (count > max_len)
-			break;
-		prev = pac;
-		pac = pac->next;
-	}
-
-	if (count <= max_len || !prev)
-		return 0;
-
-	count = 0;
-	prev->next = NULL;
-
-	while (pac) {
-		prev = pac;
-		pac = pac->next;
-		eap_teap_free_pac(prev);
-		count++;
-	}
-
-	return count;
-}
-
-
-static void eap_teap_pac_get_a_id(struct eap_teap_pac *pac)
-{
-	u8 *pos, *end;
-	u16 type, len;
-
-	pos = pac->pac_info;
-	end = pos + pac->pac_info_len;
-
-	while (end - pos > 4) {
-		type = WPA_GET_BE16(pos);
-		pos += 2;
-		len = WPA_GET_BE16(pos);
-		pos += 2;
-		if (len > (unsigned int) (end - pos))
-			break;
-
-		if (type == PAC_TYPE_A_ID) {
-			os_free(pac->a_id);
-			pac->a_id = os_memdup(pos, len);
-			if (!pac->a_id)
-				break;
-			pac->a_id_len = len;
-		}
-
-		if (type == PAC_TYPE_A_ID_INFO) {
-			os_free(pac->a_id_info);
-			pac->a_id_info = os_memdup(pos, len);
-			if (!pac->a_id_info)
-				break;
-			pac->a_id_info_len = len;
-		}
-
-		pos += len;
-	}
-}
-
-
-/**
- * eap_teap_load_pac_bin - Load PAC entries (binary format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Pointer to root of the PAC list (to be filled)
- * @pac_file: Name of the PAC file/blob to load
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-			  const char *pac_file)
-{
-	const struct wpa_config_blob *blob = NULL;
-	u8 *buf, *end, *pos;
-	size_t len, count = 0;
-	struct eap_teap_pac *pac, *prev;
-
-	*pac_root = NULL;
-
-	if (!pac_file)
-		return -1;
-
-	if (os_strncmp(pac_file, "blob://", 7) == 0) {
-		blob = eap_get_config_blob(sm, pac_file + 7);
-		if (!blob) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned",
-				   pac_file + 7);
-			return 0;
-		}
-		buf = blob->data;
-		len = blob->len;
-	} else {
-		buf = (u8 *) os_readfile(pac_file, &len);
-		if (!buf) {
-			wpa_printf(MSG_INFO,
-				   "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned",
-				   pac_file);
-			return 0;
-		}
-	}
-
-	if (len == 0) {
-		if (!blob)
-			os_free(buf);
-		return 0;
-	}
-
-	if (len < 6 || WPA_GET_BE32(buf) != EAP_TEAP_PAC_BINARY_MAGIC ||
-	    WPA_GET_BE16(buf + 4) != EAP_TEAP_PAC_BINARY_FORMAT_VERSION) {
-		wpa_printf(MSG_INFO, "EAP-TEAP: Invalid PAC file '%s' (bin)",
-			   pac_file);
-		if (!blob)
-			os_free(buf);
-		return -1;
-	}
-
-	pac = prev = NULL;
-	pos = buf + 6;
-	end = buf + len;
-	while (pos < end) {
-		u16 val;
-
-		if (end - pos < 2 + EAP_TEAP_PAC_KEY_LEN + 2 + 2) {
-			pac = NULL;
-			goto parse_fail;
-		}
-
-		pac = os_zalloc(sizeof(*pac));
-		if (!pac)
-			goto parse_fail;
-
-		pac->pac_type = WPA_GET_BE16(pos);
-		pos += 2;
-		os_memcpy(pac->pac_key, pos, EAP_TEAP_PAC_KEY_LEN);
-		pos += EAP_TEAP_PAC_KEY_LEN;
-		val = WPA_GET_BE16(pos);
-		pos += 2;
-		if (val > end - pos)
-			goto parse_fail;
-		pac->pac_opaque_len = val;
-		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
-		if (!pac->pac_opaque)
-			goto parse_fail;
-		pos += pac->pac_opaque_len;
-		if (end - pos < 2)
-			goto parse_fail;
-		val = WPA_GET_BE16(pos);
-		pos += 2;
-		if (val > end - pos)
-			goto parse_fail;
-		pac->pac_info_len = val;
-		pac->pac_info = os_memdup(pos, pac->pac_info_len);
-		if (!pac->pac_info)
-			goto parse_fail;
-		pos += pac->pac_info_len;
-		eap_teap_pac_get_a_id(pac);
-
-		count++;
-		if (prev)
-			prev->next = pac;
-		else
-			*pac_root = pac;
-		prev = pac;
-	}
-
-	if (!blob)
-		os_free(buf);
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %lu PAC entries from '%s' (bin)",
-		   (unsigned long) count, pac_file);
-
-	return 0;
-
-parse_fail:
-	wpa_printf(MSG_INFO, "EAP-TEAP: Failed to parse PAC file '%s' (bin)",
-		   pac_file);
-	if (!blob)
-		os_free(buf);
-	if (pac)
-		eap_teap_free_pac(pac);
-	return -1;
-}
-
-
-/**
- * eap_teap_save_pac_bin - Save PAC entries (binary format)
- * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
- * @pac_root: Root of the PAC list
- * @pac_file: Name of the PAC file/blob
- * Returns: 0 on success, -1 on failure
- */
-int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-			  const char *pac_file)
-{
-	size_t len, count = 0;
-	struct eap_teap_pac *pac;
-	u8 *buf, *pos;
-
-	len = 6;
-	pac = pac_root;
-	while (pac) {
-		if (pac->pac_opaque_len > 65535 ||
-		    pac->pac_info_len > 65535)
-			return -1;
-		len += 2 + EAP_TEAP_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
-			2 + pac->pac_info_len;
-		pac = pac->next;
-	}
-
-	buf = os_malloc(len);
-	if (!buf)
-		return -1;
-
-	pos = buf;
-	WPA_PUT_BE32(pos, EAP_TEAP_PAC_BINARY_MAGIC);
-	pos += 4;
-	WPA_PUT_BE16(pos, EAP_TEAP_PAC_BINARY_FORMAT_VERSION);
-	pos += 2;
-
-	pac = pac_root;
-	while (pac) {
-		WPA_PUT_BE16(pos, pac->pac_type);
-		pos += 2;
-		os_memcpy(pos, pac->pac_key, EAP_TEAP_PAC_KEY_LEN);
-		pos += EAP_TEAP_PAC_KEY_LEN;
-		WPA_PUT_BE16(pos, pac->pac_opaque_len);
-		pos += 2;
-		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
-		pos += pac->pac_opaque_len;
-		WPA_PUT_BE16(pos, pac->pac_info_len);
-		pos += 2;
-		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
-		pos += pac->pac_info_len;
-
-		pac = pac->next;
-		count++;
-	}
-
-	if (eap_teap_write_pac(sm, pac_file, (char *) buf, len)) {
-		os_free(buf);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %lu PAC entries into '%s' (bin)",
-		   (unsigned long) count, pac_file);
-
-	return 0;
-}
diff --git a/src/eap_peer/eap_teap_pac.h b/src/eap_peer/eap_teap_pac.h
deleted file mode 100644
index edf4c57..0000000
--- a/src/eap_peer/eap_teap_pac.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * EAP peer method: EAP-TEAP PAC file processing
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef EAP_TEAP_PAC_H
-#define EAP_TEAP_PAC_H
-
-#include "eap_common/eap_teap_common.h"
-
-struct eap_teap_pac {
-	struct eap_teap_pac *next;
-
-	u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
-	u8 *pac_opaque;
-	size_t pac_opaque_len;
-	u8 *pac_info;
-	size_t pac_info_len;
-	u8 *a_id;
-	size_t a_id_len;
-	u8 *i_id;
-	size_t i_id_len;
-	u8 *a_id_info;
-	size_t a_id_info_len;
-	u16 pac_type;
-};
-
-
-void eap_teap_free_pac(struct eap_teap_pac *pac);
-struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root,
-				       const u8 *a_id, size_t a_id_len,
-				       u16 pac_type);
-int eap_teap_add_pac(struct eap_teap_pac **pac_root,
-		     struct eap_teap_pac **pac_current,
-		     struct eap_teap_pac *entry);
-int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-		      const char *pac_file);
-int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-		      const char *pac_file);
-size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root,
-				  size_t max_len);
-int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root,
-			  const char *pac_file);
-int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root,
-			  const char *pac_file);
-
-#endif /* EAP_TEAP_PAC_H */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 5594216..c539738 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -184,8 +184,6 @@
 		/* RFC 7170 requires TLS v1.2 or newer to be used with TEAP */
 		params->flags |= TLS_CONN_DISABLE_TLSv1_0 |
 			TLS_CONN_DISABLE_TLSv1_1;
-		if (config->teap_anon_dh)
-			params->flags |= TLS_CONN_TEAP_ANON_DH;
 	}
 	if (data->eap_type == EAP_TYPE_FAST ||
 	    data->eap_type == EAP_TYPE_TEAP ||
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index d965a25..0a987e6 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -199,7 +199,6 @@
 	 */
 	int pac_key_refresh_time;
 	int eap_teap_auth;
-	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	enum eap_teap_id {
 		EAP_TEAP_ID_ALLOW_ANY = 0,
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index e32c6e4..d624523 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -1,6 +1,6 @@
 /*
  * EAP-TEAP server (RFC 7170)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2024, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +9,6 @@
 #include "includes.h"
 
 #include "common.h"
-#include "crypto/aes_wrap.h"
 #include "crypto/tls.h"
 #include "crypto/random.h"
 #include "eap_common/eap_teap_common.h"
@@ -20,17 +19,11 @@
 static void eap_teap_reset(struct eap_sm *sm, void *priv);
 
 
-/* Private PAC-Opaque TLV types */
-#define PAC_OPAQUE_TYPE_PAD 0
-#define PAC_OPAQUE_TYPE_KEY 1
-#define PAC_OPAQUE_TYPE_LIFETIME 2
-#define PAC_OPAQUE_TYPE_IDENTITY 3
-
 struct eap_teap_data {
 	struct eap_ssl_data ssl;
 	enum {
 		START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID,
-		PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC,
+		PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING,
 		FAILURE_SEND_RESULT, SUCCESS_SEND_RESULT, SUCCESS, FAILURE
 	} state;
 
@@ -44,34 +37,29 @@
 	u8 crypto_binding_nonce[32];
 	int final_result;
 
+	u8 simck[EAP_TEAP_SIMCK_LEN];
 	u8 simck_msk[EAP_TEAP_SIMCK_LEN];
 	u8 cmk_msk[EAP_TEAP_CMK_LEN];
 	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
 	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
 	int simck_idx;
-	int cmk_emsk_available;
+	bool cmk_emsk_available;
 
-	u8 pac_opaque_encr[16];
 	u8 *srv_id;
 	size_t srv_id_len;
 	char *srv_id_info;
 
 	unsigned int basic_auth_not_done:1;
 	unsigned int inner_eap_not_done:1;
-	int anon_provisioning;
 	int skipped_inner_auth;
-	int send_new_pac; /* server triggered re-keying of Tunnel PAC */
 	struct wpabuf *pending_phase2_resp;
 	struct wpabuf *server_outer_tlvs;
 	struct wpabuf *peer_outer_tlvs;
-	u8 *identity; /* from PAC-Opaque or client certificate */
+	u8 *identity; /* from client certificate */
 	size_t identity_len;
 	int eap_seq;
 	int tnc_started;
 
-	int pac_key_lifetime;
-	int pac_key_refresh_time;
-
 	enum teap_error_codes error_code;
 	enum teap_identity_types cur_id_type;
 
@@ -104,8 +92,6 @@
 		return "PHASE2_METHOD";
 	case CRYPTO_BINDING:
 		return "CRYPTO_BINDING";
-	case REQUEST_PAC:
-		return "REQUEST_PAC";
 	case FAILURE_SEND_RESULT:
 		return "FAILURE_SEND_RESULT";
 	case SUCCESS_SEND_RESULT:
@@ -137,158 +123,6 @@
 }
 
 
-static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
-				      const u8 *client_random,
-				      const u8 *server_random,
-				      u8 *master_secret)
-{
-	struct eap_teap_data *data = ctx;
-	const u8 *pac_opaque;
-	size_t pac_opaque_len;
-	u8 *buf, *pos, *end, *pac_key = NULL;
-	os_time_t lifetime = 0;
-	struct os_time now;
-	u8 *identity = NULL;
-	size_t identity_len = 0;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)",
-		    ticket, len);
-
-	if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket");
-		return 0;
-	}
-
-	pac_opaque_len = WPA_GET_BE16(ticket + 2);
-	pac_opaque = ticket + 4;
-	if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
-	    pac_opaque_len > len - 4) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)",
-			   (unsigned long) pac_opaque_len,
-			   (unsigned long) len);
-		return 0;
-	}
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque",
-		    pac_opaque, pac_opaque_len);
-
-	buf = os_malloc(pac_opaque_len - 8);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque");
-		return 0;
-	}
-
-	if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
-		       (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque");
-		os_free(buf);
-		/*
-		 * This may have been caused by server changing the PAC-Opaque
-		 * encryption key, so just ignore this PAC-Opaque instead of
-		 * failing the authentication completely. Provisioning can now
-		 * be used to provision a new PAC.
-		 */
-		return 0;
-	}
-
-	end = buf + pac_opaque_len - 8;
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque",
-			buf, end - buf);
-
-	pos = buf;
-	while (end - pos > 1) {
-		u8 id, elen;
-
-		id = *pos++;
-		elen = *pos++;
-		if (elen > end - pos)
-			break;
-
-		switch (id) {
-		case PAC_OPAQUE_TYPE_PAD:
-			goto done;
-		case PAC_OPAQUE_TYPE_KEY:
-			if (elen != EAP_TEAP_PAC_KEY_LEN) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Invalid PAC-Key length %d",
-					   elen);
-				os_free(buf);
-				return -1;
-			}
-			pac_key = pos;
-			wpa_hexdump_key(MSG_DEBUG,
-					"EAP-TEAP: PAC-Key from decrypted PAC-Opaque",
-					pac_key, EAP_TEAP_PAC_KEY_LEN);
-			break;
-		case PAC_OPAQUE_TYPE_LIFETIME:
-			if (elen != 4) {
-				wpa_printf(MSG_DEBUG,
-					   "EAP-TEAP: Invalid PAC-Key lifetime length %d",
-					   elen);
-				os_free(buf);
-				return -1;
-			}
-			lifetime = WPA_GET_BE32(pos);
-			break;
-		case PAC_OPAQUE_TYPE_IDENTITY:
-			identity = pos;
-			identity_len = elen;
-			break;
-		}
-
-		pos += elen;
-	}
-done:
-
-	if (!pac_key) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: No PAC-Key included in PAC-Opaque");
-		os_free(buf);
-		return -1;
-	}
-
-	if (identity) {
-		wpa_hexdump_ascii(MSG_DEBUG,
-				  "EAP-TEAP: Identity from PAC-Opaque",
-				  identity, identity_len);
-		os_free(data->identity);
-		data->identity = os_malloc(identity_len);
-		if (data->identity) {
-			os_memcpy(data->identity, identity, identity_len);
-			data->identity_len = identity_len;
-		}
-	}
-
-	if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)",
-			   lifetime, now.sec);
-		data->send_new_pac = 2;
-		/*
-		 * Allow PAC to be used to allow a PAC update with some level
-		 * of server authentication (i.e., do not fall back to full TLS
-		 * handshake since we cannot be sure that the peer would be
-		 * able to validate server certificate now). However, reject
-		 * the authentication since the PAC was not valid anymore. Peer
-		 * can connect again with the newly provisioned PAC after this.
-		 */
-	} else if (lifetime - now.sec < data->pac_key_refresh_time) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds");
-		data->send_new_pac = 1;
-	}
-
-	/* EAP-TEAP uses PAC-Key as the TLS master_secret */
-	os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	os_free(buf);
-
-	return 1;
-}
-
-
 static int eap_teap_derive_key_auth(struct eap_sm *sm,
 				    struct eap_teap_data *data)
 {
@@ -297,13 +131,14 @@
 	/* RFC 7170, Section 5.1 */
 	res = tls_connection_export_key(sm->cfg->ssl_ctx, data->ssl.conn,
 					TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
-					data->simck_msk, EAP_TEAP_SIMCK_LEN);
+					data->simck, EAP_TEAP_SIMCK_LEN);
 	if (res)
 		return res;
 	wpa_hexdump_key(MSG_DEBUG,
 			"EAP-TEAP: session_key_seed (S-IMCK[0])",
-			data->simck_msk, EAP_TEAP_SIMCK_LEN);
-	os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+			data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_msk, data->simck, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_emsk, data->simck, EAP_TEAP_SIMCK_LEN);
 	data->simck_idx = 0;
 	return 0;
 }
@@ -319,9 +154,7 @@
 		   data->simck_idx + 1);
 
 	if (sm->cfg->eap_teap_auth == 1)
-		return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							 data->simck_msk,
-							 data->cmk_msk);
+		goto out; /* no MSK derived in Basic-Password-Auth */
 
 	if (!data->phase2_method || !data->phase2_priv) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
@@ -343,8 +176,8 @@
 						     &emsk_len);
 	}
 
-	res = eap_teap_derive_imck(data->tls_cs,
-				   data->simck_msk, data->simck_emsk,
+out:
+	res = eap_teap_derive_imck(data->tls_cs, data->simck,
 				   msk, msk_len, emsk, emsk_len,
 				   data->simck_msk, data->cmk_msk,
 				   data->simck_emsk, data->cmk_emsk);
@@ -352,8 +185,7 @@
 	bin_clear_free(emsk, emsk_len);
 	if (res == 0) {
 		data->simck_idx++;
-		if (emsk)
-			data->cmk_emsk_available = 1;
+		data->cmk_emsk_available = emsk != NULL;
 	}
 	return 0;
 }
@@ -377,28 +209,6 @@
 		return NULL;
 	}
 
-	/* TODO: Add anon-DH TLS cipher suites (and if one is negotiated,
-	 * enforce inner EAP with mutual authentication to be used) */
-
-	if (tls_connection_set_session_ticket_cb(sm->cfg->ssl_ctx,
-						 data->ssl.conn,
-						 eap_teap_session_ticket_cb,
-						 data) < 0) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: Failed to set SessionTicket callback");
-		eap_teap_reset(sm, data);
-		return NULL;
-	}
-
-	if (!sm->cfg->pac_opaque_encr_key) {
-		wpa_printf(MSG_INFO,
-			   "EAP-TEAP: No PAC-Opaque encryption key configured");
-		eap_teap_reset(sm, data);
-		return NULL;
-	}
-	os_memcpy(data->pac_opaque_encr, sm->cfg->pac_opaque_encr_key,
-		  sizeof(data->pac_opaque_encr));
-
 	if (!sm->cfg->eap_fast_a_id) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured");
 		eap_teap_reset(sm, data);
@@ -424,16 +234,6 @@
 		return NULL;
 	}
 
-	/* PAC-Key lifetime in seconds (hard limit) */
-	data->pac_key_lifetime = sm->cfg->pac_key_lifetime;
-
-	/*
-	 * PAC-Key refresh time in seconds (soft limit on remaining hard
-	 * limit). The server will generate a new PAC-Key when this number of
-	 * seconds (or fewer) of the lifetime remains.
-	 */
-	data->pac_key_refresh_time = sm->cfg->pac_key_refresh_time;
-
 	return data;
 }
 
@@ -457,7 +257,6 @@
 	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
 	forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN);
 	forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN);
-	forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr));
 	bin_clear_free(data, sizeof(*data));
 }
 
@@ -504,8 +303,6 @@
 
 static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data)
 {
-	char cipher[64];
-
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
 
 	if (!data->identity && sm->cfg->eap_teap_auth == 2) {
@@ -525,18 +322,6 @@
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
 		   data->tls_cs);
 
-	if (tls_get_cipher(sm->cfg->ssl_ctx, data->ssl.conn,
-			   cipher, sizeof(cipher)) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: Failed to get cipher information");
-		eap_teap_state(data, FAILURE);
-		return -1;
-	}
-	data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
-
-	if (data->anon_provisioning)
-		wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning");
-
 	if (eap_teap_derive_key_auth(sm, data) < 0) {
 		eap_teap_state(data, FAILURE);
 		return -1;
@@ -626,8 +411,7 @@
 	if (!buf)
 		return NULL;
 
-	if (data->send_new_pac || data->anon_provisioning ||
-	    data->basic_auth_not_done || data->inner_eap_not_done ||
+	if (data->basic_auth_not_done || data->inner_eap_not_done ||
 	    data->phase2_method || sm->cfg->eap_teap_separate_result)
 		data->final_result = 0;
 	else
@@ -663,8 +447,6 @@
 	cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr));
 	cb->version = EAP_TEAP_VERSION;
 	cb->received_version = data->peer_version;
-	/* FIX: RFC 7170 is not clear on which Flags value to use when
-	 * Crypto-Binding TLV is used with Basic-Password-Auth */
 	flags = data->cmk_emsk_available ?
 		TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
 		TEAP_CRYPTO_BINDING_MSK_CMAC;
@@ -714,144 +496,6 @@
 }
 
 
-static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm,
-					  struct eap_teap_data *data)
-{
-	u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
-	u8 *pac_buf, *pac_opaque;
-	struct wpabuf *buf;
-	u8 *pos;
-	size_t buf_len, srv_id_info_len, pac_len;
-	struct teap_tlv_hdr *pac_tlv;
-	struct pac_attr_hdr *pac_info;
-	struct teap_tlv_result *result;
-	struct os_time now;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC");
-
-	if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 ||
-	    os_get_time(&now) < 0)
-		return NULL;
-	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key",
-			pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) +
-		(2 + sm->identity_len) + 8;
-	pac_buf = os_malloc(pac_len);
-	if (!pac_buf)
-		return NULL;
-
-	srv_id_info_len = os_strlen(data->srv_id_info);
-
-	pos = pac_buf;
-	*pos++ = PAC_OPAQUE_TYPE_KEY;
-	*pos++ = EAP_TEAP_PAC_KEY_LEN;
-	os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN);
-	pos += EAP_TEAP_PAC_KEY_LEN;
-
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds",
-		   data->pac_key_lifetime);
-	*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
-	*pos++ = 4;
-	WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
-	pos += 4;
-
-	if (sm->identity) {
-		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity",
-				  sm->identity, sm->identity_len);
-		*pos++ = PAC_OPAQUE_TYPE_IDENTITY;
-		*pos++ = sm->identity_len;
-		os_memcpy(pos, sm->identity, sm->identity_len);
-		pos += sm->identity_len;
-	}
-
-	pac_len = pos - pac_buf;
-	while (pac_len % 8) {
-		*pos++ = PAC_OPAQUE_TYPE_PAD;
-		pac_len++;
-	}
-
-	pac_opaque = os_malloc(pac_len + 8);
-	if (!pac_opaque) {
-		os_free(pac_buf);
-		return NULL;
-	}
-	if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
-		     pac_len / 8, pac_buf, pac_opaque) < 0) {
-		os_free(pac_buf);
-		os_free(pac_opaque);
-		return NULL;
-	}
-	os_free(pac_buf);
-
-	pac_len += 8;
-	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len);
-
-	buf_len = sizeof(*pac_tlv) +
-		sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN +
-		sizeof(struct pac_attr_hdr) + pac_len +
-		data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
-	buf = wpabuf_alloc(buf_len);
-	if (!buf) {
-		os_free(pac_opaque);
-		return NULL;
-	}
-
-	/* Result TLV */
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)");
-	result = wpabuf_put(buf, sizeof(*result));
-	WPA_PUT_BE16((u8 *) &result->tlv_type,
-		     TEAP_TLV_MANDATORY | TEAP_TLV_RESULT);
-	WPA_PUT_BE16((u8 *) &result->length, 2);
-	WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS);
-
-	/* PAC TLV */
-	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV");
-	pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
-	pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC);
-
-	/* PAC-Key */
-	eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN);
-
-	/* PAC-Opaque */
-	eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
-	os_free(pac_opaque);
-
-	/* PAC-Info */
-	pac_info = wpabuf_put(buf, sizeof(*pac_info));
-	pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
-
-	/* PAC-Lifetime (inside PAC-Info) */
-	eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
-	wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
-
-	/* A-ID (inside PAC-Info) */
-	eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
-
-	/* Note: headers may be misaligned after A-ID */
-
-	if (sm->identity) {
-		eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
-				 sm->identity_len);
-	}
-
-	/* A-ID-Info (inside PAC-Info) */
-	eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
-			 srv_id_info_len);
-
-	/* PAC-Type (inside PAC-Info) */
-	eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
-	wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
-
-	/* Update PAC-Info and PAC TLV Length fields */
-	pos = wpabuf_put(buf, 0);
-	pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
-	pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
-
-	return buf;
-}
-
-
 static int eap_teap_encrypt_phase2(struct eap_sm *sm,
 				   struct eap_teap_data *data,
 				   struct wpabuf *plain, int piggyback)
@@ -976,9 +620,6 @@
 				eap_teap_state(data, PHASE2_METHOD);
 		}
 		break;
-	case REQUEST_PAC:
-		req = eap_teap_build_pac(sm, data);
-		break;
 	case FAILURE_SEND_RESULT:
 		req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
 		if (data->error_code)
@@ -1166,18 +807,9 @@
 		}
 
 		eap_teap_state(data, PHASE2_METHOD);
-		if (data->anon_provisioning) {
-			/* TODO: Allow any inner EAP method that provides
-			 * mutual authentication and EMSK derivation (i.e.,
-			 * EAP-pwd or EAP-EKE). */
-			next_vendor = EAP_VENDOR_IETF;
-			next_type = EAP_TYPE_PWD;
-			sm->user_eap_method_index = 0;
-		} else {
-			next_vendor = sm->user->methods[0].vendor;
-			next_type = sm->user->methods[0].method;
-			sm->user_eap_method_index = 1;
-		}
+		next_vendor = sm->user->methods[0].vendor;
+		next_type = sm->user->methods[0].method;
+		sm->user_eap_method_index = 1;
 		wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %u:%u",
 			   next_vendor, next_type);
 		break;
@@ -1509,25 +1141,23 @@
 		return -1;
 	}
 
+	if (data->cmk_emsk_available &&
+	    (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+	} else if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+		   flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK_EMSK");
+		os_memcpy(data->simck, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Selected S-IMCK[j]",
+			data->simck, EAP_TEAP_SIMCK_LEN);
+
 	return 0;
 }
 
 
-static int eap_teap_pac_type(u8 *pac, size_t len, u16 type)
-{
-	struct teap_attr_pac_type *tlv;
-
-	if (!pac || len != sizeof(*tlv))
-		return 0;
-
-	tlv = (struct teap_attr_pac_type *) pac;
-
-	return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE &&
-		be_to_host16(tlv->length) == 2 &&
-		be_to_host16(tlv->pac_type) == type;
-}
-
-
 static void eap_teap_process_phase2_tlvs(struct eap_sm *sm,
 					 struct eap_teap_data *data,
 					 struct wpabuf *in_data)
@@ -1556,34 +1186,6 @@
 		return;
 	}
 
-	if (data->state == REQUEST_PAC) {
-		u16 type, len, res;
-
-		if (!tlv.pac || tlv.pac_len < 6) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: No PAC Acknowledgement received");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		type = WPA_GET_BE16(tlv.pac);
-		len = WPA_GET_BE16(tlv.pac + 2);
-		res = WPA_GET_BE16(tlv.pac + 4);
-
-		if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
-		    res != TEAP_STATUS_SUCCESS) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: PAC TLV did not contain acknowledgement");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		wpa_printf(MSG_DEBUG,
-			   "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded");
-		eap_teap_state(data, SUCCESS);
-		return;
-	}
-
 	if (check_crypto_binding) {
 		if (!tlv.crypto_binding) {
 			wpa_printf(MSG_DEBUG,
@@ -1623,42 +1225,10 @@
 				   "EAP-TEAP: Authentication completed successfully");
 		}
 
-		if (data->anon_provisioning &&
-		    sm->cfg->eap_fast_prov != ANON_PROV &&
-		    sm->cfg->eap_fast_prov != BOTH_PROV) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		if (sm->cfg->eap_fast_prov != AUTH_PROV &&
-		    sm->cfg->eap_fast_prov != BOTH_PROV &&
-		    tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
-		    eap_teap_pac_type(tlv.pac, tlv.pac_len,
-				      PAC_TYPE_TUNNEL_PAC)) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled");
-			eap_teap_state(data, FAILURE);
-			return;
-		}
-
-		if (data->anon_provisioning ||
-		    (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
-		     eap_teap_pac_type(tlv.pac, tlv.pac_len,
-				       PAC_TYPE_TUNNEL_PAC))) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Requested a new Tunnel PAC");
-			eap_teap_state(data, REQUEST_PAC);
-		} else if (data->send_new_pac) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Server triggered re-keying of Tunnel PAC");
-			eap_teap_state(data, REQUEST_PAC);
-		} else if (data->final_result) {
+		if (data->final_result)
 			eap_teap_state(data, SUCCESS);
-		} else if (sm->cfg->eap_teap_separate_result) {
+		else if (sm->cfg->eap_teap_separate_result)
 			eap_teap_state(data, SUCCESS_SEND_RESULT);
-		}
 	}
 
 	if (tlv.basic_auth_resp) {
@@ -1810,7 +1380,7 @@
 	enum eap_type next_type;
 
 	if (data->identity) {
-		/* Used PAC and identity is from PAC-Opaque */
+		/* Identity is from client certificate */
 		os_free(sm->identity);
 		sm->identity = data->identity;
 		data->identity = NULL;
@@ -1823,17 +1393,16 @@
 			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 			eap_teap_state(data, PHASE2_METHOD);
-		} else if (sm->cfg->eap_teap_pac_no_inner ||
-			sm->cfg->eap_teap_auth == 2) {
+		} else if (sm->cfg->eap_teap_auth == 2) {
 			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Used PAC or client certificate and identity already known - skip inner auth");
+				   "EAP-TEAP: Used client certificate and identity already known - skip inner auth");
 			data->skipped_inner_auth = 1;
-			/* FIX: Need to derive CMK here. However, how is that
-			 * supposed to be done? RFC 7170 does not tell that for
-			 * the no-inner-auth case. */
-			eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
-							  data->simck_msk,
-							  data->cmk_msk);
+			if (eap_teap_derive_imck(data->tls_cs, data->simck,
+						 NULL, 0, NULL, 0,
+						 data->simck_msk, data->cmk_msk,
+						 data->simck_emsk,
+						 data->cmk_emsk))
+				return -1;
 			eap_teap_state(data, CRYPTO_BINDING);
 			return 1;
 		} else if (sm->cfg->eap_teap_auth == 1) {
@@ -1880,7 +1449,6 @@
 	case PHASE2_BASIC_AUTH:
 	case PHASE2_METHOD:
 	case CRYPTO_BINDING:
-	case REQUEST_PAC:
 	case SUCCESS_SEND_RESULT:
 		eap_teap_process_phase2(sm, data, data->ssl.tls_in);
 		break;
@@ -2043,9 +1611,7 @@
 	if (!eapKeyData)
 		return NULL;
 
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk,
+	if (eap_teap_derive_eap_msk(data->tls_cs, data->simck,
 				    eapKeyData) < 0) {
 		os_free(eapKeyData);
 		return NULL;
@@ -2068,9 +1634,7 @@
 	if (!eapKeyData)
 		return NULL;
 
-	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
-	 * is used in this derivation */
-	if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
+	if (eap_teap_derive_eap_emsk(data->tls_cs, data->simck,
 				     eapKeyData) < 0) {
 		os_free(eapKeyData);
 		return NULL;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index f3742ea..0bc6f0d 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -247,8 +247,16 @@
 	peer->go_neg_conf = NULL;
 	p2p->go_neg_peer = NULL;
 
+#ifdef CONFIG_PASN
+	if (peer->p2p2 && peer->pasn)
+		wpa_pasn_reset(peer->pasn);
+	os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password));
+	os_memset(p2p->peer_sae_password, 0, sizeof(p2p->peer_sae_password));
+#endif /* CONFIG_PASN */
+
 	os_memset(&res, 0, sizeof(res));
 	res.status = status;
+	res.p2p2 = peer->p2p2;
 	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -962,6 +970,16 @@
 	}
 
 	os_free(dev->bootstrap_params);
+
+	wpabuf_free(dev->action_frame_wrapper);
+
+#ifdef CONFIG_PASN
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+		pasn_data_deinit(dev->pasn);
+	}
+#endif /* CONFIG_PASN */
+
 	wpabuf_free(dev->info.wfd_subelems);
 	wpabuf_free(dev->info.vendor_elems);
 	wpabuf_free(dev->go_neg_conf);
@@ -1850,10 +1868,27 @@
 		params->passphrase[p2p->cfg->passphrase_len] = '\0';
 	}
 	p2p->passphrase_set = 0;
+	params->cipher = WPA_CIPHER_CCMP;
+	if (p2p->cfg->pairing_config.pasn_type & 0xc)
+		params->cipher |= WPA_CIPHER_GCMP_256;
+
+	if (params->p2p2) {
+		os_strlcpy(p2p->dev_sae_password, params->passphrase,
+			   sizeof(p2p->dev_sae_password));
+		os_strlcpy(params->sae_password, p2p->dev_sae_password,
+			   sizeof(params->sae_password));
+	}
+
 	return 0;
 }
 
 
+void p2p_set_go_role(struct p2p_data *p2p, bool val)
+{
+	p2p->go_role = val;
+}
+
+
 void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
 {
 	struct p2p_go_neg_results res;
@@ -1915,8 +1950,57 @@
 	wpabuf_free(peer->go_neg_conf);
 	peer->go_neg_conf = NULL;
 
-	p2p_set_state(p2p, P2P_PROVISIONING);
-	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+#ifdef CONFIG_PASN
+	if (peer->p2p2 && peer->pasn) {
+		res.p2p2 = peer->p2p2;
+		res.akmp = peer->pasn->akmp;
+		res.cipher = peer->pasn->cipher;
+
+		if (res.akmp == WPA_KEY_MGMT_PASN) {
+			if (go) {
+				os_strlcpy(res.sae_password,
+					   p2p->dev_sae_password,
+					   sizeof(res.sae_password));
+			} else {
+				if (!os_strlen(p2p->peer_sae_password)) {
+					p2p_dbg(p2p, "No password from peer GO for P2P2 group formation");
+					return;
+				}
+				os_strlcpy(res.sae_password,
+					   p2p->peer_sae_password,
+					   sizeof(res.sae_password));
+			}
+		} else if (res.akmp == WPA_KEY_MGMT_SAE) {
+			if (peer->role == P2P_ROLE_PAIRING_INITIATOR) {
+				pasn_initiator_pmksa_cache_get(
+					peer->pasn->pmksa,
+					peer->pasn->peer_addr,
+					res.pmkid, res.pmk, &res.pmk_len);
+			} else {
+				pasn_responder_pmksa_cache_get(
+					peer->pasn->pmksa,
+					peer->pasn->peer_addr,
+					res.pmkid, res.pmk, &res.pmk_len);
+			}
+		}
+
+		os_memset(p2p->dev_sae_password, 0,
+			  sizeof(p2p->dev_sae_password));
+		os_memset(p2p->peer_sae_password, 0,
+			  sizeof(p2p->peer_sae_password));
+		wpa_pasn_reset(peer->pasn);
+	}
+#endif /* CONFIG_PASN */
+
+	if (p2p->go_role && peer->p2p2) {
+		p2p->cfg->set_go_security_config(p2p->cfg->cb_ctx, &res);
+		p2p->go_role = false;
+	} else {
+		p2p_set_state(p2p, P2P_PROVISIONING);
+		p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+	}
+
+	forced_memzero(&res, sizeof(res));
 }
 
 
@@ -2998,6 +3082,13 @@
 }
 
 
+void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr)
+{
+	if (p2p && addr)
+		os_memcpy(p2p->cfg->dev_addr, addr, ETH_ALEN);
+}
+
+
 static void p2p_pairing_info_deinit(struct p2p_data *p2p)
 {
 #ifdef CONFIG_PASN
@@ -3032,8 +3123,10 @@
 	os_memcpy(pairing_info->dev_ik.dik_data,
 		  p2p->cfg->pairing_config.dik_data,
 		  p2p->cfg->pairing_config.dik_len);
+	pairing_info->dev_ik.expiration = 24; /* hours */
 
 	p2p_pairing_info_deinit(p2p);
+
 	p2p->pairing_info = pairing_info;
 #ifdef CONFIG_PASN
 	p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init();
@@ -3928,7 +4021,7 @@
 		p2p_invitation_req_cb(p2p, success);
 		break;
 	case P2P_PENDING_INVITATION_RESPONSE:
-		p2p_invitation_resp_cb(p2p, success);
+		p2p_invitation_resp_cb(p2p, dst, success);
 		break;
 	case P2P_PENDING_DEV_DISC_REQUEST:
 		p2p_dev_disc_req_cb(p2p, success);
@@ -4228,7 +4321,7 @@
 				p2p->cfg->invitation_result(
 					p2p->cfg->cb_ctx, -1, NULL, NULL,
 					p2p->invite_peer->info.p2p_device_addr,
-					0, 0);
+					0, 0, NULL, NULL, 0);
 		}
 		p2p_set_state(p2p, P2P_IDLE);
 	}
@@ -4868,6 +4961,108 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+void p2p_set_pairing_setup(struct p2p_data *p2p, int pairing_setup)
+{
+	p2p_dbg(p2p, "Pairing Setup %s",
+		pairing_setup ? "Enabled" : "Disabled");
+	if (pairing_setup) {
+		p2p->cfg->pairing_config.pairing_capable = true;
+		p2p->cfg->pairing_config.enable_pairing_setup = true;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_setup = true;
+	} else {
+		p2p->cfg->pairing_config.pairing_capable = false;
+		p2p->cfg->pairing_config.enable_pairing_setup = false;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_setup = false;
+	}
+}
+
+
+void p2p_set_pairing_cache(struct p2p_data *p2p, int pairing_cache)
+{
+	p2p_dbg(p2p, "Pairing Cache %s",
+		pairing_cache ? "Enabled" : "Disabled");
+	if (pairing_cache) {
+		p2p->cfg->pairing_config.enable_pairing_cache = true;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_cache = true;
+	} else {
+		p2p->cfg->pairing_config.enable_pairing_cache = false;
+		if (p2p->pairing_info)
+			p2p->pairing_info->enable_pairing_cache = false;
+	}
+}
+
+
+void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods)
+{
+	p2p_dbg(p2p, "Bootstraping methods: 0x%x", bootstrap_methods);
+	p2p->cfg->pairing_config.bootstrap_methods = bootstrap_methods;
+	if (p2p->pairing_info)
+		p2p->pairing_info->supported_bootstrap = bootstrap_methods;
+}
+
+
+void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type)
+{
+	p2p_dbg(p2p, "PASN type: 0x%x", pasn_type);
+	p2p->cfg->pairing_config.pasn_type = pasn_type;
+}
+
+
+void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after)
+{
+	p2p_dbg(p2p, "Comeback after: %d", comeback_after);
+	p2p->cfg->comeback_after = comeback_after;
+}
+
+
+void p2p_set_reg_info(struct p2p_data *p2p, u8 val)
+{
+	p2p->cfg->reg_info = val;
+}
+
+
+void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val)
+{
+	p2p_dbg(p2p, "TWT-based P2P Power Mgmt: %s",
+		     val ? "Enabled" : "Disabled");
+	if (val)
+		p2p->cfg->twt_power_mgmt = true;
+	else
+		p2p->cfg->twt_power_mgmt = false;
+}
+
+
+void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val)
+{
+	p2p->cfg->chan_switch_req_enable = val;
+}
+
+
+void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq)
+{
+	u8 op_class, channel;
+
+	if (freq == -1) {
+		p2p->cfg->inv_op_class = 0;
+		p2p->cfg->inv_op_channel = 0;
+		return;
+	}
+
+	if (p2p_freq_to_channel(freq, &op_class, &channel) < 0)
+		return;
+
+	p2p->cfg->inv_op_class = op_class;
+	p2p->cfg->inv_op_channel = channel;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
 				 u8 *op_channel,
 				 struct wpa_freq_range_list *avoid_list,
@@ -5026,6 +5221,28 @@
 }
 
 
+int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr,
+			     const u8 **dik_data, size_t *dik_len, u8 *cipher)
+{
+	if (!p2p || !p2p->peer_dik_len) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P2: Failed to get device identity key for "
+			   MACSTR, MAC2STR(dev_addr));
+		return -1;
+	}
+
+	*dik_data = p2p->peer_dik_data;
+	*dik_len = p2p->peer_dik_len;
+	*cipher = p2p->dik_cipher_version;
+
+	/* Reset DIK length to invalidate DIK for successive iteration of a new
+	 * peer. */
+	p2p->peer_dik_len = 0;
+
+	return 0;
+}
+
+
 void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
 {
 	os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
@@ -5839,6 +6056,16 @@
 }
 
 
+static void p2p_validate_dira(struct p2p_data *p2p, struct p2p_device *dev,
+			      const u8 *dira, u16 dira_len)
+{
+	if (p2p->cfg->validate_dira)
+		p2p->cfg->validate_dira(p2p->cfg->cb_ctx,
+					dev->info.p2p_device_addr,
+					dira, dira_len);
+}
+
+
 struct wpabuf * p2p_usd_elems(struct p2p_data *p2p)
 {
 	struct wpabuf *buf;
@@ -5884,7 +6111,6 @@
 	if (p2p->pairing_info &&
 	    p2p->cfg->pairing_config.pairing_capable &&
 	    p2p->cfg->pairing_config.enable_pairing_cache &&
-	    p2p->cfg->pairing_config.enable_pairing_verification &&
 	    p2p_derive_nonce_tag(p2p) == 0)
 		p2p_buf_add_dira(buf, p2p);
 
@@ -5950,6 +6176,9 @@
 	if (!ether_addr_equal(peer_addr, p2p_dev_addr))
 		os_memcpy(dev->interface_addr, peer_addr, ETH_ALEN);
 
+	if (msg.dira && msg.dira_len)
+		p2p_validate_dira(p2p, dev, msg.dira, msg.dira_len);
+
 	p2p_dbg(p2p, "Updated device entry based on USD frame: " MACSTR
 		" dev_capab=0x%x group_capab=0x%x listen_freq=%d",
 		MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
@@ -5957,6 +6186,973 @@
 
 	p2p->cfg->dev_found(p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
 			    &dev->info, !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 
 	p2p_parse_free(&msg);
 }
+
+
+#ifdef CONFIG_PASN
+
+int p2p_config_sae_password(struct p2p_data *p2p, const char *pw)
+{
+	os_memset(p2p->dev_sae_password, 0, sizeof(p2p->dev_sae_password));
+	if (os_strlen(pw) >= sizeof(p2p->dev_sae_password))
+		return -1;
+
+	os_strlcpy(p2p->dev_sae_password, pw, sizeof(p2p->dev_sae_password));
+	return 0;
+}
+
+
+static int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p,
+				     struct wpabuf *extra_ies,
+				     const struct wpabuf *frame)
+{
+	struct wpabuf *buf, *buf2;
+	size_t len;
+
+	len = 100;
+	if (frame)
+		len += wpabuf_len(frame);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return -1;
+
+	/* P2P Capability Extension attribute */
+	p2p_buf_add_pcea(buf, p2p);
+
+	if (frame) {
+		p2p_dbg(p2p, "Add Action frame wrapper for PASN");
+		wpabuf_put_u8(buf, P2P_ATTR_ACTION_FRAME_WRAPPER);
+		wpabuf_put_le16(buf, wpabuf_len(frame));
+		wpabuf_put_buf(buf, frame);
+	}
+
+	buf2 = p2p_encaps_ie(buf, P2P2_IE_VENDOR_TYPE);
+	wpabuf_free(buf);
+
+	if (wpabuf_tailroom(extra_ies) < wpabuf_len(buf2)) {
+		p2p_err(p2p, "Not enough room for P2P2 IE in PASN extra IEs");
+		wpabuf_free(buf2);
+		return -1;
+	}
+	wpabuf_put_buf(extra_ies, buf2);
+	wpabuf_free(buf2);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_pairing_generate_rsnxe(struct p2p_data *p2p,
+						  int akmp)
+{
+	u32 capab;
+	size_t flen = 0;
+	struct wpabuf *buf;
+
+	capab = BIT(WLAN_RSNX_CAPAB_KEK_IN_PASN);
+
+	if (wpa_key_mgmt_sae(akmp))
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	while (capab >> flen * 8)
+		flen++;
+
+	buf = wpabuf_alloc(2 + flen);
+	if (!buf)
+		return NULL;
+
+	if (wpabuf_tailroom(buf) < 2 + flen) {
+		p2p_dbg(p2p, "wpabuf tail room too small");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+	p2p_dbg(p2p, "RSNXE capabilities: %04x", capab);
+	wpabuf_put_u8(buf, WLAN_EID_RSNX);
+	wpabuf_put_u8(buf, flen);
+	while (flen--) {
+		wpabuf_put_u8(buf, (capab & 0xff));
+		capab = capab >> 8;
+	}
+	return buf;
+}
+
+
+/* SSID used for deriving SAE pt for pairing */
+#define P2P_PAIRING_SSID "516F9A020000"
+
+static void p2p_pairing_set_password(struct pasn_data *pasn, u8 pasn_type,
+				     const char *passphrase)
+{
+	int pasn_groups[4] = { 0 };
+	size_t len;
+
+	if (!passphrase)
+		return;
+
+	len = os_strlen(passphrase);
+
+	if (pasn_type & 0xc && pasn_type & 0x3) {
+		pasn_groups[0] = 20;
+		pasn_groups[1] = 19;
+	} else if (pasn_type & 0xc) {
+		pasn_groups[0] = 20;
+	} else {
+		pasn_groups[0] = 19;
+	}
+	pasn->pt = sae_derive_pt(pasn_groups, (const u8 *) P2P_PAIRING_SSID,
+				 os_strlen(P2P_PAIRING_SSID),
+				 (const u8 *) passphrase, len, NULL);
+	/* Set passphrase for pairing responder to validate PASN auth 1 frame */
+	pasn->password = passphrase;
+}
+
+
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq, bool verify, bool derive_kek)
+{
+	struct pasn_data *pasn;
+	struct wpabuf *rsnxe;
+
+	if (!p2p || !dev)
+		return;
+
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+	} else {
+		dev->pasn = pasn_data_init();
+		if (!dev->pasn)
+			return;
+	}
+
+	pasn = dev->pasn;
+
+	os_memcpy(pasn->own_addr, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+
+	os_memcpy(pasn->bssid, dev->role == P2P_ROLE_PAIRING_INITIATOR ?
+		  pasn->peer_addr : pasn->own_addr, ETH_ALEN);
+
+	pasn->noauth = 1;
+
+	if ((p2p->cfg->pairing_config.pasn_type & 0xc) &&
+	    (dev->info.pairing_config.pasn_type & 0xc)) {
+		pasn->group = 20;
+		pasn->cipher = WPA_CIPHER_GCMP_256;
+		pasn->kek_len = 32;
+		pasn->derive_kek = true;
+	} else {
+		pasn->group = 19;
+		pasn->cipher = WPA_CIPHER_CCMP;
+		pasn->kek_len = 16;
+		pasn->derive_kek = true;
+	}
+
+	if (!derive_kek) {
+		pasn->derive_kek = false;
+		pasn->kek_len = 0;
+	}
+
+	if (dev->password[0]) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+		p2p_pairing_set_password(pasn,
+					 p2p->cfg->pairing_config.pasn_type,
+					 dev->password);
+	} else if (verify) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+	} else {
+		pasn->akmp = WPA_KEY_MGMT_PASN;
+	}
+
+	pasn->rsn_pairwise = pasn->cipher;
+	pasn->wpa_key_mgmt = pasn->akmp;
+
+	rsnxe = p2p_pairing_generate_rsnxe(p2p, pasn->akmp);
+	if (rsnxe) {
+		os_free(pasn->rsnxe_ie);
+		pasn->rsnxe_ie = os_memdup(wpabuf_head_u8(rsnxe),
+					   wpabuf_len(rsnxe));
+		if (!pasn->rsnxe_ie) {
+			wpabuf_free(rsnxe);
+			return;
+		}
+		wpabuf_free(rsnxe);
+	}
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR)
+		pasn->pmksa = p2p->initiator_pmksa;
+	else
+		pasn->pmksa = p2p->responder_pmksa;
+
+	pasn->cb_ctx = p2p->cfg->cb_ctx;
+	pasn->send_mgmt = p2p->cfg->pasn_send_mgmt;
+	pasn->prepare_data_element = p2p->cfg->prepare_data_element;
+	pasn->parse_data_element = p2p->cfg->parse_data_element;
+
+	pasn->freq = freq;
+}
+
+
+int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	int freq;
+	struct p2p_device *dev;
+
+	if (!peer_addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
+	if (freq <= 0) {
+		p2p_dbg(p2p, "No listen/operating frequency known for the peer "
+			MACSTR, MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+	return freq;
+}
+
+
+int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr,
+			     int freq, enum p2p_invite_role role,
+			     const u8 *bssid, const u8 *ssid, size_t ssid_len,
+			     unsigned int force_freq, const u8 *go_dev_addr,
+			     unsigned int pref_freq)
+{
+	struct pasn_data *pasn;
+	struct p2p_device *dev;
+	struct wpabuf *extra_ies, *req;
+	int ret = 0;
+	u8 *pasn_extra_ies = NULL;
+
+	if (!peer_addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	if (p2p_invite(p2p, peer_addr, role, bssid, ssid, ssid_len, force_freq,
+		       go_dev_addr, 1, pref_freq, -1, 1)) {
+		p2p_dbg(p2p, "p2p_invite() failed");
+		return -1;
+	}
+
+	dev->role = P2P_ROLE_PAIRING_INITIATOR;
+	p2p_pasn_initialize(p2p, dev, peer_addr, freq, true, true);
+	pasn = dev->pasn;
+
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, -1);
+	if (!req)
+		return -1;
+
+	p2p_set_state(p2p, P2P_INVITE);
+	p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+	p2p->invite_peer = dev;
+	dev->invitation_reqs++;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		wpabuf_free(req);
+		p2p_dbg(p2p, "Memory allocation failed for extra_ies");
+		return -1;
+	}
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req)) {
+		p2p_dbg(p2p, "Prepare PASN extra IEs failed");
+		ret = -1;
+		goto out;
+	}
+
+	pasn_extra_ies = os_memdup(wpabuf_head_u8(extra_ies),
+				   wpabuf_len(extra_ies));
+	if (!pasn_extra_ies) {
+		p2p_dbg(p2p, "Memory allocation failed for PASN extra IEs");
+		ret = -1;
+		goto out;
+	}
+
+	pasn->extra_ies = pasn_extra_ies;
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+
+	/* Start PASN verify */
+	if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
+			    pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
+			    NULL, 0, NULL, 0, NULL)) {
+		p2p_dbg(p2p, "PASN verify failed");
+		ret = -1;
+	} else {
+		dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
+	}
+out:
+	pasn->extra_ies = NULL;
+	pasn->extra_ies_len = 0;
+	os_free(pasn_extra_ies);
+	wpabuf_free(req);
+	wpabuf_free(extra_ies);
+	return ret;
+}
+
+
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq)
+{
+	struct pasn_data *pasn;
+	struct p2p_device *dev;
+	struct wpabuf *extra_ies, *req;
+	u8 *ies = NULL;
+	int ret = 0;
+	size_t ies_len;
+
+	if (!addr) {
+		p2p_dbg(p2p, "Peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	dev->role = P2P_ROLE_PAIRING_INITIATOR;
+	p2p_pasn_initialize(p2p, dev, addr, freq, false, true);
+	pasn = dev->pasn;
+
+	pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr);
+
+	req = p2p_build_go_neg_req(p2p, dev);
+	if (!req)
+		return -1;
+
+	p2p->go_neg_peer = dev;
+	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		wpabuf_free(req);
+		return -1;
+	}
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req)) {
+		p2p_dbg(p2p, "Failed to prepare PASN extra elements");
+		ret = -1;
+		goto out;
+	}
+
+	ies_len = wpabuf_len(extra_ies);
+	ies = os_memdup(wpabuf_head_u8(extra_ies), ies_len);
+	if (!ies) {
+		ret = -1;
+		goto out;
+	}
+
+	pasn->extra_ies = ies;
+	pasn->extra_ies_len = ies_len;
+
+	/* Start PASN authentication */
+	if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
+			    pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
+			    NULL, 0, NULL, 0, NULL)) {
+		p2p_dbg(p2p, "Failed to start PASN");
+		ret = -1;
+	}
+out:
+	os_free(ies);
+	pasn->extra_ies = NULL;
+	pasn->extra_ies_len = 0;
+	wpabuf_free(req);
+	wpabuf_free(extra_ies);
+	return ret;
+}
+
+
+static int p2p_pasn_handle_action_wrapper(struct p2p_data *p2p,
+					  struct p2p_device *dev,
+					  const struct ieee80211_mgmt *mgmt,
+					  size_t len, int freq, int trans_seq)
+{
+	const u8 *ies;
+	size_t ies_len;
+	size_t data_len = 0;
+	bool derive_kek;
+	const u8 *data = NULL;
+	struct p2p_message msg;
+	struct ieee802_11_elems elems;
+
+	ies = mgmt->u.auth.variable;
+	ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ies, ies_len, &msg)) {
+		p2p_dbg(p2p,
+			"Failed to parse P2P IE from PASN Authentication frame");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.action_frame_wrapper && msg.action_frame_wrapper_len) {
+		data = msg.action_frame_wrapper;
+		data_len = msg.action_frame_wrapper_len;
+		if (data_len >= 2 &&
+		    data[0] == WLAN_ACTION_PUBLIC &&
+		    data[1] == WLAN_PA_VENDOR_SPECIFIC) {
+			data += 2;
+			data_len -= 2;
+			if (data_len < 4 ||
+			    WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) {
+				p2p_parse_free(&msg);
+				return -1;
+			}
+			data += 4;
+			data_len -= 4;
+		} else {
+			p2p_dbg(p2p,
+				"Invalid category in Action frame wrapper in Authentication frame seq %d",
+				trans_seq);
+			p2p_parse_free(&msg);
+			return -1;
+		}
+	}
+
+	if (trans_seq == 1) {
+		if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+					   len - offsetof(struct ieee80211_mgmt,
+							  u.auth.variable),
+					   &elems, 0) == ParseFailed) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed parsing Authentication frame");
+			return -1;
+		}
+		derive_kek = ieee802_11_rsnx_capab_len(
+			elems.rsnxe, elems.rsnxe_len,
+			WLAN_RSNX_CAPAB_KEK_IN_PASN);
+		if (data && data_len >= 1 && data[0] == P2P_INVITATION_REQ) {
+			struct wpabuf *resp;
+
+			resp = p2p_process_invitation_req(p2p, mgmt->sa,
+							  data + 1,
+							  data_len - 1, freq,
+							  true);
+			if (!resp)
+				p2p_dbg(p2p, "No Invitation Response found");
+
+			dev->role = P2P_ROLE_PAIRING_RESPONDER;
+			p2p_pasn_initialize(p2p, dev, mgmt->sa, freq, true,
+					    derive_kek);
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = resp;
+		} else if (data && data_len >= 1 && data[0] == P2P_GO_NEG_REQ) {
+			struct wpabuf *resp;
+
+			if (!derive_kek) {
+				p2p_dbg(p2p, "KEK-in-PASN not set in RSNXE");
+				return -1;
+			}
+			resp = p2p_process_go_neg_req(p2p, mgmt->sa, data + 1,
+						      data_len - 1, freq, true);
+			if (!resp)
+				p2p_dbg(p2p,
+					"No GO Negotiation Response found");
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = resp;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth1");
+		}
+	} else if (trans_seq == 2) {
+		if (data && data_len >= 1 && data[0] == P2P_INVITATION_RESP) {
+			p2p_process_invitation_resp(p2p, mgmt->sa, data + 1,
+						    data_len - 1);
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = NULL;
+		} else if (data && data_len >= 1 &&
+			   data[0] == P2P_GO_NEG_RESP) {
+			struct wpabuf *conf;
+
+			conf = p2p_process_go_neg_resp(p2p, mgmt->sa, data + 1,
+						       data_len - 1, freq,
+						       true);
+			if (!conf)
+				p2p_dbg(p2p, "No GO Negotiation Confirm found");
+			wpabuf_free(dev->action_frame_wrapper);
+			dev->action_frame_wrapper = conf;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth2");
+		}
+	} else if (trans_seq == 3) {
+		if (data && data_len >= 1 && data[0] == P2P_GO_NEG_CONF)
+			p2p_handle_go_neg_conf(p2p, mgmt->sa, data + 1,
+					       data_len - 1, true);
+		else
+			p2p_invitation_resp_cb(p2p, mgmt->sa,
+					       P2P_SEND_ACTION_SUCCESS);
+	}
+	p2p_parse_free(&msg);
+	return 0;
+}
+
+
+static int p2p_pasn_add_encrypted_data(struct p2p_data *p2p,
+				       struct p2p_device *dev,
+				       struct wpabuf *buf)
+{
+	struct pasn_data *pasn;
+	struct wpabuf *p2p2_ie;
+	u8 *dika_len, *p2p2_ie_len;
+	int ret;
+
+	if (!p2p || !dev || !dev->pasn)
+		return 0;
+
+	pasn = dev->pasn;
+
+	if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC &&
+	    !p2p->pairing_info->enable_pairing_cache)
+		return 0;
+
+	p2p2_ie = wpabuf_alloc(100);
+	if (!p2p2_ie)
+		return -1;
+
+	p2p2_ie_len = p2p_buf_add_p2p2_ie_hdr(p2p2_ie);
+
+	if (p2p->pairing_info->enable_pairing_cache) {
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_DEVICE_IDENTITY_KEY);
+		dika_len = wpabuf_put(p2p2_ie, 2);
+
+		wpabuf_put_u8(p2p2_ie,
+			      p2p->pairing_info->dev_ik.cipher_version);
+		wpabuf_put_data(p2p2_ie, p2p->pairing_info->dev_ik.dik_data,
+				p2p->pairing_info->dev_ik.dik_len);
+		wpabuf_put_be32(p2p2_ie, p2p->pairing_info->dev_ik.expiration);
+
+		WPA_PUT_LE16(dika_len,
+			     (u8 *) wpabuf_put(p2p2_ie, 0) - dika_len - 2);
+	}
+
+	if (dev->req_bootstrap_method == P2P_PBMA_OPPORTUNISTIC) {
+		if (!p2p->dev_sae_password[0]) {
+			int password_len;
+
+			/* SAE password is not available as the request is not
+			 * for an existing GO. Pick a random SAE password of
+			 * length between 10 and 20. */
+			password_len = 10 + os_random() % 10;
+			if (p2p_random(p2p->dev_sae_password,
+				       password_len) < 0) {
+				wpabuf_free(p2p2_ie);
+				return -1;
+			}
+			p2p->dev_sae_password[password_len] = '\0';
+		}
+
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD);
+		wpabuf_put_le16(p2p2_ie, os_strlen(p2p->dev_sae_password));
+		wpabuf_put_str(p2p2_ie, p2p->dev_sae_password);
+	}
+
+	p2p_buf_update_ie_hdr(p2p2_ie, p2p2_ie_len);
+
+	ret = pasn_add_encrypted_data(pasn, buf, wpabuf_mhead_u8(p2p2_ie),
+				      wpabuf_len(p2p2_ie));
+	wpabuf_free(p2p2_ie);
+	return ret;
+}
+
+
+int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	int ret = -1;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpabuf *extra_ies;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "PASN: Peer not found " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+	pasn = dev->pasn;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies ||
+	    p2p_prepare_pasn_extra_ie(p2p, extra_ies,
+				      dev->action_frame_wrapper)) {
+		p2p_dbg(p2p, "Failed to prepare PASN extra elements");
+		goto out;
+	}
+
+	if (p2p_pasn_add_encrypted_data(p2p, dev, extra_ies) < 0)
+		p2p_dbg(p2p, "Failed to add PASN encrypted elements");
+
+	pasn->extra_ies = os_memdup(wpabuf_head_u8(extra_ies),
+				    wpabuf_len(extra_ies));
+	if (!pasn->extra_ies)
+		goto out;
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+	ret = 0;
+
+out:
+	wpabuf_free(extra_ies);
+	wpabuf_free(dev->action_frame_wrapper);
+	dev->action_frame_wrapper = NULL;
+
+	return ret;
+}
+
+
+int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len)
+{
+	u8 attr_id;
+	const u8 *pos, *next;
+	u16 rem_len, attr_len;
+
+	if (!p2p || !data || !len)
+		return -1;
+
+	pos = data;
+	rem_len = len;
+
+	if (rem_len < 6 ||
+	    pos[0] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[1] < 4 ||
+	    rem_len < 2 + pos[1] ||
+	    WPA_GET_BE32(&pos[2]) != P2P2_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p,
+			"P2P: P2P2 IE not present in PASN Encrypted Data element");
+		return -1;
+	}
+
+	pos += 6;
+	rem_len -= 6;
+
+	while (rem_len >= 3) {
+		attr_id = *pos++;
+		attr_len = WPA_GET_LE16(pos);
+		pos += 2;
+		rem_len -= 3;
+		if (rem_len < attr_len)
+			return -1;
+		next = pos + attr_len;
+		rem_len -= attr_len;
+
+		switch (attr_id) {
+		case P2P_ATTR_DEVICE_IDENTITY_KEY:
+			if (attr_len < 1) {
+				p2p_dbg(p2p,
+					"Too short Device Identity Key attribute");
+				return -1;
+			}
+			p2p->dik_cipher_version = *pos++;
+			attr_len--;
+			if (p2p->dik_cipher_version ==
+			    DIRA_CIPHER_VERSION_128) {
+				if (attr_len < DEVICE_IDENTITY_KEY_LEN) {
+					p2p_dbg(p2p, "Too short DevIK");
+					return -1;
+				}
+				os_memcpy(p2p->peer_dik_data, pos,
+					  DEVICE_IDENTITY_KEY_LEN);
+				p2p->peer_dik_len = DEVICE_IDENTITY_KEY_LEN;
+				pos += DEVICE_IDENTITY_KEY_LEN;
+				attr_len -= DEVICE_IDENTITY_KEY_LEN;
+			} else {
+				p2p_dbg(p2p,
+					"Unsupported cipher version %u in Device Identity Key attribute",
+					p2p->dik_cipher_version);
+				return -1;
+			}
+			if (attr_len < 4) {
+				p2p_dbg(p2p,
+					"Not enough room for DevIK lifetime");
+				return -1;
+			}
+			p2p->peer_dik_lifetime = WPA_GET_BE32(pos);
+			p2p_dbg(p2p,
+				"Received peer DevIK of length %zu octets and lifetime %u",
+				p2p->peer_dik_len, p2p->peer_dik_lifetime);
+			break;
+		case P2P_ATTR_PASSWORD:
+			if (attr_len < 1 ||
+			    attr_len > sizeof(p2p->peer_sae_password) - 1) {
+				p2p_dbg(p2p,
+					"P2P: Invalid password length %d",
+					attr_len);
+				return -1;
+			}
+			os_memset(p2p->peer_sae_password, 0,
+				  sizeof(p2p->peer_sae_password));
+			os_memcpy(p2p->peer_sae_password, pos, attr_len);
+			break;
+		default:
+			p2p_dbg(p2p,
+				"Unsupported Attribute ID %u in P2P2 IE in PASN Encrypted Data element",
+				attr_id);
+			break;
+		}
+		pos = next;
+	}
+
+	return 0;
+}
+
+
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, bool acked, bool verify)
+{
+	int ret = 0;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, mgmt->da);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found " MACSTR,
+			MAC2STR(mgmt->da));
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+	if (ret != 1 && !acked && pasn->frame)
+		return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
+				       wpabuf_len(pasn->frame), 0, pasn->freq,
+				       1000);
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
+	if (ret != 1)
+		return ret;
+
+	if (verify && dev == p2p->invite_peer)
+		p2p_start_invitation_connect(p2p, dev);
+	else if (dev == p2p->go_neg_peer)
+		p2p_go_complete(p2p, dev);
+
+	return 0;
+}
+
+
+static int p2p_handle_pasn_auth(struct p2p_data *p2p, struct p2p_device *dev,
+				const struct ieee80211_mgmt *mgmt, size_t len,
+				int freq)
+{
+	struct pasn_data *pasn;
+	u8 pasn_type;
+	int pasn_groups[4] = { 0 };
+	u16 auth_alg, auth_transaction, status_code;
+
+	if (!p2p || !dev || !dev->pasn)
+		return -1;
+
+	if (os_memcmp(mgmt->da, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "PASN Responder: Not our frame");
+		return -1;
+	}
+
+	if (len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+		return -1;
+
+	pasn = dev->pasn;
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (status_code != WLAN_STATUS_SUCCESS &&
+	    status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		p2p_dbg(p2p, "PASN: Authentication rejected - status=%u",
+			status_code);
+		return -1;
+	}
+
+	if (auth_alg != WLAN_AUTH_PASN || auth_transaction == 2) {
+		p2p_dbg(p2p,
+			"PASN Responder: Not a PASN frame or unexpected Authentication frame, auth_alg=%d",
+			auth_alg);
+		return -1;
+	}
+	if (auth_transaction == 1) {
+		pasn_type = p2p->cfg->pairing_config.pasn_type;
+		if (pasn_type & 0xc && pasn_type & 0x3) {
+			pasn_groups[0] = 20;
+			pasn_groups[1] = 19;
+		} else if (pasn_type & 0xc) {
+			pasn_groups[0] = 20;
+		} else {
+			pasn_groups[0] = 19;
+		}
+		pasn->pasn_groups = pasn_groups;
+
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 1 action wrapper failed");
+			return -1;
+		}
+		if (handle_auth_pasn_1(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len, false) < 0) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 1 failed");
+			return -1;
+		}
+	} else if (auth_transaction == 3) {
+		if (handle_auth_pasn_3(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len) < 0) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 3 failed");
+			return -1;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		p2p_pasn_store_ptk(p2p, &pasn->ptk);
+#endif /* CONFIG_TESTING_OPTIONS */
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Responder: Handle Auth 3 action wrapper failed");
+			/* Drop keying material from a failed pairing attempt */
+			os_memset(p2p->peer_dik_data, 0,
+				  sizeof(p2p->peer_dik_data));
+			os_memset(p2p->peer_sae_password, 0,
+				  sizeof(p2p->peer_sae_password));
+			return -1;
+		}
+		forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
+	}
+	return 0;
+}
+
+
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq)
+{
+	int ret = 0;
+	u8 auth_transaction;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpa_pasn_params_data pasn_data;
+
+	dev = p2p_get_device(p2p, mgmt->sa);
+	if (!dev) {
+		p2p_dbg(p2p, "PASN: Peer not found " MACSTR,
+			MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	if (!dev->pasn) {
+		p2p_dbg(p2p, "PASN: Uninitialized");
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
+	pasn_register_callbacks(pasn, p2p->cfg->cb_ctx,
+				p2p->cfg->pasn_send_mgmt, NULL);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR && auth_transaction == 2) {
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p,
+				"PASN Initiator: Handle Auth 2 action wrapper failed");
+			return -1;
+		}
+		ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len,
+				       &pasn_data);
+		if (ret < 0) {
+			p2p_dbg(p2p, "PASN: wpa_pasn_auth_rx() failed");
+			dev->role = P2P_ROLE_IDLE;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		p2p_pasn_store_ptk(p2p, &pasn->ptk);
+#endif /* CONFIG_TESTING_OPTIONS */
+		forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
+	} else {
+		ret = p2p_handle_pasn_auth(p2p, dev, mgmt, len, freq);
+	}
+	return ret;
+}
+
+
+void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst,
+			    const u8 *pmk, size_t pmk_len, const u8 *pmkid)
+{
+	pasn_initiator_pmksa_cache_add(p2p->initiator_pmksa, src, dst, pmk,
+				       pmk_len, pmkid);
+	pasn_responder_pmksa_cache_add(p2p->responder_pmksa, src, dst, pmk,
+				       pmk_len, pmkid);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk)
+{
+	u8 *pos;
+
+	if (ptk->ptk_len > sizeof(p2p->pasn_ptk)) {
+		p2p_dbg(p2p, "P2P PASN PTK exceeds: (len=%ld)", ptk->ptk_len);
+		return;
+	}
+
+	pos = p2p->pasn_ptk;
+	p2p->pasn_ptk_len = ptk->ptk_len;
+	if (ptk->kck_len) {
+		os_memcpy(pos, ptk->kck, ptk->kck_len);
+		pos += ptk->kck_len;
+	}
+	if (ptk->kek_len) {
+		os_memcpy(pos, ptk->kek, ptk->kek_len);
+		pos += ptk->kek_len;
+	}
+	if (ptk->tk_len) {
+		os_memcpy(pos, ptk->tk, ptk->tk_len);
+		pos += ptk->tk_len;
+	}
+	if (ptk->kdk_len) {
+		os_memcpy(pos, ptk->kdk, ptk->kdk_len);
+		pos += ptk->kdk_len;
+	}
+}
+
+
+int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len)
+{
+	if (!p2p || !p2p->pasn_ptk_len)
+		return -1;
+
+	*buf_len = p2p->pasn_ptk_len;
+	*buf = p2p->pasn_ptk;
+	return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 5b5c7dd..60a4a34 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -11,6 +11,7 @@
 
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
+#include "common/wpa_common.h"
 
 #define DEVICE_IDENTITY_KEY_MAX_LEN 64
 #define DEVICE_IDENTITY_KEY_LEN 16
@@ -181,6 +182,41 @@
 	 * peer_config_timeout - Peer configuration timeout (in 10 msec units)
 	 */
 	unsigned int peer_config_timeout;
+
+	/**
+	 * p2p2 - Whether this group uses P2P2
+	 */
+	bool p2p2;
+
+	/**
+	 * akmp - The negotiated PASN AKMP for P2P2
+	 */
+	int akmp;
+
+	/**
+	 * cipher - Pairwise cipher(s) for the group for P2P2
+	 */
+	int cipher;
+
+	/**
+	 * pmkid - PMKID for P2P2 when PMK is derived as part of pairing
+	 */
+	u8 pmkid[PMKID_LEN];
+
+	/**
+	 * pmk - PMK for P2P2 when PMK is derived as part of pairing
+	 */
+	u8 pmk[PMK_LEN_MAX];
+
+	/**
+	 * pmk_len - Length of @pmk in octets
+	 */
+	size_t pmk_len;
+
+	/**
+	 * sae_password - SAE password for the group (P2P2)
+	 */
+	char sae_password[100];
 };
 
 struct p2ps_provision {
@@ -350,11 +386,6 @@
 	bool enable_pairing_cache;
 
 	/**
-	 * Enable P2P pairing verification with cached NIK/NPK
-	 */
-	bool enable_pairing_verification;
-
-	/**
 	 * P2P bootstrapping methods supported
 	 */
 	u16 bootstrap_methods;
@@ -691,6 +722,23 @@
 	u16 comeback_after;
 
 	/**
+	 * chan_switch_req_enable - Enable P2P client channel switch request
+	 */
+	bool chan_switch_req_enable;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/**
+	 * Operating class for own operational channel in Invitation Response
+	 */
+	u8 inv_op_class;
+
+	/**
+	 * inv_op_channel - Own operational channel in Invitation Response
+	 */
+	u8 inv_op_channel;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -918,6 +966,20 @@
 	void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
 
 	/**
+	 * set_go_security_config - Set security configuration for P2P GO
+	 * @ctx: Callback context from cb_ctx
+	 * @res: GO Negotiation results
+	 *
+	 * This callback is used to set PMK/passphrase derived during PASN
+	 * authentication with a P2P client. This will fetch an active P2P group
+	 * owner instance and configure PMKSA in case of password based PASN, or
+	 * configures the passphrase and derive PT in case of unauthenticated
+	 * PASN.
+	 */
+	void (*set_go_security_config)(void *ctx,
+				       struct p2p_go_neg_results *res);
+
+	/**
 	 * sd_request - Callback on Service Discovery Request
 	 * @ctx: Callback context from cb_ctx
 	 * @freq: Frequency (in MHz) of the channel
@@ -1027,6 +1089,8 @@
 	 * @channels: Available operating channels for the group
 	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
 	 *	used
+	 * @p2p2: Whether invitation request was wrapped in PASN authentication
+	 * received from a P2P2 device
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -1049,7 +1113,7 @@
 				 size_t ssid_len, int *go, u8 *group_bssid,
 				 int *force_freq, int persistent_group,
 				 const struct p2p_channels *channels,
-				 int dev_pw_id);
+				 int dev_pw_id, bool p2p2);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -1071,7 +1135,8 @@
 	void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
 				    const u8 *ssid, size_t ssid_len,
 				    const u8 *go_dev_addr, u8 status,
-				    int op_freq);
+				    int op_freq, const u8 *pmkid, const u8 *pmk,
+				    size_t pmk_len);
 
 	/**
 	 * invitation_result - Callback on Invitation result
@@ -1092,7 +1157,9 @@
 	 */
 	void (*invitation_result)(void *ctx, int status, const u8 *bssid,
 				  const struct p2p_channels *channels,
-				  const u8 *addr, int freq, int peer_oper_freq);
+				  const u8 *addr, int freq, int peer_oper_freq,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len);
 
 	/**
 	 * go_connected - Check whether we are connected to a GO
@@ -1276,6 +1343,53 @@
 	 */
 	void (*bootstrap_completed)(void *ctx, const u8 *addr,
 				    enum p2p_status_code status, int freq);
+
+	/**
+	 * validate_dira - Indicate reception of DIRA to be validated against a
+	 *	list of available device identity keys
+	 * @ctx: Callback context from cb_ctx
+	 * @peer_addr: P2P Device address of the peer
+	 * @dira: DIRA attribute present in the USD frames
+	 * @dira_len: Length of DIRA
+	 *
+	 * This function can be used to validate DIRA and configure PMK of a
+	 * paired/persistent peer from configuration. The handler function is
+	 * expected to call p2p_pasn_pmksa_set_pmk() to set the PMK/PMKID in
+	 * case a matching entry is found.
+	 */
+	void (*validate_dira)(void *ctx, const u8 *peer_addr,
+			      const u8 *dira, size_t dira_len);
+
+	/**
+	 * pasn_send_mgmt - Function handler to transmit a Management frame
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Frame to transmit
+	 * @data_len: Length of frame to transmit
+	 * @noack: No ack flag
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @wait: How many milliseconds to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
+			      int noack, unsigned int freq, unsigned int wait);
+
+	/**
+	 * prepare_data_element - Function handler to update protocol specific
+	 *	elements in PASN authentication frames
+	 * @ctx: Callback context from cb_ctx
+	 * @peer_addr: Peer MAC address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*prepare_data_element)(void *ctx, const u8 *peer_addr);
+
+	/**
+	 * parse_data_element - Function handler to parse P2P data element
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Data to be parsed
+	 * @len: Length of data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*parse_data_element)(void *ctx, const u8 *data, size_t len);
 };
 
 
@@ -1609,12 +1723,14 @@
  *	force_freq == 0)
  * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
  *	case or -1 if not used
+ * @p2p2: Operating in P2P2 mode
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id,
+	       bool p2p2);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1855,6 +1971,11 @@
  */
 struct p2p_group_config {
 	/**
+	 * p2p2 - Whether the group was formed using P2P2
+	 */
+	bool p2p2;
+
+	/**
 	 * persistent_group - Whether the group is persistent
 	 * 0 = not a persistent group
 	 * 1 = persistent group without persistent reconnect
@@ -2101,6 +2222,18 @@
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
 
 /**
+ * p2p_set_go_role - Set the current role of P2P device
+ * @p2p: P2P module context from p2p_init()
+ * @val: 1 if P2P GO, 0 to reset the role variable
+ *
+ * This role is configured as P2P GO when authorizing a P2P Client to join the
+ * group. Once PASN authentication with GO negotiation with predefined GO intent
+ * values (15 for P2P GO) is completed, the role helps to configure PMK derived
+ * during the PASN authentication.
+ */
+void p2p_set_go_role(struct p2p_data *p2p, bool val);
+
+/**
  * p2p_get_group_capab - Get Group Capability from P2P IE data
  * @p2p_ie: P2P IE(s) contents
  * Returns: Group Capability
@@ -2200,6 +2333,8 @@
 			   u8 *iface_addr);
 int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
 			   u8 *dev_addr);
+int p2p_get_dev_identity_key(struct p2p_data *p2p, const u8 *dev_addr,
+			     const u8 **dik_data, size_t *dik_len, u8 *cipher);
 
 void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
 
@@ -2583,4 +2718,35 @@
 void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len,
 			   const u8 *peer_addr, unsigned int freq);
 
+void p2p_set_pairing_setup(struct p2p_data *p2p, int pairing_setup);
+void p2p_set_pairing_cache(struct p2p_data *p2p, int pairing_cache);
+void p2p_set_bootstrapmethods(struct p2p_data *p2p, int bootstrap_methods);
+void p2p_set_pasn_type(struct p2p_data *p2p, u8 pasn_type);
+void p2p_set_comeback_after(struct p2p_data *p2p, int comeback_after);
+void p2p_set_reg_info(struct p2p_data *p2p, u8 val);
+void p2p_set_twt_power_mgmt(struct p2p_data *p2p, int val);
+void p2p_set_dev_addr(struct p2p_data *p2p, const u8 *addr);
+void p2p_set_chan_switch_req_enable(struct p2p_data *p2p, bool val);
+void p2p_set_invitation_op_freq(struct p2p_data *p2p, int freq);
+
+int p2p_get_listen_freq(struct p2p_data *p2p, const u8 *peer_addr);
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq);
+int p2p_initiate_pasn_verify(struct p2p_data *p2p, const u8 *peer_addr,
+			     int freq, enum p2p_invite_role role,
+			     const u8 *bssid, const u8 *ssid, size_t ssid_len,
+			     unsigned int force_freq, const u8 *go_dev_addr,
+			     unsigned int pref_freq);
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq);
+int p2p_prepare_data_element(struct p2p_data *p2p, const u8 *peer_addr);
+int p2p_parse_data_element(struct p2p_data *p2p, const u8 *data, size_t len);
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, bool acked, bool verify);
+int p2p_config_sae_password(struct p2p_data *p2p, const char *pw);
+void p2p_pasn_pmksa_set_pmk(struct p2p_data *p2p, const u8 *src, const u8 *dst,
+			    const u8 *pmk, size_t pmk_len, const u8 *pmkid);
+void p2p_set_store_pasn_ptk(struct p2p_data *p2p, u8 val);
+void p2p_pasn_store_ptk(struct p2p_data *p2p, struct wpa_ptk *ptk);
+int p2p_pasn_get_ptk(struct p2p_data *p2p, const u8 **buf, size_t *buf_len);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index ddadd34..343566d 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -741,6 +741,9 @@
 	if (p2p->cfg->dfs_owner)
 		capability_info |= P2P_PCEA_DFS_OWNER;
 
+	if (p2p->cfg->chan_switch_req_enable)
+		capability_info |= P2P_PCEA_CLI_REQ_CS;
+
 	if (p2p->cfg->pairing_config.pairing_capable)
 		capability_info |= P2P_PCEA_PAIRING_CAPABLE;
 
@@ -806,8 +809,7 @@
 	struct p2p_id_key *dev_ik;
 
 	if (!p2p->cfg->pairing_config.pairing_capable ||
-	    !p2p->cfg->pairing_config.enable_pairing_cache ||
-	    !p2p->cfg->pairing_config.enable_pairing_verification)
+	    !p2p->cfg->pairing_config.enable_pairing_cache)
 		return;
 
 	dev_ik = &p2p->pairing_info->dev_ik;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index c036f92..2659787 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -205,11 +205,28 @@
 }
 
 
+struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p,
+					struct wpabuf *p2p2_ie, int freq)
+{
+	u8 *len;
+
+	wpabuf_put_u8(p2p2_ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(p2p2_ie, 1);
+	wpabuf_put_be32(p2p2_ie, P2P2_IE_VENDOR_TYPE);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header");
+	p2p_buf_add_pcea(p2p2_ie, p2p);
+	*len = (u8 *) wpabuf_put(p2p2_ie, 0) - len - 1;
+
+	return p2p2_ie;
+}
+
+
 static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
 	struct wpabuf *ie;
 	u8 *len;
 	size_t extra = 0;
+	struct wpabuf *p2p2_ie;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->p2p->wfd_ie_beacon)
@@ -220,7 +237,7 @@
 	    group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
 		extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
 
-	ie = wpabuf_alloc(257 + extra);
+	ie = wpabuf_alloc(500 + extra);
 	if (ie == NULL)
 		return NULL;
 
@@ -240,6 +257,17 @@
 	p2p_group_add_noa(ie, group->noa);
 	p2p_buf_update_ie_hdr(ie, len);
 
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(ie);
+			return NULL;
+		}
+
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		ie = wpabuf_concat(p2p2_ie, ie);
+	}
+
 	return ie;
 }
 
@@ -443,6 +471,7 @@
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
+	struct wpabuf *p2p2_ie;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -474,7 +503,16 @@
 		ie = wpabuf_concat(wfd, ie);
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(ie);
+			return NULL;
+		}
 
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		ie = wpabuf_concat(p2p2_ie, ie);
+	}
 	return ie;
 }
 
@@ -648,6 +686,7 @@
 	struct wpabuf *resp;
 	u8 *rlen;
 	size_t extra = 0;
+	struct wpabuf *p2p2_ie;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->wfd_ie)
@@ -683,6 +722,17 @@
 		p2p_buf_add_status(resp, status);
 	p2p_buf_update_ie_hdr(resp, rlen);
 
+	if (group->cfg->p2p2) {
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie) {
+			wpabuf_free(resp);
+			return NULL;
+		}
+
+		p2p_group_build_p2p2_ie(group->p2p, p2p2_ie, group->cfg->freq);
+		resp = wpabuf_concat(p2p2_ie, resp);
+	}
+
 	return resp;
 }
 
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 808bb96..a54e375 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -37,6 +37,13 @@
 	REMOTE_GO
 };
 
+/* Enumeration for P2P device current role */
+enum p2p_role {
+	P2P_ROLE_IDLE = 0,
+	P2P_ROLE_PAIRING_INITIATOR,
+	P2P_ROLE_PAIRING_RESPONDER,
+};
+
 /**
  * struct bootstrap_params - P2P Device bootstrap request parameters
  */
@@ -183,6 +190,21 @@
 
 	/* Password for P2P2 GO negotiation */
 	char password[100];
+
+	/* PASN data structure */
+	struct pasn_data *pasn;
+	struct wpabuf *action_frame_wrapper;
+
+	/* Device role */
+	enum p2p_role role;
+
+	/* Invitation parameters for P2P2 */
+	bool inv_reject;
+	u8 inv_status;
+	int inv_freq;
+	int inv_peer_oper_freq;
+	u8 inv_bssid[ETH_ALEN];
+	bool inv_all_channels;
 };
 
 struct p2p_sd_query {
@@ -199,6 +221,8 @@
 	int akmp;
 	/* Cipher version type */
 	int cipher_version;
+	/* DevIK expiration time in hours */
+	u32 expiration;
 	/* Buffer to hold the DevIK */
 	u8 dik_data[DEVICE_IDENTITY_KEY_MAX_LEN];
 	/* Length of DevIK */
@@ -637,6 +661,46 @@
 	struct rsn_pmksa_cache *initiator_pmksa;
 	/* Pairing responder PMKSA cache */
 	struct rsn_pmksa_cache *responder_pmksa;
+
+	/* DevIK variables: Cipher version, DevIK, and its lifetime
+	 * These are fetched from the P2P2 included in the PASN Encrypted Data
+	 * element during P2P2 group negotiation with PASN Authentication
+	 * frames. These values are stored in struct p2p_data for an ongoing GO
+	 * negotiation or join-a-group operation with the assumption that these
+	 * operations cannot happen in parallel with multiple peers. After
+	 * successful group formation and connection, these are moved to
+	 * wpa_supplicant configuration if the connection is persistent. */
+	u8 dik_cipher_version;
+	u8 peer_dik_data[DEVICE_IDENTITY_KEY_MAX_LEN];
+	size_t peer_dik_len;
+	unsigned int peer_dik_lifetime;
+
+	/* Password used during an ongoing group formation after opportunistic
+	 * PASN authentication or while joining an existing group. This will be
+	 * moved to a more permanent location from struct p2p_data at the
+	 * conclusion of a successful pairing. */
+	char dev_sae_password[100];
+	char peer_sae_password[100];
+
+	/* Variable used to know the role of the device in a given instance.
+	 * go_role variable is set while authorizing a P2P Client for PASN
+	 * authentication with predefined GO intent value for GO (15 for
+	 * P2P-GO). Once the authentication is completed and security
+	 * configuration is done, this variable is reset to false.
+	 */
+	bool go_role;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/**
+	 * PASN PTK of recent auth
+	 */
+	u8 pasn_ptk[128];
+
+	/**
+	 * PASN PTK length
+	 */
+	size_t pasn_ptk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 /**
@@ -898,6 +962,8 @@
 				   const struct weighted_pcl *pref_freq_list,
 				   unsigned int size);
 struct wpabuf * p2p_encaps_ie(const struct wpabuf *subelems, u32 ie_type);
+struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p,
+					struct wpabuf *p2p2_ie, int freq);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -951,19 +1017,23 @@
 		      struct p2p_device *dev);
 
 /* p2p_invitation.c */
+struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+					 struct p2p_device *peer,
+					 const u8 *go_dev_addr, int dev_pw_id);
 void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa,
 			       const u8 *data, size_t len, int rx_freq);
 void p2p_handle_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				const u8 *data, size_t len);
 struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 					   const u8 *data, size_t len,
-					   int rx_freq);
+					   int rx_freq, bool p2p2);
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 				 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
 		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
-void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *dst, int success);
+void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev);
 
 /* p2p_dev_disc.c */
 void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
@@ -1021,6 +1091,9 @@
 			     struct p2p_channels *res, bool go);
 
 void p2p_sd_query_cb(struct p2p_data *p2p, int success);
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq, bool verify,
+			 bool derive_kek);
 
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 3fd66c2..766b63e 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -11,14 +11,17 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "common/wpa_common.h"
+#include "pasn/pasn_common.h"
 #include "p2p_i.h"
 #include "p2p.h"
 
 
-static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
-						struct p2p_device *peer,
-						const u8 *go_dev_addr,
-						int dev_pw_id)
+struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+					 struct p2p_device *peer,
+					 const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -100,7 +103,7 @@
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
 
-	if (dev_pw_id >= 0) {
+	if (dev_pw_id >= 0 && !peer->p2p2) {
 		/* WSC IE in Invitation Request for NFC static handover */
 		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
 	}
@@ -183,7 +186,7 @@
 
 struct wpabuf * p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 					   const u8 *data, size_t len,
-					   int rx_freq)
+					   int rx_freq, bool p2p2)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
@@ -268,7 +271,8 @@
 			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
 			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
 			&go, group_bssid, &op_freq, persistent, &intersection,
-			msg.dev_password_id_present ? msg.dev_password_id : -1);
+			msg.dev_password_id_present ? msg.dev_password_id : -1,
+			p2p2);
 	}
 
 	if (go) {
@@ -311,6 +315,17 @@
 		p2p_dbg(p2p, "Own default op_class %d channel %d",
 			p2p->op_reg_class, p2p->op_channel);
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if (p2p->cfg->inv_op_class) {
+			/* Override configuration as a starting point */
+			p2p->op_reg_class = p2p->cfg->inv_op_class;
+			p2p->op_channel = p2p->cfg->inv_op_channel;
+			p2p_dbg(p2p,
+				"Override Invitation op_class %d channel %d",
+				p2p->op_reg_class, p2p->op_channel);
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		/* Use peer preference if specified and compatible */
 		if (msg.operating_channel) {
 			int req_freq;
@@ -422,7 +437,7 @@
 	int freq;
 	struct wpabuf *resp;
 
-	resp = p2p_process_invitation_req(p2p, sa, data, len, rx_freq);
+	resp = p2p_process_invitation_req(p2p, sa, data, len, rx_freq, false);
 	if (!resp)
 		return;
 
@@ -451,6 +466,7 @@
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	struct p2p_channels intersection, *channels = NULL;
+	bool all_channels = false;
 
 	p2p_dbg(p2p, "Received Invitation Response from " MACSTR,
 		MAC2STR(sa));
@@ -530,14 +546,17 @@
 #endif /* CONFIG_P2P_STRICT */
 		/* Try to survive without peer channel list */
 		channels = &p2p->channels;
+		all_channels = true;
 	} else if (!msg.channel_list) {
 		/* Non-success cases are not required to include Channel List */
 		channels = &p2p->channels;
+		all_channels = true;
 	} else if (p2p_peer_channels_check(p2p, &p2p->channels, dev,
 					   msg.channel_list,
 					   msg.channel_list_len) < 0) {
 		p2p_dbg(p2p, "No common channels found");
 		p2p_parse_free(&msg);
+		dev->inv_reject = true;
 		return;
 	} else {
 		p2p_channels_intersect(&p2p->channels, &dev->channels,
@@ -566,17 +585,74 @@
 		 */
 		p2p_check_pref_chan(p2p, 0, dev, &msg);
 
+		if (dev->p2p2) {
+			dev->inv_freq = freq;
+			dev->inv_status = *msg.status;
+			dev->inv_all_channels = all_channels;
+			dev->inv_peer_oper_freq = peer_oper_freq;
+			if (msg.group_bssid)
+				os_memcpy(dev->inv_bssid, msg.group_bssid,
+					  ETH_ALEN);
+			goto out;
+		}
+
 		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
 					    msg.group_bssid, channels, sa,
-					    freq, peer_oper_freq);
+					    freq, peer_oper_freq, NULL, NULL,
+					    0);
 	}
 
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+	p2p->invite_peer = NULL;
+
+out:
 	p2p_parse_free(&msg);
+}
+
+
+#ifdef CONFIG_PASN
+void p2p_start_invitation_connect(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	size_t pmk_len = 0;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	struct p2p_channels intersection;
+	const struct p2p_channels *inv_channels;
+
+	if (!p2p || !dev || dev->inv_reject || !dev->pasn)
+		return;
+
+	if (dev->inv_all_channels) {
+		inv_channels = &p2p->channels;
+	} else {
+		p2p_channels_intersect(&p2p->channels, &dev->channels,
+				       &intersection);
+		inv_channels = &intersection;
+	}
+
+	pasn_initiator_pmksa_cache_get(dev->pasn->pmksa, dev->pasn->peer_addr,
+				       pmkid, pmk, &pmk_len);
+
+	wpa_pasn_reset(dev->pasn);
+	p2p_dbg(p2p, "Invitation connect: msg status %d", dev->inv_status);
+	if (p2p->cfg->invitation_result)
+		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, dev->inv_status,
+					    dev->inv_bssid, inv_channels,
+					    dev->info.p2p_device_addr,
+					    dev->inv_freq,
+					    dev->inv_peer_oper_freq, pmkid,
+					    pmk, pmk_len);
+
+	/* Reset PMK and PMKID from stack */
+	forced_memzero(pmkid, sizeof(pmkid));
+	forced_memzero(pmk, sizeof(pmk));
 
 	p2p_clear_timeout(p2p);
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p->invite_peer = NULL;
 }
+#endif /* CONFIG_PASN */
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
@@ -647,8 +723,26 @@
 }
 
 
-void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+void p2p_invitation_resp_cb(struct p2p_data *p2p, const u8 *peer, int success)
 {
+	size_t pmk_len = 0;
+	const u8 *pmkid = NULL, *pmk = NULL;
+
+#ifdef CONFIG_PASN
+	u8 _pmkid[PMKID_LEN];
+	u8 _pmk[PMK_LEN_MAX];
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer);
+	if (dev && dev->pasn) {
+		pasn_responder_pmksa_cache_get(dev->pasn->pmksa,
+					       dev->pasn->peer_addr, _pmkid,
+					       _pmk, &pmk_len);
+		pmkid = _pmkid;
+		pmk = _pmk;
+	}
+#endif /* CONFIG_PASN */
+
 	p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success);
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 
@@ -662,15 +756,23 @@
 					      p2p->inv_ssid, p2p->inv_ssid_len,
 					      p2p->inv_go_dev_addr,
 					      p2p->inv_status,
-					      p2p->inv_op_freq);
+					      p2p->inv_op_freq, pmkid, pmk,
+					      pmk_len);
 	}
+
+#ifdef CONFIG_PASN
+	/* Reset PMK and PMKID from stack */
+	forced_memzero(_pmkid, sizeof(_pmkid));
+	forced_memzero(_pmk, sizeof(_pmk));
+#endif /* CONFIG_PASN */
 }
 
 
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
 	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
 	       unsigned int force_freq, const u8 *go_dev_addr,
-	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id,
+	       bool p2p2)
 {
 	struct p2p_device *dev;
 
@@ -738,5 +840,8 @@
 	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
 	p2p->inv_ssid_len = ssid_len;
 	p2p->inv_persistent = persistent_group;
+	if (p2p2)
+		return 0;
+
 	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index fb20313..a55e7e6 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -703,7 +703,6 @@
 
 	if (dev->info.pcea_cap_info & P2P_PCEA_PMK_CACHING) {
 		dev->info.pairing_config.enable_pairing_cache = true;
-		dev->info.pairing_config.enable_pairing_verification = true;
 	}
 }
 
@@ -849,6 +848,12 @@
 
 	wpa_printf(MSG_ERROR, "Bootstrap received %d", bootstrap);
 
+	if (status == P2P_SC_SUCCESS) {
+		dev->role = P2P_ROLE_PAIRING_RESPONDER;
+#ifdef CONFIG_PASN
+		p2p_pasn_initialize(p2p, dev, sa, rx_freq, false, true);
+#endif /* CONFIG_PASN */
+	}
 out:
 	/* Send PD Bootstrapping Response for the PD Request */
 	resp = p2p_build_prov_disc_bootstrap_resp(p2p, dev, msg->dialog_token,
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index bc33a25..be7293f 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -3161,9 +3161,9 @@
 		   be_to_host16(eth_hdr->ethertype));
 
 	/* the destination address shall not be an individual address */
-	if (!ether_addr_equal(eth_hdr->dest, pae_group_addr)) {
+	if (!is_multicast_ether_addr(eth_hdr->dest)) {
 		wpa_printf(MSG_DEBUG,
-			   "KaY: ethernet destination address is not PAE group address");
+			   "KaY: ethernet destination address is not a multicast adddress");
 		return -1;
 	}
 
diff --git a/src/pasn/pasn_common.c b/src/pasn/pasn_common.c
index 25e44a1..654656e 100644
--- a/src/pasn/pasn_common.c
+++ b/src/pasn/pasn_common.c
@@ -15,6 +15,8 @@
 #include "crypto/sha384.h"
 #include "crypto/crypto.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
 #include "pasn_common.h"
 
 
@@ -31,6 +33,7 @@
 	if (!pasn)
 		return;
 	os_free(pasn->rsnxe_ie);
+	wpabuf_free(pasn->frame);
 	bin_clear_free(pasn, sizeof(struct pasn_data));
 }
 
@@ -241,3 +244,107 @@
 		return NULL;
 	return &pasn->ptk;
 }
+
+
+int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf,
+			    const u8 *data, size_t data_len)
+{
+	int ret;
+	u8 *encrypted_data, *padded_data = NULL;
+	u8 *len;
+	size_t pad_len = 0;
+
+	if (!pasn->ptk.kek_len) {
+		wpa_printf(MSG_DEBUG, "PASN: KEK not available");
+		return -2;
+	}
+
+	pad_len = data_len % 8;
+	if (pad_len) {
+		pad_len = 8 - pad_len;
+		padded_data = os_zalloc(data_len + pad_len);
+		if (!padded_data)
+			return -1;
+		os_memcpy(padded_data, data, data_len);
+		data = padded_data;
+		padded_data[data_len] = 0xdd;
+	}
+	data_len += pad_len + 8;
+
+	encrypted_data = os_malloc(data_len);
+	if (!encrypted_data) {
+		os_free(padded_data);
+		return -1;
+	}
+
+	ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len,
+		       (data_len - 8) / 8, data, encrypted_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: AES wrap failed, ret=%d", ret);
+		goto out;
+	}
+
+	if (wpabuf_tailroom(buf) < 1 + 1 + 1 + data_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Not enough room in the buffer for PASN Encrypred Data element");
+		ret = -1;
+		goto out;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	len = wpabuf_put(buf, 1);
+
+	wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_DATA);
+
+	wpabuf_put_data(buf, encrypted_data, data_len);
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+
+out:
+	os_free(padded_data);
+	os_free(encrypted_data);
+	return ret;
+}
+
+
+int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data,
+			      size_t len)
+{
+	int ret = -1;
+	u8 *buf;
+	u16 buf_len;
+	struct ieee802_11_elems elems;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (len < 24 + 6 ||
+	    ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return -1;
+	}
+
+	if (!elems.pasn_encrypted_data || elems.pasn_encrypted_data_len < 8 ||
+	    elems.pasn_encrypted_data_len % 8) {
+		wpa_printf(MSG_DEBUG, "PASN: No encrypted elements");
+		return 0;
+	}
+
+	buf_len = elems.pasn_encrypted_data_len - 8;
+
+	buf = os_malloc(buf_len);
+	if (!buf)
+		return -1;
+
+	ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, buf_len / 8,
+			 elems.pasn_encrypted_data, buf);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "PASN: AES unwrap failed, ret=%d", ret);
+	else if (pasn->parse_data_element && pasn->cb_ctx)
+		ret = pasn->parse_data_element(pasn->cb_ctx, buf, buf_len);
+
+	os_free(buf);
+	return ret;
+}
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index 7b7c737..cc3abf6 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -66,6 +66,7 @@
 	size_t extra_ies_len;
 
 	/* External modules do not access below variables */
+	bool derive_kek;
 	size_t kek_len;
 	u16 group;
 	bool secure_ltf;
@@ -130,6 +131,7 @@
 	struct os_reltime last_comeback_key_update;
 	u16 comeback_idx;
 	u16 *comeback_pending_idx;
+	struct wpabuf *frame;
 
 	/**
 	 * send_mgmt - Function handler to transmit a Management frame
@@ -151,6 +153,10 @@
 	 */
 	int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
 				     const u8 *pmkid);
+
+	int (*prepare_data_element)(void *ctx, const u8 *peer_addr);
+
+	int (*parse_data_element)(void *ctx, const u8 *data, size_t len);
 };
 
 /* Initiator */
@@ -210,8 +216,9 @@
 struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void);
 void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid);
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid);
 int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 				   const u8 *bssid, u8 *pmkid, u8 *pmk,
 				   size_t *pmk_len);
@@ -232,8 +239,9 @@
 struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void);
 void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid);
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid);
 int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 				   const u8 *bssid, u8 *pmkid, u8 *pmk,
 				   size_t *pmk_len);
@@ -246,6 +254,10 @@
 size_t pasn_get_pmk_len(struct pasn_data *pasn);
 u8 * pasn_get_pmk(struct pasn_data *pasn);
 struct wpa_ptk * pasn_get_ptk(struct pasn_data *pasn);
+int pasn_add_encrypted_data(struct pasn_data *pasn, struct wpabuf *buf,
+			    const u8 *data, size_t data_len);
+int pasn_parse_encrypted_data(struct pasn_data *pasn, const u8 *data,
+			      size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index ce1055b..035ae81 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -39,8 +39,9 @@
 
 
 int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid)
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk,
+				   size_t pmk_len, const u8 *pmkid)
 {
 	if (pmksa_cache_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid,
 			    own_addr, NULL, WPA_KEY_MGMT_SAE, 0))
@@ -682,7 +683,8 @@
 {
 	struct wpabuf *buf, *wrapped_data_buf = NULL;
 	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len, data_len;
+	u8 mic_len;
+	size_t data_len;
 	const u8 *data;
 	u8 *ptr;
 	u8 wrapped_data;
@@ -716,6 +718,11 @@
 	wpabuf_free(wrapped_data_buf);
 	wrapped_data_buf = NULL;
 
+	if (pasn->prepare_data_element && pasn->cb_ctx)
+		pasn->prepare_data_element(pasn->cb_ctx, pasn->peer_addr);
+
+	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
 	/* Add the MIC */
 	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
 	wpabuf_put_u8(buf, WLAN_EID_MIC);
@@ -817,6 +824,9 @@
 		os_free((u8 *) pasn->extra_ies);
 		pasn->extra_ies = NULL;
 	}
+
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 }
 
 
@@ -987,16 +997,20 @@
 		goto fail;
 	}
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	ret = pasn->send_mgmt(pasn->cb_ctx,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 1000);
 
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
 
+	pasn->frame = frame;
 	return 0;
 
 fail:
@@ -1386,21 +1400,30 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
 
+	if (pasn_parse_encrypted_data(pasn, data, len) < 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed");
+		goto fail;
+	}
+
 	frame = wpas_pasn_build_auth_3(pasn);
 	if (!frame) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
 		goto fail;
 	}
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	ret = pasn->send_mgmt(pasn->cb_ctx,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 100);
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
 
+	pasn->frame = frame;
 	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
 
 	pasn->status = WLAN_STATUS_SUCCESS;
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index e344898..11f27e1 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -39,8 +39,9 @@
 
 
 int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa,
-				   const u8 *own_addr, const u8 *bssid, u8 *pmk,
-				   size_t pmk_len, u8 *pmkid)
+				   const u8 *own_addr, const u8 *bssid,
+				   const u8 *pmk, size_t pmk_len,
+				   const u8 *pmkid)
 {
 	if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, own_addr,
 				 bssid, 0, NULL, WPA_KEY_MGMT_SAE))
@@ -561,6 +562,9 @@
 	if (rsnxe_ie)
 		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
 
+	if (pasn->prepare_data_element && pasn->cb_ctx)
+		pasn->prepare_data_element(pasn->cb_ctx, peer_addr);
+
 	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
 
 	/* Add the mic */
@@ -636,6 +640,8 @@
 	wpa_printf(MSG_DEBUG,
 		   "PASN: Building frame 2: success; resp STA=" MACSTR,
 		   MAC2STR(peer_addr));
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
 
 	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
 			      wpabuf_len(buf), 0, pasn->freq, 0);
@@ -643,7 +649,7 @@
 		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
 
 	wpabuf_free(rsn_buf);
-	wpabuf_free(buf);
+	pasn->frame = buf;
 	return ret;
 fail:
 	wpabuf_free(wrapped_data_buf);
@@ -735,6 +741,14 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
 
+	if (!ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+				       WLAN_RSNX_CAPAB_KEK_IN_PASN)) {
+		pasn->kek_len = 0;
+		pasn->derive_kek = false;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: kek_len=%zu", pasn->kek_len);
+
 	if (!elems.pasn_params || !elems.pasn_params_len) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: No PASN Parameters element found");
@@ -1086,6 +1100,11 @@
 		wpabuf_free(wrapped_data);
 	}
 
+	if (pasn_parse_encrypted_data(pasn, (const u8 *) mgmt, len) < 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Encrypted data processing failed");
+		goto fail;
+	}
+
 	wpa_printf(MSG_INFO,
 		   "PASN: Success handling transaction == 3. Store PTK");
 	return 0;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index d145da0..d8cdebb 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -650,6 +650,8 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->encrypt_eapol_m2)
 		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	if (sm->eapol_2_key_info_set_mask)
+		key_info |= sm->eapol_2_key_info_set_mask;
 #endif /* CONFIG_TESTING_OPTIONS */
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
@@ -3816,7 +3818,8 @@
  * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
  * @len: Length of the EAPOL frame
  * @encrypted: Whether the frame was encrypted
- * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure,
+ *	    -2 = reply counter did not increase.
  *
  * This function is called for each received EAPOL frame. Other than EAPOL-Key
  * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
@@ -3925,6 +3928,7 @@
 		      WPA_REPLAY_COUNTER_LEN) <= 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: EAPOL-Key Replay Counter did not increase - dropping packet");
+		ret = -2;
 		goto out;
 	}
 
@@ -4374,6 +4378,8 @@
 	wpabuf_free(sm->test_assoc_ie);
 	wpabuf_free(sm->test_eapol_m2_elems);
 	wpabuf_free(sm->test_eapol_m4_elems);
+	wpabuf_free(sm->test_rsnxe_data);
+	wpabuf_free(sm->test_rsnxe_mask);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_FILS_SK_PFS
 	crypto_ecdh_deinit(sm->fils_ecdh);
@@ -4973,6 +4979,9 @@
 	case WPA_PARAM_ENCRYPT_EAPOL_M4:
 		sm->encrypt_eapol_m4 = value;
 		break;
+	case WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK:
+		sm->eapol_2_key_info_set_mask = value;
+		break;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_DPP2
 	case WPA_PARAM_DPP_PFS:
@@ -5230,6 +5239,76 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data,
+			       struct wpabuf *mask)
+{
+	size_t data_len = 0, mask_len = 0;
+
+	wpabuf_free(sm->test_rsnxe_data);
+	sm->test_rsnxe_data = NULL;
+	wpabuf_free(sm->test_rsnxe_mask);
+	sm->test_rsnxe_mask = NULL;
+
+	if (!data && !mask)
+		return 0;
+
+	if (data)
+		data_len = wpabuf_len(data);
+	if (mask)
+		mask_len = wpabuf_len(mask);
+
+	if (data_len != mask_len || data_len > 255)
+		return -1;
+
+	sm->test_rsnxe_data = data;
+	sm->test_rsnxe_mask = mask;
+
+	return 0;
+}
+
+
+static int wpa_set_test_rsnxe_data(struct wpa_sm *sm, u8 *rsnxe,
+				   size_t orig_len, size_t max_len)
+{
+	const u8 *data, *mask;
+	size_t i, data_len;
+
+	if (!sm->test_rsnxe_data || !sm->test_rsnxe_mask)
+		return orig_len;
+
+	mask = wpabuf_head(sm->test_rsnxe_mask);
+	data = wpabuf_head(sm->test_rsnxe_data);
+	data_len = wpabuf_len(sm->test_rsnxe_data);
+	if (max_len < data_len + 2) {
+		wpa_printf(MSG_ERROR, "Couldn't fit RSNXE test data");
+		return -1;
+	}
+
+	/* Set data after original RSNXE to zero */
+	if (orig_len < data_len + 2)
+		os_memset(&rsnxe[orig_len], 0, data_len + 2 - orig_len);
+
+	/* Set EID and length fields */
+	*rsnxe++ = WLAN_EID_RSNX;
+	*rsnxe++ = data_len;
+
+	/* Preserve original RSNXE bit value when mask bit is zero */
+	for (i = 0; i < data_len; i++) {
+		if (!mask[i])
+			continue;
+
+		rsnxe[i] &= ~mask[i];
+		rsnxe[i] |= data[i] & mask[i];
+	}
+
+	return data_len + 2;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 /**
  * wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -5248,6 +5327,11 @@
 	res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len);
 	if (res < 0)
 		return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+	res = wpa_set_test_rsnxe_data(sm, rsnxe, res, *rsnxe_len);
+	if (res < 0)
+		return -1;
+#endif /* CONFIG_TESTING_OPTIONS */
 	*rsnxe_len = res;
 
 	wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len);
@@ -5565,6 +5649,25 @@
 }
 
 
+int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk,
+			 size_t *pmk_len, const u8 **pmkid)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	pmksa = wpa_sm_pmksa_cache_get(sm, aa, NULL, NULL, 0);
+	if (!pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Failed to get PMKSA for " MACSTR,
+			   MAC2STR(aa));
+		return -1;
+	}
+
+	*pmk = pmksa->pmk;
+	*pmk_len = pmksa->pmk_len;
+	*pmkid = pmksa->pmkid;
+	return 0;
+}
+
+
 void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
 			       struct rsn_pmksa_cache_entry *entry)
 {
@@ -6670,6 +6773,29 @@
 		goto fail;
 	}
 
+	if ((sm->ap_rsnxe && !elems.rsnxe) ||
+	    (!sm->ap_rsnxe && elems.rsnxe) ||
+	    (sm->ap_rsnxe && elems.rsnxe && sm->ap_rsnxe_len >= 2 &&
+	     (sm->ap_rsnxe_len != 2U + elems.rsnxe_len ||
+	      os_memcmp(sm->ap_rsnxe + 2, elems.rsnxe, sm->ap_rsnxe_len - 2) !=
+	      0))) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"FILS: RSNXE mismatch between Beacon/Probe Response and (Re)Association Response");
+		wpa_hexdump(MSG_INFO, "FILS: RSNXE in Beacon/Probe Response",
+			    sm->ap_rsnxe, sm->ap_rsnxe_len);
+		wpa_hexdump(MSG_INFO, "RSNXE in (Re)Association Response",
+			    elems.rsnxe, elems.rsnxe_len);
+		/* As an interop workaround, allow this for now if we did not
+		 * include the RSNXE in (Re)Association Request frame since
+		 * IEEE Std 802.11-2020 does not say anything about verifying
+		 * the RSNXE in FILS cases and there have been hostapd releases
+		 * that might omit the RSNXE in cases where the STA did not
+		 * include it in the Association Request frame. This workaround
+		 * might eventually be removed. */
+		if (sm->assoc_rsnxe && sm->assoc_rsnxe_len)
+			goto fail;
+	}
+
 	/* TODO: FILS Public Key */
 
 	if (!elems.fils_key_confirm) {
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index ca64d8f..39a1e93 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -139,6 +139,7 @@
 	WPA_PARAM_SSID_PROTECTION,
 	WPA_PARAM_RSN_OVERRIDE,
 	WPA_PARAM_RSN_OVERRIDE_SUPPORT,
+	WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK,
 };
 
 enum wpa_rsn_override {
@@ -255,6 +256,8 @@
 						      const u8 *pmkid,
 						      const void *network_ctx,
 						      int akmp);
+int wpa_sm_pmksa_get_pmk(struct wpa_sm *sm, const u8 *aa, const u8 **pmk,
+			 size_t *pmk_len, const u8 **pmkid);
 void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
 			       struct rsn_pmksa_cache_entry *entry);
 bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md);
@@ -279,6 +282,8 @@
 int wpa_fils_is_completed(struct wpa_sm *sm);
 void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
 int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
+void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+				     bool driver_bss_selection);
 
 #else /* CONFIG_NO_WPA */
 
@@ -322,6 +327,11 @@
 {
 }
 
+static inline void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid,
+				   size_t ssid_len)
+{
+}
+
 static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
 {
 }
@@ -471,7 +481,7 @@
 	return NULL;
 }
 
-static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+static inline int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
 {
 	return 0;
 }
@@ -517,6 +527,11 @@
 	return 0;
 }
 
+static inline void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
+						   bool driver_bss_selection)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_IEEE80211R
@@ -645,6 +660,8 @@
 void wpa_sm_set_test_eapol_m2_elems(struct wpa_sm *sm, struct wpabuf *buf);
 void wpa_sm_set_test_eapol_m4_elems(struct wpa_sm *sm, struct wpabuf *buf);
 const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
+int wpa_sm_set_test_rsnxe_data(struct wpa_sm *sm, struct wpabuf *data,
+			       struct wpabuf *mask);
 unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
 
 struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
@@ -670,7 +687,5 @@
 void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
 			  struct rsn_pmksa_cache_entry *entry);
 const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
-void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
-				     bool driver_bss_selection);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 9a39749..544e349 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -215,7 +215,7 @@
 	struct rsn_mdie *mdie;
 	struct rsn_ie_hdr *rsnie;
 	int mdie_len;
-	u8 rsnxe[10];
+	u8 rsnxe[257];
 	size_t rsnxe_len;
 	int rsnxe_used;
 	int res;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index ef26b24..2fd08b0 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -188,12 +188,15 @@
 	struct wpabuf *test_assoc_ie;
 	struct wpabuf *test_eapol_m2_elems;
 	struct wpabuf *test_eapol_m4_elems;
+	struct wpabuf *test_rsnxe_data;
+	struct wpabuf *test_rsnxe_mask;
 	int ft_rsnxe_used;
 	unsigned int oci_freq_override_eapol;
 	unsigned int oci_freq_override_eapol_g2;
 	unsigned int oci_freq_override_ft_assoc;
 	unsigned int oci_freq_override_fils_assoc;
 	unsigned int disable_eapol_g2_tx;
+	unsigned int eapol_2_key_info_set_mask;
 	bool encrypt_eapol_m2;
 	bool encrypt_eapol_m4;
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/utils/common.h b/src/utils/common.h
index aed93fb..3deb204 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -283,6 +283,23 @@
 	a[0] = val & 0xff;
 }
 
+static inline u64 WPA_GET_LE48(const u8 *a)
+{
+	return (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE48(u8 *a, u64 val)
+{
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
+
 static inline u64 WPA_GET_BE64(const u8 *a)
 {
 	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
diff --git a/src/utils/ext_password_file.c b/src/utils/ext_password_file.c
index 4bb0095..3122512 100644
--- a/src/utils/ext_password_file.c
+++ b/src/utils/ext_password_file.c
@@ -9,7 +9,6 @@
 #include "includes.h"
 
 #include "utils/common.h"
-#include "utils/config.h"
 #include "ext_password_i.h"
 
 
@@ -97,9 +96,19 @@
 
 	wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
 
-	while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
-		char *sep = os_strchr(pos, '=');
+	while ((pos = fgets(buf, sizeof(buf), f))) {
+		char *sep;
 
+		line++;
+
+		/* Strip newline characters */
+		pos[strcspn(pos, "\r\n")] = 0;
+
+		/* Skip comments and empty lines */
+		if (*pos == '#' || *pos == '\0')
+			continue;
+
+		sep = os_strchr(pos, '=');
 		if (!sep) {
 			wpa_printf(MSG_ERROR, "Invalid password line %d.",
 				   line);
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index e190645..0b8612a 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -569,7 +569,7 @@
 struct wpa_trace_test_fail {
 	unsigned int fail_after;
 	char pattern[256];
-} wpa_trace_test_fail[5][2];
+} wpa_trace_test_fail[5][4];
 
 int testing_test_fail(const char *tag, bool is_alloc)
 {
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 291aa07..b3876c5 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -36,6 +36,7 @@
 #define wpa_debug_open_file(p) do { } while (0)
 #define wpa_debug_close_file() do { } while (0)
 #define wpa_debug_setup_stdout() do { } while (0)
+#define wpa_debug_stop_log() do { } while (0)
 #define wpa_dbg(args...) do { } while (0)
 
 static inline int wpa_debug_reopen_file(void)
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 581d907..1b5ea81 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -329,6 +329,7 @@
 ifdef CONFIG_NAN_USD
 OBJS += src/common/nan_de.c
 OBJS += nan_usd.c
+NEED_OFFCHANNEL=y
 L_CFLAGS += -DCONFIG_NAN_USD
 endif
 
@@ -763,7 +764,7 @@
 EAPDYN += src/eap_common/eap_teap_common.c
 else
 L_CFLAGS += -DEAP_TEAP
-OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c
+OBJS += src/eap_peer/eap_teap.c
 OBJS += src/eap_common/eap_teap_common.c
 endif
 TLS_FUNCS=y
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8bec178..49c1793 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -320,6 +320,7 @@
 ifdef CONFIG_NAN_USD
 OBJS += ../src/common/nan_de.o
 OBJS += nan_usd.o
+NEED_OFFCHANNEL=y
 CFLAGS += -DCONFIG_NAN_USD
 endif
 
@@ -763,7 +764,7 @@
 
 ifdef CONFIG_EAP_TEAP
 # EAP-TEAP
-SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c ../src/eap_peer/eap_teap_pac.c
+SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c
 SRC_EAP_TEAP += ../src/eap_common/eap_teap_common.c
 ifeq ($(CONFIG_EAP_TEAP), dyn)
 CFLAGS += -DEAP_TEAP_DYNAMIC
diff --git a/wpa_supplicant/aidl/vendor/p2p_iface.cpp b/wpa_supplicant/aidl/vendor/p2p_iface.cpp
index fa176a4..8590693 100644
--- a/wpa_supplicant/aidl/vendor/p2p_iface.cpp
+++ b/wpa_supplicant/aidl/vendor/p2p_iface.cpp
@@ -191,7 +191,8 @@
 	if (wpas_p2p_group_add_persistent(
 		wpa_s, wpa_network, 0, 0, freq, 0, ht40, vht,
 		CONF_OPER_CHWIDTH_USE_HT, he, 0, NULL, 0, 0, is6GhzAllowed(wpa_s),
-		P2P_JOIN_LIMIT, isAnyEtherAddr(group_owner_bssid) ? NULL : group_owner_bssid)) {
+		P2P_JOIN_LIMIT, isAnyEtherAddr(group_owner_bssid) ? NULL : group_owner_bssid,
+		NULL, NULL, NULL, 0)) {
 		ret = -1;
 	}
 
@@ -1139,7 +1140,7 @@
 		wpa_s, peer_address.data(), pin, wps_method, persistent, false,
 		join_existing_group, false, go_intent_signed, 0, 0, -1, false, ht40,
 		vht, CONF_OPER_CHWIDTH_USE_HT, he, edmg, nullptr, 0, is6GhzAllowed(wpa_s),
-		false, 0, NULL);
+		false, 0, NULL, false);
 	if (new_pin < 0) {
 		return {"", createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
 	}
@@ -1263,7 +1264,7 @@
 	}
 	if (wpas_p2p_invite(
 		wpa_s, peer_address.data(), ssid, NULL, 0, 0, ht40, vht,
-		CONF_OPER_CHWIDTH_USE_HT, 0, he, edmg, is6GhzAllowed(wpa_s))) {
+		CONF_OPER_CHWIDTH_USE_HT, 0, he, edmg, is6GhzAllowed(wpa_s), false)) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
 	return ndk::ScopedAStatus::ok();
@@ -1726,7 +1727,8 @@
 	if (ssid == NULL) {
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, 0, 0, ht40, vht,
-			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s),
+			false, WPA_P2P_MODE_WFD_R1)) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1735,7 +1737,8 @@
 		if (wpas_p2p_group_add_persistent(
 			wpa_s, ssid, 0, 0, 0, 0, ht40, vht,
 			CONF_OPER_CHWIDTH_USE_HT, he, edmg, NULL, 0, 0,
-			is6GhzAllowed(wpa_s), 0, NULL)) {
+			is6GhzAllowed(wpa_s), 0, NULL,
+			NULL, NULL, NULL, 0)) {
 			return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1785,7 +1788,8 @@
 
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, freq, 0, ht40, vht,
-			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s),
+			false, WPA_P2P_MODE_WFD_R1)) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		}
 		return ndk::ScopedAStatus::ok();
diff --git a/wpa_supplicant/aidl/vendor/sta_iface.cpp b/wpa_supplicant/aidl/vendor/sta_iface.cpp
index 9e76f56..fe6738c 100644
--- a/wpa_supplicant/aidl/vendor/sta_iface.cpp
+++ b/wpa_supplicant/aidl/vendor/sta_iface.cpp
@@ -2104,7 +2104,7 @@
 #endif
 	AidlManager *aidl_manager = AidlManager::getInstance();
 	WPA_ASSERT(aidl_manager);
-	if (aidl_manager->isAidlServiceVersionAtLeast(4) && wpas_rsn_overriding(wpa_s)) {
+	if (aidl_manager->isAidlServiceVersionAtLeast(4) && wpas_rsn_overriding(wpa_s, NULL)) {
 		mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::RSN_OVERRIDING);
 	}
 
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 69a0e5e..de18a68 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -540,12 +540,6 @@
 		conf->supported_rates = list;
 	}
 
-#ifdef CONFIG_IEEE80211AX
-	if (ssid->mode == WPAS_MODE_P2P_GO ||
-	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-		conf->ieee80211ax = ssid->he;
-#endif /* CONFIG_IEEE80211AX */
-
 	bss->isolate = !wpa_s->conf->p2p_intra_bss;
 	bss->extended_key_id = wpa_s->conf->extended_key_id;
 	bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
@@ -582,6 +576,18 @@
 	else
 		bss->wpa_key_mgmt = ssid->key_mgmt;
 	bss->wpa_pairwise = ssid->pairwise_cipher;
+
+#ifdef CONFIG_P2P
+	if (ssid->p2p_mode == WPA_P2P_MODE_WFD_PCC) {
+		bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+		bss->rsn_override_key_mgmt = WPA_KEY_MGMT_SAE |
+			WPA_KEY_MGMT_PASN;
+		bss->wpa_pairwise = WPA_CIPHER_CCMP;
+		bss->rsn_override_pairwise = WPA_CIPHER_CCMP;
+		bss->rsn_override_mfp = 2;
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) {
 		bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
 	} else if (ssid->psk_set) {
@@ -638,10 +644,7 @@
 		bss->sae_passwords = pw;
 	}
 
-	if (ssid->sae_pwe != DEFAULT_SAE_PWE)
-		bss->sae_pwe = ssid->sae_pwe;
-	else
-		bss->sae_pwe = wpa_s->conf->sae_pwe;
+	bss->sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 #endif /* CONFIG_SAE */
 
 	if (wpa_s->conf->go_interworking) {
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 39de8ba..10ebab0 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -316,7 +316,8 @@
 					       &owe_ssid_len))
 			continue;
 
-		if (owe_ssid_len == ssid_len &&
+		if (bss->ssid_len &&
+		    owe_ssid_len == ssid_len &&
 		    os_memcmp(owe_ssid, ssid, ssid_len) == 0)
 			return bss;
 #endif /* CONFIG_OWE */
@@ -1083,6 +1084,7 @@
 		if (wpa_s->reassoc_same_ess &&
 		    wpa_s->wpa_state != WPA_COMPLETED &&
 		    wpa_s->last_ssid &&
+		    wpa_s->last_ssid->ssid &&
 		    bss->ssid_len == wpa_s->last_ssid->ssid_len &&
 		    os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
 			      bss->ssid_len) == 0)
@@ -1588,7 +1590,7 @@
 	end = pos + len;
 	pos += sizeof(*ap_info);
 
-	for (i = 0; i < count; i++) {
+	for (i = 0; i < count; i++, pos += ap_info->tbtt_info_len) {
 		u8 bss_params;
 
 		if (end - pos < ap_info->tbtt_info_len)
@@ -1599,7 +1601,7 @@
 
 		link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
 		if (link_id >= MAX_NUM_MLD_LINKS)
-			return;
+			continue;
 
 		if (*mld_params != mbssid_idx) {
 			wpa_printf(MSG_DEBUG,
@@ -1637,8 +1639,6 @@
 					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
 			}
 		}
-
-		pos += ap_info->tbtt_info_len;
 	}
 }
 
@@ -1711,11 +1711,11 @@
 		const u8 *rsne;
 		size_t rsne_len;
 
-		if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s)) {
+		if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s, ssid)) {
 			rsne = elems.rsne_override_2;
 			rsne_len = elems.rsne_override_2_len;
 		} else if (elems.rsne_override &&
-			   wpas_rsn_overriding(wpa_s)) {
+			   wpas_rsn_overriding(wpa_s, ssid)) {
 			rsne = elems.rsne_override;
 			rsne_len = elems.rsne_override_len;
 		} else {
@@ -1822,8 +1822,8 @@
 				goto out;
 
 			wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
-						     ap_info, len, &seen,
-						     &missing, ssid);
+						     ap_info, ap_info_len,
+						     &seen, &missing, ssid);
 
 			pos += ap_info_len;
 			len -= ap_info_len;
@@ -1931,6 +1931,8 @@
 }
 
 
+#ifndef CONFIG_NO_WPA
+
 static bool wpa_bss_supported_cipher(struct wpa_supplicant *wpa_s,
 				     int pairwise_cipher)
 {
@@ -2056,14 +2058,17 @@
 	return true;
 }
 
+#endif /* CONFIG_NO_WPA */
+
 
 const u8 * wpa_bss_get_rsne(struct wpa_supplicant *wpa_s,
 			    const struct wpa_bss *bss, struct wpa_ssid *ssid,
 			    bool mlo)
 {
+#ifndef CONFIG_NO_WPA
 	const u8 *ie;
 
-	if (wpas_rsn_overriding(wpa_s)) {
+	if (wpas_rsn_overriding(wpa_s, ssid)) {
 		if (!ssid)
 			ssid = wpa_s->current_ssid;
 
@@ -2087,6 +2092,7 @@
 				return ie;
 		}
 	}
+#endif /* CONFIG_NO_WPA */
 
 	return wpa_bss_get_ie(bss, WLAN_EID_RSN);
 }
@@ -2098,7 +2104,7 @@
 {
 	const u8 *ie;
 
-	if (wpas_rsn_overriding(wpa_s)) {
+	if (wpas_rsn_overriding(wpa_s, ssid)) {
 		ie = wpa_bss_get_vendor_ie(bss, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
 		if (ie) {
 			const u8 *tmp;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 5b08478..9fe83a5 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2529,6 +2529,7 @@
 	{ INT(mem_only_psk) },
 	{ STR_KEY(sae_password) },
 	{ STR(sae_password_id) },
+	{ INT(sae_pwe) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
 	{ INT(bg_scan_period) },
@@ -2543,7 +2544,7 @@
 	{ INT_RANGE(he, 0, 1) },
 	{ INT_RANGE(ht40, -1, 1) },
 	{ INT_RANGE(max_oper_chwidth, CONF_OPER_CHWIDTH_USE_HT,
-		    CONF_OPER_CHWIDTH_80P80MHZ) },
+		    CONF_OPER_CHWIDTH_320MHZ) },
 	{ INT(vht_center_freq1) },
 	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
@@ -2757,6 +2758,7 @@
 	{ INT_RANGE(enable_4addr_mode, 0, 1)},
 	{ INT_RANGE(max_idle, 0, 65535)},
 	{ INT_RANGE(ssid_protection, 0, 1)},
+	{ INT_RANGE(rsn_overriding, 0, 2)},
 };
 
 #undef OFFSET
@@ -3028,6 +3030,7 @@
 {
 	struct wpa_ssid *ssid, *prev = NULL;
 	struct wpa_cred *cred, *cprev;
+	struct wpa_dev_ik *identity, *iprev;
 	int i;
 
 	ssid = config->ssid;
@@ -3044,6 +3047,13 @@
 		wpa_config_free_cred(cprev);
 	}
 
+	identity = config->identity;
+	while (identity) {
+		iprev = identity;
+		identity = identity->next;
+		wpa_config_free_identity(iprev);
+	}
+
 	wpa_config_flush_blobs(config);
 
 	wpabuf_free(config->wps_vendor_ext_m1);
@@ -3097,6 +3107,8 @@
 	os_free(config->dpp_extra_conf_req_name);
 	os_free(config->dpp_extra_conf_req_value);
 	wpabuf_free(config->dik);
+	wpabuf_free(config->wfa_gen_capa_supp);
+	wpabuf_free(config->wfa_gen_capa_cert);
 
 	os_free(config);
 }
@@ -3282,6 +3294,7 @@
 #endif /* CONFIG_MACSEC */
 	ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
 	ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
+	ssid->rsn_overriding = RSN_OVERRIDING_NOT_SET;
 }
 
 
@@ -5497,6 +5510,14 @@
 	{ INT(p2p_interface_random_mac_addr), 0 },
 	{ INT(p2p_6ghz_disable), 0 },
 	{ INT(p2p_dfs_chan_enable), 0 },
+	{ INT_RANGE(p2p_pairing_setup, 0, 1), 0 },
+	{ INT_RANGE(p2p_pairing_cache, 0, 1), 0 },
+	{ INT(p2p_bootstrap_methods), 0 },
+	{ INT(p2p_pasn_type), 0 },
+	{ INT(p2p_comeback_after), 0 },
+	{ INT_RANGE(p2p_twt_power_mgmt, 0, 1), 0 },
+	{ INT_RANGE(p2p_chan_switch_req_enable, 0, 1), 0 },
+	{ INT(p2p_reg_info), 0 },
 	{ INT(dik_cipher), 0},
 	{ BIN(dik), 0 },
 #endif /* CONFIG_P2P */
@@ -5577,6 +5598,7 @@
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
 	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
+	{ INT_RANGE(twt_requester, 0, 1), 0 },
 	{ INT(gas_rand_addr_lifetime), 0 },
 	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
 #ifdef CONFIG_DPP
@@ -5607,6 +5629,9 @@
 	{ FUNC(mld_connect_bssid_pref), 0 },
 #endif /* CONFIG_TESTING_OPTIONS */
 	{ INT_RANGE(ft_prepend_pmkid, 0, 1), CFG_CHANGED_FT_PREPEND_PMKID },
+	{ INT_RANGE(wfa_gen_capa, 0, 2), 0},
+	{ BIN(wfa_gen_capa_supp), 0 },
+	{ BIN(wfa_gen_capa_cert), 0 },
 	/* NOTE: When adding new parameters here, add_interface() in
 	 * wpa_supplicant/dbus_new_introspect.c may need to be modified to
 	 * increase the size of the iface->xml buffer. */
@@ -5774,3 +5799,129 @@
 
 	return ret;
 }
+
+
+int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var,
+			    const char *value, int line)
+{
+	char *val;
+	size_t len;
+	int ret = 0;
+
+	if (os_strcmp(var, "dik_cipher") == 0) {
+		identity->dik_cipher = atoi(value);
+		return 0;
+	}
+
+	val = wpa_config_parse_string(value, &len);
+	if (!val) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: invalid field '%s' string value '%s'.",
+			   line, var, value);
+		return -1;
+	}
+
+	if (os_strcmp(var, "dik") == 0) {
+		wpabuf_free(identity->dik);
+		identity->dik = wpabuf_alloc_copy(val, len);
+		if (!identity->dik)
+			ret = -1;
+	} else if (os_strcmp(var, "pmk") == 0) {
+		wpabuf_free(identity->pmk);
+		identity->pmk = wpabuf_alloc_copy(val, len);
+		if (!identity->pmk)
+			ret = -1;
+	} else if (os_strcmp(var, "pmkid") == 0) {
+		if (len == PMKID_LEN) {
+			wpabuf_free(identity->pmkid);
+			identity->pmkid = wpabuf_alloc_copy(val, len);
+			if (!identity->pmkid)
+				ret = -1;
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid field '%s' string value '%s'.",
+				   line, var, value);
+			ret = -1;
+		}
+	} else if (line) {
+		wpa_printf(MSG_ERROR, "Line %d: unknown identity field '%s'.",
+			   line, var);
+		ret = -1;
+	}
+
+	os_free(val);
+	return ret;
+}
+
+
+void wpa_config_free_identity(struct wpa_dev_ik *identity)
+{
+	wpabuf_clear_free(identity->dik);
+	wpabuf_clear_free(identity->pmk);
+	wpabuf_free(identity->pmkid);
+	os_free(identity);
+}
+
+
+/**
+ * wpa_config_add_identity - Add a new device identity with empty configuration
+ * @config: Configuration data from wpa_config_read()
+ * Returns: The new device identity or %NULL if operation failed
+ */
+struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config)
+{
+	int id;
+	struct wpa_dev_ik *identity, *last = NULL;
+
+	id = -1;
+	identity = config->identity;
+	while (identity) {
+		if (identity->id > id)
+			id = identity->id;
+		last = identity;
+		identity = identity->next;
+	}
+	id++;
+
+	identity = os_zalloc(sizeof(*identity));
+	if (!identity)
+		return NULL;
+	identity->id = id;
+	if (last)
+		last->next = identity;
+	else
+		config->identity = identity;
+
+	return identity;
+}
+
+
+/**
+ * wpa_config_remove_identity - Remove a configured identity based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found
+ */
+int wpa_config_remove_identity(struct wpa_config *config, int id)
+{
+	struct wpa_dev_ik *identity, *prev = NULL;
+
+	identity = config->identity;
+	while (identity) {
+		if (id == identity->id)
+			break;
+		prev = identity;
+		identity = identity->next;
+	}
+
+	if (!identity)
+		return -1;
+
+	if (prev)
+		prev->next = identity->next;
+	else
+		config->identity = identity->next;
+
+	wpa_config_free_identity(identity);
+	return 0;
+}
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 1281786..0643114 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -430,6 +430,47 @@
 	int sim_num;
 };
 
+struct wpa_dev_ik {
+	/**
+	 * next - Next device identity in the list
+	 *
+	 * This pointer can be used to iterate over all device identity keys.
+	 * The head of this list is stored in the dev_ik field of struct
+	 * wpa_config.
+	 */
+	struct wpa_dev_ik *next;
+
+	/**
+	 * id - Unique id for the identity
+	 *
+	 * This identifier is used as a unique identifier for each identity
+	 * block when using the control interface. Each identity is allocated
+	 * an id when it is being created, either when reading the
+	 * configuration file or when a new identity is added.
+	 */
+	int id;
+
+	/**
+	 * dik_cipher - Device identity key cipher version
+	 */
+	int dik_cipher;
+
+	/**
+	 * dik - Device identity key which is unique for the device
+	 */
+	struct wpabuf *dik;
+
+	/**
+	 * pmk - PMK associated with the previous connection with the device
+	 */
+	struct wpabuf *pmk;
+
+	/**
+	 * pmkid - PMKID used in the previous connection with the device
+	 */
+	struct wpabuf *pmkid;
+};
+
 
 #define CFG_CHANGED_DEVICE_NAME BIT(0)
 #define CFG_CHANGED_CONFIG_METHODS BIT(1)
@@ -872,6 +913,14 @@
 	int p2p_optimize_listen_chan;
 	int p2p_6ghz_disable;
 	int p2p_dfs_chan_enable;
+	bool p2p_pairing_setup;
+	bool p2p_pairing_cache;
+	int p2p_bootstrap_methods;
+	int p2p_pasn_type;
+	int p2p_comeback_after;
+	bool p2p_twt_power_mgmt;
+	bool p2p_chan_switch_req_enable;
+	int p2p_reg_info;
 
 	struct wpabuf *wps_vendor_ext_m1;
 
@@ -1804,17 +1853,9 @@
 	int wowlan_disconnect_on_deinit;
 
 	/**
-	 * rsn_overriding - RSN overriding
-	 *
-	 * 0 = Disabled
-	 * 1 = Enabled automatically if the driver indicates support
-	 * 2 = Forced to be enabled even without driver capability indication
+	 * rsn_overriding - RSN overriding (default behavior)
 	 */
-	enum rsn_overriding {
-		RSN_OVERRIDING_DISABLED = 0,
-		RSN_OVERRIDING_AUTO = 1,
-		RSN_OVERRIDING_ENABLED = 2,
-	} rsn_overriding;
+	enum wpas_rsn_overriding rsn_overriding;
 
 #ifdef CONFIG_PASN
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1849,6 +1890,55 @@
 
 	/* DevIK */
 	struct wpabuf *dik;
+
+	/**
+	 * identity - P2P2 peer device identities
+	 *
+	 * This is the head for the list of all the paired devices.
+	 */
+	struct wpa_dev_ik *identity;
+
+	/**
+	 * twt_requester - Whether TWT Requester Support is enabled
+	 *
+	 * This is for setting the bit 77 of the Extended Capabilities element.
+	 */
+	bool twt_requester;
+
+	/**
+	 * wfa_gen_capa: Whether to indicate Wi-Fi generational capability to
+	 *	the AP
+	 *
+	 * 0 = do not indicate (default)
+	 * 1 = indicate in protected Action frame
+	 * 2 = indicate in unprotected (Re)Association Request frame
+	 */
+	enum {
+		WFA_GEN_CAPA_DISABLED = 0,
+		WFA_GEN_CAPA_PROTECTED = 1,
+		WFA_GEN_CAPA_UNPROTECTED = 2,
+	} wfa_gen_capa;
+
+	/**
+	 * wfa_gen_capa_supp: Supported Generations (hexdump of a bit field)
+	 *
+	 * A bit field of supported Wi-Fi generations. This is encoded as an
+	 * little endian octt string.
+	 * bit 0: Wi-Fi 4
+	 * bit 1: Wi-Fi 5
+	 * bit 2: Wi-Fi 6
+	 * bit 3: Wi-Fi 7
+	 */
+	struct wpabuf *wfa_gen_capa_supp;
+
+	/**
+	 * wfa_gen_capa_cert: Certified Generations (hexdump of a bit field)
+	 *
+	 * This has the same format as wfa_gen_capa_supp. This is an optional
+	 * field, but if included, shall have the same length as
+	 * wfa_gen_capa_supp.
+	 */
+	struct wpabuf *wfa_gen_capa_cert;
 };
 
 
@@ -1895,6 +1985,12 @@
 			const char *value, int line);
 char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
 
+int wpa_config_set_identity(struct wpa_dev_ik *identity, const char *var,
+			    const char *value, int line);
+void wpa_config_free_identity(struct wpa_dev_ik *identity);
+struct wpa_dev_ik * wpa_config_add_identity(struct wpa_config *config);
+int wpa_config_remove_identity(struct wpa_config *config, int id);
+
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 					   const char *driver_param);
 #ifndef CONFIG_NO_STDOUT_DEBUG
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 53614ae..4029d49 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -296,6 +296,65 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
 
+static struct wpa_dev_ik * wpa_config_read_identity(FILE *f, int *line, int id)
+{
+	struct wpa_dev_ik *identity;
+	int errors = 0, end = 0;
+	char buf[256], *pos, *pos2;
+
+	wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new identity block",
+		   *line);
+	identity = os_zalloc(sizeof(*identity));
+	if (!identity)
+		return NULL;
+	identity->id = id;
+
+	while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+		if (os_strcmp(pos, "}") == 0) {
+			end = 1;
+			break;
+		}
+
+		pos2 = os_strchr(pos, '=');
+		if (!pos2) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid identity line '%s'.",
+				   *line, pos);
+			errors++;
+			continue;
+		}
+
+		*pos2++ = '\0';
+		if (*pos2 == '"') {
+			if (os_strchr(pos2 + 1, '"') == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid quotation '%s'.",
+					   *line, pos2);
+				errors++;
+				continue;
+			}
+		}
+
+		if (wpa_config_set_identity(identity, pos, pos2, *line) < 0)
+			errors++;
+	}
+
+	if (!end) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: identity block was not terminated properly.",
+			   *line);
+		errors++;
+	}
+
+	if (errors) {
+		wpa_config_free_identity(identity);
+		identity = NULL;
+	}
+
+	return identity;
+}
+
+
 struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
 				    bool ro)
 {
@@ -304,9 +363,11 @@
 	int errors = 0, line = 0;
 	struct wpa_ssid *ssid, *tail, *head;
 	struct wpa_cred *cred, *cred_tail, *cred_head;
+	struct wpa_dev_ik *identity, *identity_tail, *identity_head;
 	struct wpa_config *config;
 	static int id = 0;
 	static int cred_id = 0;
+	static int identity_id = 0;
 
 	if (name == NULL)
 		return NULL;
@@ -325,6 +386,9 @@
 	cred_tail = cred_head = config->cred;
 	while (cred_tail && cred_tail->next)
 		cred_tail = cred_tail->next;
+	identity_tail = identity_head = config->identity;
+	while (identity_tail && identity_tail->next)
+		identity_tail = identity_tail->next;
 
 	wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
 	f = fopen(name, "r");
@@ -383,6 +447,22 @@
 				continue;
 			}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
+		} else if (os_strcmp(pos, "identity={") == 0) {
+			identity = wpa_config_read_identity(f, &line,
+							    identity_id++);
+			if (!identity) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: failed to parse identity block.",
+					   line);
+				errors++;
+				continue;
+			}
+			if (!identity_head) {
+				identity_head = identity_tail = identity;
+			} else {
+				identity_tail->next = identity;
+				identity_tail = identity;
+			}
 		} else if (wpa_config_process_global(config, pos, line) < 0) {
 			wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
 				   "line '%s'.", line, pos);
@@ -396,6 +476,7 @@
 	config->ssid = head;
 	wpa_config_debug_dump_networks(config);
 	config->cred = cred_head;
+	config->identity = identity_head;
 
 #ifndef WPA_IGNORE_CONFIG_ERRORS
 	if (errors) {
@@ -895,6 +976,7 @@
 	INT(enable_4addr_mode);
 	INT(max_idle);
 	INT(ssid_protection);
+	INT_DEF(rsn_overriding, RSN_OVERRIDING_NOT_SET);
 
 #undef STR
 #undef INT
@@ -1298,6 +1380,27 @@
 	if (config->p2p_6ghz_disable)
 		fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable);
 
+	if (config->p2p_pairing_setup)
+		fprintf(f, "p2p_pairing_setup=%d\n", config->p2p_pairing_setup);
+	if (config->p2p_pairing_cache)
+		fprintf(f, "p2p_pairing_cache=%d\n", config->p2p_pairing_cache);
+	if (config->p2p_bootstrap_methods)
+		fprintf(f, "p2p_bootstrap_methods=%d\n",
+			config->p2p_bootstrap_methods);
+	if (config->p2p_pasn_type)
+		fprintf(f, "p2p_pasn_type=%d\n", config->p2p_pasn_type);
+	if (config->p2p_comeback_after)
+		fprintf(f, "p2p_comeback_after=%d\n",
+			config->p2p_comeback_after);
+	if (config->p2p_twt_power_mgmt)
+		fprintf(f, "p2p_twt_power_mgmt=%d\n",
+			config->p2p_twt_power_mgmt);
+	if (config->p2p_chan_switch_req_enable)
+		fprintf(f, "p2p_chan_switch_req_enable=%d\n",
+			config->p2p_chan_switch_req_enable);
+	if (config->p2p_reg_info)
+		fprintf(f, "p2p_reg_info=%d\n", config->p2p_reg_info);
+
 	if (WPA_GET_BE32(config->ip_addr_go))
 		fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
 			config->ip_addr_go[0], config->ip_addr_go[1],
@@ -1567,6 +1670,9 @@
 	if (config->ftm_initiator)
 		fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
 
+	if (config->twt_requester)
+		fprintf(f, "twt_requester=%d\n", config->twt_requester);
+
 	if (config->osu_dir)
 		fprintf(f, "osu_dir=%s\n", config->osu_dir);
 
@@ -1639,6 +1745,24 @@
 		fprintf(f, "dik_cipher=%d\n", config->dik_cipher);
 		write_global_bin(f, "dik", config->dik);
 	}
+	if (config->wfa_gen_capa)
+		fprintf(f, "wfa_gen_capa=%d\n", config->wfa_gen_capa);
+	write_global_bin(f, "wfa_gen_capa_supp", config->wfa_gen_capa_supp);
+	write_global_bin(f, "wfa_gen_capa_cert", config->wfa_gen_capa_cert);
+}
+
+static void wpa_config_write_identity(FILE *f, struct wpa_dev_ik *dev_ik)
+{
+	fprintf(f, "\tdik_cipher=%d\n", dev_ik->dik_cipher);
+
+	if (dev_ik->dik)
+		write_global_bin(f, "\tdik", dev_ik->dik);
+
+	if (dev_ik->pmk)
+		write_global_bin(f, "\tpmk", dev_ik->pmk);
+
+	if (dev_ik->pmkid)
+		write_global_bin(f, "\tpmkid", dev_ik->pmkid);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1650,6 +1774,7 @@
 	FILE *f;
 	struct wpa_ssid *ssid;
 	struct wpa_cred *cred;
+	struct wpa_dev_ik *dev_ik;
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	struct wpa_config_blob *blob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
@@ -1697,13 +1822,20 @@
 		    !ssid->psk_set && !ssid->passphrase)
 			continue; /* do not save invalid network */
 		if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
-		    !ssid->passphrase && !ssid->sae_password)
+		    !ssid->passphrase && !ssid->sae_password &&
+		    !ssid->pmk_valid)
 			continue; /* do not save invalid network */
 		fprintf(f, "\nnetwork={\n");
 		wpa_config_write_network(f, ssid);
 		fprintf(f, "}\n");
 	}
 
+	for (dev_ik = config->identity; dev_ik; dev_ik = dev_ik->next) {
+		fprintf(f, "\nidentity={\n");
+		wpa_config_write_identity(f, dev_ik);
+		fprintf(f, "}\n");
+	}
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	for (blob = config->blobs; blob; blob = blob->next) {
 		ret = wpa_config_write_blob(f, blob);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 3d1bc5e..92ee234 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -79,6 +79,20 @@
 };
 
 /**
+ * rsn_overriding - RSN overriding
+ *
+ * 0 = Disabled
+ * 1 = Enabled automatically if the driver indicates support
+ * 2 = Forced to be enabled even without driver capability indication
+ */
+enum wpas_rsn_overriding {
+	RSN_OVERRIDING_NOT_SET = -1,
+	RSN_OVERRIDING_DISABLED = 0,
+	RSN_OVERRIDING_AUTO = 1,
+	RSN_OVERRIDING_ENABLED = 2,
+};
+
+/**
  * struct wpa_ssid - Network configuration data
  *
  * This structure includes all the configuration variables for a network. This
@@ -232,6 +246,11 @@
 	char *passphrase;
 
 	/**
+	 * pmk_valid - Whether PMK is valid in case of P2P2 derived from PASN
+	 */
+	bool pmk_valid;
+
+	/**
 	 * sae_password - SAE password
 	 *
 	 * This parameter can be used to set a password for SAE. By default, the
@@ -1282,6 +1301,17 @@
 	 * ssid_protection - Whether to use SSID protection in 4-way handshake
 	 */
 	bool ssid_protection;
+
+	/**
+	 * rsn_overriding - RSN overriding (per-network override for the global
+	 *	parameter with the same name)
+	 */
+	enum wpas_rsn_overriding rsn_overriding;
+
+	/**
+	 * p2p_mode - P2P R1 only, P2P R2 only, or PCC mode
+	 */
+	enum wpa_p2p_mode p2p_mode;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a8fc962..43d1a66 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -881,6 +881,8 @@
 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
 		dpp_test = atoi(value);
 #endif /* CONFIG_DPP */
+	} else if (os_strcasecmp(cmd, "eapol_2_key_info_set_mask") == 0) {
+		wpa_s->eapol_2_key_info_set_mask = strtoul(value, NULL, 0x10);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_FILS
 	} else if (os_strcasecmp(cmd, "disable_fils") == 0) {
@@ -6354,6 +6356,29 @@
 }
 
 
+static bool bootstrap_pwd_required(u16 bootstrap)
+{
+	switch (bootstrap) {
+	case P2P_PBMA_OPPORTUNISTIC:
+		return false;
+	case P2P_PBMA_PIN_CODE_DISPLAY:
+	case P2P_PBMA_PASSPHRASE_DISPLAY:
+	case P2P_PBMA_QR_DISPLAY:
+	case P2P_PBMA_NFC_TAG:
+	case P2P_PBMA_PIN_CODE_KEYPAD:
+	case P2P_PBMA_PASSPHRASE_KEYPAD:
+	case P2P_PBMA_QR_SCAN:
+	case P2P_PBMA_NFC_READER:
+		return true;
+	case P2P_PBMA_SERVICE_MANAGED:
+	case P2P_PBMA_HANDSHAKE_SKIP:
+		return false;
+	}
+
+	return false;
+}
+
+
 static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 			    char *buf, size_t buflen)
 {
@@ -6376,7 +6401,7 @@
 	size_t group_ssid_len = 0;
 	int he;
 	bool allow_6ghz;
-	bool p2p2;
+	bool p2p2, skip_prov;
 	u16 bootstrap = 0;
 	const char *password = NULL;
 	char *token, *context = NULL;
@@ -6393,7 +6418,7 @@
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
 	 * [ht40] [vht] [he] [edmg] [auto] [ssid=<hexdump>]
-	 * [p2p2] [bstrapmethod=<value>] [password=<string>]
+	 * [p2p2] [skip_prov] [bstrapmethod=<value>] [password=<string>]
 	 */
 
 	if (hwaddr_aton(cmd, addr))
@@ -6429,6 +6454,7 @@
 	he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he;
 	edmg = (os_strstr(cmd, " edmg") != NULL) || wpa_s->conf->p2p_go_edmg;
 	p2p2 = os_strstr(pos, " p2p2") != NULL;
+	skip_prov = os_strstr(pos, " skip_prov") != NULL;
 
 	pos2 = os_strstr(pos, " go_intent=");
 	if (pos2) {
@@ -6515,12 +6541,18 @@
 		}
 	}
 
+	if (bootstrap_pwd_required(bootstrap) && !password) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: This P2P2 bootstrap method requires password");
+		return -1;
+	}
+
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, automatic, join,
 				   auth, go_intent, freq, freq2, persistent_id,
 				   pd, ht40, vht, max_oper_chwidth, he, edmg,
 				   group_ssid, group_ssid_len, allow_6ghz, p2p2,
-				   bootstrap, password);
+				   bootstrap, password, skip_prov);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -7073,6 +7105,7 @@
 	int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0;
 	int edmg;
 	bool allow_6ghz;
+	bool p2p2;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -7129,9 +7162,11 @@
 	if (allow_6ghz && chwidth == 40)
 		max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
 
+	p2p2 = os_strstr(cmd, " p2p2") != NULL;
+
 	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
 			       max_oper_chwidth, pref_freq, he, edmg,
-			       allow_6ghz);
+			       allow_6ghz, p2p2);
 }
 
 
@@ -7200,13 +7235,15 @@
 					     vht_center_freq2, ht40, vht,
 					     vht_chwidth, he, edmg,
 					     NULL, 0, 0, allow_6ghz, 0,
-					     go_bssid);
+					     go_bssid, NULL, NULL, NULL, 0);
 }
 
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int freq = 0, persistent = 0, group_id = -1;
+	bool p2p2 = false;
+	int p2pmode = WPA_P2P_MODE_WFD_R1;
 	bool allow_6ghz = false;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
@@ -7222,7 +7259,8 @@
 	while ((token = str_token(cmd, " ", &context))) {
 		if (sscanf(token, "freq2=%d", &freq2) == 1 ||
 		    sscanf(token, "persistent=%d", &group_id) == 1 ||
-		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
+		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1 ||
+		    sscanf(token, "p2pmode=%d", &p2pmode) == 1) {
 			continue;
 #ifdef CONFIG_ACS
 		} else if (os_strcmp(token, "freq=acs") == 0) {
@@ -7243,6 +7281,8 @@
 			persistent = 1;
 		} else if (os_strcmp(token, "allow_6ghz") == 0) {
 			allow_6ghz = true;
+		} else if (os_strcmp(token, "p2p2") == 0) {
+			p2p2 = true;
 		} else if (os_strncmp(token, "go_bssid=", 9) == 0) {
 			if (hwaddr_aton(token + 9, go_bssid_buf))
 				return -1;
@@ -7293,8 +7333,12 @@
 						     edmg, allow_6ghz,
 						     go_bssid);
 
+	if (p2pmode < WPA_P2P_MODE_WFD_R1 || p2pmode > WPA_P2P_MODE_WFD_PCC)
+		return -1;
+
 	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
-				  max_oper_chwidth, he, edmg, allow_6ghz);
+				  max_oper_chwidth, he, edmg, allow_6ghz, p2p2,
+				  (enum wpa_p2p_mode) p2pmode);
 }
 
 
@@ -7697,6 +7741,53 @@
 		return 0;
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (os_strcmp(cmd, "pairing_setup") == 0) {
+		p2p_set_pairing_setup(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "pairing_cache") == 0) {
+		p2p_set_pairing_cache(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "supported_bootstrapmethods") == 0) {
+		p2p_set_bootstrapmethods(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "pasn_type") == 0) {
+		p2p_set_pasn_type(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "comeback_after") == 0) {
+		p2p_set_comeback_after(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "reginfo") == 0) {
+		p2p_set_reg_info(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "twt_power_mgmt") == 0) {
+		p2p_set_twt_power_mgmt(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "chan_switch_req_enable") == 0) {
+		p2p_set_chan_switch_req_enable(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
+	if (os_strcmp(cmd, "inv_oper_freq") == 0) {
+		p2p_set_invitation_op_freq(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -7712,12 +7803,15 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(wpa_s->get_pref_freq_list_override);
 	wpa_s->get_pref_freq_list_override = NULL;
+	p2p_set_invitation_op_freq(wpa_s->global->p2p, -1);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpas_p2p_stop_find(wpa_s);
 	wpa_s->parent->p2ps_method_config_any = 0;
 	if (wpa_s->global->p2p)
 		p2p_flush(wpa_s->global->p2p);
+
+	wpa_s->p2p2 = false;
 }
 
 
@@ -7804,6 +7898,26 @@
 	return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
 }
 
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int p2p_ctrl_pmk_get(struct wpa_supplicant *wpa_s, char *buf,
+			    size_t buflen)
+{
+	const u8 *pmk;
+	size_t pmk_len;
+
+	/* Return the PMK from the first identity entry. This assumes test
+	 * cases to remove all indentities at the beginning so that only one
+	 * entry is available. */
+	if (!wpa_s->conf->identity || !wpa_s->conf->identity->pmk)
+		return -1;
+
+	pmk_len = wpabuf_len(wpa_s->conf->identity->pmk);
+	pmk = wpabuf_head(wpa_s->conf->identity->pmk);
+
+	return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_P2P */
 
 
@@ -8676,6 +8790,7 @@
 	ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
 #ifdef CONFIG_P2P
 	if (ret == 0) {
+#ifdef CONFIG_P2P
 		if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
 			struct p2p_data *p2p = wpa_s->global->p2p;
 			if (p2p) {
@@ -8686,6 +8801,7 @@
 				p2p_set_country(p2p, country);
 			}
 		}
+#endif /* CONFIG_P2P */
 		ret = os_snprintf(buf, buflen, "%s\n", "OK");
 		if (os_snprintf_error(buflen, ret))
 			ret = -1;
@@ -8931,6 +9047,7 @@
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 	wpa_sm_set_test_eapol_m2_elems(wpa_s->wpa, NULL);
 	wpa_sm_set_test_eapol_m4_elems(wpa_s->wpa, NULL);
+	wpa_sm_set_test_rsnxe_data(wpa_s->wpa, NULL, NULL);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M2, 0);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_ENCRYPT_EAPOL_M4, 0);
 	os_free(wpa_s->get_pref_freq_list_override);
@@ -8959,6 +9076,7 @@
 	wpa_s->oci_freq_override_fils_assoc = 0;
 	wpa_s->oci_freq_override_wnm_sleep = 0;
 	wpa_s->disable_eapol_g2_tx = 0;
+	wpa_s->eapol_2_key_info_set_mask = 0;
 	wpa_s->test_assoc_comeback_type = -1;
 #ifdef CONFIG_DPP
 	os_free(wpa_s->dpp_config_obj_override);
@@ -10302,6 +10420,29 @@
 }
 
 
+static int wpas_ctrl_test_rsnxe_data(struct wpa_supplicant *wpa_s,
+				     const char *cmd)
+{
+	struct wpabuf *data = NULL, *mask = NULL;
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	if (wpas_get_hex_buf(cmd, &data) < 0 ||
+	    wpas_get_hex_buf(pos, &mask) < 0 ||
+	    wpa_sm_set_test_rsnxe_data(wpa_s->wpa, data, mask) < 0) {
+		wpabuf_free(data);
+		wpabuf_free(mask);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
 {
 	u8 zero[WPA_TK_MAX_LEN];
@@ -10856,6 +10997,24 @@
 #endif /* CONFIG_AP */
 }
 
+#ifdef CONFIG_P2P
+#ifdef CONFIG_PASN
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int p2p_ctrl_get_pasn_ptk(struct wpa_supplicant *wpa_s, char *buf,
+				 size_t buflen)
+{
+	const u8 *ptk;
+	size_t ptk_len;
+
+	if (wpas_p2p_get_pasn_ptk(wpa_s, &ptk, &ptk_len))
+		return -1;
+	return wpa_snprintf_hex(buf, buflen, ptk, ptk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
+#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 
@@ -12870,6 +13029,12 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
 		reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+#ifdef CONFIG_PASN
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strcmp(buf, "P2P_GET_PASNPTK") == 0) {
+		reply_len = p2p_ctrl_get_pasn_ptk(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_PASN */
 	} else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
 		reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
 						   reply_size);
@@ -12936,6 +13101,13 @@
 	} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
 		if (wpas_p2p_lo_stop(wpa_s))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_REMOVE_IDENTITY") == 0) {
+		if (wpas_p2p_remove_all_identity(wpa_s))
+			reply_len = -1;
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strncmp(buf, "P2P_PMK_GET", 12) == 0) {
+		reply_len = p2p_ctrl_pmk_get(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
 	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
@@ -13387,6 +13559,9 @@
 	} else if (os_strncmp(buf, "ML_PROBE_REQ ", 13) == 0) {
 		if (wpas_ctrl_ml_probe(wpa_s, buf + 13))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_RSNXE_DATA ", 16) == 0) {
+		if (wpas_ctrl_test_rsnxe_data(wpa_s, buf + 16) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -13593,6 +13768,8 @@
 	} else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) {
 		if (wpas_ctrl_nan_transmit(wpa_s, buf + 13) < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "NAN_FLUSH") == 0) {
+		wpas_nan_usd_flush(wpa_s);
 #endif /* CONFIG_NAN_USD */
 #ifdef CONFIG_PASN
 	} else if (os_strncmp(buf, "PASN_START ", 11) == 0) {
@@ -13631,6 +13808,14 @@
 	} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
 		reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
 							    reply_size);
+	} else if (os_strcmp(buf, "NEW_RANDOM_MAC_ADDRESS") == 0) {
+		enum wpas_mac_addr_style mac_addr_style =
+			wpa_s->conf->preassoc_mac_addr;
+
+		wpa_s->conf->preassoc_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM;
+		if (wpas_update_random_addr_disassoc(wpa_s) != 1)
+			reply_len = -1;
+		wpa_s->conf->preassoc_mac_addr = mac_addr_style;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -13986,6 +14171,7 @@
 		"NFC_REPORT_HANDOVER ",
 		"P2P_ASP_PROVISION ",
 		"P2P_ASP_PROVISION_RESP ",
+		"NAN_",
 #ifdef CONFIG_AP
 		"STA ",
 		"STA-NEXT ",
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 27003eb..a3f32c3 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -1159,3 +1159,38 @@
 
 	os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
 }
+
+
+dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry)
+{
+	return entry->type == DBUS_TYPE_BYTE ||
+		entry->type == DBUS_TYPE_INT16 ||
+		entry->type == DBUS_TYPE_UINT16 ||
+		entry->type == DBUS_TYPE_INT32 ||
+		entry->type == DBUS_TYPE_UINT32 ||
+		entry->type == DBUS_TYPE_INT64 ||
+		entry->type == DBUS_TYPE_UINT64;
+}
+
+
+int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry)
+{
+	switch (entry->type) {
+	case DBUS_TYPE_BYTE:
+		return entry->byte_value;
+	case DBUS_TYPE_INT16:
+		return entry->int16_value;
+	case DBUS_TYPE_UINT16:
+		return entry->uint16_value;
+	case DBUS_TYPE_INT32:
+		return entry->int32_value;
+	case DBUS_TYPE_UINT32:
+		return entry->uint32_value;
+	case DBUS_TYPE_INT64:
+		return entry->int64_value;
+	case DBUS_TYPE_UINT64:
+		return entry->uint64_value;
+	}
+
+	return -1;
+}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 1d33689..15c8aba 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -158,4 +158,7 @@
 
 void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry);
 
+dbus_bool_t wpa_dbus_dict_entry_is_int(const struct wpa_dbus_dict_entry *entry);
+int wpa_dbus_dict_entry_get_int(const struct wpa_dbus_dict_entry *entry);
+
 #endif  /* DBUS_DICT_HELPERS_H */
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 5ad5bcd..ff7e003 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2332,6 +2332,113 @@
 }
 
 
+/**
+ * wpas_dbus_signal_p2p_bootstrap_req - Signals BootstrappingRequest event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the message triggering this notification
+ * @bootstrap_method: Peer's bootstrap method
+ *
+ * Sends a signal to notify that a peer P2P Device is requesting bootstrapping
+ * negotiation with us.
+ */
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(src));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "BootstrappingRequest");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+					    &bootstrap_method))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_bootstrap_completed - Signals BootstrappingCompleted event
+ * event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the peer with which bootstrapping is done
+ * @status: Status of Bootstrapping handshake
+ *
+ * Sends a signal to notify that a peer P2P Device is requesting bootstrapping
+ * negotiation with us.
+ */
+void wpas_dbus_signal_p2p_bootstrap_completed(struct wpa_supplicant *wpa_s,
+					      const u8 *src, int status)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	struct wpas_dbus_priv *iface;
+	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface)
+		return;
+
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+		    wpa_s->dbus_new_path, MAC2STR(src));
+	path = peer_obj_path;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "BootstrappingCompleted");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+					    &path) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32,
+					    &status))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
 #endif /* CONFIG_P2P */
 
 
@@ -3771,6 +3878,52 @@
 	  },
 	},
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_NAN_USD
+	{ "NANPublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_publish,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "publish_id", "u", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANCancelPublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_publish,
+	  {
+		  { "publish_id", "u", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANUpdatePublish", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_update_publish,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_subscribe,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  { "subscribe_id", "u", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANCancelSubscribe", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_cancel_subscribe,
+	  {
+		  { "subscribe_id", "u", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "NANTransmit", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_nan_transmit,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -4273,6 +4426,20 @@
 		  END_ARGS
 	  }
 	},
+	{ "BootstrappingRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "bootstrap_method", "q", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "BootstrappingCompleted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "status", "i", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_AP
 	{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
@@ -4384,6 +4551,40 @@
 	  }
 	},
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_NAN_USD
+	{ "NANDiscoveryResult", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANReplied", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANReceive", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "args", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANPublishTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "publish_id", "u", ARG_OUT },
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "NANSubscribeTerminated", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "subscribe_id", "u", ARG_OUT },
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, { END_ARGS } }
 };
 
@@ -5253,3 +5454,259 @@
 	dbus_message_unref(msg);
 }
 #endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_NAN_USD
+
+/**
+ * wpas_dbus_signal_nan_discovery_result - Send NANDiscoveryResult signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @srv_proto_type: Service Protocol Type
+ * @subscribe_id: Subscribe ID of the session
+ * @peer_publish_id: Publish ID of the sender
+ * @peer_addr: MAC address of the peer device
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI field
+ *
+ * This is used to indicate the NAN DE DiscoveryResult event.
+ */
+void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+					   enum nan_service_protocol_type
+					   srv_proto_type,
+					   int subscribe_id,
+					   int peer_publish_id,
+					   const u8 *peer_addr,
+					   bool fsd, bool fsd_gas,
+					   const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANDiscoveryResult");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id",
+					 subscribe_id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id",
+					 peer_publish_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "fsd", fsd) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "fsd_gas", fsd_gas) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type",
+					 srv_proto_type) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter,
+					      "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_replied - Send NANReplied signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @srv_proto_type: Service Protocol Type
+ * @publish_id: Publish id of the session
+ * @peer_subscribe_id: Subscribe id of the sender
+ * @peer_addr: MAC address of the peer device
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI field
+ *
+ * This is used to indicate the NAN DE Replied event.
+ */
+void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+				  enum nan_service_protocol_type srv_proto_type,
+				  int publish_id,
+				  int peer_subscribe_id,
+				  const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANReplied");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "publish_id",
+					 publish_id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "subscribe_id",
+					 peer_subscribe_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "srv_proto_type",
+					 srv_proto_type) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_receive - Send NANReceive signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: The original publish_id or subscribe_id
+ * @peer_id: Peer instance identifier
+ * @peer_addr: Address of the sender
+ * @ssi: Service specific information payload
+ * @ssi_len: Length of the SSI
+ *
+ * This is used to indicate the NAN DE Receive event to notify reception of a
+ * follow-up frame.
+ */
+void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s,
+				  int id, int peer_id, const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	char addr_str[20];
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANReceive");
+	if (!msg)
+		return;
+
+	snprintf(addr_str, sizeof(addr_str), MACSTR, MAC2STR(peer_addr));
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "id", id) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "peer_id", peer_id) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "peer_addr", addr_str) ||
+	    (ssi &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "ssi",
+					      (const char *) ssi,
+					      ssi_len)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_publish_terminated - Send NANPublishTerminated signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @publish_id: The publish_id of the session
+ * @reason: The reason of the termination
+ *
+ * This is used to indicate the NAN DE PublishTerminated event to notify when
+ * the session has expired.
+ */
+void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					     int publish_id,
+					     const char *reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	dbus_uint32_t pub_id = publish_id;
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANPublishTerminated");
+	if (!msg)
+		return;
+
+	if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &pub_id,
+				      DBUS_TYPE_INVALID) ||
+	    !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				      DBUS_TYPE_INVALID))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_nan_subscribe_terminated - Send NANSubscribeTerminated signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @subscribe_id: The subscribe_id of the session
+ * @reason: The reason of the termination
+ *
+ * This is used to indicate the NAN DE SubscribeTerminated event to notify when
+ * the session has expired.
+ */
+void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					       int subscribe_id,
+					       const char *reason)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	dbus_uint32_t sub_id = subscribe_id;
+
+	iface = wpa_s->global->dbus;
+	/* Do nothing if the interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "NANSubscribeTerminated");
+	if (!msg)
+		return;
+
+	if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &sub_id,
+				      DBUS_TYPE_INVALID) ||
+	    !dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				      DBUS_TYPE_INVALID))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 1db5fe8..f9ff636 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -21,6 +21,7 @@
 struct wps_event_m2d;
 struct wps_event_fail;
 struct wps_credential;
+enum nan_service_protocol_type;
 
 enum wpas_dbus_prop {
 	WPAS_DBUS_PROP_AP_SCAN,
@@ -265,6 +266,10 @@
 					      const u8 *sa, const u8 *dev_addr,
 					      const u8 *bssid, int id,
 					      int op_freq);
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method);
+void wpas_dbus_signal_p2p_bootstrap_completed(struct wpa_supplicant *wpa_s,
+					      const u8 *src, int status);
 void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid);
 void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s,
@@ -285,6 +290,28 @@
 				      const u8 *dst, const char *result);
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url);
+void wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+					   enum nan_service_protocol_type
+					   srv_proto_type,
+					   int subscribe_id,
+					   int peer_publish_id,
+					   const u8 *peer_addr,
+					   bool fsd, bool fsd_gas,
+					   const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+				  enum nan_service_protocol_type srv_proto_type,
+				  int publish_id, int peer_subscribe_id,
+				  const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s, int id,
+				  int peer_id, const u8 *peer_addr,
+				  const u8 *ssi, size_t ssi_len);
+void wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					     int publish_id,
+					     const char *reason);
+void wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					       int subscribe_id,
+					       const char *reason);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -617,6 +644,18 @@
 }
 
 static inline
+void wpas_dbus_signal_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+					const u8 *src, u16 bootstrap_method)
+{
+}
+
+static inline
+void wpas_dbus_signal_p2p_bootstrap_completed(struct wpa_supplicant *wpa_s,
+					      const u8 *src, int status)
+{
+}
+
+static inline
 void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid)
 {
@@ -668,6 +707,45 @@
 {
 }
 
+static inline void
+wpas_dbus_signal_nan_discovery_result(struct wpa_supplicant *wpa_s,
+				      enum nan_service_protocol_type
+				      srv_proto_type,
+				      int subscribe_id,
+				      int peer_publish_id, const u8 *peer_addr,
+				      bool fsd, bool fsd_gas,
+				      const u8 *ssi, size_t ssi_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_replied(struct wpa_supplicant *wpa_s,
+			     enum nan_service_protocol_type srv_proto_type,
+			     int publish_id, int peer_subscribe_id,
+			     const u8 *peer_addr, const u8 *ssi, size_t ssi_len)
+{
+}
+
+
+static inline void
+wpas_dbus_signal_nan_receive(struct wpa_supplicant *wpa_s,
+			     int id, int peer_id, const u8 *peer_addr,
+			     const u8 *ssi, size_t ssi_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_publish_terminated(struct wpa_supplicant *wpa_s,
+					int publish_id, const char *reason)
+{
+}
+
+static inline void
+wpas_dbus_signal_nan_subscribe_terminated(struct wpa_supplicant *wpa_s,
+					  int subscribe_id, const char *reason)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 52e35a7..2fad8dd 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -12,6 +12,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/nan_de.h"
 #include "eap_peer/eap_methods.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -27,6 +28,7 @@
 #include "../autoscan.h"
 #include "../ap.h"
 #include "../interworking.h"
+#include "../nan_usd.h"
 #include "dbus_new_helpers.h"
 #include "dbus_new.h"
 #include "dbus_new_handlers.h"
@@ -6485,3 +6487,514 @@
 	}
 	return TRUE;
 }
+
+
+#ifdef CONFIG_NAN_USD
+
+/*
+ * wpas_dbus_handler_nan_publish - Send out NAN publish messages
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANPublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int publish_id;
+	char *srv_name = NULL;
+	enum nan_service_protocol_type srv_proto_type = 0;
+	bool p2p = false;
+	struct nan_publish_params params;
+	int *freq_list = NULL;
+	struct wpabuf *ssi = NULL;
+	dbus_uint32_t id;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANPublish");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	os_memset(&params, 0, sizeof(params));
+	/* USD shall use both solicited and unsolicited transmissions */
+	params.unsolicited = true;
+	params.solicited = true;
+	/* USD shall require FSD without GAS */
+	params.fsd = true;
+	params.freq = NAN_USD_DEFAULT_FREQ;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "srv_name") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			os_free(srv_name);
+			srv_name = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!srv_name)
+				goto oom;
+		} else if (os_strcmp(entry.key, "srv_proto_type") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			srv_proto_type = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "solicited") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.solicited = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "unsolicited") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.unsolicited = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "solicited_multicast") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.solicited_multicast = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ttl") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.ttl = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "disable_events") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.disable_events = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "fsd") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.fsd = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "fsd_gas") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.fsd_gas = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "p2p") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			p2p = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.freq = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "announcement_period") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.announcement_period =
+				wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi)
+				goto oom;
+		} else if (os_strcmp(entry.key, "freq_list") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			unsigned int i;
+
+			for (i = 0; i < entry.array_len; i++)
+				int_array_add_unique(
+					&freq_list, entry.uint16array_value[i]);
+			params.freq_list = freq_list;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANPublish - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (!srv_name)
+		goto fail;
+
+	publish_id = wpas_nan_usd_publish(wpa_s, srv_name, srv_proto_type, ssi,
+					  &params, p2p);
+	if (publish_id < 0) {
+		reply = wpas_dbus_error_unknown_error(
+			message, "error publishing NAN USD");
+		goto out;
+	}
+
+	id = publish_id;
+	reply = dbus_message_new_method_return(message);
+	if (!reply) {
+		reply = wpas_dbus_error_no_memory(message);
+		goto out;
+	}
+
+	dbus_message_append_args(reply, DBUS_TYPE_UINT32,
+				 &id, DBUS_TYPE_INVALID);
+
+out:
+	wpabuf_free(ssi);
+	os_free(freq_list);
+	os_free(srv_name);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANPublish");
+	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_cancel_publish - Cancel a NAN publish
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANCancelPublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_cancel_publish(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	dbus_uint32_t publish_id;
+
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_UINT32, &publish_id,
+				   DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: NANCancelPublish failed to get args");
+		return wpas_dbus_error_invalid_args(message, NULL);
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: NANCancelPublish: id=%u", publish_id);
+	nan_de_cancel_publish(wpa_s->nan_de, publish_id);
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_update_publish - Update the SSI for a NAN publish
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANUpdatePublish" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_update_publish(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int publish_id = -1;
+	struct wpabuf *ssi = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANUpdatePublish");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "publish_id") == 0 &&
+		    entry.type == DBUS_TYPE_UINT32) {
+			publish_id = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+			wpa_printf(MSG_DEBUG, "dbus: publish_id=%d",
+				   publish_id);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi) {
+				reply = wpas_dbus_error_no_memory(message);
+				goto out;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANTransmit - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto out;
+		}
+	}
+
+	if (publish_id < 0)
+		goto fail;
+
+	if (nan_de_update_publish(wpa_s->nan_de, publish_id, ssi) < 0)
+		reply = wpas_dbus_error_unknown_error(
+			message, "error updating NAN USD publish ssi");
+
+out:
+	wpabuf_free(ssi);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(
+		message,
+		"failed to parse NANUpdatePublish");
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_subscribe - Send out NAN USD subscribe messages
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANSubscribe" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int subscribe_id;
+	char *srv_name = NULL;
+	struct nan_subscribe_params params;
+	enum nan_service_protocol_type srv_proto_type = 0;
+	bool p2p = false;
+	struct wpabuf *ssi = NULL;
+	int *freq_list = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANSubscribe");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = NAN_USD_DEFAULT_FREQ;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "srv_name") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			os_free(srv_name);
+			srv_name = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!srv_name)
+				goto oom;
+		} else if (os_strcmp(entry.key, "srv_proto_type") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			srv_proto_type = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "active") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			params.active = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "p2p") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			p2p = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ttl") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.ttl = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.freq = wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "query_period") == 0 &&
+			   wpa_dbus_dict_entry_is_int(&entry)) {
+			params.query_period =
+				wpa_dbus_dict_entry_get_int(&entry);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi)
+				goto oom;
+		} else if (os_strcmp(entry.key, "freq_list") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			unsigned int i;
+
+			for (i = 0; i < entry.array_len; i++)
+				int_array_add_unique(
+					&freq_list, entry.uint16array_value[i]);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANSubscribe - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (!srv_name)
+		goto fail;
+
+	subscribe_id = wpas_nan_usd_subscribe(wpa_s, srv_name, srv_proto_type,
+					      ssi, &params, p2p);
+	if (subscribe_id < 0) {
+		reply = wpas_dbus_error_unknown_error(
+			message, "error subscribing NAN USD");
+		goto out;
+	}
+
+	reply = dbus_message_new_method_return(message);
+	dbus_message_append_args(reply, DBUS_TYPE_UINT32,
+				 &subscribe_id, DBUS_TYPE_INVALID);
+out:
+	wpabuf_free(ssi);
+	os_free(freq_list);
+	os_free(srv_name);
+	return reply;
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANSubscribe");
+	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_cancel_subscribe - Cancel a NAN subscription
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANCancelSubscribe" method call of network interface.
+ */
+DBusMessage *
+wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message,
+				       struct wpa_supplicant *wpa_s)
+{
+	dbus_uint32_t subscribe_id;
+
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	if (!dbus_message_get_args(message, NULL,
+				   DBUS_TYPE_UINT32, &subscribe_id,
+				   DBUS_TYPE_INVALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: NANCancelSubscribe failed to get args");
+		return wpas_dbus_error_invalid_args(message, NULL);
+	}
+
+	wpa_printf(MSG_DEBUG, "dbus: NANCancelSubscribe: id=%u", subscribe_id);
+	nan_de_cancel_subscribe(wpa_s->nan_de, subscribe_id);
+	return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_nan_transmit - Send out NAN followup frames
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "NANTransmit" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	DBusMessage *reply = NULL;
+	int handle = -1;
+	int req_instance_id = -1;
+	u8 peer_addr[ETH_ALEN];
+	bool peer_addr_set = false;
+	struct wpabuf *ssi = NULL;
+
+	wpa_printf(MSG_DEBUG, "dbus: NANTransmit");
+	if (!wpa_s->nan_de)
+		return NULL;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto fail;
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto fail;
+		if (os_strcmp(entry.key, "handle") == 0 &&
+		    entry.type == DBUS_TYPE_UINT32) {
+			handle = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+			wpa_printf(MSG_DEBUG, "dbus: handle=%d", handle);
+		} else if (os_strcmp(entry.key, "req_instance_id") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			req_instance_id = entry.uint32_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "peer_addr") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, peer_addr) < 0) {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto fail;
+			}
+			peer_addr_set = true;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "ssi") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			wpabuf_free(ssi);
+			ssi = wpabuf_alloc_copy(entry.bytearray_value,
+						entry.array_len);
+			wpa_dbus_dict_entry_clear(&entry);
+			if (!ssi) {
+				reply = wpas_dbus_error_no_memory(message);
+				goto out;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: NANTransmit - unsupported dict entry '%s'",
+				   entry.key);
+			reply = wpas_dbus_error_invalid_args(message,
+							     entry.key);
+			wpa_dbus_dict_entry_clear(&entry);
+			goto fail;
+		}
+	}
+
+	if (handle < 0 || req_instance_id < 0 || !peer_addr_set || !ssi)
+		goto fail;
+
+	if (wpas_nan_usd_transmit(wpa_s, handle, ssi, NULL, peer_addr,
+				  req_instance_id) < 0)
+		reply = wpas_dbus_error_unknown_error(
+			message, "failed to transmit follow-up");
+out:
+	wpabuf_free(ssi);
+	return reply;
+
+fail:
+	reply = wpas_dbus_error_invalid_args(message,
+					     "failed to parse NANTransmit");
+	goto out;
+}
+
+#endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 7faf70a..a526090 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -290,4 +290,18 @@
 DBusMessage * wpas_dbus_handler_unsubscribe_preq(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_nan_publish(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_cancel_publish(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_update_publish(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message,
+					      struct wpa_supplicant *wpa_s);
+DBusMessage *
+wpas_dbus_handler_nan_cancel_subscribe(DBusMessage *message,
+				       struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_nan_transmit(DBusMessage *message,
+					     struct wpa_supplicant *wpa_s);
+
 #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 65bd478..ce49bce 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -477,7 +477,8 @@
 						  freq2, ht40, vht,
 						  max_oper_chwidth, he, edmg,
 						  NULL, 0, 0, allow_6ghz,
-						  retry_limit, go_bssid)) {
+						  retry_limit, go_bssid, NULL,
+						  NULL, NULL, 0)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -485,7 +486,7 @@
 		}
 	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, freq2,
 				      ht40, vht, max_oper_chwidth, he, edmg,
-				      allow_6ghz))
+				      allow_6ghz, wpa_s->p2p2, wpa_s->p2p_mode))
 		goto inv_args;
 
 out:
@@ -706,7 +707,7 @@
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
 				   go_intent, freq, 0, -1, 0, 0, 0, 0, 0, 0,
-				   NULL, 0, false, 0, 0, NULL);
+				   NULL, 0, false, 0, 0, NULL, false);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -866,7 +867,7 @@
 			goto err;
 
 		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
-				    0, 0, 0, false) < 0) {
+				    0, 0, 0, false, false) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index a8c0d28..bfdc3e2 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
 	if (!iface)
 		return NULL;
 	iface->dbus_interface = os_strdup(dbus_interface);
-	iface->xml = wpabuf_alloc(16000);
+	iface->xml = wpabuf_alloc(18000);
 	if (iface->dbus_interface == NULL || iface->xml == NULL) {
 		os_free(iface->dbus_interface);
 		wpabuf_free(iface->xml);
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 1b2c756..70f7a3b 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -59,6 +59,9 @@
 				     struct dpp_authentication *auth);
 static bool wpas_dpp_tcp_msg_sent(void *ctx, struct dpp_authentication *auth);
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_DPP3 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -1347,6 +1350,16 @@
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	int freq;
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_announcement && wpa_s->dpp_pb_discovery_done) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to send push button announcement");
+		if (eloop_register_timeout(0, 0, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
+
 	if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel",
@@ -5545,7 +5558,6 @@
 #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,
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index b6c7f50..43847cf 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -16,9 +16,17 @@
 static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
 				  const char *ifname)
 {
-	if (wpa_s->driver->init2)
+	if (wpa_s->driver->init2) {
+		enum wpa_p2p_mode p2p_mode = WPA_P2P_MODE_WFD_R1;
+
+#ifdef CONFIG_P2P
+		p2p_mode = wpa_s->p2p_mode;
+#endif /* CONFIG_P2P */
+
 		return wpa_s->driver->init2(wpa_s, ifname,
-					    wpa_s->global_drv_priv);
+					    wpa_s->global_drv_priv,
+					    p2p_mode);
+	}
 	if (wpa_s->driver->init) {
 		return wpa_s->driver->init(wpa_s, ifname);
 	}
@@ -104,6 +112,8 @@
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 			       struct wpa_driver_scan_params *params)
 {
+	params->link_id = -1;
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
 		return -EBUSY;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 2a665d7..30176a0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -437,6 +437,9 @@
 
 	wpa_s->ssid_verified = false;
 	wpa_s->bigtk_set = false;
+
+	wpabuf_free(wpa_s->pending_eapol_rx);
+	wpa_s->pending_eapol_rx = NULL;
 }
 
 
@@ -1045,7 +1048,7 @@
 #ifdef CONFIG_SAE
 			if (flagged && ((rate_ie[j] & 0x7f) ==
 					BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
-				if (wpa_s->conf->sae_pwe ==
+				if (wpas_get_ssid_sae_pwe(wpa_s, ssid) ==
 				    SAE_PWE_HUNT_AND_PECK &&
 				    !ssid->sae_password_id &&
 				    !is_6ghz_freq(bss->freq) &&
@@ -1163,7 +1166,8 @@
 			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
 			if (ssid->ssid_len == *ret_ssid_len &&
-			    os_memcmp(ssid->ssid, ret_ssid, *ret_ssid_len) == 0) {
+			    os_memcmp(ssid->ssid, *ret_ssid, *ret_ssid_len) ==
+			    0) {
 				/* OWE BSS in transition mode for a currently
 				 * enabled OWE network. */
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1267,12 +1271,15 @@
 {
 	int res;
 	bool wpa, check_ssid, osen, rsn_osen = false;
+#ifndef CONFIG_NO_WPA
 	struct wpa_ie_data data;
+#endif /* CONFIG_NO_WPA */
 #ifdef CONFIG_MBO
 	const u8 *assoc_disallow;
 #endif /* CONFIG_MBO */
 #ifdef CONFIG_SAE
 	u8 rsnxe_capa = 0;
+	enum sae_pwe sae_pwe;
 #endif /* CONFIG_SAE */
 	const u8 *ie;
 
@@ -1280,9 +1287,11 @@
 	wpa = ie && ie[1];
 	ie = wpa_bss_get_rsne(wpa_s, bss, ssid, false);
 	wpa |= ie && ie[1];
+#ifndef CONFIG_NO_WPA
 	if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
 	    (data.key_mgmt & WPA_KEY_MGMT_OSEN))
 		rsn_osen = true;
+#endif /* CONFIG_NO_WPA */
 	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	osen = ie != NULL;
 
@@ -1449,9 +1458,10 @@
 #ifdef CONFIG_SAE
 	/* When using SAE Password Identifier and when operationg on the 6 GHz
 	 * band, only H2E is allowed. */
-	if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+	if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 	     is_6ghz_freq(bss->freq) || ssid->sae_password_id) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	    !(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
@@ -1737,7 +1747,7 @@
 }
 
 
-static struct wpa_bss *
+struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
 			  struct wpa_ssid *group,
 			  struct wpa_ssid **selected_ssid,
@@ -2167,7 +2177,8 @@
 
 int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
 					   struct wpa_bss *current_bss,
-					   struct wpa_bss *selected)
+					   struct wpa_bss *selected,
+					   bool poll_current)
 {
 	int min_diff, diff;
 	int cur_band_score, sel_band_score;
@@ -2222,7 +2233,7 @@
 	 * scan results may be a bit old, since we can very quickly get fresh
 	 * information about our currently associated AP.
 	 */
-	if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
+	if (poll_current && wpa_drv_signal_poll(wpa_s, &si) == 0 &&
 	    (si.data.avg_beacon_signal || si.data.avg_signal)) {
 		/*
 		 * Normalize avg_signal to the RSSI over 20 MHz, as the
@@ -2395,7 +2406,7 @@
 
 #ifndef CONFIG_NO_ROAMING
 	return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss,
-						      selected);
+						      selected, true);
 #else /* CONFIG_NO_ROAMING */
 	return 0;
 #endif /* CONFIG_NO_ROAMING */
@@ -2557,7 +2568,27 @@
 	}
 #endif /* CONFIG_NO_RANDOM_POOL */
 
-	wpa_s->last_scan_external = data && data->scan_info.external_scan;
+	if (data) {
+		size_t idx;
+
+		wpa_s->last_scan_external = data->scan_info.external_scan;
+		wpa_s->last_scan_num_ssids = data->scan_info.num_ssids;
+		for (idx = 0; idx < wpa_s->last_scan_num_ssids; idx++) {
+			/* Copy the SSID and its length */
+			if (idx >= WPAS_MAX_SCAN_SSIDS ||
+			    data->scan_info.ssids[idx].ssid_len > SSID_MAX_LEN)
+				continue;
+
+			os_memcpy(wpa_s->last_scan_ssids[idx].ssid,
+				  data->scan_info.ssids[idx].ssid,
+				  data->scan_info.ssids[idx].ssid_len);
+			wpa_s->last_scan_ssids[idx].ssid_len =
+				data->scan_info.ssids[idx].ssid_len;
+		}
+	} else {
+		wpa_s->last_scan_external = false;
+		wpa_s->last_scan_num_ssids = 0;
+	}
 
 	if (update_only) {
 		ret = 1;
@@ -3470,7 +3501,7 @@
 #endif /* CONFIG_WNM */
 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 						data->assoc_info.resp_ies_len);
-		if (wpa_s->hw_capab == CAPAB_VHT &&
+		if ((wpa_s->hw_capab & BIT(CAPAB_VHT)) &&
 		    get_ie(data->assoc_info.resp_ies,
 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
 			wpa_s->ieee80211ac = 1;
@@ -5698,7 +5729,8 @@
 	    WPA_GET_BE32(&payload[1]) == NAN_SDF_VENDOR_TYPE) {
 		payload += 5;
 		plen -= 5;
-		wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, freq, payload, plen);
+		wpas_nan_usd_rx_sdf(wpa_s, mgmt->sa, mgmt->bssid, freq,
+				    payload, plen);
 		return;
 	}
 #endif /* CONFIG_NAN_USD */
@@ -6271,6 +6303,37 @@
 #endif /* MAINLINE_SUPPLICANT */
 
 
+#ifdef CONFIG_PASN
+static int wpas_pasn_auth(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq)
+{
+#ifdef CONFIG_P2P
+	struct ieee802_11_elems elems;
+
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short Management frame");
+		return -2;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return -2;
+	}
+
+	if (elems.p2p2_ie && elems.p2p2_ie_len)
+		return wpas_p2p_pasn_auth_rx(wpa_s, mgmt, len, freq);
+#endif /* CONFIG_P2P */
+
+	return wpas_pasn_auth_rx(wpa_s, mgmt, len);
+}
+#endif /* CONFIG_PASN */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -6509,6 +6572,17 @@
 			break;
 #endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
+#ifdef CONFIG_P2P
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
+		    !wpa_s->pasn_auth_work &&
+		    wpa_s->p2p_pasn_auth_work &&
+		    wpas_p2p_pasn_auth_tx_status(wpa_s,
+						 data->tx_status.data,
+						 data->tx_status.data_len,
+						 data->tx_status.ack) == 0)
+			break;
+#endif /* CONFIG_P2P */
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
 		    wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data,
@@ -6792,8 +6866,8 @@
 			}
 #ifdef CONFIG_PASN
 			if (stype == WLAN_FC_STYPE_AUTH &&
-			    wpas_pasn_auth_rx(wpa_s, mgmt,
-					      data->rx_mgmt.frame_len) != -2)
+			    wpas_pasn_auth(wpa_s, mgmt, data->rx_mgmt.frame_len,
+					   data->rx_mgmt.freq) != -2)
 				break;
 #endif /* CONFIG_PASN */
 
@@ -6897,12 +6971,12 @@
 					data->eapol_rx.encrypted);
 		break;
 	case EVENT_SIGNAL_CHANGE:
-		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
-			"above=%d signal=%d noise=%d txrate=%lu",
-			data->signal_change.above_threshold,
-			data->signal_change.data.signal,
-			data->signal_change.current_noise,
-			data->signal_change.data.current_tx_rate);
+		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+			     "above=%d signal=%d noise=%d txrate=%lu",
+			     data->signal_change.above_threshold,
+			     data->signal_change.data.signal,
+			     data->signal_change.current_noise,
+			     data->signal_change.data.current_tx_rate);
 		wpa_bss_update_level(wpa_s->current_bss,
 				     data->signal_change.data.signal);
 		bgscan_notify_signal_change(
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 80fbe01..273bd25 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -460,6 +460,10 @@
 {
 	u8 *len;
 
+	if (wpa_s->drv_max_probe_req_ie_len <
+	    9 + ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
+		return;
+
 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 	len = wpabuf_put(ie, 1);
 
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 85c1ea8..869f0b3 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -464,6 +464,9 @@
 		case 160:
 			conf->op_class = 134;
 			break;
+		case 320:
+			conf->op_class = 137;
+			break;
 		default:
 			conf->op_class = 131;
 			break;
@@ -602,7 +605,8 @@
 	/* EID + 0-length (wildcard) mesh-id */
 	size_t ielen = 2;
 
-	if (wpabuf_resize(extra_ie, ielen) == 0) {
+	if (ielen <= wpa_s->drv_max_probe_req_ie_len &&
+	    wpabuf_resize(extra_ie, ielen) == 0) {
 		wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
 		wpabuf_put_u8(*extra_ie, 0);
 	}
diff --git a/wpa_supplicant/nan_usd.c b/wpa_supplicant/nan_usd.c
index 1125f95..577c8ac 100644
--- a/wpa_supplicant/nan_usd.c
+++ b/wpa_supplicant/nan_usd.c
@@ -321,7 +321,8 @@
 	cb.process_p2p_usd_elems = wpas_nan_process_p2p_usd_elems;
 #endif /* CONFIG_P2P */
 
-	wpa_s->nan_de = nan_de_init(wpa_s->own_addr, offload, false, &cb);
+	wpa_s->nan_de = nan_de_init(wpa_s->own_addr, offload, false,
+				    wpa_s->max_remain_on_chan, &cb);
 	if (!wpa_s->nan_de)
 		return -1;
 	return 0;
@@ -336,11 +337,12 @@
 
 
 void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
+			 const u8 *a3,
 			 unsigned int freq, const u8 *buf, size_t len)
 {
 	if (!wpa_s->nan_de)
 		return;
-	nan_de_rx_sdf(wpa_s->nan_de, src, freq, buf, len);
+	nan_de_rx_sdf(wpa_s->nan_de, src, a3, freq, buf, len);
 }
 
 
@@ -409,7 +411,7 @@
 		return -1;
 	ret = nan_de_update_publish(wpa_s->nan_de, publish_id, ssi);
 	if (ret == 0 && (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_NAN_OFFLOAD) &&
-	    wpas_drv_nan_cancel_publish(wpa_s, publish_id) < 0)
+	    wpas_drv_nan_update_publish(wpa_s, publish_id, ssi) < 0)
 		return -1;
 	return ret;
 }
diff --git a/wpa_supplicant/nan_usd.h b/wpa_supplicant/nan_usd.h
index ecb4973..59c0989 100644
--- a/wpa_supplicant/nan_usd.h
+++ b/wpa_supplicant/nan_usd.h
@@ -16,6 +16,7 @@
 int wpas_nan_usd_init(struct wpa_supplicant *wpa_s);
 void wpas_nan_usd_deinit(struct wpa_supplicant *wpa_s);
 void wpas_nan_usd_rx_sdf(struct wpa_supplicant *wpa_s, const u8 *src,
+			 const u8 *a3,
 			 unsigned int freq, const u8 *buf, size_t len);
 void wpas_nan_usd_flush(struct wpa_supplicant *wpa_s);
 int wpas_nan_usd_publish(struct wpa_supplicant *wpa_s, const char *service_name,
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 0cd7c63..d5a34c5 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -122,6 +122,8 @@
 			       enum wpa_states new_state,
 			       enum wpa_states old_state)
 {
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
 	if (wpa_s->p2p_mgmt)
 		return;
 
@@ -138,10 +140,14 @@
 	}
 #endif /* CONFIG_FST */
 
-	if (new_state == WPA_COMPLETED)
+	if (new_state == WPA_COMPLETED) {
 		wpas_p2p_notif_connected(wpa_s);
-	else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+		if (ssid)
+			wpa_drv_roaming(wpa_s, !ssid->bssid_set,
+					ssid->bssid_set ? ssid->bssid : NULL);
+	} else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED) {
 		wpas_p2p_notif_disconnected(wpa_s);
+	}
 
 	sme_state_changed(wpa_s);
 
@@ -918,6 +924,18 @@
 						 id, op_freq);
 }
 
+void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+				   const u8 *src, u16 bootstrap_method)
+{
+	wpas_dbus_signal_p2p_bootstrap_req(wpa_s, src, bootstrap_method);
+}
+
+void wpas_notify_p2p_bootstrap_completed(struct wpa_supplicant *wpa_s,
+					 const u8 *src, int status)
+{
+	wpas_dbus_signal_p2p_bootstrap_completed(wpa_s, src, status);
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -1506,15 +1524,20 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT
-		"subscribe_id=%d publish_id=%d address=" MACSTR
-		" fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
-		subscribe_id, peer_publish_id, MAC2STR(peer_addr),
-		fsd, fsd_gas, srv_proto_type, ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_DISCOVERY_RESULT
+		       "subscribe_id=%d publish_id=%d address=" MACSTR
+		       " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+		       subscribe_id, peer_publish_id, MAC2STR(peer_addr),
+		       fsd, fsd_gas, srv_proto_type, ssi_hex);
 	os_free(ssi_hex);
 
 	wpas_aidl_notify_usd_service_discovered(wpa_s, srv_proto_type,
 		subscribe_id, peer_publish_id, peer_addr, fsd, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_discovery_result(wpa_s, srv_proto_type,
+					      subscribe_id, peer_publish_id,
+					      peer_addr, fsd, fsd_gas,
+					      ssi, ssi_len);
 }
 
 
@@ -1531,15 +1554,19 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_REPLIED
-		"publish_id=%d address=" MACSTR
-		" subscribe_id=%d srv_proto_type=%u ssi=%s",
-		publish_id, MAC2STR(peer_addr), peer_subscribe_id,
-		srv_proto_type, ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_REPLIED
+		       "publish_id=%d address=" MACSTR
+		       " subscribe_id=%d srv_proto_type=%u ssi=%s",
+		       publish_id, MAC2STR(peer_addr), peer_subscribe_id,
+		       srv_proto_type, ssi_hex);
 	os_free(ssi_hex);
 
 	wpas_aidl_notify_usd_publish_replied(wpa_s, srv_proto_type,
 		publish_id, peer_subscribe_id, peer_addr, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_replied(wpa_s, srv_proto_type, publish_id,
+				     peer_subscribe_id, peer_addr,
+				     ssi, ssi_len);
 }
 
 
@@ -1554,13 +1581,16 @@
 		return;
 	if (ssi)
 		wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
-	wpa_msg(wpa_s, MSG_INFO, NAN_RECEIVE
-		"id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
-		id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_RECEIVE
+		       "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
+		       id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
 	os_free(ssi_hex);
 
 	wpas_aidl_notify_usd_message_received(wpa_s, id, peer_instance_id,
 		peer_addr, ssi, ssi_len);
+
+	wpas_dbus_signal_nan_receive(wpa_s, id, peer_instance_id, peer_addr,
+				     ssi, ssi_len);
 }
 
 
@@ -1583,10 +1613,14 @@
 					int publish_id,
 					enum nan_de_reason reason)
 {
-	wpa_msg(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
-		"publish_id=%d reason=%s",
-		publish_id, nan_reason_txt(reason));
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_PUBLISH_TERMINATED
+		       "publish_id=%d reason=%s",
+		       publish_id, nan_reason_txt(reason));
+        
 	wpas_aidl_notify_usd_publish_terminated(wpa_s, publish_id, reason);
+
+	wpas_dbus_signal_nan_publish_terminated(wpa_s, publish_id,
+						nan_reason_txt(reason));
 }
 
 
@@ -1594,10 +1628,14 @@
 					  int subscribe_id,
 					  enum nan_de_reason reason)
 {
-	wpa_msg(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
-		"subscribe_id=%d reason=%s",
-		subscribe_id, nan_reason_txt(reason));
+	wpa_msg_global(wpa_s, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
+		       "subscribe_id=%d reason=%s",
+		       subscribe_id, nan_reason_txt(reason));
+
 	wpas_aidl_notify_usd_subscribe_terminated(wpa_s, subscribe_id, reason);
+
+	wpas_dbus_signal_nan_subscribe_terminated(wpa_s, subscribe_id,
+						  nan_reason_txt(reason));
 }
 
 #endif /* CONFIG_NAN_USD */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 4e172de..5775e37 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -166,6 +166,10 @@
 void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *go_dev_addr,
 					 const u8 *bssid, int id, int op_freq);
+void wpas_notify_p2p_bootstrap_req(struct wpa_supplicant *wpa_s,
+				   const u8 *src, u16 bootstrap_method);
+void wpas_notify_p2p_bootstrap_completed(struct wpa_supplicant *wpa_s,
+					 const u8 *src, int status);
 void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
 				    struct wpa_ssid *ssid);
 void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 768b917..0c88e17 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -23,8 +23,10 @@
 #include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
 #include "ap/dfs.h"
+#include "ap/wpa_auth.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "ap.h"
@@ -1229,12 +1231,18 @@
 	s->bssid_set = 1;
 	os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
 	s->mode = ssid->mode;
-	s->auth_alg = WPA_AUTH_ALG_OPEN;
-	s->key_mgmt = WPA_KEY_MGMT_PSK;
-	s->proto = WPA_PROTO_RSN;
+	s->auth_alg = ssid->auth_alg;
+	s->key_mgmt = ssid->key_mgmt;
+	s->proto = ssid->proto;
 	s->pbss = ssid->pbss;
+	s->pmk_valid = ssid->pmk_valid;
 	s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	s->export_keys = 1;
+
+	if (ssid->sae_password) {
+		os_free(s->sae_password);
+		s->sae_password = os_strdup(ssid->sae_password);
+	}
 	if (ssid->passphrase) {
 		os_free(s->passphrase);
 		s->passphrase = os_strdup(ssid->passphrase);
@@ -1392,6 +1400,106 @@
 }
 
 
+int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_dev_ik *ik;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next)
+		wpa_config_remove_identity(wpa_s->conf, ik->id);
+
+	if (wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+		return -1;
+	}
+	return 0;
+}
+
+
+static void wpas_p2p_store_identity(struct wpa_supplicant *wpa_s, u8 cipher,
+				    const u8 *dik_data, size_t dik_len,
+				    const u8 *pmk, size_t pmk_len,
+				    const u8 *pmkid)
+{
+	struct wpa_dev_ik *ik;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next) {
+		if (dik_len == wpabuf_len(ik->dik) &&
+		    os_memcmp(dik_data, wpabuf_head(ik->dik), dik_len) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Remove previous device identity entry for matching DIK");
+			wpa_config_remove_identity(wpa_s->conf, ik->id);
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Create a new device identity entry");
+	ik = wpa_config_add_identity(wpa_s->conf);
+	if (!ik)
+		return;
+
+	ik->dik = wpabuf_alloc_copy(dik_data, dik_len);
+	if (!ik->dik)
+		goto fail;
+	ik->pmk = wpabuf_alloc_copy(pmk, pmk_len);
+	if (!ik->pmk)
+		goto fail;
+	ik->pmkid = wpabuf_alloc_copy(pmkid, PMKID_LEN);
+	if (!ik->pmkid)
+		goto fail;
+
+	ik->dik_cipher = cipher;
+
+	if (wpa_s->conf->update_config &&
+	    wpa_config_write(wpa_s->confname, wpa_s->conf))
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+	return;
+
+fail:
+	wpa_config_remove_identity(wpa_s->conf, ik->id);
+}
+
+
+static void wpas_p2p_store_go_identity(struct wpa_supplicant *wpa_s,
+				       const u8 *go_dev_addr, const u8 *bssid)
+{
+	int ret;
+	u8 cipher;
+	const u8 *dik_data, *pmk, *pmkid;
+	size_t dik_len, pmk_len;
+	u8 iface_addr[ETH_ALEN];
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (!wpa_s->p2p2)
+		return;
+
+	ret = p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, go_dev_addr,
+				       &dik_data, &dik_len, &cipher);
+	if (ret)
+		return;
+
+	ret = p2p_get_interface_addr(p2p_wpa_s->global->p2p, go_dev_addr,
+				     iface_addr);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Fetch PMK for GO BSSID " MACSTR,
+			   MAC2STR(bssid));
+		os_memcpy(iface_addr, bssid, ETH_ALEN);
+	}
+	ret = wpa_sm_pmksa_get_pmk(wpa_s->wpa, iface_addr, &pmk, &pmk_len,
+				   &pmkid);
+	if (ret)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Storing Device identity of GO (Interface Addr " MACSTR
+		   ")",
+		   MAC2STR(iface_addr));
+	wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len, pmk,
+				pmk_len, pmkid);
+}
+
+
 static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 					   int success, int already_deleted)
 {
@@ -1690,8 +1798,11 @@
 	if (listen_freq != (int) freq && send_freq != (int) freq) {
 		int res;
 
-		wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)",
-			   listen_freq, send_freq, freq);
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u dst="
+			   MACSTR " src=" MACSTR " bssid=" MACSTR,
+			   listen_freq, send_freq, freq, MAC2STR(dst),
+			   MAC2STR(src), MAC2STR(bssid));
 		res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
 					    len, wait_time);
 		if (res == 0 && scheduled)
@@ -1722,6 +1833,125 @@
 }
 
 
+#ifdef CONFIG_PASN
+
+struct wpa_p2p_pasn_auth_work {
+	u8 peer_addr[ETH_ALEN];
+	int freq;
+	bool verify;
+	int force_freq;
+	int pref_freq;
+	enum p2p_invite_role role;
+	u8 *ssid;
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+	u8 go_dev_addr[ETH_ALEN];
+};
+
+
+static void wpas_p2p_pasn_free_auth_work(struct wpa_p2p_pasn_auth_work *awork)
+{
+	if (!awork)
+		return;
+	os_free(awork->ssid);
+	os_free(awork);
+}
+
+
+static void wpas_p2p_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "P2P PASN: Cancel p2p-pasn-start-auth work");
+
+	/* Remove pending/started work */
+	radio_remove_works(wpa_s, "p2p-pasn-start-auth", 0);
+}
+
+
+static void wpas_p2p_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	int ret;
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_p2p_pasn_auth_work *awork = work->ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	const u8 *peer_addr = NULL;
+	const u8 *bssid = NULL;
+	const u8 *go_dev_addr = NULL;
+
+	if (deinit) {
+		if (!work->started) {
+			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					     wpa_s->p2pdev, NULL);
+		}
+		wpas_p2p_pasn_free_auth_work(awork);
+		return;
+	}
+
+	if (!is_zero_ether_addr(awork->peer_addr))
+		peer_addr = awork->peer_addr;
+	if (!is_zero_ether_addr(awork->bssid))
+		bssid = awork->bssid;
+	if (!is_zero_ether_addr(awork->go_dev_addr))
+		go_dev_addr = awork->go_dev_addr;
+
+
+	if (awork->verify)
+		ret = p2p_initiate_pasn_verify(p2p, peer_addr, awork->freq,
+					       awork->role, bssid, awork->ssid,
+					       awork->ssid_len,
+					       awork->force_freq, go_dev_addr,
+					       awork->pref_freq);
+	else
+		ret = p2p_initiate_pasn_auth(p2p, peer_addr, awork->freq);
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P PASN: Failed to start PASN authentication");
+		goto fail;
+	}
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->p2pdev, NULL);
+	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+			       wpas_p2p_group_formation_timeout,
+			       wpa_s->p2pdev, NULL);
+	wpa_s->p2p_pasn_auth_work = work;
+	return;
+
+fail:
+	wpas_p2p_pasn_free_auth_work(awork);
+	work->ctx = NULL;
+	radio_work_done(work);
+}
+
+
+static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+				       const u8 *peer_addr, int freq)
+{
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	wpas_p2p_pasn_cancel_auth_work(wpa_s);
+	wpa_s->p2p_pasn_auth_work = NULL;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->freq = freq;
+	os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
+
+	if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1,
+			   wpas_p2p_pasn_auth_start_cb, awork) < 0) {
+		wpas_p2p_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P PASN: Authentication work successfully added");
+	return 0;
+}
+
+#endif /* CONFIG_PASN */
+
+
 static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *params)
 {
@@ -1735,6 +1965,101 @@
 }
 
 
+static void wpas_start_gc(struct wpa_supplicant *wpa_s,
+			  struct p2p_go_neg_results *res)
+{
+	struct os_reltime now;
+	struct wpa_ssid *ssid;
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (!res->ssid_len) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: SSID info not present");
+		return;
+	}
+
+	wpa_s->group_formation_reported = 0;
+	wpa_printf(MSG_DEBUG, "P2P: Start connect for peer " MACSTR
+		   " dev_addr " MACSTR,
+		   MAC2STR(res->peer_interface_addr),
+		   MAC2STR(res->peer_device_addr));
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start connect for SSID",
+			  res->ssid, res->ssid_len);
+	wpa_supplicant_ap_deinit(wpa_s);
+	wpas_copy_go_neg_results(wpa_s, res);
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Could not add network for client");
+		return;
+	}
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	wpa_config_set_network_defaults(ssid);
+	ssid->temporary = 1;
+	ssid->p2p_group = 1;
+
+	ssid->ssid = os_memdup(res->ssid, res->ssid_len);
+	if (!ssid->ssid)
+		return;
+	ssid->ssid_len = res->ssid_len;
+
+	os_memcpy(ssid->bssid, res->peer_interface_addr, ETH_ALEN);
+
+	if (res->akmp == WPA_KEY_MGMT_PASN && res->sae_password[0]) {
+		ssid->auth_alg = WPA_AUTH_ALG_SAE;
+		ssid->sae_password = os_strdup(res->sae_password);
+		if (!ssid->sae_password)
+			return;
+	} else if (res->akmp == WPA_KEY_MGMT_SAE && res->pmk_len) {
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		entry = os_zalloc(sizeof(*entry));
+		if (!entry)
+			return;
+		os_memcpy(entry->aa, res->peer_interface_addr, ETH_ALEN);
+		os_memcpy(entry->pmkid, res->pmkid, PMKID_LEN);
+		entry->pmk_len = res->pmk_len;
+		os_memcpy(entry->pmk, res->pmk, res->pmk_len);
+		entry->akmp = res->akmp;
+		os_get_reltime(&now);
+		entry->expiration = now.sec + 43200;
+		entry->reauth_time = now.sec + 43200 * 70 / 100;
+		entry->network_ctx = ssid;
+		os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
+
+		wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+		ssid->pmk_valid = true;
+	} else if (res->akmp == WPA_KEY_MGMT_SAE && res->sae_password[0]) {
+		ssid->auth_alg = WPA_AUTH_ALG_SAE;
+		ssid->sae_password = os_strdup(res->sae_password);
+		if (!ssid->sae_password)
+			return;
+	}
+
+	if (res->psk_set) {
+		os_memcpy(ssid->psk, res->psk, 32);
+		ssid->psk_set = 1;
+	}
+	ssid->proto = WPA_PROTO_RSN;
+	ssid->key_mgmt = WPA_KEY_MGMT_SAE;
+	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	ssid->group_cipher = WPA_CIPHER_CCMP;
+	if (res->cipher)
+		ssid->pairwise_cipher |= res->cipher;
+	ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+	ssid->disabled = 0;
+	wpa_s->show_group_started = 1;
+	wpa_s->p2p_in_invitation = 1;
+	wpa_s->p2p_go_group_formation_completed = 0;
+	wpa_s->global->p2p_group_formation = wpa_s;
+	ssid->rsn_overriding = RSN_OVERRIDING_ENABLED;
+
+	wpa_s->current_ssid = ssid;
+	wpa_supplicant_update_scan_results(wpa_s, res->peer_interface_addr);
+	wpa_supplicant_select_network(wpa_s, ssid);
+}
+
+
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *res)
 {
@@ -1879,6 +2204,19 @@
 		return;
 	}
 
+	if (wpa_s->ap_iface && params->p2p2 &&
+	    params->akmp == WPA_KEY_MGMT_SAE) {
+		struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+
+		wpa_auth_pmksa_add_sae(hapd->wpa_auth,
+				       params->peer_device_addr,
+				       params->pmk, params->pmk_len,
+				       params->pmkid, WPA_KEY_MGMT_SAE);
+		hostapd_add_pmkid(hapd, params->peer_device_addr,
+				  params->pmk, params->pmk_len,
+				  params->pmkid, WPA_KEY_MGMT_SAE);
+	}
+
 	p2p_go_save_group_common_freqs(wpa_s, params);
 	p2p_go_dump_common_freqs(wpa_s);
 
@@ -1944,13 +2282,21 @@
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
 	if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
 					      params->peer_interface_addr)) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
 			   "filtering");
 		return;
 	}
+
+	if (params->p2p2) {
+		wpas_group_formation_completed(wpa_s, 1, 0);
+		wpa_printf(MSG_DEBUG,
+			   "P2P2: Group formation completed - first connection in progress");
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
 	if (params->wps_method == WPS_PBC) {
 		wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
 					  params->peer_device_addr);
@@ -1971,6 +2317,7 @@
 	} else if (wpa_s->p2p_pin[0])
 		wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
 					  wpa_s->p2p_pin, NULL, 0, 0);
+out:
 	os_free(wpa_s->go_params);
 	wpa_s->go_params = NULL;
 }
@@ -2053,9 +2400,9 @@
 }
 
 
-static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
-			      struct p2p_go_neg_results *params,
-			      int group_formation)
+static void wpas_start_go(struct wpa_supplicant *wpa_s,
+			  struct p2p_go_neg_results *params,
+			  int group_formation, enum wpa_p2p_mode p2p_mode)
 {
 	struct wpa_ssid *ssid;
 
@@ -2150,6 +2497,24 @@
 		wpa_config_update_psk(ssid);
 	ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity;
 
+	ssid->p2p_mode = p2p_mode;
+	if (params->p2p2) {
+		if (params->akmp == WPA_KEY_MGMT_SAE)
+			ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		else
+			ssid->auth_alg |= WPA_AUTH_ALG_SAE;
+
+		ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN;
+		ssid->sae_password = os_strdup(params->sae_password);
+		/* In PCC, RSNE indicates PMF to be disabled while RSNOE/RSNO2E
+		 * requires PMF for SAE. */
+		if (ssid->p2p_mode != WPA_P2P_MODE_WFD_PCC)
+			ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+		if (params->cipher)
+			ssid->pairwise_cipher |= params->cipher;
+	}
+
 	wpa_s->ap_configured_cb = p2p_go_configured;
 	wpa_s->ap_configured_cb_ctx = wpa_s;
 	wpa_s->ap_configured_cb_data = wpa_s->go_params;
@@ -2364,6 +2729,7 @@
 	wpa_s->global->pending_group_iface_for_p2ps = 0;
 
 	wpas_p2p_clone_config(group_wpa_s, wpa_s);
+	group_wpa_s->p2p2 = wpa_s->p2p2;
 
 	if (wpa_s->conf->p2p_interface_random_mac_addr) {
 		if (wpa_drv_set_mac_addr(group_wpa_s,
@@ -2396,6 +2762,14 @@
 					     void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+#endif /* CONFIG_PASN */
+
 	wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
 	wpas_p2p_group_formation_failed(wpa_s, 0);
 }
@@ -2450,6 +2824,49 @@
 }
 
 
+static void wpas_set_go_security_config(void *ctx,
+					struct p2p_go_neg_results *params)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *tmp, *ifs = NULL;
+	struct hostapd_data *hapd;
+
+	if (!params->p2p2)
+		return;
+
+	dl_list_for_each(tmp, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		struct wpa_ssid *ssid = tmp->current_ssid;
+
+		if (ssid && ssid->mode == WPAS_MODE_P2P_GO &&
+		    ssid->ssid && ssid->ssid_len == params->ssid_len &&
+		    os_memcmp(ssid->ssid, params->ssid, params->ssid_len) == 0)
+		{
+			ifs = tmp;
+			break;
+		}
+	}
+
+	if (!ifs || !ifs->ap_iface)
+		return;
+
+	hapd = ifs->ap_iface->bss[0];
+	hapd->conf->wps_state = 0;
+
+	if (params->akmp == WPA_KEY_MGMT_SAE) {
+		wpa_printf(MSG_DEBUG, "P2P: Adding PMK for peer: " MACSTR,
+			   MAC2STR(params->peer_device_addr));
+		wpa_auth_pmksa_add_sae(hapd->wpa_auth,
+				       params->peer_device_addr,
+				       params->pmk, params->pmk_len,
+				       params->pmkid, WPA_KEY_MGMT_SAE);
+		hostapd_add_pmkid(hapd, params->peer_device_addr,
+				  params->pmk, params->pmk_len,
+				  params->pmkid, WPA_KEY_MGMT_SAE);
+	}
+}
+
+
 static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -2461,6 +2878,13 @@
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
 
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+#endif /* CONFIG_PASN */
+
 	if (res->status) {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_GO_NEG_FAILURE "status=%d",
@@ -2534,12 +2958,18 @@
 		os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
 			  sizeof(group_wpa_s->p2p_pin));
 		group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+		group_wpa_s->p2p2 = res->p2p2;
+		group_wpa_s->p2p_bootstrap = wpa_s->p2p_bootstrap;
 	}
+
 	if (res->role_go) {
-		wpas_start_wps_go(group_wpa_s, res, 1);
+		wpas_start_go(group_wpa_s, res, 1, group_wpa_s->p2p_mode);
 	} else {
 		os_get_reltime(&group_wpa_s->scan_min_time);
-		wpas_start_wps_enrollee(group_wpa_s, res);
+		if (res->p2p2)
+			wpas_start_gc(group_wpa_s, res);
+		else
+			wpas_start_wps_enrollee(group_wpa_s, res);
 	}
 
 	wpa_s->global->p2p_long_listen = 0;
@@ -2646,8 +3076,7 @@
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
 		       " p2p_dev_addr=" MACSTR
-		       " pri_dev_type=%s name='%s' config_methods=0x%x "
-		       "dev_capab=0x%x group_capab=0x%x%s%s%s%s%s new=%d",
+		       " pri_dev_type=%s name='%s' config_methods=0x%x dev_capab=0x%x group_capab=0x%x%s%s%s%s%s new=%d pcea_cap_info=0x%x bootstrap_methods=0x%x pasn_type=0x%x",
 		       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
 		       wps_dev_type_bin2str(info->pri_dev_type, devtype,
 					    sizeof(devtype)),
@@ -2658,7 +3087,9 @@
 		       wfd_r2_dev_info_hex ? " wfd_r2_dev_info=0x" : "",
 		       wfd_r2_dev_info_hex ? wfd_r2_dev_info_hex : "",
 		       info->vendor_elems ? " vendor_elems=1" : "",
-		       new_device);
+		       new_device, info->pcea_cap_info,
+		       info->pairing_config.bootstrap_methods,
+		       info->pairing_config.pasn_type);
 
 done:
 	os_free(wfd_dev_info_hex);
@@ -2831,10 +3262,13 @@
 
 	wpas_p2p_listen_work_done(wpa_s);
 
-	if (radio_work_pending(wpa_s, "p2p-listen")) {
+	if (!wpa_s->p2p_removing_listen_work &&
+	    radio_work_pending(wpa_s, "p2p-listen")) {
+		wpa_s->p2p_removing_listen_work = true;
 		wpa_printf(MSG_DEBUG,
 			   "P2P: p2p-listen is still pending - remove it");
 		radio_remove_works(wpa_s, "p2p-listen", 0);
+		wpa_s->p2p_removing_listen_work = false;
 	}
 }
 
@@ -3217,7 +3651,7 @@
 				  size_t ssid_len, int *go, u8 *group_bssid,
 				  int *force_freq, int persistent_group,
 				  const struct p2p_channels *channels,
-				  int dev_pw_id)
+				  int dev_pw_id, bool p2p2)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3281,7 +3715,7 @@
 
 	for (s = wpa_s->conf->ssid; s; s = s->next) {
 		if (s->disabled == 2 &&
-		    ether_addr_equal(s->bssid, go_dev_addr) &&
+		    (p2p2 || ether_addr_equal(s->bssid, go_dev_addr)) &&
 		    s->ssid_len == ssid_len &&
 		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
 			break;
@@ -3376,7 +3810,8 @@
 static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
 				     const u8 *ssid, size_t ssid_len,
 				     const u8 *go_dev_addr, u8 status,
-				     int op_freq)
+				     int op_freq, const u8 *pmkid,
+				     const u8 *pmk, size_t pmk_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3416,7 +3851,7 @@
 				wpa_s->conf->p2p_go_edmg, NULL,
 				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
 				1, is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
-				NULL);
+				bssid, sa, pmkid, pmk, pmk_len);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpa_msg_global(wpa_s, MSG_INFO,
@@ -3534,12 +3969,20 @@
 static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
 				   const struct p2p_channels *channels,
 				   const u8 *peer, int neg_freq,
-				   int peer_oper_freq)
+				   int peer_oper_freq, const u8 *pmkid,
+				   const u8 *pmk, size_t pmk_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid;
 	int freq;
 
+#ifdef CONFIG_PASN
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+#endif /* CONFIG_PASN */
+
 	if (bssid) {
 		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
 			       "status=%d " MACSTR,
@@ -3647,7 +4090,7 @@
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
 				      0, 1,
 				      is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
-				      NULL);
+				      bssid, peer, pmkid, pmk, pmk_len);
 }
 
 
@@ -4716,10 +5159,13 @@
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0, 0, false, 0, NULL);
+					0, 0, false, 0, NULL, NULL, NULL, NULL,
+					0);
 			} else if (response_done) {
 				wpas_p2p_group_add(wpa_s, 1, freq,
-						   0, 0, 0, 0, 0, 0, false);
+						   0, 0, 0, 0, 0, 0, false,
+						   wpa_s->p2p2,
+						   WPA_P2P_MODE_WFD_R1);
 			}
 
 			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
@@ -4839,10 +5285,12 @@
 			NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
 			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0,
-			is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL);
+			is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL, NULL,
+			NULL, NULL, 0);
 	} else {
 		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0,
-				   is_p2p_allow_6ghz(wpa_s->global->p2p));
+				   is_p2p_allow_6ghz(wpa_s->global->p2p),
+				   wpa_s->p2p2, WPA_P2P_MODE_WFD_R1);
 	}
 
 	return 1;
@@ -4877,7 +5325,7 @@
 			 wpa_s->p2p_go_he,
 			 wpa_s->p2p_go_edmg,
 			 NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p),
-			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL);
+			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL, false);
 }
 
 
@@ -4903,6 +5351,8 @@
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_BOOTSTRAP_REQUEST MACSTR
 		       " bootstrap_method=%u", MAC2STR(addr), bootstrap_method);
+
+	wpas_notify_p2p_bootstrap_req(wpa_s, addr, bootstrap_method);
 }
 
 
@@ -4911,18 +5361,175 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	wpas_notify_p2p_bootstrap_completed(wpa_s, addr, status);
+
 	if (status) {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_BOOTSTRAP_FAILURE MACSTR " status=%d",
 			       MAC2STR(addr), status);
-	} else {
-		wpa_msg_global(wpa_s, MSG_INFO,
-			       P2P_EVENT_BOOTSTRAP_SUCCESS MACSTR " status=%d",
-			       MAC2STR(addr), status);
+		return;
 	}
+
+	wpa_msg_global(wpa_s, MSG_INFO,
+		       P2P_EVENT_BOOTSTRAP_SUCCESS MACSTR " status=%d",
+		       MAC2STR(addr), status);
+
+#ifdef CONFIG_PASN
+	wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq);
+#endif /* CONFIG_PASN */
 }
 
 
+static void wpas_validate_dira(void *ctx, const u8 *peer_addr,
+			       const u8 *dira, size_t dira_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int ret;
+	u8 tag[DEVICE_MAX_HASH_LEN];
+	struct wpa_dev_ik *ik;
+	const u8 *addr[3];
+	size_t len[3];
+	const char *label = "DIR";
+
+	if (dira_len < 1 || dira[0] != DIRA_CIPHER_VERSION_128) {
+		wpa_printf(MSG_ERROR,
+			   "P2P2: Unsupported DIRA cipher version %d", dira[0]);
+		return;
+	}
+
+	if (dira_len < 1 + DEVICE_IDENTITY_NONCE_LEN + DEVICE_IDENTITY_TAG_LEN)
+	{
+		wpa_printf(MSG_INFO, "P2P2: Truncated DIRA (length %zu)",
+			   dira_len);
+		return;
+	}
+
+	addr[0] = (const u8 *) label;
+	len[0] = DIR_STR_LEN;
+	addr[1] = peer_addr;
+	len[1] = ETH_ALEN;
+	addr[2] = &dira[1];
+	len[2] = DEVICE_IDENTITY_NONCE_LEN;
+
+	for (ik = wpa_s->conf->identity; ik; ik = ik->next) {
+		if (wpabuf_len(ik->dik) != DEVICE_IDENTITY_KEY_LEN ||
+		    ik->dik_cipher != DIRA_CIPHER_VERSION_128)
+			continue;
+
+		ret = hmac_sha256_vector(wpabuf_head(ik->dik),
+					 DEVICE_IDENTITY_KEY_LEN,
+					 3, addr, len, tag);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR,
+				   "P2P2: Failed to derive DIRA Tag");
+			return;
+		}
+
+		if (os_memcmp(tag, &dira[1 + DEVICE_IDENTITY_NONCE_LEN],
+			      DEVICE_IDENTITY_TAG_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "P2P2: DIRA Tag matched");
+			break;
+		}
+	}
+
+	if (!ik)
+		return;
+
+#ifdef CONFIG_PASN
+	p2p_pasn_pmksa_set_pmk(wpa_s->global->p2p, wpa_s->global->p2p_dev_addr,
+			       peer_addr,
+			       wpabuf_head(ik->pmk), wpabuf_len(ik->pmk),
+			       wpabuf_head(ik->pmkid));
+#endif /* CONFIG_PASN */
+}
+
+
+#ifdef CONFIG_PASN
+
+static int wpas_p2p_initiate_pasn_verify(struct wpa_supplicant *wpa_s,
+					 const u8 *peer,
+					 enum p2p_invite_role role,
+					 const u8 *bssid, const u8 *ssid,
+					 size_t ssid_len,
+					 unsigned int force_freq,
+					 const u8 *go_dev_addr,
+					 unsigned int pref_freq)
+{
+	int freq;
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	wpas_p2p_pasn_cancel_auth_work(wpa_s);
+	wpa_s->p2p_pasn_auth_work = NULL;
+
+	freq = p2p_get_listen_freq(wpa_s->global->p2p, peer);
+	if (freq == -1)
+		return -1;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->verify = 1;
+	awork->role = role;
+	awork->freq = freq;
+	awork->force_freq = force_freq;
+	awork->pref_freq = pref_freq;
+	os_memcpy(awork->peer_addr, peer, ETH_ALEN);
+	if (go_dev_addr)
+		os_memcpy(awork->go_dev_addr, go_dev_addr, ETH_ALEN);
+	if (bssid)
+		os_memcpy(awork->bssid, bssid, ETH_ALEN);
+	if (ssid_len) {
+		awork->ssid = os_zalloc(ssid_len);
+		if (!awork->ssid) {
+			os_free(awork);
+			return -1;
+		}
+		os_memcpy(awork->ssid, ssid, ssid_len);
+		awork->ssid_len = ssid_len;
+	}
+
+	if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1,
+			   wpas_p2p_pasn_auth_start_cb, awork) < 0) {
+		wpas_p2p_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P PASN: Auth work successfully added");
+	return 0;
+}
+
+
+static int wpas_p2p_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len,
+				   int noack, unsigned int freq,
+				   unsigned int wait)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
+static int wpas_p2p_prepare_data_element(void *ctx, const u8 *peer_addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_prepare_data_element(p2p, peer_addr);
+}
+
+
+static int wpas_p2p_parse_data_element(void *ctx, const u8 *data, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_parse_data_element(p2p, data, len);
+}
+
+#endif /* CONFIG_PASN */
+
+
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
 {
 	int ret = 0;
@@ -5018,6 +5625,7 @@
 	p2p.send_action = wpas_send_action;
 	p2p.send_action_done = wpas_send_action_done;
 	p2p.go_neg_completed = wpas_go_neg_completed;
+	p2p.set_go_security_config = wpas_set_go_security_config;
 	p2p.go_neg_req_rx = wpas_go_neg_req_rx;
 	p2p.dev_found = wpas_dev_found;
 	p2p.dev_lost = wpas_dev_lost;
@@ -5050,6 +5658,12 @@
 	p2p.register_bootstrap_comeback = wpas_p2p_register_bootstrap_comeback;
 	p2p.bootstrap_req_rx = wpas_bootstrap_req_rx;
 	p2p.bootstrap_completed = wpas_bootstrap_completed;
+	p2p.validate_dira = wpas_validate_dira;
+#ifdef CONFIG_PASN
+	p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mgmt;
+	p2p.prepare_data_element = wpas_p2p_prepare_data_element;
+	p2p.parse_data_element = wpas_p2p_parse_data_element;
+#endif /* CONFIG_PASN */
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -5193,6 +5807,18 @@
 				   "P2P: Failed to update configuration");
 	}
 
+	p2p.pairing_config.enable_pairing_setup =
+		wpa_s->conf->p2p_pairing_setup;
+	p2p.pairing_config.enable_pairing_cache =
+		wpa_s->conf->p2p_pairing_cache;
+	p2p.pairing_config.bootstrap_methods =
+		wpa_s->conf->p2p_bootstrap_methods;
+	p2p.pairing_config.pasn_type = wpa_s->conf->p2p_pasn_type;
+	p2p.comeback_after = wpa_s->conf->p2p_comeback_after;
+	p2p.reg_info = wpa_s->conf->p2p_reg_info;
+	p2p.twt_power_mgmt = wpa_s->conf->p2p_twt_power_mgmt;
+	p2p.chan_switch_req_enable = wpa_s->conf->p2p_chan_switch_req_enable;
+
 	global->p2p = p2p_init(&p2p);
 	if (global->p2p == NULL)
 		return -1;
@@ -5325,6 +5951,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static int wpas_p2p_config_sae_password(struct wpa_supplicant *wpa_s,
+					struct wpa_ssid *ssid)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p || !ssid->sae_password)
+		return -2;
+
+	return p2p_config_sae_password(p2p, ssid->sae_password);
+}
+#endif /* CONFIG_PASN */
+
+
 static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
 				 const u8 *peer_addr,
 				 enum p2p_wps_method wps_method,
@@ -5489,7 +6129,7 @@
 	if (scan_res)
 		wpas_p2p_scan_res_handler(wpa_s, scan_res);
 
-	if (wpa_s->p2p_auto_pd) {
+	if (!wpa_s->p2p2 && wpa_s->p2p_auto_pd) {
 		int join = wpas_p2p_peer_go(wpa_s,
 					    wpa_s->pending_join_dev_addr);
 		if (join == 0 &&
@@ -5530,15 +6170,22 @@
 		return;
 	}
 
-	if (wpa_s->p2p_auto_join) {
+	if (wpa_s->p2p2 || wpa_s->p2p_auto_join) {
 		int join = wpas_p2p_peer_go(wpa_s,
 					    wpa_s->pending_join_dev_addr);
-		if (join < 0) {
-			wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
-				   "running a GO -> use GO Negotiation");
-			wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
-				       P2P_EVENT_FALLBACK_TO_GO_NEG
-				       "reason=peer-not-running-GO");
+
+		if (wpa_s->p2p2 || join < 0) {
+			if (join < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Peer was not found to be running a GO -> use GO Negotiation");
+				wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
+					       P2P_EVENT_FALLBACK_TO_GO_NEG
+					       "reason=peer-not-running-GO");
+			}
+
+			if (wpa_s->p2p2)
+				wpa_printf(MSG_DEBUG,
+					   "P2P2: Initiate GO negotiation and provisioning using PASN Authentication");
 			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
 					 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
 					 wpa_s->p2p_persistent_group, 0, 0, 0,
@@ -5555,7 +6202,9 @@
 					 NULL, 0,
 					 is_p2p_allow_6ghz(wpa_s->global->p2p),
 					 wpa_s->p2p2, wpa_s->p2p_bootstrap,
-					 NULL);
+					 wpa_s->pending_join_password[0] ?
+					 wpa_s->pending_join_password : NULL,
+					 false);
 			return;
 		}
 
@@ -5709,7 +6358,7 @@
 {
 	int ret;
 	struct wpa_driver_scan_params params;
-	struct wpabuf *wps_ie, *ies;
+	struct wpabuf *wps_ie = NULL, *ies;
 	size_t ielen;
 	int freqs[2] = { 0, 0 };
 	unsigned int bands;
@@ -5729,13 +6378,16 @@
 		wpa_s->p2p_join_ssid_len = 0;
 	}
 
-	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
-					wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
-					NULL);
-	if (wps_ie == NULL) {
-		wpas_p2p_scan_res_join(wpa_s, NULL);
-		return;
+	if (!wpa_s->p2p2) {
+		wpa_s->wps->dev.p2p = 1;
+		wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT,
+						&wpa_s->wps->dev,
+						wpa_s->wps->uuid,
+						WPS_REQ_ENROLLEE, 0, NULL);
+		if (!wps_ie) {
+			wpas_p2p_scan_res_join(wpa_s, NULL);
+			return;
+		}
 	}
 
 	if (!freq) {
@@ -5757,14 +6409,21 @@
 	}
 
 	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
-	ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
-	if (ies == NULL) {
+
+	if (wps_ie)
+		ielen += wpabuf_len(wps_ie);
+
+	ies = wpabuf_alloc(ielen);
+	if (!ies) {
 		wpabuf_free(wps_ie);
 		wpas_p2p_scan_res_join(wpa_s, NULL);
 		return;
 	}
-	wpabuf_put_buf(ies, wps_ie);
-	wpabuf_free(wps_ie);
+
+	if (wps_ie) {
+		wpabuf_put_buf(ies, wps_ie);
+		wpabuf_free(wps_ie);
+	}
 
 	bands = wpas_get_bands(wpa_s, freqs);
 	p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
@@ -5847,6 +6506,7 @@
 	struct wpa_supplicant *group;
 	struct p2p_go_neg_results res;
 	struct wpa_bss *bss;
+	const u8 *iface_addr = NULL;
 
 	group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
 	if (group == NULL)
@@ -5874,15 +6534,27 @@
 	os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
+	if (!is_zero_ether_addr(wpa_s->pending_join_iface_addr))
+		iface_addr = wpa_s->pending_join_iface_addr;
+
+	if (wpa_s->pending_join_password[0]) {
+		res.akmp = WPA_KEY_MGMT_SAE;
+		os_strlcpy(res.sae_password, wpa_s->pending_join_password,
+			   sizeof(res.sae_password));
+		os_memset(wpa_s->pending_join_password, 0,
+			  sizeof(wpa_s->pending_join_password));
+	}
 	res.wps_method = wpa_s->pending_join_wps_method;
+	res.p2p2 = wpa_s->p2p2;
+	res.cipher = WPA_CIPHER_CCMP;
+
 	if (freq && ssid && ssid_len) {
 		res.freq = freq;
 		res.ssid_len = ssid_len;
 		os_memcpy(res.ssid, ssid, ssid_len);
 	} else {
 		if (ssid && ssid_len) {
-			bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
-					  ssid, ssid_len);
+			bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
 		} else {
 			bss = wpa_bss_get_bssid_latest(
 				wpa_s, wpa_s->pending_join_iface_addr);
@@ -5891,6 +6563,8 @@
 			res.freq = bss->freq;
 			res.ssid_len = bss->ssid_len;
 			os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+			os_memcpy(res.peer_interface_addr, bss->bssid,
+				  ETH_ALEN);
 			wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
 				   bss->freq,
 				   wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -5909,7 +6583,10 @@
 		wpa_s->off_channel_freq = 0;
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
-	wpas_start_wps_enrollee(group, &res);
+	if (res.p2p2)
+		wpas_start_gc(group, &res);
+	else
+		wpas_start_wps_enrollee(group, &res);
 
 	/*
 	 * Allow a longer timeout for join-a-running-group than normal 15
@@ -6127,8 +6804,9 @@
  * @allow_6ghz: Allow P2P connection on 6 GHz channels
  * @p2p2: Whether device is in P2P R2 mode
  * @bootstrap: Requested bootstrap method for pairing in P2P2
- * @password: Password for pairing setup or NULL for oppurtunistic method
+ * @password: Password for pairing setup or NULL for opportunistic method
  *	in P2P2
+ * @skip_prov: Connect without provisioning
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
@@ -6141,7 +6819,7 @@
 		     unsigned int vht_chwidth, int he, int edmg,
 		     const u8 *group_ssid, size_t group_ssid_len,
 		     bool allow_6ghz, bool p2p2, u16 bootstrap,
-		     const char *password)
+		     const char *password, bool skip_prov)
 {
 	int force_freq = 0, pref_freq = 0;
 	int ret = 0, res;
@@ -6162,6 +6840,7 @@
 	}
 
 	wpa_s->p2p2 = p2p2;
+	wpa_s->p2p_mode = p2p2 ? WPA_P2P_MODE_WFD_R2 : WPA_P2P_MODE_WFD_R1;
 
 	if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
 		return -2;
@@ -6169,6 +6848,7 @@
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
 
+	p2p_set_go_role(wpa_s->global->p2p, false);
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
 	wpa_s->global->pending_p2ps_group = 0;
 	wpa_s->global->pending_p2ps_group_freq = 0;
@@ -6212,14 +6892,64 @@
 	} else
 		wpa_s->p2p_pin[0] = '\0';
 
+	if (!password)
+		os_memset(wpa_s->pending_join_password, 0,
+			  sizeof(wpa_s->pending_join_password));
+
 	if (join || auto_join) {
 		u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
 		if (auth) {
+#ifdef CONFIG_PASN
+			struct wpa_supplicant *ifs;
+#endif /* CONFIG_PASN */
+
 			wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
 				   "connect a running group from " MACSTR,
 				   MAC2STR(peer_addr));
 			os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+
+#ifdef CONFIG_PASN
+			if (!wpa_s->p2p2)
+				return ret;
+
+			wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+			if (wpa_s->create_p2p_iface) {
+				if_addr = wpa_s->pending_interface_addr;
+			} else {
+				if (wpa_s->p2p_mgmt)
+					if_addr = wpa_s->parent->own_addr;
+				else
+					if_addr = wpa_s->own_addr;
+				os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+			}
+
+			dl_list_for_each(ifs, &wpa_s->radio->ifaces,
+					 struct wpa_supplicant, radio_list) {
+				if (!ifs->current_ssid ||
+				    ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+					continue;
+
+				ssid = ifs->current_ssid;
+
+				if (bootstrap == P2P_PBMA_OPPORTUNISTIC &&
+				    wpas_p2p_config_sae_password(wpa_s, ssid)) {
+					ssid = NULL;
+					continue;
+				}
+
+				force_freq = ifs->ap_iface->freq;
+				break;
+			}
+			p2p_set_go_role(wpa_s->global->p2p, true);
+			return wpas_p2p_auth_go_neg(wpa_s, peer_addr,
+						    wps_method, 15, if_addr,
+						    force_freq,
+						    persistent_group, ssid,
+						    pref_freq, bootstrap,
+						    password);
+#else /* CONFIG_PASN */
 			return ret;
+#endif /* CONFIG_PASN */
 		}
 		os_memcpy(dev_addr, peer_addr, ETH_ALEN);
 		if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
@@ -6236,6 +6966,20 @@
 				   wpa_s->p2p_auto_started.usec);
 		}
 		wpa_s->user_initiated_pd = 1;
+		if (password)
+			os_strlcpy(wpa_s->pending_join_password, password,
+				   sizeof(wpa_s->pending_join_password));
+
+		if (skip_prov) {
+			if (!wpa_s->p2p2) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Join without provisioning not supported");
+				return -1;
+			}
+			/* Start join operation immediately */
+			return wpas_p2p_join_start(wpa_s, 0, group_ssid,
+						   group_ssid_len);
+		}
 		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
 				  auto_join, freq,
 				  group_ssid, group_ssid_len) < 0)
@@ -6592,6 +7336,10 @@
 				      const struct p2p_channels *channels,
 				      int freq)
 {
+	if (is_6ghz_freq(freq) &&
+	    !is_p2p_6ghz_capable(wpa_s->global->p2p))
+		return 0;
+
 	if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
 	    p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
 	    freq_included(wpa_s, channels, freq))
@@ -6699,6 +7447,7 @@
 	params->max_oper_chwidth = max_oper_chwidth;
 	params->vht_center_freq2 = vht_center_freq2;
 	params->edmg = edmg;
+	params->p2p2 = wpa_s->p2p2;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent,
 			  sizeof(struct wpa_used_freq_data));
@@ -7087,6 +7836,7 @@
  * @vht_chwidth: channel bandwidth for GO operating with VHT support
  * @edmg: Start GO with EDMG support
  * @allow_6ghz: Allow P2P group creation on a 6 GHz channel
+ * @p2p_mode: Operation mode for GO (R1/R2/PCC)
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
@@ -7095,7 +7845,7 @@
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq, int vht_center_freq2, int ht40, int vht,
 		       int max_oper_chwidth, int he, int edmg,
-		       bool allow_6ghz)
+		       bool allow_6ghz, bool p2p2, enum wpa_p2p_mode p2p_mode)
 {
 	struct p2p_go_neg_results params;
 	int selected_freq = 0;
@@ -7107,6 +7857,8 @@
 
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
+	wpa_s->p2p2 = p2p2;
+	wpa_s->p2p_mode = p2p_mode;
 
 	/* Make sure we are not running find during connection establishment */
 	wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
@@ -7132,7 +7884,8 @@
 		return -1;
 	if (freq > 0)
 		wpa_s->p2p_go_no_pri_sec_switch = 1;
-	wpas_start_wps_go(wpa_s, &params, 0);
+	params.p2p2 = wpa_s->p2p2;
+	wpas_start_go(wpa_s, &params, 0, p2p_mode);
 
 	return 0;
 }
@@ -7141,11 +7894,14 @@
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *params, int addr_allocated,
 				 int freq, int force_scan, int retry_limit,
-				 const u8 *go_bssid)
+				 const u8 *go_bssid, bool p2p2, const u8 *pmkid,
+				 const u8 *pmk, size_t pmk_len)
 {
+	struct os_reltime now;
 	struct wpa_ssid *ssid;
 	int other_iface_found = 0;
 	struct wpa_supplicant *ifs;
+	struct rsn_pmksa_cache_entry *entry;
 
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
 	if (wpa_s == NULL)
@@ -7199,6 +7955,34 @@
 		os_memcpy(ssid->bssid, go_bssid, ETH_ALEN);
 	}
 
+	if (p2p2) {
+		ssid->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PASN;
+		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+		ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+		ssid->disabled = 0;
+
+		if (pmk && pmk_len && pmkid) {
+			entry = os_zalloc(sizeof(*entry));
+			if (!entry)
+				return -1;
+			os_memcpy(entry->aa, ssid->bssid, ETH_ALEN);
+			os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+			entry->pmk_len = pmk_len;
+			os_memcpy(entry->pmk, pmk, pmk_len);
+			entry->akmp = WPA_KEY_MGMT_SAE;
+			os_get_reltime(&now);
+			entry->expiration = now.sec + 43200;
+			entry->reauth_time = now.sec + 43200 * 70 / 100;
+			entry->network_ctx = ssid;
+			os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
+
+			wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+			ssid->pmk_valid = true;
+		}
+		wpa_s->current_ssid = ssid;
+	}
+
 	wpa_s->show_group_started = 1;
 	wpa_s->p2p_in_invitation = 1;
 	wpa_s->p2p_retry_limit = retry_limit;
@@ -7246,7 +8030,9 @@
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
 				  bool allow_6ghz, int retry_limit,
-				  const u8 *go_bssid)
+				  const u8 *go_bssid, const u8 *dev_addr,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -7315,7 +8101,8 @@
 		}
 
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
-					     force_scan, retry_limit, go_bssid);
+					     force_scan, retry_limit, go_bssid,
+					     wpa_s->p2p2, pmkid, pmk, pmk_len);
 	} else {
 		return -1;
 	}
@@ -7342,6 +8129,15 @@
 	params.ssid_len = ssid->ssid_len;
 	params.persistent_group = 1;
 
+	if (wpa_s->p2p2 && pmk_len && pmk && pmkid) {
+		os_memcpy(params.peer_device_addr, dev_addr, ETH_ALEN);
+		os_memcpy(params.pmkid, pmkid, PMKID_LEN);
+		os_memcpy(params.pmk, pmk, pmk_len);
+		params.pmk_len = pmk_len;
+		params.akmp = WPA_KEY_MGMT_SAE;
+		params.p2p2 = true;
+	}
+
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
 	if (wpa_s == NULL)
 		return -1;
@@ -7349,7 +8145,8 @@
 	p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
 
 	wpa_s->p2p_first_connection_timeout = connection_timeout;
-	wpas_start_wps_go(wpa_s, &params, 0);
+	params.p2p2 = wpa_s->p2p2;
+	wpas_start_go(wpa_s, &params, 0, wpa_s->p2p_mode);
 
 	return 0;
 }
@@ -7372,6 +8169,12 @@
 		}
 		wpabuf_free(hapd->p2p_probe_resp_ie);
 		hapd->p2p_probe_resp_ie = proberesp_ies;
+
+		if (wpa_s->p2p2) {
+			hapd->iconf->peer_to_peer_twt = true;
+			hapd->iconf->channel_usage = true;
+		}
+
 	} else {
 		wpabuf_free(beacon_ies);
 		wpabuf_free(proberesp_ies);
@@ -7430,6 +8233,7 @@
 	cfg->idle_update = wpas_p2p_idle_update;
 	cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
 		!= 0;
+	cfg->p2p2 = wpa_s->p2p2;
 
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
@@ -7879,7 +8683,7 @@
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
 		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
-		    int pref_freq, int he, int edmg, bool allow_6ghz)
+		    int pref_freq, int he, int edmg, bool allow_6ghz, bool p2p2)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
@@ -7905,6 +8709,7 @@
 	wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
 	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
 	wpa_s->p2p_go_edmg = !!edmg;
+	wpa_s->p2p2 = p2p2;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -7927,7 +8732,8 @@
 			bssid = wpa_s->own_addr;
 	} else {
 		role = P2P_INVITE_ROLE_CLIENT;
-		peer_addr = ssid->bssid;
+		if (!wpa_s->p2p2)
+			peer_addr = ssid->bssid;
 	}
 	wpa_s->pending_invite_ssid_id = ssid->id;
 
@@ -7958,9 +8764,23 @@
 	 */
 	wpas_p2p_stop_find_oper(wpa_s);
 
+#ifdef CONFIG_PASN
+	if (p2p2) {
+		if (wpas_p2p_initiate_pasn_verify(wpa_s, peer_addr, role, bssid,
+						  ssid->ssid, ssid->ssid_len,
+						  force_freq, go_dev_addr,
+						  pref_freq) < 0) {
+			if (wpa_s->create_p2p_iface)
+				wpas_p2p_remove_pending_group_interface(wpa_s);
+			return -1;
+		}
+		return 0;
+	}
+#endif /* CONFIG_PASN */
+
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-			  1, pref_freq, -1);
+			  1, pref_freq, -1, false);
 }
 
 
@@ -8044,13 +8864,14 @@
 
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq,
-			  go_dev_addr, persistent, pref_freq, -1);
+			  go_dev_addr, persistent, pref_freq, -1, false);
 }
 
 
 void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	const u8 *bssid;
 	u8 go_dev_addr[ETH_ALEN];
 	int persistent;
 	int freq;
@@ -8065,6 +8886,11 @@
 	if (!wpa_s->show_group_started || !ssid)
 		return;
 
+	if (wpa_s->go_params)
+		bssid = wpa_s->go_params->peer_interface_addr;
+	else
+		bssid = wpa_s->bssid;
+
 	wpa_s->show_group_started = 0;
 	if (!wpa_s->p2p_go_group_formation_completed &&
 	    wpa_s->global->p2p_group_formation == wpa_s) {
@@ -8111,9 +8937,11 @@
 			       ssid->passphrase, go_dev_addr, persistent,
 			       ip_addr);
 
-	if (persistent)
+	if (persistent) {
 		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
 						ssid, go_dev_addr);
+		wpas_p2p_store_go_identity(wpa_s, go_dev_addr, bssid);
+	}
 
 	wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr);
 }
@@ -8872,6 +9700,52 @@
 }
 
 
+static void wpas_p2p_store_client_identity(struct wpa_supplicant *wpa_s,
+					   const u8 *addr)
+{
+	u8 cipher;
+	size_t dik_len;
+	const u8 *dik_data;
+	const u8 *pmk, *pmkid;
+	size_t pmk_len;
+	u8 iface_addr[ETH_ALEN];
+	struct hostapd_data *hapd;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (!wpa_s->p2p2 || !wpa_s->ap_iface)
+		return;
+
+	hapd = wpa_s->ap_iface->bss[0];
+	if (!hapd)
+		return;
+
+	if (p2p_get_dev_identity_key(p2p_wpa_s->global->p2p, addr,
+				     &dik_data, &dik_len, &cipher))
+		return;
+
+	wpa_printf(MSG_DEBUG, "P2P: Fetch PMK from client (Device Addr " MACSTR
+		   ")", MAC2STR(addr));
+	if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, addr, &pmk, &pmk_len,
+				   &pmkid)) {
+		if (p2p_get_interface_addr(p2p_wpa_s->global->p2p, addr,
+					   iface_addr))
+			return;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Fetch PMK from client (Interface Addr " MACSTR
+			   ")", MAC2STR(iface_addr));
+		if (wpa_auth_pmksa_get_pmk(hapd->wpa_auth, iface_addr, &pmk,
+					   &pmk_len, &pmkid))
+			return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Storing device identity of client (Device Addr "
+		   MACSTR ")", MAC2STR(addr));
+	wpas_p2p_store_identity(p2p_wpa_s, cipher, dik_data, dik_len, pmk,
+				pmk_len, pmkid);
+}
+
+
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 				       const u8 *addr)
 {
@@ -8913,6 +9787,8 @@
 	wpa_s->global->p2p_go_wait_client.sec = 0;
 	if (addr == NULL)
 		return;
+
+	wpas_p2p_store_client_identity(wpa_s, addr);
 	wpas_p2p_add_persistent_group_client(wpa_s, addr);
 }
 
@@ -8942,7 +9818,7 @@
 			 wpa_s->p2p_go_he,
 			 wpa_s->p2p_go_edmg,
 			 NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p),
-			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL);
+			 wpa_s->p2p2, wpa_s->p2p_bootstrap, NULL, false);
 	return ret;
 }
 
@@ -9410,6 +10286,8 @@
 		return NULL;
 	}
 
+	wpa_s->p2p2 = false;
+
 	if (cli_freq == 0) {
 		wsc = wps_build_nfc_handover_req_p2p(
 			wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
@@ -9439,6 +10317,8 @@
 			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
 		return NULL;
 
+	wpa_s->p2p2 = false;
+
 	if (cli_freq == 0) {
 		wsc = wps_build_nfc_handover_sel_p2p(
 			wpa_s->parent->wps,
@@ -9481,7 +10361,7 @@
 				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 				params->go_ssid_len ? params->go_ssid : NULL,
 				params->go_ssid_len, false, wpa_s->p2p2,
-				wpa_s->p2p_bootstrap, NULL);
+				wpa_s->p2p_bootstrap, NULL, false);
 }
 
 
@@ -9545,7 +10425,7 @@
 			  P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
 			  ssid->ssid, ssid->ssid_len, ssid->frequency,
 			  wpa_s->global->p2p_dev_addr, persistent, 0,
-			  wpa_s->p2pdev->p2p_oob_dev_pw_id);
+			  wpa_s->p2pdev->p2p_oob_dev_pw_id, false);
 }
 
 
@@ -9561,7 +10441,7 @@
 				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
 				wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 				NULL, 0, false, wpa_s->p2p2,
-				wpa_s->p2p_bootstrap, NULL);
+				wpa_s->p2p_bootstrap, NULL, false);
 }
 
 
@@ -9579,7 +10459,7 @@
 			       -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
 			       wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
 			       NULL, 0, false, wpa_s->p2p2,
-			       wpa_s->p2p_bootstrap, NULL);
+			       wpa_s->p2p_bootstrap, NULL, false);
 	if (res)
 		return res;
 
@@ -9707,6 +10587,8 @@
 		  params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
 	wpa_s->p2p_peer_oob_pk_hash_known = 1;
 
+	wpa_s->p2p2 = false;
+
 	if (tag) {
 		if (id < 0x10) {
 			wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
@@ -10469,3 +11351,54 @@
 		return;
 	p2p_process_usd_elems(p2p, buf, buf_len, peer_addr, freq);
 }
+
+
+#ifdef CONFIG_PASN
+
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return -2;
+	return p2p_pasn_auth_rx(p2p, mgmt, len, freq);
+}
+
+
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, bool acked)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	if (!wpa_s->p2p_pasn_auth_work)
+		return -1;
+	awork = wpa_s->p2p_pasn_auth_work->ctx;
+
+	return p2p_pasn_auth_tx_status(p2p, data, data_len, acked,
+				       awork->verify);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk,
+			  size_t *ptk_len)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return -2;
+	return p2p_pasn_get_ptk(p2p, ptk, ptk_len);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#endif /* CONFIG_PASN */
+
+
+void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s)
+{
+	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+	p2p_set_dev_addr(wpa_s->global->p2p, wpa_s->own_addr);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index a0fbddc..888bce5 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -40,12 +40,13 @@
 		     unsigned int vht_chwidth, int he, int edmg,
 		     const u8 *group_ssid, size_t group_ssid_len,
 		     bool allow_6ghz, bool p2p2, u16 bootstrap,
-		     const char *password);
+		     const char *password, bool skip_prov);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq, int vht_center_freq2, int ht40, int vht,
-		       int max_oper_chwidth, int he, int edmg, bool allow_6ghz);
+		       int max_oper_chwidth, int he, int edmg, bool allow_6ghz,
+		       bool p2p2, enum wpa_p2p_mode p2p_mode);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
 				  int force_freq, int neg_freq,
@@ -54,7 +55,9 @@
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
 				  bool allow_6ghz, int retry_limit,
-				  const u8 *go_bssid);
+				  const u8 *go_bssid, const u8 *dev_addr,
+				  const u8 *pmkid, const u8 *pmk,
+				  size_t pmk_len);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid);
 enum wpas_p2p_prov_disc_use {
@@ -122,7 +125,8 @@
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
 		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
-		    int pref_freq, int he, int edmg, bool allow_6ghz);
+		    int pref_freq, int he, int edmg, bool allow_6ghz,
+		    bool p2p2);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr,
 			  bool allow_6ghz);
@@ -182,6 +186,9 @@
 void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf,
 				u16 buf_len, const u8 *peer_addr,
 				unsigned int freq);
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, bool acked);
+int wpas_p2p_remove_all_identity(struct wpa_supplicant *wpa_s);
 
 #ifdef CONFIG_P2P
 
@@ -230,6 +237,12 @@
 int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s);
 struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s);
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq);
+int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s, const u8 **ptk,
+			  size_t *ptk_len);
 
 #else /* CONFIG_P2P */
 
@@ -361,6 +374,18 @@
 	return NULL;
 }
 
+static inline void wpas_p2p_update_dev_addr(struct wpa_supplicant *wpa_s)
+{
+}
+
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int wpas_p2p_get_pasn_ptk(struct wpa_supplicant *wpa_s,
+					const u8 **ptk, size_t *ptk_len)
+{
+	return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 89edad4..a46fe46 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -806,6 +806,9 @@
 	if (!wpa_s->pasn_auth_work)
 		return -2;
 
+	wpabuf_free(pasn->frame);
+	pasn->frame = NULL;
+
 	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
 	if (ret == 0) {
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index f0ab122..ccedcc9 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -750,17 +750,20 @@
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab), NULL);
 	if (ext_capab_len > 0 &&
+	    (size_t) ext_capab_len < wpa_s->drv_max_probe_req_ie_len &&
 	    wpabuf_resize(&extra_ie, ext_capab_len) == 0)
 		wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
 
 #ifdef CONFIG_INTERWORKING
 	if (wpa_s->conf->interworking &&
+	    wpa_s->drv_max_probe_req_ie_len >= 2 &&
 	    wpabuf_resize(&extra_ie, 100) == 0)
 		wpas_add_interworking_elements(wpa_s, extra_ie);
 #endif /* CONFIG_INTERWORKING */
 
 #ifdef CONFIG_MBO
-	if (wpa_s->enable_oce & OCE_STA)
+	if ((wpa_s->enable_oce & OCE_STA) &&
+	    wpa_s->drv_max_probe_req_ie_len >= 5)
 		wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
 #endif /* CONFIG_MBO */
 
@@ -774,17 +777,19 @@
 						&wpa_s->wps->dev,
 						wpa_s->wps->uuid, req_type,
 						0, NULL);
-		if (wps_ie) {
-			if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
-				wpabuf_put_buf(extra_ie, wps_ie);
-			wpabuf_free(wps_ie);
-		}
+		if (wps_ie &&
+		    wpabuf_len(wps_ie) <= wpa_s->drv_max_probe_req_ie_len &&
+		    wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+			wpabuf_put_buf(extra_ie, wps_ie);
+		wpabuf_free(wps_ie);
 	}
 
 #ifdef CONFIG_P2P
 	if (wps) {
 		size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
-		if (wpabuf_resize(&extra_ie, ielen) == 0)
+
+		if (ielen <= wpa_s->drv_max_probe_req_ie_len &&
+		    wpabuf_resize(&extra_ie, ielen) == 0)
 			wpas_p2p_scan_ie(wpa_s, extra_ie);
 	}
 #endif /* CONFIG_P2P */
@@ -794,12 +799,14 @@
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_HS20
-	if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0)
+	if (wpa_s->conf->hs20 && wpa_s->drv_max_probe_req_ie_len >= 9 &&
+	    wpabuf_resize(&extra_ie, 9) == 0)
 		wpas_hs20_add_indication(extra_ie, -1, 0);
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_FST
 	if (wpa_s->fst_ies &&
+	    wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(wpa_s->fst_ies) &&
 	    wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
 		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
 #endif /* CONFIG_FST */
@@ -813,7 +820,8 @@
 	if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
 
-		if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
+		if (wpa_s->drv_max_probe_req_ie_len >= wpabuf_len(buf) &&
+		    wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
 			wpabuf_put_buf(extra_ie, buf);
 	}
 
@@ -2368,7 +2376,9 @@
 	int wpa_a, wpa_b;
 	int snr_a, snr_b, snr_a_full, snr_b_full;
 	size_t ies_len;
+#ifndef CONFIG_NO_WPA
 	const u8 *rsne_a, *rsne_b;
+#endif /* CONFIG_NO_WPA */
 
 	/* WPA/WPA2 support preferred */
 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -2412,6 +2422,7 @@
 		snr_b = snr_b_full = wb->level;
 	}
 
+#ifndef CONFIG_NO_WPA
 	/* If SNR of a SAE BSS is good or at least as high as the PSK BSS,
 	 * prefer SAE over PSK for mixed WPA3-Personal transition mode and
 	 * WPA2-Personal deployments */
@@ -2437,6 +2448,7 @@
 		    (snr_b >= GREAT_SNR || snr_b >= snr_a))
 			return 1;
 	}
+#endif /* CONFIG_NO_WPA */
 
 	/* If SNR is close, decide by max rate or frequency band. For cases
 	 * involving the 6 GHz band, use the throughput estimate irrespective
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e4be388..0a0a50f 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -105,6 +105,7 @@
 	int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt :
 		wpa_s->key_mgmt;
 	const u8 *addr = mld_addr ? mld_addr : bssid;
+	enum sae_pwe sae_pwe;
 
 	if (ret_use_pt)
 		*ret_use_pt = 0;
@@ -198,14 +199,16 @@
 			rsnxe_capa = rsnxe[2];
 	}
 
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+
 	if (ssid->sae_password_id &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	if (bss && is_6ghz_freq(bss->freq) &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 #ifdef CONFIG_SAE_PK
 	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -225,14 +228,14 @@
 	}
 #endif /* CONFIG_SAE_PK */
 
-	if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-	    wpa_s->conf->sae_pwe == SAE_PWE_BOTH) {
+	if (use_pt || sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	    sae_pwe == SAE_PWE_BOTH) {
 		use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
 
-		if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		if ((sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 		     ssid->sae_password_id ||
 		     wpa_key_mgmt_sae_ext_key(key_mgmt)) &&
-		    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+		    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		    !use_pt) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Cannot use H2E with the selected AP");
@@ -241,7 +244,7 @@
 	}
 
 	if (use_pt && !ssid->pt)
-		wpa_s_setup_sae_pt(wpa_s->conf, ssid, true);
+		wpa_s_setup_sae_pt(wpa_s, ssid, true);
 	if (use_pt &&
 	    sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
 				  wpa_s->own_addr, addr,
@@ -810,7 +813,6 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x",
 			md[0], md[1]);
 
-		omit_rsnxe = !wpa_bss_get_rsnxe(wpa_s, bss, ssid, false);
 		if (wpa_s->sme.assoc_req_ie_len + 5 <
 		    sizeof(wpa_s->sme.assoc_req_ie)) {
 			struct rsn_mdie *mdie;
@@ -830,6 +832,8 @@
 		    wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
 				"over-the-air");
+			omit_rsnxe = !wpa_bss_get_rsnxe(wpa_s, bss, ssid,
+							false);
 			params.auth_alg = WPA_AUTH_ALG_FT;
 			params.ie = wpa_s->sme.ft_ies;
 			params.ie_len = wpa_s->sme.ft_ies_len;
@@ -1453,7 +1457,7 @@
 		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
 		    wpa_key_mgmt_sae(ssid->key_mgmt)) {
 			/* Make sure PT is derived */
-			wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+			wpa_s_setup_sae_pt(wpa_s, ssid, false);
 			wpa_s->sme.ext_auth_wpa_ssid = ssid;
 			break;
 		}
@@ -1729,6 +1733,30 @@
 				return -1;
 			}
 			token_len = elen - 1;
+#ifdef CONFIG_IEEE80211BE
+		} else if ((wpa_s->valid_links ||
+			    (external && wpa_s->sme.ext_ml_auth)) &&
+			   token_len > 12 &&
+			   token_pos[token_len - 12] == WLAN_EID_EXTENSION &&
+			   token_pos[token_len - 11] == 10 &&
+			   token_pos[token_len - 10] ==
+			   WLAN_EID_EXT_MULTI_LINK) {
+			/* IEEE P802.11be requires H2E to be used whenever SAE
+			 * is used for ML association. However, some early
+			 * Wi-Fi 7 APs enable MLO without H2E. Recognize this
+			 * special case based on the fixed length Basic
+			 * Multi-Link element being at the end of the data that
+			 * would contain the unknown variable length
+			 * Anti-Clogging Token field. The Basic Multi-Link
+			 * element in Authentication frames include the MLD MAC
+			 * addreess in the Common Info field and all subfields
+			 * of the Presence Bitmap subfield of the Multi-Link
+			 * Control field of the element zero and consequently,
+			 * has a fixed length of 12 octets. */
+			wpa_printf(MSG_DEBUG,
+				   "SME: Detected Basic Multi-Link element at the end of Anti-Clogging Token field");
+			token_len -= 12;
+#endif /* CONFIG_IEEE80211BE */
 		}
 
 		*ie_offset = token_pos + token_len - data;
@@ -2451,6 +2479,12 @@
 mscs_fail:
 #endif /* CONFIG_NO_ROBUST_AV */
 
+	wpa_s->sme.assoc_req_ie_len =
+		wpas_populate_wfa_capa(wpa_s, wpa_s->current_bss,
+				       wpa_s->sme.assoc_req_ie,
+				       wpa_s->sme.assoc_req_ie_len,
+				       sizeof(wpa_s->sme.assoc_req_ie));
+
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 		struct multi_ap_params multi_ap = { 0 };
@@ -2472,10 +2506,10 @@
 	}
 
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT,
-			 wpas_rsn_overriding(wpa_s));
+			 wpas_rsn_overriding(wpa_s, ssid));
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
 			 RSN_OVERRIDE_NOT_USED);
-	if (wpas_rsn_overriding(wpa_s) &&
+	if (wpas_rsn_overriding(wpa_s, ssid) &&
 	    wpas_ap_supports_rsn_overriding(wpa_s, wpa_s->current_bss) &&
 	    wpa_s->sme.assoc_req_ie_len + 2 + 4 <=
 	    sizeof(wpa_s->sme.assoc_req_ie)) {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 3bb621d..31d1007 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -614,38 +614,37 @@
 }
 
 
-static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
-{
-	unsigned int i;
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
-		wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
-}
-
-#ifdef CONFIG_MBO
-static struct wpa_bss *
-get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+static void
+fetch_drv_mbo_candidate_info(struct wpa_supplicant *wpa_s,
 			     enum mbo_transition_reject_reason *reason)
 {
-	struct wpa_bss *target = NULL;
+#ifdef CONFIG_MBO
 	struct wpa_bss_trans_info params;
 	struct wpa_bss_candidate_info *info = NULL;
-	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
-	u8 *first_candidate_bssid = NULL, *pos;
+	struct neighbor_report *nei;
+	u8 *pos;
 	unsigned int i;
 
+	if (!wpa_s->wnm_mbo_trans_reason_present)
+		return;
+
 	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
 	params.n_candidates = 0;
 	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
 	if (!params.bssid)
-		return NULL;
+		return;
 
 	pos = params.bssid;
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
-		if (nei->is_first)
-			first_candidate_bssid = nei->bssid;
-		if (!nei->acceptable)
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+		nei->drv_mbo_reject = 0;
+
+		if (nei->preference_present && nei->preference == 0)
 			continue;
+
+		/* Should we query BSSIDs that we reject for other reasons? */
+
 		os_memcpy(pos, nei->bssid, ETH_ALEN);
 		pos += ETH_ALEN;
 		params.n_candidates++;
@@ -655,185 +654,36 @@
 		goto end;
 
 	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
-	if (!info) {
-		/* If failed to get candidate BSS transition status from driver,
-		 * get the first acceptable candidate from wpa_supplicant.
-		 */
-		target = wpa_bss_get_bssid(wpa_s, params.bssid);
+	if (!info)
 		goto end;
-	}
 
-	/* Get the first acceptable candidate from driver */
 	for (i = 0; i < info->num; i++) {
-		if (info->candidates[i].is_accept) {
-			target = wpa_bss_get_bssid(wpa_s,
-						   info->candidates[i].bssid);
-			goto end;
-		}
-	}
+		int j;
 
-	/* If Disassociation Imminent is set and driver rejects all the
-	 * candidate select first acceptable candidate which has
-	 * rssi > disassoc_imminent_rssi_threshold
-	 */
-	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
-		for (i = 0; i < info->num; i++) {
-			target = wpa_bss_get_bssid(wpa_s,
-						   info->candidates[i].bssid);
-			if (target &&
-			    (target->level <
-			     wpa_s->conf->disassoc_imminent_rssi_threshold))
+		for (j = 0; j < wpa_s->wnm_num_neighbor_report; j++) {
+			nei = &wpa_s->wnm_neighbor_report_elements[j];
+
+			if (!ether_addr_equal(info->candidates[i].bssid,
+					      nei->bssid))
 				continue;
-			goto end;
-		}
-	}
 
-	/* While sending BTM reject use reason code of the first candidate
-	 * received in BTM request frame
-	 */
-	if (reason) {
-		for (i = 0; i < info->num; i++) {
-			if (first_candidate_bssid &&
-			    ether_addr_equal(first_candidate_bssid,
-					     info->candidates[i].bssid)) {
+			nei->drv_mbo_reject = !info->candidates[i].is_accept;
+
+			/* Use the reject reason from the first candidate */
+			if (i == 0 && nei->drv_mbo_reject)
 				*reason = info->candidates[i].reject_reason;
-				break;
-			}
+
+			break;
 		}
 	}
 
-	target = NULL;
-
 end:
 	os_free(params.bssid);
 	if (info) {
 		os_free(info->candidates);
 		os_free(info);
 	}
-	return target;
-}
 #endif /* CONFIG_MBO */
-
-
-static struct wpa_bss * find_better_target(struct wpa_bss *a,
-					   struct wpa_bss *b)
-{
-	if (!a)
-		return b;
-	if (!b)
-		return a;
-
-	if (a->est_throughput > b->est_throughput) {
-		wpa_printf(MSG_DEBUG, "WNM: A is better: " MACSTR
-			   " est-tput: %d  B: " MACSTR " est-tput: %d",
-			   MAC2STR(a->bssid), a->est_throughput,
-			   MAC2STR(b->bssid), b->est_throughput);
-		return a;
-	}
-
-	wpa_printf(MSG_DEBUG, "WNM: B is better, A: " MACSTR
-		   " est-tput: %d  B: " MACSTR " est-tput: %d",
-		   MAC2STR(a->bssid), a->est_throughput,
-		   MAC2STR(b->bssid), b->est_throughput);
-	return b;
-}
-
-static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
-			      enum mbo_transition_reject_reason *reason)
-{
-	u8 i;
-	struct wpa_bss *bss = wpa_s->current_bss;
-	struct wpa_bss *target;
-	struct wpa_bss *best_target = NULL;
-	struct wpa_bss *bss_in_list = NULL;
-
-	if (!bss)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
-		   MAC2STR(wpa_s->bssid), bss->level);
-
-	wnm_clear_acceptable(wpa_s);
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-		struct neighbor_report *nei;
-
-		nei = &wpa_s->wnm_neighbor_report_elements[i];
-
-		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
-		if (!target) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) not found in scan results",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		/*
-		 * TODO: Could consider allowing transition to another ESS if
-		 * PMF was enabled for the association.
-		 */
-		if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
-					1, 0)) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) does not match the current network profile",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		if (target->level < bss->level && target->level < -80) {
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) does not have sufficient signal level (%d)",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1,
-				   target->level);
-			continue;
-		}
-
-		nei->acceptable = 1;
-
-		best_target = find_better_target(target, best_target);
-		if (target == bss)
-			bss_in_list = bss;
-	}
-
-#ifdef CONFIG_MBO
-	if (wpa_s->wnm_mbo_trans_reason_present)
-		target = get_mbo_transition_candidate(wpa_s, reason);
-	else
-		target = best_target;
-#else /* CONFIG_MBO */
-	target = best_target;
-#endif /* CONFIG_MBO */
-
-	if (!target)
-		return NULL;
-
-	wpa_printf(MSG_DEBUG,
-		   "WNM: Found an acceptable preferred transition candidate BSS "
-		   MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
-		   MAC2STR(target->bssid), target->level,
-		   target->est_throughput, bss->est_throughput);
-
-	if (!bss_in_list)
-		return target;
-
-	if ((!target->est_throughput && !bss_in_list->est_throughput) ||
-	    (target->est_throughput > bss_in_list->est_throughput &&
-	     target->est_throughput - bss_in_list->est_throughput >
-	     bss_in_list->est_throughput >> 4)) {
-		/* It is more than 100/16 percent better, so switch. */
-		return target;
-	}
-
-	wpa_printf(MSG_DEBUG,
-		   "WNM: Stay with our current BSS, not enough change in estimated throughput to switch");
-	return bss_in_list;
 }
 
 
@@ -1154,13 +1004,14 @@
 
 int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
 {
-	struct wpa_bss *bss;
+	struct wpa_bss *bss, *current_bss = wpa_s->current_bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 	enum mbo_transition_reject_reason reason =
 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+	struct wpa_ssid *selected_ssid = NULL;
 
-	if (!wpa_s->wnm_dialog_token)
+	if (!ssid || !wpa_s->wnm_dialog_token)
 		return 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1178,8 +1029,45 @@
 
 	wpa_s->wnm_transition_scan = false;
 
+	/* Fetch MBO transition candidate rejection information from driver */
+	fetch_drv_mbo_candidate_info(wpa_s, &reason);
+
 	/* Compare the Neighbor Report and scan results */
-	bss = compare_scan_neighbor_results(wpa_s, &reason);
+	bss = wpa_supplicant_select_bss(wpa_s, ssid, &selected_ssid, 1);
+#ifdef CONFIG_MBO
+	if (!bss && wpa_s->wnm_mbo_trans_reason_present &&
+	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT)) {
+		int i;
+		bool changed = false;
+
+		/*
+		 * We didn't find any candidate, the driver had a say about
+		 * which targets to reject and disassociation is immiment.
+		 *
+		 * We should still try to roam, so retry after ignoring the
+		 * driver reject for any BSS that has an RSSI better than
+		 * disassoc_imminent_rssi_threshold.
+		 */
+		for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+			struct neighbor_report *nei;
+
+			nei = &wpa_s->wnm_neighbor_report_elements[i];
+			bss = wpa_bss_get_bssid(wpa_s, nei->bssid);
+			if (bss && bss->level >
+			    wpa_s->conf->disassoc_imminent_rssi_threshold) {
+				nei->drv_mbo_reject = 0;
+				changed = true;
+			}
+		}
+
+		if (changed) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Ignore driver rejection due to imminent disassociation and acceptable RSSI");
+			bss = wpa_supplicant_select_bss(wpa_s, ssid,
+							&selected_ssid, 1);
+		}
+	}
+#endif /* CONFIG_MBO */
 
 	/*
 	 * If this is a pre-scan check, returning 0 will trigger a scan and
@@ -1202,20 +1090,35 @@
 			return 0;
 
 #ifndef CONFIG_NO_ROAMING
-		if (wpa_s->current_bss && bss != wpa_s->current_bss &&
-		    wpa_supplicant_need_to_roam_within_ess(wpa_s,
-							   wpa_s->current_bss,
-							   bss))
+		if (current_bss && bss != current_bss &&
+		    wpa_supplicant_need_to_roam_within_ess(wpa_s, bss,
+							   current_bss, false))
 			return 0;
 #endif /* CONFIG_NO_ROAMING */
 	}
 
+#ifndef CONFIG_NO_ROAMING
+	/* Apply normal roaming rules if we can stay with the current BSS */
+	if (current_bss && bss != current_bss &&
+	    wpa_scan_res_match(wpa_s, 0, current_bss, wpa_s->current_ssid,
+			       1, 0) &&
+	    !wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss, bss,
+						    true))
+		bss = current_bss;
+#endif /* CONFIG_NO_ROAMING */
+
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
 		goto send_bss_resp_fail;
 	}
 
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Found an acceptable preferred transition candidate BSS "
+		   MACSTR " (RSSI %d, tput: %d  bss-tput: %d)",
+		   MAC2STR(bss->bssid), bss->level, bss->est_throughput,
+		   current_bss ? (int) current_bss->est_throughput : -1);
+
 	/* Associate to the network */
 	wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
 	return 1;
@@ -1409,15 +1312,6 @@
 				*num_valid_candidates += 1;
 
 			wpa_s->wnm_num_neighbor_report++;
-#ifdef CONFIG_MBO
-			if (wpa_s->wnm_mbo_trans_reason_present &&
-			    wpa_s->wnm_num_neighbor_report == 1) {
-				rep->is_first = 1;
-				wpa_printf(MSG_DEBUG,
-					   "WNM: First transition candidate is "
-					   MACSTR, MAC2STR(rep->bssid));
-			}
-#endif /* CONFIG_MBO */
 		}
 
 		pos += len;
@@ -2133,6 +2027,11 @@
 		if (nei->preference_present && nei->preference == 0)
 			return true;
 
+#ifdef CONFIG_MBO
+		if (nei->drv_mbo_reject)
+			return true;
+#endif /* CONFIG_MBO */
+
 		break;
 	}
 
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 235a838..80928f7 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -44,9 +44,8 @@
 	unsigned int rm_capab_present:1;
 	unsigned int bearing_present:1;
 	unsigned int bss_term_present:1;
-	unsigned int acceptable:1;
 #ifdef CONFIG_MBO
-	unsigned int is_first:1;
+	unsigned int drv_mbo_reject:1;
 #endif /* CONFIG_MBO */
 	struct measurement_pilot *meas_pilot;
 	struct multiple_bssid *mul_bssid;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index af00e79..2f57be8 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -3335,6 +3335,59 @@
 }
 
 
+#ifdef CONFIG_NAN_USD
+
+static int wpa_cli_cmd_nan_publish(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_cancel_publish(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_CANCEL_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_update_publish(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_UPDATE_PUBLISH", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_subscribe(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_SUBSCRIBE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_cancel_subscribe(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_CANCEL_SUBSCRIBE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_transmit(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NAN_TRANSMIT", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nan_flush(struct wpa_ctrl *ctrl, int argc,
+				 char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "NAN_FLUSH");
+}
+
+#endif /* CONFIG_NAN_USD */
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -4084,6 +4137,24 @@
 	{ "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get mlo signal parameters" },
+#ifdef CONFIG_NAN_USD
+	{ "nan_publish", wpa_cli_cmd_nan_publish, NULL,
+	  cli_cmd_flag_none,
+	  "service_name=<name> [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [freq_list=<comma separate list of MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [solicited=0] [unsolicited=0] [fsd=0] [p2p=1] = Publish NAN service" },
+	{ "nan_cancel_publish", wpa_cli_cmd_nan_cancel_publish, NULL,
+	  cli_cmd_flag_none, "publish_id=<id from NAN_PUBLISH> = Cancel NAN USD publish instance" },
+	{ "nan_update_publish", wpa_cli_cmd_nan_update_publish, NULL,
+	  cli_cmd_flag_none, "publish_id=<id from NAN_PUBLISH> [ssi=<service specific information (hexdump)> = Update publish" },
+	{ "nan_subscribe", wpa_cli_cmd_nan_subscribe, NULL,
+	  cli_cmd_flag_none,
+	  "service_name=<name> [active=1] [ttl=<time-to-live-in-sec>] [freq=<in MHz>] [srv_proto_type=<type>] [ssi=<service specific information (hexdump)>] [p2p=1] = Subscribe to NAN service" },
+	{ "nan_cancel_subscribe", wpa_cli_cmd_nan_cancel_subscribe, NULL,
+	  cli_cmd_flag_none, "subscribe_id=<id from NAN_PUBLISH> = Cancel NAN USD subscribe instance" },
+	{ "nan_transmit", wpa_cli_cmd_nan_transmit, NULL,
+	  cli_cmd_flag_none, "handle=<id from NAN_PUBLISH or NAN_SUBSCRIBE> req_instance_id=<peer's id> address=<peer's MAC address> [ssi=<service specific information (hexdump)>] = Transmit NAN follow up" },
+	{ "nan_flush", wpa_cli_cmd_nan_flush, NULL,
+	  cli_cmd_flag_none, "= Flush all NAN USD services" },
+#endif /* CONFIG_NAN_USD */
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index e3ed858..9843679 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -507,6 +507,102 @@
 }
 
 
+static struct wpabuf * wpas_wfa_gen_capab_attr(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *attr;
+	size_t gen_len, supp_len;
+	const u8 *supp;
+	u8 supp_buf[1];
+	bool add_cert;
+
+	if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_DISABLED)
+		return NULL;
+
+	if (!wpa_s->conf->wfa_gen_capa_supp ||
+	    wpabuf_len(wpa_s->conf->wfa_gen_capa_supp) == 0) {
+		supp_len = 1;
+		supp_buf[0] = 0;
+		if (wpa_s->hw_capab & BIT(CAPAB_HT))
+			supp_buf[0] |= BIT(0); /* Wi-Fi 4 */
+		if (wpa_s->hw_capab & BIT(CAPAB_VHT))
+			supp_buf[0] |= BIT(1); /* Wi-Fi 5 */
+		if (wpa_s->hw_capab & BIT(CAPAB_HE))
+			supp_buf[0] |= BIT(2); /* Wi-Fi 6 */
+		if (wpa_s->hw_capab & BIT(CAPAB_EHT))
+			supp_buf[0] |= BIT(3); /* Wi-Fi 7 */
+		supp = supp_buf;
+	} else {
+		supp_len = wpabuf_len(wpa_s->conf->wfa_gen_capa_supp);
+		supp = wpabuf_head(wpa_s->conf->wfa_gen_capa_supp);
+	}
+
+	add_cert = wpa_s->conf->wfa_gen_capa_cert &&
+		wpabuf_len(wpa_s->conf->wfa_gen_capa_cert) == supp_len;
+
+	gen_len = 1 + supp_len;
+	if (add_cert) {
+		gen_len++;
+		gen_len += wpabuf_len(wpa_s->conf->wfa_gen_capa_cert);
+	}
+
+	attr = wpabuf_alloc(2 + gen_len);
+	if (!attr)
+		return NULL;
+
+	wpabuf_put_u8(attr, WFA_CAPA_ATTR_GENERATIONAL_CAPAB);
+	wpabuf_put_u8(attr, gen_len);
+	wpabuf_put_u8(attr, supp_len);
+	wpabuf_put_data(attr, supp, supp_len);
+	if (add_cert) {
+		wpabuf_put_u8(attr,
+			      wpabuf_len(wpa_s->conf->wfa_gen_capa_cert));
+		wpabuf_put_buf(attr, wpa_s->conf->wfa_gen_capa_cert);
+	}
+
+	return attr;
+}
+
+
+
+static void wpas_wfa_capab_tx(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpabuf *attr, *buf;
+	size_t buf_len;
+
+	if (wpa_s->conf->wfa_gen_capa != WFA_GEN_CAPA_PROTECTED ||
+	    wpa_s->wpa_state != WPA_COMPLETED ||
+	    !pmf_in_use(wpa_s, wpa_s->bssid))
+		return;
+
+	attr = wpas_wfa_gen_capab_attr(wpa_s);
+	if (!attr)
+		return;
+
+	buf_len = 1 + 3 + 1 + 1 + wpabuf_len(attr);
+	buf = wpabuf_alloc(buf_len);
+	if (!buf) {
+		wpabuf_free(attr);
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
+	wpabuf_put_be32(buf, WFA_CAPAB_VENDOR_TYPE);
+	wpabuf_put_u8(buf, 0); /* Capabilities Length */
+	wpabuf_put_buf(buf, attr);
+	wpabuf_free(attr);
+
+	wpa_printf(MSG_DEBUG, "WFA: Send WFA Capabilities frame");
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "WFA: Failed to send WFA Capabilities frame");
+
+	wpabuf_free(buf);
+}
+
+
 void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -620,6 +716,7 @@
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_verify_ssid_beacon, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL);
 
 	wpas_wps_deinit(wpa_s);
 
@@ -1148,6 +1245,12 @@
 #ifdef CONFIG_HS20
 		hs20_configure_frame_filters(wpa_s);
 #endif
+		if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_PROTECTED &&
+		    pmf_in_use(wpa_s, wpa_s->bssid)) {
+			eloop_cancel_timeout(wpas_wfa_capab_tx, wpa_s, NULL);
+			eloop_register_timeout(0, 100000, wpas_wfa_capab_tx,
+					       wpa_s, NULL);
+		}
 	} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 		   state == WPA_ASSOCIATED) {
 		wpa_s->new_connection = 1;
@@ -1581,6 +1684,9 @@
 {
 	int akm_count = wpa_s->max_num_akms;
 	u8 capab = 0;
+#ifdef CONFIG_SAE
+	enum sae_pwe sae_pwe;
+#endif /* CONFIG_SAE */
 
 	if (akm_count < 2)
 		return;
@@ -1659,13 +1765,16 @@
 		return;
 	}
 
-	if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK &&
-	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+#ifdef CONFIG_SAE
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+	if (sae_pwe != SAE_PWE_HUNT_AND_PECK &&
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 	if (ssid->sae_pk)
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
 #endif /* CONFIG_SAE_PK */
+#endif /* CONFIG_SAE */
 
 	if (!((wpa_s->allowed_key_mgmts &
 	       (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab))
@@ -1704,7 +1813,9 @@
 {
 	struct wpa_ie_data ie;
 	int sel, proto;
+#ifdef CONFIG_SAE
 	enum sae_pwe sae_pwe;
+#endif /* CONFIG_SAE */
 	const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
 	bool wmm;
 
@@ -2063,7 +2174,8 @@
 	    (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV))
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
-	sae_pwe = wpa_s->conf->sae_pwe;
+#ifdef CONFIG_SAE
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 	if ((ssid->sae_password_id ||
 	     wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
 	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
@@ -2084,6 +2196,7 @@
 			  (!ssid->sae_password && ssid->passphrase &&
 			   sae_pk_valid_password(ssid->passphrase))));
 #endif /* CONFIG_SAE_PK */
+#endif /* CONFIG_SAE */
 	if (bss && is_6ghz_freq(bss->freq) &&
 	    wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz");
@@ -2103,6 +2216,9 @@
 			 wpa_s->oci_freq_override_fils_assoc);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX,
 			 wpa_s->disable_eapol_g2_tx);
+	wpa_sm_set_param(wpa_s->wpa,
+			 WPA_PARAM_EAPOL_2_KEY_INFO_SET_MASK,
+			 wpa_s->eapol_2_key_info_set_mask);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	/* Extended Key ID is only supported in infrastructure BSS so far */
@@ -2208,9 +2324,9 @@
 		    (ssid->sae_password || ssid->passphrase || ssid->ext_psk))
 			psk_set = 1;
 
-		if (!psk_set) {
+		if (!psk_set && !ssid->pmk_valid) {
 			wpa_msg(wpa_s, MSG_INFO,
-				"No PSK available for association");
+				"No PSK/PMK available for association");
 			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
 			return -1;
 		}
@@ -2340,6 +2456,8 @@
 		if (!wpa_s->disable_fils)
 			*pos |= 0x01;
 #endif /* CONFIG_FILS */
+		if (wpa_s->conf->twt_requester)
+			*pos |= 0x20; /* Bit 77 - TWT Requester Support */
 		break;
 	case 10: /* Bits 80-87 */
 #ifndef CONFIG_NO_ROBUST_AV
@@ -2526,6 +2644,8 @@
 		return -1;
 	}
 
+	wpas_p2p_update_dev_addr(wpa_s);
+
 	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
 		MAC2STR(addr));
 
@@ -2544,13 +2664,15 @@
 }
 
 
-void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			bool force)
 {
 #ifdef CONFIG_SAE
+	struct wpa_config *conf = wpa_s->conf;
 	int *groups = conf->sae_groups;
 	int default_groups[] = { 19, 20, 21, 0 };
 	const char *password;
+	enum sae_pwe sae_pwe;
 
 	if (!groups || groups[0] <= 0)
 		groups = default_groups;
@@ -2559,13 +2681,15 @@
 	if (!password)
 		password = ssid->passphrase;
 
+	sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
+
 	if (!password ||
 	    !wpa_key_mgmt_sae(ssid->key_mgmt) ||
-	    (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
+	    (sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
 	     !wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
 	     !force &&
 	     !sae_pk_valid_password(password)) ||
-	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
+	    sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
 		/* PT derivation not needed */
 		sae_deinit_pt(ssid->pt);
 		ssid->pt = NULL;
@@ -2616,6 +2740,9 @@
 			"Could not update MAC address information");
 		return -1;
 	}
+
+	wpas_p2p_update_dev_addr(wpa_s);
+
 	wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
 	return 0;
 }
@@ -2686,7 +2813,7 @@
 		wpa_s_clear_sae_rejected(wpa_s);
 
 #ifdef CONFIG_SAE
-	wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+	wpa_s_setup_sae_pt(wpa_s, ssid, false);
 #endif /* CONFIG_SAE */
 
 	if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) {
@@ -3096,6 +3223,27 @@
 }
 
 
+static int ibss_get_center_320mhz(int channel)
+{
+	int seg0;
+
+	if (channel >= 1 && channel <= 45)
+		seg0 = 31;
+	else if (channel >= 49 && channel <= 77)
+		seg0 = 63;
+	else if (channel >= 81 && channel <= 109)
+		seg0 = 95;
+	else if (channel >= 113 && channel <= 141)
+		seg0 = 127;
+	else if (channel >= 145 && channel <= 173)
+		seg0 = 159;
+	else
+		seg0 = 191;
+
+	return seg0;
+}
+
+
 static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
 				       const struct wpa_ssid *ssid,
 				       struct hostapd_hw_modes *mode,
@@ -3109,6 +3257,11 @@
 	static const int bw160[] = {
 		5955, 6115, 6275, 6435, 6595, 6755, 6915
 	};
+	static const int bw320[]= {
+		5955, 6255, 6115, 6415, 6275, 6575, 6435,
+		6735, 6595, 6895, 6755, 7055
+	};
+
 	struct hostapd_freq_params vht_freq;
 	int i;
 	unsigned int j, k;
@@ -3169,6 +3322,26 @@
 		}
 	}
 
+	/* In 320 MHz, the initial four 20 MHz channels were validated
+	 * above. If 320 MHz is supported, check the remaining 12 20 MHz
+	 * channels for the total of 320 MHz bandwidth for 6 GHz.
+	 */
+	if ((mode->eht_capab[ieee80211_mode].phy_cap[
+		     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK) && is_6ghz &&
+	    ibss_mesh_is_80mhz_avail(channel + 16, mode) &&
+	    ibss_mesh_is_80mhz_avail(channel + 32, mode) &&
+	    ibss_mesh_is_80mhz_avail(channel + 48, mode)) {
+		for (j = 0; j < ARRAY_SIZE(bw320); j += 2) {
+			if (freq->freq >= bw320[j] &&
+			    freq->freq <= bw320[j + 1]) {
+				chwidth = CONF_OPER_CHWIDTH_320MHZ;
+				seg0 = ibss_get_center_320mhz(freq->channel);
+				break;
+			}
+		}
+	}
+
 	if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
 		/* setup center_freq2, bandwidth */
 		for (k = 0; k < ARRAY_SIZE(bw80); k++) {
@@ -3413,13 +3586,12 @@
 }
 
 
-static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s,
-				  struct wpa_bss *bss,
-				  u8 *wpa_ie, size_t wpa_ie_len,
-				  size_t max_wpa_ie_len)
+int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   u8 *wpa_ie, size_t wpa_ie_len, size_t max_wpa_ie_len)
 {
-	struct wpabuf *wfa_ie = NULL;
+	struct wpabuf *wfa_ie = NULL, *attr = NULL;
 	u8 wfa_capa[1];
+	u8 capab_len = 0;
 	size_t wfa_ie_len, buf_len;
 
 	os_memset(wfa_capa, 0, sizeof(wfa_capa));
@@ -3431,7 +3603,13 @@
 	if (wpa_is_non_eht_scs_traffic_desc_supported(bss))
 		wfa_capa[0] |= WFA_CAPA_QM_NON_EHT_SCS_TRAFFIC_DESC;
 
-	if (!wfa_capa[0])
+	if (wfa_capa[0])
+		capab_len = 1;
+
+	if (wpa_s->conf->wfa_gen_capa == WFA_GEN_CAPA_UNPROTECTED)
+		attr = wpas_wfa_gen_capab_attr(wpa_s);
+
+	if (capab_len == 0 && !attr)
 		return wpa_ie_len;
 
 	/* Wi-Fi Alliance element */
@@ -3440,17 +3618,23 @@
 		  3 +	/* OUI */
 		  1 +	/* OUI Type */
 		  1 +	/* Capabilities Length */
-		  sizeof(wfa_capa);	/* Capabilities */
+		  capab_len +	/* Capabilities */
+		  (attr ? wpabuf_len(attr) : 0) /* Attributes */;
 	wfa_ie = wpabuf_alloc(buf_len);
-	if (!wfa_ie)
+	if (!wfa_ie) {
+		wpabuf_free(attr);
 		return wpa_ie_len;
+	}
 
 	wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC);
 	wpabuf_put_u8(wfa_ie, buf_len - 2);
 	wpabuf_put_be24(wfa_ie, OUI_WFA);
 	wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE);
-	wpabuf_put_u8(wfa_ie, sizeof(wfa_capa));
-	wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa));
+	wpabuf_put_u8(wfa_ie, capab_len);
+	wpabuf_put_data(wfa_ie, wfa_capa, capab_len);
+	if (attr)
+		wpabuf_put_buf(wfa_ie, attr);
+	wpabuf_free(attr);
 
 	wfa_ie_len = wpabuf_len(wfa_ie);
 	if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) {
@@ -3995,10 +4179,10 @@
 	}
 
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE_SUPPORT,
-			 wpas_rsn_overriding(wpa_s));
+			 wpas_rsn_overriding(wpa_s, ssid));
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_OVERRIDE,
 			 RSN_OVERRIDE_NOT_USED);
-	if (wpas_rsn_overriding(wpa_s) &&
+	if (wpas_rsn_overriding(wpa_s, ssid) &&
 	    wpas_ap_supports_rsn_overriding(wpa_s, bss) &&
 	    wpa_ie_len + 2 + 4 + 1 <= max_wpa_ie_len) {
 		u8 *pos = wpa_ie + wpa_ie_len, *start = pos;
@@ -4035,7 +4219,7 @@
 		wpa_ie_len += pos - start;
 	}
 
-	params->rsn_overriding = wpas_rsn_overriding(wpa_s);
+	params->rsn_overriding = wpas_rsn_overriding(wpa_s, ssid);
 	params->wpa_ie = wpa_ie;
 	params->wpa_ie_len = wpa_ie_len;
 	params->auth_alg = algs;
@@ -4686,7 +4870,7 @@
 		params.prev_bssid = prev_bssid;
 
 #ifdef CONFIG_SAE
-	params.sae_pwe = wpa_s->conf->sae_pwe;
+	params.sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 #endif /* CONFIG_SAE */
 
 	ret = wpa_drv_associate(wpa_s, &params);
@@ -5144,6 +5328,51 @@
 }
 
 
+static bool ssid_in_last_scan(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid)
+{
+	size_t i;
+
+	/* Check if the previous scan included the selected network */
+	if (wpa_s->last_scan_num_ssids <= 1 ||
+	    !ssid->ssid || ssid->ssid_len == 0)
+		return false;
+
+	/* Iterate through the previous scan SSIDs */
+	for (i = 0; i < wpa_s->last_scan_num_ssids;  i++) {
+		if (os_memcmp(wpa_s->last_scan_ssids[i].ssid, ssid->ssid,
+			      ssid->ssid_len) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
+/**
+ * Checks whether an SSID was discovered in the last scan.
+ * @wpa_s: wpa_supplicant structure for a network interface.
+ * @ssid: wpa_ssid structure for a configured network.
+ * Returns: true if ssid found, false otherwise.
+ */
+static bool ssid_in_last_scan_res(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid)
+{
+	size_t i;
+
+	if (!wpa_s->last_scan_res || !ssid->ssid || ssid->ssid_len == 0)
+		return false;
+
+	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+		if (os_memcmp(wpa_s->last_scan_res[i]->ssid,
+			      ssid->ssid, ssid->ssid_len) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
 /**
  * wpa_supplicant_select_network - Attempt association with a network
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -5201,13 +5430,22 @@
 			(ssid->mode == WPAS_MODE_MESH ||
 			 ssid->mode == WPAS_MODE_AP) ? ssid : NULL;
 
-		if (ssid->scan_ssid &&
-		    (wpa_s->no_suitable_network || wpa_s->last_scan_external)) {
-			wpa_printf(MSG_DEBUG,
-				   "Request a new scan for hidden network");
-			request_new_scan = true;
-		} else if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
-			   !ssid->owe_only) {
+		if (ssid->scan_ssid) {
+			if (ssid_in_last_scan(wpa_s, ssid)) {
+				wpa_printf(MSG_DEBUG,
+					   "Hidden network was scanned for in last scan");
+			} else if (ssid_in_last_scan_res(wpa_s, ssid)) {
+				wpa_printf(MSG_DEBUG,
+					   "Hidden network was found in last scan results");
+			} else {
+				request_new_scan = true;
+				wpa_printf(MSG_DEBUG,
+					   "Request a new scan for hidden network");
+			}
+		}
+
+		if (!request_new_scan && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+		    !ssid->owe_only) {
 			wpa_printf(MSG_DEBUG,
 				   "Request a new scan for OWE transition SSID");
 			request_new_scan = true;
@@ -5229,7 +5467,7 @@
 	wpa_s->last_owe_group = 0;
 	if (ssid) {
 		ssid->owe_transition_bss_select_count = 0;
-		wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+		wpa_s_setup_sae_pt(wpa_s, ssid, false);
 	}
 
 	if (wpa_s->connect_without_scan || request_new_scan ||
@@ -5765,6 +6003,7 @@
 			MACSTR ")",
 			wpa_supplicant_state_txt(wpa_s->wpa_state),
 			MAC2STR(connected_addr));
+	delay_processing:
 		wpabuf_free(wpa_s->pending_eapol_rx);
 		wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
 		if (wpa_s->pending_eapol_rx) {
@@ -5862,9 +6101,23 @@
 			      encrypted) > 0)
 		return;
 	wpa_drv_poll(wpa_s);
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK))
-		wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len, encrypted);
-	else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
+		if (wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len,
+				    encrypted) == -2 &&
+#ifdef CONFIG_AP
+		    !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+		    wpa_s->last_eapol_matches_bssid) {
+			/* Handle the case where reassociation occurs to the
+			 * current connected AP */
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Delay processing of received EAPOL frame for reassociation to the current connected AP (state=%s connected_addr="
+				MACSTR ")",
+				wpa_supplicant_state_txt(wpa_s->wpa_state),
+				MAC2STR(connected_addr));
+			goto delay_processing;
+		}
+	} else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 		/*
 		 * Set portValid = true here since we are going to skip 4-way
 		 * handshake processing which would normally set portValid. We
@@ -6119,6 +6372,10 @@
 	wpa_s->new_connection = 1;
 	wpa_s->parent = parent ? parent : wpa_s;
 	wpa_s->p2pdev = wpa_s->parent;
+#ifdef CONFIG_P2P
+	if (parent)
+		wpa_s->p2p_mode = parent->p2p_mode;
+#endif /* CONFIG_P2P */
 	wpa_s->sched_scanning = 0;
 	wpa_s->setband_mask = WPA_SETBAND_AUTO;
 
@@ -7470,17 +7727,16 @@
 		u16 i;
 
 		for (i = 0; i < wpa_s->hw.num_modes; i++) {
-			if (wpa_s->hw.modes[i].vht_capab) {
-				wpa_s->hw_capab = CAPAB_VHT;
-				break;
-			}
-
-			if (wpa_s->hw.modes[i].ht_capab &
-			    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
-				wpa_s->hw_capab = CAPAB_HT40;
-			else if (wpa_s->hw.modes[i].ht_capab &&
-				 wpa_s->hw_capab == CAPAB_NO_HT_VHT)
-				wpa_s->hw_capab = CAPAB_HT;
+			if (wpa_s->hw.modes[i].eht_capab[IEEE80211_MODE_INFRA].
+			    eht_supported)
+				wpa_s->hw_capab |= BIT(CAPAB_EHT);
+			if (wpa_s->hw.modes[i].he_capab[IEEE80211_MODE_INFRA].
+			    he_supported)
+				wpa_s->hw_capab |= BIT(CAPAB_HE);
+			if (wpa_s->hw.modes[i].vht_capab)
+				wpa_s->hw_capab |= BIT(CAPAB_VHT);
+			if (wpa_s->hw.modes[i].ht_capab)
+				wpa_s->hw_capab |= BIT(CAPAB_HT);
 		}
 		wpa_s->support_6ghz = wpas_is_6ghz_supported(wpa_s, false);
 	}
@@ -7522,12 +7778,15 @@
 		    capa.mac_addr_rand_sched_scan_supported)
 			wpa_s->mac_addr_rand_supported |=
 				(MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
+		wpa_s->drv_max_probe_req_ie_len = capa.max_probe_req_ie_len;
 
 		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
 		if (wpa_s->extended_capa &&
 		    wpa_s->extended_capa_len >= 3 &&
 		    wpa_s->extended_capa[2] & 0x40)
 			wpa_s->multi_bss_support = 1;
+	} else {
+		wpa_s->drv_max_probe_req_ie_len = 1500;
 	}
 #ifdef CONFIG_PASN
 	wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2);
@@ -8713,12 +8972,19 @@
 }
 
 
-bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s)
+bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
-	if (wpa_s->conf->rsn_overriding == RSN_OVERRIDING_DISABLED)
+	enum wpas_rsn_overriding rsno;
+
+	if (ssid && ssid->rsn_overriding != RSN_OVERRIDING_NOT_SET)
+		rsno = ssid->rsn_overriding;
+	else
+		rsno = wpa_s->conf->rsn_overriding;
+
+	if (rsno == RSN_OVERRIDING_DISABLED)
 		return false;
 
-	if (wpa_s->conf->rsn_overriding == RSN_OVERRIDING_ENABLED)
+	if (rsno == RSN_OVERRIDING_ENABLED)
 		return true;
 
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
@@ -8912,7 +9178,8 @@
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
 	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
-	    !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
+	    !(wpa_key_mgmt_sae(ssid->key_mgmt) &&
+	      (ssid->sae_password || ssid->pmk_valid)) &&
 	    !ssid->mem_only_psk)
 		return 1;
 
@@ -8981,6 +9248,16 @@
 
 
 #ifdef CONFIG_SAE
+
+enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid)
+{
+	if (!ssid || ssid->sae_pwe == DEFAULT_SAE_PWE)
+		return wpa_s->conf->sae_pwe;
+	return ssid->sae_pwe;
+}
+
+
 bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
 			 struct wpa_ssid *ssid,
 			 const struct wpa_ie_data *ie)
@@ -8990,6 +9267,7 @@
 		   (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
 		 wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
 }
+
 #endif /* CONFIG_SAE */
 
 
@@ -9725,7 +10003,7 @@
 	}
 
 	return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
-					  bssid, data, data_len, no_cck);
+					  bssid, data, data_len, no_cck, -1);
 }
 
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index abbe7d7..ae181ee 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -401,6 +401,50 @@
 # it from taking 100% of radio resources. The default value is 500 ms.
 #p2p_search_delay=500
 
+# Enable/disable P2P pairing setup
+#p2p_pairing_setup=0
+
+# Enable/disable P2P pairing cache for verification
+#p2p_pairing_cache=0
+
+# Enable/disable P2P pairing verification with cached NIK/NPK
+#p2p_pairing_verification=0
+
+# Supported P2P bootstrapping method bitmap
+# b0: whether opportunistic bootstrapping is supported
+# b1: whether PIN display is supported
+# b2: whether passphrase display is supported
+# b3: whether QR Code display is supported
+# b4: whether NFC tag is supported
+# b5: whether keypad (PIN only) is supported
+# b6: whether keypad (passphrase) is supported
+# b7: whether QR Code scan is supported
+# b8: whether NFC reader is supported
+# b14: whether service managed bootstrapping is supported
+# b15: whether bootstrapping handshakes skipped is supported
+#p2p_bootstrap_methods=0
+
+# Bitmap of supported PASN types
+# B0: whether DH Group 19 with unauthenticated PASN is supported
+# B1: whether DH Group 19 with authenticated PASN is supported
+# B2: whether DH Group 20 with unauthenticated PASN is supported
+# B3: whether DH Group 20 authenticated PASN is supported
+#p2p_pasn_type=0
+
+# Bootstrap request for unauthorized peer is asked to come back after
+# this many TUs.
+#p2p_comeback_after=977
+
+# Enable/disable TWT based power management for P2P
+#p2p_twt_power_mgmt=0
+
+# Enable/disable P2P client channel switch request
+#p2p_chan_switch_req_enable=0
+
+# Regulatory info encoding for operation in 6 GHz band
+# As defined in Table E-12 and E-13 of IEEE P802.11-REVme/D7.0.
+#p2p_reg_info=0
+
 # Opportunistic Key Caching (also known as Proactive Key Caching) default
 # This parameter can be used to set the default behavior for the
 # proactive_key_caching parameter. By default, OKC is disabled unless enabled
@@ -608,6 +652,34 @@
 # 1 = Publish
 #ftm_initiator=0
 
+#twt_requester: Whether TWT requester is enabled
+# 0 = disabled (default)
+# 1 = enabled if supported by the driver
+#twt_requester=0
+
+# Wi-Fi Alliance generational capabilities indication
+#
+# wfa_gen_capa: Whether to indicate Wi-Fi generational capability to the AP
+# 0 = do not indicate (default)
+# 1 = indicate in protected Action frame
+# 2 = indicate in unprotected (Re)Association Request frame
+#wfa_gen_capa=0
+#
+# wfa_gen_capa_supp: Supported Generations (hexdump of a bit field)
+# A bit field of supported Wi-Fi generations. This is encoded as an little
+# endian octet string. If this is not set, the driver capabilities are
+# determined automatically.
+# bit 0: Wi-Fi 4
+# bit 1: Wi-Fi 5
+# bit 2: Wi-Fi 6
+# bit 3: Wi-Fi 7
+#wfa_gen_capa_supp=07
+#
+# wfa_gen_capa_cert: Certified Generations (hexdump of a bit field)
+# This has the same format as wfa_gen_capa_supp. This is an optional field, but
+# if included, shall have the same length as wfa_gen_capa_supp.
+#wfa_gen_capa_cert=07
+
 # credential block
 #
 # Each credential used for automatic network selection is configured as a set
@@ -882,6 +954,8 @@
 # NOTE: The protocol used for this mechanism is still subject to change and as
 # such, this should not yet be enabled for production uses to avoid issues if
 # something were to change.
+# A per-network block parameter with the same name can be used to override this
+# global parameter.
 # 0 = Disabled (default)
 # 1 = Enabled automatically if the driver indicates support
 # 2 = Forced to be enabled even without driver capability indication
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 84b7bd5..d27ff1a 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -686,6 +686,18 @@
 };
 
 
+enum local_hw_capab {
+	CAPAB_HT,
+	CAPAB_VHT,
+	CAPAB_HE,
+	CAPAB_EHT,
+};
+
+struct last_scan_ssid {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -823,6 +835,8 @@
 	size_t last_scan_res_size;
 	struct os_reltime last_scan;
 	bool last_scan_external;
+	struct last_scan_ssid last_scan_ssids[WPAS_MAX_SCAN_SSIDS];
+	size_t last_scan_num_ssids;
 
 	const struct wpa_driver_ops *driver;
 	int interface_removed; /* whether the network interface has been
@@ -844,7 +858,7 @@
 	int eapol_received; /* number of EAPOL packets received after the
 			     * previous association event */
 
-	u8 rsnxe[20];
+	u8 rsnxe[257];
 	size_t rsnxe_len;
 
 	struct scard_data *scard;
@@ -941,6 +955,7 @@
 	unsigned int drv_key_mgmt;
 	unsigned int drv_rrm_flags;
 	unsigned int drv_max_acl_mac_addrs;
+	size_t drv_max_probe_req_ie_len;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -1137,6 +1152,7 @@
 	u8 pending_join_dev_addr[ETH_ALEN];
 	u8 p2p_bootstrap_dev_addr[ETH_ALEN];
 	int pending_join_wps_method;
+	char pending_join_password[100];
 	u8 p2p_join_ssid[SSID_MAX_LEN];
 	size_t p2p_join_ssid_len;
 	int p2p_join_scan_count;
@@ -1193,6 +1209,7 @@
 	unsigned int p2p2:1;
 	u16 p2p_bootstrap;
 	enum hostapd_hw_mode p2p_go_acs_band;
+	enum wpa_p2p_mode p2p_mode;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
 	int p2p_go_intent;
@@ -1202,6 +1219,7 @@
 	struct wpa_radio_work *p2p_scan_work;
 	struct wpa_radio_work *p2p_listen_work;
 	struct wpa_radio_work *p2p_send_action_work;
+	bool p2p_removing_listen_work;
 
 	u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
 	struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
@@ -1274,12 +1292,7 @@
 		u16 num_modes;
 		u16 flags;
 	} hw;
-	enum local_hw_capab {
-		CAPAB_NO_HT_VHT,
-		CAPAB_HT,
-		CAPAB_HT40,
-		CAPAB_VHT,
-	} hw_capab;
+	unsigned int hw_capab; /* bitmap of enum local_hw_capab bits */
 #ifdef CONFIG_MACSEC
 	struct ieee802_1x_kay *kay;
 #endif /* CONFIG_MACSEC */
@@ -1400,6 +1413,7 @@
 	unsigned int oci_freq_override_fils_assoc;
 	unsigned int oci_freq_override_wnm_sleep;
 	unsigned int disable_eapol_g2_tx;
+	unsigned int eapol_2_key_info_set_mask;
 	int test_assoc_comeback_type;
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -1619,6 +1633,9 @@
 	struct wpa_radio_work *pasn_auth_work;
 	unsigned int pasn_count;
 	struct pasn_auth *pasn_params;
+#ifdef CONFIG_P2P
+	struct wpa_radio_work *p2p_pasn_auth_work;
+#endif /* CONFIG_P2P */
 #endif /* CONFIG_PASN */
 
 	bool is_6ghz_enabled;
@@ -1760,7 +1777,7 @@
 void fils_connection_failure(struct wpa_supplicant *wpa_s);
 void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
-bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s);
+bool wpas_rsn_overriding(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
 void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
 		      const u8 *bssid);
@@ -1773,6 +1790,9 @@
 void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen,
 			 struct wpa_bss *bss);
+int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			   u8 *wpa_ie, size_t wpa_ie_len,
+			   size_t max_wpa_ie_len);
 int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
 			    enum wpas_mac_addr_style style,
 			    struct wpa_ssid *ssid);
@@ -1908,7 +1928,8 @@
 					struct channel_list_changed *info);
 int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
 					   struct wpa_bss *current_bss,
-					   struct wpa_bss *seleceted);
+					   struct wpa_bss *selected,
+					   bool poll_current);
 void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s);
 
 /* eap_register.c */
@@ -1944,8 +1965,10 @@
 
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+enum sae_pwe wpas_get_ssid_sae_pwe(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid);
 int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
-void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+void wpa_s_setup_sae_pt(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			bool force);
 void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s);
 
@@ -2003,6 +2026,11 @@
 				     struct wpa_ssid *group,
 				     int only_first_ssid, int debug_print);
 
+struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
+					   struct wpa_ssid *group,
+					   struct wpa_ssid **selected_ssid,
+					   int only_first_ssid);
+
 int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 						enum wpa_driver_if_type if_type,
 						unsigned int *num,
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 741ac6c..0011274 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -355,7 +355,10 @@
 			   "driver-based 4-way hs and FT");
 		res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
 		if (res == 0) {
-			os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+			if (wpa_key_mgmt_sha384(wpa_s->key_mgmt))
+				os_memcpy(pmk, buf, pmk_len);
+			else
+				os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
 			os_memset(buf, 0, sizeof(buf));
 		}
 #else /* CONFIG_IEEE80211R */
@@ -1473,8 +1476,6 @@
 			ptk, NULL, NULL, 0);
 }
 
-#endif /* CONFIG_NO_WPA */
-
 
 #ifdef CONFIG_PASN
 static int wpa_supplicant_set_ltf_keyseed(void *_wpa_s, const u8 *own_addr,
@@ -1509,6 +1510,8 @@
 	wpa_msg(wpa_s, MSG_INFO, "RSN: SSID matched expected value");
 }
 
+#endif /* CONFIG_NO_WPA */
+
 
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index f3a8c9c..d8f6198 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1375,7 +1375,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
 		wpa_supplicant_cancel_scan(wpa_s);
 		wpas_clear_wps(wpa_s);
-	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+	} else if (wpa_s->wpa_state >= WPA_ASSOCIATING) {
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
 			   "deauthenticate");
 		wpa_s->own_disconnect_req = 1;