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/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;