cumilative patch from commit 95ad71157

Bug: 341971059
Bug: 394110783
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 (401126565)

Fixed the TLS failure caused by 400b89162294f0344d82334218e8950fd01bb12f
95ad71157 P2P2: Get ID of device identity block from wpas_p2p_validate_dira()
f02e1d777 nl80211: Determine capability for P2P-R2 and PCC mode
c144dcbdf P2P2: Add support to fetch the P2P2 and PCC capability
d0528fb5e Add QCA vendor feature flags to indicate P2P-R2 and PCC support
b3b0297d8 P2P2: Indicate bootstrapping comeback response to upper layers
5ec339723 nl80211: Fix typo in SAE PWE debug message
d0213ad1e AP MLD: Fix hostapd crash during interface deinit with non-ML BSS
de49e55d7 BSS: Validate partner link BSSs while parsing Basic MLE
800e481bd MLD: Verify Per-STA Profile subelement length in reconf MLE
351089daa MLD: Fix Reconfiguration Multi-Link element parsing on non-AP MLD
53303bb3e Update the link BSS pointer during BSS reallocation on scan results
a4d2288de MACsec: Add option to always include ICV Indicator
ed423e44f authsrv: Log RADIUS accounting data
96dd8a03b RADIUS server: Add accounting message callback
373378f89 EAP-pwd: Do not include MS_FUNCS in CONFIG_FIPS=y builds
67feaa563 OpenSSL: Enable HMAC with short salt in FIPS configuration
0f92c8d8c OpenSSL: Use default provider instead of fips provider for DH group 5
6a15737b3 OpenSSL: Allow MD5 if FIPS mode or FIPS provider is set externally
573e9c1bb OpenSSL: Disable FIPS mode if MD4 is needed
1592d3416 OpenSSL: Print more failure details for EC failures
3b5b19e9c OpenSSL: Make debug log clearer on TLS initialization failure
651370325 SAE: Add an explicit debug print for failure to derive PWE
1af015a5d DPP: Remove a duplicated check for priv_key
c0b180d94 DPP: Add an explicit debug print for failed to build DPPEnvelopedData
ccc841470 SAE: Do not mark SAE enabled network disabled if PSK is not set
26c19188b OpenSSL: More debug prints on EVP digest/cipher failures
c063724b9 RADIUS: Check MD5 processing result
c073f61c4 Define additional QCA vendor roam trigger attribute values
f85a79cea Share wpa_init() error path handling
4c1ae9101 AP MLD: Search MLD-level and per-link PMKSA caches
0de2f1865 AP MLD: Store PMKSA from DPP to both per-link and MLD-level cache
4645fac93 AP MLD: Store PMKSA from control interface to both caches
c4dcf7577 AP MLD: List PMKSA entries from MLD-level cache too
a56d2bd97 AP MLD: Mark STA as MLD before checking association IEs
0d71e041b AP MLD: Store OWE/FILS PMKSA into the MLD-level PMKSA cache for MLO
8e326cabc AP MLD: Also remove from ml_pmksa when removing a PMKSA entry
c439291fc AP MLD: Store PMKSA generated from SAE authentication into ml_pmksa
799cc8eca AP MLD: Define a new MLD-level PMKSA cache shared by all links
1993770a5 Fix current_bss use in checking whether SSID has been verified
870d13f97 ERP: Initialize hapd->erp_keys earlier to avoid undefined behavior
caa22873c OpenSSL: Avoid undefined behavior in altSubjectName matching
616d85a42 Avoid undefined behavior in get_vendor_ie()
4cb1b7c31 trace: Avoid undefined behavior in backtrace search
449135c26 Remove undefined behavior from ieee802_11_defrag()
d185ab38f Fix wpa_supplicant global config bool reading/writing
e0baab3b6 mesh: Fix mesh_external_pmksa_cache initialization to cover error cases
47ff1b68f FT: Do not discard EAPOL-Start frame during initial MD association
012a893c4 wolfssl: Update suiteb ciphersuites
2ed980627 wpa_gui: Port to Qt6
39c7ef222 nl80211: Mark HT disabled on channel switch to a 6 GHz channel
b49542f42 nl80211: Fix hostapd crash when managing AP MLD interfaces
daeb5e111 EAP-TEAP: Check session_id length explicitly to avoid warnings
1bfd4398f RNR: Silence static analyzer warnings
dc05cbc46 WPS: Use 0xffff instead of -1 to set all u16 bits to 1
bc3ee85e5 AP MLD: Bounds checking for own Probe Response to silence analyzers
41398a873 nl80211: Debug print setsockopt() failures for NETLINK_EXT_ACK
97c4999f1 MLD: Try to avoid static analyzer warnings about tainted variable
a90f2f7a8 Use pointer to Action frame body instead of Category field
cb3a47247 Check random_get_bytes() output even in testing case
1c3b564af SME: Use a helper function for adding wpabuf to AssocReq IEs
bf936cdd1 P2P2: Check ssid != NULL more consistently in wpas_p2p_invite()
86dc3e369 mka: Simplify dl_list entry freeing
a36577c82 Make eht_cap != NULL check explicit to help static analyzers
553e114e0 PASN: Make ssid != NULL check easier for static analyzers
fab6e6546 P2P: Initialize new_ssid explicitly to make this easier for analyzers
741d23c3c P2P: Make sure go_dev_addr is initialized
12cd887c4 P2P2: Check peer_addr being available more consistently
714734135 Check network configuration availability before use in MAC randomization
ecf62b4d1 HS 2.0: Remove subscription remediation notification
7bb11e35d HS 2.0: Remove OSU related ANQP operations
e83528859 OSEN: Remove all OSEN functionality
f61b5faa4 XML: Remove now unused code
e960c3741 HTTP: Remove now unused code
b76b896d9 HS 2.0 client: Remove OSU/SPP/OMA-DM/EST functionality
3462d242a HS 2.0 server: Remove example OSU SPP server
326f093b9 Interworking: Prefer cred realm over realm in username for anonymous NAI
ca9f86a19 SAE: Multiple default password iteration
3e592a1a7 nl80211: Fix PASN auth alg check on big-endian CPUs
ff5248a8e nl80211: Fix HE 6 GHz capability fetching on big-endian CPUs
dacfb3956 MLD: Fix MLE parsing in for association on big-endian CPUs
7d2d4c1bd wpa_supplicant: Fix VHT SGI disabling on big-endian CPUs
4fd90f61d wpa_supplicant: Fix HT STBC capability overriding on big-endian CPUs
6b837f61b dbus: Remove unused wpas_dbus_sta_signal_prop_changed()
6dcc4c795 OpenSSL: Use proper ANSI C function declaration
f1425f2e1 RNR: Mark internal functions static
6fe387ad5 OCV: Fix HT/VHT capability check on big-endian CPUs
43dae6b35 PASN: Use NULL instead of 0 as a pointer
be7beba15 DPP: Mark internal functions static
33381b724 trace: Mark wpa_trace_test_fail static
3e4782549 SME: MLD: Fix byte order for the link reconfig MLE control field
ebfe8be3a Convert one-bit signed bitfield to unsigned
9db8ed4b9 SCS: Convert endianness of MAC Capabilities Information before use
ad8d30e0a AP MLD: Request MLO Group KDEs for a subset of links
00dc99117 nl80211: Remove support for use_monitor
539648699 AP MLD: Set WLAN_STA_ASSOC flag before copying STA entry (SME-in-driver)
90856b195 DPP: Send connection status result if disconnected during 4-way HS
ab3791d91 AP MLD: Allow new link to be added through control interface
00a7cad9c Make sure wpa_sm has correct PMK when PMKSA cache used by driver
298592db0 AP MLD: Cancel ap_sta_assoc_timeout for partner links
25876e936 Define a QCA vendor subcmd to set periodic probe response config
a971fa5b5 P2P: Check IEs buffer before use more consistently
f2bf0a638 nl80211: Avoid uninitialized err_info on test failure path
5066a39e0 Simplify supported rates and BSS membership selectors generation
ea626cb8c AP: Remove xrates_supported config
71c5ceb21 MBSSID: Add Extended Supported Rates element in MBSSID profile
085e5fa07 MBSSID: Correctly compute the Extended Supported Rates element length
2605fbc81 MBSSID: Fix Non-Inheritance element length calculation
5edbb257d GAS: Fix eloop timeout clearing on random MAC address error path
c8c7d56a3 P2P2: Reject P2P_VALIDATE_DIRA without nonce or tag
6fe367d6c EAP-pwd: Make code easier for static analyzers
c3fefaf41 JSON: More explicit check for depth in arrays/objects
50330cb91 SHA-PRF: Make code easier for static analyzers
218659c58 BSS: Clear wpa_bss pointers on bss entry removal
39b6e6efe P2P: Clear pending_listen_freq if listen failed
ec50904c1 Fix a typo in documentation of a configuration parameter
8ead26ff5 wolfSSL: Include asn.h to fix build with some library configs
13a30f10c drivers: RX-only configuration of the next TK during 4-way handshake
a58a0c592 MLD: Fix Multi-Link element parsing for association failures
a9eb687c0 AP MLD: Fix STA Info field parsing in Basic Multi-Link element
104e47a60 AP MLD: Simplify MLD MAC Address parsing from Multi-Link element
038cb0fc5 AP MLD: Fix Multi-Link element parsing in (Re)Association Request frame
2ba179722 P2P: Add Group ID info into P2P invitation control interface events
37b618743 EHT: Fix 6 GHz HE operation bandwidth for 320 MHz with puncturing
e35a79ab0 Fix crash due to iteratively calling radio_remove_works()
58ea0a652 nl80211: Fix compilation error due to uninitialized variable
5546f0ac0 Remove extra statement terminators
339a33455 RADIUS: Fix pending request dropping
b25769e48 QCA vendor values for traffic types for browsing and aperiodic bursts
f4c7c13ec Add QCA vendor test config attribute for link reconfiguration
dac63d98b Add QCA vendor attribute to enable/disable link reconfiguration support
8a8d66f00 Add QCA vendor command to trigger primary link migration

c12fc97e3 wolfssl: wc_PBKDF2() in FIPS requires unlocking the private key
e41e91a43 wolfSSL: Implement openssl_ecdh_curves
a1150cc60 wolfSSL: Simplify option setting in tls_set_conn_flags()
7bd855f34 wolfssl: Verify that session ticket setup does not fail
c38150cfe wolfssl: Actually use ocsp_stapling_response
b870181d4 wolfssl: Implement check_cert_subject
5c07e9be0 wolfssl: Add missing return in tls_init() in an error case
26e2ff781 wolfssl: Remove unused and non-compiling code for OCSP
77f82dcb3 wolfssl: Log error number on failure
0797fd2ab wolfssl: Remove unnecessary WOLFSSL_X509_STORE manipulation
59c3bd658 wolfssl: Generate events when OCSP status is revoked
49d0c323a wolfssl: Set additional sigalgs when using anonymous cipher
f50f530da wolfssl: Implement SuiteB ciphersuites
99239d08d wolfssl: Implement RSA-OAEP-SHA256 for EAP-AKA privacy protection
5ed2778db wolfssl: Support tod policy
34914311c wolfssl: Fix get_x509_cert()
d83903545 wolfssl: Use defines for ex_data access
a3890142d wolfssl: Simplify tls_get_cipher()
400b89162 OpenSSL: Use pkcs11-provider when OPENSSL_NO_ENGINE is defined
d37045e85 AP: Fix disconnect from EAPOL state machine handling
c924335ad AP: Include the Probe Request frame inside RX_PROBE_REQUEST event
5545ca8f9 GnuTLS: Do not override priority string on shutdown for reauth
26d64d737 GnuTLS: Use standard comment for falling through switch case
4a504c4e5 GnuTLS: Disable TLS 1.3 if instructed
5543f5efa wpa_supplicant: Use wpa_dbg() for "Successfully set 4addr mode"
9693a9018 Extend QCA vendor attributes for PASN offload from driver to userspace
24ef9862f nl80211: Send a list of BSS membership selectors supported by SME
2d2c42a47 AP: Only include SAE H2E BSS membership selector if SAE is required
bb6b2b854 Sync with wireless-next.git include/uapi/linux/nl80211.h
82c60dd54 STA: Support SPP A-MSDU negotiation
9c03bb14d nl80211: Option to tell the driver about SPP A-MSDUs in assoc params
61f505078 AP: Support SPP A-MSDU negotiation
27aedf346 AP: Add spp_amsdu configuration parameter
72ffc71d5 nl80211: SPP A-MSDU driver capability
f8d02941b RSNXE definition of SPP A-MSDU Capable
43c65f8a5 AP: Add a csa_ie_only testing option
4ba989fd3 NAN USD: Add NAN_UNPAUSE_PUBLISH to cancel pauseState
5b0201a27 hostapd: Fix 'start_disabled' option being ignored
0ca781b93 build: Disable some optimizations if TRACE_BFD is enabled
92cea9a77 P2P: Provide better failure reason for group formation errors
1ce37105d ext_password_file: Ensure full key match with password file entries
77ff2b61a STA: Known STA Identification to skip association comeback mechanism
e62c2dc09 AP: Known STA Identification to skip association comeback mechanism
726432d76 RADIUS: Drop pending request only when accepting the response
6799809ee P2P2: Bootstrapping through wpas_p2p_prov_disc()
9f7dc9c30 P2P2: Control interface command to validate DIRA info
fee68c237 P2P2: Control interface command to get DIRA info of a P2P device
99de77c57 P2P2: Save the latest nonce and tag from a P2P USD discovery frame
7c9081685 wlantest: Parse Link Reconfiguration Request/Response
9e95f0d38 AP MLD: Skip over Extended MLD Capabilities And Operations field
b375b7630 Do not write mesh_fwding network parameter if it has default value
b6cab22c2 P2P2: Update P2P_INVITE processing for SSID randomization
374353d31 P2P2: SSID randomization on group reinvoke
ce193d6e1 P2P: Make p2p_build_ssid() available outside src/p2p
ec4569174 P2P2: Store ID of Device Identity block in network block
c96fd75b1 P2P2: Add USD service hash in the P2P2 PASN M1 frame
4845c87eb P2P2: Random PMKID in pairing verification
99905ab44 P2P2: Return ID of identity block for p2p_validate_dira()
f92959500 P2P2: Add DIRA to PASN-M1 during pairing verification
ee41bacfa OWE: Consider the currently associated transition mode SSID known
b0f587944 Do not remove a currently used BSS entry when removing oldest unknown BSS
c78295c35 nl80211: Fix compilation error when CONFIG_DRIVER_NL80211_BRCM is enabled
d73ad8cbc Enable beacon protection if IEEE 802.11be/EHT is enabled for BSS
9cb7b0bce DPP: Discard DPP Action frame in AP mode if no global DPP context
12f1edc9e RSNO: Generate IGTK if any of the RSN variants has PMF enabled
dd65d13ce Add QCA vendor attribute for BTM reject support
001a728fe Add QCA vendor attribute for extra EHT-LTF support
c54f4aab6 Add QCA vendor attribute for triggered SU BF support

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source

Change-Id: I0e1b0cda9e035d946283b6a83a1f6d796b882bb7
Signed-off-by: Sunil Ravi <sunilravi@google.com>
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index c8fbb6a..69550cf 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -793,6 +793,8 @@
 #ifdef CONFIG_SAE_PK
 		sae_deinit_pk(tmp->pk);
 #endif /* CONFIG_SAE_PK */
+		os_free(tmp->success_mac);
+		os_free(tmp->fail_mac);
 		os_free(tmp);
 	}
 }
@@ -931,31 +933,6 @@
 	os_free(conf->hs20_wan_metrics);
 	os_free(conf->hs20_connection_capability);
 	os_free(conf->hs20_operating_class);
-	os_free(conf->hs20_icons);
-	if (conf->hs20_osu_providers) {
-		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
-			struct hs20_osu_provider *p;
-			size_t j;
-			p = &conf->hs20_osu_providers[i];
-			os_free(p->friendly_name);
-			os_free(p->server_uri);
-			os_free(p->method_list);
-			for (j = 0; j < p->icons_count; j++)
-				os_free(p->icons[j]);
-			os_free(p->icons);
-			os_free(p->osu_nai);
-			os_free(p->osu_nai2);
-			os_free(p->service_desc);
-		}
-		os_free(conf->hs20_osu_providers);
-	}
-	if (conf->hs20_operator_icon) {
-		for (i = 0; i < conf->hs20_operator_icon_count; i++)
-			os_free(conf->hs20_operator_icon[i]);
-		os_free(conf->hs20_operator_icon);
-	}
-	os_free(conf->subscr_remediation_url);
-	os_free(conf->hs20_sim_provisioning_url);
 	os_free(conf->t_c_filename);
 	os_free(conf->t_c_server_url);
 #endif /* CONFIG_HS20 */
@@ -1513,6 +1490,13 @@
 		wpa_printf(MSG_INFO,
 			   "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
 	}
+
+	if (full_config && conf->ieee80211be && !bss->disable_11be &&
+	    !bss->beacon_prot && ap_pmf_enabled(bss)) {
+		bss->beacon_prot = 1;
+		wpa_printf(MSG_INFO,
+			   "Enabling beacon protection as IEEE 802.11be is enabled for this BSS");
+	}
 #endif /* CONFIG_IEEE80211BE */
 
 	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
@@ -1521,6 +1505,13 @@
 		return -1;
 	}
 
+	/* Do not advertise SPP A-MSDU support if not using CCMP/GCMP */
+	if (full_config && bss->spp_amsdu &&
+	    !(bss->wpa &&
+	      bss->rsn_pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+				   WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+		bss->spp_amsdu = false;
+
 	return 0;
 }
 
@@ -1687,11 +1678,6 @@
 		if (full_config)
 			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
 #endif /* CONFIG_WEP */
-	} else if (bss->osen) {
-		bss->ssid.security_policy = SECURITY_OSEN;
-		bss->wpa_group = WPA_CIPHER_CCMP;
-		bss->wpa_pairwise = 0;
-		bss->rsn_pairwise = WPA_CIPHER_CCMP;
 	} else {
 		bss->ssid.security_policy = SECURITY_PLAINTEXT;
 		if (full_config) {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index d33ba9d..a587b96 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -93,7 +93,6 @@
 	SECURITY_IEEE_802_1X = 2,
 	SECURITY_WPA_PSK = 3,
 	SECURITY_WPA = 4,
-	SECURITY_OSEN = 5
 } secpolicy;
 
 struct hostapd_ssid {
@@ -189,7 +188,6 @@
 	unsigned int wildcard_prefix:1;
 	unsigned int password_hash:1; /* whether password is hashed with
 				       * nt_password_hash() */
-	unsigned int remediation:1;
 	unsigned int macacl:1;
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
 	struct hostapd_radius_attr *accept_attr;
@@ -260,6 +258,10 @@
 	int vlan_id;
 	struct sae_pt *pt;
 	struct sae_pk *pk;
+	u8 *success_mac;
+	unsigned int num_success_mac, next_success_mac;
+	u8 *fail_mac;
+	unsigned int num_fail_mac, next_fail_mac;
 };
 
 struct dpp_controller_conf {
@@ -465,6 +467,7 @@
 	char *radius_server_clients;
 	int radius_server_auth_port;
 	int radius_server_acct_port;
+	int radius_server_acct_log;
 	int radius_server_ipv6;
 
 	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
@@ -620,7 +623,6 @@
 	u8 qos_map_set[16 + 2 * 21];
 	unsigned int qos_map_set_len;
 
-	int osen;
 	int proxy_arp;
 	int na_mcast_to_ucast;
 
@@ -636,37 +638,7 @@
 	size_t hs20_connection_capability_len;
 	u8 *hs20_operating_class;
 	u8 hs20_operating_class_len;
-	struct hs20_icon {
-		u16 width;
-		u16 height;
-		char language[3];
-		char type[256];
-		char name[256];
-		char file[256];
-	} *hs20_icons;
-	size_t hs20_icons_count;
-	u8 osu_ssid[SSID_MAX_LEN];
-	size_t osu_ssid_len;
-	struct hs20_osu_provider {
-		unsigned int friendly_name_count;
-		struct hostapd_lang_string *friendly_name;
-		char *server_uri;
-		int *method_list;
-		char **icons;
-		size_t icons_count;
-		char *osu_nai;
-		char *osu_nai2;
-		unsigned int service_desc_count;
-		struct hostapd_lang_string *service_desc;
-	} *hs20_osu_providers, *last_osu;
-	size_t hs20_osu_providers_count;
-	size_t hs20_osu_providers_nai_count;
-	char **hs20_operator_icon;
-	size_t hs20_operator_icon_count;
 	unsigned int hs20_deauth_req_timeout;
-	char *subscr_remediation_url;
-	u8 subscr_remediation_method;
-	char *hs20_sim_provisioning_url;
 	char *t_c_filename;
 	u32 t_c_timestamp;
 	char *t_c_server_url;
@@ -688,6 +660,7 @@
 	enum sae_pwe sae_pwe;
 	int *sae_groups;
 	struct sae_password_entry *sae_passwords;
+	int sae_track_password;
 
 	char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
@@ -919,6 +892,14 @@
 	int macsec_csindex;
 
 	/**
+	 * macsec_icv_indicator - Always include ICV Indicator
+	 * (for compatibility with older MACsec switches)
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_icv_indicator;
+
+	/**
 	 * mka_ckn - MKA pre-shared CKN
 	 */
 #define MACSEC_CKN_MAX_LEN 32
@@ -972,9 +953,9 @@
 
 	u8 rnr;
 	char *config_id;
-	bool xrates_supported;
 
 	bool ssid_protection;
+	bool known_sta_identification;
 
 #ifdef CONFIG_IEEE80211BE
 	/* The AP is part of an AP MLD */
@@ -995,6 +976,8 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_IEEE80211BE */
 	int mbssid_index;
+
+	bool spp_amsdu;
 };
 
 /**
@@ -1152,9 +1135,10 @@
 	double ignore_assoc_probability;
 	double ignore_reassoc_probability;
 	double corrupt_gtk_rekey_mic_probability;
-	int ecsa_ie_only;
 	unsigned int skip_send_eapol;
 	unsigned int enable_eapol_large_timeout;
+	int ecsa_ie_only;
+	int csa_ie_only;
 	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 65e83f4..d342132 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -39,6 +39,8 @@
 		res |= WPA_STA_AUTHENTICATED;
 	if (flags & WLAN_STA_ASSOC)
 		res |= WPA_STA_ASSOCIATED;
+	if (flags & WLAN_STA_SPP_AMSDU)
+		res |= WPA_STA_SPP_AMSDU;
 	return res;
 }
 
@@ -183,11 +185,6 @@
 	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
 	    add_buf_data(&proberesp, buf, pos - buf) < 0)
 		goto fail;
-
-	pos = hostapd_eid_osen(hapd, buf);
-	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
-	    add_buf_data(&proberesp, buf, pos - buf) < 0)
-		goto fail;
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index cbb8044..b527636 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -491,6 +491,6 @@
 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);;
+		      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 630cef6..3e80318 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -14,6 +14,7 @@
 #include "eap_server/eap.h"
 #include "eap_server/eap_sim_db.h"
 #include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius.h"
 #include "radius/radius_server.h"
 #include "hostapd.h"
 #include "ap_config.h"
@@ -89,7 +90,6 @@
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
-	user->remediation = eap_user->remediation;
 	user->accept_attr = eap_user->accept_attr;
 	user->t_c_timestamp = eap_user->t_c_timestamp;
 	rv = 0;
@@ -102,6 +102,114 @@
 }
 
 
+/**
+ * hostapd_radius_log_acct_req - Callback for logging received RADIUS
+ * accounting requests
+ * @ctx: Context (struct hostapd_data)
+ * @msg: Received RADIUS accounting request
+ * @status_type: Status type from the message (parsed Acct-Status-Type
+ * attribute)
+ * Returns: 0 on success, -1 on failure
+ */
+static int hostapd_radius_log_acct_req(void *ctx, struct radius_msg *msg,
+				       u32 status_type)
+{
+	char nas_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char session_id[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char username[RADIUS_MAX_ATTR_LEN + 1] = "";
+	char calling_station_id[3 * ETH_ALEN] = "";
+	u32 session_time = 0, terminate_cause = 0,
+		bytes_in = 0, bytes_out = 0,
+		packets_in = 0, packets_out = 0,
+		gigawords_in = 0, gigawords_out = 0;
+	unsigned long long total_bytes_in = 0, total_bytes_out = 0;
+
+	/* Parse NAS identification (required by RFC 2866, section 4.1) */
+	if (radius_msg_get_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) nas_id,
+				sizeof(nas_id) - 1))
+		nas_id[0] = '\0';
+
+	/* Process Accounting-On and Accounting-Off messages separately */
+	if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON ||
+	    status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
+		wpa_printf(MSG_INFO, "RADIUS ACCT: NAS='%s' status='%s'",
+			   nas_id,
+			   status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON
+			   ? "Accounting-On" : "Accounting-Off");
+		return 0;
+	}
+
+	/* Parse session ID (required by RFC 2866, section 5.5) */
+	if (radius_msg_get_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				(u8 *) session_id,
+				sizeof(session_id) - 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS ACCT: request doesn't include session ID");
+		return -1;
+	}
+
+	/* Parse user name */
+	radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) username,
+			    sizeof(username) - 1);
+
+	/* Parse device identifier */
+	radius_msg_get_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+			    (u8 *) calling_station_id,
+			    sizeof(calling_station_id) - 1);
+
+	switch (status_type) {
+	case RADIUS_ACCT_STATUS_TYPE_START:
+		wpa_printf(MSG_INFO,
+			   "RADIUS ACCT: NAS='%s' session='%s' status='Accounting-Start' station='%s' username='%s'",
+			   nas_id, session_id, calling_station_id, username);
+		break;
+	case RADIUS_ACCT_STATUS_TYPE_STOP:
+	case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
+		/* Parse counters */
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+					  &session_time);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+					  &terminate_cause);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS,
+					  &bytes_in);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+					  &bytes_out);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS,
+					  &packets_in);
+		radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+					  &packets_out);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+					  &gigawords_in);
+		radius_msg_get_attr_int32(msg,
+					  RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+					  &gigawords_out);
+
+		/* RFC 2869, section 5.1 and 5.2 */
+		total_bytes_in = ((u64) gigawords_in << 32) + bytes_in;
+		total_bytes_out = ((u64) gigawords_out << 32) + bytes_out;
+
+		wpa_printf(MSG_INFO,
+			   "RADIUS ACCT: NAS='%s' session='%s' status='%s' station='%s' username='%s' session_time=%u term_cause=%u pck_in=%u pck_out=%u bytes_in=%llu bytes_out=%llu",
+			   nas_id, session_id,
+			   status_type == RADIUS_ACCT_STATUS_TYPE_STOP ?
+			   "Accounting-Stop" : "Accounting-Interim-Update",
+			   calling_station_id, username, session_time,
+			   terminate_cause, packets_in, packets_out,
+			   total_bytes_in, total_bytes_out);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS ACCT: Unknown request status type %u",
+			   status_type);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
 {
 	struct radius_server_conf srv;
@@ -129,6 +237,8 @@
 	srv.conf_ctx = hapd;
 	srv.ipv6 = conf->radius_server_ipv6;
 	srv.get_eap_user = hostapd_radius_get_eap_user;
+	if (conf->radius_server_acct_log)
+		srv.acct_req_cb = hostapd_radius_log_acct_req;
 	srv.eap_req_id_text = conf->eap_req_id_text;
 	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
 	srv.sqlite_file = conf->eap_user_sqlite;
@@ -136,9 +246,6 @@
 	srv.dump_msk_file = conf->dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
 #ifdef CONFIG_HS20
-	srv.subscr_remediation_url = conf->subscr_remediation_url;
-	srv.subscr_remediation_method = conf->subscr_remediation_method;
-	srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
 	srv.t_c_server_url = conf->t_c_server_url;
 #endif /* CONFIG_HS20 */
 	srv.erp_domain = conf->erp_domain;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 542768d..a7d7ecd 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -403,19 +403,6 @@
 }
 
 
-static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
-{
-	const u8 *ie;
-
-	ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
-	if (!ie || 2U + ie[1] > len)
-		return pos;
-
-	os_memcpy(pos, ie, 2 + ie[1]);
-	return pos + 2 + ie[1];
-}
-
-
 static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
 				      size_t len)
 {
@@ -516,6 +503,11 @@
 	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
 		return eid;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iconf->csa_ie_only)
+		return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
 	*eid++ = 4;
 	*eid++ = hapd->cs_block_tx;
@@ -593,7 +585,6 @@
 	size_t len, rnr_len = 0;
 	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
 	u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
-	size_t i;
 
 	if (!iface->mbssid_max_interfaces ||
 	    iface->num_bss > iface->mbssid_max_interfaces ||
@@ -601,14 +592,6 @@
 	     !iface->ema_max_periodicity))
 		goto fail;
 
-	/* Make sure bss->xrates_supported is set for all BSSs to know whether
-	 * it need to be non-inherited. */
-	for (i = 0; i < iface->num_bss; i++) {
-		u8 buf[100];
-
-		hostapd_eid_ext_supp_rates(iface->bss[i], buf);
-	}
-
 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
 	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
 				     NULL, 0, &rnr_len);
@@ -959,9 +942,8 @@
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
-	/* WPA / OSEN */
+	/* WPA */
 	pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
-	pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
 
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
@@ -1427,6 +1409,7 @@
 	size_t csa_offs_len;
 	struct radius_sta rad_info;
 	struct probe_resp_params params;
+	char *hex = NULL;
 #ifdef CONFIG_IEEE80211BE
 	int mld_id;
 	u16 links;
@@ -1659,8 +1642,20 @@
 	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
 		return;
 
+	if (hapd->conf->notify_mgmt_frames) {
+		size_t hex_len;
+
+		hex_len = len * 2 + 1;
+		hex = os_malloc(hex_len);
+		if (hex)
+			wpa_snprintf_hex(hex, hex_len, (const u8 *) mgmt, len);
+	}
+
 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
-		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+		     " signal=%d%s%s", MAC2STR(mgmt->sa), ssi_signal,
+		     hex ? " buf=" : "", hex ? hex : "");
+
+	os_free(hex);
 
 	os_memset(&params, 0, sizeof(params));
 
@@ -2414,9 +2409,8 @@
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
-	/* WPA / OSEN */
+	/* WPA */
 	tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
-	tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
 
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
@@ -2588,10 +2582,6 @@
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HS20
 	params->disable_dgaf = hapd->conf->disable_dgaf;
-	if (hapd->conf->osen) {
-		params->privacy = 1;
-		params->osen = 1;
-	}
 #endif /* CONFIG_HS20 */
 	params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
 	params->pbss = hapd->conf->pbss;
@@ -3111,7 +3101,7 @@
 {
 	bool tx_vap = hapd == hostapd_mbssid_get_tx_bss(hapd);
 	size_t link_data_len, sta_profile_len;
-	size_t own_data_len;
+	size_t own_data_len, fixed;
 	struct probe_resp_params link_params;
 	struct probe_resp_params own_params;
 	struct ieee80211_mgmt *link_data;
@@ -3139,7 +3129,10 @@
 	own_data_len = own_params.resp_len;
 
 	/* Consider the length of the variable fields */
-	own_data_len -= offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+	fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+	if (own_data_len < fixed)
+		goto fail;
+	own_data_len -= fixed;
 
 	for_each_mld_link(link_bss, hapd) {
 		if (link_bss == hapd || !link_bss->started)
@@ -3164,8 +3157,10 @@
 		link_data_len = link_params.resp_len;
 
 		/* Consider length of the variable fields */
-		link_data_len -= offsetof(struct ieee80211_mgmt,
-					  u.probe_resp.variable);
+		fixed = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+		if (link_data_len < fixed)
+			continue;
+		link_data_len -= fixed;
 
 		sta_profile = hostapd_gen_sta_profile(link_data, link_data_len,
 						      own_data, own_data_len,
@@ -3198,6 +3193,7 @@
 		os_free(link_params.resp);
 	}
 
+fail:
 	os_free(own_params.resp);
 }
 
@@ -3248,7 +3244,8 @@
 				continue;
 #endif /* CONFIG_IEEE80211BE */
 
-			if (other->bss[i] && other->bss[i]->started)
+			if (other->bss[i] && other->bss[i]->started &&
+			    other->bss[i]->beacon_set_done)
 				__ieee802_11_set_beacon(other->bss[i]);
 		}
 	}
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 4a51e63..441995b 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1198,6 +1198,7 @@
 	size_t pmk_len;
 	char *pos, *pos2;
 	int akmp = 0, expiration = 0;
+	int ret;
 
 	/*
 	 * Entry format:
@@ -1233,8 +1234,18 @@
 	if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
 		return -1;
 
-	return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
-				   pmkid, expiration, akmp, NULL);
+	ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+				  pmkid, expiration, akmp, NULL, false);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		ret = wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+					  pmkid, expiration, akmp, NULL, true);
+#endif /* CONFIG_IEEE80211BE */
+
+	return ret;
 }
 
 
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 3dc4639..d94ca9e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -2160,11 +2160,22 @@
 
 	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap &&
+	    wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to add PMKSA cache entry (MLD)");
+		goto done;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 					DPP_STATUS_OK);
 done:
@@ -2934,11 +2945,22 @@
 
 	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash, false) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap &&
+	    wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash, true) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to add PMKSA cache entry (MLD)");
+		goto done;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
 		   MACSTR, MAC2STR(src));
 
@@ -2958,6 +2980,10 @@
 	const u8 *hdr;
 	unsigned int pkex_t;
 
+	/* Discard DPP Action frames if there is no global DPP context */
+	if (!hapd->iface->interfaces || !hapd->iface->interfaces->dpp)
+		return;
+
 	if (len < DPP_HDR_LEN)
 		return;
 	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 82a922e..0b4613e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -366,12 +366,6 @@
 		ie = elems.wpa_ie - 2;
 		ielen = elems.wpa_ie_len + 2;
 		wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
-#ifdef CONFIG_HS20
-	} else if (elems.osen) {
-		ie = elems.osen - 2;
-		ielen = elems.osen_len + 2;
-		wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
-#endif /* CONFIG_HS20 */
 	} else {
 		ie = NULL;
 		ielen = 0;
@@ -579,7 +573,8 @@
 					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
 					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
 					  elems.mdie, elems.mdie_len,
-					  elems.owe_dh, elems.owe_dh_len, NULL);
+					  elems.owe_dh, elems.owe_dh_len, NULL,
+					  ap_sta_is_mld(hapd, sta));
 		reason = WLAN_REASON_INVALID_IE;
 		status = WLAN_STATUS_INVALID_IE;
 		switch (res) {
@@ -649,6 +644,11 @@
 		else
 			sta->flags &= ~WLAN_STA_MFP;
 
+		if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+			sta->flags |= WLAN_STA_SPP_AMSDU;
+		else
+			sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
@@ -713,29 +713,6 @@
 			sta->flags |= WLAN_STA_MAYBE_WPS;
 		wpabuf_free(wps);
 #endif /* CONFIG_WPS */
-#ifdef CONFIG_HS20
-	} else if (hapd->conf->osen) {
-		if (elems.osen == NULL) {
-			hostapd_logger(
-				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-				HOSTAPD_LEVEL_INFO,
-				"No HS 2.0 OSEN element in association request");
-			return WLAN_STATUS_INVALID_IE;
-		}
-
-		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
-		if (sta->wpa_sm == NULL)
-			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-							sta->addr, NULL);
-		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING,
-				   "Failed to initialize WPA state machine");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
-		}
-		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems.osen - 2, elems.osen_len + 2) < 0)
-			return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
 	}
 #ifdef CONFIG_WPS
 skip_wpa_check:
@@ -918,6 +895,12 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
 
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+	hostapd_set_sta_flags(hapd, sta);
+
 #ifdef CONFIG_IEEE80211BE
 	if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
 					  !!reassoc, WLAN_STATUS_SUCCESS,
@@ -928,11 +911,6 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
-	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
-	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
-	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-
-	hostapd_set_sta_flags(hapd, sta);
 	if (updated)
 		ap_sta_set_authorized_event(hapd, sta, 1);
 
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index a510ee3..c0e9030 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -89,8 +89,6 @@
 			user->next = (void *) 1;
 		} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
 			set_user_methods(user, argv[i]);
-		} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
-			user->remediation = strlen(argv[i]) > 0;
 		} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
 			user->t_c_timestamp = strtol(argv[i], NULL, 10);
 		}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 4642e49..13cf766 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -179,14 +179,6 @@
 		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
 	if (hapd->conf->hs20_operating_class)
 		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
-	if (hapd->conf->hs20_osu_providers_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
-	if (hapd->conf->hs20_osu_providers_nai_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
-	if (hapd->conf->hs20_icons_count)
-		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
-	if (hapd->conf->hs20_operator_icon_count)
-		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
 	gas_anqp_set_element_len(buf, len);
 }
 #endif /* CONFIG_HS20 */
@@ -706,232 +698,6 @@
 	}
 }
 
-
-static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
-			  const char *name)
-{
-	size_t j;
-	struct hs20_icon *icon = NULL;
-
-	for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
-		if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
-			icon = &bss->hs20_icons[j];
-	}
-	if (!icon)
-		return; /* icon info not found */
-
-	wpabuf_put_le16(buf, icon->width);
-	wpabuf_put_le16(buf, icon->height);
-	wpabuf_put_data(buf, icon->language, 3);
-	wpabuf_put_u8(buf, os_strlen(icon->type));
-	wpabuf_put_str(buf, icon->type);
-	wpabuf_put_u8(buf, os_strlen(icon->name));
-	wpabuf_put_str(buf, icon->name);
-}
-
-
-static void anqp_add_osu_provider(struct wpabuf *buf,
-				  struct hostapd_bss_config *bss,
-				  struct hs20_osu_provider *p)
-{
-	u8 *len, *len2, *count;
-	unsigned int i;
-
-	len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
-
-	/* OSU Friendly Name Duples */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->friendly_name_count; i++) {
-		struct hostapd_lang_string *s = &p->friendly_name[i];
-		wpabuf_put_u8(buf, 3 + s->name_len);
-		wpabuf_put_data(buf, s->lang, 3);
-		wpabuf_put_data(buf, s->name, s->name_len);
-	}
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	/* OSU Server URI */
-	if (p->server_uri) {
-		wpabuf_put_u8(buf, os_strlen(p->server_uri));
-		wpabuf_put_str(buf, p->server_uri);
-	} else
-		wpabuf_put_u8(buf, 0);
-
-	/* OSU Method List */
-	count = wpabuf_put(buf, 1);
-	for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
-		wpabuf_put_u8(buf, p->method_list[i]);
-	*count = i;
-
-	/* Icons Available */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->icons_count; i++)
-		anqp_add_icon(buf, bss, p->icons[i]);
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	/* OSU_NAI */
-	if (p->osu_nai) {
-		wpabuf_put_u8(buf, os_strlen(p->osu_nai));
-		wpabuf_put_str(buf, p->osu_nai);
-	} else
-		wpabuf_put_u8(buf, 0);
-
-	/* OSU Service Description Duples */
-	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->service_desc_count; i++) {
-		struct hostapd_lang_string *s = &p->service_desc[i];
-		wpabuf_put_u8(buf, 3 + s->name_len);
-		wpabuf_put_data(buf, s->lang, 3);
-		wpabuf_put_data(buf, s->name, s->name_len);
-	}
-	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
-
-	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
-}
-
-
-static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
-					struct wpabuf *buf)
-{
-	if (hapd->conf->hs20_osu_providers_count) {
-		size_t i;
-		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
-		wpabuf_put_u8(buf, 0); /* Reserved */
-
-		/* OSU SSID */
-		wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
-		wpabuf_put_data(buf, hapd->conf->osu_ssid,
-				hapd->conf->osu_ssid_len);
-
-		/* Number of OSU Providers */
-		wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
-
-		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
-			anqp_add_osu_provider(
-				buf, hapd->conf,
-				&hapd->conf->hs20_osu_providers[i]);
-		}
-
-		gas_anqp_set_element_len(buf, len);
-	}
-}
-
-
-static void anqp_add_osu_provider_nai(struct wpabuf *buf,
-				      struct hs20_osu_provider *p)
-{
-	/* OSU_NAI for shared BSS (Single SSID) */
-	if (p->osu_nai2) {
-		wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
-		wpabuf_put_str(buf, p->osu_nai2);
-	} else {
-		wpabuf_put_u8(buf, 0);
-	}
-}
-
-
-static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
-					    struct wpabuf *buf)
-{
-	if (hapd->conf->hs20_osu_providers_nai_count) {
-		size_t i;
-		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-		wpabuf_put_be24(buf, OUI_WFA);
-		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
-		wpabuf_put_u8(buf, 0); /* Reserved */
-
-		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
-			anqp_add_osu_provider_nai(
-				buf, &hapd->conf->hs20_osu_providers[i]);
-		}
-
-		gas_anqp_set_element_len(buf, len);
-	}
-}
-
-
-static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
-				      struct wpabuf *buf,
-				      const u8 *name, size_t name_len)
-{
-	struct hs20_icon *icon;
-	size_t i;
-	u8 *len;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
-			  name, name_len);
-	for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
-		icon = &hapd->conf->hs20_icons[i];
-		if (name_len == os_strlen(icon->name) &&
-		    os_memcmp(name, icon->name, name_len) == 0)
-			break;
-	}
-
-	if (i < hapd->conf->hs20_icons_count)
-		icon = &hapd->conf->hs20_icons[i];
-	else
-		icon = NULL;
-
-	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-	wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
-	wpabuf_put_u8(buf, 0); /* Reserved */
-
-	if (icon) {
-		char *data;
-		size_t data_len;
-
-		data = os_readfile(icon->file, &data_len);
-		if (data == NULL || data_len > 65535) {
-			wpabuf_put_u8(buf, 2); /* Download Status:
-						* Unspecified file error */
-			wpabuf_put_u8(buf, 0);
-			wpabuf_put_le16(buf, 0);
-		} else {
-			wpabuf_put_u8(buf, 0); /* Download Status: Success */
-			wpabuf_put_u8(buf, os_strlen(icon->type));
-			wpabuf_put_str(buf, icon->type);
-			wpabuf_put_le16(buf, data_len);
-			wpabuf_put_data(buf, data, data_len);
-		}
-		os_free(data);
-	} else {
-		wpabuf_put_u8(buf, 1); /* Download Status: File not found */
-		wpabuf_put_u8(buf, 0);
-		wpabuf_put_le16(buf, 0);
-	}
-
-	gas_anqp_set_element_len(buf, len);
-}
-
-
-static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
-					    struct wpabuf *buf)
-{
-	struct hostapd_bss_config *bss = hapd->conf;
-	size_t i;
-	u8 *len;
-
-	if (!bss->hs20_operator_icon_count)
-		return;
-
-	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
-	wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
-	wpabuf_put_u8(buf, 0); /* Reserved */
-
-	for (i = 0; i < bss->hs20_operator_icon_count; i++)
-		anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
-
-	gas_anqp_set_element_len(buf, len);
-}
-
 #endif /* CONFIG_HS20 */
 
 
@@ -973,7 +739,6 @@
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
 				unsigned int request,
 				const u8 *home_realm, size_t home_realm_len,
-				const u8 *icon_name, size_t icon_name_len,
 				const u16 *extra_req,
 				unsigned int num_extra_req)
 {
@@ -984,8 +749,6 @@
 	len = 1400;
 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
 		len += 1000;
-	if (request & ANQP_REQ_ICON_REQUEST)
-		len += 65536;
 #ifdef CONFIG_FILS
 	if (request & ANQP_FILS_REALM_INFO)
 		len += 2 * dl_list_len(&hapd->conf->fils_realms);
@@ -1054,14 +817,6 @@
 		anqp_add_connection_capability(hapd, buf);
 	if (request & ANQP_REQ_OPERATING_CLASS)
 		anqp_add_operating_class(hapd, buf);
-	if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
-		anqp_add_osu_providers_list(hapd, buf);
-	if (request & ANQP_REQ_ICON_REQUEST)
-		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
-	if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
-		anqp_add_operator_icon_metadata(hapd, buf);
-	if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
-		anqp_add_osu_providers_nai_list(hapd, buf);
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
@@ -1079,8 +834,6 @@
 	unsigned int request;
 	const u8 *home_realm_query;
 	size_t home_realm_query_len;
-	const u8 *icon_name;
-	size_t icon_name_len;
 	int p2p_sd;
 	u16 extra_req[ANQP_MAX_EXTRA_REQ];
 	unsigned int num_extra_req;
@@ -1245,20 +998,6 @@
 		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
 			     hapd->conf->hs20_operating_class != NULL, qi);
 		break;
-	case HS20_STYPE_OSU_PROVIDERS_LIST:
-		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
-			     hapd->conf->hs20_osu_providers_count, qi);
-		break;
-	case HS20_STYPE_OPERATOR_ICON_METADATA:
-		set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
-			     "Operator Icon Metadata",
-			     hapd->conf->hs20_operator_icon_count, qi);
-		break;
-	case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
-		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
-			     "OSU Providers NAI List",
-			     hapd->conf->hs20_osu_providers_nai_count, qi);
-		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
 			   subtype);
@@ -1284,23 +1023,6 @@
 }
 
 
-static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
-				    const u8 *pos, const u8 *end,
-				    struct anqp_query_info *qi)
-{
-	qi->request |= ANQP_REQ_ICON_REQUEST;
-	qi->icon_name = pos;
-	qi->icon_name_len = end - pos;
-	if (hapd->conf->hs20_icons_count) {
-		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
-			   "(local)");
-	} else {
-		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
-			   "available");
-	}
-}
-
-
 static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
 					 const u8 *pos, const u8 *end,
 					 struct anqp_query_info *qi)
@@ -1323,9 +1045,6 @@
 	case HS20_STYPE_NAI_HOME_REALM_QUERY:
 		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
 		break;
-	case HS20_STYPE_ICON_REQUEST:
-		rx_anqp_hs_icon_request(hapd, pos, end, qi);
-		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
 			   "%u", subtype);
@@ -1455,7 +1174,6 @@
 	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
 					      qi->home_realm_query,
 					      qi->home_realm_query_len,
-					      qi->icon_name, qi->icon_name_len,
 					      qi->extra_req, qi->num_extra_req);
 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
 			buf);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 4bc6b3a..65dc14d 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -162,7 +162,7 @@
 	else
 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
-	if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
+	if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
 		hostapd_setup_wpa(hapd);
 		if (hapd->wpa_auth)
 			wpa_init_keys(hapd->wpa_auth);
@@ -358,7 +358,7 @@
 				   ifname, i);
 		}
 	}
-	if (hapd->conf->ieee80211w) {
+	if (ap_pmf_enabled(hapd->conf)) {
 		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
 			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
 						NULL, i, 0, 0, NULL,
@@ -1671,7 +1671,7 @@
 		return -1;
 	}
 
-	if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+	if (conf->wpa && hostapd_setup_wpa(hapd))
 		return -1;
 
 	if (accounting_init(hapd)) {
@@ -2765,7 +2765,7 @@
 		hostapd_neighbor_set_own_report(iface->bss[j]);
 
 	if (iface->interfaces && iface->interfaces->count > 1)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 
 	return 0;
 
@@ -2965,6 +2965,7 @@
 #ifdef CONFIG_SAE
 	dl_list_init(&hapd->sae_commit_queue);
 #endif /* CONFIG_SAE */
+	dl_list_init(&hapd->erp_keys);
 
 	return hapd;
 }
@@ -3354,6 +3355,7 @@
 {
 	struct hostapd_iface *new_iface = NULL, *iface = NULL;
 	struct hostapd_data *hapd;
+	struct hostapd_config *conf;
 	int k;
 	size_t i, bss_idx;
 
@@ -3369,17 +3371,26 @@
 
 	wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
 		   config_fname, phy, iface ? "" : " --> new PHY");
+
+	conf = interfaces->config_read_cb(config_fname);
+	if (!conf)
+		return NULL;
+
+#ifdef CONFIG_IEEE80211BE
+	/* AP MLD can be enabled with the same interface name, so even if we
+	 * get the interface, we still need to allocate a new hostapd_iface
+	 * structure. */
+	if (conf->bss[0]->mld_ap)
+		iface = NULL;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (iface) {
-		struct hostapd_config *conf;
 		struct hostapd_bss_config **tmp_conf;
 		struct hostapd_data **tmp_bss;
 		struct hostapd_bss_config *bss;
 		const char *ifname;
 
 		/* Add new BSS to existing iface */
-		conf = interfaces->config_read_cb(config_fname);
-		if (conf == NULL)
-			return NULL;
 		if (conf->num_bss > 1) {
 			wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
 			hostapd_config_free(conf);
@@ -3429,6 +3440,8 @@
 		conf->bss[0] = NULL;
 		hostapd_config_free(conf);
 	} else {
+		hostapd_config_free(conf);
+
 		/* Add a new iface with the first BSS */
 		new_iface = iface = hostapd_init(interfaces, config_fname);
 		if (!iface)
@@ -3463,21 +3476,24 @@
 		return;
 
 #ifdef CONFIG_IEEE80211BE
-	/* In case of non-ML operation, de-init. But if ML operation exist,
-	 * even if that's the last BSS in the interface, the driver (drv) could
-	 * be in use for a different AP MLD. Hence, need to check if drv is
-	 * still being used by some other BSS before de-initiallizing. */
-	if (!iface->bss[0]->conf->mld_ap) {
-		driver->hapd_deinit(drv_priv);
-	} else if (driver->is_drv_shared &&
-		   !driver->is_drv_shared(drv_priv,
-					  iface->bss[0]->mld_link_id)) {
+	if (!driver->is_drv_shared ||
+	    !driver->is_drv_shared(drv_priv, iface->bss[0]->mld_link_id)) {
 		driver->hapd_deinit(drv_priv);
 		hostapd_mld_interface_freed(iface->bss[0]);
-	} else if (hostapd_if_link_remove(iface->bss[0],
-					  WPA_IF_AP_BSS,
-					  iface->bss[0]->conf->iface,
-					  iface->bss[0]->mld_link_id)) {
+		iface->bss[0]->drv_priv = NULL;
+		return;
+	}
+
+	if (iface->bss[0]->conf->mld_ap) {
+		if (hostapd_if_link_remove(iface->bss[0],
+					WPA_IF_AP_BSS,
+					iface->bss[0]->conf->iface,
+					iface->bss[0]->mld_link_id))
+			wpa_printf(MSG_WARNING,
+				   "Failed to remove link BSS interface %s",
+				   iface->bss[0]->conf->iface);
+	} else if (hostapd_if_remove(iface->bss[0], WPA_IF_AP_BSS,
+				     iface->bss[0]->conf->iface)) {
 		wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
 			   iface->bss[0]->conf->iface);
 	}
@@ -4089,6 +4105,22 @@
 
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 	ap_sta_clear_assoc_timeout(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+	if (ap_sta_is_mld(hapd, sta)) {
+		struct hostapd_data *bss;
+		struct sta_info *lsta;
+
+		for_each_mld_link(bss, hapd) {
+			if (bss == hapd)
+				continue;
+			lsta = ap_get_sta(bss, sta->addr);
+			if (lsta)
+				ap_sta_clear_assoc_timeout(bss, lsta);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	sta->post_csa_sa_query = 0;
 
 #ifdef CONFIG_P2P
@@ -4105,7 +4137,7 @@
 	/* Start accounting here, if IEEE 802.1X and WPA are not used.
 	 * 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) {
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
 		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
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 846535a..bb85de9 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -892,4 +892,11 @@
 
 u16 hostapd_get_punct_bitmap(struct hostapd_data *hapd);
 
+static inline bool ap_pmf_enabled(struct hostapd_bss_config *conf)
+{
+	return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 05e9b9d..4ae3b6b 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -44,113 +44,6 @@
 }
 
 
-u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
-{
-	u8 *len;
-	u16 capab;
-
-	if (!hapd->conf->osen)
-		return eid;
-
-	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
-	len = eid++; /* to be filled */
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = HS20_OSEN_OUI_TYPE;
-
-	/* Group Data Cipher Suite */
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
-	eid += RSN_SELECTOR_LEN;
-
-	/* Pairwise Cipher Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
-	eid += RSN_SELECTOR_LEN;
-
-	/* AKM Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
-	eid += RSN_SELECTOR_LEN;
-
-	/* RSN Capabilities */
-	capab = 0;
-	if (hapd->conf->wmm_enabled) {
-		/* 4 PTKSA replay counters when using WMM */
-		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
-	}
-	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		capab |= WPA_CAPABILITY_MFPC;
-		if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
-			capab |= WPA_CAPABILITY_MFPR;
-	}
-#ifdef CONFIG_OCV
-	if (hapd->conf->ocv &&
-	    (hapd->iface->drv_flags2 &
-	     (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
-		capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
-	WPA_PUT_LE16(eid, capab);
-	eid += 2;
-
-	*len = eid - len - 1;
-
-	return eid;
-}
-
-
-int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
-			       u8 osu_method, const char *url)
-{
-	struct wpabuf *buf;
-	size_t len = 0;
-	int ret;
-
-	/* TODO: should refuse to send notification if the STA is not associated
-	 * or if the STA did not indicate support for WNM-Notification */
-
-	if (url) {
-		len = 1 + os_strlen(url);
-		if (5 + len > 255) {
-			wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
-				   "WNM-Notification: '%s'", url);
-			return -1;
-		}
-	}
-
-	buf = wpabuf_alloc(4 + 7 + len);
-	if (buf == NULL)
-		return -1;
-
-	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
-	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
-	wpabuf_put_u8(buf, 1); /* Dialog token */
-	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
-
-	/* Subscription Remediation subelement */
-	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
-	wpabuf_put_u8(buf, 5 + len);
-	wpabuf_put_be24(buf, OUI_WFA);
-	wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
-	if (url) {
-		wpabuf_put_u8(buf, len - 1);
-		wpabuf_put_data(buf, url, len - 1);
-		wpabuf_put_u8(buf, osu_method);
-	} else {
-		/* Server URL and Server Method fields not included */
-		wpabuf_put_u8(buf, 0);
-	}
-
-	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
-				      wpabuf_head(buf), wpabuf_len(buf));
-
-	wpabuf_free(buf);
-
-	return ret;
-}
-
-
 int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
 					  const u8 *addr,
 					  const struct wpabuf *payload)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a9ed6eb..523e0a3 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -117,68 +117,61 @@
 }
 
 
-u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
 {
-	u8 *pos = eid;
-	int i, num, count;
-	int h2e_required;
+	u8 *pos = buf;
+	int i;
 
-	if (hapd->iface->current_rates == NULL)
-		return eid;
+	if (!hapd->iface->current_rates)
+		return 0;
 
-	*pos++ = WLAN_EID_SUPP_RATES;
-	num = hapd->iface->num_rates;
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
-		num++;
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
-		num++;
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
-		num++;
-#endif /* CONFIG_IEEE80211AX */
-	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
-		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
-	if (h2e_required)
-		num++;
-	if (num > 8) {
-		/* rest of the rates are encoded in Extended supported
-		 * rates element */
-		num = 8;
-	}
-
-	*pos++ = num;
-	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
-	     i++) {
-		count++;
+	for (i = 0; i < hapd->iface->num_rates; i++) {
 		*pos = hapd->iface->current_rates[i].rate / 5;
 		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
 			*pos |= 0x80;
 		pos++;
 	}
 
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
-	}
 
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
-	}
 
 #ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
-		count++;
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
-	}
 #endif /* CONFIG_IEEE80211AX */
 
-	if (h2e_required && count < 8) {
-		count++;
+#ifdef CONFIG_SAE
+	if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+	    hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+	    wpa_key_mgmt_only_sae(hapd->conf->wpa_key_mgmt))
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
-	}
+#endif /* CONFIG_SAE */
+
+	return pos - buf;
+}
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 buf[100];
+	size_t len;
+
+	len = hostapd_supp_rates(hapd, buf);
+	if (len == 0)
+		return eid;
+	/* Only up to first eight values in this element */
+	if (len > 8)
+		len = 8;
+
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = len;
+	os_memcpy(pos, buf, len);
+	pos += len;
 
 	return pos;
 }
@@ -187,72 +180,19 @@
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
 {
 	u8 *pos = eid;
-	int i, num, count;
-	int h2e_required;
+	u8 buf[100];
+	size_t len;
 
-	hapd->conf->xrates_supported = false;
-	if (hapd->iface->current_rates == NULL)
+	len = hostapd_supp_rates(hapd, buf);
+	/* Starting from the 9th value for this element */
+	if (len <= 8)
 		return eid;
 
-	num = hapd->iface->num_rates;
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
-		num++;
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
-		num++;
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
-		num++;
-#endif /* CONFIG_IEEE80211AX */
-	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
-			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
-		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
-	if (h2e_required)
-		num++;
-	if (num <= 8)
-		return eid;
-	num -= 8;
-
 	*pos++ = WLAN_EID_EXT_SUPP_RATES;
-	*pos++ = num;
-	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
-	     i++) {
-		count++;
-		if (count <= 8)
-			continue; /* already in SuppRates IE */
-		*pos = hapd->iface->current_rates[i].rate / 5;
-		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
-			*pos |= 0x80;
-		pos++;
-	}
+	*pos++ = len - 8;
+	os_memcpy(pos, &buf[8], len - 8);
+	pos += len - 8;
 
-	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
-	}
-
-	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
-	}
-
-#ifdef CONFIG_IEEE80211AX
-	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
-	}
-#endif /* CONFIG_IEEE80211AX */
-
-	if (h2e_required) {
-		count++;
-		if (count > 8)
-			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
-	}
-
-	hapd->conf->xrates_supported = true;
 	return pos;
 }
 
@@ -309,11 +249,6 @@
 	if (hapd->conf->wpa)
 		privacy = 1;
 
-#ifdef CONFIG_HS20
-	if (hapd->conf->osen)
-		privacy = 1;
-#endif /* CONFIG_HS20 */
-
 	if (privacy)
 		capab |= WLAN_CAPABILITY_PRIVACY;
 
@@ -548,6 +483,147 @@
 }
 
 
+static bool in_mac_addr_list(const u8 *list, unsigned int num, const u8 *addr)
+{
+	unsigned int i;
+
+	for (i = 0; list && i < num; i++) {
+		if (ether_addr_equal(&list[i * ETH_ALEN], addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static struct sae_password_entry *
+sae_password_find_pw(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sae_password_entry *pw = NULL;
+
+	if (!sta->sae || !sta->sae->tmp || !sta->sae->tmp->used_pw)
+		return NULL;
+
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw == sta->sae->tmp->used_pw)
+			return pw;
+	}
+
+	return NULL;
+}
+
+
+static bool is_other_sae_password(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  struct sae_password_entry *used_pw)
+{
+	struct sae_password_entry *pw;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw == used_pw ||
+		    pw->identifier ||
+		    !is_broadcast_ether_addr(pw->peer_addr))
+			continue;
+
+		if (in_mac_addr_list(pw->success_mac,
+				     pw->num_success_mac,
+				     sta->addr))
+			return true;
+
+		if (!in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+				      sta->addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static bool has_sae_success_seen(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (pw->identifier ||
+		    !is_broadcast_ether_addr(pw->peer_addr))
+			continue;
+
+		if (in_mac_addr_list(pw->success_mac,
+				     pw->num_success_mac,
+				     sta->addr))
+			return true;
+	}
+
+	return false;
+}
+
+
+static void sae_password_track_success(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	if (!hapd->conf->sae_track_password)
+		return;
+
+	pw = sae_password_find_pw(hapd, sta);
+	if (!pw)
+		return;
+
+	if (in_mac_addr_list(pw->success_mac,
+			     pw->num_success_mac,
+			     sta->addr))
+		return;
+
+	if (!pw->success_mac) {
+		pw->success_mac = os_zalloc(hapd->conf->sae_track_password *
+					    ETH_ALEN);
+		if (!pw->success_mac)
+			return;
+		pw->num_success_mac = hapd->conf->sae_track_password;
+	}
+
+	os_memcpy(&pw->success_mac[pw->next_success_mac * ETH_ALEN], sta->addr,
+		  ETH_ALEN);
+	pw->next_success_mac = (pw->next_success_mac + 1) % pw->num_success_mac;
+}
+
+
+static bool sae_password_track_fail(struct hostapd_data *hapd,
+				    struct sta_info *sta)
+{
+	struct sae_password_entry *pw;
+
+	if (!hapd->conf->sae_track_password)
+		return false;
+
+	pw = sae_password_find_pw(hapd, sta);
+	if (!pw)
+		return false;
+
+	if (in_mac_addr_list(pw->fail_mac,
+			     pw->num_fail_mac,
+			     sta->addr))
+		return is_other_sae_password(hapd, sta, pw);
+
+	if (!pw->fail_mac) {
+		pw->fail_mac = os_zalloc(hapd->conf->sae_track_password *
+					 ETH_ALEN);
+		if (!pw->fail_mac)
+			return false;
+		pw->num_fail_mac = hapd->conf->sae_track_password;
+	}
+
+	os_memcpy(&pw->fail_mac[pw->next_fail_mac * ETH_ALEN], sta->addr,
+		  ETH_ALEN);
+	pw->next_fail_mac = (pw->next_fail_mac + 1) % pw->num_fail_mac;
+
+	return is_other_sae_password(hapd, sta, pw);
+}
+
+
 const char * sae_get_password(struct hostapd_data *hapd,
 			      struct sta_info *sta,
 			      const char *rx_id,
@@ -561,6 +637,45 @@
 	const struct sae_pk *pk = NULL;
 	struct hostapd_sta_wpa_psk_short *psk = NULL;
 
+	/* With sae_track_password functionality enabled, try to first find the
+	 * next viable wildcard-address password if a password identifier was
+	 * not used. Select an wildcard-addr entry if the STA is known to have
+	 * used it successfully before. If no such entry exists, pick a
+	 * wildcard-addr entry that does not have a failed entry tracked for the
+	 * STA. */
+	if (!rx_id && sta && hapd->conf->sae_track_password) {
+		struct sae_password_entry *success = NULL, *no_fail = NULL;
+
+		for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+			if (pw->identifier ||
+			    !is_broadcast_ether_addr(pw->peer_addr))
+				continue;
+			if (in_mac_addr_list(pw->success_mac,
+					     pw->num_success_mac,
+					     sta->addr)) {
+				success = pw;
+				break;
+			}
+
+			if (!no_fail &&
+			    !in_mac_addr_list(pw->fail_mac, pw->num_fail_mac,
+					      sta->addr))
+				no_fail = pw;
+		}
+
+		pw = success ? success : no_fail;
+		if (pw) {
+			password = pw->password;
+			pt = pw->pt;
+			if (!(hapd->conf->mesh & MESH_ENABLED))
+				pk = pw->pk;
+			goto found;
+		}
+	}
+
+	/* If sae_track_password functionality is not enabled or no suitable
+	 * password entry was found with it, pick the first entry that matches
+	 * the STA MAC address and password identifier (if used). */
 	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
 		if (!is_broadcast_ether_addr(pw->peer_addr) &&
 		    (!sta ||
@@ -591,6 +706,7 @@
 		}
 	}
 
+found:
 	if (pw_entry)
 		*pw_entry = pw;
 	if (s_pt)
@@ -657,6 +773,9 @@
 		return NULL;
 	}
 
+	if (pw && sta->sae->tmp)
+		sta->sae->tmp->used_pw = pw;
+
 	if (pw && pw->vlan_id) {
 		if (!sta->sae->tmp) {
 			wpa_printf(MSG_INFO,
@@ -975,7 +1094,8 @@
 	sta->sae->peer_commit_scalar = NULL;
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
 			       sta->sae->pmk, sta->sae->pmk_len,
-			       sta->sae->pmkid, sta->sae->akmp);
+			       sta->sae->pmkid, sta->sae->akmp,
+			       ap_sta_is_mld(hapd, sta));
 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
 }
 
@@ -1005,6 +1125,7 @@
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
 			struct sae_temporary_data *tmp = sta->sae->tmp;
+			bool immediate_confirm;
 
 			if (tmp) {
 				sta->sae->h2e =
@@ -1046,8 +1167,22 @@
 			 * overridden with explicit configuration so that the
 			 * infrastructure BSS case sends both frames together.
 			 */
-			if ((hapd->conf->mesh & MESH_ENABLED) ||
-			    hapd->conf->sae_confirm_immediate) {
+			immediate_confirm = (hapd->conf->mesh & MESH_ENABLED) ||
+				hapd->conf->sae_confirm_immediate;
+
+			/* If sae_track_password is enabled and the STA has not
+			 * yet been tracked to having successfully completed
+			 * SAE authentication with the password that the AP
+			 * tries to use, do not send Confirm immediately to
+			 * avoid an explicit indication on the STA side on
+			 * password mismatch. */
+			if (immediate_confirm &&
+			    hapd->conf->sae_track_password &&
+			    (!sta->sae->tmp || !sta->sae->tmp->parsed_pw_id) &&
+			    !has_sae_success_seen(hapd, sta))
+				immediate_confirm = false;
+
+			if (immediate_confirm) {
 				/*
 				 * Send both Commit and Confirm immediately
 				 * based on SAE finite state machine
@@ -1495,7 +1630,9 @@
 			 * previously set parameters. */
 			pos = mgmt->u.auth.variable;
 			end = ((const u8 *) mgmt) + len;
-			if (end - pos >= (int) sizeof(le16) &&
+			if ((!sta->sae->tmp ||
+			     !sta->sae->tmp->try_other_password) &&
+			    end - pos >= (int) sizeof(le16) &&
 			    sae_group_allowed(sta->sae, groups,
 					      WPA_GET_LE16(pos)) ==
 			    WLAN_STATUS_SUCCESS) {
@@ -1621,9 +1758,21 @@
 
 			if (sae_check_confirm(sta->sae, var, var_len,
 					      NULL) < 0) {
+				if (sae_password_track_fail(hapd, sta)) {
+					wpa_printf(MSG_DEBUG,
+						   "SAE: Reject mismatching Confirm so that another password can be attempted by "
+						   MACSTR,
+						   MAC2STR(sta->addr));
+					if (sta->sae->tmp)
+						sta->sae->tmp->
+							try_other_password = 1;
+					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+					goto reply;
+				}
 				resp = WLAN_STATUS_CHALLENGE_FAIL;
 				goto reply;
 			}
+			sae_password_track_success(hapd, sta);
 			sta->sae->rc = peer_send_confirm;
 		}
 		resp = sae_sm_step(hapd, sta, auth_transaction,
@@ -1975,7 +2124,8 @@
 				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				  elems.rsnxe ? elems.rsnxe - 2 : NULL,
 				  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
-				  elems.mdie, elems.mdie_len, NULL, 0, NULL);
+				  elems.mdie, elems.mdie_len, NULL, 0, NULL,
+				  ap_sta_is_mld(hapd, sta));
 	resp = wpa_res_to_status_code(res);
 	if (resp != WLAN_STATUS_SUCCESS)
 		goto fail;
@@ -2252,7 +2402,7 @@
 				    sta->fils_erp_pmkid,
 				    session_timeout,
 				    wpa_auth_sta_key_mgmt(sta->wpa_sm),
-				    NULL) < 0) {
+				    NULL, ap_sta_is_mld(hapd, sta)) < 0) {
 				wpa_printf(MSG_ERROR,
 					   "FILS: Failed to add PMKSA cache entry based on ERP");
 			}
@@ -3829,7 +3979,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
 	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
 	wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
-			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
+			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE,
+			    NULL, ap_sta_is_mld(hapd, sta));
 
 	return WLAN_STATUS_SUCCESS;
 }
@@ -3909,7 +4060,8 @@
 	rsn_ie_len += 2;
 	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 				  hapd->iface->freq, rsn_ie, rsn_ie_len,
-				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
+				  NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL,
+				  ap_sta_is_mld(hapd, sta));
 	status = wpa_res_to_status_code(res);
 	if (status != WLAN_STATUS_SUCCESS)
 		goto end;
@@ -3961,8 +4113,58 @@
 #endif /* CONFIG_OWE */
 
 
+static bool hapd_is_known_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			      const u8 *ies, size_t ies_len)
+{
+	const u8 *ie, *pos, *end, *timestamp_pos, *mic;
+	u64 timestamp;
+	u8 mic_len;
+
+	if (!hapd->conf->known_sta_identification)
+		return false;
+
+	ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_KNOWN_STA_IDENTIFICATION);
+	if (!ie)
+		return false;
+
+	pos = ie + 3;
+	end = &ie[2 + ie[1]];
+	if (end - pos < 8 + 1)
+		return false; /* truncated element */
+	timestamp_pos = pos;
+	timestamp = WPA_GET_LE64(pos);
+	pos += 8;
+	mic_len = *pos++;
+	if (mic_len > end - pos)
+		return false; /* truncated element */
+	mic = pos;
+
+	wpa_printf(MSG_DEBUG, "RSN: STA " MACSTR
+		   " included Known STA Identification element: Timestamp=0x%llx mic_len=%u",
+		   MAC2STR(sta->addr), (unsigned long long) timestamp, mic_len);
+
+	if (timestamp <= sta->last_known_sta_id_timestamp) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Ignore reused or old Known STA Identification");
+		return false;
+	}
+
+	if (!wpa_auth_sm_known_sta_identification(sta->wpa_sm, timestamp_pos,
+						  mic, mic_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Ignore Known STA Identification with invalid MIC or due to KCK not available");
+		return false;
+	}
+
+	wpa_printf(MSG_DEBUG, "RSN: Valid Known STA Identification");
+	sta->last_known_sta_id_timestamp = timestamp;
+
+	return true;
+}
+
+
 static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
-			   int reassoc)
+			   int reassoc, const u8 *ies, size_t ies_len)
 {
 	if ((sta->flags &
 	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
@@ -3974,6 +4176,9 @@
 
 	if (!sta->sa_query_timed_out &&
 	    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+		if (hapd_is_known_sta(hapd, sta, ies, ies_len))
+			return false;
+
 		/*
 		 * STA has already been associated with MFP and SA Query timeout
 		 * has not been reached. Reject the association attempt
@@ -4212,7 +4417,8 @@
 					  0,
 					  elems->mdie, elems->mdie_len,
 					  elems->owe_dh, elems->owe_dh_len,
-					  assoc_sta ? assoc_sta->wpa_sm : NULL);
+					  assoc_sta ? assoc_sta->wpa_sm : NULL,
+					  ap_sta_is_mld(hapd, sta));
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -4222,6 +4428,11 @@
 		else
 			sta->flags &= ~WLAN_STA_MFP;
 
+		if (wpa_auth_uses_spp_amsdu(sta->wpa_sm))
+			sta->flags |= WLAN_STA_SPP_AMSDU;
+		else
+			sta->flags &= ~WLAN_STA_SPP_AMSDU;
+
 #ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			if (!reassoc) {
@@ -4340,34 +4551,12 @@
 			ieee802_11_rsnx_capab_len(
 				elems->rsnxe, elems->rsnxe_len,
 				WLAN_RSNX_CAPAB_SSID_PROTECTION));
-#ifdef CONFIG_HS20
-	} else if (hapd->conf->osen) {
-		if (!elems->osen) {
-			hostapd_logger(
-				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-				HOSTAPD_LEVEL_INFO,
-				"No HS 2.0 OSEN element in association request");
-			return WLAN_STATUS_INVALID_IE;
-		}
-
-		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
-		if (sta->wpa_sm == NULL)
-			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-							sta->addr, NULL);
-		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
-				   "state machine");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
-		}
-		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems->osen - 2, elems->osen_len + 2) < 0)
-			return WLAN_STATUS_INVALID_IE;
-#endif /* CONFIG_HS20 */
 	} else
 		wpa_auth_sta_no_wpa(sta->wpa_sm);
 
 #ifdef CONFIG_P2P
-	p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+	if (ies && ies_len)
+		p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_HS20
@@ -4627,6 +4816,7 @@
 
 	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+	ap_sta_set_mld(sta, true);
 
 	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
 	if (status != WLAN_STATUS_SUCCESS) {
@@ -4634,8 +4824,6 @@
 		goto out;
 	}
 
-	ap_sta_set_mld(sta, true);
-
 	os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
 	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
 		struct mld_link_info *li = &sta->mld_info.links[i];
@@ -5617,7 +5805,7 @@
 	}
 #endif /* CONFIG_MBO */
 
-	if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc)) {
+	if (hapd->conf->wpa && check_sa_query(hapd, sta, reassoc, pos, left)) {
 		resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 		goto fail;
 	}
@@ -6064,7 +6252,7 @@
 	const u8 *pos, *end;
 	u32 oui_type;
 
-	pos = &mgmt->u.action.category;
+	pos = (const u8 *) &mgmt->u.action;
 	end = ((const u8 *) mgmt) + len;
 
 	if (end - pos < 1 + 4)
@@ -6741,8 +6929,7 @@
 		new_assoc = 0;
 	sta->flags |= WLAN_STA_ASSOC;
 	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
-	     !hapd->conf->osen) ||
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
 	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
 	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
 	    sta->auth_alg == WLAN_AUTH_FILS_PK ||
@@ -7678,7 +7865,8 @@
 
 		/* If no TBTT was found, adjust the len and total_len since it
 		 * would have incremented before we checked all BSSs. */
-		if (!tbtt_count) {
+		if (!tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
+		    total_len >= RNR_TBTT_HEADER_LEN) {
 			len -= RNR_TBTT_HEADER_LEN;
 			total_len -= RNR_TBTT_HEADER_LEN;
 		}
@@ -7688,7 +7876,8 @@
 
 	/* This is possible when in the re-built case and no suitable TBTT was
 	 * found. Adjust the length accordingly. */
-	if (!tbtt_count && total_tbtt_count) {
+	if (!tbtt_count && total_tbtt_count && len >= RNR_TBTT_HEADER_LEN &&
+	    total_len >= RNR_TBTT_HEADER_LEN) {
 		len -= RNR_TBTT_HEADER_LEN;
 		total_len -= RNR_TBTT_HEADER_LEN;
 	}
@@ -8067,8 +8256,8 @@
 }
 
 
-u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
-				size_t *current_len)
+static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+				       size_t *current_len)
 {
 	struct hostapd_iface *iface;
 	size_t i;
@@ -8092,8 +8281,8 @@
 }
 
 
-u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
-			 u8 *eid, size_t *current_len)
+static u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+				u8 *eid, size_t *current_len)
 {
 #ifdef CONFIG_IEEE80211BE
 	struct hostapd_iface *iface;
@@ -8204,8 +8393,8 @@
 					  size_t known_bss_len)
 {
 	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
-	size_t len, i;
-	u8 ext_capa[20];
+	size_t len, i, tx_xrate_len;
+	u8 ext_capa[20], buf[100];
 
 	/* Element ID: 1 octet
 	 * Length: 1 octet
@@ -8218,10 +8407,12 @@
 	 */
 	len = 1;
 
+	tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
 	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
 		struct hostapd_data *bss = hapd->iface->bss[i];
 		const u8 *auth, *rsn = NULL, *rsnx = NULL;
-		size_t nontx_profile_len, auth_len;
+		size_t nontx_profile_len, auth_len, xrate_len;
 		u8 ie_count = 0;
 
 		if (!bss || !bss->conf || !bss->started ||
@@ -8259,12 +8450,15 @@
 			ie_count++;
 		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
 			ie_count++;
-		if (bss->conf->xrates_supported)
-			nontx_profile_len += 8;
-		else if (hapd->conf->xrates_supported)
+
+		xrate_len = hostapd_eid_ext_supp_rates(bss, buf) - buf;
+
+		if (xrate_len)
+			nontx_profile_len += xrate_len;
+		else if (tx_xrate_len)
 			ie_count++;
 		if (ie_count)
-			nontx_profile_len += 4 + ie_count;
+			nontx_profile_len += 4 + ie_count + 1;
 
 		if (len + nontx_profile_len > 255)
 			break;
@@ -8338,13 +8532,16 @@
 				    const u8 *known_bss, size_t known_bss_len)
 {
 	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
-	size_t i;
+	size_t i, tx_xrate_len;
 	u8 *eid_len_offset, *max_bssid_indicator_offset;
+	u8 buf[100];
 
 	*eid++ = WLAN_EID_MULTIPLE_BSSID;
 	eid_len_offset = eid++;
 	max_bssid_indicator_offset = eid++;
 
+	tx_xrate_len = hostapd_eid_ext_supp_rates(tx_bss, buf) - buf;
+
 	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
 		struct hostapd_data *bss = hapd->iface->bss[i];
 		struct hostapd_bss_config *conf;
@@ -8352,7 +8549,7 @@
 		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;
+		size_t auth_len = 0, xrate_len;
 		u16 capab_info;
 		u8 mbssindex = i;
 
@@ -8417,12 +8614,13 @@
 		}
 
 		eid += hostapd_mbssid_ext_capa(bss, tx_bss, eid);
+		xrate_len = hostapd_eid_ext_supp_rates(bss, eid) - eid;
+		eid += xrate_len;
 
 		/* List of Element ID values in increasing order */
 		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
 			non_inherit_ie[ie_count++] = WLAN_EID_RSN;
-		if (hapd->conf->xrates_supported &&
-		    !bss->conf->xrates_supported)
+		if (tx_xrate_len && !xrate_len)
 			non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
 		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
 			non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index e778041..0d0a009 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -1127,29 +1127,7 @@
 
 	/* Common Info Length and MLD MAC Address must always be present */
 	common_info_len = 1 + ETH_ALEN;
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
-		wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
-		wpa_printf(MSG_DEBUG,
-			   "MLD: BSS Parameters Change Count not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
-		wpa_printf(MSG_DEBUG,
-			   "MLD: Medium Synchronization Delay Information not expected");
-		goto out;
-	}
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
-		common_info_len += 2;
-
-	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
-		common_info_len += 2;
+	/* Ignore optional fields */
 
 	if (sizeof(*ml) + common_info_len > ml_len) {
 		wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
@@ -1159,7 +1137,7 @@
 	common_info = (struct eht_ml_basic_common_info *) ml->variable;
 
 	/* Common information length includes the length octet */
-	if (common_info->len != common_info_len) {
+	if (common_info->len < common_info_len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Invalid common info len=%u", common_info->len);
 		goto out;
@@ -1185,9 +1163,10 @@
 	size_t ml_len, common_info_len;
 	struct mld_link_info *link_info;
 	struct mld_info *info = &sta->mld_info;
-	const u8 *pos;
+	const u8 *pos, *end;
 	int ret = -1;
 	u16 ml_control;
+	const u8 *ml_end;
 
 	mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
 	if (!mlbuf)
@@ -1195,6 +1174,7 @@
 
 	ml = wpabuf_head(mlbuf);
 	ml_len = wpabuf_len(mlbuf);
+	ml_end = ((const u8 *) ml) + ml_len;
 
 	ml_control = le_to_host16(ml->ml_control);
 	if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
@@ -1236,6 +1216,12 @@
 		goto out;
 	}
 
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+		common_info_len += 2;
+	} else {
+		wpa_printf(MSG_DEBUG, "MLD: EXT ML capabilities not present");
+	}
+
 	wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%zu",
 		   common_info_len);
 
@@ -1247,7 +1233,7 @@
 	common_info = (const struct eht_ml_basic_common_info *) ml->variable;
 
 	/* Common information length includes the length octet */
-	if (common_info->len != common_info_len) {
+	if (common_info->len < common_info_len) {
 		wpa_printf(MSG_DEBUG,
 			   "MLD: Invalid common info len=%u (expected %zu)",
 			   common_info->len, common_info_len);
@@ -1255,6 +1241,7 @@
 	}
 
 	pos = common_info->variable;
+	end = ((const u8 *) common_info) + common_info->len;
 
 	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
 		info->common_info.eml_capa = WPA_GET_LE16(pos);
@@ -1266,6 +1253,10 @@
 	info->common_info.mld_capa = WPA_GET_LE16(pos);
 	pos += 2;
 
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EXT_MLD_CAP) {
+		pos += 2;
+	}
+
 	wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
 		   MAC2STR(info->common_info.mld_addr),
 		   info->common_info.eml_capa, info->common_info.mld_capa);
@@ -1283,21 +1274,22 @@
 
 	info->links[hapd->mld_link_id].valid = 1;
 
-	/* Parse the link info field */
-	ml_len -= sizeof(*ml) + common_info_len;
-
-	while (ml_len > 2) {
+	/* Parse the Link Info field that starts after the end of the variable
+	 * length Common Info field. */
+	pos = end;
+	while (ml_end - pos > 2) {
 		size_t sub_elem_len = *(pos + 1);
 		size_t sta_info_len;
 		u16 control;
+		const u8 *sub_elem_end;
 
 		wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
 			   sub_elem_len);
 
-		if (2 + sub_elem_len > ml_len) {
+		if (2 + sub_elem_len > (size_t) (ml_end - pos)) {
 			wpa_printf(MSG_DEBUG,
 				   "MLD: Invalid link info len: %zu %zu",
-				   2 + sub_elem_len, ml_len);
+				   2 + sub_elem_len, ml_end - pos);
 			goto out;
 		}
 
@@ -1306,7 +1298,6 @@
 				   "MLD: Skip vendor specific subelement");
 
 			pos += 2 + sub_elem_len;
-			ml_len -= 2 + sub_elem_len;
 			continue;
 		}
 
@@ -1315,16 +1306,15 @@
 				   "MLD: Skip unknown Multi-Link element subelement ID=%u",
 				   *pos);
 			pos += 2 + sub_elem_len;
-			ml_len -= 2 + sub_elem_len;
 			continue;
 		}
 
 		/* Skip the subelement ID and the length */
 		pos += 2;
-		ml_len -= 2;
+		sub_elem_end = pos + sub_elem_len;
 
 		/* Get the station control field */
-		if (sub_elem_len < 2) {
+		if (sub_elem_end - pos < 2) {
 			wpa_printf(MSG_DEBUG,
 				   "MLD: Too short Per-STA Profile subelement");
 			goto out;
@@ -1333,8 +1323,6 @@
 		link_info = &info->links[control &
 					 EHT_PER_STA_CTRL_LINK_ID_MSK];
 		pos += 2;
-		ml_len -= 2;
-		sub_elem_len -= 2;
 
 		if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
 			wpa_printf(MSG_DEBUG,
@@ -1367,15 +1355,19 @@
 
 		sta_info_len += link_info->nstr_bitmap_len;
 
-		if (sta_info_len > ml_len || sta_info_len != *pos ||
-		    sta_info_len > sub_elem_len) {
+		if (sta_info_len > (size_t) (sub_elem_end - pos) ||
+		    sta_info_len > *pos ||
+		    *pos > sub_elem_end - pos ||
+		    sta_info_len > (size_t) (sub_elem_end - pos)) {
 			wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
 			goto out;
 		}
 
+		sta_info_len = *pos;
+		end = pos + sta_info_len;
+
 		/* skip the length */
 		pos++;
-		ml_len--;
 
 		/* get the link address */
 		os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
@@ -1385,27 +1377,20 @@
 			   MAC2STR(link_info->peer_addr));
 
 		pos += ETH_ALEN;
-		ml_len -= ETH_ALEN;
 
 		/* Get the NSTR bitmap */
 		if (link_info->nstr_bitmap_len) {
 			os_memcpy(link_info->nstr_bitmap, pos,
 				  link_info->nstr_bitmap_len);
 			pos += link_info->nstr_bitmap_len;
-			ml_len -= link_info->nstr_bitmap_len;
 		}
 
-		sub_elem_len -= sta_info_len;
+		pos = end;
 
-		wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
-		if (sub_elem_len > ml_len)
-			goto out;
-
-		if (sub_elem_len > 2)
+		if (sub_elem_end - pos >= 2)
 			link_info->capability = WPA_GET_LE16(pos);
 
-		pos += sub_elem_len;
-		ml_len -= sub_elem_len;
+		pos = sub_elem_end;
 
 		wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
 			   ", nstr bitmap len=%zu",
@@ -1415,12 +1400,6 @@
 		link_info->valid = true;
 	}
 
-	if (ml_len) {
-		wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
-			   ml_len);
-		goto out;
-	}
-
 	ret = hostapd_mld_validate_assoc_info(hapd, sta);
 out:
 	wpabuf_free(mlbuf);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index cd9f8bc..cc731b9 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -229,6 +229,9 @@
 		u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
 
 		if (punct_bitmap) {
+			oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+			seg0 = hostapd_get_oper_centr_freq_seg0_idx(
+				hapd->iconf);
 			punct_update_legacy_bw(punct_bitmap,
 					       hapd->iconf->channel,
 					       &oper_chwidth, &seg0, &seg1);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 5e67216..986b7b8 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -479,6 +479,9 @@
 	case 12: /* Bits 96-103 */
 		if (hapd->iconf->peer_to_peer_twt)
 			*pos |= 0x10; /* Bit 100 - Peer to Peer TWT */
+		if (hapd->conf->known_sta_identification)
+			*pos |= 0x40; /* Bit 102 - Known STA Identification
+				       * Enabled */
 		break;
 	case 13: /* Bits 104-111 */
 		if (hapd->iconf->channel_usage)
@@ -1042,7 +1045,8 @@
 	int requested_bw;
 
 	if (sta->ht_capabilities)
-		ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+		ht_40mhz = !!(le_to_host16(sta->ht_capabilities->
+					   ht_capabilities_info) &
 			      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
 
 	if (sta->vht_operation) {
@@ -1078,9 +1082,9 @@
 		 * normal clients), use it to determine the supported channel
 		 * bandwidth.
 		 */
-		vht_chanwidth = capab->vht_capabilities_info &
+		vht_chanwidth = le_to_host32(capab->vht_capabilities_info) &
 			VHT_CAP_SUPP_CHAN_WIDTH_MASK;
-		vht_80p80 = capab->vht_capabilities_info &
+		vht_80p80 = le_to_host32(capab->vht_capabilities_info) &
 			VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 
 		/* TODO: Also take into account Extended NSS BW Support field */
@@ -1136,6 +1140,9 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 	if (hapd->conf->ssid_protection)
 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+	if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU) &&
+	    hapd->conf->spp_amsdu)
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
 
 	if (!capab)
 		return eid; /* no supported extended RSN capabilities */
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index e8d21ff..efdf607 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -45,7 +45,7 @@
 #endif /* CONFIG_HS20 */
 static bool ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
-				int remediation, bool logoff);
+				bool logoff);
 
 
 static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -451,8 +451,7 @@
 		return -1;
 	}
 
-	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
-				     hapd->conf->osen) ?
+	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2)) ?
 				    WPA_PROTO_RSN : WPA_PROTO_WPA,
 				    hapd->conf->wpa_group);
 	if (!hostapd_config_get_radius_attr(req_attr,
@@ -581,7 +580,7 @@
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
-	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+	if (hapd->conf->wpa && sta->wpa_sm &&
 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
 		return -1;
 
@@ -1123,7 +1122,7 @@
 	struct rsn_pmksa_cache_entry *pmksa;
 	int key_mgmt;
 
-	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
 	    !hapd->conf->wps_state)
 		return;
 
@@ -1183,7 +1182,7 @@
 		return;
 	}
 
-	if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
+	if (!hapd->conf->ieee802_1x &&
 	    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
 		wpa_printf(MSG_DEBUG,
 			   "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
@@ -1251,8 +1250,10 @@
 			       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)) ||
+		if (hapd->conf->wpa &&
+		    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm &&
+		    ((wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) &&
+		      (sta->flags & WLAN_STA_AUTHORIZED)) ||
 		     sta->auth_alg == WLAN_AUTH_FT)) {
 			/* When FT is used, reauthentication to generate a new
 			 * PMK-R0 would be complicated since the current AP
@@ -1356,7 +1357,7 @@
 	}
 #endif /* CONFIG_WPS */
 
-	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+	if (!force_1x && !hapd->conf->ieee802_1x) {
 		wpa_printf(MSG_DEBUG,
 			   "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
 		/*
@@ -1490,10 +1491,6 @@
 {
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
-#ifdef CONFIG_HS20
-	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
-#endif /* CONFIG_HS20 */
-
 	if (sta->pending_eapol_rx) {
 		wpabuf_free(sta->pending_eapol_rx->buf);
 		os_free(sta->pending_eapol_rx);
@@ -1769,32 +1766,6 @@
 
 #ifdef CONFIG_HS20
 
-static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
-{
-	sta->remediation = 1;
-	os_free(sta->remediation_url);
-	if (len > 2) {
-		sta->remediation_url = os_malloc(len);
-		if (!sta->remediation_url)
-			return;
-		sta->remediation_method = pos[0];
-		os_memcpy(sta->remediation_url, pos + 1, len - 1);
-		sta->remediation_url[len - 1] = '\0';
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Subscription remediation needed for "
-			   MACSTR " - server method %u URL %s",
-			   MAC2STR(sta->addr), sta->remediation_method,
-			   sta->remediation_url);
-	} else {
-		sta->remediation_url = NULL;
-		wpa_printf(MSG_DEBUG,
-			   "HS 2.0: Subscription remediation needed for "
-			   MACSTR, MAC2STR(sta->addr));
-	}
-	/* TODO: assign the STA into remediation VLAN or add filtering */
-}
-
-
 static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
 				       struct sta_info *sta, const u8 *pos,
 				       size_t len)
@@ -1910,7 +1881,6 @@
 	size_t len;
 
 	buf = NULL;
-	sta->remediation = 0;
 	sta->hs20_deauth_requested = 0;
 	sta->hs20_deauth_on_ack = 0;
 
@@ -1935,9 +1905,6 @@
 			continue; /* invalid WFA VSA */
 
 		switch (type) {
-		case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
-			ieee802_1x_hs20_sub_rem(sta, pos, sublen);
-			break;
 		case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
 			ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
 			break;
@@ -2366,7 +2333,7 @@
 
 
 static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
-				 int preauth, int remediation, bool logoff)
+				 int preauth, bool logoff)
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
@@ -2376,7 +2343,7 @@
 		return false;
 	}
 
-	return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
+	return ieee802_1x_finished(hapd, sta, success, logoff);
 }
 
 
@@ -2418,7 +2385,6 @@
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
-	user->remediation = eap_user->remediation;
 	rv = 0;
 
 out:
@@ -2576,8 +2542,6 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
-	dl_list_init(&hapd->erp_keys);
-
 	os_memset(&conf, 0, sizeof(conf));
 	conf.eap_cfg = hapd->eap_cfg;
 	conf.ctx = hapd;
@@ -3060,17 +3024,6 @@
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
-	if (sta->remediation) {
-		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
-			   MACSTR " to indicate Subscription Remediation",
-			   MAC2STR(sta->addr));
-		hs20_send_wnm_notification(hapd, sta->addr,
-					   sta->remediation_method,
-					   sta->remediation_url);
-		os_free(sta->remediation_url);
-		sta->remediation_url = NULL;
-	}
-
 	if (sta->hs20_deauth_req) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
 			   MACSTR " to indicate imminent deauthentication",
@@ -3093,7 +3046,7 @@
 
 static bool ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
-				int remediation, bool logoff)
+				bool logoff)
 {
 	const u8 *key;
 	size_t len;
@@ -3103,16 +3056,7 @@
 	struct os_reltime now, remaining;
 
 #ifdef CONFIG_HS20
-	if (remediation && !sta->remediation) {
-		sta->remediation = 1;
-		os_free(sta->remediation_url);
-		sta->remediation_url =
-			os_strdup(hapd->conf->subscr_remediation_url);
-		sta->remediation_method = 1; /* SOAP-XML SPP */
-	}
-
-	if (success && (sta->remediation || sta->hs20_deauth_req ||
-			sta->hs20_t_c_filtering)) {
+	if (success && (sta->hs20_deauth_req || sta->hs20_t_c_filtering)) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
 			   MACSTR " in 100 ms", MAC2STR(sta->addr));
 		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -3133,7 +3077,7 @@
 	} else {
 		session_timeout = dot11RSNAConfigPMKLifetime;
 	}
-	if (success && key && len >= PMK_LEN && !sta->remediation &&
+	if (success && key && len >= PMK_LEN &&
 	    !sta->hs20_deauth_requested &&
 	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
 			       sta->eapol_sm) == 0) {
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 2fce838..0715540 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -644,29 +644,25 @@
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
  * @buf: Buffer for the list
  * @len: Length of the buffer
+ * @index: Externally stored index counter
  * Returns: Number of bytes written to buffer
  *
  * This function is used to generate a text format representation of the
  * current PMKSA cache contents for the ctrl_iface PMKSA command.
  */
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+			  int *index)
 {
-	int i, ret;
+	int ret;
 	char *pos = buf;
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
 	os_get_reltime(&now);
-	ret = os_snprintf(pos, buf + len - pos,
-			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
-	if (os_snprintf_error(buf + len - pos, ret))
-		return pos - buf;
-	pos += ret;
-	i = 0;
 	entry = pmksa->pmksa;
 	while (entry) {
 		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
-				  i, MAC2STR(entry->spa));
+				  *index, MAC2STR(entry->spa));
 		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
@@ -679,6 +675,7 @@
 			return pos - buf;
 		pos += ret;
 		entry = entry->next;
+		(*index)++;
 	}
 	return pos - buf;
 }
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index e38e7ec..ade1c49 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -75,7 +75,8 @@
 			    struct rsn_pmksa_cache_entry *entry);
 int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
 					   struct radius_das_attrs *attr);
-int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len,
+			  int *index);
 void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
 int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
 			       char *buf, size_t len);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 9d49569..8aa96d2 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -419,7 +419,6 @@
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
-	os_free(sta->remediation_url);
 	os_free(sta->t_c_url);
 	wpabuf_free(sta->hs20_deauth_req);
 	os_free(sta->hs20_session_info_url);
@@ -947,7 +946,8 @@
 
 
 static void ap_sta_disconnect_common(struct hostapd_data *hapd,
-				     struct sta_info *sta, unsigned int timeout)
+				     struct sta_info *sta, unsigned int timeout,
+				     bool free_1x)
 {
 	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 
@@ -961,7 +961,8 @@
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
-	ieee802_1x_free_station(hapd, sta);
+	if (free_1x)
+		ieee802_1x_free_station(hapd, sta);
 #ifdef CONFIG_IEEE80211BE
 	if (!hapd->conf->mld_ap ||
 	    hapd->mld_link_id == sta->mld_assoc_link_id) {
@@ -1005,7 +1006,8 @@
 		sta->timeout_next = STA_DEAUTH;
 	}
 
-	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC,
+				 true);
 	ap_sta_disassociate_common(hapd, sta, reason);
 }
 
@@ -1043,7 +1045,8 @@
 	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_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+				 true);
 	ap_sta_deauthenticate_common(hapd, sta, reason);
 }
 
@@ -1060,7 +1063,8 @@
 	sta->timeout_next = STA_REMOVE;
 
 	sta->deauth_reason = reason;
-	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH,
+				 false);
 	ap_sta_deauthenticate_common(hapd, sta, reason);
 }
 
@@ -1767,7 +1771,7 @@
 
 	buf[0] = '\0';
 	res = os_snprintf(buf, buflen,
-			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1790,6 +1794,7 @@
 			  (flags & WLAN_STA_EHT ? "[EHT]" : ""),
 			  (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+			  (flags & WLAN_STA_SPP_AMSDU ? "[SPP-A-MSDU]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
 			   "[WNM_SLEEP_MODE]" : ""));
 	if (os_snprintf_error(buflen, res))
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d22e86d..1730742 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -22,6 +22,7 @@
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_SPP_AMSDU BIT(2)
 #define WLAN_STA_AUTHORIZED BIT(5)
 #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
 #define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -131,7 +132,6 @@
 	unsigned int ht_20mhz_set:1;
 	unsigned int no_p2p_set:1;
 	unsigned int qos_map_enabled:1;
-	unsigned int remediation:1;
 	unsigned int hs20_deauth_requested:1;
 	unsigned int hs20_deauth_on_ack:1;
 	unsigned int session_timeout_set:1;
@@ -217,8 +217,6 @@
 	struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
 	/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
 	struct wpabuf *roaming_consortium;
-	u8 remediation_method;
-	char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
 	char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
 	struct wpabuf *hs20_deauth_req;
 	char *hs20_session_info_url;
@@ -322,6 +320,8 @@
 
 	u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
 			      * units of 1000 TUs */
+
+	u64 last_known_sta_id_timestamp;
 };
 
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 5531aae..9295dc6 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -38,6 +38,7 @@
 #define STATE_MACHINE_DATA struct wpa_state_machine
 #define STATE_MACHINE_DEBUG_PREFIX "WPA"
 #define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
+#define KDE_ALL_LINKS 0xffff
 
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -738,6 +739,19 @@
 }
 
 
+static void wpa_deinit_groups(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group, *prev;
+
+	group = wpa_auth->group;
+	while (group) {
+		prev = group;
+		group = group->next;
+		bin_clear_free(prev, sizeof(*prev));
+	}
+}
+
+
 /**
  * wpa_init - Initialize WPA authenticator
  * @addr: Authenticator address
@@ -773,36 +787,48 @@
 
 	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
 		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 
 	wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
-	if (!wpa_auth->group) {
-		os_free(wpa_auth->wpa_ie);
-		os_free(wpa_auth);
-		return NULL;
-	}
+	if (!wpa_auth->group)
+		goto fail;
 
+	/* Per-link PMKSA cache */
 	wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
 						wpa_auth);
 	if (!wpa_auth->pmksa) {
 		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
-		os_free(wpa_auth->group);
-		os_free(wpa_auth->wpa_ie);
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	/* MLD-level PMKSA cache */
+	if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+		wpa_auth->ml_pmksa = pmksa_cache_auth_init(
+			wpa_auth_pmksa_free_cb, wpa_auth);
+		if (!wpa_auth->ml_pmksa) {
+			wpa_printf(MSG_ERROR,
+				   "MLD-level PMKSA cache initialization failed.");
+			goto fail;
+		}
+	} else if (wpa_auth->is_ml) {
+		struct wpa_authenticator *pa = wpa_get_primary_auth(wpa_auth);
+
+		if (!pa) {
+			wpa_printf(MSG_ERROR,
+				   "Could not find primary authenticator.");
+			goto fail;
+		}
+		wpa_auth->ml_pmksa = pa->ml_pmksa;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (!wpa_auth->ft_pmk_cache) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
-		os_free(wpa_auth->group);
-		os_free(wpa_auth->wpa_ie);
-		pmksa_cache_auth_deinit(wpa_auth->pmksa);
-		os_free(wpa_auth);
-		return NULL;
+		goto fail;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -845,6 +871,17 @@
 	}
 
 	return wpa_auth;
+
+fail:
+	wpa_deinit_groups(wpa_auth);
+	os_free(wpa_auth->wpa_ie);
+	pmksa_cache_auth_deinit(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->primary_auth)
+		pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+	os_free(wpa_auth);
+	return NULL;
 }
 
 
@@ -880,16 +917,35 @@
  */
 void wpa_deinit(struct wpa_authenticator *wpa_auth)
 {
-	struct wpa_group *group, *prev;
+#ifdef CONFIG_IEEE80211BE
+	struct wpa_authenticator *next_pa;
+#endif /* CONFIG_IEEE80211BE */
 
 	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
-
-	/* TODO: Assign ML primary authenticator to next link authenticator and
-	 * start rekey timer. */
 	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 
 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
 
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->is_ml && wpa_auth->primary_auth) {
+		next_pa = wpa_auth->cb->next_primary_auth(wpa_auth->cb_ctx);
+
+		if (!next_pa) {
+			/* Deinit PMKSA entry list if last link */
+			pmksa_cache_auth_deinit(wpa_auth->ml_pmksa);
+		} else {
+			/* Assign ML primary authenticator to the next link
+			 * authenticator and start rekey timer.
+			 */
+			next_pa->primary_auth = true;
+			if (next_pa->conf.wpa_group_rekey)
+				eloop_register_timeout(
+					next_pa->conf.wpa_group_rekey,
+					0, wpa_rekey_gtk, next_pa, NULL);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
@@ -900,16 +956,8 @@
 	bitfield_free(wpa_auth->ip_pool);
 #endif /* CONFIG_P2P */
 
-
 	os_free(wpa_auth->wpa_ie);
-
-	group = wpa_auth->group;
-	while (group) {
-		prev = group;
-		group = group->next;
-		bin_clear_free(prev, sizeof(*prev));
-	}
-
+	wpa_deinit_groups(wpa_auth);
 	wpa_auth_free_conf(&wpa_auth->conf);
 	os_free(wpa_auth);
 }
@@ -2087,8 +2135,9 @@
 		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (conf->eapol_key_reserved_random)
-		random_get_bytes(key->key_id, sizeof(key->key_id));
+	if (conf->eapol_key_reserved_random &&
+	    random_get_bytes(key->key_id, sizeof(key->key_id)) < 0)
+		os_memset(key->key_id, 0x11, sizeof(key->key_id));
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (kde && !encr) {
@@ -2792,8 +2841,7 @@
 	if (sm->wpa == WPA_VERSION_WPA2 &&
 	    (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
 	     (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
-	     wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
-	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+	     wpa_key_mgmt_sae(sm->wpa_key_mgmt))) {
 		pmkid = buf;
 		kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
 		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -3440,7 +3488,7 @@
 	/* GTK KDE */
 	gtk = gsm->GTK[gsm->GN - 1];
 	gtk_len = gsm->GTK_len;
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -3861,9 +3909,6 @@
 	if (kde.rsn_ie) {
 		eapol_key_ie = kde.rsn_ie;
 		eapol_key_ie_len = kde.rsn_ie_len;
-	} else if (kde.osen) {
-		eapol_key_ie = kde.osen;
-		eapol_key_ie_len = kde.osen_len;
 	} else {
 		eapol_key_ie = kde.wpa_ie;
 		eapol_key_ie_len = kde.wpa_ie_len;
@@ -4117,7 +4162,7 @@
 	else
 		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
 	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
@@ -4148,14 +4193,6 @@
 	else
 		os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
 	os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each OSEN STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(bigtk.bigtk, len) < 0)
-			return pos;
-	}
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
 			  (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
 			  NULL, 0);
@@ -4301,7 +4338,8 @@
 }
 
 
-static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm,
+					 u16 req_links)
 {
 	struct wpa_authenticator *wpa_auth;
 	size_t kde_len = 0;
@@ -4314,6 +4352,9 @@
 		if (!sm->mld_links[link_id].valid)
 			continue;
 
+		if (!(req_links & BIT(link_id)))
+			continue;
+
 		wpa_auth = sm->mld_links[link_id].wpa_auth;
 		if (!wpa_auth || !wpa_auth->group)
 			continue;
@@ -4349,7 +4390,8 @@
 }
 
 
-static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos,
+				   u16 req_links)
 {
 	struct wpa_auth_ml_key_info ml_key_info;
 	unsigned int i, link_id;
@@ -4358,7 +4400,6 @@
 
 	/* First fetch the key information from all the authenticators */
 	os_memset(&ml_key_info, 0, sizeof(ml_key_info));
-	ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
 
 	/*
 	 * Assume that management frame protection and beacon protection are the
@@ -4371,13 +4412,19 @@
 		if (!sm->mld_links[link_id].valid)
 			continue;
 
+		if (!(req_links & BIT(link_id)))
+			continue;
+
 		ml_key_info.links[i++].link_id = link_id;
 	}
+	ml_key_info.n_mld_links = i;
 
 	wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info, rekey);
 
 	/* Add MLO GTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].gtk_len)
 			continue;
@@ -4402,8 +4449,6 @@
 		os_memcpy(pos, ml_key_info.links[i].gtk,
 			  ml_key_info.links[i].gtk_len);
 		pos += ml_key_info.links[i].gtk_len;
-
-		i++;
 	}
 
 	if (!sm->mgmt_frame_prot) {
@@ -4413,7 +4458,9 @@
 	}
 
 	/* Add MLO IGTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].igtk_len)
 			continue;
@@ -4445,8 +4492,6 @@
 		os_memcpy(pos, ml_key_info.links[i].igtk,
 			  ml_key_info.links[i].igtk_len);
 		pos += ml_key_info.links[i].igtk_len;
-
-		i++;
 	}
 
 	if (!sm->wpa_auth->conf.beacon_prot) {
@@ -4456,7 +4501,9 @@
 	}
 
 	/* Add MLO BIGTK KDEs */
-	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
 		if (!sm->mld_links[link_id].valid ||
 		    !ml_key_info.links[i].bigtk ||
 		    !ml_key_info.links[i].igtk_len)
@@ -4489,8 +4536,6 @@
 		os_memcpy(pos, ml_key_info.links[i].bigtk,
 			  ml_key_info.links[i].igtk_len);
 		pos += ml_key_info.links[i].igtk_len;
-
-		i++;
 	}
 
 	wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start);
@@ -4557,7 +4602,7 @@
 			kde_len += 2 + ie[1];
 	}
 
-	kde_len += wpa_auth_ml_group_kdes_len(sm);
+	kde_len += wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
 #endif /* CONFIG_IEEE80211BE */
 
 	return kde_len;
@@ -4685,7 +4730,7 @@
 	wpa_printf(MSG_DEBUG,
 		   "RSN: MLO Link KDEs and RSN Override Link KDEs len = %ld",
 		   pos - start);
-	pos = wpa_auth_ml_group_kdes(sm, pos);
+	pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
 #endif /* CONFIG_IEEE80211BE */
 
 	return pos;
@@ -4808,6 +4853,20 @@
 			return;
 		}
 
+		if (!sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
+		    wpa_auth_set_key(sm->wpa_auth, 0,
+				     wpa_cipher_to_alg(sm->pairwise),
+				     sm->addr, 0, sm->PTK.tk,
+				     wpa_cipher_key_len(sm->pairwise),
+				     KEY_FLAG_PAIRWISE_NEXT)) {
+			/* Continue anyway since the many drivers do not support
+			 * configuration of the TK for RX-only purposes for
+			 * cases where multiple keys might be in use in parallel
+			 * and this being an optional optimization to avoid race
+			 * condition during TK changes that could result in some
+			 * protected frames getting discarded. */
+		}
+
 #ifdef CONFIG_PASN
 		if (sm->wpa_auth->conf.secure_ltf &&
 		    ieee802_11_rsnx_capab(sm->rsnxe,
@@ -4827,8 +4886,7 @@
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
 		gtk_len = gsm->GTK_len;
-		if (conf->disable_gtk ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		if (conf->disable_gtk) {
 			/*
 			 * Provide unique random GTK to each STA to prevent use
 			 * of GTK in the BSS.
@@ -5383,7 +5441,7 @@
 			"sending 1/2 msg of Group Key Handshake");
 
 	gtk = gsm->GTK[gsm->GN - 1];
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -5414,14 +5472,14 @@
 		kde_len = pos - kde;
 #ifdef CONFIG_IEEE80211BE
 	} else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
-		kde_len = wpa_auth_ml_group_kdes_len(sm);
+		kde_len = wpa_auth_ml_group_kdes_len(sm, KDE_ALL_LINKS);
 		if (kde_len) {
 			kde_buf = os_malloc(kde_len);
 			if (!kde_buf)
 				return;
 
 			kde = pos = kde_buf;
-			pos = wpa_auth_ml_group_kdes(sm, pos);
+			pos = wpa_auth_ml_group_kdes(sm, pos, KDE_ALL_LINKS);
 			kde_len = pos - kde_buf;
 		}
 #endif /* CONFIG_IEEE80211BE */
@@ -5596,7 +5654,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "GTK",
 			group->GTK[group->GN - 1], group->GTK_len);
 
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+	if (wpa_auth_pmf_enabled(conf)) {
 		len = wpa_cipher_key_len(conf->group_mgmt_cipher);
 		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
 		inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -5609,7 +5667,7 @@
 	}
 
 	if (!wpa_auth->non_tx_beacon_prot &&
-	    conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+	     !wpa_auth_pmf_enabled(conf))
 		return ret;
 	if (!conf->beacon_prot)
 		return ret;
@@ -5764,7 +5822,7 @@
 		return 0;
 	pos += 8;
 	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -5803,7 +5861,7 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use
 		 * of IGTK in the BSS.
@@ -5842,14 +5900,6 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(pos, len) < 0)
-			return 0;
-	}
 	pos += len;
 
 	wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
@@ -5930,7 +5980,7 @@
 			     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 		ret = -1;
 
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+	if (wpa_auth_pmf_enabled(conf)) {
 		enum wpa_alg alg;
 		size_t len;
 
@@ -6486,16 +6536,27 @@
 
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			   int akmp)
+			   int akmp, bool is_ml)
 {
+	struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+	const u8 *aa = wpa_auth->addr;
+
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
 	if (!akmp)
 		akmp = WPA_KEY_MGMT_SAE;
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
-				 NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
+
+#ifdef CONFIG_IEEE80211BE
+	if (is_ml) {
+		pmksa = wpa_auth->ml_pmksa;
+		aa = wpa_auth->mld_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa, addr,
+				 0, NULL, akmp))
 		return 0;
 
 	return -1;
@@ -6511,17 +6572,27 @@
 
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			int session_timeout, int akmp, const u8 *dpp_pkhash)
+			int session_timeout, int akmp, const u8 *dpp_pkhash,
+			bool is_ml)
 {
+	struct rsn_pmksa_cache *pmksa;
+	const u8 *aa;
 	struct rsn_pmksa_cache_entry *entry;
 
 	if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
-	entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
-				 NULL, 0, wpa_auth->addr, addr, session_timeout,
-				 NULL, akmp);
+	pmksa = wpa_auth->pmksa;
+	aa = wpa_auth->addr;
+#ifdef CONFIG_IEEE80211BE
+	if (is_ml) {
+		pmksa = wpa_auth->ml_pmksa;
+		aa = wpa_auth->mld_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+	entry = pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, aa,
+				     addr, session_timeout, NULL, akmp);
 	if (!entry)
 		return -1;
 
@@ -6539,28 +6610,66 @@
 
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return;
+
 	pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
 	if (pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
 			   MACSTR " based on request", MAC2STR(sta_addr));
 		pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
 	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->ml_pmksa) {
+		pmksa = pmksa_cache_auth_get(wpa_auth->ml_pmksa,
+					     sta_addr, NULL);
+		if (pmksa) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Remove PMKSA cache entry for " MACSTR
+				   " based on request (MLD)",
+				   MAC2STR(sta_addr));
+			pmksa_cache_free_entry(wpa_auth->ml_pmksa, pmksa);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
 }
 
 
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
 			size_t len)
 {
+	int ret, index;
+	char *pos = buf, *end = buf + len;
+
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return 0;
-	return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+
+	ret = os_snprintf(pos, len,
+			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	index = 0;
+	pos += pmksa_cache_auth_list(wpa_auth->pmksa, pos, end - pos, &index);
+#ifdef CONFIG_IEEE80211BE
+	if (wpa_auth->ml_pmksa)
+		pos += pmksa_cache_auth_list(wpa_auth->ml_pmksa,
+					     pos, end - pos, &index);
+#endif /* CONFIG_IEEE80211BE */
+
+	return pos - buf;
 }
 
 
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
 {
-	if (wpa_auth && wpa_auth->pmksa)
+	if (wpa_auth && wpa_auth->pmksa) {
 		pmksa_cache_auth_flush(wpa_auth->pmksa);
+#ifdef CONFIG_IEEE80211BE
+		if (wpa_auth->ml_pmksa && wpa_auth->primary_auth)
+			pmksa_cache_auth_flush(wpa_auth->ml_pmksa);
+#endif /* CONFIG_IEEE80211BE */
+	}
 }
 
 
@@ -7525,3 +7634,49 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 }
+
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+					  const u8 *timestamp,
+					  const u8 *mic, size_t mic_len)
+{
+	size_t exp_mic_len;
+	u8 exp_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	int ver;
+
+	if (!sm)
+		return false;
+
+	if (!sm->PTK_valid || !mic_len || sm->PTK.kck_len == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: No KCK to verify Known STA Identification");
+		return false;
+	}
+
+	exp_mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+	if (mic_len != exp_mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: MIC length mismatch in Known STA Identification (received %zu, expected %zu)",
+			   mic_len, exp_mic_len);
+		return false;
+	}
+
+	if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+		ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+	else if (wpa_use_cmac(sm->wpa_key_mgmt))
+		ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+	else if (sm->pairwise != WPA_CIPHER_TKIP)
+		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, sm->wpa_key_mgmt,
+			      ver, timestamp, 8, exp_mic) ||
+	    os_memcmp_const(mic, exp_mic, exp_mic_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Invalid MIC in Known STA Identification");
+		return false;
+	}
+
+	return true;
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index d4ef49c..45c8dd6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -321,6 +321,8 @@
 	bool ssid_protection;
 
 	int rsn_override_omit_rsnxe;
+
+	bool spp_amsdu;
 };
 
 typedef enum {
@@ -428,6 +430,7 @@
 #ifdef CONFIG_IEEE80211BE
 	int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info,
 			       bool rekey);
+	struct wpa_authenticator * (*next_primary_auth)(void *ctx);
 #endif /* CONFIG_IEEE80211BE */
 	int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
 };
@@ -456,11 +459,13 @@
 		    const u8 *rsnxe, size_t rsnxe_len,
 		    const u8 *mdie, size_t mdie_len,
 		    const u8 *owe_dh, size_t owe_dh_len,
-		    struct wpa_state_machine *assoc_sm);
+		    struct wpa_state_machine *assoc_sm,
+		    bool is_ml);
 int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      struct wpa_state_machine *sm,
 		      const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm);
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
 int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
 struct wpa_state_machine *
@@ -508,11 +513,12 @@
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			   int akmp);
+			   int akmp, bool is_ml);
 void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
-			int session_timeout, int akmp, const u8 *dpp_pkhash);
+			int session_timeout, int akmp, const u8 *dpp_pkhash,
+			bool is_ml);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -692,4 +698,15 @@
 		    sm->mld_links[link_id].wpa_auth &&			\
 		    sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
 
+static inline bool wpa_auth_pmf_enabled(struct wpa_auth_config *conf)
+{
+	return conf->ieee80211w != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp != NO_MGMT_FRAME_PROTECTION ||
+		conf->rsn_override_mfp_2 != NO_MGMT_FRAME_PROTECTION;
+}
+
+bool wpa_auth_sm_known_sta_identification(struct wpa_state_machine *sm,
+					  const u8 *timestamp,
+					  const u8 *mic, size_t mic_len);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index de16c31..d5400a9 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2247,8 +2247,7 @@
 		pad_len += 8;
 	if (pad_len && key_len < sizeof(keybuf)) {
 		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
-		if (conf->disable_gtk ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		if (conf->disable_gtk) {
 			/*
 			 * Provide unique random GTK to each STA to prevent use
 			 * of GTK in the BSS.
@@ -2260,7 +2259,7 @@
 		keybuf[key_len] = 0xdd;
 		key_len += pad_len;
 		key = keybuf;
-	} else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	} else if (conf->disable_gtk) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use of GTK
 		 * in the BSS.
@@ -2339,7 +2338,7 @@
 	pos += 6;
 	*pos++ = igtk_len;
 	igtk = gsm->IGTK[gsm->GN_igtk - 4];
-	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+	if (conf->disable_gtk) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
@@ -2372,7 +2371,6 @@
 	const u8 *kek, *bigtk;
 	size_t kek_len;
 	size_t bigtk_len;
-	u8 stub_bigtk[WPA_IGTK_MAX_LEN];
 
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
 		kek = sm->PTK.kek2;
@@ -2400,17 +2398,6 @@
 	pos += 6;
 	*pos++ = bigtk_len;
 	bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
-		/*
-		 * Provide unique random BIGTK to each OSEN STA to prevent use
-		 * of BIGTK in the BSS.
-		 */
-		if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
-			os_free(subelem);
-			return NULL;
-		}
-		bigtk = stub_bigtk;
-	}
 	if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
 		wpa_printf(MSG_DEBUG,
 			   "FT: BIGTK subelem encryption failed: kek_len=%d",
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 2323a59..94cec78 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -35,7 +35,8 @@
 #include "wpa_auth_glue.h"
 
 
-static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+static void hostapd_wpa_auth_conf(struct hostapd_iface *iface,
+				  struct hostapd_bss_config *conf,
 				  struct hostapd_config *iconf,
 				  struct wpa_auth_config *wconf)
 {
@@ -109,17 +110,6 @@
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
-	if (conf->osen) {
-		wconf->disable_gtk = 1;
-		wconf->wpa = WPA_PROTO_OSEN;
-		wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-		wconf->wpa_pairwise = 0;
-		wconf->wpa_group = WPA_CIPHER_CCMP;
-		wconf->rsn_pairwise = WPA_CIPHER_CCMP;
-		wconf->rsn_preauth = 0;
-		wconf->disable_pmksa_caching = 1;
-		wconf->ieee80211w = 1;
-	}
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
@@ -280,6 +270,8 @@
 		conf->no_disconnect_on_group_keyerror;
 
 	wconf->rsn_override_omit_rsnxe = conf->rsn_override_omit_rsnxe;
+	wconf->spp_amsdu = conf->spp_amsdu &&
+		(iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU);
 }
 
 
@@ -512,6 +504,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	const char *ifname = hapd->conf->iface;
+	int set_tx = !(key_flag & KEY_FLAG_NEXT);
 
 	if (vlan_id > 0) {
 		ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
@@ -564,8 +557,8 @@
 		hapd->last_gtk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1,
-				   NULL, 0, key, key_len, key_flag);
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id,
+				   set_tx, NULL, 0, key, key_len, key_flag);
 }
 
 
@@ -1641,6 +1634,21 @@
 	return 0;
 }
 
+
+static struct wpa_authenticator * hostapd_next_primary_auth(void *cb_ctx)
+{
+	struct hostapd_data *hapd = cb_ctx, *bss;
+
+	for_each_mld_link(bss, hapd) {
+		if (bss == hapd)
+			continue;
+		if (bss->wpa_auth)
+			return bss->wpa_auth;
+	}
+
+	return NULL;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -1710,6 +1718,7 @@
 #endif /* CONFIG_PASN */
 #ifdef CONFIG_IEEE80211BE
 		.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+		.next_primary_auth = hostapd_next_primary_auth,
 #endif /* CONFIG_IEEE80211BE */
 		.get_drv_flags = hostapd_wpa_auth_get_drv_flags,
 	};
@@ -1717,7 +1726,7 @@
 	size_t wpa_ie_len;
 	struct hostapd_data *tx_bss;
 
-	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+	hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf, &_conf);
 	_conf.msg_ctx = hapd->msg_ctx;
 	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
 	if (tx_bss != hapd)
@@ -1843,7 +1852,9 @@
 void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config wpa_auth_conf;
-	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+
+	hostapd_wpa_auth_conf(hapd->iface, hapd->conf, hapd->iconf,
+			      &wpa_auth_conf);
 	wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
 }
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index cb902e4..0aa25b9 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -97,6 +97,7 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
 	unsigned int pmkid_set:1;
+	unsigned int spp_amsdu:1;
 
 	unsigned int ptkstart_without_success;
 
@@ -266,6 +267,8 @@
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_IEEE80211BE
+	/* MLD-level PMKSA cache for non-AP MLD entries only. */
+	struct rsn_pmksa_cache *ml_pmksa;
 	bool is_ml;
 	u8 mld_addr[ETH_ALEN];
 	u8 link_id;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index ce7f90a..d56eeaa 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -296,13 +296,6 @@
 		num_suites++;
 	}
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-	if (key_mgmt & WPA_KEY_MGMT_OSEN) {
-		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
-		pos += RSN_SELECTOR_LEN;
-		num_suites++;
-	}
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_PASN
 	if (key_mgmt & WPA_KEY_MGMT_PASN) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
@@ -507,6 +500,8 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 	if (conf->ssid_protection)
 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+	if (conf->spp_amsdu)
+		capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
 
 	return capab;
 }
@@ -579,57 +574,6 @@
 }
 
 
-static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
-{
-	u8 *len;
-	u16 capab;
-
-	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
-	len = eid++; /* to be filled */
-	WPA_PUT_BE24(eid, OUI_WFA);
-	eid += 3;
-	*eid++ = HS20_OSEN_OUI_TYPE;
-
-	/* Group Data Cipher Suite */
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
-	eid += RSN_SELECTOR_LEN;
-
-	/* Pairwise Cipher Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
-	eid += RSN_SELECTOR_LEN;
-
-	/* AKM Suite Count and List */
-	WPA_PUT_LE16(eid, 1);
-	eid += 2;
-	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
-	eid += RSN_SELECTOR_LEN;
-
-	/* RSN Capabilities */
-	capab = 0;
-	if (conf->wmm_enabled) {
-		/* 4 PTKSA replay counters when using WMM */
-		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
-	}
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		capab |= WPA_CAPABILITY_MFPC;
-		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
-			capab |= WPA_CAPABILITY_MFPR;
-	}
-#ifdef CONFIG_OCV
-	if (conf->ocv)
-		capab |= WPA_CAPABILITY_OCVC;
-#endif /* CONFIG_OCV */
-	WPA_PUT_LE16(eid, capab);
-	eid += 2;
-
-	*len = eid - len - 1;
-
-	return eid;
-}
-
-
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
 	u8 *pos, buf[1500];
@@ -654,9 +598,6 @@
 
 	pos = buf;
 
-	if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
-		pos = wpa_write_osen(&wpa_auth->conf, pos);
-	}
 	if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
 #ifdef CONFIG_TESTING_OPTIONS
 		if (wpa_auth->conf.rsne_override_set) {
@@ -850,6 +791,32 @@
 }
 
 
+#ifdef CONFIG_IEEE80211BE
+
+struct wpa_auth_link_iter_data {
+	struct wpa_authenticator *wpa_auth;
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+static int wpa_auth_pmksa_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_link_iter_data *data = ctx;
+
+	if (a == data->wpa_auth ||
+	    !ether_addr_equal(a->mld_addr, data->wpa_auth->mld_addr))
+		return 0;
+
+	data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+	if (data->pmksa)
+		return 1;
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
 enum wpa_validate_result
 wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 		    struct wpa_state_machine *sm, int freq,
@@ -857,7 +824,7 @@
 		    const u8 *rsnxe, size_t rsnxe_len,
 		    const u8 *mdie, size_t mdie_len,
 		    const u8 *owe_dh, size_t owe_dh_len,
-		    struct wpa_state_machine *assoc_sm)
+		    struct wpa_state_machine *assoc_sm, bool is_ml)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 	struct wpa_ie_data data;
@@ -958,10 +925,6 @@
 		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
 			selector = RSN_AUTH_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-		else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
-			selector = RSN_AUTH_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 #ifdef CONFIG_SHA384
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA384;
@@ -1082,10 +1045,6 @@
 	else if (key_mgmt & WPA_KEY_MGMT_DPP)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
-#ifdef CONFIG_HS20
-	else if (key_mgmt & WPA_KEY_MGMT_OSEN)
-		sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-#endif /* CONFIG_HS20 */
 	else
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
@@ -1156,7 +1115,7 @@
 	}
 #endif /* CONFIG_OCV */
 
-	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+	if (!wpa_auth_pmf_enabled(conf) ||
 	    !(data.capabilities & WPA_CAPABILITY_MFPC))
 		sm->mgmt_frame_prot = 0;
 	else
@@ -1169,6 +1128,14 @@
 		    return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 	}
 
+	if (wpa_auth->conf.spp_amsdu &&
+	    ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SPP_A_MSDU) &&
+	    (ciphers & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+			WPA_CIPHER_GCMP_256 | WPA_CIPHER_GCMP)))
+		sm->spp_amsdu = 1;
+	else
+		sm->spp_amsdu = 0;
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
@@ -1243,10 +1210,35 @@
 
 	sm->pmksa = NULL;
 	for (i = 0; i < data.num_pmkid; i++) {
+		struct rsn_pmksa_cache *pmksa = wpa_auth->pmksa;
+
 		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
 			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
-		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+#ifdef CONFIG_IEEE80211BE
+		if (is_ml)
+			pmksa = wpa_auth->ml_pmksa;
+#endif /* CONFIG_IEEE80211BE */
+		sm->pmksa = pmksa_cache_auth_get(pmksa, sm->addr,
 						 &data.pmkid[i * PMKID_LEN]);
+#ifdef CONFIG_IEEE80211BE
+		if (!sm->pmksa && !is_ml && wpa_auth->is_ml)
+			sm->pmksa = pmksa_cache_auth_get(
+				wpa_auth->ml_pmksa, sm->addr,
+				&data.pmkid[i * PMKID_LEN]);
+		if (!sm->pmksa && is_ml) {
+			struct wpa_auth_link_iter_data idata;
+
+			idata.wpa_auth = wpa_auth;
+			idata.pmksa = NULL;
+			idata.spa = sm->addr;
+			idata.pmkid = &data.pmkid[i * PMKID_LEN];
+			wpa_auth_for_each_auth(wpa_auth,
+					       wpa_auth_pmksa_iter,
+					       &idata);
+			if (idata.pmksa)
+				sm->pmksa = idata.pmksa;
+		}
+#endif /* CONFIG_IEEE80211BE */
 		if (!sm->pmksa && !is_zero_ether_addr(sm->p2p_dev_addr))
 			sm->pmksa = pmksa_cache_auth_get(
 				wpa_auth->pmksa, sm->p2p_dev_addr,
@@ -1376,42 +1368,17 @@
 }
 
 
-#ifdef CONFIG_HS20
-int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
-		      struct wpa_state_machine *sm,
-		      const u8 *osen_ie, size_t osen_ie_len)
-{
-	if (wpa_auth == NULL || sm == NULL)
-		return -1;
-
-	/* TODO: parse OSEN element */
-	sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
-	sm->mgmt_frame_prot = 1;
-	sm->pairwise = WPA_CIPHER_CCMP;
-	sm->wpa = WPA_VERSION_WPA2;
-
-	if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
-		os_free(sm->wpa_ie);
-		sm->wpa_ie = os_malloc(osen_ie_len);
-		if (sm->wpa_ie == NULL)
-			return -1;
-	}
-
-	os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
-	sm->wpa_ie_len = osen_ie_len;
-
-	return 0;
-}
-
-#endif /* CONFIG_HS20 */
-
-
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
 {
 	return sm ? sm->mgmt_frame_prot : 0;
 }
 
 
+int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm)
+{
+	return sm ? sm->spp_amsdu : 0;
+}
+
 #ifdef CONFIG_OCV
 
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index 625f405..20a5aaa 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -331,6 +331,7 @@
 				  hapd->conf->macsec_port,
 				  hapd->conf->mka_priority,
 				  hapd->conf->macsec_csindex,
+				  hapd->conf->macsec_icv_indicator,
 				  hapd->conf->iface,
 				  hapd->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
@@ -477,7 +478,7 @@
 	cak->len = hapd->conf->mka_cak_len;
 	os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
 
-	ckn->len = hapd->conf->mka_ckn_len;;
+	ckn->len = hapd->conf->mka_ckn_len;
 	os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
 
 	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true);