Merge "Explicitly set a user for the wpa_supplicant service" into main am: 05c525178d am: 93b42145a9

Original change: https://android-review.googlesource.com/c/platform/external/wpa_supplicant_8/+/2651763

Change-Id: I9328e268367156b406c309f28422e080c2108c8e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index cd9dfa9..bed29cb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,3 @@
 etancohen@google.com
-haishalom@google.com
 arabawy@google.com
+kumachang@google.com
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index 020396d..e4b3718 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -55,7 +55,6 @@
         "libcrypto",
         "libssl",
         "libnl",
-        "libkeystore-wifi-hidl",
     ],
     relative_install_path: "hw",
     soc_specific: true,
@@ -331,6 +330,8 @@
         "src/utils/json.c",
         "src/ap/wmm.c",
         "src/ap/ap_list.c",
+        "src/ap/comeback_token.c",
+        "src/pasn/pasn_responder.c",
         "src/ap/ieee802_11.c",
         "src/ap/hw_features.c",
         "src/ap/dfs.c",
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index adb4c08..7a1b612 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -272,6 +272,7 @@
 OBJS += src/common/sae.c
 ifdef CONFIG_SAE_PK
 L_CFLAGS += -DCONFIG_SAE_PK
+NEED_AES_SIV=y
 OBJS += src/common/sae_pk.c
 endif
 NEED_ECC=y
@@ -1055,6 +1056,8 @@
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
 OBJS += src/ap/dfs.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 5f06378..a2adc85 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -295,6 +295,7 @@
 OBJS += ../src/common/sae.o
 ifdef CONFIG_SAE_PK
 CFLAGS += -DCONFIG_SAE_PK
+NEED_AES_SIV=y
 OBJS += ../src/common/sae_pk.o
 endif
 NEED_ECC=y
@@ -1185,6 +1186,8 @@
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
 OBJS += ../src/ap/dfs.o
diff --git a/hostapd/aidl/aidl.cpp b/hostapd/aidl/aidl.cpp
index e02708c..c6088ed 100644
--- a/hostapd/aidl/aidl.cpp
+++ b/hostapd/aidl/aidl.cpp
@@ -34,7 +34,8 @@
 
 int hostapd_aidl_init(struct hapd_interfaces *interfaces)
 {
-	wpa_printf(MSG_DEBUG, "Initializing aidl control");
+	wpa_printf(MSG_INFO, "Initializing aidl control");
+	wpa_printf(MSG_INFO, "Interface version: %d", Hostapd::version);
 	std::string instance;   // declared here to allow use of goto
 
 	ABinderProcess_setupPolling(&aidl_fd);
diff --git a/hostapd/aidl/hostapd.cpp b/hostapd/aidl/hostapd.cpp
index b7d7e06..b313e54 100644
--- a/hostapd/aidl/hostapd.cpp
+++ b/hostapd/aidl/hostapd.cpp
@@ -376,12 +376,18 @@
 		encryption_config_as_string = StringPrintf(
 			"wpa=2\n"
 			"rsn_pairwise=%s\n"
-			"wpa_key_mgmt=WPA-PSK SAE\n"
+			"wpa_key_mgmt=%s\n"
 			"ieee80211w=1\n"
 			"sae_require_mfp=1\n"
 			"wpa_passphrase=%s\n"
 			"sae_password=%s",
 			is_60Ghz_band_only ? "GCMP" : "CCMP",
+#ifdef CONFIG_IEEE80211BE
+			iface_params.hwModeParams.enable80211BE ?
+			    "WPA-PSK SAE SAE-EXT-KEY" : "WPA-PSK SAE",
+#else
+			"WPA-PSK SAE",
+#endif
 			nw_params.passphrase.c_str(),
 			nw_params.passphrase.c_str());
 		break;
@@ -392,12 +398,17 @@
 		encryption_config_as_string = StringPrintf(
 			"wpa=2\n"
 			"rsn_pairwise=%s\n"
-			"wpa_key_mgmt=SAE\n"
+			"wpa_key_mgmt=%s\n"
 			"ieee80211w=2\n"
 			"sae_require_mfp=2\n"
 			"sae_pwe=%d\n"
 			"sae_password=%s",
 			is_60Ghz_band_only ? "GCMP" : "CCMP",
+#ifdef CONFIG_IEEE80211BE
+			iface_params.hwModeParams.enable80211BE ? "SAE SAE-EXT-KEY" : "SAE",
+#else
+			"SAE",
+#endif
 			is_6Ghz_band_only ? 1 : 2,
 			nw_params.passphrase.c_str());
 		break;
@@ -504,6 +515,15 @@
 		he_params_as_string = "ieee80211ax=0";
 	}
 #endif /* CONFIG_IEEE80211AX */
+	std::string eht_params_as_string;
+#ifdef CONFIG_IEEE80211BE
+	if (iface_params.hwModeParams.enable80211BE && !is_60Ghz_used) {
+		eht_params_as_string = "ieee80211be=1";
+		/* TODO set eht_su_beamformer, eht_su_beamformee, eht_mu_beamformer */
+	} else {
+		eht_params_as_string = "ieee80211be=0";
+	}
+#endif /* CONFIG_IEEE80211BE */
 
 	std::string ht_cap_vht_oper_he_oper_chwidth_as_string;
 	switch (iface_params.hwModeParams.maximumChannelBandwidth) {
@@ -611,6 +631,7 @@
 		"%s\n"
 		"%s\n"
 		"%s\n"
+		"%s\n"
 		"ignore_broadcast_ssid=%d\n"
 		"wowlan_triggers=any\n"
 #ifdef CONFIG_INTERWORKING
@@ -627,6 +648,7 @@
 		iface_params.hwModeParams.enable80211N ? 1 : 0,
 		iface_params.hwModeParams.enable80211AC ? 1 : 0,
 		he_params_as_string.c_str(),
+		eht_params_as_string.c_str(),
 		hw_mode_as_string.c_str(), ht_cap_vht_oper_he_oper_chwidth_as_string.c_str(),
 		nw_params.isHidden ? 1 : 0,
 #ifdef CONFIG_INTERWORKING
@@ -671,27 +693,27 @@
 		   iconf->vht_oper_chwidth, iconf->ieee80211n,
 		   iconf->secondary_channel);
 	switch (iconf->vht_oper_chwidth) {
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		return ChannelBandwidth::BANDWIDTH_80;
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		return ChannelBandwidth::BANDWIDTH_80P80;
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		return ChannelBandwidth::BANDWIDTH_160;
 		break;
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (iconf->ieee80211n) {
 			return iconf->secondary_channel != 0 ?
 				ChannelBandwidth::BANDWIDTH_40 : ChannelBandwidth::BANDWIDTH_20;
 		}
 		return ChannelBandwidth::BANDWIDTH_20_NOHT;
-	case CHANWIDTH_2160MHZ:
+	case CONF_OPER_CHWIDTH_2160MHZ:
 		return ChannelBandwidth::BANDWIDTH_2160;
-	case CHANWIDTH_4320MHZ:
+	case CONF_OPER_CHWIDTH_4320MHZ:
 		return ChannelBandwidth::BANDWIDTH_4320;
-	case CHANWIDTH_6480MHZ:
+	case CONF_OPER_CHWIDTH_6480MHZ:
 		return ChannelBandwidth::BANDWIDTH_6480;
-	case CHANWIDTH_8640MHZ:
+	case CONF_OPER_CHWIDTH_8640MHZ:
 		return ChannelBandwidth::BANDWIDTH_8640;
 	default:
 		return ChannelBandwidth::BANDWIDTH_INVALID;
@@ -1031,7 +1053,7 @@
 	iface_hapd->setup_complete_cb_ctx = iface_hapd;
 	iface_hapd->sta_authorized_cb = onAsyncStaAuthorizedCb;
 	iface_hapd->sta_authorized_cb_ctx = iface_hapd;
-	wpa_msg_register_cb(onAsyncWpaEventCb);
+	wpa_msg_register_aidl_cb(onAsyncWpaEventCb);
 
 	if (hostapd_enable_iface(iface_hapd->iface) < 0) {
 		wpa_printf(
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 2d5a510..17b1b4a 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -674,8 +674,12 @@
 #ifdef CONFIG_SAE
 		else if (os_strcmp(start, "SAE") == 0)
 			val |= WPA_KEY_MGMT_SAE;
+		else if (os_strcmp(start, "SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_SAE_EXT_KEY;
 		else if (os_strcmp(start, "FT-SAE") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE;
+		else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_SUITEB
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
@@ -2312,6 +2316,8 @@
 			   sizeof(conf->bss[0]->iface));
 	} else if (os_strcmp(buf, "bridge") == 0) {
 		os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
+	} else if (os_strcmp(buf, "bridge_hairpin") == 0) {
+		bss->bridge_hairpin = atoi(pos);
 	} else if (os_strcmp(buf, "vlan_bridge") == 0) {
 		os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
 	} else if (os_strcmp(buf, "wds_bridge") == 0) {
@@ -2361,7 +2367,7 @@
 		}
 		os_memcpy(ssid->ssid, pos, ssid->ssid_len);
 		ssid->ssid_set = 1;
-		ssid->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
+		ssid->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
 	} else if (os_strcmp(buf, "ssid2") == 0) {
 		struct hostapd_ssid *ssid = &bss->ssid;
 		size_t slen;
@@ -2375,7 +2381,7 @@
 		os_memcpy(ssid->ssid, str, slen);
 		ssid->ssid_len = slen;
 		ssid->ssid_set = 1;
-		ssid->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
+		ssid->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
 		os_free(str);
 	} else if (os_strcmp(buf, "utf8_ssid") == 0) {
 		bss->ssid.utf8_ssid = atoi(pos) > 0;
@@ -2414,6 +2420,9 @@
 		bss->ap_max_inactivity = atoi(pos);
 	} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
 		bss->skip_inactivity_poll = atoi(pos);
+	} else if (os_strcmp(buf, "config_id") == 0) {
+		os_free(bss->config_id);
+		bss->config_id = os_strdup(pos);
 	} else if (os_strcmp(buf, "country_code") == 0) {
 		if (pos[0] < 'A' || pos[0] > 'Z' ||
 		    pos[1] < 'A' || pos[1] > 'Z') {
@@ -2578,6 +2587,8 @@
 		bss->eap_teap_separate_result = atoi(pos);
 	} else if (os_strcmp(buf, "eap_teap_id") == 0) {
 		bss->eap_teap_id = atoi(pos);
+	} else if (os_strcmp(buf, "eap_teap_method_sequence") == 0) {
+		bss->eap_teap_method_sequence = atoi(pos);
 #endif /* EAP_SERVER_TEAP */
 #ifdef EAP_SERVER_SIM
 	} else if (os_strcmp(buf, "eap_sim_db") == 0) {
@@ -3472,6 +3483,8 @@
 #ifdef CONFIG_IEEE80211AX
 	} else if (os_strcmp(buf, "ieee80211ax") == 0) {
 		conf->ieee80211ax = atoi(pos);
+	} else if (os_strcmp(buf, "require_he") == 0) {
+		conf->require_he = atoi(pos);
 	} else if (os_strcmp(buf, "he_su_beamformer") == 0) {
 		conf->he_phy_capab.he_su_beamformer = atoi(pos);
 	} else if (os_strcmp(buf, "he_su_beamformee") == 0) {
@@ -3629,6 +3642,15 @@
 			return 1;
 		}
 		bss->unsol_bcast_probe_resp_interval = val;
+	} else if (os_strcmp(buf, "mbssid") == 0) {
+		int mbssid = atoi(pos);
+		if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid mbssid (%d): '%s'.",
+				   line, mbssid, pos);
+			return 1;
+		}
+		conf->mbssid = mbssid;
 #endif /* CONFIG_IEEE80211AX */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
@@ -4266,6 +4288,8 @@
 		conf->enable_eapol_large_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "eap_skip_prot_success") == 0) {
 		bss->eap_skip_prot_success = atoi(pos);
+	} else if (os_strcmp(buf, "delay_eapol_tx") == 0) {
+		conf->delay_eapol_tx = atoi(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_SAE
 	} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -4449,6 +4473,8 @@
 #endif /* CONFIG_FILS */
 	} else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
 		bss->multicast_to_unicast = atoi(pos);
+	} else if (os_strcmp(buf, "bridge_multicast_to_unicast") == 0) {
+		bss->bridge_multicast_to_unicast = atoi(pos);
 	} else if (os_strcmp(buf, "broadcast_deauth") == 0) {
 		bss->broadcast_deauth = atoi(pos);
 	} else if (os_strcmp(buf, "notify_mgmt_frames") == 0) {
@@ -4460,6 +4486,12 @@
 	} else if (os_strcmp(buf, "dpp_mud_url") == 0) {
 		os_free(bss->dpp_mud_url);
 		bss->dpp_mud_url = os_strdup(pos);
+	} else if (os_strcmp(buf, "dpp_extra_conf_req_name") == 0) {
+		os_free(bss->dpp_extra_conf_req_name);
+		bss->dpp_extra_conf_req_name = os_strdup(pos);
+	} else if (os_strcmp(buf, "dpp_extra_conf_req_value") == 0) {
+		os_free(bss->dpp_extra_conf_req_value);
+		bss->dpp_extra_conf_req_value = os_strdup(pos);
 	} else if (os_strcmp(buf, "dpp_connector") == 0) {
 		os_free(bss->dpp_connector);
 		bss->dpp_connector = os_strdup(pos);
@@ -4475,6 +4507,8 @@
 	} else if (os_strcmp(buf, "dpp_controller") == 0) {
 		if (hostapd_dpp_controller_parse(bss, pos))
 			return 1;
+	} else if (os_strcmp(buf, "dpp_relay_port") == 0) {
+		bss->dpp_relay_port = atoi(pos);
 	} else if (os_strcmp(buf, "dpp_configurator_connectivity") == 0) {
 		bss->dpp_configurator_connectivity = atoi(pos);
 	} else if (os_strcmp(buf, "dpp_pfs") == 0) {
@@ -4610,6 +4644,16 @@
 		bss->macsec_replay_protect = macsec_replay_protect;
 	} else if (os_strcmp(buf, "macsec_replay_window") == 0) {
 		bss->macsec_replay_window = atoi(pos);
+	} else if (os_strcmp(buf, "macsec_offload") == 0) {
+		int macsec_offload = atoi(pos);
+
+		if (macsec_offload < 0 || macsec_offload > 2) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_offload (%d): '%s'.",
+				   line, macsec_offload, pos);
+			return 1;
+		}
+		bss->macsec_offload = macsec_offload;
 	} else if (os_strcmp(buf, "macsec_port") == 0) {
 		int macsec_port = atoi(pos);
 
@@ -4718,6 +4762,18 @@
 		conf->eht_phy_capab.su_beamformee = atoi(pos);
 	} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
 		conf->eht_phy_capab.mu_beamformer = atoi(pos);
+	} else if (os_strcmp(buf, "punct_bitmap") == 0) {
+		conf->punct_bitmap = atoi(pos);
+	} else if (os_strcmp(buf, "punct_acs_threshold") == 0) {
+		int val = atoi(pos);
+
+		if (val < 0 || val > 100) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: punct_acs_threshold must be between 0 and 100",
+				   line);
+			return 1;
+		}
+		conf->punct_acs_threshold = val;
 #endif /* CONFIG_IEEE80211BE */
 	} else {
 		wpa_printf(MSG_ERROR,
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index ad994d4..ea9c444 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -38,6 +38,7 @@
 #endif /* CONFIG_DPP */
 #include "common/wpa_ctrl.h"
 #include "common/ptksa_cache.h"
+#include "common/hw_features_common.h"
 #include "crypto/tls.h"
 #include "drivers/driver.h"
 #include "eapol_auth/eapol_auth_sm.h"
@@ -861,6 +862,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "FT-SAE-EXT-KEY ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
@@ -896,6 +903,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "SAE-EXT-KEY ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
@@ -971,6 +984,14 @@
 		return pos - buf;
 	pos += ret;
 
+	if ((hapd->conf->config_id)) {
+		ret = os_snprintf(pos, end - pos, "config_id=%s\n",
+				  hapd->conf->config_id);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 #ifdef CONFIG_WPS
 	ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
 			  hapd->conf->wps_state == 0 ? "disabled" :
@@ -1368,6 +1389,16 @@
 }
 
 
+static int hostapd_ctrl_iface_reload_bss(struct hostapd_data *bss)
+{
+	if (hostapd_reload_bss_only(bss) < 0) {
+		wpa_printf(MSG_ERROR, "Reloading of BSS failed");
+		return -1;
+	}
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
 {
 	if (hostapd_disable_iface(iface) < 0) {
@@ -1686,7 +1717,7 @@
 		return -1;
 	}
 
-	ieee802_1x_receive(hapd, src, buf, len);
+	ieee802_1x_receive(hapd, src, buf, len, FRAME_ENCRYPTION_UNKNOWN);
 	os_free(buf);
 
 	return 0;
@@ -2405,8 +2436,31 @@
 
 
 #ifdef NEED_AP_MLME
-static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
+static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params,
+					  u16 punct_bitmap)
 {
+	u32 start_freq;
+
+	if (is_6ghz_freq(params->freq)) {
+		const int bw_idx[] = { 20, 40, 80, 160, 320 };
+		int idx, bw;
+
+		/* The 6 GHz band requires HE to be enabled. */
+		params->he_enabled = 1;
+
+		if (params->center_freq1) {
+			if (params->freq == 5935)
+				idx = (params->center_freq1 - 5925) / 5;
+			else
+				idx = (params->center_freq1 - 5950) / 5;
+
+			bw = center_idx_to_bw_6ghz(idx);
+			if (bw < 0 || bw > (int) ARRAY_SIZE(bw_idx) ||
+			    bw_idx[bw] != params->bandwidth)
+				return -1;
+		}
+	}
+
 	switch (params->bandwidth) {
 	case 0:
 		/* bandwidth not specified: use 20 MHz by default */
@@ -2418,11 +2472,17 @@
 
 		if (params->center_freq2 || params->sec_channel_offset)
 			return -1;
+
+		if (punct_bitmap)
+			return -1;
 		break;
 	case 40:
 		if (params->center_freq2 || !params->sec_channel_offset)
 			return -1;
 
+		if (punct_bitmap)
+			return -1;
+
 		if (!params->center_freq1)
 			break;
 		switch (params->sec_channel_offset) {
@@ -2457,6 +2517,9 @@
 			return -1;
 		}
 
+		if (params->center_freq2 && punct_bitmap)
+			return -1;
+
 		/* Adjacent and overlapped are not allowed for 80+80 */
 		if (params->center_freq2 &&
 		    params->center_freq1 - params->center_freq2 <= 80 &&
@@ -2491,6 +2554,29 @@
 		return -1;
 	}
 
+	if (!punct_bitmap)
+		return 0;
+
+	if (!params->eht_enabled) {
+		wpa_printf(MSG_ERROR,
+			   "Preamble puncturing supported only in EHT");
+		return -1;
+	}
+
+	if (params->freq >= 2412 && params->freq <= 2484) {
+		wpa_printf(MSG_ERROR,
+			   "Preamble puncturing is not supported in 2.4 GHz");
+		return -1;
+	}
+
+	start_freq = params->center_freq1 - (params->bandwidth / 2);
+	if (!is_punct_bitmap_valid(params->bandwidth,
+				   (params->freq - start_freq) / 20,
+				   punct_bitmap)) {
+		wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap");
+		return -1;
+	}
+
 	return 0;
 }
 #endif /* NEED_AP_MLME */
@@ -2511,7 +2597,8 @@
 	if (ret)
 		return ret;
 
-	ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
+	ret = hostapd_ctrl_check_freq_params(&settings.freq_params,
+					     settings.punct_bitmap);
 	if (ret) {
 		wpa_printf(MSG_INFO,
 			   "chanswitch: invalid frequency settings provided");
@@ -2531,6 +2618,9 @@
 	case 160:
 		bandwidth = CHAN_WIDTH_160;
 		break;
+	case 320:
+		bandwidth = CHAN_WIDTH_320;
+		break;
 	default:
 		bandwidth = CHAN_WIDTH_20;
 		break;
@@ -3184,6 +3274,8 @@
 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
 		if (wpa_debug_reopen_file() < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "CLOSE_LOG") == 0) {
+		wpa_debug_stop_log();
 	} else if (os_strncmp(buf, "NOTE ", 5) == 0) {
 		wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
 	} else if (os_strcmp(buf, "STATUS") == 0) {
@@ -3355,6 +3447,9 @@
 	} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
 		if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "RELOAD_BSS") == 0) {
+		if (hostapd_ctrl_iface_reload_bss(hapd))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
 		if (hostapd_ctrl_iface_reload(hapd->iface))
 			reply_len = -1;
@@ -3516,6 +3611,8 @@
 			if (hostapd_set_acl(hapd) ||
 			    hostapd_disassoc_accept_mac(hapd))
 				reply_len = -1;
+		} else {
+			reply_len = -1;
 		}
 	} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
 		if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
@@ -3541,6 +3638,8 @@
 				&hapd->conf->num_deny_mac);
 			if (hostapd_set_acl(hapd))
 				reply_len = -1;
+		} else {
+			reply_len = -1;
 		}
 #ifdef CONFIG_DPP
 	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
@@ -3674,7 +3773,20 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
 		hostapd_dpp_chirp_stop(hapd);
+	} else if (os_strncmp(buf, "DPP_RELAY_ADD_CONTROLLER ", 25) == 0) {
+		if (hostapd_dpp_add_controller(hapd, buf + 25) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_RELAY_REMOVE_CONTROLLER ", 28) == 0) {
+		hostapd_dpp_remove_controller(hapd, buf + 28);
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	} else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) {
+		if (hostapd_dpp_push_button(hapd, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) {
+		if (hostapd_dpp_push_button(hapd, buf + 15) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 #ifdef RADIUS_SERVER
 	} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
@@ -4183,6 +4295,19 @@
 
 #ifdef CONFIG_DPP
 	dpp_global_clear(interfaces->dpp);
+#ifdef CONFIG_DPP3
+	{
+		int i;
+
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			struct dpp_pb_info *info;
+
+			info = &interfaces->dpp_pb[i];
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+		}
+	}
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 }
 
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index f37d563..bc4a0f6 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -812,6 +812,9 @@
 # 1 = enabled
 #ieee80211ax=1
 
+# Require stations to support HE PHY (reject association if they do not)
+#require_he=1
+
 # disable_11ax: Boolean (0/1) to disable HE for a specific BSS
 #disable_11ax=0
 
@@ -871,7 +874,7 @@
 # he_oper_chwidth is ignored, and the channel width is derived from the
 # configured operating class or center frequency indexes (see
 # IEEE P802.11ax/D6.1 Annex E, Table E-4).
-#he_oper_chwidth
+#he_oper_chwidth (see vht_oper_chwidth)
 #he_oper_centr_freq_seg0_idx
 #he_oper_centr_freq_seg1_idx
 
@@ -1021,9 +1024,22 @@
 # In the 6 GHz band, eht_oper_chwidth is ignored and the channel width is
 # derived from the configured operating class (IEEE P802.11be/D1.5,
 # Annex E.1 - Country information and operating classes).
-#eht_oper_chwidth
+#eht_oper_chwidth (see vht_oper_chwidth)
 #eht_oper_centr_freq_seg0_idx
 
+# Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0,
+# Figure 9-1002c (EHT Operation Information field format). Each bit corresponds
+# to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A
+# bit set to 1 indicates that the channel is punctured (disabled). The default
+# value is 0 indicating that all channels are active.
+#punct_bitmap=0
+
+# Preamble puncturing threshold in automatic channel selection (ACS).
+# The value indicates the percentage of ideal channel average interference
+# factor above which a channel should be punctured.
+# Default is 0, indicates that ACS algorithm should not puncture any channel.
+#punct_acs_threshold=75
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
@@ -1114,6 +1130,14 @@
 # 0: No replay window, strict check (default)
 # 1..2^32-1: number of packets that could be misordered
 #
+# macsec_offload: IEEE 802.1X/MACsec hardware offload
+# This setting applies only when MACsec is in use, i.e.,
+#  - macsec_policy is enabled
+#  - the key server has decided to enable MACsec
+# 0 = MACSEC_OFFLOAD_OFF (default)
+# 1 = MACSEC_OFFLOAD_PHY
+# 2 = MACSEC_OFFLOAD_MAC
+#
 # macsec_port: IEEE 802.1X/MACsec port
 # Port component of the SCI
 # Range: 1-65534 (default: 1)
@@ -1412,6 +1436,12 @@
 # 5 = require both user and machine identity
 #eap_teap_id=0
 
+# EAP-TEAP tunneled EAP method behavior
+# 0 = minimize roundtrips by merging start of the next EAP method with the
+#     crypto-binding of the previous one.
+# 1 = complete crypto-binding before starting the next EAP method
+#eap_teap_method_sequence=0
+
 # EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
 # (default: 0 = disabled).
 #eap_sim_aka_result_ind=1
@@ -2103,6 +2133,8 @@
 # If fils_discovery_max_interval is non-zero, the AP enables FILS Discovery
 # frame transmission. These values use TUs as the unit and have allowed range
 # of 0-10000. fils_discovery_min_interval defaults to 20.
+# This feature is currently supported only when ieee80211ax is enabled for
+# the radio and disable_11ax is not set for the BSS.
 #fils_discovery_min_interval=20
 #fils_discovery_max_interval=0
 
@@ -2514,12 +2546,24 @@
 # MUD URL for Enrollee's DPP Configuration Request (optional)
 #dpp_mud_url=https://example.com/mud
 
+# JSON node name of additional data for Enrollee's DPP Configuration Request
+#dpp_extra_conf_req_name=org.example
+
+# JSON node data of additional data for Enrollee's DPP Configuration Request
+#dpp_extra_conf_req_value="abc":123
+
 #dpp_connector
 #dpp_netaccesskey
 #dpp_netaccesskey_expiry
 #dpp_csign
 #dpp_controller
 
+# DPP Relay port number
+# TCP port to listen to for incoming connections from a Controller. This can be
+# used to allow Controller initiated exchanges in addition to the
+# Controller-as-responder cases covered by the dpp_controller parameter.
+#dpp_relay_port=12345
+
 # Configurator Connectivity indication
 # 0: no Configurator is currently connected (default)
 # 1: advertise that a Configurator is available
@@ -2787,7 +2831,12 @@
 # If the RADIUS server indicates that the station is not allowed to connect to
 # the BSS/ESS, the AP can allow the station some time to download a
 # notification page (URL included in the message). This parameter sets that
-# timeout in seconds.
+# timeout in seconds. If the RADIUS server provides no URL, this value is
+# reduced to two seconds with an additional trigger for immediate
+# deauthentication when the STA acknowledges reception of the deauthentication
+# imminent indication. Note that setting this value to 0 will prevent delivery
+# of the notification to the STA, so a value of at least 1 should be used here
+# for normal use cases.
 #hs20_deauth_req_timeout=60
 
 # Operator Friendly Name
@@ -3063,6 +3112,11 @@
 # Include only ECSA IE without CSA IE where possible
 # (channel switch operating class is needed)
 #ecsa_ie_only=0
+#
+# Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last
+# attempt (wpa_pairwise_update_count). This will trigger a timeout on all
+# previous attempts and thus delays the frame. (testing only)
+#delay_eapol_tx=0
 
 ##### Multiple BSSID support ##################################################
 #
@@ -3106,3 +3160,63 @@
 #bss=wlan0_1
 #bssid=00:13:10:95:fe:0b
 # ...
+#
+# Multiple BSSID Advertisement in IEEE 802.11ax
+# IEEE Std 802.11ax-2021 added a feature where instead of multiple interfaces
+# on a common radio transmitting individual Beacon frames, those interfaces can
+# form a set with a common Beacon frame transmitted for all. The interface
+# which is brought up first is called the transmitting profile of the MBSSID
+# set which transmits the Beacon frames. The remaining interfaces are called
+# the non-transmitting profiles and these are advertised inside the Multiple
+# BSSID element in the Beacon and Probe Response frames from the first
+# interface.
+#
+# The transmitting interface is visible to all stations in the vicinity, however
+# the stations that do not support parsing of the Multiple BSSID element will
+# not be able to connect to the non-transmitting interfaces.
+#
+# Enhanced Multiple BSSID Advertisements (EMA)
+# When enabled, the non-transmitting interfaces are split into multiple
+# Beacon frames. The number of Beacon frames required to cover all the
+# non-transmitting profiles is called the profile periodicity.
+#
+# Refer to IEEE Std 802.11-2020 for details regarding the procedure and
+# required MAC address assignment.
+#
+# Following configuration is per radio.
+# 0 = Disabled (default)
+# 1 = Multiple BSSID advertisement enabled.
+# 2 = Enhanced multiple BSSID advertisement enabled.
+#mbssid=0
+#
+# The transmitting interface should be added with the 'interface' option while
+# the non-transmitting interfaces should be added using the 'bss' option.
+# Security configuration should be added separately per interface, if required.
+#
+# Example:
+#mbssid=2
+#interface=wlan2
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-0>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:84
+#
+#bss=wlan2-1
+#ctrl_interface=/var/run/hostapd
+#wpa_passphrase=0123456789
+#ieee80211w=2
+#sae_pwe=1
+#auth_algs=1
+#wpa=2
+#wpa_pairwise=CCMP
+#ssid=<SSID-1>
+#bridge=br-lan
+#wpa_key_mgmt=SAE
+#bssid=00:03:7f:12:84:85
diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user
index 00edc95..61ef937 100644
--- a/hostapd/hostapd.eap_user
+++ b/hostapd/hostapd.eap_user
@@ -52,8 +52,8 @@
 # Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
 # to the way radius_auth_req_attr is used for Access-Request packet in
 # hostapd.conf. For EAP server, this is configured separately for each user
-# entry with radius_accept_attr=<value> line(s) following the main user entry
-# line.
+# entry with radius_accept_attr=<attr_id>[:<syntax:value>] line(s) following
+# the main user entry line.
 
 # Phase 1 users
 "user"		MD5	"password"
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 60396f3..95edea8 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -252,6 +252,13 @@
 }
 
 
+static int hostapd_cli_cmd_close_log(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "CLOSE_LOG");
+}
+
+
 static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
@@ -307,6 +314,12 @@
 }
 
 
+static void hostapd_cli_action_cb(char *msg, size_t len)
+{
+	hostapd_cli_action_process(msg, len);
+}
+
+
 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char buf[64];
@@ -1208,6 +1221,13 @@
 }
 
 
+static int hostapd_cli_cmd_reload_bss(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOAD_BSS");
+}
+
+
 static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
@@ -1478,7 +1498,7 @@
 static int hostapd_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
 						char *argv[])
 {
-	return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
+	return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 0, argc, argv);
 }
 
 
@@ -1503,6 +1523,15 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+static int hostapd_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_PUSH_BUTTON", 1, argc, argv);
+}
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 
 
@@ -1563,6 +1592,8 @@
 	  "= get MIB variables (dot1x, dot11, radius)" },
 	{ "relog", hostapd_cli_cmd_relog, NULL,
 	  "= reload/truncate debug log output file" },
+	{ "close_log", hostapd_cli_cmd_close_log, NULL,
+	  "= disable debug log output file" },
 	{ "status", hostapd_cli_cmd_status, NULL,
 	  "= show interface status info" },
 	{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
@@ -1661,6 +1692,8 @@
 	  "= enable hostapd on current interface" },
 	{ "reload", hostapd_cli_cmd_reload, NULL,
 	  "= reload configuration for current interface" },
+	{ "reload_bss", hostapd_cli_cmd_reload_bss, NULL,
+	  "= reload configuration for current BSS" },
 	{ "disable", hostapd_cli_cmd_disable, NULL,
 	  "= disable hostapd on current interface" },
 	{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
@@ -1729,6 +1762,10 @@
 	{ "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
 	  "= stop DPP chirp" },
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	{ "dpp_push_button", hostapd_cli_cmd_dpp_push_button, NULL,
+	  "= press DPP push button" },
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 	{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
 	  "=Add/Delete/Show/Clear accept MAC ACL" },
@@ -2002,7 +2039,6 @@
 			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
 	}
 
-	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
 	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
 		  hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
@@ -2026,40 +2062,46 @@
 }
 
 
-static void hostapd_cli_action(struct wpa_ctrl *ctrl)
+static void hostapd_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
 {
-	fd_set rfds;
-	int fd, res;
-	struct timeval tv;
+	struct wpa_ctrl *ctrl = eloop_ctx;
 	char buf[256];
 	size_t len;
 
-	fd = wpa_ctrl_get_fd(ctrl);
-
-	while (!hostapd_cli_quit) {
-		FD_ZERO(&rfds);
-		FD_SET(fd, &rfds);
-		tv.tv_sec = ping_interval;
-		tv.tv_usec = 0;
-		res = select(fd + 1, &rfds, NULL, NULL, &tv);
-		if (res < 0 && errno != EINTR) {
-			perror("select");
-			break;
-		}
-
-		if (FD_ISSET(fd, &rfds))
-			hostapd_cli_recv_pending(ctrl, 0, 1);
-		else {
-			len = sizeof(buf) - 1;
-			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
-					     hostapd_cli_action_process) < 0 ||
-			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
-				printf("hostapd did not reply to PING "
-				       "command - exiting\n");
-				break;
-			}
-		}
+	/* verify that connection is still working */
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
+			     hostapd_cli_action_cb) < 0 ||
+	    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
+		printf("hostapd did not reply to PING command - exiting\n");
+		eloop_terminate();
+		return;
 	}
+	eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping,
+			       ctrl, NULL);
+}
+
+
+static void hostapd_cli_action_receive(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct wpa_ctrl *ctrl = eloop_ctx;
+
+	hostapd_cli_recv_pending(ctrl, 0, 1);
+}
+
+
+static void hostapd_cli_action(struct wpa_ctrl *ctrl)
+{
+	int fd;
+
+	fd = wpa_ctrl_get_fd(ctrl);
+	eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping,
+			       ctrl, NULL);
+	eloop_register_read_sock(fd, hostapd_cli_action_receive, ctrl, NULL);
+	eloop_run();
+	eloop_cancel_timeout(hostapd_cli_action_ping, ctrl, NULL);
+	eloop_unregister_read_sock(fd);
 }
 
 
@@ -2162,6 +2204,8 @@
 		continue;
 	}
 
+	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
+
 	if (action_file && !hostapd_cli_attached)
 		return -1;
 	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
diff --git a/hostapd/main.c b/hostapd/main.c
index eab57b6..18b2dd9 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -243,6 +243,9 @@
 				wpa_printf(MSG_ERROR, "set_wowlan failed");
 		}
 		os_free(triggs);
+
+		iface->mbssid_max_interfaces = capa.mbssid_max_interfaces;
+		iface->ema_max_periodicity = capa.ema_max_periodicity;
 	}
 
 	return 0;
@@ -445,6 +448,13 @@
 		}
 	}
 
+#ifdef CONFIG_CTRL_IFACE_AIDL
+	if (hostapd_aidl_init(ifaces)) {
+		wpa_printf(MSG_ERROR, "Failed to initialize AIDL interface");
+		return -1;
+	}
+#endif /* CONFIG_CTRL_IFACE_AIDL */
+
 	eloop_run();
 
 	return 0;
@@ -468,7 +478,7 @@
 	show_version();
 	fprintf(stderr,
 		"\n"
-		"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
+		"usage: hostapd [-hdBKtvq] [-P <PID file>] [-e <entropy file>] "
 		"\\\n"
 		"         [-g <global ctrl_iface>] [-G <group>]\\\n"
 		"         [-i <comma-separated list of interface names>]\\\n"
@@ -496,7 +506,8 @@
 #endif /* CONFIG_DEBUG_SYSLOG */
 		"   -S   start all the interfaces synchronously\n"
 		"   -t   include timestamps in some debug messages\n"
-		"   -v   show hostapd version\n");
+		"   -v   show hostapd version\n"
+		"   -q   show less debug messages (-qq for even less)\n");
 
 	exit(1);
 }
@@ -687,7 +698,7 @@
 #endif /* CONFIG_DPP */
 
 	for (;;) {
-		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
+		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -760,6 +771,9 @@
 							&if_names_size, optarg))
 				goto out;
 			break;
+		case 'q':
+			wpa_debug_level++;
+			break;
 		default:
 			usage();
 			break;
@@ -891,12 +905,6 @@
 			goto out;
 	}
 
-#ifdef CONFIG_CTRL_IFACE_AIDL
-	if (hostapd_aidl_init(&interfaces)) {
-		wpa_printf(MSG_ERROR, "Failed to initialize AIDL interface");
-		goto out;
-	}
-#endif /* CONFIG_CTRL_IFACE_AIDL */
 	hostapd_global_ctrl_iface_init(&interfaces);
 
 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
diff --git a/hs20/client/est.c b/hs20/client/est.c
index c3f27e1..425b72d 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -11,7 +11,6 @@
 #include <openssl/evp.h>
 #include <openssl/pem.h>
 #include <openssl/pkcs7.h>
-#include <openssl/rsa.h>
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
 #include <openssl/x509.h>
@@ -218,7 +217,9 @@
 	} d;
 } AttrOrOID;
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
 DEFINE_STACK_OF(AttrOrOID)
+#endif
 
 typedef struct {
 	int type;
@@ -365,7 +366,6 @@
 {
 	EVP_PKEY_CTX *pctx = NULL;
 	EVP_PKEY *pkey = NULL;
-	RSA *rsa;
 	X509_REQ *req = NULL;
 	int ret = -1;
 	unsigned int val;
@@ -393,16 +393,11 @@
 	EVP_PKEY_CTX_free(pctx);
 	pctx = NULL;
 
-	rsa = EVP_PKEY_get1_RSA(pkey);
-	if (rsa == NULL)
-		goto fail;
-
 	if (key_pem) {
 		FILE *f = fopen(key_pem, "wb");
 		if (f == NULL)
 			goto fail;
-		if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
-					     NULL)) {
+		if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
 			wpa_printf(MSG_INFO, "Could not write private key: %s",
 				   ERR_error_string(ERR_get_error(), NULL));
 			fclose(f);
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 7b274da..2ca85f9 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -1231,12 +1231,13 @@
 		   homeoi, required);
 
 	if (required) {
-		if (set_cred(ctx->ifname, id, "required_roaming_consortium",
-			     homeoi) < 0)
-			wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+		if (set_cred_quoted(ctx->ifname, id, "required_home_ois",
+				    homeoi) < 0)
+			wpa_printf(MSG_INFO,
+				   "Failed to set cred required_home_ois");
 	} else {
-		if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0)
-			wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+		if (set_cred_quoted(ctx->ifname, id, "home_ois", homeoi) < 0)
+			wpa_printf(MSG_INFO, "Failed to set cred home_ois");
 	}
 
 	xml_node_get_text_free(ctx->xml, homeoi);
@@ -2018,6 +2019,7 @@
 	struct osu_data *osu = NULL, *last = NULL;
 	size_t osu_count = 0;
 	char *pos, *end;
+	int res;
 
 	f = fopen(fname, "r");
 	if (f == NULL) {
@@ -2037,15 +2039,20 @@
 			osu = last;
 			last = &osu[osu_count++];
 			memset(last, 0, sizeof(*last));
-			snprintf(last->bssid, sizeof(last->bssid), "%s",
-				 buf + 13);
+			res = os_snprintf(last->bssid, sizeof(last->bssid),
+					  "%s", buf + 13);
+			if (os_snprintf_error(sizeof(last->bssid), res))
+				break;
 			continue;
 		}
 		if (!last)
 			continue;
 
 		if (strncmp(buf, "uri=", 4) == 0) {
-			snprintf(last->url, sizeof(last->url), "%s", buf + 4);
+			res = os_snprintf(last->url, sizeof(last->url),
+					  "%s", buf + 4);
+			if (os_snprintf_error(sizeof(last->url), res))
+				break;
 			continue;
 		}
 
@@ -2055,26 +2062,37 @@
 		}
 
 		if (strncmp(buf, "osu_ssid=", 9) == 0) {
-			snprintf(last->osu_ssid, sizeof(last->osu_ssid),
-				 "%s", buf + 9);
+			res = os_snprintf(last->osu_ssid,
+					  sizeof(last->osu_ssid),
+					  "%s", buf + 9);
+			if (os_snprintf_error(sizeof(last->osu_ssid), res))
+				break;
 			continue;
 		}
 
 		if (strncmp(buf, "osu_ssid2=", 10) == 0) {
-			snprintf(last->osu_ssid2, sizeof(last->osu_ssid2),
-				 "%s", buf + 10);
+			res = os_snprintf(last->osu_ssid2,
+					  sizeof(last->osu_ssid2),
+					  "%s", buf + 10);
+			if (os_snprintf_error(sizeof(last->osu_ssid2), res))
+				break;
 			continue;
 		}
 
 		if (os_strncmp(buf, "osu_nai=", 8) == 0) {
-			os_snprintf(last->osu_nai, sizeof(last->osu_nai),
-				    "%s", buf + 8);
+			res = os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+					  "%s", buf + 8);
+			if (os_snprintf_error(sizeof(last->osu_nai), res))
+				break;
 			continue;
 		}
 
 		if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
-			os_snprintf(last->osu_nai2, sizeof(last->osu_nai2),
-				    "%s", buf + 9);
+			res = os_snprintf(last->osu_nai2,
+					  sizeof(last->osu_nai2),
+					  "%s", buf + 9);
+			if (os_snprintf_error(sizeof(last->osu_nai2), res))
+				break;
 			continue;
 		}
 
@@ -2087,8 +2105,14 @@
 				continue;
 			*pos++ = '\0';
 			txt = &last->friendly_name[last->friendly_name_count++];
-			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
-			snprintf(txt->text, sizeof(txt->text), "%s", pos);
+			res = os_snprintf(txt->lang, sizeof(txt->lang),
+					  "%s", buf + 14);
+			if (os_snprintf_error(sizeof(txt->lang), res))
+				break;
+			res = os_snprintf(txt->text, sizeof(txt->text),
+					  "%s", pos);
+			if (os_snprintf_error(sizeof(txt->text), res))
+				break;
 		}
 
 		if (strncmp(buf, "desc=", 5) == 0) {
@@ -2100,8 +2124,14 @@
 				continue;
 			*pos++ = '\0';
 			txt = &last->serv_desc[last->serv_desc_count++];
-			snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
-			snprintf(txt->text, sizeof(txt->text), "%s", pos);
+			res = os_snprintf(txt->lang, sizeof(txt->lang),
+					  "%s", buf + 5);
+			if (os_snprintf_error(sizeof(txt->lang), res))
+				break;
+			res = os_snprintf(txt->text, sizeof(txt->text),
+					  "%s", pos);
+			if (os_snprintf_error(sizeof(txt->text), res))
+				break;
 		}
 
 		if (strncmp(buf, "icon=", 5) == 0) {
@@ -2124,23 +2154,30 @@
 			if (!end)
 				continue;
 			*end = '\0';
-			snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
+			res = os_snprintf(icon->lang, sizeof(icon->lang),
+					  "%s", pos);
+			if (os_snprintf_error(sizeof(icon->lang), res))
+				break;
 			pos = end + 1;
 
 			end = strchr(pos, ':');
 			if (end)
 				*end = '\0';
-			snprintf(icon->mime_type, sizeof(icon->mime_type),
-				 "%s", pos);
-			if (!pos)
+			res = os_snprintf(icon->mime_type,
+					  sizeof(icon->mime_type), "%s", pos);
+			if (os_snprintf_error(sizeof(icon->mime_type), res))
+				break;
+			if (!end)
 				continue;
 			pos = end + 1;
 
 			end = strchr(pos, ':');
 			if (end)
 				*end = '\0';
-			snprintf(icon->filename, sizeof(icon->filename),
-				 "%s", pos);
+			res = os_snprintf(icon->filename,
+					  sizeof(icon->filename), "%s", pos);
+			if (os_snprintf_error(sizeof(icon->filename), res))
+				break;
 			continue;
 		}
 	}
diff --git a/src/Makefile b/src/Makefile
index 6eb7f2a..d15cf32 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae pasn radius rsn_supp tls utils wps
 SUBDIRS += fst
 
 all:
diff --git a/src/ap/acs.c b/src/ap/acs.c
index faaedbf..1181c7d 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -241,6 +241,57 @@
  * [1] http://en.wikipedia.org/wiki/Near_and_far_field
  */
 
+enum bw_type {
+	ACS_BW40,
+	ACS_BW80,
+	ACS_BW160,
+};
+
+struct bw_item {
+	int first;
+	int last;
+	int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+	{ 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+	{ 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+	{ 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+	{ 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+	{ 5825, 5845, 167 }, { 5865, 5885, 175 },
+	{ 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+	{ 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+	{ 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+	{ 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+	{ 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+	{ 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+	{ 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+	{ 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+	{ 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+	{ 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+	{ 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+	{ 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+	{ 5825, 5885, 171},
+	{ 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+	{ 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+	{ 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+	{ 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+	{ 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+	{ 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+	{ 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
+	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+	[ACS_BW40] = bw_40,
+	[ACS_BW80] = bw_80,
+	[ACS_BW160] = bw_160,
+};
+
 
 static int acs_request_scan(struct hostapd_iface *iface);
 static int acs_survey_is_sufficient(struct freq_survey *survey);
@@ -275,6 +326,7 @@
 		dl_list_init(&chan->survey_list);
 		chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
 		chan->min_nf = 0;
+		chan->punct_bitmap = 0;
 	}
 }
 
@@ -370,48 +422,31 @@
 }
 
 
-static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+			       enum bw_type bw)
 {
-	const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
-				5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
-				6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
-				6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
-				6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
-				7035, 7075 };
-	unsigned int i;
+	unsigned int i = 0;
 
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
+	while (bw_desc[bw][i].first != -1) {
+		if (chan->freq == bw_desc[bw][i].first)
+			return true;
+		i++;
+	}
 
-	return 0;
+	return false;
 }
 
 
-static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
 {
-	const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
-				6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
-				6755, 6835, 6915, 6995 };
-	unsigned int i;
+	unsigned int i = 0;
 
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
-
-	return 0;
-}
-
-
-static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
-{
-	const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
-				6915 };
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
+	while (bw_desc[bw][i].first != -1) {
+		if (freq >= bw_desc[bw][i].first &&
+		    freq <= bw_desc[bw][i].last)
+			return bw_desc[bw][i].center_chan;
+		i++;
+	}
 
 	return 0;
 }
@@ -420,19 +455,24 @@
 static int acs_survey_is_sufficient(struct freq_survey *survey)
 {
 	if (!(survey->filled & SURVEY_HAS_NF)) {
-		wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing noise floor",
+			   survey->freq);
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-		wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing channel time",
+			   survey->freq);
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
 	    !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
 		wpa_printf(MSG_INFO,
-			   "ACS: Survey is missing RX and busy time (at least one is required)");
+			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+			   survey->freq);
 		return 0;
 	}
 
@@ -553,6 +593,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
 			   chan->chan, chan->freq);
 
@@ -598,6 +642,26 @@
 }
 
 
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode)) {
+			chan = acs_find_chan_mode(mode, freq);
+			if (chan)
+				return mode->mode;
+		}
+	}
+
+	return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
 static struct hostapd_channel_data *
 acs_find_chan(struct hostapd_iface *iface, int freq)
 {
@@ -648,6 +712,62 @@
 #define ACS_24GHZ_PREFER_1_6_11 0.8
 #endif /* ACS_24GHZ_PREFER_1_6_11 */
 
+
+#ifdef CONFIG_IEEE80211BE
+static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
+					 struct hostapd_hw_modes *mode, u32 bw,
+					 int n_chans,
+					 struct hostapd_channel_data *chan,
+					 long double factor,
+					 int index_primary)
+{
+	struct hostapd_config *conf = iface->conf;
+	struct hostapd_channel_data *adj_chan = NULL, *first_chan = chan;
+	int i;
+	long double threshold;
+
+	/*
+	 * If threshold is 0 or user configured puncturing pattern is
+	 * available then don't add additional puncturing.
+	 */
+	if (!conf->punct_acs_threshold || conf->punct_bitmap)
+		return;
+
+	if (is_24ghz_mode(mode->mode) || bw < 80)
+		return;
+
+	threshold = factor * conf->punct_acs_threshold / 100;
+	for (i = 0; i < n_chans; i++) {
+		int adj_freq;
+
+		if (i == index_primary)
+			continue; /* Cannot puncture primary channel */
+
+		if (i > index_primary)
+			adj_freq = chan->freq + (i - index_primary) * 20;
+		else
+			adj_freq = chan->freq - (index_primary - i) * 20;
+
+		adj_chan = acs_find_chan(iface, adj_freq);
+		if (!adj_chan) {
+			chan->punct_bitmap = 0;
+			return;
+		}
+
+		if (i == 0)
+			first_chan = adj_chan;
+
+		if (adj_chan->interference_factor > threshold)
+			chan->punct_bitmap |= BIT(i);
+	}
+
+	if (!is_punct_bitmap_valid(bw, (chan->freq - first_chan->freq) / 20,
+				   chan->punct_bitmap))
+		chan->punct_bitmap = 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 static void
 acs_find_ideal_chan_mode(struct hostapd_iface *iface,
 			 struct hostapd_hw_modes *mode,
@@ -656,7 +776,7 @@
 			 struct hostapd_channel_data **ideal_chan,
 			 long double *ideal_factor)
 {
-	struct hostapd_channel_data *chan, *adj_chan = NULL;
+	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
 	long double factor;
 	int i, j;
 	unsigned int k;
@@ -664,13 +784,20 @@
 	for (i = 0; i < mode->num_channels; i++) {
 		double total_weight;
 		struct acs_bias *bias, tmp_bias;
+		bool update_best = true;
 
-		chan = &mode->channels[i];
+		best = chan = &mode->channels[i];
 
 		/* Since in the current ACS implementation the first channel is
 		 * always a primary channel, skip channels not available as
 		 * primary until more sophisticated channel selection is
-		 * implemented. */
+		 * implemented.
+		 *
+		 * If this implementation is changed to allow any channel in
+		 * the bandwidth to be the primary one, the last parameter to
+		 * acs_update_puncturing_bitmap() should be changed to the index
+		 * of the primary channel
+		 */
 		if (!chan_pri_allowed(chan))
 			continue;
 
@@ -687,6 +814,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		if (!chan_bw_allowed(chan, bw, 1, 1)) {
 			wpa_printf(MSG_DEBUG,
 				   "ACS: Channel %d: BW %u is not supported",
@@ -700,7 +831,7 @@
 		    ((iface->conf->ieee80211n &&
 		      iface->conf->secondary_channel) ||
 		     is_6ghz_freq(chan->freq)) &&
-		    !acs_usable_bw40_chan(chan)) {
+		    !acs_usable_bw_chan(chan, ACS_BW40)) {
 			wpa_printf(MSG_DEBUG,
 				   "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
 				   chan->chan);
@@ -710,8 +841,8 @@
 		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
 		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
 			if (hostapd_get_oper_chwidth(iface->conf) ==
-			    CHANWIDTH_80MHZ &&
-			    !acs_usable_bw80_chan(chan)) {
+			    CONF_OPER_CHWIDTH_80MHZ &&
+			    !acs_usable_bw_chan(chan, ACS_BW80)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
 					   chan->chan);
@@ -719,8 +850,8 @@
 			}
 
 			if (hostapd_get_oper_chwidth(iface->conf) ==
-			    CHANWIDTH_160MHZ &&
-			    !acs_usable_bw160_chan(chan)) {
+			    CONF_OPER_CHWIDTH_160MHZ &&
+			    !acs_usable_bw_chan(chan, ACS_BW160)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
 					   chan->chan);
@@ -748,7 +879,15 @@
 			if (acs_usable_chan(adj_chan)) {
 				factor += adj_chan->interference_factor;
 				total_weight += 1;
+			} else {
+				update_best = false;
 			}
+
+			/* find the best channel in this segment */
+			if (update_best &&
+			    adj_chan->interference_factor <
+			    best->interference_factor)
+				best = adj_chan;
 		}
 
 		if (j != n_chans) {
@@ -757,6 +896,18 @@
 			continue;
 		}
 
+		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+		 * crowded primary channel if one was found in the segment */
+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    chan != best) {
+			wpa_printf(MSG_DEBUG,
+				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+				   best->chan, chan->chan,
+				   chan->interference_factor,
+				   best->interference_factor);
+			chan = best;
+		}
+
 		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
 		 * channel interference factor. */
 		if (is_24ghz_mode(mode->mode)) {
@@ -825,8 +976,20 @@
 
 		if (acs_usable_chan(chan) &&
 		    (!*ideal_chan || factor < *ideal_factor)) {
+			/* Reset puncturing bitmap for the previous ideal
+			 * channel */
+			if (*ideal_chan)
+				(*ideal_chan)->punct_bitmap = 0;
+
 			*ideal_factor = factor;
 			*ideal_chan = chan;
+
+#ifdef CONFIG_IEEE80211BE
+			if (iface->conf->ieee80211be)
+				acs_update_puncturing_bitmap(iface, mode, bw,
+							     n_chans, chan,
+							     factor, 0);
+#endif /* CONFIG_IEEE80211BE */
 		}
 
 		/* This channel would at least be usable */
@@ -873,12 +1036,14 @@
 
 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
 		switch (hostapd_get_oper_chwidth(iface->conf)) {
-		case CHANWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			n_chans = 8;
 			break;
+		default:
+			break;
 		}
 	}
 
@@ -901,28 +1066,70 @@
 	if (ideal_chan) {
 		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
+
+#ifdef CONFIG_IEEE80211BE
+		if (iface->conf->punct_acs_threshold)
+			wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+				   ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
 		return ideal_chan;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (iface->conf->punct_acs_threshold)
+		wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+			   ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
 	return rand_chan;
 }
 
 
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+	unsigned int i;
+
+	/* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+	 * ACS can return a secondary channel which is not the first channel of
+	 * the segment and we need to adjust. */
+	if (!iface->conf->secondary_channel ||
+	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+		return;
+
+	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+		if (iface->freq == bw_desc[ACS_BW40][i].first)
+			iface->conf->secondary_channel = 1;
+		else if (iface->freq == bw_desc[ACS_BW40][i].last)
+			iface->conf->secondary_channel = -1;
+	}
+}
+
+
 static void acs_adjust_center_freq(struct hostapd_iface *iface)
 {
-	int offset;
+	int center;
 
 	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
 
 	switch (hostapd_get_oper_chwidth(iface->conf)) {
-	case CHANWIDTH_USE_HT:
-		offset = 2 * iface->conf->secondary_channel;
+	case CONF_OPER_CHWIDTH_USE_HT:
+		if (iface->conf->secondary_channel &&
+		    iface->freq >= 2400 && iface->freq < 2500)
+			center = iface->conf->channel +
+				2 * iface->conf->secondary_channel;
+		else if (iface->conf->secondary_channel)
+			center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+		else
+			center = iface->conf->channel;
 		break;
-	case CHANWIDTH_80MHZ:
-		offset = 6;
+	case CONF_OPER_CHWIDTH_80MHZ:
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
 		break;
-	case CHANWIDTH_160MHZ:
-		offset = 14;
+	case CONF_OPER_CHWIDTH_160MHZ:
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
 		break;
 	default:
 		/* TODO: How can this be calculated? Adjust
@@ -932,8 +1139,7 @@
 		return;
 	}
 
-	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
-					     iface->conf->channel + offset);
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
 }
 
 
@@ -988,9 +1194,23 @@
 
 	iface->conf->channel = ideal_chan->chan;
 	iface->freq = ideal_chan->freq;
+#ifdef CONFIG_IEEE80211BE
+	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 
-	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		acs_adjust_secondary(iface);
 		acs_adjust_center_freq(iface);
+	}
+
+	err = hostapd_select_hw_mode(iface);
+	if (err) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
+			err, iface->freq, iface->conf->channel);
+		err = -1;
+		goto fail;
+	}
 
 	err = 0;
 fail:
@@ -1066,6 +1286,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		*freq++ = chan->freq;
 	}
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index db86a76..b5fcc38 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -481,9 +481,11 @@
 	struct hostapd_ssid *ssid = &conf->ssid;
 	struct sae_password_entry *pw;
 
-	if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+	if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+	     !hostapd_sae_pw_id_in_use(conf) &&
+	     !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
 	     !hostapd_sae_pk_in_use(conf)) ||
-	    conf->sae_pwe == 3 ||
+	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
 	    !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
 		return 0; /* PT not needed */
 
@@ -797,6 +799,7 @@
 	os_free(conf->radius_req_attr_sqlite);
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
+	os_free(conf->config_id);
 	os_free(conf->ca_cert);
 	os_free(conf->server_cert);
 	os_free(conf->server_cert2);
@@ -949,6 +952,8 @@
 #ifdef CONFIG_DPP
 	os_free(conf->dpp_name);
 	os_free(conf->dpp_mud_url);
+	os_free(conf->dpp_extra_conf_req_name);
+	os_free(conf->dpp_extra_conf_req_value);
 	os_free(conf->dpp_connector);
 	wpabuf_free(conf->dpp_netaccesskey);
 	wpabuf_free(conf->dpp_csign);
@@ -1208,6 +1213,14 @@
 		return false;
 	}
 
+#ifdef CONFIG_SAE
+	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+	    bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+		wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+		bss->sae_pwe = SAE_PWE_BOTH;
+	}
+#endif /* CONFIG_SAE */
+
 	return true;
 }
 
@@ -1434,6 +1447,13 @@
 
 #ifdef CONFIG_FILS
 	if (full_config && bss->fils_discovery_max_int &&
+	    (!conf->ieee80211ax || bss->disable_11ax)) {
+		wpa_printf(MSG_ERROR,
+			   "Currently IEEE 802.11ax support is mandatory to enable FILS discovery transmission.");
+		return -1;
+	}
+
+	if (full_config && bss->fils_discovery_max_int &&
 	    bss->unsol_bcast_probe_resp_interval) {
 		wpa_printf(MSG_ERROR,
 			   "Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
@@ -1449,6 +1469,12 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+		wpa_printf(MSG_ERROR,
+			   "Hidden SSID is not suppored when MBSSID is enabled");
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -1532,6 +1558,12 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (full_config && conf->mbssid && !conf->ieee80211ax) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot enable multiple BSSID support without ieee80211ax");
+		return -1;
+	}
+
 	for (i = 0; i < conf->num_bss; i++) {
 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
 			return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index b97d49c..07ee31c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -284,6 +284,7 @@
 	char bridge[IFNAMSIZ + 1];
 	char vlan_bridge[IFNAMSIZ + 1];
 	char wds_bridge[IFNAMSIZ + 1];
+	int bridge_hairpin; /* hairpin_mode on bridge members */
 
 	enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
 
@@ -443,6 +444,7 @@
 	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	int eap_teap_id;
+	int eap_teap_method_sequence;
 	int eap_sim_aka_result_ind;
 	int eap_sim_id;
 	char *imsi_privacy_key;
@@ -670,7 +672,7 @@
 	unsigned int sae_sync;
 	int sae_require_mfp;
 	int sae_confirm_immediate;
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 	int *sae_groups;
 	struct sae_password_entry *sae_passwords;
 
@@ -747,6 +749,7 @@
 #endif /* CONFIG_FILS */
 
 	int multicast_to_unicast;
+	int bridge_multicast_to_unicast;
 
 	int broadcast_deauth;
 
@@ -755,12 +758,15 @@
 #ifdef CONFIG_DPP
 	char *dpp_name;
 	char *dpp_mud_url;
+	char *dpp_extra_conf_req_name;
+	char *dpp_extra_conf_req_value;
 	char *dpp_connector;
 	struct wpabuf *dpp_netaccesskey;
 	unsigned int dpp_netaccesskey_expiry;
 	struct wpabuf *dpp_csign;
 #ifdef CONFIG_DPP2
 	struct dpp_controller_conf *dpp_controller;
+	int dpp_relay_port;
 	int dpp_configurator_connectivity;
 	int dpp_pfs;
 #endif /* CONFIG_DPP2 */
@@ -841,6 +847,19 @@
 	u32 macsec_replay_window;
 
 	/**
+	 * macsec_offload - Enable MACsec offload
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0 = MACSEC_OFFLOAD_OFF (default)
+	 * 1 = MACSEC_OFFLOAD_PHY
+	 * 2 = MACSEC_OFFLOAD_MAC
+	 */
+	int macsec_offload;
+
+	/**
 	 * macsec_port - MACsec port (in SCI)
 	 *
 	 * Port component of the SCI.
@@ -913,6 +932,8 @@
 	u8 ext_capa[EXT_CAPA_MAX_LEN];
 
 	u8 rnr;
+	char *config_id;
+	bool xrates_supported;
 };
 
 /**
@@ -1048,7 +1069,7 @@
 	u32 vht_capab;
 	int ieee80211ac;
 	int require_vht;
-	u8 vht_oper_chwidth;
+	enum oper_chan_width vht_oper_chwidth;
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
 	u8 ht40_plus_minus_allowed;
@@ -1073,6 +1094,7 @@
 	int ecsa_ie_only;
 	unsigned int skip_send_eapol;
 	unsigned int enable_eapol_large_timeout;
+	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
@@ -1094,7 +1116,7 @@
 	struct he_operation he_op;
 	struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
 	struct spatial_reuse spr;
-	u8 he_oper_chwidth;
+	enum oper_chan_width he_oper_chwidth;
 	u8 he_oper_centr_freq_seg0_idx;
 	u8 he_oper_centr_freq_seg1_idx;
 	u8 he_6ghz_max_mpdu;
@@ -1102,6 +1124,7 @@
 	u8 he_6ghz_rx_ant_pat;
 	u8 he_6ghz_tx_ant_pat;
 	u8 he_6ghz_reg_pwr_type;
+	bool require_he;
 #endif /* CONFIG_IEEE80211AX */
 
 	/* VHT enable/disable config from CHAN_SWITCH */
@@ -1132,19 +1155,28 @@
 
 	int ieee80211be;
 #ifdef CONFIG_IEEE80211BE
-	u8 eht_oper_chwidth;
+	enum oper_chan_width eht_oper_chwidth;
 	u8 eht_oper_centr_freq_seg0_idx;
 	struct eht_phy_capabilities_info eht_phy_capab;
+	u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
+	u8 punct_acs_threshold;
 #endif /* CONFIG_IEEE80211BE */
 
 	/* EHT enable/disable config from CHAN_SWITCH */
 #define CH_SWITCH_EHT_ENABLED BIT(0)
 #define CH_SWITCH_EHT_DISABLED BIT(1)
 	unsigned int ch_switch_eht_config;
+
+	enum mbssid {
+		MBSSID_DISABLED = 0,
+		MBSSID_ENABLED = 1,
+		ENHANCED_MBSSID_ENABLED = 2,
+	} mbssid;
 };
 
 
-static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
+static inline enum oper_chan_width
+hostapd_get_oper_chwidth(struct hostapd_config *conf)
 {
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
@@ -1158,11 +1190,14 @@
 }
 
 static inline void
-hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
+hostapd_set_oper_chwidth(struct hostapd_config *conf,
+			 enum oper_chan_width oper_chwidth)
 {
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		conf->eht_oper_chwidth = oper_chwidth;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+		oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
@@ -1192,6 +1227,9 @@
 #ifdef CONFIG_IEEE80211BE
 	if (conf->ieee80211be)
 		conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+	if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+		oper_centr_freq_seg0_idx +=
+			conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8af7a0e..f77f738 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -75,6 +75,14 @@
 
 	*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
 
+#ifdef NEED_AP_MLME
+	pos = buf;
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, sizeof(buf));
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* NEED_AP_MLME */
+
 	pos = buf;
 	pos = hostapd_eid_time_adv(hapd, pos);
 	if (add_buf_data(&beacon, buf, pos - buf) < 0)
@@ -84,7 +92,7 @@
 		goto fail;
 
 	pos = buf;
-	pos = hostapd_eid_ext_capab(hapd, pos);
+	pos = hostapd_eid_ext_capab(hapd, pos, false);
 	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
 		goto fail;
 	pos = hostapd_eid_interworking(hapd, pos);
@@ -451,6 +459,7 @@
 	params.qosinfo = qosinfo;
 	params.support_p2p_ps = supp_p2p_ps;
 	params.set = set;
+	params.mld_link_id = -1;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
 
@@ -716,6 +725,7 @@
 	params.key_len = key_len;
 	params.vlan_id = vlan_id;
 	params.key_flag = key_flag;
+	params.link_id = -1;
 
 	return hapd->driver->set_key(hapd->drv_priv, &params);
 }
@@ -897,8 +907,10 @@
 			     chan->chan)))
 			continue;
 		if (is_6ghz_freq(chan->freq) &&
-		    hapd->iface->conf->acs_exclude_6ghz_non_psc &&
-		    !is_6ghz_psc_frequency(chan->freq))
+		    ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+		      !is_6ghz_psc_frequency(chan->freq)) ||
+		     (!hapd->iface->conf->ieee80211ax &&
+		      !hapd->iface->conf->ieee80211be)))
 			continue;
 		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		    !(hapd->iface->conf->acs_exclude_dfs &&
@@ -978,13 +990,16 @@
 	     hapd->iface->conf->ieee80211ax ||
 	     hapd->iface->conf->ieee80211ac) &&
 	    params.ht40_enabled) {
-		u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+		enum oper_chan_width oper_chwidth;
 
-		if (oper_chwidth == CHANWIDTH_80MHZ)
+		oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+		if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 			params.ch_width = 80;
-		else if (oper_chwidth == CHANWIDTH_160MHZ ||
-			 oper_chwidth == CHANWIDTH_80P80MHZ)
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+			 oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
 			params.ch_width = 160;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+			params.ch_width = 320;
 	}
 
 	if (hapd->iface->conf->op_class)
@@ -1013,3 +1028,30 @@
 		return 0;
 	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
 }
+
+
+#ifdef CONFIG_PASN
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *peer_addr,
+				       u32 cipher, u8 tk_len, const u8 *tk,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action)
+{
+	struct secure_ranging_params params;
+
+	if (!hapd->driver || !hapd->driver->set_secure_ranging_ctx)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.own_addr = own_addr;
+	params.peer_addr = peer_addr;
+	params.cipher = cipher;
+	params.tk_len = tk_len;
+	params.tk = tk;
+	params.ltf_keyseed_len = ltf_keyseed_len;
+	params.ltf_keyseed = ltf_keyseed;
+	params.action = action;
+
+	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
+}
+#endif /* CONFIG_PASN */
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index b4fb766..93b2244 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -138,6 +138,11 @@
 int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
 			     u16 reason_code, const u8 *ie, size_t ielen);
 int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *addr,
+				       u32 cipher, u8 key_len, const u8 *key,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action);
 
 
 #include "drivers/driver.h"
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index db8a267..309e69a 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -29,9 +29,9 @@
 		return "SHARED_KEY";
 	case WLAN_AUTH_FT:
 		return "FT";
+	default:
+		return "unknown";
 	}
-
-	return "unknown";
 }
 #endif /* CONFIG_NO_HOSTAPD_LOGGER */
 
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index fd9c96f..4ab2a4a 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -211,6 +211,7 @@
 	cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
 	cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
 	cfg->eap_teap_id = hapd->conf->eap_teap_id;
+	cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
 	cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
 	cfg->eap_sim_id = hapd->conf->eap_sim_id;
 	cfg->imsi_privacy_key = hapd->imsi_privacy_key;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index eaa4033..c25a5bb 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -462,14 +462,85 @@
 }
 
 
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+				  struct wpa_driver_ap_params *params)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_data *tx_bss;
+	size_t len;
+	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+
+	if (!iface->mbssid_max_interfaces ||
+	    iface->num_bss > iface->mbssid_max_interfaces ||
+	    (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	     !iface->ema_max_periodicity))
+		goto fail;
+
+	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+				     NULL, 0);
+	if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+		     elem_count > iface->ema_max_periodicity))
+		goto fail;
+
+	elem = os_zalloc(len);
+	if (!elem)
+		goto fail;
+
+	elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+	if (!elem_offset)
+		goto fail;
+
+	end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+				 elem_count, elem_offset, NULL, 0);
+
+	params->mbssid_tx_iface = tx_bss->conf->iface;
+	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+	params->mbssid_elem = elem;
+	params->mbssid_elem_len = end - elem;
+	params->mbssid_elem_count = elem_count;
+	params->mbssid_elem_offset = elem_offset;
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+		params->ema = true;
+
+	return 0;
+
+fail:
+	os_free(elem);
+	wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+	return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+				      u8 mbssid_elem_count)
+{
+	struct hostapd_iface *iface = hapd->iface;
+
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+		*eid++ = WLAN_EID_EXTENSION;
+		*eid++ = 3;
+		*eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+		*eid++ = iface->num_bss;
+		*eid++ = mbssid_elem_count;
+	}
+
+	return eid;
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
-				   int is_p2p, size_t *resp_len)
+				   int is_p2p, size_t *resp_len,
+				   const u8 *known_bss, u8 known_bss_len)
 {
 	struct ieee80211_mgmt *resp;
 	u8 *pos, *epos, *csa_pos;
 	size_t buflen;
 
+	hapd = hostapd_mbssid_get_tx_bss(hapd);
+
 #define MAX_PROBERESP_LEN 768
 	buflen = MAX_PROBERESP_LEN;
 #ifdef CONFIG_WPS
@@ -513,9 +584,13 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+					 known_bss, known_bss_len);
 	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
 	buflen += hostapd_mbo_ie_len(hapd);
 	buflen += hostapd_eid_owe_trans_len(hapd);
@@ -529,8 +604,18 @@
 
 	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					   WLAN_FC_STYPE_PROBE_RESP);
-	if (req)
+	/* Unicast the response to all requests on bands other than 6 GHz. For
+	 * the 6 GHz, unicast is used only if the actual SSID is not included in
+	 * the Beacon frames. Otherwise, broadcast response is used per IEEE
+	 * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+	 * the Probe Response frame template for the unsolicited (i.e., not as
+	 * a response to a specific request) case. */
+	if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+		    hapd->conf->ignore_broadcast_ssid))
 		os_memcpy(resp->da, req->sa, ETH_ALEN);
+	else
+		os_memset(resp->da, 0xff, ETH_ALEN);
+
 	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
 
 	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -572,6 +657,8 @@
 
 	pos = hostapd_get_rsne(hapd, pos, epos - pos);
 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+				 NULL, known_bss, known_bss_len);
 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
 	pos = hostapd_get_mde(hapd, pos, epos - pos);
 
@@ -585,7 +672,11 @@
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
 
-	pos = hostapd_eid_ext_capab(hapd, pos);
+	/* Probe Response frames always include all non-TX profiles except
+	 * when a list of known BSSes is included in the Probe Request frame. */
+	pos = hostapd_eid_ext_capab(hapd, pos,
+				    hapd->iconf->mbssid >= MBSSID_ENABLED &&
+				    !known_bss_len);
 
 	pos = hostapd_eid_time_adv(hapd, pos);
 	pos = hostapd_eid_time_zone(hapd, pos);
@@ -1137,11 +1228,18 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	/* Do not send Probe Response frame from a non-transmitting multiple
+	 * BSSID profile unless the Probe Request frame is directed at that
+	 * particular BSS. */
+	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+		return;
+
 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
 
 	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
-				      &resp_len);
+				      &resp_len, elems.mbssid_known_bss,
+				      elems.mbssid_known_bss_len);
 	if (resp == NULL)
 		return;
 
@@ -1163,7 +1261,8 @@
 				hapd->cs_c_off_ecsa_proberesp;
 	}
 
-	ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
+	ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+				    resp_len, noack,
 				    csa_offs_len ? csa_offs : NULL,
 				    csa_offs_len, 0);
 
@@ -1210,7 +1309,7 @@
 			   "this");
 
 	/* Generate a Probe Response template for the non-P2P case */
-	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len);
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
 }
 
 #endif /* NEED_AP_MLME */
@@ -1228,7 +1327,8 @@
 		hapd->conf->unsol_bcast_probe_resp_interval;
 
 	return hostapd_gen_probe_resp(hapd, NULL, 0,
-				      &params->unsol_bcast_probe_resp_tmpl_len);
+				      &params->unsol_bcast_probe_resp_tmpl_len,
+				      NULL, 0);
 }
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1259,6 +1359,9 @@
 		phy_index = FD_CAP_PHY_INDEX_HE;
 
 		switch (hapd->iconf->op_class) {
+		case 137:
+			chwidth = FD_CAP_BSS_CHWIDTH_320;
+			break;
 		case 135:
 			mcs_nss_size += 4;
 			/* fallthrough */
@@ -1275,22 +1378,24 @@
 		}
 	} else {
 		switch (hostapd_get_oper_chwidth(hapd->iconf)) {
-		case CHANWIDTH_80P80MHZ:
+		case CONF_OPER_CHWIDTH_80P80MHZ:
 			mcs_nss_size += 4;
 			/* fallthrough */
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			mcs_nss_size += 4;
 			chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
 			break;
-		case CHANWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
 			chwidth = FD_CAP_BSS_CHWIDTH_80;
 			break;
-		case CHANWIDTH_USE_HT:
+		case CONF_OPER_CHWIDTH_USE_HT:
 			if (hapd->iconf->secondary_channel)
 				chwidth = FD_CAP_BSS_CHWIDTH_40;
 			else
 				chwidth = FD_CAP_BSS_CHWIDTH_20;
 			break;
+		default:
+			break;
 		}
 
 #ifdef CONFIG_IEEE80211AX
@@ -1310,14 +1415,37 @@
 	cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
 	cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
 
-	if (mode) {
-		u16 *mcs = (u16 *) mode->he_capab[IEEE80211_MODE_AP].mcs;
+	if (mode && phy_index == FD_CAP_PHY_INDEX_HE) {
+		const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
 		int i;
-		u16 nss = 0;
+		u16 nss = 0, mcs[6];
+
+		os_memset(mcs, 0xffff, 6 * sizeof(u16));
+
+		if (mcs_nss_size == 4) {
+			mcs[0] = WPA_GET_LE16(&he_mcs[0]);
+			mcs[1] = WPA_GET_LE16(&he_mcs[2]);
+		}
+
+		if (mcs_nss_size == 8) {
+			mcs[2] = WPA_GET_LE16(&he_mcs[4]);
+			mcs[3] = WPA_GET_LE16(&he_mcs[6]);
+		}
+
+		if (mcs_nss_size == 12) {
+			mcs[4] = WPA_GET_LE16(&he_mcs[8]);
+			mcs[5] = WPA_GET_LE16(&he_mcs[10]);
+		}
 
 		for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
 			u16 nss_mask = 0x3 << (i * 2);
 
+			/*
+			 * If NSS values supported by RX and TX are different
+			 * then choose the smaller of the two as the maximum
+			 * supported NSS as that is the value supported by
+			 * both RX and TX.
+			 */
 			if (mcs_nss_size == 4 &&
 			    (((mcs[0] & nss_mask) == nss_mask) ||
 			     ((mcs[1] & nss_mask) == nss_mask)))
@@ -1507,7 +1635,12 @@
 #ifdef NEED_AP_MLME
 	u16 capab_info;
 	u8 *pos, *tailpos, *tailend, *csa_pos;
+	bool complete = false;
+#endif /* NEED_AP_MLME */
 
+	os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
 	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
@@ -1556,9 +1689,14 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		tail_len += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	    hapd == hostapd_mbssid_get_tx_bss(hapd))
+		tail_len += 5; /* Multiple BSSID Configuration element */
 	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
 	tail_len += hostapd_mbo_ie_len(hapd);
 	tail_len += hostapd_eid_owe_trans_len(hapd);
@@ -1645,7 +1783,20 @@
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
 
-	tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+	if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+		if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+			os_free(head);
+			os_free(tail);
+			wpa_printf(MSG_ERROR,
+				   "MBSSID: Failed to set beacon data");
+			return -1;
+		}
+		complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+			(hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			 params->mbssid_elem_count == 1);
+	}
+
+	tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
 
 	/*
 	 * TODO: Time Advertisement element should only be included in some
@@ -1685,6 +1836,8 @@
 	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+					    params->mbssid_elem_count);
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
@@ -1767,7 +1920,6 @@
 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
 #endif /* NEED_AP_MLME */
 
-	os_memset(params, 0, sizeof(*params));
 	params->head = (u8 *) head;
 	params->head_len = head_len;
 	params->tail = tail;
@@ -1870,6 +2022,10 @@
 	params->head = NULL;
 	os_free(params->proberesp);
 	params->proberesp = NULL;
+	os_free(params->mbssid_elem);
+	params->mbssid_elem = NULL;
+	os_free(params->mbssid_elem_offset);
+	params->mbssid_elem_offset = NULL;
 #ifdef CONFIG_FILS
 	os_free(params->fd_frame_tmpl);
 	params->fd_frame_tmpl = NULL;
@@ -1945,6 +2101,10 @@
 	params.fd_frame_tmpl = hostapd_fils_discovery(hapd, &params);
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_IEEE80211BE
+	params.punct_bitmap = iconf->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (cmode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
 				    iconf->channel, iconf->enable_edmg,
diff --git a/src/ap/comeback_token.c b/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+			addr, ETH_ALEN, hash) < 0)
+		return -1;
+	*idx = hash[0];
+	return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len)
+{
+	u8 mac[SHA256_MAC_LEN];
+	const u8 *addrs[2];
+	size_t len[2];
+	u16 token_idx;
+	u8 idx;
+
+	if (token_len != SHA256_MAC_LEN ||
+	    comeback_token_hash(comeback_key, addr, &idx) < 0)
+		return -1;
+	token_idx = comeback_pending_idx[idx];
+	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+		wpa_printf(MSG_DEBUG,
+			   "Comeback: Invalid anti-clogging token from "
+			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
+			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+		return -1;
+	}
+
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = token;
+	len[1] = 2;
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, mac) < 0 ||
+	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+		return -1;
+
+	comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+	return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_reltime now;
+	u8 idx[2];
+	const u8 *addrs[2];
+	size_t len[2];
+	u8 p_idx;
+	u16 token_idx;
+
+	os_get_reltime(&now);
+	if (!os_reltime_initialized(last_comeback_key_update) ||
+	    os_reltime_expired(&now, last_comeback_key_update, 60) ||
+	    comeback_idx == 0xffff) {
+		if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+			    comeback_key, COMEBACK_KEY_SIZE);
+		*last_comeback_key_update = now;
+		comeback_idx = 0;
+		os_memset(comeback_pending_idx, 0, idx_len);
+	}
+
+	buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	if (group)
+		wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+	if (h2e) {
+		/* Encapsulate Anti-clogging Token field in a container IE */
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+	}
+
+	if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	token_idx = comeback_pending_idx[p_idx];
+	if (!token_idx) {
+		comeback_idx++;
+		token_idx = comeback_idx;
+		comeback_pending_idx[p_idx] = token_idx;
+	}
+	WPA_PUT_BE16(idx, token_idx);
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = idx;
+	len[1] = sizeof(idx);
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, token) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	WPA_PUT_BE16(token, token_idx);
+
+	return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/src/ap/comeback_token.h b/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 29b41f5..6934a73 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -99,7 +99,7 @@
 	len += ret;
 
 	ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
-			  data.current_rx_rate);
+			  data.current_rx_rate / 100);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -131,7 +131,7 @@
 		len += ret;
 
 	ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
-			  data.current_tx_rate);
+			  data.current_tx_rate / 100);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -206,9 +206,29 @@
 		return "REMOVE";
 	case STA_DISASSOC_FROM_CLI:
 		return "DISASSOC_FROM_CLI";
+	default:
+		return "?";
 	}
+}
 
-	return "?";
+
+static const char * hw_mode_str(enum hostapd_hw_mode mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return "b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "g";
+	case HOSTAPD_MODE_IEEE80211A:
+		return "a";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "ad";
+	case HOSTAPD_MODE_IEEE80211ANY:
+		return "any";
+	case NUM_HOSTAPD_MODES:
+		return "invalid";
+	}
+	return "unknown";
 }
 
 
@@ -218,6 +238,7 @@
 {
 	int len, res, ret, i;
 	const char *keyid;
+	const u8 *dpp_pkhash;
 
 	if (!sta)
 		return 0;
@@ -386,6 +407,18 @@
 			len += ret;
 	}
 
+	dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+	if (dpp_pkhash) {
+		ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+		len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+					SHA256_MAC_LEN);
+		ret = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
 	return len;
 }
 
@@ -683,6 +716,7 @@
 {
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_hw_modes *mode = iface->current_mode;
+	struct hostapd_config *iconf = hapd->iconf;
 	int len = 0, ret, j;
 	size_t i;
 
@@ -717,6 +751,24 @@
 		return len;
 	len += ret;
 
+	if (mode) {
+		ret = os_snprintf(buf + len, buflen - len, "hw_mode=%s\n",
+				  hw_mode_str(mode->mode));
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iconf->country[0] && iconf->country[1]) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "country_code=%c%c\ncountry3=0x%X\n",
+				  iconf->country[0], iconf->country[1],
+				  iconf->country[2]);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
 	if (!iface->cac_started || !iface->dfs_cac_ms) {
 		ret = os_snprintf(buf + len, buflen - len,
 				  "cac_time_seconds=%d\n"
@@ -928,16 +980,27 @@
 		} \
 	} while (0)
 
+#define SET_CSA_SETTING_EXT(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			settings->str = atoi(pos2); \
+		} \
+	} while (0)
+
 	SET_CSA_SETTING(center_freq1);
 	SET_CSA_SETTING(center_freq2);
 	SET_CSA_SETTING(bandwidth);
 	SET_CSA_SETTING(sec_channel_offset);
+	SET_CSA_SETTING_EXT(punct_bitmap);
 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
 	settings->block_tx = !!os_strstr(pos, " blocktx");
 #undef SET_CSA_SETTING
+#undef SET_CSA_SETTING_EXT
 
 	return 0;
 }
@@ -1060,7 +1123,8 @@
 	if (sscanf(pos, "%d", &expiration) != 1)
 		return NULL;
 
-	return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
+	return wpa_auth_pmksa_create_entry(aa, spa, pmk, PMK_LEN,
+					   WPA_KEY_MGMT_SAE, pmkid, expiration);
 }
 
 #endif /* CONFIG_MESH */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index e46dd7e..e8c5ec9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -50,15 +50,15 @@
 
 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
 		switch (hostapd_get_oper_chwidth(iface->conf)) {
-		case CHANWIDTH_USE_HT:
+		case CONF_OPER_CHWIDTH_USE_HT:
 			break;
-		case CHANWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
 			n_chans = 4;
 			break;
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			n_chans = 8;
 			break;
-		case CHANWIDTH_80P80MHZ:
+		case CONF_OPER_CHWIDTH_80P80MHZ:
 			n_chans = 4;
 			*seg1 = 4;
 			break;
@@ -281,6 +281,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		if (ret_chan && idx == channel_idx) {
 			wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
 				   chan->freq, chan->chan);
@@ -311,7 +315,7 @@
 	*oper_centr_freq_seg1_idx = 0;
 
 	switch (hostapd_get_oper_chwidth(iface->conf)) {
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (secondary_channel == 1)
 			*oper_centr_freq_seg0_idx = chan->chan + 2;
 		else if (secondary_channel == -1)
@@ -319,13 +323,13 @@
 		else
 			*oper_centr_freq_seg0_idx = chan->chan;
 		break;
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		*oper_centr_freq_seg0_idx = chan->chan + 6;
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		*oper_centr_freq_seg0_idx = chan->chan + 14;
 		break;
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		*oper_centr_freq_seg0_idx = chan->chan + 6;
 		*oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6;
 		break;
@@ -361,17 +365,17 @@
 	/* VHT/HE */
 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
 		switch (hostapd_get_oper_chwidth(iface->conf)) {
-		case CHANWIDTH_USE_HT:
+		case CONF_OPER_CHWIDTH_USE_HT:
 			break;
-		case CHANWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
 				iface->conf) - 6;
 			break;
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
 				iface->conf) - 14;
 			break;
-		case CHANWIDTH_80P80MHZ:
+		case CONF_OPER_CHWIDTH_80P80MHZ:
 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
 				iface->conf) - 6;
 			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
@@ -441,6 +445,8 @@
 	mode = iface->current_mode;
 
 	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
 		channel = &mode->channels[start_chan_idx + i];
 		if (channel->flag & HOSTAPD_CHAN_RADAR)
 			res++;
@@ -555,7 +561,8 @@
 		*secondary_channel = 0;
 
 	/* Get secondary channel for HT80P80 */
-	if (hostapd_get_oper_chwidth(iface->conf) == CHANWIDTH_80P80MHZ) {
+	if (hostapd_get_oper_chwidth(iface->conf) ==
+	    CONF_OPER_CHWIDTH_80P80MHZ) {
 		if (num_available_chandefs <= 1) {
 			wpa_printf(MSG_ERROR,
 				   "only 1 valid chan, can't support 80+80");
@@ -792,6 +799,8 @@
 	mode = iface->current_mode;
 
 	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
 		channel = &mode->channels[start_chan_idx + i];
 		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
 			continue;
@@ -1220,7 +1229,7 @@
 			int oper_chwidth;
 
 			oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-			if (oper_chwidth == CHANWIDTH_USE_HT)
+			if (oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
 				break;
 			*channel_type = DFS_AVAILABLE;
 			hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index d4cbed8..70dd18e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -15,11 +15,13 @@
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/random.h"
 #include "hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_query_ap.h"
 #include "gas_serv.h"
 #include "wpa_auth.h"
+#include "beacon.h"
 #include "dpp_hostapd.h"
 
 
@@ -278,19 +280,37 @@
 }
 
 
+static void hostapd_dpp_pkex_clear_code(struct hostapd_data *hapd)
+{
+	if (!hapd->dpp_pkex_code && !hapd->dpp_pkex_identifier)
+		return;
+
+	/* Delete PKEX code and identifier on successful completion of
+	 * PKEX. We are not supposed to reuse these without being
+	 * explicitly requested to perform PKEX again. */
+	wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+}
+
+
 #ifdef CONFIG_DPP2
 static int hostapd_dpp_pkex_done(void *ctx, void *conn,
 				 struct dpp_bootstrap_info *peer_bi)
 {
 	struct hostapd_data *hapd = ctx;
-	const char *cmd = hapd->dpp_pkex_auth_cmd;
+	char cmd[500];
 	const char *pos;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	struct dpp_bootstrap_info *own_bi = NULL;
 	struct dpp_authentication *auth;
 
-	if (!cmd)
-		cmd = "";
+	hostapd_dpp_pkex_clear_code(hapd);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
 	wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
 		   cmd);
 
@@ -342,6 +362,9 @@
 
 	return dpp_tcp_auth(hapd->iface->interfaces->dpp, conn, auth,
 			    hapd->conf->dpp_name, DPP_NETROLE_AP,
+			    hapd->conf->dpp_mud_url,
+			    hapd->conf->dpp_extra_conf_req_name,
+			    hapd->conf->dpp_extra_conf_req_value,
 			    hostapd_dpp_process_conf_obj, NULL);
 }
 #endif /* CONFIG_DPP2 */
@@ -362,7 +385,7 @@
 	hapd->dpp_pkex = NULL;
 	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
 			     hapd->dpp_pkex_identifier,
-			     hapd->dpp_pkex_code, v2);
+			     hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2);
 	if (!pkex)
 		return -1;
 	pkex->forced_ver = ver != PKEX_VER_AUTO;
@@ -832,7 +855,17 @@
 	}
 
 	addr = get_param(cmd, " tcp_addr=");
-	if (addr) {
+	if (addr && os_strcmp(addr, "from-uri") == 0) {
+		os_free(addr);
+		if (!peer_bi->host) {
+			wpa_printf(MSG_INFO,
+				   "DPP: TCP address not available in peer URI");
+			return -1;
+		}
+		tcp = 1;
+		os_memcpy(&ipaddr, peer_bi->host, sizeof(ipaddr));
+		tcp_port = peer_bi->port;
+	} else if (addr) {
 		int res;
 
 		res = hostapd_parse_ip_addr(addr, &ipaddr);
@@ -917,7 +950,10 @@
 	if (tcp)
 		return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
 				    &ipaddr, tcp_port, hapd->conf->dpp_name,
-				    DPP_NETROLE_AP, hapd->msg_ctx, hapd,
+				    DPP_NETROLE_AP, hapd->conf->dpp_mud_url,
+				    hapd->conf->dpp_extra_conf_req_name,
+				    hapd->conf->dpp_extra_conf_req_value,
+				    hapd->msg_ctx, hapd,
 				    hostapd_dpp_process_conf_obj, NULL);
 #endif /* CONFIG_DPP2 */
 
@@ -965,6 +1001,27 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static void
+hostapd_dpp_relay_needs_controller(struct hostapd_data *hapd, const u8 *src,
+				   enum dpp_public_action_frame_type type)
+{
+	struct os_reltime now;
+
+	if (!hapd->conf->dpp_relay_port)
+		return;
+
+	os_get_reltime(&now);
+	if (hapd->dpp_relay_last_needs_ctrl.sec &&
+	    !os_reltime_expired(&now, &hapd->dpp_relay_last_needs_ctrl, 60))
+		return;
+	hapd->dpp_relay_last_needs_ctrl = now;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RELAY_NEEDS_CONTROLLER
+		MACSTR " %u", MAC2STR(src), type);
+}
+#endif /* CONFIG_DPP2 */
+
+
 static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
 				    const u8 *hdr, const u8 *buf, size_t len,
 				    unsigned int freq)
@@ -1013,6 +1070,8 @@
 					src, hdr, buf, len, freq, i_bootstrap,
 					r_bootstrap, hapd) == 0)
 			return;
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_AUTHENTICATION_REQ);
 	}
 #endif /* CONFIG_DPP2 */
 	if (!own_bi) {
@@ -1021,6 +1080,21 @@
 		return;
 	}
 
+	if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"No matching peer bootstrapping key found for PKEX - ignore message");
+			return;
+		}
+
+		if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"Mismatching peer PKEX bootstrapping key - ignore message");
+			return;
+		}
+	}
+
 	if (hapd->dpp_auth) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Already in DPP authentication exchange - ignore new one");
@@ -1276,7 +1350,9 @@
 
 	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
 					DPP_NETROLE_AP,
-					hapd->conf->dpp_mud_url, NULL);
+					hapd->conf->dpp_mud_url, NULL,
+					hapd->conf->dpp_extra_conf_req_name,
+					hapd->conf->dpp_extra_conf_req_value);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -1302,8 +1378,7 @@
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
-		initiator);
+	dpp_notify_auth_success(hapd->dpp_auth, initiator);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
 		wpa_printf(MSG_INFO,
@@ -1440,11 +1515,53 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static bool hostapd_dpp_pb_active(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	return ifaces && (ifaces->dpp_pb_time.sec ||
+			  ifaces->dpp_pb_time.usec);
+}
+
+
+static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	int i;
+
+	if (!ifaces->dpp_pb_bi)
+		return;
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		struct dpp_pb_info *info = &ifaces->dpp_pb[i];
+
+		if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+			continue;
+		if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash,
+			      SHA256_MAC_LEN) == 0) {
+			/* Allow a new push button session to be established
+			 * immediately without the successfully completed
+			 * session triggering session overlap. */
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+		}
+	}
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
 				       const u8 *hdr, const u8 *buf, size_t len)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	enum dpp_status_error status;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
 
 	wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
 		   MAC2STR(src));
@@ -1465,7 +1582,8 @@
 
 	if (status == DPP_STATUS_OK && auth->send_conn_status) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+			DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+			auth->conf_resp_status);
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
 		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
 				     hapd, NULL);
@@ -1481,13 +1599,28 @@
 	hostapd_drv_send_action_cancel_wait(hapd);
 	hostapd_dpp_listen_stop(hapd);
 	if (status == DPP_STATUS_OK)
-		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
 	else
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
 	dpp_auth_deinit(auth);
 	hapd->dpp_auth = NULL;
 	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
 			     NULL);
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (status == DPP_STATUS_OK)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"no-configuration-available");
+		ifaces->dpp_pb_result_indicated = true;
+		if (status == DPP_STATUS_OK)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -1559,6 +1692,8 @@
 			return;
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No matching bootstrapping information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_PRESENCE_ANNOUNCEMENT);
 		return;
 	}
 
@@ -1649,6 +1784,8 @@
 			return;
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No matching Configurator information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_RECONFIG_ANNOUNCEMENT);
 		return;
 	}
 
@@ -1883,6 +2020,25 @@
 }
 
 
+static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
+{
+	if (!hapd->wpa_auth ||
+	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+		return false;
+	}
+
+	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+	    !hapd->conf->dpp_csign) {
+		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+		return false;
+	}
+
+	return true;
+}
+
+
 static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 					 const u8 *src,
 					 const u8 *buf, size_t len,
@@ -1895,21 +2051,14 @@
 	os_time_t expire;
 	int expiration;
 	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
+
+	os_memset(&intro, 0, sizeof(intro));
 
 	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
 		   MAC2STR(src));
-	if (!hapd->wpa_auth ||
-	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
-	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
-		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+	if (!hapd_dpp_connector_available(hapd))
 		return;
-	}
-
-	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
-	    !hapd->conf->dpp_csign) {
-		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
-		return;
-	}
 
 	os_get_time(&now);
 
@@ -1939,12 +2088,12 @@
 			     wpabuf_len(hapd->conf->dpp_netaccesskey),
 			     wpabuf_head(hapd->conf->dpp_csign),
 			     wpabuf_len(hapd->conf->dpp_csign),
-			     connector, connector_len, &expire);
+			     connector, connector_len, &expire, pkhash);
 	if (res == 255) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in internal failure (peer "
 			   MACSTR ")", MAC2STR(src));
-		return;
+		goto done;
 	}
 	if (res != DPP_STATUS_OK) {
 		wpa_printf(MSG_INFO,
@@ -1952,7 +2101,7 @@
 			   MACSTR " status %d)", MAC2STR(src), res);
 		hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 						res);
-		return;
+		goto done;
 	}
 
 #ifdef CONFIG_DPP3
@@ -1972,7 +2121,7 @@
 			hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
 							trans_id[0],
 							DPP_STATUS_NO_MATCH);
-			return;
+			goto done;
 		}
 	}
 #endif /* CONFIG_DPP3 */
@@ -1984,15 +2133,17 @@
 	else
 		expiration = 0;
 
-	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
-		return;
+		goto done;
 	}
 
 	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 					DPP_STATUS_OK);
+done:
+	dpp_peer_intro_deinit(&intro);
 }
 
 
@@ -2026,6 +2177,15 @@
 		goto try_relay;
 	}
 
+#ifdef CONFIG_DPP2
+	if (dpp_controller_is_own_pkex_req(hapd->iface->interfaces->dpp,
+					   buf, len)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX Exchange Request is from local Controller - ignore request");
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (hapd->dpp_pkex) {
 		/* TODO: Support parallel operations */
 		wpa_printf(MSG_DEBUG,
@@ -2038,6 +2198,7 @@
 						  hapd->own_addr, src,
 						  hapd->dpp_pkex_identifier,
 						  hapd->dpp_pkex_code,
+						  hapd->dpp_pkex_code_len,
 						  buf, len, v2);
 	if (!hapd->dpp_pkex) {
 		wpa_printf(MSG_DEBUG,
@@ -2064,9 +2225,14 @@
 
 try_relay:
 #ifdef CONFIG_DPP2
-	if (v2)
-		dpp_relay_rx_action(hapd->iface->interfaces->dpp,
-				    src, hdr, buf, len, freq, NULL, NULL, hapd);
+	if (v2 && dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+				      src, hdr, buf, len, freq, NULL, NULL,
+				      hapd) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No Relay available for the message");
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_PKEX_EXCHANGE_REQ);
+	}
 #else /* CONFIG_DPP2 */
 	wpa_printf(MSG_DEBUG, "DPP: No relay functionality included - skip");
 #endif /* CONFIG_DPP2 */
@@ -2153,6 +2319,7 @@
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
 
+	hostapd_dpp_pkex_clear_code(hapd);
 	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
@@ -2165,6 +2332,7 @@
 				       const u8 *hdr, const u8 *buf, size_t len,
 				       unsigned int freq)
 {
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
 	int res;
 	struct dpp_bootstrap_info *bi;
 	struct dpp_pkex *pkex = hapd->dpp_pkex;
@@ -2184,11 +2352,34 @@
 		return;
 	}
 
-	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
+	hostapd_dpp_pkex_clear_code(hapd);
+	bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
 	hapd->dpp_pkex = NULL;
 
+#ifdef CONFIG_DPP3
+	if (ifaces->dpp_pb_bi &&
+	    os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+			    ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
 		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
@@ -2203,6 +2394,535 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd,
+				     unsigned int freq, const u8 *src,
+				     const u8 *r_hash)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
+	char cmd[300];
+	const char *password = NULL;
+	struct sae_password_entry *e;
+	int conf_id = -1;
+	bool sae = false, psk = false;
+	size_t len;
+
+	if (hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "PDP: Sending previously generated PKEX Exchange Request to "
+			   MACSTR, MAC2STR(src));
+		msg = hapd->dpp_pkex->exchange_req;
+		hostapd_drv_send_action(hapd, freq, 0, src,
+					wpabuf_head(msg), wpabuf_len(msg));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+		   MACSTR, MAC2STR(src));
+
+	hapd->dpp_pkex_bi = ifaces->dpp_pb_bi;
+	os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+			     "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce,
+			     ifaces->dpp_pb_bi->curve->nonce_len,
+			     true);
+	if (!pkex) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	pkex->freq = freq;
+
+	hapd->dpp_pkex = pkex;
+	msg = hapd->dpp_pkex->exchange_req;
+
+	if (ifaces->dpp_pb_cmd) {
+		/* Use the externally provided configuration */
+		os_free(hapd->dpp_pkex_auth_cmd);
+		len = 30 + os_strlen(ifaces->dpp_pb_cmd);
+		hapd->dpp_pkex_auth_cmd = os_malloc(len);
+		if (!hapd->dpp_pkex_auth_cmd) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, ifaces->dpp_pb_cmd);
+		goto send_frame;
+	}
+
+	/* Build config based on the current AP configuration */
+	wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+			 (const u8 *) hapd->conf->ssid.ssid,
+			 hapd->conf->ssid.ssid_len);
+
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		/* TODO: If a local Configurator has been enabled, allow a
+		 * DPP AKM credential to be provisioned by setting conf_id. */
+	}
+
+	if (hapd->conf->wpa & WPA_PROTO_RSN) {
+		psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+						  WPA_KEY_MGMT_PSK_SHA256);
+#ifdef CONFIG_SAE
+		sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE;
+#endif /* CONFIG_SAE */
+	}
+
+#ifdef CONFIG_SAE
+	for (e = hapd->conf->sae_passwords; sae && e && !password;
+	     e = e->next) {
+		if (e->identifier || !is_broadcast_ether_addr(e->peer_addr))
+			continue;
+		password = e->password;
+	}
+#endif /* CONFIG_SAE */
+	if (!password && hapd->conf->ssid.wpa_passphrase_set &&
+	    hapd->conf->ssid.wpa_passphrase)
+		password = hapd->conf->ssid.wpa_passphrase;
+	if (password) {
+		len = 2 * os_strlen(password) + 1;
+		pass_hex = os_malloc(len);
+		if (!pass_hex) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		wpa_snprintf_hex(pass_hex, len, (const u8 *) password,
+				 os_strlen(password));
+	}
+
+	if (conf_id > 0 && sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0 && sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp configurator=%d ssid=%s",
+			    conf_id, ssid_hex);
+	} if (sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk+sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported AP configuration for push button");
+		str_clear_free(pass_hex);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	str_clear_free(pass_hex);
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	len = 30 + os_strlen(cmd);
+	hapd->dpp_pkex_auth_cmd = os_malloc(len);
+	if (hapd->dpp_pkex_auth_cmd)
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, cmd);
+	forced_memzero(cmd, sizeof(cmd));
+	if (!hapd->dpp_pkex_auth_cmd) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+send_frame:
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	pkex->exch_req_wait_time = 2000;
+	pkex->exch_req_tries = 1;
+}
+
+
+static void
+hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
+					const u8 *src, const u8 *hdr,
+					const u8 *buf, size_t len,
+					unsigned int freq)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	const u8 *r_hash;
+	u16 r_hash_len;
+	unsigned int i;
+	bool found = false;
+	struct dpp_pb_info *info, *tmp;
+	struct os_reltime now, age;
+	struct wpabuf *msg;
+
+	if (!ifaces)
+		return;
+
+	os_get_reltime(&now);
+	wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+		   MACSTR, MAC2STR(src));
+
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		info = &ifaces->dpp_pb[i];
+		if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+		    os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Active push button Enrollee already known");
+		found = true;
+		info->rx_time = now;
+	}
+
+	if (!found) {
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+				continue;
+
+			if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+				wpa_hexdump(MSG_DEBUG,
+					    "DPP: Push button Enrollee hash expired",
+					    tmp->hash, SHA256_MAC_LEN);
+				tmp->rx_time.sec = 0;
+				tmp->rx_time.usec = 0;
+				continue;
+			}
+
+			wpa_hexdump(MSG_DEBUG,
+				    "DPP: Push button session overlap with hash",
+				    tmp->hash, SHA256_MAC_LEN);
+			if (!ifaces->dpp_pb_result_indicated &&
+			    hostapd_dpp_pb_active(hapd)) {
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_PB_RESULT "session-overlap");
+				ifaces->dpp_pb_result_indicated = true;
+			}
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+
+		/* Replace the oldest entry */
+		info = &ifaces->dpp_pb[0];
+		for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+				info = tmp;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+		os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+		info->rx_time = now;
+	}
+
+	if (!hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Discard message since own push button has not been pressed");
+		return;
+	}
+
+	if (ifaces->dpp_pb_announce_time.sec == 0 &&
+	    ifaces->dpp_pb_announce_time.usec == 0) {
+		/* Start a wait before allowing PKEX to be initiated */
+		ifaces->dpp_pb_announce_time = now;
+	}
+
+	if (!ifaces->dpp_pb_bi) {
+		int res;
+
+		res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex");
+		if (res < 0)
+			return;
+		ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res);
+		if (!ifaces->dpp_pb_bi)
+			return;
+
+		if (random_get_bytes(ifaces->dpp_pb_c_nonce,
+				     ifaces->dpp_pb_bi->curve->nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate C-nonce");
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+	}
+
+	/* Skip the response if one was sent within last 50 ms since the
+	 * Enrollee is going to send out at least three announcement messages.
+	 */
+	os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age);
+	if (age.sec == 0 && age.usec < 50000) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+		return;
+	}
+
+	msg = dpp_build_pb_announcement_resp(
+		ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce,
+		ifaces->dpp_pb_bi->curve->nonce_len);
+	if (!msg) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send Push Button Presence Announcement Response to "
+		   MACSTR, MAC2STR(src));
+	ifaces->dpp_pb_last_resp = now;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15))
+		hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *trans_id, *version;
+	u16 trans_id_len, version_len;
+	struct wpabuf *msg;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
+		   trans_id[0], version[0]);
+
+	len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
+	if (!msg)
+		return;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id[0]);
+
+	/* Protocol Version */
+	conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, ver);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+	wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
+		   MACSTR, MAC2STR(src));
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PRIV_PEER_INTRO_NOTIFY);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct crypto_ec_key *own_key;
+	const struct dpp_curve_params *curve;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
+	const u8 *aad = hdr;
+	size_t aad_len = DPP_HDR_LEN;
+	struct wpabuf *pt;
+	const u8 *trans_id, *wrapped, *version, *connector;
+	u16 trans_id_len, wrapped_len, version_len, connector_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+			       &wrapped_len);
+	if (!wrapped) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
+		return;
+	}
+
+	own_key = dpp_set_keypair(&curve,
+				  wpabuf_head(hapd->conf->dpp_netaccesskey),
+				  wpabuf_len(hapd->conf->dpp_netaccesskey));
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		return;
+	}
+
+	if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
+			   curve->ike_group);
+		crypto_ec_key_deinit(own_key);
+		return;
+	}
+
+	pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
+			    aad, aad_len, wrapped, wrapped_len);
+	crypto_ec_key_deinit(own_key);
+	if (!pt) {
+		wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
+		return;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
+
+	connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+				 DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		goto done;
+	}
+
+	version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+			       DPP_ATTR_PROTOCOL_VERSION, &version_len);
+	if (!version || version_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		goto done;
+	}
+
+	res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			     wpabuf_head(hapd->conf->dpp_netaccesskey),
+			     wpabuf_len(hapd->conf->dpp_netaccesskey),
+			     wpabuf_head(hapd->conf->dpp_csign),
+			     wpabuf_len(hapd->conf->dpp_csign),
+			     connector, connector_len, &expire, pkhash);
+	if (res == 255) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in internal failure (peer "
+			   MACSTR ")", MAC2STR(src));
+		goto done;
+	}
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure (peer "
+			   MACSTR " status %d)", MAC2STR(src), res);
+		goto done;
+	}
+
+	if (intro.peer_version && intro.peer_version >= 2) {
+		u8 attr_version = 1;
+
+		if (version && version_len >= 1)
+			attr_version = version[0];
+		if (attr_version != intro.peer_version) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+				   intro.peer_version, attr_version);
+			goto done;
+		}
+	}
+
+	if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		goto done;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
+		   MACSTR, MAC2STR(src));
+
+done:
+	dpp_peer_intro_deinit(&intro);
+	wpabuf_free(pt);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
 			   const u8 *buf, size_t len, unsigned int freq)
 {
@@ -2308,6 +3028,20 @@
 						  freq);
 		break;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+		hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
+							buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_QUERY:
+		hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
+						     buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_UPDATE:
+		hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
+						      buf, len, freq);
+		break;
+#endif /* CONFIG_DPP3 */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignored unsupported frame subtype %d", type);
@@ -2356,8 +3090,7 @@
 		 * from TX status handler, but since there was no such handler
 		 * call yet, simply send out the event message and proceed with
 		 * exchange. */
-		wpa_msg(hapd->msg_ctx, MSG_INFO,
-			DPP_EVENT_AUTH_SUCCESS "init=1");
+		dpp_notify_auth_success(hapd->dpp_auth, 1);
 		hapd->dpp_auth_ok_on_ack = 0;
 	}
 
@@ -2376,6 +3109,9 @@
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
 
 	if (!auth)
 		return;
@@ -2410,11 +3146,26 @@
 	hostapd_drv_send_action_cancel_wait(hapd);
 
 	if (ok)
-		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
 	else
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (ok)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"could-not-connect");
+		ifaces->dpp_pb_result_indicated = true;
+		if (ok)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2516,6 +3267,7 @@
 	hapd->dpp_pkex_code = os_strdup(pos + 6);
 	if (!hapd->dpp_pkex_code)
 		return -1;
+	hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code);
 
 	pos = os_strstr(cmd, " ver=");
 	if (pos) {
@@ -2564,7 +3316,7 @@
 			return -1;
 	}
 
-	if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+	if ((id_val != 0 && id_val != 1))
 		return -1;
 
 	/* TODO: Support multiple PKEX entries */
@@ -2588,6 +3340,9 @@
 	hapd->dpp_auth = NULL;
 	dpp_pkex_free(hapd->dpp_pkex);
 	hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+	hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2599,6 +3354,9 @@
 	struct hostapd_data *hapd = ctx;
 	u8 *buf;
 
+	if (freq == 0)
+		freq = hapd->iface->freq;
+
 	wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
 		   MAC2STR(addr), freq);
 	buf = os_malloc(2 + len);
@@ -2618,7 +3376,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 
-	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
 }
 
 #endif /* CONFIG_DPP2 */
@@ -2631,6 +3389,7 @@
 	struct dpp_relay_config config;
 
 	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
 	config.cb_ctx = hapd;
 	config.tx = hostapd_dpp_relay_tx;
 	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
@@ -2641,12 +3400,76 @@
 					     &config) < 0)
 			return -1;
 	}
+
+	if (hapd->conf->dpp_relay_port)
+		dpp_relay_listen(hapd->iface->interfaces->dpp,
+				 hapd->conf->dpp_relay_port,
+				 &config);
 #endif /* CONFIG_DPP2 */
 
 	return 0;
 }
 
 
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_relay_config config;
+	struct hostapd_ip_addr addr;
+	u8 pkhash[SHA256_MAC_LEN];
+	char *pos, *tmp;
+	int ret = -1;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	tmp = os_strdup(cmd);
+	if (!tmp)
+		goto fail;
+	pos = os_strchr(tmp, ' ');
+	if (!pos)
+		goto fail;
+	*pos++ = '\0';
+	if (hostapd_parse_ip_addr(tmp, &addr) < 0 ||
+	    hexstr2bin(pos, pkhash, SHA256_MAC_LEN) < 0)
+		goto fail;
+
+	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	config.ipaddr = &addr;
+	config.pkhash = pkhash;
+	prev_state = dpp_relay_controller_available(dpp);
+	ret = dpp_relay_add_controller(dpp, &config);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+fail:
+	os_free(tmp);
+	return ret;
+}
+
+
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hostapd_ip_addr addr;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	if (hostapd_parse_ip_addr(cmd, &addr) < 0)
+		return;
+	prev_state = dpp_relay_controller_available(dpp);
+	dpp_relay_remove_controller(dpp, &addr);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 int hostapd_dpp_init(struct hostapd_data *hapd)
 {
 	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
@@ -2681,11 +3504,14 @@
 	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
 			     NULL);
 	hostapd_dpp_chirp_stop(hapd);
-	if (hapd->iface->interfaces)
+	if (hapd->iface->interfaces) {
+		dpp_relay_stop_listen(hapd->iface->interfaces->dpp);
 		dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
+	}
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_DPP3
 	eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+	hostapd_dpp_push_button_stop(hapd);
 #endif /* CONFIG_DPP3 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
@@ -2693,6 +3519,8 @@
 	hapd->dpp_pkex = NULL;
 	os_free(hapd->dpp_configurator_params);
 	hapd->dpp_configurator_params = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
 }
 
 
@@ -3065,3 +3893,84 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired");
+	hostapd_dpp_push_button_stop(hapd);
+}
+
+
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return -1;
+	os_get_reltime(&ifaces->dpp_pb_time);
+	ifaces->dpp_pb_announce_time.sec = 0;
+	ifaces->dpp_pb_announce_time.usec = 0;
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+	if (cmd) {
+		ifaces->dpp_pb_cmd = os_strdup(cmd);
+		if (!ifaces->dpp_pb_cmd)
+			return -1;
+	}
+	eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
+			       hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return;
+	eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
+	if (hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+		if (!ifaces->dpp_pb_result_indicated)
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_PB_RESULT "failed");
+	}
+	ifaces->dpp_pb_time.sec = 0;
+	ifaces->dpp_pb_time.usec = 0;
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+
+	if (ifaces->dpp_pb_bi) {
+		char id[20];
+
+		os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		ifaces->dpp_pb_bi = NULL;
+	}
+
+	ifaces->dpp_pb_result_indicated = false;
+
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+#ifdef CONFIG_DPP2
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd)
+{
+	return hapd->conf->dpp_configurator_connectivity ||
+		(hapd->iface->interfaces &&
+		 dpp_relay_controller_available(hapd->iface->interfaces->dpp));
+}
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 264d3e4..55f1fce 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -45,5 +45,10 @@
 int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
 void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
 
 #endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 643a273..8fc128e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -340,6 +340,16 @@
 		}
 #endif /* CONFIG_WPS */
 
+		if (check_sa_query_need(hapd, sta)) {
+			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+					  p - buf);
+			return 0;
+		}
+
 		if (sta->wpa_sm == NULL)
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 							sta->addr,
@@ -420,16 +430,6 @@
 			goto fail;
 		}
 
-		if (check_sa_query_need(hapd, sta)) {
-			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-
-			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
-
-			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
-					  p - buf);
-			return 0;
-		}
-
 		if (wpa_auth_uses_mfp(sta->wpa_sm))
 			sta->flags |= WLAN_STA_MFP;
 		else
@@ -451,7 +451,7 @@
 		}
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
-		if (hapd->conf->sae_pwe == 2 &&
+		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
 		    sta->sae && !sta->sae->h2e &&
 		    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -838,6 +838,9 @@
 	case CHAN_WIDTH_160:
 		txt = "160";
 		break;
+	case CHAN_WIDTH_320:
+		txt = "320";
+		break;
 	default:
 		txt = NULL;
 		break;
@@ -856,21 +859,23 @@
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2,
-			     int finished)
+			     u16 punct_bitmap, int finished)
 {
 #ifdef NEED_AP_MLME
-	int channel, chwidth, is_dfs;
+	int channel, chwidth, is_dfs0, is_dfs;
 	u8 seg0_idx = 0, seg1_idx = 0;
 	size_t i;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d, puncturing_bitmap=0x%x",
 		       finished ? "had" : "starting",
+		       hapd->iface->freq,
 		       freq, ht, hapd->iconf->ch_switch_vht_config,
 		       hapd->iconf->ch_switch_he_config,
 		       hapd->iconf->ch_switch_eht_config, offset,
-		       width, channel_width_to_string(width), cf1, cf2);
+		       width, channel_width_to_string(width), cf1, cf2,
+		       punct_bitmap);
 
 	if (!hapd->iface->current_mode) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
@@ -879,6 +884,8 @@
 		return;
 	}
 
+	/* Check if any of configured channels require DFS */
+	is_dfs0 = hostapd_is_dfs_required(hapd->iface);
 	hapd->iface->freq = freq;
 
 	channel = hostapd_hw_get_channel(hapd, freq);
@@ -891,19 +898,22 @@
 
 	switch (width) {
 	case CHAN_WIDTH_80:
-		chwidth = CHANWIDTH_80MHZ;
+		chwidth = CONF_OPER_CHWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		chwidth = CHANWIDTH_80P80MHZ;
+		chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		chwidth = CHANWIDTH_160MHZ;
+		chwidth = CONF_OPER_CHWIDTH_160MHZ;
+		break;
+	case CHAN_WIDTH_320:
+		chwidth = CONF_OPER_CHWIDTH_320MHZ;
 		break;
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
 	default:
-		chwidth = CHANWIDTH_USE_HT;
+		chwidth = CONF_OPER_CHWIDTH_USE_HT;
 		break;
 	}
 
@@ -977,12 +987,15 @@
 	hostapd_set_oper_chwidth(hapd->iconf, chwidth);
 	hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
 	hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+#ifdef CONFIG_IEEE80211BE
+	hapd->iconf->punct_bitmap = punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	if (hapd->iconf->ieee80211ac) {
 		hapd->iconf->vht_capab &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
-		if (chwidth == CHANWIDTH_160MHZ)
+		if (chwidth == CONF_OPER_CHWIDTH_160MHZ)
 			hapd->iconf->vht_capab |=
 				VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
-		else if (chwidth == CHANWIDTH_80P80MHZ)
+		else if (chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
 			hapd->iconf->vht_capab |=
 				VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 	}
@@ -991,11 +1004,11 @@
 				  hapd->iface->num_hw_features);
 
 	wpa_msg(hapd->msg_ctx, MSG_INFO,
-		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d puncturing_bitmap=0x%04x",
 		finished ? WPA_EVENT_CHANNEL_SWITCH :
 		WPA_EVENT_CHANNEL_SWITCH_STARTED,
 		freq, ht, offset, channel_width_to_string(width),
-		cf1, cf2, is_dfs);
+		cf1, cf2, is_dfs0, is_dfs, punct_bitmap);
 	if (!finished)
 		return;
 
@@ -1007,6 +1020,14 @@
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
 			"freq=%d dfs=%d", freq, is_dfs);
 	} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+		/* Complete AP configuration for the first bring up. */
+		if (is_dfs0 > 0 &&
+		    hostapd_is_dfs_required(hapd->iface) <= 0 &&
+		    hapd->iface->state != HAPD_IFACE_ENABLED) {
+			/* Fake a CAC start bit to skip setting channel */
+			hapd->iface->cac_started = 1;
+			hostapd_setup_interface_complete(hapd->iface, 0);
+		}
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
 			"freq=%d dfs=%d", freq, is_dfs);
 	} else if (is_dfs &&
@@ -1145,7 +1166,7 @@
 		/* set defaults for backwards compatibility */
 		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
 		hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
-		hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_USE_HT);
+		hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_USE_HT);
 		if (acs_res->ch_width == 40) {
 			if (is_6ghz_freq(acs_res->pri_freq))
 				hostapd_set_oper_centr_freq_seg0_idx(
@@ -1155,22 +1176,33 @@
 			hostapd_set_oper_centr_freq_seg0_idx(
 				hapd->iconf, acs_res->vht_seg0_center_ch);
 			if (acs_res->vht_seg1_center_ch == 0) {
-				hostapd_set_oper_chwidth(hapd->iconf,
-							 CHANWIDTH_80MHZ);
+				hostapd_set_oper_chwidth(
+					hapd->iconf, CONF_OPER_CHWIDTH_80MHZ);
 			} else {
-				hostapd_set_oper_chwidth(hapd->iconf,
-							 CHANWIDTH_80P80MHZ);
+				hostapd_set_oper_chwidth(
+					hapd->iconf,
+					CONF_OPER_CHWIDTH_80P80MHZ);
 				hostapd_set_oper_centr_freq_seg1_idx(
 					hapd->iconf,
 					acs_res->vht_seg1_center_ch);
 			}
 		} else if (acs_res->ch_width == 160) {
-			hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_160MHZ);
+			hostapd_set_oper_chwidth(hapd->iconf,
+						 CONF_OPER_CHWIDTH_160MHZ);
 			hostapd_set_oper_centr_freq_seg0_idx(
 				hapd->iconf, acs_res->vht_seg1_center_ch);
 		}
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iface->conf->ieee80211be && acs_res->ch_width == 320) {
+		hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_320MHZ);
+		hostapd_set_oper_centr_freq_seg0_idx(
+			hapd->iconf, acs_res->vht_seg1_center_ch);
+		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 out:
 	ret = hostapd_acs_completed(hapd->iface, err);
 	if (ret) {
@@ -1543,7 +1575,8 @@
 
 
 static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
-				   const u8 *data, size_t data_len)
+				   const u8 *data, size_t data_len,
+				   enum frame_encryption encrypted)
 {
 	struct hostapd_iface *iface = hapd->iface;
 	struct sta_info *sta;
@@ -1557,7 +1590,7 @@
 		}
 	}
 
-	ieee802_1x_receive(hapd, src, data, data_len);
+	ieee802_1x_receive(hapd, src, data, data_len, encrypted);
 }
 
 #endif /* HOSTAPD */
@@ -1952,7 +1985,8 @@
 	case EVENT_EAPOL_RX:
 		hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
 				       data->eapol_rx.data,
-				       data->eapol_rx.data_len);
+				       data->eapol_rx.data_len,
+				       data->eapol_rx.encrypted);
 		break;
 	case EVENT_ASSOC:
 		if (!data)
@@ -1997,6 +2031,7 @@
 					data->ch_switch.ch_width,
 					data->ch_switch.cf1,
 					data->ch_switch.cf2,
+					data->ch_switch.punct_bitmap,
 					event == EVENT_CH_SWITCH);
 		break;
 	case EVENT_CONNECT_FAILED_REASON:
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 0310aab..d64fb8c 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -530,9 +530,9 @@
 	switch (iph->ip_p) {
 	case 17:
 		return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
@@ -567,9 +567,9 @@
 	case ETH_P_IP:
 		return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
 					   end - pkt - 2);
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
index fdb3cad..3d94407 100644
--- a/src/ap/gas_query_ap.c
+++ b/src/ap/gas_query_ap.c
@@ -29,6 +29,8 @@
 #define GAS_QUERY_WAIT_TIME_INITIAL 1000
 #define GAS_QUERY_WAIT_TIME_COMEBACK 150
 
+#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
+
 /**
  * struct gas_query_pending - Pending GAS query
  */
@@ -545,6 +547,8 @@
 	if (pos + 2 > data + len)
 		return 0;
 	comeback_delay = WPA_GET_LE16(pos);
+	if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
+		comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
 	pos += 2;
 
 	/* Advertisement Protocol element */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 90f1577..4642e49 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1524,7 +1524,7 @@
 #ifdef CONFIG_DPP
 void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
 				 const u8 *sa, u8 dialog_token,
-				 int prot, struct wpabuf *buf)
+				 int prot, struct wpabuf *buf, int freq)
 {
 	struct wpabuf *tx_buf;
 
@@ -1582,7 +1582,7 @@
 		return;
 	if (prot)
 		convert_to_protected_dual(tx_buf);
-	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+	hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
 				wpabuf_head(tx_buf),
 				wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
@@ -1593,7 +1593,7 @@
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
 					const u8 *data, size_t len, int prot,
-					int std_addr3)
+					int std_addr3, int freq)
 {
 	const u8 *pos = data;
 	const u8 *end = data + len;
@@ -1688,7 +1688,8 @@
 						  data, len);
 		if (!msg)
 			return;
-		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+					    freq);
 		return;
 	}
 #endif /* CONFIG_DPP */
@@ -1871,7 +1872,7 @@
 	switch (data[0]) {
 	case WLAN_PA_GAS_INITIAL_REQ:
 		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
-					    std_addr3);
+					    std_addr3, freq);
 		break;
 	case WLAN_PA_GAS_COMEBACK_REQ:
 		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 1528af4..7646a98 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -90,6 +90,6 @@
 
 void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
 				 const u8 *sa, u8 dialog_token,
-				 int prot, struct wpabuf *buf);
+				 int prot, struct wpabuf *buf, int freq);
 
 #endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index ef53c41..8b3fb40 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -91,6 +91,29 @@
 }
 
 
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid)
+		return hapd->iface->bss[0];
+
+	return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid) {
+		size_t i;
+
+		for (i = 1; i < hapd->iface->num_bss; i++)
+			if (hapd->iface->bss[i] == hapd)
+				return i;
+	}
+
+	return 0;
+}
+
+
 void hostapd_reconfig_encryption(struct hostapd_data *hapd)
 {
 	if (hapd->wpa_auth)
@@ -172,27 +195,34 @@
 }
 
 
-static void hostapd_clear_old(struct hostapd_iface *iface)
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
 {
-	size_t j;
+	wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+		   bss->conf->iface);
 
 	/*
 	 * Deauthenticate all stations since the new configuration may not
 	 * allow them to use the BSS anymore.
 	 */
-	for (j = 0; j < iface->num_bss; j++) {
-		hostapd_flush_old_stations(iface->bss[j],
-					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
 #ifdef CONFIG_WEP
-		hostapd_broadcast_wep_clear(iface->bss[j]);
+	hostapd_broadcast_wep_clear(bss);
 #endif /* CONFIG_WEP */
 
 #ifndef CONFIG_NO_RADIUS
-		/* TODO: update dynamic data based on changed configuration
-		 * items (e.g., open/close sockets, etc.) */
-		radius_client_flush(iface->bss[j]->radius, 0);
+	/* TODO: update dynamic data based on changed configuration
+	 * items (e.g., open/close sockets, etc.) */
+	radius_client_flush(bss->radius, 0);
 #endif /* CONFIG_NO_RADIUS */
-	}
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_clear_old_bss(iface->bss[j]);
 }
 
 
@@ -236,13 +266,13 @@
 	if (newconf == NULL)
 		return -1;
 
-	hostapd_clear_old(iface);
-
 	oldconf = hapd->iconf;
 	if (hostapd_iface_conf_changed(newconf, oldconf)) {
 		char *fname;
 		int res;
 
+		hostapd_clear_old(iface);
+
 		wpa_printf(MSG_DEBUG,
 			   "Configuration changes include interface/BSS modification - force full disable+enable sequence");
 		fname = os_strdup(iface->config_fname);
@@ -272,6 +302,10 @@
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
+		if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+		    os_strcmp(hapd->conf->config_id,
+			      newconf->bss[j]->config_id) != 0)
+			hostapd_clear_old_bss(hapd);
 		hapd->iconf = newconf;
 		hapd->iconf->channel = oldconf->channel;
 		hapd->iconf->acs = oldconf->acs;
@@ -525,6 +559,7 @@
 	iface->current_rates = NULL;
 	os_free(iface->basic_rates);
 	iface->basic_rates = NULL;
+	iface->cac_started = 0;
 	ap_list_deinit(iface);
 	sta_track_deinit(iface);
 	airtime_policy_update_deinit(iface);
@@ -1104,18 +1139,55 @@
 #endif /* CONFIG_NO_RADIUS */
 
 
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+				bool flush_old_stations)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+		return -1;
+
+	if (flush_old_stations && !conf->start_disabled &&
+	    conf->broadcast_deauth) {
+		u8 addr[ETH_ALEN];
+
+		/* Should any previously associated STA not have noticed that
+		 * the AP had stopped and restarted, send one more
+		 * deauthentication notification now that the AP is ready to
+		 * operate. */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations at BSS start");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+
+	if (hapd->driver && hapd->driver->set_operstate)
+		hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+	return 0;
+}
+
+
 /**
  * hostapd_setup_bss - Per-BSS setup (initialization)
  * @hapd: Pointer to BSS data
  * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
  *	but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ *	transmission of Beaconf rames started at this time. This is used when
+ *	MBSSID element is enabled where the information regarding all BSSes
+ *	should be retrieved before configuring the Beacon frame template. The
+ *	calling functions are responsible for configuring the Beacon frame
+ *	explicitly if this is set to false.
  *
  * This function is used to initialize all per-BSS data structures and
  * resources. This gets called in a loop for each BSS when an interface is
  * initialized. Most of the modules that are initialized here will be
  * deinitialized in hostapd_cleanup().
  */
-static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+			     bool start_beacon)
 {
 	struct hostapd_bss_config *conf = hapd->conf;
 	u8 ssid[SSID_MAX_LEN + 1];
@@ -1238,7 +1310,8 @@
 	 * Short SSID calculation is identical to FCS and it is defined in
 	 * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
 	 */
-	conf->ssid.short_ssid = crc32(conf->ssid.ssid, conf->ssid.ssid_len);
+	conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
+						conf->ssid.ssid_len);
 
 	if (!hostapd_drv_none(hapd)) {
 		wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
@@ -1362,6 +1435,22 @@
 		return -1;
 	}
 
+	if (conf->bridge[0]) {
+		/* Set explicitly configured bridge parameters that might have
+		 * been lost if the interface has been removed out of the
+		 * bridge. */
+
+		/* multicast to unicast on bridge ports */
+		if (conf->bridge_multicast_to_unicast)
+			hostapd_drv_br_port_set_attr(
+				hapd, DRV_BR_PORT_ATTR_MCAST2UCAST, 1);
+
+		/* hairpin mode */
+		if (conf->bridge_hairpin)
+			hostapd_drv_br_port_set_attr(
+				hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 1);
+	}
+
 	if (conf->proxy_arp) {
 		if (x_snoop_init(hapd)) {
 			wpa_printf(MSG_ERROR,
@@ -1387,29 +1476,11 @@
 		return -1;
 	}
 
-	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
-		return -1;
-
-	if (flush_old_stations && !conf->start_disabled &&
-	    conf->broadcast_deauth) {
-		u8 addr[ETH_ALEN];
-
-		/* Should any previously associated STA not have noticed that
-		 * the AP had stopped and restarted, send one more
-		 * deauthentication notification now that the AP is ready to
-		 * operate. */
-		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
-			"Deauthenticate all stations at BSS start");
-		os_memset(addr, 0xff, ETH_ALEN);
-		hostapd_drv_sta_deauth(hapd, addr,
-				       WLAN_REASON_PREV_AUTH_NOT_VALID);
-	}
-
 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
 		return -1;
 
-	if (hapd->driver && hapd->driver->set_operstate)
-		hapd->driver->set_operstate(hapd->drv_priv, 1);
+	if (start_beacon)
+		return hostapd_start_beacon(hapd, flush_old_stations);
 
 	return 0;
 }
@@ -1694,7 +1765,7 @@
 	bw = center_idx_to_bw_6ghz(seg0);
 	/* Assign the secondary channel if absent in config for
 	 * bandwidths > 20 MHz */
-	if (bw > 20 && !iface->conf->secondary_channel) {
+	if (bw > 0 && !iface->conf->secondary_channel) {
 		if (((iface->conf->channel - 1) / 4) % 2)
 			iface->conf->secondary_channel = -1;
 		else
@@ -1713,12 +1784,17 @@
 	} else {
 		int ret;
 
+		if (iface->conf->acs) {
+			iface->freq = 0;
+			iface->conf->channel = 0;
+		}
+
 		ret = configured_fixed_chan_to_freq(iface);
 		if (ret < 0)
 			goto fail;
 
 		if (iface->conf->op_class) {
-			int ch_width;
+			enum oper_chan_width ch_width;
 
 			ch_width = op_class_to_ch_width(iface->conf->op_class);
 			hostapd_set_oper_chwidth(iface->conf, ch_width);
@@ -1784,6 +1860,16 @@
 }
 
 
+static int fst_hostapd_get_hw_modes_cb(void *ctx,
+				       struct hostapd_hw_modes **modes)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*modes = hapd->iface->hw_features;
+	return hapd->iface->num_hw_features;
+}
+
+
 static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
 {
 	struct hostapd_data *hapd = ctx;
@@ -1876,9 +1962,11 @@
 void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
 				struct fst_wpa_obj *iface_obj)
 {
+	os_memset(iface_obj, 0, sizeof(*iface_obj));
 	iface_obj->ctx = hapd;
 	iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
 	iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+	iface_obj->get_hw_modes = fst_hostapd_get_hw_modes_cb;
 	iface_obj->set_ies = fst_hostapd_set_ies_cb;
 	iface_obj->send_action = fst_hostapd_send_action_cb;
 	iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
@@ -2122,7 +2210,7 @@
 		hapd = iface->bss[j];
 		if (j)
 			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
-		if (hostapd_setup_bss(hapd, j == 0)) {
+		if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
 			for (;;) {
 				hapd = iface->bss[j];
 				hostapd_bss_deinit_no_free(hapd);
@@ -2136,6 +2224,24 @@
 		if (is_zero_ether_addr(hapd->conf->bssid))
 			prev_addr = hapd->own_addr;
 	}
+
+	if (hapd->iconf->mbssid) {
+		for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (hostapd_start_beacon(hapd, true)) {
+				for (;;) {
+					hapd = iface->bss[j];
+					hostapd_bss_deinit_no_free(hapd);
+					hostapd_free_hapd_data(hapd);
+					if (j == 0)
+						break;
+					j--;
+				}
+				goto fail;
+			}
+		}
+	}
+
 	hapd = iface->bss[0];
 
 	hostapd_tx_queue_params(iface);
@@ -2789,6 +2895,21 @@
 }
 
 
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+	wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+	hostapd_set_security_params(bss->conf, 1);
+	if (hostapd_config_check(bss->iconf, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+		return -1;
+	}
+	hostapd_clear_old_bss(bss);
+	hostapd_reload_bss(bss);
+	return 0;
+}
+
+
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
 	size_t j;
@@ -3017,7 +3138,7 @@
 
 			if (start_ctrl_iface_bss(hapd) < 0 ||
 			    (hapd_iface->state == HAPD_IFACE_ENABLED &&
-			     hostapd_setup_bss(hapd, -1))) {
+			     hostapd_setup_bss(hapd, -1, true))) {
 				hostapd_cleanup(hapd);
 				hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
 				hapd_iface->conf->num_bss--;
@@ -3491,16 +3612,21 @@
 	case 0:
 	case 20:
 	case 40:
-		hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_USE_HT);
 		break;
 	case 80:
 		if (params->center_freq2)
-			hostapd_set_oper_chwidth(conf, CHANWIDTH_80P80MHZ);
+			hostapd_set_oper_chwidth(conf,
+						 CONF_OPER_CHWIDTH_80P80MHZ);
 		else
-			hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
+			hostapd_set_oper_chwidth(conf,
+						 CONF_OPER_CHWIDTH_80MHZ);
 		break;
 	case 160:
-		hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_160MHZ);
+		break;
+	case 320:
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_320MHZ);
 		break;
 	default:
 		return -1;
@@ -3529,6 +3655,9 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
+#ifdef CONFIG_IEEE80211BE
+	u16 old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
@@ -3538,15 +3667,15 @@
 	switch (settings->freq_params.bandwidth) {
 	case 80:
 		if (settings->freq_params.center_freq2)
-			bandwidth = CHANWIDTH_80P80MHZ;
+			bandwidth = CONF_OPER_CHWIDTH_80P80MHZ;
 		else
-			bandwidth = CHANWIDTH_80MHZ;
+			bandwidth = CONF_OPER_CHWIDTH_80MHZ;
 		break;
 	case 160:
-		bandwidth = CHANWIDTH_160MHZ;
+		bandwidth = CONF_OPER_CHWIDTH_160MHZ;
 		break;
 	default:
-		bandwidth = CHANWIDTH_USE_HT;
+		bandwidth = CONF_OPER_CHWIDTH_USE_HT;
 		break;
 	}
 
@@ -3574,9 +3703,16 @@
 	if (ret)
 		return ret;
 
+#ifdef CONFIG_IEEE80211BE
+	old_punct_bitmap = iface->conf->punct_bitmap;
+	iface->conf->punct_bitmap = settings->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
 
 	/* change back the configuration */
+#ifdef CONFIG_IEEE80211BE
+	iface->conf->punct_bitmap = old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	hostapd_change_config_freq(iface->bss[0], iface->conf,
 				   &old_freq, NULL);
 
@@ -3676,7 +3812,8 @@
 hostapd_switch_channel_fallback(struct hostapd_iface *iface,
 				const struct hostapd_freq_params *freq_params)
 {
-	int seg0_idx = 0, seg1_idx = 0, bw = CHANWIDTH_USE_HT;
+	int seg0_idx = 0, seg1_idx = 0;
+	enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT;
 
 	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
 
@@ -3689,16 +3826,19 @@
 	case 0:
 	case 20:
 	case 40:
-		bw = CHANWIDTH_USE_HT;
+		bw = CONF_OPER_CHWIDTH_USE_HT;
 		break;
 	case 80:
 		if (freq_params->center_freq2)
-			bw = CHANWIDTH_80P80MHZ;
+			bw = CONF_OPER_CHWIDTH_80P80MHZ;
 		else
-			bw = CHANWIDTH_80MHZ;
+			bw = CONF_OPER_CHWIDTH_80MHZ;
 		break;
 	case 160:
-		bw = CHANWIDTH_160MHZ;
+		bw = CONF_OPER_CHWIDTH_160MHZ;
+		break;
+	case 320:
+		bw = CONF_OPER_CHWIDTH_320MHZ;
 		break;
 	default:
 		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
@@ -3792,7 +3932,7 @@
 
 	r = os_random() % HE_OPERATION_BSS_COLOR_MAX;
 	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
-		if (r && !(hapd->color_collision_bitmap & BIT(r)))
+		if (r && !(hapd->color_collision_bitmap & (1ULL << r)))
 			break;
 
 		r = (r + 1) % HE_OPERATION_BSS_COLOR_MAX;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 6b9b65f..b81da30 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -76,6 +76,17 @@
 
 #ifdef CONFIG_DPP
 	struct dpp_global *dpp;
+#ifdef CONFIG_DPP3
+	struct os_reltime dpp_pb_time;
+	struct os_reltime dpp_pb_announce_time;
+	struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+	struct dpp_bootstrap_info *dpp_pb_bi;
+	u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+	u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+	struct os_reltime dpp_pb_last_resp;
+	bool dpp_pb_result_indicated;
+	char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 
 #ifdef CONFIG_CTRL_IFACE_UDP
@@ -324,6 +335,7 @@
 #ifdef CONFIG_PROXYARP
 	struct l2_packet_data *sock_dhcp;
 	struct l2_packet_data *sock_ndisc;
+	bool x_snoop_initialized;
 #endif /* CONFIG_PROXYARP */
 #ifdef CONFIG_MESH
 	int num_plinks;
@@ -340,11 +352,15 @@
 #endif /* CONFIG_SQLITE */
 
 #ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
 	/** Key used for generating SAE anti-clogging tokens */
-	u8 comeback_key[8];
+	u8 comeback_key[COMEBACK_KEY_SIZE];
 	struct os_reltime last_comeback_key_update;
 	u16 comeback_idx;
-	u16 comeback_pending_idx[256];
+	u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
 	int dot11RSNASAERetransPeriod; /* msec */
 	struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
 #endif /* CONFIG_SAE */
@@ -400,6 +416,7 @@
 	struct dpp_pkex *dpp_pkex;
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
+	size_t dpp_pkex_code_len;
 	char *dpp_pkex_identifier;
 	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
@@ -420,6 +437,7 @@
 	int dpp_chirp_round;
 	int dpp_chirp_scan_done;
 	int dpp_chirp_listen;
+	struct os_reltime dpp_relay_last_needs_ctrl;
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
@@ -626,6 +644,11 @@
 	/* Previous WMM element information */
 	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
 
+	/* Maximum number of interfaces supported for MBSSID advertisement */
+	unsigned int mbssid_max_interfaces;
+	/* Maximum profile periodicity for enhanced MBSSID advertisement */
+	unsigned int ema_max_periodicity;
+
 	int (*enable_iface_cb)(struct hostapd_iface *iface);
 	int (*disable_iface_cb)(struct hostapd_iface *iface);
 };
@@ -655,6 +678,7 @@
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
 int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
 int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_bss_only(struct hostapd_data *bss);
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
 void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
 void hostapd_free_hapd_data(struct hostapd_data *hapd);
@@ -703,7 +727,7 @@
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2,
-			     int finished);
+			     u16 punct_bitmap, int finished);
 struct survey_results;
 void hostapd_event_get_survey(struct hostapd_iface *iface,
 			      struct survey_results *survey_results);
@@ -726,5 +750,7 @@
 #endif /* CONFIG_FST */
 
 int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
 
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 4b66b02..842d9f5 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -371,7 +371,7 @@
 		iface->conf->secondary_channel = 0;
 		hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
 		hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
-		hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
+		hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT);
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -893,6 +893,55 @@
 }
 
 
+static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct hostapd_config *conf = iface->conf;
+	u8 bw, start_chan;
+
+	if (!conf->punct_bitmap)
+		return true;
+
+	if (!conf->ieee80211be) {
+		wpa_printf(MSG_ERROR,
+			   "Currently RU puncturing is supported only if ieee80211be is enabled");
+		return false;
+	}
+
+	if (iface->freq >= 2412 && iface->freq <= 2484) {
+		wpa_printf(MSG_ERROR,
+			   "RU puncturing not supported in 2.4 GHz");
+		return false;
+	}
+
+	switch (conf->eht_oper_chwidth) {
+	case 0:
+		wpa_printf(MSG_ERROR,
+			   "RU puncturing is supported only in 80 MHz and 160 MHz");
+		return false;
+	case 1:
+		bw = 80;
+		start_chan = conf->eht_oper_centr_freq_seg0_idx - 6;
+		break;
+	case 2:
+		bw = 160;
+		start_chan = conf->eht_oper_centr_freq_seg0_idx - 14;
+		break;
+	default:
+		return false;
+	}
+
+	if (!is_punct_bitmap_valid(bw, (conf->channel - start_chan) / 4,
+				   conf->punct_bitmap)) {
+		wpa_printf(MSG_ERROR, "Invalid puncturing bitmap");
+		return false;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return true;
+}
+
+
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
 	int secondary_freq;
@@ -915,6 +964,9 @@
 	if (!hostapd_is_usable_edmg(iface))
 		return 0;
 
+	if (!hostapd_is_usable_punct_bitmap(iface))
+		return 0;
+
 	if (!iface->conf->secondary_channel)
 		return 1;
 
@@ -1087,13 +1139,14 @@
 
 	if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
 	     iface->conf->ieee80211n || iface->conf->ieee80211ac ||
-	     iface->conf->ieee80211ax) &&
+	     iface->conf->ieee80211ax || iface->conf->ieee80211be) &&
 	    iface->conf->channel == 14) {
-		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
+		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE/EHT on channel 14");
 		iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
 		iface->conf->ieee80211n = 0;
 		iface->conf->ieee80211ac = 0;
 		iface->conf->ieee80211ax = 0;
+		iface->conf->ieee80211be = 0;
 	}
 
 	iface->current_mode = NULL;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 394e292..0142ee4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -55,6 +55,8 @@
 #include "fils_hlp.h"
 #include "dpp_hostapd.h"
 #include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
 
 
 #ifdef CONFIG_FILS
@@ -68,11 +70,6 @@
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_PASN
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
-				 struct sta_info *sta,
-				 struct rsn_pmksa_cache_entry *pmksa,
-				 u16 status);
 #ifdef CONFIG_FILS
 
 static void pasn_fils_auth_resp(struct hostapd_data *hapd,
@@ -118,9 +115,13 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	h2e_required = (hapd->conf->sae_pwe == 1 ||
+#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 != 3 &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
 	if (h2e_required)
 		num++;
@@ -150,6 +151,13 @@
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	if (h2e_required && count < 8) {
 		count++;
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
@@ -165,6 +173,7 @@
 	int i, num, count;
 	int h2e_required;
 
+	hapd->conf->xrates_supported = false;
 	if (hapd->iface->current_rates == NULL)
 		return eid;
 
@@ -173,9 +182,13 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	h2e_required = (hapd->conf->sae_pwe == 1 ||
+#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 != 3 &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
 	if (h2e_required)
 		num++;
@@ -208,12 +221,21 @@
 			*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;
 }
 
@@ -561,7 +583,7 @@
 #endif /* CONFIG_SAE_PK */
 	}
 
-	if (rx_id && hapd->conf->sae_pwe != 3)
+	if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	else if (status_code == WLAN_STATUS_SUCCESS)
 		use_pt = 0;
@@ -737,124 +759,6 @@
 	return 0;
 }
 
-
-static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr,
-			       u8 *idx)
-{
-	u8 hash[SHA256_MAC_LEN];
-
-	if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key),
-			addr, ETH_ALEN, hash) < 0)
-		return -1;
-	*idx = hash[0];
-	return 0;
-}
-
-
-static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr,
-				const u8 *token, size_t token_len)
-{
-	u8 mac[SHA256_MAC_LEN];
-	const u8 *addrs[2];
-	size_t len[2];
-	u16 token_idx;
-	u8 idx;
-
-	if (token_len != SHA256_MAC_LEN ||
-	    comeback_token_hash(hapd, addr, &idx) < 0)
-		return -1;
-	token_idx = hapd->comeback_pending_idx[idx];
-	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
-		wpa_printf(MSG_DEBUG,
-			   "Comeback: Invalid anti-clogging token from "
-			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
-			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
-		return -1;
-	}
-
-	addrs[0] = addr;
-	len[0] = ETH_ALEN;
-	addrs[1] = token;
-	len[1] = 2;
-	if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
-			       2, addrs, len, mac) < 0 ||
-	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
-		return -1;
-
-	hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */
-
-	return 0;
-}
-
-
-static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
-					    int group, const u8 *addr, int h2e)
-{
-	struct wpabuf *buf;
-	u8 *token;
-	struct os_reltime now;
-	u8 idx[2];
-	const u8 *addrs[2];
-	size_t len[2];
-	u8 p_idx;
-	u16 token_idx;
-
-	os_get_reltime(&now);
-	if (!os_reltime_initialized(&hapd->last_comeback_key_update) ||
-	    os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) ||
-	    hapd->comeback_idx == 0xffff) {
-		if (random_get_bytes(hapd->comeback_key,
-				     sizeof(hapd->comeback_key)) < 0)
-			return NULL;
-		wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
-			    hapd->comeback_key, sizeof(hapd->comeback_key));
-		hapd->last_comeback_key_update = now;
-		hapd->comeback_idx = 0;
-		os_memset(hapd->comeback_pending_idx, 0,
-			  sizeof(hapd->comeback_pending_idx));
-	}
-
-	buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
-	if (buf == NULL)
-		return NULL;
-
-	if (group)
-		wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
-
-	if (h2e) {
-		/* Encapsulate Anti-clogging Token field in a container IE */
-		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-		wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
-		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
-	}
-
-	if (comeback_token_hash(hapd, addr, &p_idx) < 0) {
-		wpabuf_free(buf);
-		return NULL;
-	}
-
-	token_idx = hapd->comeback_pending_idx[p_idx];
-	if (!token_idx) {
-		hapd->comeback_idx++;
-		token_idx = hapd->comeback_idx;
-		hapd->comeback_pending_idx[p_idx] = token_idx;
-	}
-	WPA_PUT_BE16(idx, token_idx);
-	token = wpabuf_put(buf, SHA256_MAC_LEN);
-	addrs[0] = addr;
-	len[0] = ETH_ALEN;
-	addrs[1] = idx;
-	len[1] = sizeof(idx);
-	if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
-			       2, addrs, len, token) < 0) {
-		wpabuf_free(buf);
-		return NULL;
-	}
-	WPA_PUT_BE16(token, token_idx);
-
-	return buf;
-}
-
 #endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
 
 
@@ -982,7 +886,8 @@
 	sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
 	sta->sae->peer_commit_scalar = NULL;
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-			       sta->sae->pmk, sta->sae->pmkid);
+			       sta->sae->pmk, sta->sae->pmk_len,
+			       sta->sae->pmkid, sta->sae->akmp);
 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
 }
 
@@ -1219,27 +1124,32 @@
 
 static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
 {
-	int sae_pwe = hapd->conf->sae_pwe;
+	enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
 	int id_in_use;
 	bool sae_pk = false;
 
 	id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
-	if (id_in_use == 2 && sae_pwe != 3)
-		sae_pwe = 1;
-	else if (id_in_use == 1 && sae_pwe == 0)
-		sae_pwe = 2;
+	if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_BOTH;
 #ifdef CONFIG_SAE_PK
 	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-	if (sae_pwe == 0 && sae_pk)
-		sae_pwe = 2;
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+		sae_pwe = SAE_PWE_BOTH;
 #endif /* CONFIG_SAE_PK */
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+	    (hapd->conf->wpa_key_mgmt &
+	     (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
+		sae_pwe = SAE_PWE_BOTH;
 
-	return ((sae_pwe == 0 || sae_pwe == 3) &&
+	return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+		 sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
 		status_code == WLAN_STATUS_SUCCESS) ||
-		(sae_pwe == 1 &&
+		(sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
 		 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 		  (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
-		(sae_pwe == 2 &&
+		(sae_pwe == SAE_PWE_BOTH &&
 		 (status_code == WLAN_STATUS_SUCCESS ||
 		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 		  (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
@@ -1463,7 +1373,8 @@
 					mgmt->u.auth.variable, &token,
 					&token_len, groups, status_code ==
 					WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
-					status_code == WLAN_STATUS_SAE_PK);
+					status_code == WLAN_STATUS_SAE_PK,
+					NULL);
 		if (resp == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1482,7 +1393,9 @@
 		}
 
 		if (token &&
-		    check_comeback_token(hapd, sta->addr, token, token_len)
+		    check_comeback_token(hapd->comeback_key,
+					 hapd->comeback_pending_idx, sta->addr,
+					 token, token_len)
 		    < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
 				   "incorrect token from " MACSTR,
@@ -1510,8 +1423,14 @@
 			if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 			    status_code == WLAN_STATUS_SAE_PK)
 				h2e = 1;
-			data = auth_build_token_req(hapd, sta->sae->group,
-						    sta->addr, h2e);
+			data = auth_build_token_req(
+				&hapd->last_comeback_key_update,
+				hapd->comeback_key,
+				hapd->comeback_idx,
+				hapd->comeback_pending_idx,
+				sizeof(hapd->comeback_pending_idx),
+				sta->sae->group,
+				sta->addr, h2e);
 			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
 			if (hapd->conf->mesh & MESH_ENABLED)
 				sae_set_state(sta, SAE_NOTHING,
@@ -1555,8 +1474,9 @@
 				return;
 			}
 
-			if (sae_check_confirm(sta->sae, var, var_len) < 0) {
-				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			if (sae_check_confirm(sta->sae, var, var_len,
+					      NULL) < 0) {
+				resp = WLAN_STATUS_CHALLENGE_FAIL;
 				goto reply;
 			}
 			sta->sae->rc = peer_send_confirm;
@@ -2105,10 +2025,8 @@
 	if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
 		/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
 		int res;
-		int use_sha384 = wpa_key_mgmt_sha384(
-			wpa_auth_sta_key_mgmt(sta->wpa_sm));
 
-		res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+		res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
 					 wpabuf_put(data, 0),
 					 wpabuf_tailroom(data));
 		if (res < 0) {
@@ -2385,242 +2303,15 @@
 
 
 #ifdef CONFIG_PASN
-#ifdef CONFIG_SAE
-
-static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd,
-				     struct sta_info *sta,
-				     struct wpabuf *wd)
-{
-	struct pasn_data *pasn = sta->pasn;
-	const char *password;
-	const u8 *data;
-	size_t buf_len;
-	u16 res, alg, seq, status;
-	int groups[] = { pasn->group, 0 };
-	struct sae_pt *pt = NULL;
-	int ret;
-
-	if (!wd)
-		return -1;
-
-	data = wpabuf_head_u8(wd);
-	buf_len = wpabuf_len(wd);
-
-	if (buf_len < 6) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
-			   buf_len);
-		return -1;
-	}
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 1 ||
-	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
-		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
-		return -1;
-	}
-
-	sae_clear_data(&pasn->sae);
-	pasn->sae.state = SAE_NOTHING;
-
-	ret = sae_set_group(&pasn->sae, pasn->group);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
-		return -1;
-	}
-
-	password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL);
-	if (!password || !pt) {
-		wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
-		return -1;
-	}
-
-	ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr,
-				    NULL, NULL);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
-		return -1;
-	}
-
-	res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
-			       groups, 0);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
-		return -1;
-	}
-
-	/* Process the commit message and derive the PMK */
-	ret = sae_process_commit(&pasn->sae);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-		return -1;
-	}
-
-	pasn->sae.state = SAE_COMMITTED;
-
-	return 0;
-}
-
-
-static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd,
-				      struct sta_info *sta,
-				      struct wpabuf *wd)
-{
-	struct pasn_data *pasn = sta->pasn;
-	const u8 *data;
-	size_t buf_len;
-	u16 res, alg, seq, status;
-
-	if (!wd)
-		return -1;
-
-	data = wpabuf_head_u8(wd);
-	buf_len = wpabuf_len(wd);
-
-	if (buf_len < 6) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
-			   buf_len);
-		return -1;
-	}
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
-		return -1;
-	}
-
-	res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
-		return -1;
-	}
-
-	pasn->sae.state = SAE_ACCEPTED;
-
-	/*
-	 * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
-	 * PASN/SAE should only be allowed with future PASN only. For now do not
-	 * restrict this only for PASN.
-	 */
-	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-			       pasn->sae.pmk, pasn->sae.pmkid);
-	return 0;
-}
-
-
-static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd,
-				       struct sta_info *sta)
-{
-	struct pasn_data *pasn = sta->pasn;
-	struct wpabuf *buf = NULL;
-	u8 *len_ptr;
-	size_t len;
-
-	/* Need to add the entire Authentication frame body */
-	buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
-		return NULL;
-	}
-
-	/* Need to add the entire authentication frame body for the commit */
-	len_ptr = wpabuf_put(buf, 2);
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 1);
-	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
-	/* Write the actual commit and update the length accordingly */
-	sae_write_commit(&pasn->sae, buf, NULL, 0);
-	len = wpabuf_len(buf);
-	WPA_PUT_LE16(len_ptr, len - 2);
-
-	/* Need to add the entire Authentication frame body for the confirm */
-	len_ptr = wpabuf_put(buf, 2);
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 2);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	sae_write_confirm(&pasn->sae, buf);
-	WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
-
-	pasn->sae.state = SAE_CONFIRMED;
-
-	return buf;
-}
-
-#endif /* CONFIG_SAE */
-
-
 #ifdef CONFIG_FILS
 
-static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd,
-					struct sta_info *sta)
-{
-	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
-	struct wpabuf *buf = NULL;
-
-	if (!fils->erp_resp) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
-		return NULL;
-	}
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		return NULL;
-
-	/* Add the authentication algorithm */
-	wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
-	/* Authentication Transaction seq# */
-	wpabuf_put_le16(buf, 2);
-
-	/* Status Code */
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	/* Own RSNE */
-	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
-
-	/* FILS Nonce */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
-	wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
-
-	/* FILS Session */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
-	wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
-
-	/* Wrapped Data */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
-	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
-	wpabuf_put_buf(buf, fils->erp_resp);
-
-	return buf;
-}
-
-
 static void pasn_fils_auth_resp(struct hostapd_data *hapd,
 				struct sta_info *sta, u16 status,
 				struct wpabuf *erp_resp,
 				const u8 *msk, size_t msk_len)
 {
 	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
+	struct pasn_fils *fils = &pasn->fils;
 	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	int ret;
@@ -2661,13 +2352,23 @@
 		goto fail;
 	}
 
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FILS: Failed to derive LTF keyseed");
+			goto fail;
+		}
+	}
+
 	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
 
 	wpabuf_free(pasn->secret);
 	pasn->secret = NULL;
 
 	fils->erp_resp = erp_resp;
-	ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS);
+	ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+				    WLAN_STATUS_SUCCESS);
 	fils->erp_resp = NULL;
 
 	if (ret) {
@@ -2690,7 +2391,7 @@
 	return -1;
 #else /* CONFIG_NO_RADIUS */
 	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
+	struct pasn_fils *fils = &pasn->fils;
 	struct ieee802_11_elems elems;
 	struct wpa_ie_data rsne_data;
 	struct wpabuf *fils_wd;
@@ -2741,7 +2442,7 @@
 	}
 
 	if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
-	    !elems.wrapped_data) {
+	    !elems.wrapped_data || !elems.fils_session) {
 		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
 		return -1;
 	}
@@ -2749,7 +2450,7 @@
 	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				   &rsne_data);
 	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
 		return -1;
 	}
 
@@ -2809,598 +2510,90 @@
 #endif /* CONFIG_FILS */
 
 
-static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd,
-					     struct sta_info *sta)
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+			       int noack, unsigned int freq, unsigned int wait)
 {
-	switch (sta->pasn->akmp) {
-	case WPA_KEY_MGMT_PASN:
-		/* no wrapped data */
-		return NULL;
-	case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
-		return pasn_get_sae_wd(hapd, sta);
-#else /* CONFIG_SAE */
-		wpa_printf(MSG_ERROR,
-			   "PASN: SAE: Cannot derive wrapped data");
-		return NULL;
-#endif /* CONFIG_SAE */
-	case WPA_KEY_MGMT_FILS_SHA256:
-	case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
-		return pasn_get_fils_wd(hapd, sta);
-#endif /* CONFIG_FILS */
-		/* fall through */
-	case WPA_KEY_MGMT_FT_PSK:
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-	default:
-		wpa_printf(MSG_ERROR,
-			   "PASN: TODO: Wrapped data for akmp=0x%x",
-			   sta->pasn->akmp);
-		return NULL;
-	}
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
 }
 
 
-static int
-pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
-		 const u8 *cached_pmk, size_t cached_pmk_len,
-		 struct wpa_pasn_params_data *pasn_data,
-		 struct wpabuf *wrapped_data,
-		 struct wpabuf *secret)
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+				 struct sta_info *sta)
 {
-	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
-	u8 pmk[PMK_LEN_MAX];
-	u8 pmk_len;
-	int ret;
+	struct pasn_data *pasn = sta->pasn;
 
-	os_memset(pmk, 0, sizeof(pmk));
-	pmk_len = 0;
+	pasn->cb_ctx = hapd;
+	pasn->send_mgmt = hapd_pasn_send_mlme;
+	pasn->pasn_groups = hapd->conf->pasn_groups;
+	pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+	pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+	pasn->derive_kdk = hapd->iface->drv_flags2 &
+		WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+	pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+	if (hapd->conf->force_kdk_derivation)
+		pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+	pasn->use_anti_clogging = use_anti_clogging(hapd);
+	pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+					  NULL);
+	pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+	pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+	pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+	pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
 
-	if (!cached_pmk || !cached_pmk_len)
-		wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
+	pasn->comeback_after = hapd->conf->pasn_comeback_after;
+	pasn->comeback_idx = hapd->comeback_idx;
+	pasn->comeback_key =  hapd->comeback_key;
+	pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+	os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
+}
 
-	if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) {
-		wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
 
-		pmk_len = WPA_PASN_PMK_LEN;
-		os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
-	} else if (cached_pmk && cached_pmk_len) {
-		wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
+static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
+				    const u8 *own_addr, const u8 *sta_addr,
+				    int cipher, int akmp)
+{
+	struct ptksa_cache_entry *entry;
 
-		pmk_len = cached_pmk_len;
-		os_memcpy(pmk, cached_pmk, cached_pmk_len);
-	} else {
-		switch (sta->pasn->akmp) {
-#ifdef CONFIG_SAE
-		case WPA_KEY_MGMT_SAE:
-			if (sta->pasn->sae.state == SAE_COMMITTED) {
-				pmk_len = PMK_LEN;
-				os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN);
-				break;
-			}
-#endif /* CONFIG_SAE */
-			/* fall through */
-		default:
-			/* TODO: Derive PMK based on wrapped data */
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Missing PMK derivation");
-			return -1;
-		}
-	}
-
-	ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
-			      wpabuf_head(secret), wpabuf_len(secret),
-			      &sta->pasn->ptk, sta->pasn->akmp,
-			      sta->pasn->cipher, sta->pasn->kdk_len);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+	entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
+	if (!entry) {
+		wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+			   " not present in PTKSA cache", MAC2STR(sta_addr));
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+	if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: own addr " MACSTR " and PTKSA entry own addr "
+			   MACSTR " differ",
+			   MAC2STR(own_addr), MAC2STR(entry->own_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+		   MAC2STR(sta_addr));
+	hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
+					   entry->ptk.tk_len, entry->ptk.tk,
+					   entry->ptk.ltf_keyseed_len,
+					   entry->ptk.ltf_keyseed, 0);
+
 	return 0;
 }
 
 
-static void handle_auth_pasn_comeback(struct hostapd_data *hapd,
-				      struct sta_info *sta, u16 group)
+static void hapd_pasn_update_params(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len)
 {
-	struct wpabuf *buf, *comeback;
-	int ret;
-
-	wpa_printf(MSG_DEBUG,
-		   "PASN: Building comeback frame 2. Comeback after=%u",
-		   hapd->conf->pasn_comeback_after);
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		return;
-
-	wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
-				   sta->addr, 2,
-				   WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
-
-	/*
-	 * Do not include the group as a part of the token since it is not going
-	 * to be used.
-	 */
-	comeback = auth_build_token_req(hapd, 0, sta->addr, 0);
-	if (!comeback) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed sending auth with comeback");
-		wpabuf_free(buf);
-		return;
-	}
-
-	wpa_pasn_add_parameter_ie(buf, group,
-				  WPA_PASN_WRAPPED_DATA_NO,
-				  NULL, 0, comeback,
-				  hapd->conf->pasn_comeback_after);
-	wpabuf_free(comeback);
-
-	wpa_printf(MSG_DEBUG,
-		   "PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr));
-
-	ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
-				    NULL, 0, 0);
-	if (ret)
-		wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
-
-	wpabuf_free(buf);
-}
-
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
-				 struct sta_info *sta,
-				 struct rsn_pmksa_cache_entry *pmksa,
-				 u16 status)
-{
-	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len;
-	u8 *ptr;
-	const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
-	u8 *data_buf = NULL;
-	size_t rsn_ie_len, frame_len, data_len;
-	int ret;
-	const u8 *pmkid = NULL;
-
-	wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		goto fail;
-
-	wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
-				   sta->addr, 2, status);
-
-	if (status != WLAN_STATUS_SUCCESS)
-		goto done;
-
-	if (pmksa) {
-		pmkid = pmksa->pmkid;
-#ifdef CONFIG_SAE
-	} else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-		wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
-		pmkid = sta->pasn->sae.pmkid;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-	} else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		   sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-		wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
-		pmkid = sta->pasn->fils.erp_pmkid;
-#endif /* CONFIG_FILS */
-	}
-
-	if (wpa_pasn_add_rsne(buf, pmkid,
-			      sta->pasn->akmp, sta->pasn->cipher) < 0)
-		goto fail;
-
-	/* No need to derive PMK if PMKSA is given */
-	if (!pmksa)
-		wrapped_data_buf = pasn_get_wrapped_data(hapd, sta);
-	else
-		sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
-
-	/* Get public key */
-	pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0);
-	pubkey = wpabuf_zeropad(pubkey,
-				crypto_ecdh_prime_len(sta->pasn->ecdh));
-	if (!pubkey) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
-		goto fail;
-	}
-
-	wpa_pasn_add_parameter_ie(buf, sta->pasn->group,
-				  sta->pasn->wrapped_data_format,
-				  pubkey, true, NULL, 0);
-
-	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
-		goto fail;
-
-	wpabuf_free(wrapped_data_buf);
-	wrapped_data_buf = NULL;
-	wpabuf_free(pubkey);
-	pubkey = NULL;
-
-	/* Add RSNXE if needed */
-	rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
-	if (rsnxe_ie)
-		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
-
-	/* Add the mic */
-	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
-	wpabuf_put_u8(buf, WLAN_EID_MIC);
-	wpabuf_put_u8(buf, mic_len);
-	ptr = wpabuf_put(buf, mic_len);
-
-	os_memset(ptr, 0, mic_len);
-
-	frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
-	frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
-	rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len);
-	if (!rsn_ie || !rsn_ie_len)
-		goto fail;
-
-	/*
-	 * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
-	 * MDE, etc. Thus, do not use the returned length but instead use the
-	 * length specified in the IE header.
-	 */
-	data_len = rsn_ie[1] + 2;
-	if (rsnxe_ie) {
-		data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
-		if (!data_buf)
-			goto fail;
-
-		os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
-		os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
-		data_len += rsnxe_ie[1] + 2;
-		data = data_buf;
-	} else {
-		data = rsn_ie;
-	}
-
-	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
-		       hapd->own_addr, sta->addr, data, data_len,
-		       frame, frame_len, mic);
-	os_free(data_buf);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
-		goto fail;
-	}
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->conf->pasn_corrupt_mic) {
-		wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
-		mic[0] = ~mic[0];
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	os_memcpy(ptr, mic, mic_len);
-
-done:
-	wpa_printf(MSG_DEBUG,
-		   "PASN: Building frame 2: success; resp STA=" MACSTR,
-		   MAC2STR(sta->addr));
-
-	ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
-				    NULL, 0, 0);
-	if (ret)
-		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
-
-	wpabuf_free(buf);
-	return ret;
-fail:
-	wpabuf_free(wrapped_data_buf);
-	wpabuf_free(pubkey);
-	wpabuf_free(buf);
-	return -1;
-}
-
-
-static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
-			       const struct ieee80211_mgmt *mgmt, size_t len)
-{
+	struct pasn_data *pasn = sta->pasn;
 	struct ieee802_11_elems elems;
 	struct wpa_ie_data rsn_data;
 	struct wpa_pasn_params_data pasn_params;
-	struct rsn_pmksa_cache_entry *pmksa = NULL;
-	const u8 *cached_pmk = NULL;
-	size_t cached_pmk_len = 0;
-#ifdef CONFIG_IEEE80211R_AP
-	u8 pmk_r1[PMK_LEN_MAX];
-	size_t pmk_r1_len;
-#endif /* CONFIG_IEEE80211R_AP */
-	struct wpabuf *wrapped_data = NULL, *secret = NULL;
-	const int *groups = hapd->conf->pasn_groups;
-	static const int default_groups[] = { 19, 0 };
-	u16 status = WLAN_STATUS_SUCCESS;
-	int ret, inc_y;
-	bool derive_keys;
-	u32 i;
-	bool derive_kdk;
-
-	if (!groups)
-		groups = default_groups;
-
-	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
-				   len - offsetof(struct ieee80211_mgmt,
-						  u.auth.variable),
-				   &elems, 0) == ParseFailed) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed parsing Authentication frame");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-				   &rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	ret = wpa_pasn_validate_rsne(&rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) ||
-	    !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) {
-		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	sta->pasn->akmp = rsn_data.key_mgmt;
-	sta->pasn->cipher = rsn_data.pairwise_cipher;
-
-	derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
-		ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
-					  WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
-	if (!derive_kdk)
-		derive_kdk = hapd->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
-	if (derive_kdk)
-		sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
-	else
-		sta->pasn->kdk_len = 0;
-	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
-
-	if (!elems.pasn_params || !elems.pasn_params_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: No PASN Parameters element found");
-		status = WLAN_STATUS_INVALID_PARAMETERS;
-		goto send_resp;
-	}
-
-	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
-					  elems.pasn_params_len + 3,
-					  false, &pasn_params);
-	if (ret) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed validation of PASN Parameters IE");
-		status = WLAN_STATUS_INVALID_PARAMETERS;
-		goto send_resp;
-	}
-
-	for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
-		;
-
-	if (!pasn_params.group || groups[i] != pasn_params.group) {
-		wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
-			   pasn_params.group);
-		status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
-		goto send_resp;
-	}
-
-	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
-		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	if (pasn_params.comeback) {
-		wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
-
-		ret = check_comeback_token(hapd, sta->addr,
-					   pasn_params.comeback,
-					   pasn_params.comeback_len);
-
-		if (ret) {
-			wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-		}
-	} else if (use_anti_clogging(hapd)) {
-		wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
-		handle_auth_pasn_comeback(hapd, sta, pasn_params.group);
-		ap_free_sta(hapd, sta);
-		return;
-	}
-
-	sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group);
-	if (!sta->pasn->ecdh) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	sta->pasn->group = pasn_params.group;
-
-	if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
-		inc_y = 1;
-	} else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
-		   pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
-		inc_y = 0;
-	} else {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Invalid first octet in pubkey=0x%x",
-			   pasn_params.pubkey[0]);
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y,
-					 pasn_params.pubkey + 1,
-					 pasn_params.pubkey_len - 1);
-	if (!secret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	derive_keys = true;
-	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
-		wrapped_data = ieee802_11_defrag(&elems,
-						 WLAN_EID_EXTENSION,
-						 WLAN_EID_EXT_WRAPPED_DATA);
-		if (!wrapped_data) {
-			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-		}
-
-#ifdef CONFIG_SAE
-		if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-			ret = pasn_wd_handle_sae_commit(hapd, sta,
-							wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing SAE commit");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-		}
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-		if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		    sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-			ret = pasn_wd_handle_fils(hapd, sta, wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing FILS wrapped data");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-
-			wpa_printf(MSG_DEBUG,
-				   "PASN: FILS: Pending AS response");
-
-			/*
-			 * With PASN/FILS, keys can be derived only after a
-			 * response from the AS is processed.
-			 */
-			derive_keys = false;
-		}
-#endif /* CONFIG_FILS */
-	}
-
-	sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
-
-	ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
-				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
-				   len - IEEE80211_HDRLEN, sta->pasn->hash);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	if (!derive_keys) {
-		wpa_printf(MSG_DEBUG, "PASN: Storing secret");
-		sta->pasn->secret = secret;
-		wpabuf_free(wrapped_data);
-		return;
-	}
-
-	if (rsn_data.num_pmkid) {
-		if (wpa_key_mgmt_ft(sta->pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R_AP
-			wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
-
-			ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
-						  rsn_data.pmkid,
-						  pmk_r1, &pmk_r1_len, NULL,
-						  NULL, NULL, NULL,
-						  NULL, NULL, NULL);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: FT: Failed getting PMK-R1");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-			cached_pmk = pmk_r1;
-			cached_pmk_len = pmk_r1_len;
-#else /* CONFIG_IEEE80211R_AP */
-			wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-#endif /* CONFIG_IEEE80211R_AP */
-		} else {
-			wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
-
-			pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
-						   rsn_data.pmkid);
-			if (pmksa) {
-				cached_pmk = pmksa->pmk;
-				cached_pmk_len = pmksa->pmk_len;
-			}
-		}
-	} else {
-		wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
-	}
-
-	ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len,
-			       &pasn_params, wrapped_data, secret);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
-				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
-				   len - IEEE80211_HDRLEN, sta->pasn->hash);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
-
-send_resp:
-	ret = handle_auth_pasn_resp(hapd, sta, pmksa, status);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	} else {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Success handling transaction == 1");
-	}
-
-	wpabuf_free(secret);
-	wpabuf_free(wrapped_data);
-
-	if (status != WLAN_STATUS_SUCCESS)
-		ap_free_sta(hapd, sta);
-}
-
-
-static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
-			       const struct ieee80211_mgmt *mgmt, size_t len)
-{
-	struct ieee802_11_elems elems;
-	struct wpa_pasn_params_data pasn_params;
 	struct wpabuf *wrapped_data = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len;
-	int ret;
 
 	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
 				   len - offsetof(struct ieee80211_mgmt,
@@ -3408,98 +2601,62 @@
 				   &elems, 0) == ParseFailed) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Failed parsing Authentication frame");
-		goto fail;
+		return;
 	}
 
-	/* Check that the MIC IE exists. Save it and zero out the memory. */
-	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
-	if (!elems.mic || elems.mic_len != mic_len) {
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn_data)) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+		return;
+	}
+
+	if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+	    !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+		return;
+	}
+
+	pasn->akmp = rsn_data.key_mgmt;
+	pasn->cipher = rsn_data.pairwise_cipher;
+
+	if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+		pasn->pmk_r1_len = 0;
+		wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+				    rsn_data.pmkid,
+				    pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+				    NULL, NULL, NULL,
+				    NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+	}
+#ifdef CONFIG_FILS
+	if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+	    pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+		return;
+	if (!elems.pasn_params ||
+	    wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					elems.pasn_params_len + 3,
+					false, &pasn_params)) {
 		wpa_printf(MSG_DEBUG,
-			   "PASN: Invalid MIC. Expecting len=%u", mic_len);
-		goto fail;
-	} else {
-		os_memcpy(mic, elems.mic, mic_len);
-		/* TODO: Clean this up.. Should not modify received frame
-		 * buffer. */
-		os_memset((u8 *) elems.mic, 0, mic_len);
+			   "PASN: Failed validation of PASN Parameters element");
+		return;
 	}
-
-	if (!elems.pasn_params || !elems.pasn_params_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: No PASN Parameters element found");
-		goto fail;
-	}
-
-	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
-					  elems.pasn_params_len + 3,
-					  false, &pasn_params);
-	if (ret) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed validation of PASN Parameters IE");
-		goto fail;
-	}
-
-	if (pasn_params.pubkey || pasn_params.pubkey_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Public key should not be included");
-		goto fail;
-	}
-
-	/* Verify the MIC */
-	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
-		       sta->addr, hapd->own_addr,
-		       sta->pasn->hash, mic_len * 2,
-		       (u8 *) &mgmt->u.auth,
-		       len - offsetof(struct ieee80211_mgmt, u.auth),
-		       out_mic);
-
-	wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
-	if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
-		goto fail;
-	}
-
 	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
-		wrapped_data = ieee802_11_defrag(&elems,
-						 WLAN_EID_EXTENSION,
+		wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
 						 WLAN_EID_EXT_WRAPPED_DATA);
-
 		if (!wrapped_data) {
 			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
-			goto fail;
+			return;
 		}
-
-#ifdef CONFIG_SAE
-		if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-			ret = pasn_wd_handle_sae_confirm(hapd, sta,
-							 wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing SAE confirm");
-				wpabuf_free(wrapped_data);
-				goto fail;
-			}
-		}
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-		if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		    sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-			if (wrapped_data) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: FILS: Ignore wrapped data");
-			}
-		}
-#endif /* CONFIG_FILS */
-		wpabuf_free(wrapped_data);
+		if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed processing FILS wrapped data");
+		else
+			pasn->fils_wd_valid = true;
 	}
-
-	wpa_printf(MSG_INFO,
-		   "PASN: Success handling transaction == 3. Store PTK");
-
-	ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200,
-			&sta->pasn->ptk);
-fail:
-	ap_free_sta(hapd, sta);
+	wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3535,7 +2692,12 @@
 			return;
 		}
 
-		handle_auth_pasn_1(hapd, sta, mgmt, len);
+		hapd_initialize_pasn(hapd, sta);
+
+		hapd_pasn_update_params(hapd, sta, mgmt, len);
+		if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) < 0)
+			ap_free_sta(hapd, sta);
 	} else if (trans_seq == 3) {
 		if (!sta->pasn) {
 			wpa_printf(MSG_DEBUG,
@@ -3550,7 +2712,18 @@
 			return;
 		}
 
-		handle_auth_pasn_3(hapd, sta, mgmt, len);
+		if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) == 0) {
+			ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+					sta->pasn->cipher, 43200,
+					&sta->pasn->ptk, NULL, NULL,
+					sta->pasn->akmp);
+
+			pasn_set_keys_from_cache(hapd, hapd->own_addr,
+						 sta->addr, sta->pasn->cipher,
+						 sta->pasn->akmp);
+		}
+		ap_free_sta(hapd, sta);
 	} else {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Invalid transaction %u - ignore", trans_seq);
@@ -3970,6 +3143,23 @@
 }
 
 
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+	size_t num_bss_nontx;
+	u8 max_bssid_ind = 0;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+		return 0;
+
+	num_bss_nontx = hapd->iface->num_bss - 1;
+	while (num_bss_nontx > 0) {
+		max_bssid_ind++;
+		num_bss_nontx >>= 1;
+	}
+	return max_bssid_ind;
+}
+
+
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int i, j = 32, aid;
@@ -3995,7 +3185,7 @@
 	}
 	if (j == 32)
 		return -1;
-	aid = i * 32 + j + 1;
+	aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
 	if (aid > 2007)
 		return -1;
 
@@ -4462,40 +3652,34 @@
 }
 
 
-static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
-			   const u8 *ies, size_t ies_len, int reassoc)
+static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+			     const u8 *ies, size_t ies_len,
+			     struct ieee802_11_elems *elems, int reassoc)
 {
-	struct ieee802_11_elems elems;
 	int resp;
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
 	const u8 *p2p_dev_addr = NULL;
 
-	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
-		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_INFO, "Station sent an invalid "
-			       "association request");
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
-
-	resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+	resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
+	resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+	resp = check_ext_capab(hapd, sta, elems->ext_capab,
+			       elems->ext_capab_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = copy_supp_rates(hapd, sta, &elems);
+	resp = copy_supp_rates(hapd, sta, elems);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 
-	resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
+	resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 
-	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
+	resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
@@ -4508,11 +3692,11 @@
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac) {
-		resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
+		resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
-		resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+		resp = set_sta_vht_opmode(hapd, sta, elems->vht_opmode_notif);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
@@ -4525,9 +3709,9 @@
 		return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
 	}
 
-	if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
-		resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
-					   elems.vendor_vht_len);
+	if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
+		resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
+					   elems->vendor_vht_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
@@ -4535,10 +3719,19 @@
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
 		resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
-					 elems.he_capabilities,
-					 elems.he_capabilities_len);
+					 elems->he_capabilities,
+					 elems->he_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
+
+		if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station does not support mandatory HE PHY - reject association");
+			return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+		}
+
 		if (is_6ghz_op_class(hapd->iconf->op_class)) {
 			if (!(sta->flags & WLAN_STA_HE)) {
 				hostapd_logger(hapd, sta->addr,
@@ -4548,7 +3741,7 @@
 				return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
 			}
 			resp = copy_sta_he_6ghz_capab(hapd, sta,
-						      elems.he_6ghz_band_cap);
+						      elems->he_6ghz_band_cap);
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
@@ -4557,17 +3750,17 @@
 #ifdef CONFIG_IEEE80211BE
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
-					  elems.he_capabilities,
-					  elems.he_capabilities_len,
-					  elems.eht_capabilities,
-					  elems.eht_capabilities_len);
+					  elems->he_capabilities,
+					  elems->he_capabilities_len,
+					  elems->eht_capabilities,
+					  elems->eht_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_P2P
-	if (elems.p2p) {
+	if (elems->p2p && ies && ies_len) {
 		wpabuf_free(sta->p2p_ie);
 		sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
 							  P2P_IE_VENDOR_TYPE);
@@ -4579,13 +3772,13 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
-		wpa_ie = elems.rsn_ie;
-		wpa_ie_len = elems.rsn_ie_len;
+	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
+		wpa_ie = elems->rsn_ie;
+		wpa_ie_len = elems->rsn_ie_len;
 	} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
-		   elems.wpa_ie) {
-		wpa_ie = elems.wpa_ie;
-		wpa_ie_len = elems.wpa_ie_len;
+		   elems->wpa_ie) {
+		wpa_ie = elems->wpa_ie;
+		wpa_ie_len = elems->wpa_ie_len;
 	} else {
 		wpa_ie = NULL;
 		wpa_ie_len = 0;
@@ -4593,7 +3786,7 @@
 
 #ifdef CONFIG_WPS
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
-	if (hapd->conf->wps_state && elems.wps_ie) {
+	if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
 		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
 			   "Request - assume WPS is used");
 		if (check_sa_query(hapd, sta, reassoc))
@@ -4629,6 +3822,9 @@
 	if (hapd->conf->wpa && wpa_ie) {
 		enum wpa_validate_result res;
 
+		if (check_sa_query(hapd, sta, reassoc))
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
 		wpa_ie -= 2;
 		wpa_ie_len += 2;
 		if (sta->wpa_sm == NULL)
@@ -4644,17 +3840,16 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  hapd->iface->freq,
 					  wpa_ie, wpa_ie_len,
-					  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);
+					  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);
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
-		if (check_sa_query(hapd, sta, reassoc))
-			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-
 		if (wpa_auth_uses_mfp(sta->wpa_sm))
 			sta->flags |= WLAN_STA_MFP;
 		else
@@ -4686,7 +3881,7 @@
 		    sta->auth_alg == WLAN_AUTH_OPEN) {
 			struct rsn_pmksa_cache_entry *sa;
 			sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
-			if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+			if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
 				wpa_printf(MSG_DEBUG,
 					   "SAE: No PMKSA cache entry found for "
 					   MACSTR, MAC2STR(sta->addr));
@@ -4704,10 +3899,10 @@
 			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
 		}
 
-		if (hapd->conf->sae_pwe == 2 &&
+		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
 		    sta->sae && !sta->sae->h2e &&
-		    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+		    ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
 					      WLAN_RSNX_CAPAB_SAE_H2E)) {
 			wpa_printf(MSG_INFO, "SAE: " MACSTR
 				   " indicates support for SAE H2E, but did not use it",
@@ -4719,9 +3914,9 @@
 #ifdef CONFIG_OWE
 		if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
 		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
-		    elems.owe_dh) {
-			resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
-						     elems.owe_dh_len);
+		    elems->owe_dh) {
+			resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
+						     elems->owe_dh_len);
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
@@ -4735,7 +3930,7 @@
 		    (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
 		    hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
 		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
-		    elems.owe_dh) {
+		    elems->owe_dh) {
 			sta->dpp_pfs = dpp_pfs_init(
 				wpabuf_head(hapd->conf->dpp_netaccesskey),
 				wpabuf_len(hapd->conf->dpp_netaccesskey));
@@ -4746,8 +3941,8 @@
 				goto pfs_fail;
 			}
 
-			if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
-					    elems.owe_dh_len) < 0) {
+			if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh,
+					    elems->owe_dh_len) < 0) {
 				dpp_pfs_free(sta->dpp_pfs);
 				sta->dpp_pfs = NULL;
 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -4770,7 +3965,7 @@
 		}
 #ifdef CONFIG_HS20
 	} else if (hapd->conf->osen) {
-		if (elems.osen == NULL) {
+		if (!elems->osen) {
 			hostapd_logger(
 				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 				HOSTAPD_LEVEL_INFO,
@@ -4788,7 +3983,7 @@
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		}
 		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems.osen - 2, elems.osen_len + 2) < 0)
+				      elems->osen - 2, elems->osen_len + 2) < 0)
 			return WLAN_STATUS_INVALID_IE;
 #endif /* CONFIG_HS20 */
 	} else
@@ -4800,12 +3995,12 @@
 
 #ifdef CONFIG_HS20
 	wpabuf_free(sta->hs20_ie);
-	if (elems.hs20 && elems.hs20_len > 4) {
+	if (elems->hs20 && elems->hs20_len > 4) {
 		int release;
 
-		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
-						 elems.hs20_len - 4);
-		release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
+		sta->hs20_ie = wpabuf_alloc_copy(elems->hs20 + 4,
+						 elems->hs20_len - 4);
+		release = ((elems->hs20[4] >> 4) & 0x0f) + 1;
 		if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
 		    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 			wpa_printf(MSG_DEBUG,
@@ -4818,10 +4013,10 @@
 	}
 
 	wpabuf_free(sta->roaming_consortium);
-	if (elems.roaming_cons_sel)
+	if (elems->roaming_cons_sel)
 		sta->roaming_consortium = wpabuf_alloc_copy(
-			elems.roaming_cons_sel + 4,
-			elems.roaming_cons_sel_len - 4);
+			elems->roaming_cons_sel + 4,
+			elems->roaming_cons_sel_len - 4);
 	else
 		sta->roaming_consortium = NULL;
 #endif /* CONFIG_HS20 */
@@ -4829,16 +4024,16 @@
 #ifdef CONFIG_FST
 	wpabuf_free(sta->mb_ies);
 	if (hapd->iface->fst)
-		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+		sta->mb_ies = mb_ies_by_info(&elems->mb_ies);
 	else
 		sta->mb_ies = NULL;
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_MBO
-	mbo_ap_check_sta_assoc(hapd, sta, &elems);
+	mbo_ap_check_sta_assoc(hapd, sta, elems);
 
 	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
-	    elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+	    elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
 	    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		wpa_printf(MSG_INFO,
 			   "MBO: Reject WPA2 association without PMF");
@@ -4868,7 +4063,7 @@
 					  &tx_seg1_idx) < 0)
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-		res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+		res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
 					   tx_chanwidth, tx_seg1_idx);
 		if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
 		    res == OCI_NOT_FOUND) {
@@ -4887,18 +4082,18 @@
 	}
 #endif /* CONFIG_FILS && CONFIG_OCV */
 
-	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
-				    elems.supp_op_classes_len);
+	ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes,
+				    elems->supp_op_classes_len);
 
 	if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
-	    elems.rrm_enabled &&
-	    elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
-		os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
+	    elems->rrm_enabled &&
+	    elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+		os_memcpy(sta->rrm_enabled_capa, elems->rrm_enabled,
 			  sizeof(sta->rrm_enabled_capa));
 
-	if (elems.power_capab) {
-		sta->min_tx_power = elems.power_capab[0];
-		sta->max_tx_power = elems.power_capab[1];
+	if (elems->power_capab) {
+		sta->min_tx_power = elems->power_capab[0];
+		sta->max_tx_power = elems->power_capab[1];
 		sta->power_capab = 1;
 	} else {
 		sta->power_capab = 0;
@@ -4908,6 +4103,22 @@
 }
 
 
+static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *ies, size_t ies_len, int reassoc)
+{
+	struct ieee802_11_elems elems;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Station sent an invalid association request");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc);
+}
+
+
 static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
 			u16 reason_code)
 {
@@ -5067,6 +4278,8 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -5185,7 +4398,7 @@
 	}
 #endif /* CONFIG_IEEE80211AX */
 
-	p = hostapd_eid_ext_capab(hapd, p);
+	p = hostapd_eid_ext_capab(hapd, p, false);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
 	if (sta && sta->qos_map_enabled)
 		p = hostapd_eid_qos_map_set(hapd, p);
@@ -6131,10 +5344,10 @@
 
 			pos = &mgmt->u.action.u.public_action.action;
 			end = ((const u8 *) mgmt) + len;
-			gas_query_ap_rx(hapd->gas, mgmt->sa,
-					mgmt->u.action.category,
-					pos, end - pos, freq);
-			return 1;
+			if (gas_query_ap_rx(hapd->gas, mgmt->sa,
+					    mgmt->u.action.category,
+					    pos, end - pos, freq) == 0)
+				return 1;
 		}
 #endif /* CONFIG_DPP */
 		if (hapd->public_action_cb) {
@@ -6589,7 +5802,8 @@
 			ieee802_1x_receive(
 				hapd, mgmt->da,
 				wpabuf_head(sta->pending_eapol_rx->buf),
-				wpabuf_len(sta->pending_eapol_rx->buf));
+				wpabuf_len(sta->pending_eapol_rx->buf),
+				sta->pending_eapol_rx->encrypted);
 		}
 		wpabuf_free(sta->pending_eapol_rx->buf);
 		os_free(sta->pending_eapol_rx);
@@ -6692,6 +5906,19 @@
 		return;
 	}
 
+#ifdef CONFIG_HS20
+	if (ok && len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_WNM &&
+	    mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+	    sta->hs20_deauth_on_ack) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+			   " on acknowledging the WNM-Notification",
+			   MAC2STR(sta->addr));
+		ap_sta_session_timeout(hapd, sta, 0);
+		return;
+	}
+#endif /* CONFIG_HS20 */
+
 	if (len < 24 + 5 + sizeof(*report))
 		return;
 	report = (const struct rrm_measurement_report_element *)
@@ -7032,7 +6259,7 @@
 #endif /* CONFIG_IEEE80211AX */
 
 	switch (hostapd_get_oper_chwidth(iconf)) {
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (iconf->secondary_channel == 0) {
 			/* Max Transmit Power count = 0 (20 MHz) */
 			tx_pwr_count = 0;
@@ -7041,12 +6268,12 @@
 			tx_pwr_count = 1;
 		}
 		break;
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
 		tx_pwr_count = 2;
 		break;
-	case CHANWIDTH_80P80MHZ:
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
 		tx_pwr_count = 3;
 		break;
@@ -7449,7 +6676,7 @@
 				break;
 
 			*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
-			os_memcpy(eid, bss->conf->bssid, ETH_ALEN);
+			os_memcpy(eid, bss->own_addr, ETH_ALEN);
 			eid += ETH_ALEN;
 			os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
 			eid += 4;
@@ -7457,6 +6684,14 @@
 			    reporting_hapd->conf->ssid.short_ssid)
 				bss_param |= RNR_BSS_PARAM_SAME_SSID;
 
+			if (iface->conf->mbssid != MBSSID_DISABLED &&
+			    iface->num_bss > 1) {
+				bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+				if (i == 0)
+					bss_param |=
+						RNR_BSS_PARAM_TRANSMITTED_BSSID;
+			}
+
 			if (is_6ghz_op_class(hapd->iconf->op_class) &&
 			    bss->conf->unsol_bcast_probe_resp_interval)
 				bss_param |=
@@ -7545,4 +6780,322 @@
 	return eid;
 }
 
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+			     size_t known_bss_len)
+{
+	if (!known_bss || known_bss_len <= i / 8)
+		return false;
+	known_bss = &known_bss[i / 8];
+	return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+					  u32 frame_type, size_t *bss_index,
+					  const u8 *known_bss,
+					  size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t len = 3, i;
+
+	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;
+		u8 ie_count = 0;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+
+		/*
+		 * Sublement ID: 1 octet
+		 * Length: 1 octet
+		 * Nontransmitted capabilities: 4 octets
+		 * SSID element: 2 + variable
+		 * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+		 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+		 */
+		nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			nontx_profile_len += 2;
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn)
+				nontx_profile_len += 2 + rsn[1];
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx)
+				nontx_profile_len += 2 + rsnx[1];
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			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)
+			ie_count++;
+		if (ie_count)
+			nontx_profile_len += 4 + ie_count;
+
+		if (len + nontx_profile_len > 255)
+			break;
+
+		len += nontx_profile_len;
+	}
+
+	*bss_index = i;
+	return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len)
+{
+	size_t len = 0, bss_index = 1;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_type != WLAN_FC_STYPE_BEACON &&
+	     frame_type != WLAN_FC_STYPE_PROBE_RESP))
+		return 0;
+
+	if (frame_type == WLAN_FC_STYPE_BEACON) {
+		if (!elem_count) {
+			wpa_printf(MSG_INFO,
+				   "MBSSID: Insufficient data for Beacon frames");
+			return 0;
+		}
+		*elem_count = 0;
+	}
+
+	while (bss_index < hapd->iface->num_bss) {
+		len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+						   &bss_index, known_bss,
+						   known_bss_len);
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			*elem_count += 1;
+	}
+	return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+				    u32 frame_type, u8 max_bssid_indicator,
+				    size_t *bss_index, u8 elem_count,
+				    const u8 *known_bss, size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t i;
+	u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+	*eid++ = WLAN_EID_MULTIPLE_BSSID;
+	eid_len_offset = eid++;
+	max_bssid_indicator_offset = eid++;
+
+	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		struct hostapd_bss_config *conf;
+		u8 *eid_len_pos, *nontx_bss_start = eid;
+		const u8 *auth, *rsn = NULL, *rsnx = NULL;
+		u8 ie_count = 0, non_inherit_ie[3];
+		size_t auth_len = 0;
+		u16 capab_info;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+		conf = bss->conf;
+
+		*eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+		eid_len_pos = eid++;
+
+		capab_info = hostapd_own_capab_info(bss);
+		*eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+		*eid++ = sizeof(capab_info);
+		WPA_PUT_LE16(eid, capab_info);
+		eid += sizeof(capab_info);
+
+		*eid++ = WLAN_EID_SSID;
+		*eid++ = conf->ssid.ssid_len;
+		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+		eid += conf->ssid.ssid_len;
+
+		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+		if (frame_type == WLAN_FC_STYPE_BEACON) {
+			*eid++ = 3;
+			*eid++ = i; /* BSSID Index */
+			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			    (conf->dtim_period % elem_count))
+				conf->dtim_period = elem_count;
+			*eid++ = conf->dtim_period;
+			*eid++ = 0xFF; /* DTIM Count */
+		} else {
+			/* Probe Request frame does not include DTIM Period and
+			 * DTIM Count fields. */
+			*eid++ = 1;
+			*eid++ = i; /* BSSID Index */
+		}
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn) {
+				os_memcpy(eid, rsn, 2 + rsn[1]);
+				eid += 2 + rsn[1];
+			}
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx) {
+				os_memcpy(eid, rsnx, 2 + rsnx[1]);
+				eid += 2 + rsnx[1];
+			}
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+		if (hapd->conf->xrates_supported &&
+		    !bss->conf->xrates_supported)
+			non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+		if (ie_count) {
+			*eid++ = WLAN_EID_EXTENSION;
+			*eid++ = 2 + ie_count;
+			*eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+			*eid++ = ie_count;
+			os_memcpy(eid, non_inherit_ie, ie_count);
+			eid += ie_count;
+		}
+
+		*eid_len_pos = (eid - eid_len_pos) - 1;
+
+		if (((eid - eid_len_offset) - 1) > 255) {
+			eid = nontx_bss_start;
+			break;
+		}
+	}
+
+	*bss_index = i;
+	*max_bssid_indicator_offset = max_bssid_indicator;
+	if (*max_bssid_indicator_offset < 1)
+		*max_bssid_indicator_offset = 1;
+	*eid_len_offset = (eid - eid_len_offset) - 1;
+	return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len)
+{
+	size_t bss_index = 1;
+	u8 elem_index = 0;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_stype != WLAN_FC_STYPE_BEACON &&
+	     frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+		return eid;
+
+	if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+		wpa_printf(MSG_INFO,
+			   "MBSSID: Insufficient data for Beacon frames");
+		return eid;
+	}
+
+	while (bss_index < hapd->iface->num_bss) {
+		if (frame_stype == WLAN_FC_STYPE_BEACON) {
+			if (elem_index == elem_count) {
+				wpa_printf(MSG_WARNING,
+					   "MBSSID: Larger number of elements than there is room in the provided array");
+				break;
+			}
+
+			elem_offset[elem_index] = eid;
+			elem_index = elem_index + 1;
+		}
+		eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+					      hostapd_max_bssid_indicator(hapd),
+					      &bss_index, elem_count,
+					      known_bss, known_bss_len);
+	}
+
+	return eid;
+}
+
+
+static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
+{
+	u8 first_chan = *seg0 - 6, sec_chan;
+
+	switch (bitmap) {
+	case 0x6:
+		*seg0 = 0;
+		return;
+	case 0x8:
+	case 0x4:
+	case 0x2:
+	case 0x1:
+	case 0xC:
+	case 0x3:
+		if (pri_chan < *seg0)
+			*seg0 -= 4;
+		else
+			*seg0 += 4;
+		break;
+	}
+
+	if (pri_chan < *seg0)
+		sec_chan = pri_chan + 4;
+	else
+		sec_chan = pri_chan - 4;
+
+	if (bitmap & BIT((sec_chan - first_chan) / 4))
+		*seg0 = 0;
+}
+
+
+static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
+				       enum oper_chan_width *width, u8 *seg0)
+{
+	if (pri < *seg0) {
+		*seg0 -= 8;
+		if (bitmap & 0x0F) {
+			*width = 0;
+			punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+		}
+	} else {
+		*seg0 += 8;
+		if (bitmap & 0xF0) {
+			*width = 0;
+			punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
+						  seg0);
+		}
+	}
+}
+
+
+void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
+			    u8 *seg0, u8 *seg1)
+{
+	if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
+		*width = CONF_OPER_CHWIDTH_USE_HT;
+		punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+	}
+
+	if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
+		*width = CONF_OPER_CHWIDTH_80MHZ;
+		*seg1 = 0;
+		punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
+	}
+
+	/* TODO: 320 MHz */
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index fa1f47b..1e4c843 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -18,6 +18,7 @@
 struct ieee80211_mgmt;
 struct radius_sta;
 enum ieee80211_op_mode;
+enum oper_chan_width;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
@@ -45,7 +46,8 @@
 #endif /* NEED_AP_MLME */
 u16 hostapd_own_capab_info(struct hostapd_data *hapd);
 void ap_ht2040_timeout(void *eloop_data, void *user_data);
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete);
 u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
@@ -214,5 +216,14 @@
 		       enum ieee80211_op_mode opmode,
 		       const u8 *he_capab, size_t he_capab_len,
 		       const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+			    enum oper_chan_width *width, u8 *seg0, u8 *seg1);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index dbbf9a6..6ebe0f9 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -41,25 +41,58 @@
 }
 
 
-static u8 ieee80211_eht_mcs_set_size(const u8 *he_phy_cap,
+static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
+				     u8 he_oper_chwidth, const u8 *he_phy_cap,
 				     const u8 *eht_phy_cap)
 {
 	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+	bool band24, band5, band6;
+	u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
 
-	if ((he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
-	    (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
+	switch (he_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		he_phy_cap_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_160MHZ:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_USE_HT:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		break;
+	}
+
+	he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+
+	band24 = mode == HOSTAPD_MODE_IEEE80211B ||
+		mode == HOSTAPD_MODE_IEEE80211G ||
+		mode == NUM_HOSTAPD_MODES;
+	band5 = mode == HOSTAPD_MODE_IEEE80211A ||
+		mode == NUM_HOSTAPD_MODES;
+	band6 = is_6ghz_op_class(opclass);
+
+	if (band24 &&
+	    (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
 
-	if (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
-	    (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))
-		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+	if (band5 &&
+	    (he_phy_cap_chwidth &
+	     (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
+		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
 
-	if (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
-	    EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)
+	if (band5 &&
+	    (he_phy_cap_chwidth &
+	     (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
+	    sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+	if (band6 &&
+	    (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
 		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
 
 	return sz;
@@ -81,7 +114,9 @@
 	if (!eht_cap->eht_supported)
 		return 0;
 
-	len += ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+	len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+					  hapd->iconf->he_oper_chwidth,
+					  mode->he_capab[opmode].phy_cap,
 					  eht_cap->phy_cap);
 	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
 				       eht_cap->phy_cap);
@@ -133,7 +168,10 @@
 
 	pos = cap->optional;
 
-	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
+						 hapd->iconf->op_class,
+						 hapd->iconf->he_oper_chwidth,
+						 mode->he_capab[opmode].phy_cap,
 						 eht_cap->phy_cap);
 	if (mcs_nss_len) {
 		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
@@ -157,18 +195,29 @@
 {
 	struct hostapd_config *conf = hapd->iconf;
 	struct ieee80211_eht_operation *oper;
-	u8 *pos = eid, chwidth, seg0 = 0, seg1 = 0;
+	u8 *pos = eid, seg0 = 0, seg1 = 0;
+	enum oper_chan_width chwidth;
+	size_t elen = 1 + 4 + 3;
 
 	if (!hapd->iface->current_mode)
 		return eid;
 
+	if (hapd->iconf->punct_bitmap)
+		elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 5;
+	*pos++ = 1 + elen;
 	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
 
 	oper = (struct ieee80211_eht_operation *) pos;
 	oper->oper_params = EHT_OPER_INFO_PRESENT;
 
+	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+	oper->basic_eht_mcs_nss_set[0] = 0x11;
+	oper->basic_eht_mcs_nss_set[1] = 0x00;
+	oper->basic_eht_mcs_nss_set[2] = 0x00;
+	oper->basic_eht_mcs_nss_set[3] = 0x00;
+
 	if (is_6ghz_op_class(conf->op_class))
 		chwidth = op_class_to_ch_width(conf->op_class);
 	else
@@ -177,9 +226,7 @@
 	seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
 
 	switch (chwidth) {
-#if 0 /* FIX: Need to clean up CHANWIDTH_* use for protocol vs. internal
-       * needs to be able to define this. */
-	case CHANWIDTH_320MHZ:
+	case CONF_OPER_CHWIDTH_320MHZ:
 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
 		seg1 = seg0;
 		if (hapd->iconf->channel < seg0)
@@ -187,8 +234,7 @@
 		else
 			seg0 += 16;
 		break;
-#endif
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
 		seg1 = seg0;
 		if (hapd->iconf->channel < seg0)
@@ -196,10 +242,10 @@
 		else
 			seg0 += 8;
 		break;
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
 		break;
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (seg0)
 			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
 		break;
@@ -210,7 +256,13 @@
 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
 	oper->oper_info.ccfs1 = seg1;
 
-	return pos + 4;
+	if (hapd->iconf->punct_bitmap) {
+		oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
+		oper->oper_info.disabled_chan_bitmap =
+			host_to_le16(hapd->iconf->punct_bitmap);
+	}
+
+	return pos + elen;
 }
 
 
@@ -257,7 +309,9 @@
 	capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
 	sta_mcs = capab->optional;
 
-	if (ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+	if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+				       hapd->iconf->he_oper_chwidth,
+				       mode->he_capab[opmode].phy_cap,
 				       mode->eht_capab[opmode].phy_cap) ==
 	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
 		return check_valid_eht_mcs_nss(
@@ -265,10 +319,14 @@
 			EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
 
 	switch (hapd->iface->conf->eht_oper_chwidth) {
-	/* TODO: CHANWIDTH_320MHZ */
-	case CHANWIDTH_80P80MHZ:
-	case CHANWIDTH_160MHZ:
-		mcs_count = 2;
+	case CONF_OPER_CHWIDTH_320MHZ:
+		mcs_count++;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
+		mcs_count++;
+		break;
+	default:
 		break;
 	}
 
@@ -277,7 +335,9 @@
 }
 
 
-static bool ieee80211_invalid_eht_cap_size(const u8 *he_cap, const u8 *eht_cap,
+static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
+					   u8 opclass, u8 he_oper_chwidth,
+					   const u8 *he_cap, const u8 *eht_cap,
 					   size_t len)
 {
 	const struct ieee80211_he_capabilities *he_capab;
@@ -293,7 +353,8 @@
 	if (len < cap_len)
 		return true;
 
-	cap_len += ieee80211_eht_mcs_set_size(he_phy_cap, cap->phy_cap);
+	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+					      he_phy_cap, cap->phy_cap);
 	if (len < cap_len)
 		return true;
 
@@ -310,10 +371,15 @@
 		       const u8 *he_capab, size_t he_capab_len,
 		       const u8 *eht_capab, size_t eht_capab_len)
 {
+	struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
+	enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
+
 	if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
 	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
 	    !eht_capab ||
-	    ieee80211_invalid_eht_cap_size(he_capab, eht_capab,
+	    ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+					   hapd->iconf->he_oper_chwidth,
+					   he_capab, eht_capab,
 					   eht_capab_len) ||
 	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
 		sta->flags &= ~WLAN_STA_EHT;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 1e74c58..548a448 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -102,20 +102,22 @@
 					   mode->he_capab[opmode].phy_cap);
 
 	switch (hapd->iface->conf->he_oper_chwidth) {
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		he_oper_chwidth |=
 			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
 		mcs_nss_size += 4;
 		/* fall through */
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
 		mcs_nss_size += 4;
 		/* fall through */
-	case CHANWIDTH_80MHZ:
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
 			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
 		break;
+	default:
+		break;
 	}
 
 	ie_size += mcs_nss_size + ppet_size;
@@ -217,10 +219,20 @@
 	pos += 6; /* skip the fixed part */
 
 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
-		u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
+		enum oper_chan_width oper_chwidth =
+			hostapd_get_oper_chwidth(hapd->iconf);
+		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
 		u8 control;
 
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->iconf->punct_bitmap) {
+			punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+					       hapd->iconf->channel,
+					       &oper_chwidth, &seg0, &seg1);
+		}
+#endif /* CONFIG_IEEE80211BE */
+
 		if (!seg0)
 			seg0 = hapd->iconf->channel;
 
@@ -251,7 +263,7 @@
 		*pos++ = control;
 
 		/* Channel Center Freq Seg0/Seg1 */
-		if (hapd->iconf->he_oper_chwidth == 2) {
+		if (oper_chwidth == 2) {
 			/*
 			 * Seg 0 indicates the channel center frequency index of
 			 * the 160 MHz channel.
@@ -430,10 +442,10 @@
 	 * band/stream cases.
 	 */
 	switch (hapd->iface->conf->he_oper_chwidth) {
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		mcs_count = 3;
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		mcs_count = 2;
 		break;
 	default:
@@ -531,7 +543,8 @@
 	u8 *mac_cap;
 
 	if (!hapd->iface->current_mode ||
-	    !hapd->iface->current_mode->he_capab[mode].he_supported)
+	    !hapd->iface->current_mode->he_capab[mode].he_supported ||
+	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
 		return 0;
 
 	mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 6154895..31dfb62 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -17,6 +17,7 @@
 #include "ap_config.h"
 #include "ap_drv_ops.h"
 #include "wpa_auth.h"
+#include "dpp_hostapd.h"
 #include "ieee802_11.h"
 
 
@@ -339,7 +340,8 @@
 }
 
 
-static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+				   bool mbssid_complete)
 {
 	*pos = 0x00;
 
@@ -363,6 +365,8 @@
 			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
 		if (hapd->conf->bss_transition)
 			*pos |= 0x08; /* Bit 19 - BSS Transition */
+		if (hapd->iconf->mbssid)
+			*pos |= 0x40; /* Bit 22 - Multiple BSSID */
 		break;
 	case 3: /* Bits 24-31 */
 #ifdef CONFIG_WNM_AP
@@ -412,8 +416,7 @@
 			*pos |= 0x01;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211AX
-		if (hapd->iconf->ieee80211ax &&
-		    hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
+		if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
 			*pos |= 0x40; /* Bit 78 - TWT responder */
 #endif /* CONFIG_IEEE80211AX */
 		break;
@@ -435,6 +438,11 @@
 		    (hapd->iface->drv_flags &
 		     WPA_DRIVER_FLAGS_BEACON_PROTECTION))
 			*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+		if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+			*pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+		if (mbssid_complete)
+			*pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+				       * Profiles */
 		break;
 	case 11: /* Bits 88-95 */
 #ifdef CONFIG_SAE_PK
@@ -448,7 +456,8 @@
 }
 
 
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete)
 {
 	u8 *pos = eid;
 	u8 len = EXT_CAPA_MAX_LEN, i;
@@ -459,7 +468,7 @@
 	*pos++ = WLAN_EID_EXT_CAPAB;
 	*pos++ = len;
 	for (i = 0; i < len; i++, pos++) {
-		hostapd_ext_capab_byte(hapd, pos, i);
+		hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
 
 		if (i < hapd->iface->extended_capa_len) {
 			*pos &= ~hapd->iface->extended_capa_mask[i];
@@ -470,6 +479,13 @@
 			*pos &= ~hapd->conf->ext_capa_mask[i];
 			*pos |= hapd->conf->ext_capa[i];
 		}
+
+		/* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+		 * otherwise association fails with some clients */
+		if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+			*pos &= ~0x08;
+		if (i == 2 && !hapd->iconf->mbssid)
+			*pos &= ~0x40;
 	}
 
 	while (len > 0 && eid[1 + len] == 0) {
@@ -873,7 +889,7 @@
 size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
 {
 #ifdef CONFIG_DPP2
-	if (hapd->conf->dpp_configurator_connectivity)
+	if (hostapd_dpp_configurator_connectivity(hapd))
 		return 6;
 #endif /* CONFIG_DPP2 */
 	return 0;
@@ -885,7 +901,7 @@
 	u8 *pos = eid;
 
 #ifdef CONFIG_DPP2
-	if (!hapd->conf->dpp_configurator_connectivity || len < 6)
+	if (!hostapd_dpp_configurator_connectivity(hapd) || len < 6)
 		return pos;
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -998,7 +1014,7 @@
 		 * If a VHT Operation element was present, use it to determine
 		 * the supported channel bandwidth.
 		 */
-		if (oper->vht_op_info_chwidth == 0) {
+		if (oper->vht_op_info_chwidth == CHANWIDTH_USE_HT) {
 			requested_bw = ht_40mhz ? 40 : 20;
 		} else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
 			requested_bw = 80;
@@ -1062,9 +1078,11 @@
 #endif /* CONFIG_SAE_PK */
 
 	if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-	    (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
-	     hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk) &&
-	    hapd->conf->sae_pwe != 3) {
+	    (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     hapd->conf->sae_pwe == SAE_PWE_BOTH ||
+	     hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
+	    hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 		if (sae_pk)
@@ -1072,12 +1090,12 @@
 #endif /* CONFIG_SAE_PK */
 	}
 
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 
 	flen = (capab & 0xff00) ? 2 : 1;
 	if (len < 2 + flen || !capab)
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 828f0ab..db615a3 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -75,6 +75,10 @@
 {
 	struct ieee80211_vht_operation *oper;
 	u8 *pos = eid;
+	enum oper_chan_width oper_chwidth =
+		hostapd_get_oper_chwidth(hapd->iconf);
+	u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+	u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
 
 	if (is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
@@ -85,23 +89,29 @@
 	oper = (struct ieee80211_vht_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->punct_bitmap) {
+		punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+				       hapd->iconf->channel,
+				       &oper_chwidth, &seg0, &seg1);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	/*
 	 * center freq = 5 GHz + (5 * index)
 	 * So index 42 gives center freq 5.210 GHz
 	 * which is channel 42 in 5G band
 	 */
-	oper->vht_op_info_chan_center_freq_seg0_idx =
-		hapd->iconf->vht_oper_centr_freq_seg0_idx;
-	oper->vht_op_info_chan_center_freq_seg1_idx =
-		hapd->iconf->vht_oper_centr_freq_seg1_idx;
+	oper->vht_op_info_chan_center_freq_seg0_idx = seg0;
+	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
 
-	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
-	if (hapd->iconf->vht_oper_chwidth == 2) {
+	oper->vht_op_info_chwidth = oper_chwidth;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
 		/*
 		 * Convert 160 MHz channel width to new style as interop
 		 * workaround.
 		 */
-		oper->vht_op_info_chwidth = 1;
+		oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
 		oper->vht_op_info_chan_center_freq_seg1_idx =
 			oper->vht_op_info_chan_center_freq_seg0_idx;
 		if (hapd->iconf->channel <
@@ -109,12 +119,12 @@
 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
 		else
 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-	} else if (hapd->iconf->vht_oper_chwidth == 3) {
+	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
 		/*
 		 * Convert 80+80 MHz channel width to new style as interop
 		 * workaround.
 		 */
-		oper->vht_op_info_chwidth = 1;
+		oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
 	}
 
 	/* VHT Basic MCS set comes from hw */
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index fb5e920..46a47d0 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -998,7 +998,7 @@
 
 
 static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
-				  size_t len)
+				  size_t len, enum frame_encryption encrypted)
 {
 	if (sta->pending_eapol_rx) {
 		wpabuf_free(sta->pending_eapol_rx->buf);
@@ -1016,21 +1016,39 @@
 		return;
 	}
 
+	sta->pending_eapol_rx->encrypted = encrypted;
 	os_get_reltime(&sta->pending_eapol_rx->rx_time);
 }
 
 
+static bool ieee802_1x_check_encryption(struct sta_info *sta,
+					enum frame_encryption encrypted,
+					u8 type)
+{
+	if (encrypted != FRAME_NOT_ENCRYPTED)
+		return true;
+	if (type != IEEE802_1X_TYPE_EAP_PACKET &&
+	    type != IEEE802_1X_TYPE_EAPOL_START &&
+	    type != IEEE802_1X_TYPE_EAPOL_LOGOFF)
+		return true;
+	if (!(sta->flags & WLAN_STA_MFP))
+		return true;
+	return !wpa_auth_pairwise_set(sta->wpa_sm);
+}
+
+
 /**
  * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
  * @hapd: hostapd BSS data
  * @sa: Source address (sender of the EAPOL frame)
  * @buf: EAPOL frame
  * @len: Length of buf in octets
+ * @encrypted: Whether the frame was encrypted
  *
  * This function is called for each incoming EAPOL frame from the interface
  */
 void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
-			size_t len)
+			size_t len, enum frame_encryption encrypted)
 {
 	struct sta_info *sta;
 	struct ieee802_1x_hdr *hdr;
@@ -1043,8 +1061,9 @@
 	    !hapd->conf->wps_state)
 		return;
 
-	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
-		   (unsigned long) len, MAC2STR(sa));
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR
+		   " (encrypted=%d)",
+		   (unsigned long) len, MAC2STR(sa), encrypted);
 	sta = ap_get_sta(hapd, sa);
 	if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
 		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
@@ -1054,7 +1073,7 @@
 		if (sta && (sta->flags & WLAN_STA_AUTH)) {
 			wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
 				   " for later use", MAC2STR(sta->addr));
-			ieee802_1x_save_eapol(sta, buf, len);
+			ieee802_1x_save_eapol(sta, buf, len, encrypted);
 		}
 
 		return;
@@ -1114,6 +1133,12 @@
 		return;
 	}
 
+	if (!ieee802_1x_check_encryption(sta, encrypted, hdr->type)) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Discard unencrypted EAPOL message - encryption was expected");
+		return;
+	}
+
 	if (!sta->eapol_sm) {
 		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
 		if (!sta->eapol_sm)
@@ -1684,23 +1709,35 @@
 
 
 static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
-				       struct sta_info *sta, u8 *pos,
+				       struct sta_info *sta, const u8 *pos,
 				       size_t len)
 {
+	size_t url_len;
+	unsigned int timeout;
+
 	if (len < 3)
 		return; /* Malformed information */
+	url_len = len - 3;
 	sta->hs20_deauth_requested = 1;
+	sta->hs20_deauth_on_ack = url_len == 0;
 	wpa_printf(MSG_DEBUG,
-		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u",
-		   *pos, WPA_GET_LE16(pos + 1));
+		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u  URL length %zu",
+		   *pos, WPA_GET_LE16(pos + 1), url_len);
 	wpabuf_free(sta->hs20_deauth_req);
 	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
 	if (sta->hs20_deauth_req) {
 		wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
-		wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
-		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+		wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
 	}
-	ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+	timeout = hapd->conf->hs20_deauth_req_timeout;
+	/* If there is no URL, no need to provide time to fetch it. Use a short
+	 * timeout here to allow maximum time for completing 4-way handshake and
+	 * WNM-Notification delivery. Acknowledgement of the frame will result
+	 * in cutting this wait further. */
+	if (!url_len && timeout > 2)
+		timeout = 2;
+	ap_sta_session_timeout(hapd, sta, timeout);
 }
 
 
@@ -1788,6 +1825,7 @@
 	buf = NULL;
 	sta->remediation = 0;
 	sta->hs20_deauth_requested = 0;
+	sta->hs20_deauth_on_ack = 0;
 
 	for (;;) {
 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index 70dc11a..1469351 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -19,7 +19,7 @@
 
 
 void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
-			size_t len);
+			size_t len, enum frame_encryption encrypted);
 void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
 void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
 
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index e37324f..5b276e8 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -136,7 +136,7 @@
 
 	os_memcpy(entry->bssid, bssid, ETH_ALEN);
 	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
-	entry->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
+	entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
 
 	entry->nr = wpabuf_dup(nr);
 	if (!entry->nr)
@@ -199,19 +199,21 @@
 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
 						    int ht, int vht, int he)
 {
-	u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+	enum oper_chan_width oper_chwidth;
+
+	oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
 
 	if (!ht && !vht && !he)
 		return NR_CHAN_WIDTH_20;
 	if (!hapd->iconf->secondary_channel)
 		return NR_CHAN_WIDTH_20;
-	if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
+	if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
 		return NR_CHAN_WIDTH_40;
-	if (oper_chwidth == CHANWIDTH_80MHZ)
+	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 		return NR_CHAN_WIDTH_80;
-	if (oper_chwidth == CHANWIDTH_160MHZ)
+	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
 		return NR_CHAN_WIDTH_160;
-	if (oper_chwidth == CHANWIDTH_80P80MHZ)
+	if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
 		return NR_CHAN_WIDTH_80P80;
 	return NR_CHAN_WIDTH_20;
 }
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index b67b852..32d291d 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -40,6 +40,7 @@
 {
 	os_free(entry->vlan_desc);
 	os_free(entry->identity);
+	os_free(entry->dpp_pkhash);
 	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&entry->radius_class);
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 2ef2174..e3cee4a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -23,6 +23,8 @@
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 spa[ETH_ALEN];
 
+	u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+			 * public key */
 	u8 *identity;
 	size_t identity_len;
 	struct wpabuf *cui;
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
index 2ff1861..3284a10 100644
--- a/src/ap/preauth_auth.c
+++ b/src/ap/preauth_auth.c
@@ -90,7 +90,7 @@
 		return;
 	sta->preauth_iface = piface;
 	ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
-			   len - sizeof(*ethhdr));
+			   len - sizeof(*ethhdr), FRAME_ENCRYPTION_UNKNOWN);
 }
 
 
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c541926..63f514c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1260,6 +1260,13 @@
 }
 
 
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
 void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
@@ -1298,10 +1305,13 @@
 					sta->addr, authorized, dev_addr);
 
 	if (authorized) {
+		const u8 *dpp_pkhash;
 		const char *keyid;
+		char dpp_pkhash_buf[100];
 		char keyid_buf[100];
 		char ip_addr[100];
 
+		dpp_pkhash_buf[0] = '\0';
 		keyid_buf[0] = '\0';
 		ip_addr[0] = '\0';
 #ifdef CONFIG_P2P
@@ -1319,14 +1329,27 @@
 				    " keyid=%s", keyid);
 		}
 
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
-			buf, ip_addr, keyid_buf);
+		dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+		if (dpp_pkhash) {
+			const char *prefix = " dpp_pkhash=";
+			size_t plen = os_strlen(prefix);
+
+			os_strlcpy(dpp_pkhash_buf, prefix,
+				   sizeof(dpp_pkhash_buf));
+			wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+					 sizeof(dpp_pkhash_buf) - plen,
+					 dpp_pkhash, SHA256_MAC_LEN);
+		}
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s%s%s",
-					  buf, ip_addr, keyid_buf);
+					  AP_STA_CONNECTED "%s%s%s%s",
+					  buf, ip_addr, keyid_buf,
+					  dpp_pkhash_buf);
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index af8f171..b59b758 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -16,6 +16,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -65,43 +66,7 @@
 struct pending_eapol_rx {
 	struct wpabuf *buf;
 	struct os_reltime rx_time;
-};
-
-enum pasn_fils_state {
-	PASN_FILS_STATE_NONE = 0,
-	PASN_FILS_STATE_PENDING_AS,
-	PASN_FILS_STATE_COMPLETE
-};
-
-struct pasn_fils_data {
-	u8 state;
-	u8 nonce[FILS_NONCE_LEN];
-	u8 anonce[FILS_NONCE_LEN];
-	u8 session[FILS_SESSION_LEN];
-	u8 erp_pmkid[PMKID_LEN];
-
-	struct wpabuf *erp_resp;
-};
-
-struct pasn_data {
-	int akmp;
-	int cipher;
-	u16 group;
-	u8 trans_seq;
-	u8 wrapped_data_format;
-	size_t kdk_len;
-
-	u8 hash[SHA384_MAC_LEN];
-	struct wpa_ptk ptk;
-	struct crypto_ecdh *ecdh;
-
-	struct wpabuf *secret;
-#ifdef CONFIG_SAE
-	struct sae_data sae;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-	struct pasn_fils_data fils;
-#endif /* CONFIG_FILS */
+	enum frame_encryption encrypted;
 };
 
 struct sta_info {
@@ -153,6 +118,7 @@
 	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;
 	unsigned int radius_das_match:1;
 	unsigned int ecsa_supported:1;
@@ -388,6 +354,8 @@
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
 const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
 				  struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta);
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *addr, u16 reason);
 
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 23a352c..153ee40 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -643,6 +643,133 @@
 }
 
 
+
+static const char * wnm_event_type2str(enum wnm_event_report_type wtype)
+{
+#define W2S(wtype) case WNM_EVENT_TYPE_ ## wtype: return #wtype;
+	switch (wtype) {
+	W2S(TRANSITION)
+	W2S(RSNA)
+	W2S(P2P_LINK)
+	W2S(WNM_LOG)
+	W2S(BSS_COLOR_COLLISION)
+	W2S(BSS_COLOR_IN_USE)
+	}
+	return "UNKNOWN";
+#undef W2S
+}
+
+
+static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
+					   const u8 *addr, const u8 *buf,
+					   size_t len)
+{
+	struct sta_info *sta;
+	u8 dialog_token;
+	struct wnm_event_report_element *report_ie;
+	const u8 *pos = buf, *end = buf + len;
+	const size_t fixed_field_len = 3; /* Event Token/Type/Report Status */
+#ifdef CONFIG_IEEE80211AX
+	const size_t tsf_len = 8;
+	u8 color;
+	u64 bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
+	if (end - pos < 1 + 2) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore too short WNM Event Report frame from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	dialog_token = *pos++;
+	report_ie = (struct wnm_event_report_element *) pos;
+
+	if (end - pos < 2 + report_ie->len ||
+	    report_ie->len < fixed_field_len) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore truncated WNM Event Report frame from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	if (report_ie->eid != WLAN_EID_EVENT_REPORT ||
+	    report_ie->status != WNM_STATUS_SUCCESSFUL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WNM: Received WNM Event Report frame from "
+		   MACSTR " dialog_token=%u event_token=%u type=%d (%s)",
+		   MAC2STR(addr), dialog_token, report_ie->token,
+		   report_ie->type, wnm_event_type2str(report_ie->type));
+
+	pos += 2 + fixed_field_len;
+	wpa_hexdump(MSG_MSGDUMP, "WNM: Event Report", pos, end - pos);
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for received WNM Event Report",
+			   MAC2STR(addr));
+		return;
+	}
+
+	switch (report_ie->type) {
+#ifdef CONFIG_IEEE80211AX
+	case WNM_EVENT_TYPE_BSS_COLOR_COLLISION:
+		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+			return;
+		if (report_ie->len <
+		    fixed_field_len + tsf_len + 8) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Too short BSS color collision event report from "
+				   MACSTR, MAC2STR(addr));
+			return;
+		}
+		bitmap = WPA_GET_LE64(report_ie->u.bss_color_collision.color_bitmap);
+		wpa_printf(MSG_DEBUG,
+			   "WNM: BSS color collision bitmap 0x%llx reported by "
+			   MACSTR, (unsigned long long) bitmap, MAC2STR(addr));
+		hostapd_switch_color(hapd->iface->bss[0], bitmap);
+		break;
+	case WNM_EVENT_TYPE_BSS_COLOR_IN_USE:
+		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+			return;
+		if (report_ie->len < fixed_field_len + tsf_len + 1) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Too short BSS color in use event report from "
+				   MACSTR, MAC2STR(addr));
+			return;
+		}
+		color = report_ie->u.bss_color_in_use.color;
+		if (color > 63) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Invalid BSS color %u report from "
+				   MACSTR, color, MAC2STR(addr));
+			return;
+		}
+		if (color == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: BSS color use report canceled by "
+				   MACSTR, MAC2STR(addr));
+			/* TODO: Clear stored color from the collision bitmap
+			 * if there are no other users for it. */
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "WNM: BSS color %u use report by "
+			   MACSTR, color, MAC2STR(addr));
+		hapd->color_collision_bitmap |= 1ULL << color;
+		break;
+#endif /* CONFIG_IEEE80211AX */
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "WNM Event Report type=%d (%s) not supported",
+			   report_ie->type,
+			   wnm_event_type2str(report_ie->type));
+		break;
+	}
+}
+
+
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
 				const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -658,6 +785,10 @@
 	plen = len - IEEE80211_HDRLEN - 2;
 
 	switch (action) {
+	case WNM_EVENT_REPORT:
+		ieee802_11_rx_wnm_event_report(hapd, mgmt->sa, payload,
+					       plen);
+		return 0;
 	case WNM_BSS_TRANS_MGMT_QUERY:
 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
 						   plen);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index ad91883..635a74a 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -23,6 +23,7 @@
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "drivers/driver.h"
@@ -35,7 +36,7 @@
 
 #define STATE_MACHINE_DATA struct wpa_state_machine
 #define STATE_MACHINE_DEBUG_PREFIX "WPA"
-#define STATE_MACHINE_ADDR sm->addr
+#define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
 
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -58,7 +59,9 @@
 				       struct wpa_group *group);
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 			  const u8 *pmk, unsigned int pmk_len,
-			  struct wpa_ptk *ptk, int force_sha256);
+			  struct wpa_ptk *ptk, int force_sha256,
+			  u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			  size_t *key_len);
 static void wpa_group_free(struct wpa_authenticator *wpa_auth,
 			   struct wpa_group *group);
 static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@@ -79,6 +82,18 @@
 static const int dot11RSNAConfigSATimeout = 60;
 
 
+static const u8 * wpa_auth_get_aa(const struct wpa_state_machine *sm)
+{
+	return sm->wpa_auth->addr;
+}
+
+
+static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+{
+	return sm->addr;
+}
+
+
 static inline int wpa_auth_mic_failure_report(
 	struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
@@ -149,6 +164,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
 static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, int idx, u8 *seq)
 {
@@ -238,13 +267,14 @@
 }
 
 
-void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
-			   const u8 *addr, int cipher)
+static void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
+				  const u8 *addr, int cipher)
 {
 	if (wpa_auth->cb->clear_ptksa)
 		wpa_auth->cb->clear_ptksa(wpa_auth->cb_ctx, addr, cipher);
 }
 
+
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		     logger_level level, const char *txt)
 {
@@ -363,7 +393,8 @@
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
 	struct wpa_state_machine *sm = timeout_ctx;
 
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"rekeying PTK");
 	wpa_request_new_ptk(sm);
 	wpa_sm_step(sm);
 }
@@ -373,7 +404,8 @@
 {
 	if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
 		wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
-			   MACSTR " (%d seconds)", MAC2STR(sm->addr),
+			   MACSTR " (%d seconds)",
+			   MAC2STR(wpa_auth_get_spa(sm)),
 			   sm->wpa_auth->conf.wpa_ptk_rekey);
 		eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 		eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
@@ -680,7 +712,7 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (sm->ft_completed) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"FT authentication already completed - do not start 4-way handshake");
 		/* Go to PTKINITDONE state to allow GTK rekeying */
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
@@ -691,7 +723,7 @@
 
 #ifdef CONFIG_FILS
 	if (sm->fils_completed) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"FILS authentication already completed - do not start 4-way handshake");
 		/* Go to PTKINITDONE state to allow GTK rekeying */
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
@@ -706,7 +738,7 @@
 		return wpa_sm_step(sm);
 	}
 
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"start authentication");
 	sm->started = 1;
 
@@ -735,16 +767,14 @@
 {
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr)) {
-		u32 start;
 		wpa_printf(MSG_DEBUG,
 			   "P2P: Free assigned IP address %u.%u.%u.%u from "
-			   MACSTR,
+			   MACSTR " (bit %u)",
 			   sm->ip_addr[0], sm->ip_addr[1],
 			   sm->ip_addr[2], sm->ip_addr[3],
-			   MAC2STR(sm->addr));
-		start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
-		bitfield_clear(sm->wpa_auth->ip_pool,
-			       WPA_GET_BE32(sm->ip_addr) - start);
+			   MAC2STR(wpa_auth_get_spa(sm)),
+			   sm->ip_addr_bit);
+		bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
 	}
 #endif /* CONFIG_P2P */
 	if (sm->GUpdateStationKeys) {
@@ -775,7 +805,7 @@
 
 	wpa_auth = sm->wpa_auth;
 	if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"strict rekeying - force GTK rekey since STA is leaving");
 		if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
 					  wpa_auth, NULL) == -1)
@@ -795,7 +825,7 @@
 		 * Freeing will be completed in the end of wpa_sm_step(). */
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Registering pending STA state machine deinit for "
-			   MACSTR, MAC2STR(sm->addr));
+			   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 		sm->pending_deinit = 1;
 	} else
 		wpa_free_sta_sm(sm);
@@ -810,7 +840,7 @@
 	if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
 		wpa_printf(MSG_INFO,
 			   "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
-			   MAC2STR(sm->addr));
+			   MAC2STR(wpa_auth_get_spa(sm)));
 		sm->Disconnect = true;
 		/* Try to encourage the STA to reconnect */
 		sm->disconnect_reason =
@@ -908,18 +938,19 @@
 				    struct wpa_state_machine *sm, int group)
 {
 	/* Supplicant reported a Michael MIC error */
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "received EAPOL-Key Error Request (STA detected Michael MIC failure (group=%d))",
 			 group);
 
 	if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"ignore Michael MIC failure report since group cipher is not TKIP");
 	} else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"ignore Michael MIC failure report since pairwise cipher is not TKIP");
 	} else {
-		if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+		if (wpa_auth_mic_failure_report(wpa_auth,
+						wpa_auth_get_spa(sm)) > 0)
 			return 1; /* STA entry was removed */
 		sm->dot11RSNAStatsTKIPRemoteMICFailures++;
 		wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
@@ -942,6 +973,10 @@
 	const u8 *pmk = NULL;
 	size_t pmk_len;
 	int vlan_id = 0;
+	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t key_len;
+	int ret = -1;
 
 	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
@@ -963,8 +998,8 @@
 			pmk_len = sm->pmk_len;
 		}
 
-		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) <
-		    0)
+		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0,
+				   pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0)
 			break;
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -985,7 +1020,7 @@
 	if (!ok) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Earlier SNonce did not result in matching MIC");
-		return -1;
+		goto fail;
 	}
 
 	wpa_printf(MSG_DEBUG,
@@ -994,14 +1029,26 @@
 
 	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
 	    wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
-		return -1;
+		goto fail;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+		wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+		wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+				       key_len);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = true;
 
-	return 0;
+	ret = 0;
+fail:
+	forced_memzero(pmk_r0, sizeof(pmk_r0));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
+	return ret;
 }
 
 
@@ -1051,7 +1098,7 @@
 	key_data_length = WPA_GET_BE16(mic + mic_len);
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
 		   " key_info=0x%x type=%u mic_len=%zu key_data_length=%u",
-		   MAC2STR(sm->addr), key_info, key->type,
+		   MAC2STR(wpa_auth_get_spa(sm)), key_info, key->type,
 		   mic_len, key_data_length);
 	wpa_hexdump(MSG_MSGDUMP,
 		    "WPA: EAPOL-Key header (ending before Key MIC)",
@@ -1126,7 +1173,7 @@
 			if (wpa_use_cmac(sm->wpa_key_mgmt) &&
 			    !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-				wpa_auth_logger(wpa_auth, sm->addr,
+				wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
 						LOGGER_WARNING,
 						"advertised support for AES-128-CMAC, but did not use it");
 				return;
@@ -1135,7 +1182,7 @@
 			if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
 			    !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-				wpa_auth_logger(wpa_auth, sm->addr,
+				wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
 						LOGGER_WARNING,
 						"did not use HMAC-SHA1-AES with CCMP/GCMP");
 				return;
@@ -1144,7 +1191,8 @@
 
 		if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
 			return;
 		}
@@ -1154,7 +1202,8 @@
 		if (sm->req_replay_counter_used &&
 		    os_memcmp(key->replay_counter, sm->req_replay_counter,
 			      WPA_REPLAY_COUNTER_LEN) <= 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_WARNING,
 					"received EAPOL-Key request with replayed counter");
 			return;
 		}
@@ -1177,7 +1226,8 @@
 			 * pending requests, so allow the SNonce to be updated
 			 * even if we have already sent out EAPOL-Key 3/4.
 			 */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
 			sm->update_snonce = 1;
 			os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
@@ -1197,7 +1247,8 @@
 			 * there was two EAPOL-Key 2/4 messages and they had
 			 * different SNonce values.
 			 */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
 			goto continue_processing;
 		}
@@ -1206,11 +1257,13 @@
 		    wpa_replay_counter_valid(sm->prev_key_replay,
 					     key->replay_counter) &&
 		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "ignore retransmitted EAPOL-Key %s - SNonce did not change",
 					 msgtxt);
 		} else {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "received EAPOL-Key %s with unexpected replay counter",
 					 msgtxt);
 		}
@@ -1230,7 +1283,7 @@
 #ifdef CONFIG_FILS
 	if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
 	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
 		return;
 	}
@@ -1242,7 +1295,8 @@
 		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
 		    (!sm->update_snonce ||
 		     sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_state);
 			return;
@@ -1269,7 +1323,8 @@
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
 		    !sm->PTK_valid) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_state);
 			return;
@@ -1278,7 +1333,8 @@
 	case GROUP_2:
 		if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
 		    || !sm->PTK_valid) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_group_state);
 			return;
@@ -1288,18 +1344,18 @@
 		break;
 	}
 
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			 "received EAPOL-Key frame (%s)", msgtxt);
 
 	if (key_info & WPA_KEY_INFO_ACK) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key Ack set");
 		return;
 	}
 
 	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 	    !(key_info & WPA_KEY_INFO_MIC)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC not set");
 		return;
 	}
@@ -1307,7 +1363,7 @@
 #ifdef CONFIG_FILS
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 	    (key_info & WPA_KEY_INFO_MIC)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC set");
 		return;
 	}
@@ -1320,7 +1376,8 @@
 				       data, data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key with invalid MIC");
 #ifdef TEST_FUZZ
 			wpa_printf(MSG_INFO,
@@ -1333,7 +1390,8 @@
 		if (!mic_len &&
 		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
 				     &key_data_length) < 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key with invalid MIC");
 #ifdef TEST_FUZZ
 			wpa_printf(MSG_INFO,
@@ -1357,7 +1415,8 @@
 			os_memcpy(sm->req_replay_counter, key->replay_counter,
 				  WPA_REPLAY_COUNTER_LEN);
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key request with invalid MIC");
 			return;
 		}
@@ -1373,7 +1432,8 @@
 				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
 				return; /* STA entry was removed */
 		} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key Request for new 4-Way Handshake");
 			wpa_request_new_ptk(sm);
 		} else if (key_data_length > 0 &&
@@ -1381,7 +1441,8 @@
 					     &kde) == 0 &&
 			   kde.mac_addr) {
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key Request for GTK rekeying");
 			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 			if (wpa_auth_gtk_rekey_in_process(wpa_auth))
@@ -1492,7 +1553,8 @@
 	}
 
 	sm->pending_1_of_4_timeout = 0;
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"EAPOL-Key timeout");
 	sm->TimeoutEvt = true;
 	wpa_sm_step(sm);
 }
@@ -1641,22 +1703,25 @@
 		if (pad_len)
 			*pos++ = 0xdd;
 
-		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+		wpa_hexdump_key(MSG_DEBUG,
+				"Plaintext EAPOL-Key Key Data (+ padding)",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		    wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-			wpa_printf(MSG_DEBUG,
-				   "WPA: Encrypt Key Data using AES-WRAP (KEK length %zu)",
-				   sm->PTK.kek_len);
+			wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+					sm->PTK.kek, sm->PTK.kek_len);
 			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
 				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
 				bin_clear_free(buf, key_data_len);
 				return;
 			}
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Encrypted Key Data from AES-WRAP",
+				    key_data, key_data_len);
 			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#ifndef CONFIG_NO_RC4
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
 
@@ -1670,7 +1735,7 @@
 			os_memcpy(key_data, buf, key_data_len);
 			rc4_skip(ek, 32, 256, key_data, key_data_len);
 			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#endif /* CONFIG_NO_RC4 */
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
 		} else {
 			os_free(hdr);
 			bin_clear_free(buf, key_data_len);
@@ -1681,7 +1746,8 @@
 
 	if (key_info & WPA_KEY_INFO_MIC) {
 		if (!sm->PTK_valid || !mic_len) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key frame");
 			os_free(hdr);
 			return;
@@ -1697,7 +1763,8 @@
 		if (!pairwise &&
 		    conf->corrupt_gtk_rekey_mic_probability > 0.0 &&
 		    drand48() < conf->corrupt_gtk_rekey_mic_probability) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Corrupting group EAPOL-Key Key MIC");
 			key_mic[0]++;
 		}
@@ -1705,6 +1772,7 @@
 	}
 
 	wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+	wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
 	wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
 			sm->pairwise_set);
 	os_free(hdr);
@@ -1724,10 +1792,25 @@
 	if (!sm)
 		return;
 
+	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+	 * sending it only on the last attempt after all timeouts for the prior
+	 * skipped attemps. */
+	if (wpa_auth->conf.delay_eapol_tx &&
+	    ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+		wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+			"DELAY-EAPOL-TX-%d", ctr);
+		goto skip_tx;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
 			 keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
 
-	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
 	if (ctr == 1 && wpa_auth->conf.tx_status)
 		timeout_ms = pairwise ? eapol_key_timeout_first :
 			eapol_key_timeout_first_group;
@@ -1811,7 +1894,7 @@
 	if (!sm)
 		return -1;
 
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			 "event %d notification", event);
 
 	switch (event) {
@@ -1871,7 +1954,7 @@
 		    sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
 			wpa_printf(MSG_INFO,
 				   "WPA: PTK0 rekey not allowed, disconnect "
-				   MACSTR, MAC2STR(sm->addr));
+				   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 			sm->Disconnect = true;
 			/* Try to encourage the STA to reconnect */
 			sm->disconnect_reason =
@@ -2118,7 +2201,8 @@
 		sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
 		return;
 #endif /* CONFIG_DPP */
-	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+	} else if (wpa_auth_get_msk(sm->wpa_auth, wpa_auth_get_spa(sm),
+				    msk, &len) == 0) {
 		unsigned int pmk_len;
 
 		if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
@@ -2181,13 +2265,20 @@
 		os_memcpy(sm->PMK, psk, psk_len);
 		sm->pmk_len = psk_len;
 #ifdef CONFIG_IEEE80211R_AP
-		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
+#ifdef CONFIG_SAE
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+		     psk_len == SHA256_MAC_LEN))
+			sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+		os_memcpy(sm->xxkey, psk, sm->xxkey_len);
 #endif /* CONFIG_IEEE80211R_AP */
 	}
 #ifdef CONFIG_SAE
 	if (wpa_auth_uses_sae(sm) && sm->pmksa) {
-		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+			   sm->pmksa->pmk_len);
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
 		sm->pmk_len = sm->pmksa->pmk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -2204,6 +2295,7 @@
 {
 	u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
 	size_t pmkid_len = 0;
+	u16 key_info;
 
 	SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
 	sm->PTKRequest = false;
@@ -2218,7 +2310,7 @@
 		return;
 	}
 
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/4 msg of 4-Way Handshake");
 	/*
 	 * For infrastructure BSS cases, it is better for the AP not to include
@@ -2297,8 +2389,10 @@
 			 * Calculate PMKID since no PMKSA cache entry was
 			 * available with pre-calculated PMKID.
 			 */
-			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
-				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+			rsn_pmkid(sm->PMK, sm->pmk_len,
+				  wpa_auth_get_aa(sm),
+				  wpa_auth_get_spa(sm),
+				  &pmkid[2 + RSN_SELECTOR_LEN],
 				  sm->wpa_key_mgmt);
 			wpa_hexdump(MSG_DEBUG,
 				    "RSN: Message 1/4 PMKID derived from PMK",
@@ -2307,19 +2401,24 @@
 	}
 	if (!pmkid)
 		pmkid_len = 0;
-	wpa_send_eapol(sm->wpa_auth, sm,
-		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+	key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
+	if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
+		key_info |= WPA_KEY_INFO_SECURE;
+	wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
 		       sm->ANonce, pmkid, pmkid_len, 0, 0);
 }
 
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 			  const u8 *pmk, unsigned int pmk_len,
-			  struct wpa_ptk *ptk, int force_sha256)
+			  struct wpa_ptk *ptk, int force_sha256,
+			  u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			  size_t *key_len)
 {
 	const u8 *z = NULL;
 	size_t z_len = 0, kdk_len;
 	int akmp;
+	int ret;
 
 	if (sm->wpa_auth->conf.force_kdk_derivation ||
 	    (sm->wpa_auth->conf.secure_ltf &&
@@ -2333,16 +2432,36 @@
 		if (sm->ft_completed) {
 			u8 ptk_name[WPA_PMK_NAME_LEN];
 
-			return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
-						 sm->SNonce, sm->ANonce,
-						 sm->addr, sm->wpa_auth->addr,
-						 sm->pmk_r1_name,
-						 ptk, ptk_name,
-						 sm->wpa_key_mgmt,
-						 sm->pairwise,
-						 kdk_len);
+			ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+						sm->SNonce, sm->ANonce,
+						wpa_auth_get_spa(sm),
+						wpa_auth_get_aa(sm),
+						sm->pmk_r1_name, ptk,
+						ptk_name, sm->wpa_key_mgmt,
+						sm->pairwise, kdk_len);
+		} else {
+			ret = wpa_auth_derive_ptk_ft(sm, ptk, pmk_r0, pmk_r1,
+						     pmk_r0_name, key_len,
+						     kdk_len);
 		}
-		return wpa_auth_derive_ptk_ft(sm, ptk);
+		if (ret) {
+			wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+			return ret;
+		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF)) {
+			ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
+					      sm->pairwise);
+			if (ret) {
+				wpa_printf(MSG_ERROR,
+					   "FT: LTF keyseed derivation failed");
+			}
+		}
+#endif /* CONFIG_PASN */
+		return ret;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -2356,9 +2475,27 @@
 	akmp = sm->wpa_key_mgmt;
 	if (force_sha256)
 		akmp |= WPA_KEY_MGMT_PSK_SHA256;
-	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
-			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
-			      ptk, akmp, sm->pairwise, z, z_len, kdk_len);
+	ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+			     wpa_auth_get_aa(sm), wpa_auth_get_spa(sm),
+			     sm->ANonce, snonce, ptk, akmp,
+			     sm->pairwise, z, z_len, kdk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: PTK derivation failed");
+		return ret;
+	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: LTF keyseed derivation failed");
+		}
+	}
+#endif /* CONFIG_PASN */
+	return ret;
 }
 
 
@@ -2382,13 +2519,27 @@
 	else
 		kdk_len = 0;
 
-	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+	res = fils_pmk_to_ptk(pmk, pmk_len, wpa_auth_get_spa(sm),
+			      wpa_auth_get_aa(sm),
 			      snonce, anonce, dhss, dhss_len,
 			      &sm->PTK, ick, &ick_len,
 			      sm->wpa_key_mgmt, sm->pairwise,
 			      fils_ft, &fils_ft_len, kdk_len);
 	if (res < 0)
 		return res;
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		res = wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, sm->pairwise);
+		if (res) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: LTF keyseed derivation failed");
+			return res;
+		}
+	}
+#endif /* CONFIG_PASN */
+
 	sm->PTK_valid = true;
 	sm->tk_already_set = false;
 
@@ -2397,23 +2548,23 @@
 		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 		struct wpa_auth_config *conf = &wpa_auth->conf;
 		u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
-		int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 
 		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
 				      conf->ssid, conf->ssid_len,
 				      conf->mobility_domain,
 				      conf->r0_key_holder,
 				      conf->r0_key_holder_len,
-				      sm->addr, pmk_r0, pmk_r0_name,
-				      use_sha384) < 0)
+				      wpa_auth_get_spa(sm), pmk_r0, pmk_r0_name,
+				      sm->wpa_key_mgmt) < 0)
 			return -1;
 
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
 		forced_memzero(fils_ft, sizeof(fils_ft));
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
-					     sm->addr, sm->pmk_r1_name,
-					     use_sha384);
+					     wpa_auth_get_spa(sm),
+					     sm->pmk_r1_name,
+					     fils_ft_len);
 		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
 			return -1;
@@ -2424,7 +2575,8 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
-			       sm->addr, sm->wpa_auth->addr,
+			       wpa_auth_get_spa(sm),
+			       wpa_auth_get_aa(sm),
 			       g_sta ? wpabuf_head(g_sta) : NULL,
 			       g_sta ? wpabuf_len(g_sta) : 0,
 			       g_ap ? wpabuf_head(g_ap) : NULL,
@@ -2459,7 +2611,7 @@
 	key_data_len = WPA_GET_BE16(pos);
 	if (key_data_len < AES_BLOCK_SIZE ||
 	    key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"No room for AES-SIV data in the frame");
 		return -1;
 	}
@@ -2475,7 +2627,7 @@
 	aad_len[0] = pos - buf;
 	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
 			    1, aad, aad_len, tmp) < 0) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"Invalid AES-SIV data in the frame");
 		bin_clear_free(tmp, key_data_len);
 		return -1;
@@ -2892,6 +3044,20 @@
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FILS: Failed to set LTF keyseed to driver");
+		return -1;
+	}
+#endif /* CONFIG_PASN */
+
+	sm->pairwise_set = true;
 	sm->tk_already_set = true;
 
 	wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
@@ -2964,6 +3130,9 @@
 	struct wpa_eapol_ie_parse kde;
 	int vlan_id = 0;
 	int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
+	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t key_len;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = false;
@@ -3002,7 +3171,8 @@
 		}
 
 		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
-				   owe_ptk_workaround == 2) < 0)
+				   owe_ptk_workaround == 2, pmk_r0, pmk_r1,
+				   pmk_r0_name, &key_len) < 0)
 			break;
 
 		if (mic_len &&
@@ -3051,15 +3221,16 @@
 						 sm->last_rx_eapol_key,
 						 sm->last_rx_eapol_key_len);
 		sm->waiting_radius_psk = 1;
-		return;
+		goto out;
 	}
 
 	if (!ok) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"invalid MIC in msg 2/4 of 4-Way Handshake");
 		if (psk_found)
 			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
-		return;
+		goto out;
 	}
 
 	/*
@@ -3073,12 +3244,12 @@
 	key_data_length = WPA_GET_BE16(mic + mic_len);
 	if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
 	    sizeof(*key) - mic_len - 2)
-		return;
+		goto out;
 
 	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
-		return;
+		goto out;
 	}
 	if (kde.rsn_ie) {
 		eapol_key_ie = kde.rsn_ie;
@@ -3094,7 +3265,7 @@
 	if (!sm->wpa_ie ||
 	    wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
 			       eapol_key_ie, eapol_key_ie_len)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"WPA IE from (Re)AssocReq did not match with msg 2/4");
 		if (sm->wpa_ie) {
 			wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
@@ -3105,14 +3276,14 @@
 		/* MLME-DEAUTHENTICATE.request */
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 	if ((!sm->rsnxe && kde.rsnxe) ||
 	    (sm->rsnxe && !kde.rsnxe) ||
 	    (sm->rsnxe && kde.rsnxe &&
 	     (sm->rsnxe_len != kde.rsnxe_len ||
 	      os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
 		wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
 			    sm->rsnxe, sm->rsnxe_len);
@@ -3121,7 +3292,7 @@
 		/* MLME-DEAUTHENTICATE.request */
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 #ifdef CONFIG_OCV
 	if (wpa_auth_uses_ocv(sm)) {
@@ -3131,33 +3302,37 @@
 		enum oci_verify_result res;
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
-			return;
+			goto out;
 		}
 
 		if (get_sta_tx_parameters(sm,
 					  channel_width_to_int(ci.chanwidth),
 					  ci.seg1_idx, &tx_chanwidth,
 					  &tx_seg1_idx) < 0)
-			return;
+			goto out;
 
 		res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
 					   tx_chanwidth, tx_seg1_idx);
 		if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
 			/* Work around misbehaving STAs */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "Disable OCV with a STA that does not send OCI");
 			wpa_auth_set_ocv(sm, 0);
 		} else if (res != OCI_SUCCESS) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "OCV failed: %s", ocv_errorstr);
 			if (wpa_auth->conf.msg_ctx)
 				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
 					OCV_FAILURE "addr=" MACSTR
 					" frame=eapol-key-m2 error=%s",
-					MAC2STR(sm->addr), ocv_errorstr);
-			return;
+					MAC2STR(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
+			goto out;
 		}
 	}
 #endif /* CONFIG_OCV */
@@ -3165,7 +3340,7 @@
 	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
@@ -3178,12 +3353,15 @@
 		if (idx >= 0) {
 			u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
 			bitfield_set(wpa_auth->ip_pool, idx);
+			sm->ip_addr_bit = idx;
 			WPA_PUT_BE32(sm->ip_addr, start + idx);
 			wpa_printf(MSG_DEBUG,
 				   "P2P: Assigned IP address %u.%u.%u.%u to "
-				   MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+				   MACSTR " (bit %u)",
+				   sm->ip_addr[0], sm->ip_addr[1],
 				   sm->ip_addr[2], sm->ip_addr[3],
-				   MAC2STR(sm->addr));
+				   MAC2STR(wpa_auth_get_spa(sm)),
+				   sm->ip_addr_bit);
 		}
 	}
 #endif /* CONFIG_P2P */
@@ -3201,7 +3379,7 @@
 				   "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
 			wpa_sta_disconnect(wpa_auth, sm->addr,
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
-			return;
+			goto out;
 		}
 	}
 #endif /* CONFIG_DPP2 */
@@ -3214,14 +3392,15 @@
 		 */
 		if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
 				    WPA_PMK_NAME_LEN) != 0) {
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"PMKR1Name mismatch in FT 4-way handshake");
 			wpa_hexdump(MSG_DEBUG,
 				    "FT: PMKR1Name from Supplicant",
 				    sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
 			wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
 				    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
-			return;
+			goto out;
 		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -3230,7 +3409,7 @@
 	    wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 
 	sm->pending_1_of_4_timeout = 0;
@@ -3246,9 +3425,20 @@
 
 	sm->MICVerified = true;
 
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+		wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+		wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+				       key_len);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = true;
+out:
+	forced_memzero(pmk_r0, sizeof(pmk_r0));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
 }
 
 
@@ -3472,7 +3662,7 @@
 		wpa_ie = wpa_ie_buf;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake");
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
@@ -3487,6 +3677,21 @@
 			return;
 		}
 
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
 		/* WPA2 send GTK in the 4-way handshake */
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
@@ -3519,7 +3724,8 @@
 			 * by setting the Secure bit here even in the case of
 			 * WPA if the supplicant used it first.
 			 */
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
 			secure = 1;
 		}
@@ -3600,9 +3806,8 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
-			res = wpa_write_ftie(conf, use_sha384,
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
 					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
@@ -3698,6 +3903,22 @@
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 		sm->pairwise_set = true;
 
@@ -3730,14 +3951,14 @@
 		sm->PInitAKeys = true;
 	else
 		sm->has_GTK = true;
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "pairwise key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 	wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, "EAPOL-4WAY-HS-COMPLETED "
 		MACSTR, MAC2STR(sm->addr));
 
 #ifdef CONFIG_IEEE80211R_AP
-	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+	wpa_ft_push_pmk_r1(sm->wpa_auth, wpa_auth_get_spa(sm));
 #endif /* CONFIG_IEEE80211R_AP */
 
 	sm->ptkstart_without_success = 0;
@@ -3753,7 +3974,7 @@
 		SM_ENTER(WPA_PTK, INITIALIZE);
 	else if (sm->Disconnect
 		 /* || FIX: dot11RSNAConfigSALifetime timeout */) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"WPA_PTK: sm->Disconnect");
 		SM_ENTER(WPA_PTK, DISCONNECT);
 	}
@@ -3802,7 +4023,8 @@
 #endif /* CONFIG_DPP */
 		} else {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"INITPMK - keyAvailable = false");
 			SM_ENTER(WPA_PTK, DISCONNECT);
 		}
@@ -3821,7 +4043,8 @@
 				   "INITPSK: No PSK yet available for STA - use RADIUS later");
 			SM_ENTER(WPA_PTK, PTKSTART);
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"no PSK configured for the STA");
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			SM_ENTER(WPA_PTK, DISCONNECT);
@@ -3833,7 +4056,8 @@
 			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 		else if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "PTKSTART: Retry limit %u reached",
 					 conf->wpa_pairwise_update_count);
 			sm->disconnect_reason =
@@ -3865,7 +4089,8 @@
 			 (conf->wpa_disable_eapol_key_retries &&
 			  sm->TimeoutCtr > 1)) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "PTKINITNEGOTIATING: Retry limit %u reached",
 					 conf->wpa_pairwise_update_count);
 			sm->disconnect_reason =
@@ -3922,7 +4147,7 @@
 	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 	if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
 		wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/2 msg of Group Key Handshake");
 
 	gtk = gsm->GTK[gsm->GN - 1];
@@ -4003,7 +4228,7 @@
 		return;
 
 	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				 "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
 		return;
 	}
@@ -4014,7 +4239,8 @@
 		int tx_seg1_idx;
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
 			return;
 		}
@@ -4028,13 +4254,15 @@
 		if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
 					 tx_chanwidth, tx_seg1_idx) !=
 		    OCI_SUCCESS) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "OCV failed: %s", ocv_errorstr);
 			if (wpa_auth->conf.msg_ctx)
 				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
 					OCV_FAILURE "addr=" MACSTR
 					" frame=eapol-key-g2 error=%s",
-					MAC2STR(sm->addr), ocv_errorstr);
+					MAC2STR(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
 			return;
 		}
 	}
@@ -4045,7 +4273,7 @@
 	sm->GUpdateStationKeys = false;
 	sm->GTimeoutCtr = 0;
 	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "group key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 	sm->has_GTK = true;
@@ -4060,7 +4288,7 @@
 	sm->GUpdateStationKeys = false;
 	sm->Disconnect = true;
 	sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "group key handshake failed (%s) after %u tries",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
 			 sm->wpa_auth->conf.wpa_group_update_count);
@@ -4173,7 +4401,8 @@
 		return 0;
 
 	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"Not in PTKINITDONE; skip Group Key update");
 		sm->GUpdateStationKeys = false;
 		return 0;
@@ -4184,7 +4413,8 @@
 		 * Since we clear the GKeyDoneStations before the loop, the
 		 * station needs to be counted here anyway.
 		 */
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"GUpdateStationKeys was already set when marking station for GTK rekeying");
 	}
 
@@ -4418,7 +4648,7 @@
 	if (sm->group == ctx) {
 		wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
 			   " for disconnection due to fatal failure",
-			   MAC2STR(sm->addr));
+			   MAC2STR(wpa_auth_get_spa(sm)));
 		sm->Disconnect = true;
 	}
 
@@ -4511,7 +4741,7 @@
 	if (sm->pending_deinit) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Completing pending STA state machine deinit for "
-			   MACSTR, MAC2STR(sm->addr));
+			   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 		wpa_free_sta_sm(sm);
 		return 1;
 	}
@@ -4704,11 +4934,13 @@
 			  "wpa=%d\n"
 			  "AKMSuiteSelector=" RSN_SUITE "\n"
 			  "hostapdWPAPTKState=%d\n"
-			  "hostapdWPAPTKGroupState=%d\n",
+			  "hostapdWPAPTKGroupState=%d\n"
+			  "hostapdMFPR=%d\n",
 			  sm->wpa,
 			  RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
 			  sm->wpa_ptk_state,
-			  sm->wpa_ptk_group_state);
+			  sm->wpa_ptk_group_state,
+			  sm->mfpr);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -4745,6 +4977,14 @@
 }
 
 
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+	if (!sm || !sm->pmksa)
+		return NULL;
+	return sm->pmksa->dpp_pkhash;
+}
+
+
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
 {
 	if (!sm)
@@ -4837,7 +5077,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
 	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
 				 sm->PTK.kck, sm->PTK.kck_len,
-				 sm->wpa_auth->addr, sm->addr, session_timeout,
+				 wpa_auth_get_aa(sm),
+				 wpa_auth_get_spa(sm), session_timeout,
 				 eapol, sm->wpa_key_mgmt))
 		return 0;
 
@@ -4866,16 +5107,17 @@
 
 
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
-			   const u8 *pmk, const u8 *pmkid)
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp)
 {
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
-	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
-				 NULL, 0,
-				 wpa_auth->addr, addr, 0, NULL,
-				 WPA_KEY_MGMT_SAE))
+	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))
 		return 0;
 
 	return -1;
@@ -4906,6 +5148,29 @@
 }
 
 
+int wpa_auth_pmksa_add3(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)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (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);
+	if (!entry)
+		return -1;
+
+	if (dpp_pkhash)
+		entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
@@ -4953,13 +5218,14 @@
 
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
 			    const u8 *pmkid, int expiration)
 {
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
-	entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
-					      spa, 0, NULL, WPA_KEY_MGMT_SAE);
+	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, NULL, 0, aa,
+					      spa, 0, NULL, akmp);
 	if (!entry)
 		return NULL;
 
@@ -4990,6 +5256,15 @@
 #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 
 
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return wpa_auth->pmksa;
+}
+
+
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid)
@@ -5213,7 +5488,7 @@
 
 	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR
 		   " to use group state machine for VLAN ID %d",
-		   MAC2STR(sm->addr), vlan_id);
+		   MAC2STR(wpa_auth_get_spa(sm)), vlan_id);
 
 	wpa_group_get(sm->wpa_auth, group);
 	wpa_group_put(sm->wpa_auth, sm->group);
@@ -5229,7 +5504,7 @@
 	if (!wpa_auth || !sm)
 		return;
 	wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
-		   " ack=%d", MAC2STR(sm->addr), ack);
+		   " ack=%d", MAC2STR(wpa_auth_get_spa(sm)), ack);
 	if (sm->pending_1_of_4_timeout && ack) {
 		/*
 		 * Some deployed supplicant implementations update their SNonce
@@ -5273,7 +5548,8 @@
 {
 	if (!sm)
 		return 0;
-	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+		sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 }
 
 
@@ -5349,13 +5625,14 @@
 
 
 #ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
 		       u8 *buf, size_t len)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 
-	return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
-			      conf->r0_key_holder_len,
+	return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+			      conf->r0_key_holder, conf->r0_key_holder_len,
 			      NULL, NULL, buf, len, NULL, 0, 0);
 }
 #endif /* CONFIG_IEEE80211R_AP */
@@ -5425,7 +5702,7 @@
 		anonce = anonce_buf;
 	}
 
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/4 msg of 4-Way Handshake (TESTING)");
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
@@ -5467,7 +5744,7 @@
 			wpa_ie = wpa_ie + wpa_ie[1] + 2;
 		wpa_ie_len = wpa_ie[1] + 2;
 	}
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake (TESTING)");
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		/* WPA2 send GTK in the 4-way handshake */
@@ -5492,7 +5769,8 @@
 			 * by setting the Secure bit here even in the case of
 			 * WPA if the supplicant used it first.
 			 */
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
 			secure = 1;
 		}
@@ -5569,9 +5847,8 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
-			res = wpa_write_ftie(conf, use_sha384,
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
 					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
@@ -5630,7 +5907,7 @@
 	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
 	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 	/* Use 0 RSC */
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/2 msg of Group Key Handshake (TESTING)");
 
 	gtk = gsm->GTK[gsm->GN - 1];
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 348a1de..3b32fe3 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -242,6 +242,7 @@
 	int ft_rsnxe_used;
 	unsigned int skip_send_eapol:1;
 	unsigned int enable_eapol_large_timeout:1;
+	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 	unsigned int oci_freq_override_eapol_m3;
 	unsigned int oci_freq_override_eapol_g1;
@@ -257,7 +258,7 @@
 	unsigned int fils_cache_id_set:1;
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 #endif /* CONFIG_FILS */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 	bool sae_pk;
 
 	unsigned int secure_ltf:1;
@@ -352,6 +353,10 @@
 #ifdef CONFIG_MESH
 	int (*start_ampe)(void *ctx, const u8 *sta_addr);
 #endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+	int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
+			       size_t ltf_keyseed_len);
+#endif /* CONFIG_PASN */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -408,6 +413,7 @@
 int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
 int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
 const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -427,11 +433,15 @@
 			       int session_timeout,
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
-			   const u8 *pmk, const u8 *pmkid);
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp);
 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);
+int wpa_auth_pmksa_add3(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);
 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,
@@ -441,9 +451,12 @@
 			     char *buf, size_t len);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
 			    const u8 *pmkid, int expiration);
 int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
 			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
@@ -531,7 +544,8 @@
 int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
 			  int ap_seg1_idx, int *bandwidth, int *seg1_idx);
 
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
 		       u8 *buf, size_t len);
 void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
 				   u8 *fils_anonce, u8 *fils_snonce,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 7a97613..35585cd 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -20,6 +20,7 @@
 #include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
@@ -34,6 +35,9 @@
 const unsigned int ftRRBseqTimeout = 10;
 const unsigned int ftRRBmaxQueueLen = 100;
 
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
 
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
@@ -805,15 +809,29 @@
 }
 
 
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
 		   const u8 *r0kh_id, size_t r0kh_id_len,
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len, int rsnxe_used)
 {
 	u8 *pos = buf, *ielen;
-	size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
-		sizeof(struct rsn_ftie);
+	size_t hdrlen;
+	u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA256_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA384_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA512_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha512);
+	else if (wpa_key_mgmt_sha384(key_mgmt))
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else
+		hdrlen = sizeof(struct rsn_ftie);
 
 	if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
 	    subelem_len)
@@ -822,12 +840,27 @@
 	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 	ielen = pos++;
 
-	if (use_sha384) {
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	} else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(key_mgmt)) {
 		struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
 
 		os_memset(hdr, 0, sizeof(*hdr));
 		pos += sizeof(*hdr);
-		WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+		mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
 		if (anonce)
 			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
 		if (snonce)
@@ -837,7 +870,8 @@
 
 		os_memset(hdr, 0, sizeof(*hdr));
 		pos += sizeof(*hdr);
-		WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+		mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
 		if (anonce)
 			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
 		if (snonce)
@@ -2078,13 +2112,11 @@
 }
 
 
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			   u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			   size_t *key_len, size_t kdk_len)
 {
-	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
-	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
-		SHA384_MAC_LEN : PMK_LEN;
-	size_t pmk_r1_len = pmk_r0_len;
-	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r0_len, pmk_r1_len;
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
 	const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -2092,15 +2124,20 @@
 	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
-	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
-	int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
-	struct vlan_description vlan;
-	const u8 *identity, *radius_cui;
-	size_t identity_len, radius_cui_len;
-	int session_timeout;
 	const u8 *mpmk;
 	size_t mpmk_len;
 
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    (sm->xxkey_len == SHA256_MAC_LEN ||
+	     sm->xxkey_len == SHA384_MAC_LEN ||
+	     sm->xxkey_len == SHA512_MAC_LEN))
+		pmk_r0_len = sm->xxkey_len;
+	else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		pmk_r0_len = SHA384_MAC_LEN;
+	else
+		pmk_r0_len = PMK_LEN;
+	*key_len = pmk_r1_len = pmk_r0_len;
+
 	if (sm->xxkey_len > 0) {
 		mpmk = sm->xxkey;
 		mpmk_len = sm->xxkey_len;
@@ -2113,10 +2150,39 @@
 		return -1;
 	}
 
+	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
+			      r0kh, r0kh_len, sm->addr,
+			      pmk_r0, pmk_r0_name,
+			      sm->wpa_key_mgmt) < 0 ||
+	    wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
+			      pmk_r1, sm->pmk_r1_name) < 0)
+		return -1;
+
+	return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+				 sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
+				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise,
+				 kdk_len);
+}
+
+
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			    const u8 *pmk_r1, const u8 *pmk_r0_name,
+			    size_t key_len)
+{
+	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+	int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+	struct vlan_description vlan;
+	const u8 *identity, *radius_cui;
+	size_t identity_len, radius_cui_len;
+	int session_timeout;
+
+	if (psk_local && wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		return;
+
 	if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
 			   MAC2STR(sm->addr));
-		return -1;
+		return;
 	}
 
 	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
@@ -2124,31 +2190,16 @@
 					       &radius_cui);
 	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
 
-	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
-			      r0kh, r0kh_len, sm->addr,
-			      pmk_r0, pmk_r0_name,
-			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
-		return -1;
-	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
-		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
-				    pmk_r0_name,
-				    sm->pairwise, &vlan, expires_in,
-				    session_timeout, identity, identity_len,
-				    radius_cui, radius_cui_len);
 
-	if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
-			      pmk_r1, sm->pmk_r1_name) < 0)
-		return -1;
-	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
-		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
-				    sm->pmk_r1_name, sm->pairwise, &vlan,
-				    expires_in, session_timeout, identity,
-				    identity_len, radius_cui, radius_cui_len);
-
-	return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
-				 sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
-				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise,
-				 0);
+	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, key_len,
+			    pmk_r0_name,
+			    sm->pairwise, &vlan, expires_in,
+			    session_timeout, identity, identity_len,
+			    radius_cui, radius_cui_len);
+	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len,
+			    sm->pmk_r1_name, sm->pairwise, &vlan,
+			    expires_in, session_timeout, identity,
+			    identity_len, radius_cui, radius_cui_len);
 }
 
 
@@ -2521,12 +2572,11 @@
 	u8 *anonce, *snonce;
 	const u8 *kck;
 	size_t kck_len;
-	int use_sha384;
+	size_t key_len;
 
 	if (sm == NULL)
 		return pos;
 
-	use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 	conf = &sm->wpa_auth->conf;
 
 	if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -2697,7 +2747,8 @@
 		snonce = NULL;
 	}
 	rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
-		(conf->sae_pwe == 1 || conf->sae_pwe == 2);
+		(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		 conf->sae_pwe == SAE_PWE_BOTH);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->wpa_auth->conf.ft_rsnxe_used) {
 		rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
@@ -2705,7 +2756,20 @@
 			   rsnxe_used);
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+	key_len = sm->xxkey_len;
+	if (!key_len)
+		key_len = sm->pmk_r1_len;
+	if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->wpa_auth->cb->get_psk) {
+		size_t psk_len;
+
+		if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+					      sm->addr, sm->p2p_dev_addr,
+					      NULL, &psk_len, NULL))
+			key_len = psk_len;
+	}
+	res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+			     r0kh_id, r0kh_id_len,
 			     anonce, snonce, pos, end - pos,
 			     subelem, subelem_len, rsnxe_used);
 	os_free(subelem);
@@ -2715,7 +2779,16 @@
 	ftie_len = res;
 	pos += res;
 
-	if (use_sha384) {
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *_ftie =
+			(struct rsn_ftie_sha512 *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	} else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
 		struct rsn_ftie_sha384 *_ftie =
 			(struct rsn_ftie_sha384 *) (ftie + 2);
 
@@ -2731,8 +2804,8 @@
 		*elem_count = 3; /* Information element count */
 
 	ric_start = pos;
-	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
-	    && parse.ric) {
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+			     sm->wpa_key_mgmt) == 0 && parse.ric) {
 		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
 					 parse.ric_len);
 		if (auth_alg == WLAN_AUTH_FT)
@@ -2772,7 +2845,8 @@
 		kck_len = sm->PTK.kck_len;
 	}
 	if (auth_alg == WLAN_AUTH_FT &&
-	    wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
+	    wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 6,
 		       mdie, mdie_len, ftie, ftie_len,
 		       rsnie, rsnie_len,
 		       ric_start, ric_start ? pos - ric_start : 0,
@@ -2805,6 +2879,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
 static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr)
 {
@@ -2849,9 +2937,24 @@
 			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
 		return;
 
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FT: Failed to set LTF keyseed to driver");
+		return;
+	}
+#endif /* CONFIG_PASN */
+
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 	sm->pairwise_set = true;
 	sm->tk_already_set = true;
+
+	wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+			     dot11RSNAConfigPMKLifetime, &sm->PTK);
 }
 
 
@@ -2887,7 +2990,8 @@
 
 		if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
 				      r0kh_len, sm->addr,
-				      pmk_r0, pmk_r0_name, 0) < 0 ||
+				      pmk_r0, pmk_r0_name,
+				      WPA_KEY_MGMT_FT_PSK) < 0 ||
 		    wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
 				      sm->addr, pmk_r1, pmk_r1_name) < 0 ||
 		    os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
@@ -2987,7 +3091,8 @@
 				      const u8 **identity, size_t *identity_len,
 				      const u8 **radius_cui,
 				      size_t *radius_cui_len,
-				      int *out_session_timeout)
+				      int *out_session_timeout,
+				      size_t *pmk_r1_len)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 	const struct wpa_ft_pmk_r0_sa *r0;
@@ -3046,6 +3151,8 @@
 
 	*out_session_timeout = session_timeout;
 
+	*pmk_r1_len = r0->pmk_r0_len;
+
 	return 0;
 }
 
@@ -3066,8 +3173,7 @@
 	struct vlan_description vlan;
 	const u8 *identity, *radius_cui;
 	size_t identity_len = 0, radius_cui_len = 0;
-	int use_sha384;
-	size_t pmk_r1_len, kdk_len;
+	size_t pmk_r1_len, kdk_len, len;
 
 	*resp_ies = NULL;
 	*resp_ies_len = 0;
@@ -3078,12 +3184,10 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
 		    ies, ies_len);
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
-	use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
-	pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 
 	mdie = (struct rsn_mdie *) parse.mdie;
 	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -3094,26 +3198,9 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
-
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r0kh_id == NULL) {
@@ -3136,49 +3223,73 @@
 
 	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
-	if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
-				   sm->wpa_auth->conf.r1_key_holder, sm->addr,
-				   pmk_r1_name, use_sha384) < 0)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	if (conf->ft_psk_generate_local &&
 	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, PMK_LEN) < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
 				      &vlan, &identity, &identity_len,
 				      &radius_cui, &radius_cui_len,
 				      &session_timeout) < 0)
 			return WLAN_STATUS_INVALID_PMKID;
+		pmk_r1_len = PMK_LEN;
 		wpa_printf(MSG_DEBUG,
 			   "FT: Generated PMK-R1 for FT-PSK locally");
-	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
-				       pmk_r1, &pmk_r1_len, &pairwise, &vlan,
-				       &identity, &identity_len, &radius_cui,
-				       &radius_cui_len, &session_timeout) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
-		if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
-					       parse.r0kh_id, parse.r0kh_id_len,
-					       parse.rsn_pmkid,
-					       pmk_r1_name, pmk_r1, &pairwise,
-					       &vlan, &identity, &identity_len,
-					       &radius_cui, &radius_cui_len,
-					       &session_timeout) == 0) {
+		goto pmk_r1_derived;
+	}
+
+	/* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+	 * the key length is not yet known. For other AKMs, only the length
+	 * identified by the AKM is used. */
+	for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+		if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA384_MAC_LEN) ||
+		     (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA256_MAC_LEN)))
+			continue;
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, len) < 0)
+			continue;
+
+		if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+					pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+					&identity, &identity_len, &radius_cui,
+					&radius_cui_len,
+					&session_timeout) == 0) {
 			wpa_printf(MSG_DEBUG,
-				   "FT: Generated PMK-R1 based on local PMK-R0");
+				   "FT: Found PMKR1Name (using SHA%zu) from local cache",
+				   pmk_r1_len * 8);
 			goto pmk_r1_derived;
 		}
-
-		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
-			wpa_printf(MSG_DEBUG,
-				   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
-			return WLAN_STATUS_INVALID_PMKID;
-		}
-
-		return -1; /* Status pending */
-	} else {
-		wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
 	}
 
+	wpa_printf(MSG_DEBUG,
+		   "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+	if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+				       parse.r0kh_id, parse.r0kh_id_len,
+				       parse.rsn_pmkid,
+				       pmk_r1_name, pmk_r1, &pairwise,
+				       &vlan, &identity, &identity_len,
+				       &radius_cui, &radius_cui_len,
+				       &session_timeout, &pmk_r1_len) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Generated PMK-R1 based on local PMK-R0");
+		goto pmk_r1_derived;
+	}
+
+	if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	return -1; /* Status pending */
+
 pmk_r1_derived:
 	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
 	sm->pmk_r1_name_valid = 1;
@@ -3192,6 +3303,40 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
+	/* Now that we know the correct PMK-R1 length and as such, the length
+	 * of the MIC field, fetch the SNonce. */
+	if (pmk_r1_len == SHA512_MAC_LEN) {
+		const struct rsn_ftie_sha512 *ftie;
+
+		ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else if (pmk_r1_len == SHA384_MAC_LEN) {
+		const struct rsn_ftie_sha384 *ftie;
+
+		ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else {
+		const struct rsn_ftie *ftie;
+
+		ftie = (const struct rsn_ftie *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	}
+
 	wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 		    sm->SNonce, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
@@ -3206,10 +3351,19 @@
 
 	if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
 			      sm->addr, sm->wpa_auth->addr, pmk_r1_name,
-			      &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+			      &sm->PTK, ptk_name, parse.key_mgmt,
 			      pairwise, kdk_len) < 0)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_PASN */
+
 	sm->pairwise = pairwise;
 	sm->PTK_valid = true;
 	sm->tk_already_set = false;
@@ -3247,7 +3401,8 @@
 		goto fail;
 	pos += ret;
 
-	ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
+	ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+			     parse.r0kh_id, parse.r0kh_id_len,
 			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
 			     0);
 	if (ret < 0)
@@ -3314,25 +3469,20 @@
 	struct wpa_ft_ies parse;
 	struct rsn_mdie *mdie;
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
-	size_t mic_len = 16;
+	size_t mic_len;
 	unsigned int count;
 	const u8 *kck;
 	size_t kck_len;
-	int use_sha384;
-	const u8 *anonce, *snonce, *fte_mic;
-	u8 fte_elem_count;
-	int rsnxe_used;
 	struct wpa_auth_config *conf;
 
 	if (sm == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	conf = &sm->wpa_auth->conf;
-	use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 
 	wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -3362,55 +3512,42 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->pmk_r1_len == SHA512_MAC_LEN)
+		mic_len = 32;
+	else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		  sm->pmk_r1_len == SHA384_MAC_LEN) ||
+		 wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		mic_len = 24;
+	else
+		mic_len = 16;
 
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
+	if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+	    parse.fte_mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+			   parse.fte_mic_len, mic_len);
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
-			    snonce, WPA_NONCE_LEN);
+			    parse.fte_snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->SNonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
-			    anonce, WPA_NONCE_LEN);
+			    parse.fte_anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->ANonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-
 	if (parse.r0kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
 		return WLAN_STATUS_INVALID_FTIE;
@@ -3457,10 +3594,10 @@
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
 	if (parse.rsnxe)
 		count++;
-	if (fte_elem_count != count) {
+	if (parse.fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
-			   fte_elem_count, count);
+			   parse.fte_elem_count, count);
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
@@ -3471,7 +3608,8 @@
 		kck = sm->PTK.kck;
 		kck_len = sm->PTK.kck_len;
 	}
-	if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
+	if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 5,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
@@ -3483,12 +3621,12 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+	if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
 		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
 			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
 		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
-			    fte_mic, mic_len);
+			    parse.fte_mic, mic_len);
 		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
 		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
 			    parse.mdie - 2, parse.mdie_len + 2);
@@ -3502,7 +3640,9 @@
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (rsnxe_used && (conf->sae_pwe == 1 || conf->sae_pwe == 2) &&
+	if (parse.fte_rsnxe_used &&
+	    (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH) &&
 	    !parse.rsnxe) {
 		wpa_printf(MSG_INFO,
 			   "FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
@@ -4070,7 +4210,8 @@
 	pmk_r1_len = PMK_LEN;
 	if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
 			       &f_pmk_r1) == 0 &&
-	    (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+	    (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+	     f_pmk_r1_len == SHA512_MAC_LEN))
 		pmk_r1_len = f_pmk_r1_len;
 	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9e8dae1..9f6699b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -117,6 +117,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
+	wconf->delay_eapol_tx = iconf->delay_eapol_tx;
 	if (conf->own_ie_override &&
 	    wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
 		wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
@@ -198,10 +199,10 @@
 #endif /* CONFIG_FILS */
 	wconf->sae_pwe = conf->sae_pwe;
 	sae_pw_id = hostapd_sae_pw_id_in_use(conf);
-	if (sae_pw_id == 2 && wconf->sae_pwe != 3)
-		wconf->sae_pwe = 1;
-	else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
-		wconf->sae_pwe = 2;
+	if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_BOTH;
 #ifdef CONFIG_SAE_PK
 	wconf->sae_pk = hostapd_sae_pk_in_use(conf);
 #endif /* CONFIG_SAE_PK */
@@ -350,6 +351,8 @@
 	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
 		if (!sta->sae || prev_psk)
 			return NULL;
+		if (psk_len)
+			*psk_len = sta->sae->pmk_len;
 		return sta->sae->pmk;
 	}
 	if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
@@ -934,7 +937,8 @@
 {
 	struct hostapd_data *hapd = ctx;
 
-	ptksa_cache_add(hapd->ptksa, addr, cipher, life_time, ptk);
+	ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
+			ptk, NULL, NULL, 0);
 }
 
 
@@ -1469,6 +1473,21 @@
 #endif /* CONFIG_NO_RADIUS */
 
 
+#ifdef CONFIG_PASN
+static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
+				   const u8 *ltf_keyseed,
+				   size_t ltf_keyseed_len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+						  peer_addr, 0, 0, NULL,
+						  ltf_keyseed_len,
+						  ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
 int hostapd_setup_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config _conf;
@@ -1515,6 +1534,9 @@
 #ifndef CONFIG_NO_RADIUS
 		.request_radius_psk = hostapd_request_radius_psk,
 #endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+		.set_ltf_keyseed = hostapd_set_ltf_keyseed,
+#endif /* CONFIG_PASN */
 	};
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
@@ -1551,11 +1573,12 @@
 #endif /* CONFIG_OCV */
 
 	_conf.secure_ltf =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF);
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
 	_conf.secure_rtt =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT);
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
 	_conf.prot_range_neg =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG);
+		!!(hapd->iface->drv_flags2 &
+		   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
 
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
 	if (hapd->wpa_auth == NULL) {
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 17cb5a2..d401550 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -86,6 +86,7 @@
 	unsigned int pending_deinit:1;
 	unsigned int started:1;
 	unsigned int mgmt_frame_prot:1;
+	unsigned int mfpr:1;
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
 	unsigned int alt_snonce_valid:1;
@@ -152,6 +153,7 @@
 
 #ifdef CONFIG_P2P
 	u8 ip_addr[4];
+	unsigned int ip_addr_bit;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_FILS
@@ -294,15 +296,23 @@
 int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx);
+void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
+			  const u8 *addr, int cipher,
+			  u32 life_time, const struct wpa_ptk *ptk);
 
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
 		   const u8 *r0kh_id, size_t r0kh_id_len,
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len, int rsnxe_used);
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			   u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			   size_t *key_len, size_t kdk_len);
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			    const u8 *pmk_r1, const u8 *pmk_r0_name,
+			    size_t key_len);
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 524922e..43ccec9 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -228,11 +228,21 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
 #endif /* CONFIG_SAE */
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
@@ -395,7 +405,9 @@
 	size_t flen;
 
 	if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
-	    (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk)) {
+	    (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 		if (conf->sae_pk)
@@ -408,7 +420,7 @@
 	if (conf->secure_rtt)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (conf->prot_range_neg)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 
 	flen = (capab & 0xff00) ? 2 : 1;
 	if (!capab)
@@ -670,8 +682,12 @@
 #ifdef CONFIG_SAE
 		else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
 			selector = RSN_AUTH_KEY_MGMT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
 			selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
@@ -778,8 +794,12 @@
 #ifdef CONFIG_SAE
 	else if (key_mgmt & WPA_KEY_MGMT_SAE)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
@@ -864,6 +884,7 @@
 		sm->mgmt_frame_prot = 0;
 	else
 		sm->mgmt_frame_prot = 1;
+	sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
 
 	if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
 		    wpa_printf(MSG_DEBUG,
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index e2c4e10..625f405 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -327,6 +327,7 @@
 	res = ieee802_1x_kay_init(kay_ctx, policy,
 				  hapd->conf->macsec_replay_protect,
 				  hapd->conf->macsec_replay_window,
+				  hapd->conf->macsec_offload,
 				  hapd->conf->macsec_port,
 				  hapd->conf->mka_priority,
 				  hapd->conf->macsec_csindex,
@@ -352,33 +353,6 @@
 }
 
 
-static int ieee802_1x_auth_get_session_id(struct hostapd_data *hapd,
-					  struct sta_info *sta, u8 *sid,
-					  size_t *len)
-{
-	const u8 *session_id;
-	size_t id_len, need_len;
-
-	session_id = ieee802_1x_get_session_id(sta->eapol_sm, &id_len);
-	if (!session_id) {
-		wpa_printf(MSG_DEBUG,
-			   "MACsec: Failed to get SessionID from EAPOL state machines");
-		return -1;
-	}
-
-	need_len = 1 + 2 * 32 /* random size */;
-	if (need_len > id_len) {
-		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
-		return -1;
-	}
-
-	os_memcpy(sid, session_id, need_len);
-	*len = need_len;
-
-	return 0;
-}
-
-
 static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
 				   struct sta_info *sta, u8 *msk, size_t *len)
 {
@@ -410,8 +384,8 @@
 void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
 					   struct sta_info *sta)
 {
-	u8 *sid;
-	size_t sid_len = 128;
+	const u8 *sid;
+	size_t sid_len;
 	struct mka_key_name *ckn;
 	struct mka_key *cak;
 	struct mka_key *msk;
@@ -425,10 +399,9 @@
 		   MACSTR, MAC2STR(sta->addr));
 
 	msk = os_zalloc(sizeof(*msk));
-	sid = os_zalloc(sid_len);
 	ckn = os_zalloc(sizeof(*ckn));
 	cak = os_zalloc(sizeof(*cak));
-	if (!msk || !sid || !ckn || !cak)
+	if (!msk || !ckn || !cak)
 		goto fail;
 
 	msk->len = DEFAULT_KEY_LEN;
@@ -437,8 +410,8 @@
 		goto fail;
 	}
 
-	if (ieee802_1x_auth_get_session_id(hapd, sta, sid, &sid_len))
-	{
+	sid = ieee802_1x_get_session_id(sta->eapol_sm, &sid_len);
+	if (!sid) {
 		wpa_printf(MSG_ERROR,
 			   "IEEE 802.1X: Could not get EAP Session Id");
 		goto fail;
@@ -470,7 +443,6 @@
 
 fail:
 	bin_clear_free(msk, sizeof(*msk));
-	os_free(sid);
 	os_free(ckn);
 	bin_clear_free(cak, sizeof(*cak));
 
diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
index aef9a53..029f4de 100644
--- a/src/ap/x_snoop.c
+++ b/src/ap/x_snoop.c
@@ -31,6 +31,8 @@
 		return -1;
 	}
 
+	hapd->x_snoop_initialized = true;
+
 	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
 					 1)) {
 		wpa_printf(MSG_DEBUG,
@@ -125,7 +127,10 @@
 
 void x_snoop_deinit(struct hostapd_data *hapd)
 {
+	if (!hapd->x_snoop_initialized)
+		return;
 	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+	hapd->x_snoop_initialized = false;
 }
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 8aba713..a95ae36 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -428,7 +428,7 @@
 	}
 
 	if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
-			     NULL, 0) != 0 ||
+			     NULL, 0, NULL) != 0 ||
 	    sae_process_commit(&sae) < 0)
 		goto fail;
 
diff --git a/src/common/defs.h b/src/common/defs.h
index f43bdb5..c0c6dbe 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -50,12 +50,15 @@
 #define WPA_KEY_MGMT_DPP BIT(23)
 #define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
 #define WPA_KEY_MGMT_PASN BIT(25)
+#define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26)
+#define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27)
 
 
 #define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
 			 WPA_KEY_MGMT_FT_IEEE8021X | \
 			 WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \
 			 WPA_KEY_MGMT_FT_SAE | \
+			 WPA_KEY_MGMT_FT_SAE_EXT_KEY | \
 			 WPA_KEY_MGMT_FT_FILS_SHA256 | \
 			 WPA_KEY_MGMT_FT_FILS_SHA384)
 
@@ -88,7 +91,9 @@
 			 WPA_KEY_MGMT_FT_PSK |
 			 WPA_KEY_MGMT_PSK_SHA256 |
 			 WPA_KEY_MGMT_SAE |
-			 WPA_KEY_MGMT_FT_SAE));
+			 WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE |
+			 WPA_KEY_MGMT_FT_SAE_EXT_KEY));
 }
 
 static inline int wpa_key_mgmt_ft(int akm)
@@ -111,7 +116,15 @@
 static inline int wpa_key_mgmt_sae(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_SAE |
-			 WPA_KEY_MGMT_FT_SAE));
+			 WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE |
+			 WPA_KEY_MGMT_FT_SAE_EXT_KEY));
+}
+
+static inline int wpa_key_mgmt_sae_ext_key(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE_EXT_KEY));
 }
 
 static inline int wpa_key_mgmt_fils(int akm)
@@ -124,7 +137,8 @@
 
 static inline int wpa_key_mgmt_sha256(int akm)
 {
-	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+	return !!(akm & (WPA_KEY_MGMT_FT_IEEE8021X |
+			 WPA_KEY_MGMT_PSK_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_SAE |
 			 WPA_KEY_MGMT_FT_SAE |
@@ -168,6 +182,13 @@
 	return akm == WPA_KEY_MGMT_CCKM;
 }
 
+static inline int wpa_key_mgmt_cross_akm(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_PSK |
+			 WPA_KEY_MGMT_PSK_SHA256 |
+			 WPA_KEY_MGMT_SAE |
+			 WPA_KEY_MGMT_SAE_EXT_KEY));
+}
 
 #define WPA_PROTO_WPA BIT(0)
 #define WPA_PROTO_RSN BIT(1)
@@ -429,9 +450,26 @@
 	CHAN_WIDTH_4320,
 	CHAN_WIDTH_6480,
 	CHAN_WIDTH_8640,
+	CHAN_WIDTH_320,
 	CHAN_WIDTH_UNKNOWN
 };
 
+/* VHT/EDMG/etc. channel widths
+ * Note: The first four values are used in hostapd.conf and as such, must
+ * maintain their defined values. Other values are used internally. */
+enum oper_chan_width {
+	CONF_OPER_CHWIDTH_USE_HT = 0,
+	CONF_OPER_CHWIDTH_80MHZ = 1,
+	CONF_OPER_CHWIDTH_160MHZ = 2,
+	CONF_OPER_CHWIDTH_80P80MHZ = 3,
+	CONF_OPER_CHWIDTH_2160MHZ,
+	CONF_OPER_CHWIDTH_4320MHZ,
+	CONF_OPER_CHWIDTH_6480MHZ,
+	CONF_OPER_CHWIDTH_8640MHZ,
+	CONF_OPER_CHWIDTH_40MHZ_6GHZ,
+	CONF_OPER_CHWIDTH_320MHZ,
+};
+
 enum key_flag {
 	KEY_FLAG_MODIFY			= BIT(0),
 	KEY_FLAG_DEFAULT		= BIT(1),
@@ -475,4 +513,20 @@
 	PTK0_REKEY_ALLOW_NEVER
 };
 
+enum frame_encryption {
+	FRAME_ENCRYPTION_UNKNOWN = -1,
+	FRAME_NOT_ENCRYPTED = 0,
+	FRAME_ENCRYPTED = 1
+};
+
+#define MAX_NUM_MLD_LINKS 15
+
+enum sae_pwe {
+	SAE_PWE_HUNT_AND_PECK = 0,
+	SAE_PWE_HASH_TO_ELEMENT = 1,
+	SAE_PWE_BOTH = 2,
+	SAE_PWE_FORCE_HUNT_AND_PECK = 3,
+	SAE_PWE_NOT_SET = 4,
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
index cc26b80..02c32dc 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -13,6 +13,7 @@
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "utils/json.h"
+#include "utils/ip_addr.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/gas.h"
@@ -162,6 +163,7 @@
 	os_free(info->uri);
 	os_free(info->info);
 	os_free(info->chan);
+	os_free(info->host);
 	os_free(info->pk);
 	crypto_ec_key_deinit(info->pubkey);
 	str_clear_free(info->configurator_params);
@@ -369,12 +371,70 @@
 }
 
 
+static int dpp_parse_uri_host(struct dpp_bootstrap_info *bi, const char *txt)
+{
+	const char *end;
+	char *port;
+	struct hostapd_ip_addr addr;
+	char buf[100], *pos;
+
+	if (!txt)
+		return 0;
+
+	end = os_strchr(txt, ';');
+	if (!end)
+		end = txt + os_strlen(txt);
+	if (end - txt > (int) sizeof(buf) - 1)
+		return -1;
+	os_memcpy(buf, txt, end - txt);
+	buf[end - txt] = '\0';
+
+	bi->port = DPP_TCP_PORT;
+
+	pos = buf;
+	if (*pos == '[') {
+		pos = &buf[1];
+		port = os_strchr(pos, ']');
+		if (!port)
+			return -1;
+		*port++ = '\0';
+		if (*port == ':')
+			bi->port = atoi(port + 1);
+	}
+
+	if (hostapd_parse_ip_addr(pos, &addr) < 0) {
+		if (buf[0] != '[') {
+			port = os_strrchr(pos, ':');
+			if (port) {
+				*port++ = '\0';
+				bi->port = atoi(port);
+			}
+		}
+		if (hostapd_parse_ip_addr(pos, &addr) < 0) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Invalid IP address in URI host entry: %s",
+				   pos);
+			return -1;
+		}
+	}
+	os_free(bi->host);
+	bi->host = os_memdup(&addr, sizeof(addr));
+	if (!bi->host)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: host: %s port: %u",
+		   hostapd_ip_txt(bi->host, buf, sizeof(buf)), bi->port);
+
+	return 0;
+}
+
+
 static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
 {
 	const char *pos = uri;
 	const char *end;
 	const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
-	const char *version = NULL, *supported_curves = NULL;
+	const char *version = NULL, *supported_curves = NULL, *host = NULL;
 	struct dpp_bootstrap_info *bi;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
@@ -409,6 +469,8 @@
 			version = pos + 2;
 		else if (pos[0] == 'B' && pos[1] == ':' && !supported_curves)
 			supported_curves = pos + 2;
+		else if (pos[0] == 'H' && pos[1] == ':' && !host)
+			host = pos + 2;
 		else
 			wpa_hexdump_ascii(MSG_DEBUG,
 					  "DPP: Ignore unrecognized URI parameter",
@@ -431,6 +493,7 @@
 	    dpp_parse_uri_info(bi, info) < 0 ||
 	    dpp_parse_uri_version(bi, version) < 0 ||
 	    dpp_parse_uri_supported_curves(bi, supported_curves) < 0 ||
+	    dpp_parse_uri_host(bi, host) < 0 ||
 	    dpp_parse_uri_pk(bi, pk) < 0) {
 		dpp_bootstrap_info_free(bi);
 		bi = NULL;
@@ -632,6 +695,7 @@
 	char macstr[ETH_ALEN * 2 + 10];
 	size_t len;
 	char supp_curves[10];
+	char host[100];
 
 	len = 4; /* "DPP:" */
 	if (bi->chan)
@@ -664,11 +728,29 @@
 		supp_curves[0] = '\0';
 	}
 
+	host[0] = '\0';
+	if (bi->host) {
+		char buf[100];
+		const char *addr;
+
+		addr = hostapd_ip_txt(bi->host, buf, sizeof(buf));
+		if (!addr)
+			return -1;
+		if (bi->port == DPP_TCP_PORT)
+			len += os_snprintf(host, sizeof(host), "H:%s;", addr);
+		else if (bi->host->af == AF_INET)
+			len += os_snprintf(host, sizeof(host), "H:%s:%u;",
+					   addr, bi->port);
+		else
+			len += os_snprintf(host, sizeof(host), "H:[%s]:%u;",
+					   addr, bi->port);
+	}
+
 	os_free(bi->uri);
 	bi->uri = os_malloc(len + 1);
 	if (!bi->uri)
 		return -1;
-	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%s%sK:%s;;",
 		    bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
 		    bi->chan ? ";" : "",
 		    macstr,
@@ -677,6 +759,7 @@
 		    DPP_VERSION == 3 ? "V:3;" :
 		    (DPP_VERSION == 2 ? "V:2;" : ""),
 		    supp_curves,
+		    host,
 		    bi->pk);
 	return 0;
 }
@@ -886,7 +969,9 @@
 struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
 					  const char *name,
 					  enum dpp_netrole netrole,
-					  const char *mud_url, int *opclasses)
+					  const char *mud_url, int *opclasses,
+					  const char *extra_name,
+					  const char *extra_value)
 {
 	size_t len, name_len;
 	const char *tech = "infra";
@@ -909,6 +994,8 @@
 	len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
 	if (mud_url && mud_url[0])
 		len += 10 + os_strlen(mud_url);
+	if (extra_name && extra_value && extra_name[0] && extra_value[0])
+		len += 10 + os_strlen(extra_name) + os_strlen(extra_value);
 #ifdef CONFIG_DPP2
 	if (auth->csr) {
 		size_t csr_len;
@@ -948,6 +1035,10 @@
 		json_value_sep(json);
 		json_add_string(json, "pkcs10", csr);
 	}
+	if (extra_name && extra_value && extra_name[0] && extra_value[0]) {
+		json_value_sep(json);
+		wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value);
+	}
 	json_end_object(json);
 
 	buf = dpp_build_conf_req(auth, wpabuf_head(json));
@@ -1061,6 +1152,8 @@
 	str_clear_free(conf->passphrase);
 	os_free(conf->group_id);
 	os_free(conf->csrattrs);
+	os_free(conf->extra_name);
+	os_free(conf->extra_value);
 	bin_clear_free(conf, sizeof(*conf));
 }
 
@@ -1187,6 +1280,29 @@
 		os_memcpy(conf->csrattrs, pos, len);
 	}
 
+	pos = os_strstr(cmd, " conf_extra_name=");
+	if (pos) {
+		pos += 17;
+		end = os_strchr(pos, ' ');
+		len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->extra_name = os_zalloc(len + 1);
+		if (!conf->extra_name)
+			goto fail;
+		os_memcpy(conf->extra_name, pos, len);
+	}
+
+	pos = os_strstr(cmd, " conf_extra_value=");
+	if (pos) {
+		pos += 18;
+		end = os_strchr(pos, ' ');
+		len = end ? (size_t) (end - pos) : os_strlen(pos);
+		len /= 2;
+		conf->extra_value = os_zalloc(len + 1);
+		if (!conf->extra_value ||
+		    hexstr2bin(pos, (u8 *) conf->extra_value, len) < 0)
+			goto fail;
+	}
+
 	if (!dpp_configuration_valid(conf))
 		goto fail;
 
@@ -1194,8 +1310,14 @@
 		auth->conf_sta = conf_sta;
 		auth->conf_ap = conf_ap;
 	} else if (idx == 1) {
-		auth->conf2_sta = conf_sta;
-		auth->conf2_ap = conf_ap;
+		if (!auth->conf_sta)
+			auth->conf_sta = conf_sta;
+		else
+			auth->conf2_sta = conf_sta;
+		if (!auth->conf_ap)
+			auth->conf_ap = conf_ap;
+		else
+			auth->conf2_ap = conf_ap;
 	} else {
 		goto fail;
 	}
@@ -1226,7 +1348,7 @@
 		goto fail;
 	os_memcpy(tmp, cmd, len);
 	tmp[len] = '\0';
-	res = dpp_configuration_parse_helper(auth, cmd, 0);
+	res = dpp_configuration_parse_helper(auth, tmp, 0);
 	str_clear_free(tmp);
 	if (res)
 		goto fail;
@@ -1499,6 +1621,32 @@
 }
 
 
+static bool dpp_supports_curve(const char *curve, struct dpp_bootstrap_info *bi)
+{
+	enum dpp_bootstrap_supported_curves idx;
+
+	if (!bi || !bi->supported_curves)
+		return true; /* no support indication available */
+
+	if (os_strcmp(curve, "prime256v1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_P_256;
+	else if (os_strcmp(curve, "secp384r1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_P_384;
+	else if (os_strcmp(curve, "secp521r1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_P_521;
+	else if (os_strcmp(curve, "brainpoolP256r1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_BP_256;
+	else if (os_strcmp(curve, "brainpoolP384r1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_BP_384;
+	else if (os_strcmp(curve, "brainpoolP512r1") == 0)
+		idx = DPP_BOOTSTRAP_CURVE_BP_512;
+	else
+		return true;
+
+	return bi->supported_curves & BIT(idx);
+}
+
+
 static struct wpabuf *
 dpp_build_conf_obj_dpp(struct dpp_authentication *auth,
 		       struct dpp_configuration *conf)
@@ -1520,10 +1668,23 @@
 		goto fail;
 	}
 	curve = auth->conf->curve;
+	if (dpp_akm_dpp(conf->akm) &&
+	    !dpp_supports_curve(curve->name, auth->peer_bi)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Enrollee does not support C-sign-key curve (%s) - cannot generate config object",
+			   curve->name);
+		goto fail;
+	}
 	if (auth->new_curve && auth->new_key_received)
 		nak_curve = auth->new_curve;
 	else
 		nak_curve = auth->curve;
+	if (!dpp_supports_curve(nak_curve->name, auth->peer_bi)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Enrollee does not support netAccessKey curve (%s) - cannot generate config object",
+			   nak_curve->name);
+		goto fail;
+	}
 
 	akm = conf->akm;
 	if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -1580,6 +1741,13 @@
 	if (auth->conf->net_access_key_curve &&
 	    auth->curve != auth->conf->net_access_key_curve &&
 	    !auth->new_key_received) {
+		if (!dpp_supports_curve(auth->conf->net_access_key_curve->name,
+					auth->peer_bi)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Enrollee does not support the required netAccessKey curve (%s) - cannot generate config object",
+				   auth->conf->net_access_key_curve->name);
+			goto fail;
+		}
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Peer protocol key curve (%s) does not match the required netAccessKey curve (%s) - %s",
 			   auth->curve->name,
@@ -1642,6 +1810,9 @@
 			tailroom += os_strlen(auth->trusted_eap_server_name);
 		tailroom += 1000;
 	}
+	if (conf->extra_name && conf->extra_value)
+		tailroom += 10 + os_strlen(conf->extra_name) +
+			os_strlen(conf->extra_value);
 	buf = dpp_build_conf_start(auth, conf, tailroom);
 	if (!buf)
 		goto fail;
@@ -1702,6 +1873,11 @@
 #endif /* CONFIG_DPP2 */
 
 	json_end_object(buf);
+	if (conf->extra_name && conf->extra_value) {
+		json_value_sep(buf);
+		wpabuf_printf(buf, "\"%s\":%s", conf->extra_name,
+			      conf->extra_value);
+	}
 	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
@@ -1739,8 +1915,12 @@
 {
 	struct wpabuf *buf;
 	const char *akm_str;
+	size_t len = 1000;
 
-	buf = dpp_build_conf_start(auth, conf, 1000);
+	if (conf->extra_name && conf->extra_value)
+		len += 10 + os_strlen(conf->extra_name) +
+			os_strlen(conf->extra_value);
+	buf = dpp_build_conf_start(auth, conf, len);
 	if (!buf)
 		return NULL;
 
@@ -1753,6 +1933,11 @@
 	json_value_sep(buf);
 	dpp_build_legacy_cred_params(buf, conf);
 	json_end_object(buf);
+	if (conf->extra_name && conf->extra_value) {
+		json_value_sep(buf);
+		wpabuf_printf(buf, "\"%s\":%s", conf->extra_name,
+			      conf->extra_value);
+	}
 	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
@@ -3967,12 +4152,12 @@
 	       const u8 *net_access_key, size_t net_access_key_len,
 	       const u8 *csign_key, size_t csign_key_len,
 	       const u8 *peer_connector, size_t peer_connector_len,
-	       os_time_t *expiry)
+	       os_time_t *expiry, u8 *peer_key_hash)
 {
 	struct json_token *root = NULL, *netkey, *token;
 	struct json_token *own_root = NULL;
 	enum dpp_status_error ret = 255, res;
-	struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
+	struct crypto_ec_key *own_key = NULL;
 	struct wpabuf *own_key_pub = NULL;
 	const struct dpp_curve_params *curve, *own_curve;
 	struct dpp_signed_connector_info info;
@@ -4045,12 +4230,12 @@
 		goto fail;
 	}
 
-	peer_key = dpp_parse_jwk(netkey, &curve);
-	if (!peer_key) {
+	intro->peer_key = dpp_parse_jwk(netkey, &curve);
+	if (!intro->peer_key) {
 		ret = DPP_STATUS_INVALID_CONNECTOR;
 		goto fail;
 	}
-	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+	dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key);
 
 	if (own_curve != curve) {
 		wpa_printf(MSG_DEBUG,
@@ -4061,7 +4246,7 @@
 	}
 
 	/* ECDH: N = nk * PK */
-	if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0)
+	if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0)
 		goto fail;
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
@@ -4075,26 +4260,48 @@
 	intro->pmk_len = curve->hash_len;
 
 	/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
-	if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
+	if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) <
+	    0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
 		goto fail;
 	}
 
+#ifdef CONFIG_DPP3
+	if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id,
+			   &intro->aead_id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Unsupported group %d",
+			   curve->ike_group);
+		goto fail;
+	}
+#endif /* CONFIG_DPP3 */
+
+	if (peer_key_hash)
+		dpp_get_pubkey_hash(intro->peer_key, peer_key_hash);
+
 	ret = DPP_STATUS_OK;
 fail:
 	if (ret != DPP_STATUS_OK)
-		os_memset(intro, 0, sizeof(*intro));
+		dpp_peer_intro_deinit(intro);
 	os_memset(Nx, 0, sizeof(Nx));
 	os_free(info.payload);
 	crypto_ec_key_deinit(own_key);
 	wpabuf_free(own_key_pub);
-	crypto_ec_key_deinit(peer_key);
 	json_free(root);
 	json_free(own_root);
 	return ret;
 }
 
 
+void dpp_peer_intro_deinit(struct dpp_introduction *intro)
+{
+	if (!intro)
+		return;
+
+	crypto_ec_key_deinit(intro->peer_key);
+	os_memset(intro, 0, sizeof(*intro));
+}
+
+
 #ifdef CONFIG_DPP3
 int dpp_get_connector_version(const char *connector)
 {
@@ -4233,7 +4440,7 @@
 int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
 {
 	char *mac = NULL, *info = NULL, *curve = NULL;
-	char *key = NULL, *supported_curves = NULL;
+	char *key = NULL, *supported_curves = NULL, *host = NULL;
 	u8 *privkey = NULL;
 	size_t privkey_len = 0;
 	int ret = -1;
@@ -4261,6 +4468,7 @@
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
 	supported_curves = get_param(cmd, " supported_curves=");
+	host = get_param(cmd, " host=");
 
 	if (key) {
 		privkey_len = os_strlen(key) / 2;
@@ -4275,6 +4483,7 @@
 	    dpp_parse_uri_mac(bi, mac) < 0 ||
 	    dpp_parse_uri_info(bi, info) < 0 ||
 	    dpp_parse_supported_curves_list(bi, supported_curves) < 0 ||
+	    dpp_parse_uri_host(bi, host) < 0 ||
 	    dpp_gen_uri(bi) < 0)
 		goto fail;
 
@@ -4288,6 +4497,7 @@
 	os_free(info);
 	str_clear_free(key);
 	os_free(supported_curves);
+	os_free(host);
 	bin_clear_free(privkey, privkey_len);
 	dpp_bootstrap_info_free(bi);
 	return ret;
@@ -4343,6 +4553,8 @@
 	struct dpp_bootstrap_info *bi;
 	char pkhash[2 * SHA256_MAC_LEN + 1];
 	char supp_curves[100];
+	char host[100];
+	int ret;
 
 	bi = dpp_bootstrap_get_id(dpp, id);
 	if (!bi)
@@ -4352,7 +4564,6 @@
 
 	supp_curves[0] = '\0';
 	if (bi->supported_curves) {
-		int ret;
 		size_t i;
 		char *pos = supp_curves;
 		char *end = &supp_curves[sizeof(supp_curves)];
@@ -4379,6 +4590,17 @@
 			supp_curves[0] = '\0';
 	}
 
+	host[0] = '\0';
+	if (bi->host) {
+		char buf[100];
+
+		ret = os_snprintf(host, sizeof(host), "host=%s %u\n",
+				  hostapd_ip_txt(bi->host, buf, sizeof(buf)),
+				  bi->port);
+		if (os_snprintf_error(sizeof(host), ret))
+			return -1;
+	}
+
 	return os_snprintf(reply, reply_size, "type=%s\n"
 			   "mac_addr=" MACSTR "\n"
 			   "info=%s\n"
@@ -4386,7 +4608,7 @@
 			   "use_freq=%u\n"
 			   "curve=%s\n"
 			   "pkhash=%s\n"
-			   "version=%d\n%s",
+			   "version=%d\n%s%s",
 			   dpp_bootstrap_type_txt(bi->type),
 			   MAC2STR(bi->mac_addr),
 			   bi->info ? bi->info : "",
@@ -4395,7 +4617,8 @@
 			   bi->curve->name,
 			   pkhash,
 			   bi->version,
-			   supp_curves);
+			   supp_curves,
+			   host);
 }
 
 
@@ -4796,6 +5019,7 @@
 #ifdef CONFIG_DPP2
 	dl_list_init(&dpp->controllers);
 	dl_list_init(&dpp->tcp_init);
+	dpp->relay_sock = -1;
 #endif /* CONFIG_DPP2 */
 
 	return dpp;
@@ -4824,6 +5048,24 @@
 }
 
 
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator)
+{
+	u8 hash[SHA256_MAC_LEN];
+	char hex[SHA256_MAC_LEN * 2 + 1];
+
+	if (auth->peer_protocol_key) {
+		dpp_get_pubkey_hash(auth->peer_protocol_key, hash);
+		wpa_snprintf_hex(hex, sizeof(hex), hash, sizeof(hash));
+	} else {
+		hex[0] = '\0';
+	}
+	wpa_msg(auth->msg_ctx, MSG_INFO,
+		DPP_EVENT_AUTH_SUCCESS "init=%d pkhash=%s own=%d peer=%d",
+		initiator, hex, auth->own_bi ? (int) auth->own_bi->id : -1,
+		auth->peer_bi ? (int) auth->peer_bi->id : -1);
+}
+
+
 #ifdef CONFIG_DPP2
 
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
@@ -4856,3 +5098,98 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi)
+{
+	struct wpabuf *msg;
+	const u8 *r_hash = bi->pubkey_hash_chirp;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Build Push Button Presence Announcement frame");
+
+	msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT,
+			    4 + SHA256_MAC_LEN);
+	if (!msg)
+		return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, r_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		r_hash = test_hash;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Responder Bootstrapping Key Hash */
+	dpp_build_attr_r_bootstrap_key_hash(msg, r_hash);
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Push Button Presence Announcement frame attributes",
+			msg);
+	return msg;
+}
+
+
+struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi,
+					       const u8 *e_hash,
+					       const u8 *c_nonce,
+					       size_t c_nonce_len)
+{
+	struct wpabuf *msg;
+	const u8 *i_hash = bi->pubkey_hash_chirp;
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Build Push Button Presence Announcement Response frame");
+
+	msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP,
+			    2 * (4 + SHA256_MAC_LEN) + 4 + c_nonce_len);
+	if (!msg)
+		return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid I-Bootstrap Key Hash");
+		os_memcpy(test_hash, i_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		i_hash = test_hash;
+	} else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - invalid R-Bootstrap Key Hash");
+		os_memcpy(test_hash, e_hash, SHA256_MAC_LEN);
+		test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+		e_hash = test_hash;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Initiator Bootstrapping Key Hash */
+	wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+	wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+	wpabuf_put_le16(msg, SHA256_MAC_LEN);
+	wpabuf_put_data(msg, i_hash, SHA256_MAC_LEN);
+
+	/* Responder Bootstrapping Key Hash */
+	dpp_build_attr_r_bootstrap_key_hash(msg, e_hash);
+
+	/* Configurator Nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
+	wpabuf_put_le16(msg, c_nonce_len);
+	wpabuf_put_data(msg, c_nonce, c_nonce_len);
+
+	wpa_hexdump_buf(MSG_DEBUG,
+			"DPP: Push Button Presence Announcement Response frame attributes",
+			msg);
+	return msg;
+}
+
+#endif /* CONFIG_DPP3 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index fba4119..86f8478 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -56,6 +56,11 @@
 	DPP_PA_RECONFIG_AUTH_RESP = 16,
 	DPP_PA_RECONFIG_AUTH_CONF = 17,
 	DPP_PA_PKEX_EXCHANGE_REQ = 18,
+	DPP_PA_PB_PRESENCE_ANNOUNCEMENT = 19,
+	DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP = 20,
+	DPP_PA_PRIV_PEER_INTRO_QUERY = 21,
+	DPP_PA_PRIV_PEER_INTRO_NOTIFY = 22,
+	DPP_PA_PRIV_PEER_INTRO_UPDATE = 23,
 };
 
 enum dpp_attribute_id {
@@ -163,6 +168,8 @@
 	u8 mac_addr[ETH_ALEN];
 	char *chan;
 	char *info;
+	struct hostapd_ip_addr *host;
+	unsigned int port;
 	char *pk;
 	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
 	unsigned int num_freq;
@@ -179,6 +186,9 @@
 	int nfc_negotiated; /* whether this has been used in NFC negotiated
 			     * connection handover */
 	char *configurator_params;
+	u8 peer_pubkey_hash[SHA256_MAC_LEN]; /* for enforcing a specific
+					      * peer bootstrapping key with
+					      * PKEX */
 };
 
 #define PKEX_COUNTER_T_LIMIT 5
@@ -201,6 +211,7 @@
 	u8 peer_mac[ETH_ALEN];
 	char *identifier;
 	char *code;
+	size_t code_len;
 	struct crypto_ec_key *x;
 	struct crypto_ec_key *y;
 	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
@@ -214,6 +225,7 @@
 	unsigned int exch_req_tries;
 	unsigned int freq;
 	u8 peer_version;
+	struct wpabuf *enc_key;
 };
 
 enum dpp_akm {
@@ -252,6 +264,8 @@
 	int psk_set;
 
 	char *csrattrs;
+	char *extra_name;
+	char *extra_value;
 };
 
 struct dpp_asymmetric_key {
@@ -282,6 +296,7 @@
 	enum dpp_status_error auth_resp_status;
 	enum dpp_status_error conf_resp_status;
 	enum dpp_status_error force_conf_resp_status;
+	enum dpp_status_error conn_result_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -326,6 +341,7 @@
 	int connect_on_tx_status;
 	int waiting_conf_result;
 	int waiting_conn_status_result;
+	int tx_conn_status_result_started;
 	int auth_success;
 	bool reconfig_success;
 	struct wpabuf *conf_req;
@@ -409,6 +425,10 @@
 	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	int peer_version;
+	struct crypto_ec_key *peer_key;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
 };
 
 struct dpp_relay_config {
@@ -435,6 +455,13 @@
 	bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth);
 };
 
+#define DPP_PB_INFO_COUNT 2
+
+struct dpp_pb_info {
+	u8 hash[SHA256_MAC_LEN];
+	struct os_reltime rx_time;
+};
+
 #ifdef CONFIG_TESTING_OPTIONS
 enum dpp_test_behavior {
 	DPP_TEST_DISABLED = 0,
@@ -535,6 +562,9 @@
 	DPP_TEST_INVALID_PROTOCOL_VERSION_PEER_DISC_RESP = 95,
 	DPP_TEST_INVALID_PROTOCOL_VERSION_RECONFIG_AUTH_REQ = 96,
 	DPP_TEST_NO_PROTOCOL_VERSION_RECONFIG_AUTH_REQ = 97,
+	DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ = 98,
+	DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP = 99,
+	DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP = 100,
 };
 
 extern enum dpp_test_behavior dpp_test;
@@ -581,7 +611,9 @@
 struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
 					  const char *name,
 					  enum dpp_netrole netrole,
-					  const char *mud_url, int *opclasses);
+					  const char *mud_url, int *opclasses,
+					  const char *extra_name,
+					  const char *extra_value);
 int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 		     const u8 *attr_start, size_t attr_len);
 int dpp_notify_new_qr_code(struct dpp_authentication *auth,
@@ -589,6 +621,8 @@
 void dpp_controller_pkex_add(struct dpp_global *dpp,
 			     struct dpp_bootstrap_info *bi,
 			     const char *code, const char *identifier);
+bool dpp_controller_is_own_pkex_req(struct dpp_global *dpp,
+				    const u8 *buf, size_t len);
 struct dpp_configuration * dpp_configuration_alloc(const char *type);
 int dpp_akm_psk(enum dpp_akm akm);
 int dpp_akm_sae(enum dpp_akm akm);
@@ -640,18 +674,19 @@
 	       const u8 *net_access_key, size_t net_access_key_len,
 	       const u8 *csign_key, size_t csign_key_len,
 	       const u8 *peer_connector, size_t peer_connector_len,
-	       os_time_t *expiry);
+	       os_time_t *expiry, u8 *peer_key_hash);
+void dpp_peer_intro_deinit(struct dpp_introduction *intro);
 int dpp_get_connector_version(const char *connector);
 struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
 				const u8 *own_mac,
 				const char *identifier, const char *code,
-				bool v2);
+				size_t code_len, bool v2);
 struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
 					   struct dpp_bootstrap_info *bi,
 					   const u8 *own_mac,
 					   const u8 *peer_mac,
 					   const char *identifier,
-					   const char *code,
+					   const char *code, size_t code_len,
 					   const u8 *buf, size_t len, bool v2);
 struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
 					  const u8 *peer_mac,
@@ -678,6 +713,11 @@
 int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
 void dpp_pfs_free(struct dpp_pfs *pfs);
 
+struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
+				       const u8 *privkey, size_t privkey_len);
+int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
+		   enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id);
+
 struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
 			      const char *name);
 int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
@@ -714,12 +754,18 @@
 						    const u8 *kid);
 int dpp_relay_add_controller(struct dpp_global *dpp,
 			     struct dpp_relay_config *config);
+void dpp_relay_remove_controller(struct dpp_global *dpp,
+				 const struct hostapd_ip_addr *addr);
+int dpp_relay_listen(struct dpp_global *dpp, int port,
+		     struct dpp_relay_config *config);
+void dpp_relay_stop_listen(struct dpp_global *dpp);
 int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
 			const u8 *buf, size_t len, unsigned int freq,
 			const u8 *i_bootstrap, const u8 *r_bootstrap,
 			void *cb_ctx);
 int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
 			 size_t data_len);
+bool dpp_relay_controller_available(struct dpp_global *dpp);
 int dpp_controller_start(struct dpp_global *dpp,
 			 struct dpp_controller_config *config);
 int dpp_controller_set_params(struct dpp_global *dpp,
@@ -737,15 +783,20 @@
 				       struct dpp_bootstrap_info *bi));
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
 		 const struct hostapd_ip_addr *addr, int port,
-		 const char *name, enum dpp_netrole netrole, void *msg_ctx,
-		 void *cb_ctx,
+		 const char *name, enum dpp_netrole netrole,
+		 const char *mud_url,
+		 const char *extra_conf_req_name,
+		 const char *extra_conf_req_value,
+		 void *msg_ctx, void *cb_ctx,
 		 int (*process_conf_obj)(void *ctx,
 					 struct dpp_authentication *auth),
 		 bool (*tcp_msg_sent)(void *ctx,
 				      struct dpp_authentication *auth));
 int dpp_tcp_auth(struct dpp_global *dpp, void *_conn,
 		 struct dpp_authentication *auth, const char *name,
-		 enum dpp_netrole netrole,
+		 enum dpp_netrole netrole, const char *mud_url,
+		 const char *extra_conf_req_name,
+		 const char *extra_conf_req_value,
 		 int (*process_conf_obj)(void *ctx,
 					 struct dpp_authentication *auth),
 		 bool (*tcp_msg_sent)(void *ctx,
@@ -760,6 +811,12 @@
 void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
 				unsigned int freq, const u8 *hash);
 
+struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi);
+struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi,
+					       const u8 *e_hash,
+					       const u8 *c_nonce,
+					       size_t c_nonce_len);
+
 struct dpp_global_config {
 	void *cb_ctx;
 	void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
@@ -768,6 +825,7 @@
 struct dpp_global * dpp_global_init(struct dpp_global_config *config);
 void dpp_global_clear(struct dpp_global *dpp);
 void dpp_global_deinit(struct dpp_global *dpp);
+void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator);
 
 /* dpp_reconfig.c */
 
@@ -800,6 +858,7 @@
 					     size_t pp_key_len);
 int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
 void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash);
 
 #endif /* CONFIG_DPP */
 #endif /* DPP_H */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 47f56c2..f17f95a 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -246,6 +246,27 @@
 }
 
 
+int dpp_get_pubkey_hash(struct crypto_ec_key *key, u8 *hash)
+{
+	struct wpabuf *uncomp;
+	const u8 *addr[1];
+	size_t len[1];
+	int res;
+
+	if (!key)
+		return -1;
+
+	uncomp = crypto_ec_key_get_pubkey_point(key, 1);
+	if (!uncomp)
+		return -1;
+	addr[0] = wpabuf_head(uncomp);
+	len[0] = wpabuf_len(uncomp);
+	res = sha256_vector(1, addr, len, hash);
+	wpabuf_free(uncomp);
+	return res;
+}
+
+
 struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
 {
 	struct crypto_ec_key *key;
@@ -1035,10 +1056,9 @@
 int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
 {
 	struct crypto_ec *ec;
-	struct crypto_ec_point *L = NULL;
-	const struct crypto_ec_point *BI;
-	const struct crypto_bignum *bR, *pR, *q;
-	struct crypto_bignum *sum = NULL, *lx = NULL;
+	struct crypto_ec_point *L = NULL, *BI = NULL;
+	const struct crypto_bignum *q;
+	struct crypto_bignum *sum = NULL, *lx = NULL, *bR = NULL, *pR = NULL;
 	int ret = -1;
 
 	/* L = ((bR + pR) modulo q) * BI */
@@ -1068,7 +1088,10 @@
 fail:
 	crypto_bignum_deinit(lx, 1);
 	crypto_bignum_deinit(sum, 1);
+	crypto_bignum_deinit(bR, 1);
+	crypto_bignum_deinit(pR, 1);
 	crypto_ec_point_deinit(L, 1);
+	crypto_ec_point_deinit(BI, 1);
 	crypto_ec_deinit(ec);
 	return ret;
 }
@@ -1077,10 +1100,8 @@
 int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
 {
 	struct crypto_ec *ec;
-	struct crypto_ec_point *L = NULL, *sum = NULL;
-	const struct crypto_ec_point *BR, *PR;
-	const struct crypto_bignum *bI;
-	struct crypto_bignum *lx = NULL;
+	struct crypto_ec_point *L = NULL, *sum = NULL, *BR = NULL, *PR = NULL;
+	struct crypto_bignum *lx = NULL, *bI = NULL;
 	int ret = -1;
 
 	/* L = bI * (BR + PR) */
@@ -1108,8 +1129,11 @@
 	ret = 0;
 fail:
 	crypto_bignum_deinit(lx, 1);
+	crypto_bignum_deinit(bI, 1);
 	crypto_ec_point_deinit(sum, 1);
 	crypto_ec_point_deinit(L, 1);
+	crypto_ec_point_deinit(BR, 1);
+	crypto_ec_point_deinit(PR, 1);
 	crypto_ec_deinit(ec);
 	return ret;
 }
@@ -1434,16 +1458,15 @@
 
 struct crypto_ec_point *
 dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
-		   const char *code, const char *identifier,
+		   const char *code, size_t code_len, const char *identifier,
 		   struct crypto_ec **ret_ec)
 {
 	u8 hash[DPP_MAX_HASH_LEN];
 	const u8 *addr[3];
 	size_t len[3];
 	unsigned int num_elem = 0;
-	struct crypto_ec_point *Qi = NULL;
+	struct crypto_ec_point *Qi = NULL, *Pi = NULL;
 	struct crypto_ec_key *Pi_key = NULL;
-	const struct crypto_ec_point *Pi = NULL;
 	struct crypto_bignum *hash_bn = NULL;
 	struct crypto_ec *ec = NULL;
 
@@ -1463,9 +1486,9 @@
 		len[num_elem] = os_strlen(identifier);
 		num_elem++;
 	}
-	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len);
 	addr[num_elem] = (const u8 *) code;
-	len[num_elem] = os_strlen(code);
+	len[num_elem] = code_len;
 	num_elem++;
 	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
 		goto fail;
@@ -1494,6 +1517,7 @@
 	crypto_ec_point_debug_print(ec, Qi, "DPP: Qi");
 out:
 	crypto_ec_key_deinit(Pi_key);
+	crypto_ec_point_deinit(Pi, 1);
 	crypto_bignum_deinit(hash_bn, 1);
 	if (ret_ec && Qi)
 		*ret_ec = ec;
@@ -1509,16 +1533,15 @@
 
 struct crypto_ec_point *
 dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
-		   const char *code, const char *identifier,
+		   const char *code, size_t code_len, const char *identifier,
 		   struct crypto_ec **ret_ec)
 {
 	u8 hash[DPP_MAX_HASH_LEN];
 	const u8 *addr[3];
 	size_t len[3];
 	unsigned int num_elem = 0;
-	struct crypto_ec_point *Qr = NULL;
+	struct crypto_ec_point *Qr = NULL, *Pr = NULL;
 	struct crypto_ec_key *Pr_key = NULL;
-	const struct crypto_ec_point *Pr = NULL;
 	struct crypto_bignum *hash_bn = NULL;
 	struct crypto_ec *ec = NULL;
 
@@ -1538,9 +1561,9 @@
 		len[num_elem] = os_strlen(identifier);
 		num_elem++;
 	}
-	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len);
 	addr[num_elem] = (const u8 *) code;
-	len[num_elem] = os_strlen(code);
+	len[num_elem] = code_len;
 	num_elem++;
 	if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
 		goto fail;
@@ -1570,6 +1593,7 @@
 
 out:
 	crypto_ec_key_deinit(Pr_key);
+	crypto_ec_point_deinit(Pr, 1);
 	crypto_bignum_deinit(hash_bn, 1);
 	if (ret_ec && Qr)
 		*ret_ec = ec;
@@ -1587,7 +1611,7 @@
 		      u8 ver_init, u8 ver_resp,
 		      const u8 *Mx, size_t Mx_len,
 		      const u8 *Nx, size_t Nx_len,
-		      const char *code,
+		      const char *code, size_t code_len,
 		      const u8 *Kx, size_t Kx_len,
 		      u8 *z, unsigned int hash_len)
 {
@@ -1612,7 +1636,7 @@
 		info_len = 2 * ETH_ALEN;
 	else
 		info_len = 2;
-	info_len += Mx_len + Nx_len + os_strlen(code);
+	info_len += Mx_len + Nx_len + code_len;
 	info = os_malloc(info_len);
 	if (!info)
 		return -1;
@@ -1630,7 +1654,7 @@
 	pos += Mx_len;
 	os_memcpy(pos, Nx, Nx_len);
 	pos += Nx_len;
-	os_memcpy(pos, code, os_strlen(code));
+	os_memcpy(pos, code, code_len);
 
 	/* HKDF-Expand(PRK, info, L) */
 	if (hash_len == 32)
@@ -1661,11 +1685,10 @@
 				     struct json_token *peer_net_access_key)
 {
 	struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
-	struct crypto_bignum *sum = NULL;
-	const struct crypto_bignum *q, *cR, *pR;
+	struct crypto_bignum *sum = NULL, *cR = NULL, *pR = NULL;
+	const struct crypto_bignum *q;
 	struct crypto_ec *ec = NULL;
-	struct crypto_ec_point *M = NULL;
-	const struct crypto_ec_point *CI;
+	struct crypto_ec_point *M = NULL, *CI = NULL;
 	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
 	u8 prk[DPP_MAX_HASH_LEN];
 	const struct dpp_curve_params *curve;
@@ -1748,7 +1771,10 @@
 	forced_memzero(prk, sizeof(prk));
 	forced_memzero(Mx, sizeof(Mx));
 	crypto_ec_point_deinit(M, 1);
+	crypto_ec_point_deinit(CI, 1);
 	crypto_bignum_deinit(sum, 1);
+	crypto_bignum_deinit(cR, 1);
+	crypto_bignum_deinit(pR, 1);
 	crypto_ec_key_deinit(own_key);
 	crypto_ec_key_deinit(peer_key);
 	crypto_ec_deinit(ec);
@@ -1761,10 +1787,9 @@
 				     struct json_token *net_access_key)
 {
 	struct crypto_ec_key *pr = NULL, *peer_key = NULL;
-	const struct crypto_ec_point *CR, *PR;
-	const struct crypto_bignum *cI;
+	struct crypto_bignum *cI = NULL;
 	struct crypto_ec *ec = NULL;
-	struct crypto_ec_point *sum = NULL, *M = NULL;
+	struct crypto_ec_point *sum = NULL, *M = NULL, *CR = NULL, *PR = NULL;
 	u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
 	u8 prk[DPP_MAX_HASH_LEN];
 	int res = -1;
@@ -1835,10 +1860,13 @@
 fail:
 	forced_memzero(prk, sizeof(prk));
 	forced_memzero(Mx, sizeof(Mx));
+	crypto_bignum_deinit(cI, 1);
 	crypto_ec_key_deinit(pr);
 	crypto_ec_key_deinit(peer_key);
 	crypto_ec_point_deinit(sum, 1);
 	crypto_ec_point_deinit(M, 1);
+	crypto_ec_point_deinit(CR, 1);
+	crypto_ec_point_deinit(PR, 1);
 	crypto_ec_deinit(ec);
 	return res;
 }
@@ -2259,8 +2287,8 @@
 {
 	const struct crypto_bignum *q;
 	struct crypto_bignum *bn;
-	const struct crypto_ec_point *pp, *generator;
-	struct crypto_ec_point *e_prime_id, *a_nonce;
+	const struct crypto_ec_point *generator;
+	struct crypto_ec_point *e_prime_id, *a_nonce, *pp;
 	int ret = -1;
 
 	pp = crypto_ec_key_get_public_key(id->pp_key);
@@ -2297,6 +2325,7 @@
 fail:
 	crypto_ec_point_deinit(e_prime_id, 1);
 	crypto_ec_point_deinit(a_nonce, 1);
+	crypto_ec_point_deinit(pp, 1);
 	crypto_bignum_deinit(bn, 1);
 	return ret;
 }
@@ -2321,9 +2350,9 @@
 					  struct crypto_ec_key *e_prime_id)
 {
 	struct crypto_ec *ec;
-	const struct crypto_bignum *pp;
+	struct crypto_bignum *pp = NULL;
 	struct crypto_ec_point *e_id = NULL;
-	const struct crypto_ec_point *a_nonce_point, *e_prime_id_point;
+	struct crypto_ec_point *a_nonce_point, *e_prime_id_point;
 
 	if (!ppkey)
 		return NULL;
@@ -2348,6 +2377,9 @@
 	crypto_ec_point_debug_print(ec, e_id, "DPP: Decrypted E-id");
 
 fail:
+	crypto_ec_point_deinit(a_nonce_point, 1);
+	crypto_ec_point_deinit(e_prime_id_point, 1);
+	crypto_bignum_deinit(pp, 1);
 	crypto_ec_deinit(ec);
 	return e_id;
 }
@@ -2356,6 +2388,7 @@
 
 
 #ifdef CONFIG_DPP3
+
 int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i)
 {
 	int ret = -1, res;
@@ -2443,6 +2476,47 @@
 	wpabuf_free(pex);
 	return ret;
 }
+
+
+int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
+		   enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id)
+{
+	switch (iana_group) {
+	case 19:
+		*kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
+		*kdf_id = HPKE_KDF_HKDF_SHA256;
+		*aead_id = HPKE_AEAD_AES_128_GCM;
+		return 0;
+	case 20:
+		*kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
+		*kdf_id = HPKE_KDF_HKDF_SHA384;
+		*aead_id = HPKE_AEAD_AES_256_GCM;
+		return 0;
+	case 21:
+		*kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
+		*kdf_id = HPKE_KDF_HKDF_SHA512;
+		*aead_id = HPKE_AEAD_AES_256_GCM;
+		return 0;
+	case 28:
+		*kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
+		*kdf_id = HPKE_KDF_HKDF_SHA256;
+		*aead_id = HPKE_AEAD_AES_128_GCM;
+		return 0;
+	case 29:
+		*kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
+		*kdf_id = HPKE_KDF_HKDF_SHA384;
+		*aead_id = HPKE_AEAD_AES_256_GCM;
+		return 0;
+	case 30:
+		*kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
+		*kdf_id = HPKE_KDF_HKDF_SHA512;
+		*aead_id = HPKE_AEAD_AES_256_GCM;
+		return 0;
+	}
+
+	return -1;
+}
+
 #endif /* CONFIG_DPP3 */
 
 
@@ -2453,8 +2527,7 @@
 {
 	struct crypto_ec *ec;
 	struct crypto_ec_key *key = NULL;
-	const struct crypto_ec_point *pub_key;
-	struct crypto_ec_point *p = NULL;
+	struct crypto_ec_point *p = NULL, *pub_key = NULL;
 	u8 *x, *y;
 	int ret = -1;
 
@@ -2472,11 +2545,9 @@
 
 	/* Retrieve public key coordinates */
 	pub_key = crypto_ec_key_get_public_key(key);
-	if (!pub_key)
+	if (!pub_key || crypto_ec_point_to_bin(ec, pub_key, x, y))
 		goto fail;
 
-	crypto_ec_point_to_bin(ec, pub_key, x, y);
-
 	/* And corrupt them */
 	y[curve->prime_len - 1] ^= 0x01;
 	p = crypto_ec_point_from_bin(ec, x);
@@ -2489,6 +2560,7 @@
 	ret = 0;
 fail:
 	crypto_ec_point_deinit(p, 0);
+	crypto_ec_point_deinit(pub_key, 0);
 	crypto_ec_key_deinit(key);
 	crypto_ec_deinit(ec);
 	return ret;
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index 10db4e8..dfa4a3c 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -19,8 +19,16 @@
 	struct dl_list configurator; /* struct dpp_configurator */
 #ifdef CONFIG_DPP2
 	struct dl_list controllers; /* struct dpp_relay_controller */
+	struct dpp_relay_controller *tmp_controller;
 	struct dpp_controller *controller;
 	struct dl_list tcp_init; /* struct dpp_connection */
+	int relay_sock;
+	void *relay_msg_ctx;
+	void *relay_cb_ctx;
+	void (*relay_tx)(void *ctx, const u8 *addr, unsigned int freq,
+			 const u8 *msg, size_t len);
+	void (*relay_gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+				  int prot, struct wpabuf *buf);
 	void *cb_ctx;
 	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 	bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth);
@@ -97,8 +105,6 @@
 int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
 int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 	       const u8 *privkey, size_t privkey_len);
-struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
-				       const u8 *privkey, size_t privkey_len);
 struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve);
 int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
 int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
@@ -113,17 +119,17 @@
 		     struct crypto_ec_key *peer_key, u8 *pmkid);
 struct crypto_ec_point *
 dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
-		   const char *code, const char *identifier,
+		   const char *code, size_t code_len, const char *identifier,
 		   struct crypto_ec **ret_ec);
 struct crypto_ec_point *
 dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
-		   const char *code, const char *identifier,
+		   const char *code, size_t code_len, const char *identifier,
 		   struct crypto_ec **ret_ec);
 int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
 		      u8 ver_init, u8 ver_resp,
 		      const u8 *Mx, size_t Mx_len,
 		      const u8 *Nx, size_t Nx_len,
-		      const char *code,
+		      const char *code, size_t code_len,
 		      const u8 *Kx, size_t Kx_len,
 		      u8 *z, unsigned int hash_len);
 int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c
index 72084d9..dca0d8d 100644
--- a/src/common/dpp_pkex.c
+++ b/src/common/dpp_pkex.c
@@ -30,8 +30,7 @@
 						   bool v2)
 {
 	struct crypto_ec *ec = NULL;
-	const struct crypto_ec_point *X;
-	struct crypto_ec_point *Qi = NULL, *M = NULL;
+	struct crypto_ec_point *Qi = NULL, *M = NULL, *X = NULL;
 	u8 *Mx, *My;
 	struct wpabuf *msg = NULL;
 	size_t attr_len;
@@ -42,7 +41,7 @@
 
 	/* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */
 	Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : pkex->own_mac, pkex->code,
-				pkex->identifier, &ec);
+				pkex->code_len, pkex->identifier, &ec);
 	if (!Qi)
 		goto fail;
 
@@ -146,10 +145,13 @@
 	My = wpabuf_put(msg, curve->prime_len);
 	if (crypto_ec_point_to_bin(ec, M, Mx, My))
 		goto fail;
+	wpabuf_free(pkex->enc_key);
+	pkex->enc_key = wpabuf_alloc_copy(Mx, 2 * curve->prime_len);
 
 	os_memcpy(pkex->Mx, Mx, curve->prime_len);
 
 out:
+	crypto_ec_point_deinit(X, 1);
 	crypto_ec_point_deinit(M, 1);
 	crypto_ec_point_deinit(Qi, 1);
 	crypto_ec_deinit(ec);
@@ -171,7 +173,7 @@
 struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
 				const u8 *own_mac,
 				const char *identifier, const char *code,
-				bool v2)
+				size_t code_len, bool v2)
 {
 	struct dpp_pkex *pkex;
 
@@ -196,9 +198,10 @@
 		if (!pkex->identifier)
 			goto fail;
 	}
-	pkex->code = os_strdup(code);
+	pkex->code = os_memdup(code, code_len);
 	if (!pkex->code)
 		goto fail;
+	pkex->code_len = code_len;
 	pkex->exchange_req = dpp_pkex_build_exchange_req(pkex, v2);
 	if (!pkex->exchange_req)
 		goto fail;
@@ -340,7 +343,7 @@
 					   const u8 *own_mac,
 					   const u8 *peer_mac,
 					   const char *identifier,
-					   const char *code,
+					   const char *code, size_t code_len,
 					   const u8 *buf, size_t len, bool v2)
 {
 	const u8 *attr_group, *attr_id, *attr_key;
@@ -349,9 +352,8 @@
 	u16 ike_group;
 	struct dpp_pkex *pkex = NULL;
 	struct crypto_ec_point *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL,
-		*N = NULL;
+		*N = NULL, *Y = NULL;
 	struct crypto_ec *ec = NULL;
-	const struct crypto_ec_point *Y;
 	u8 *x_coord = NULL, *y_coord = NULL;
 	u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
 	size_t Kx_len;
@@ -438,8 +440,8 @@
 	}
 
 	/* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */
-	Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, identifier,
-				&ec);
+	Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, code_len,
+				identifier, &ec);
 	if (!Qi)
 		goto fail;
 
@@ -478,9 +480,10 @@
 		if (!pkex->identifier)
 			goto fail;
 	}
-	pkex->code = os_strdup(code);
+	pkex->code = os_memdup(code, code_len);
 	if (!pkex->code)
 		goto fail;
+	pkex->code_len = code_len;
 
 	os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
 
@@ -496,8 +499,8 @@
 		goto fail;
 
 	/* Qr = H([MAC-Responder |] [identifier |] code) * Pr */
-	Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, identifier,
-				NULL);
+	Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, code_len,
+				identifier, NULL);
 	if (!Qr)
 		goto fail;
 
@@ -551,7 +554,8 @@
 				pkex->peer_version, DPP_VERSION,
 				pkex->Mx, curve->prime_len,
 				pkex->Nx, curve->prime_len, pkex->code,
-				Kx, Kx_len, pkex->z, curve->hash_len);
+				pkex->code_len,	Kx, Kx_len, pkex->z,
+				curve->hash_len);
 	os_memset(Kx, 0, Kx_len);
 	if (res < 0)
 		goto fail;
@@ -566,6 +570,7 @@
 	crypto_ec_point_deinit(M, 1);
 	crypto_ec_point_deinit(N, 1);
 	crypto_ec_point_deinit(X, 1);
+	crypto_ec_point_deinit(Y, 1);
 	crypto_ec_deinit(ec);
 	return pkex;
 fail:
@@ -791,7 +796,8 @@
 
 	/* Qr = H([MAC-Responder |] [identifier |] code) * Pr */
 	Qr = dpp_pkex_derive_Qr(curve, pkex->v2 ? NULL : pkex->peer_mac,
-				pkex->code, pkex->identifier, &ec);
+				pkex->code, pkex->code_len, pkex->identifier,
+				&ec);
 	if (!Qr)
 		goto fail;
 
@@ -869,7 +875,7 @@
 				DPP_VERSION, pkex->peer_version,
 				pkex->Mx, curve->prime_len,
 				attr_key /* N.x */, attr_key_len / 2,
-				pkex->code, Kx, Kx_len,
+				pkex->code, pkex->code_len, Kx, Kx_len,
 				pkex->z, curve->hash_len);
 	os_memset(Kx, 0, Kx_len);
 	if (res < 0)
@@ -1357,6 +1363,8 @@
 		dpp_bootstrap_info_free(bi);
 		return NULL;
 	}
+	os_memcpy(pkex->own_bi->peer_pubkey_hash, bi->pubkey_hash,
+		  SHA256_MAC_LEN);
 	dpp_pkex_free(pkex);
 	dl_list_add(&dpp->bootstrap, &bi->list);
 	return bi;
@@ -1375,5 +1383,6 @@
 	crypto_ec_key_deinit(pkex->peer_bootstrap_key);
 	wpabuf_free(pkex->exchange_req);
 	wpabuf_free(pkex->exchange_resp);
+	wpabuf_free(pkex->enc_key);
 	os_free(pkex);
 }
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index c83fb2d..d226a8a 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -48,6 +48,9 @@
 	unsigned int gas_comeback_in_progress:1;
 	u8 gas_dialog_token;
 	char *name;
+	char *mud_url;
+	char *extra_conf_req_name;
+	char *extra_conf_req_value;
 	enum dpp_netrole netrole;
 };
 
@@ -118,6 +121,9 @@
 	dpp_auth_deinit(conn->auth);
 	dpp_pkex_free(conn->pkex);
 	os_free(conn->name);
+	os_free(conn->mud_url);
+	os_free(conn->extra_conf_req_name);
+	os_free(conn->extra_conf_req_value);
 	os_free(conn);
 }
 
@@ -133,6 +139,7 @@
 			     struct dpp_relay_config *config)
 {
 	struct dpp_relay_controller *ctrl;
+	char txt[100];
 
 	if (!dpp)
 		return -1;
@@ -148,6 +155,8 @@
 	ctrl->cb_ctx = config->cb_ctx;
 	ctrl->tx = config->tx;
 	ctrl->gas_resp_tx = config->gas_resp_tx;
+	wpa_printf(MSG_DEBUG, "DPP: Add Relay connection to Controller %s",
+		   hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
 	dl_list_add(&dpp->controllers, &ctrl->list);
 	return 0;
 }
@@ -189,6 +198,31 @@
 }
 
 
+static struct dpp_relay_controller *
+dpp_relay_controller_get_addr(struct dpp_global *dpp,
+			      const struct sockaddr_in *addr)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return NULL;
+
+	dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+			 list) {
+		if (ctrl->ipaddr.af == AF_INET &&
+		    addr->sin_addr.s_addr == ctrl->ipaddr.u.v4.s_addr)
+			return ctrl;
+	}
+
+	if (dpp->tmp_controller &&
+	    dpp->tmp_controller->ipaddr.af == AF_INET &&
+	    addr->sin_addr.s_addr == dpp->tmp_controller->ipaddr.u.v4.s_addr)
+		return dpp->tmp_controller;
+
+	return NULL;
+}
+
+
 static void dpp_controller_gas_done(struct dpp_connection *conn)
 {
 	struct dpp_authentication *auth = conn->auth;
@@ -214,7 +248,8 @@
 		return;
 	}
 
-	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+		auth->conf_resp_status);
 	dpp_connection_remove(conn);
 }
 
@@ -312,8 +347,10 @@
 	const char *dpp_name;
 
 	dpp_name = conn->name ? conn->name : "Test";
-	buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
-					NULL);
+	buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole,
+					conn->mud_url, NULL,
+					conn->extra_conf_req_name,
+					conn->extra_conf_req_value);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -334,8 +371,7 @@
 		return;
 
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(conn->msg_ctx, MSG_INFO,
-		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+	dpp_notify_auth_success(auth, initiator);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
 		wpa_printf(MSG_INFO,
@@ -520,6 +556,31 @@
 }
 
 
+static struct dpp_connection *
+dpp_relay_match_ctrl(struct dpp_relay_controller *ctrl, const u8 *src,
+		     unsigned int freq, u8 type)
+{
+	struct dpp_connection *conn;
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+			return conn;
+		if ((type == DPP_PA_PKEX_EXCHANGE_RESP ||
+		     type == DPP_PA_AUTHENTICATION_RESP) &&
+		    conn->freq == 0 &&
+		    is_broadcast_ether_addr(conn->mac_addr)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Associate this peer to the new Controller initiated connection");
+			os_memcpy(conn->mac_addr, src, ETH_ALEN);
+			conn->freq = freq;
+			return conn;
+		}
+	}
+
+	return NULL;
+}
+
+
 int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
 			const u8 *buf, size_t len, unsigned int freq,
 			const u8 *i_bootstrap, const u8 *r_bootstrap,
@@ -538,12 +599,16 @@
 	    type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
 		dl_list_for_each(ctrl, &dpp->controllers,
 				 struct dpp_relay_controller, list) {
-			dl_list_for_each(conn, &ctrl->conn,
-					 struct dpp_connection, list) {
-				if (os_memcmp(src, conn->mac_addr,
-					      ETH_ALEN) == 0)
-					return dpp_relay_tx(conn, hdr, buf, len);
-			}
+			conn = dpp_relay_match_ctrl(ctrl, src, freq, type);
+			if (conn)
+				return dpp_relay_tx(conn, hdr, buf, len);
+		}
+
+		if (dpp->tmp_controller) {
+			conn = dpp_relay_match_ctrl(dpp->tmp_controller, src,
+						    freq, type);
+			if (conn)
+				return dpp_relay_tx(conn, hdr, buf, len);
 		}
 	}
 
@@ -562,6 +627,17 @@
 	if (!ctrl)
 		return -1;
 
+	if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+	    type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+		conn = dpp_relay_match_ctrl(ctrl, src, freq, type);
+		if (conn &&
+		    (!conn->auth || conn->auth->waiting_auth_resp)) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Use existing TCP connection to Controller since no Auth Resp seen on it yet");
+			return dpp_relay_tx(conn, hdr, buf, len);
+		}
+	}
+
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Authentication Request for a configured Controller");
 	conn = dpp_relay_new_conn(ctrl, src, freq);
@@ -579,11 +655,25 @@
 }
 
 
+static struct dpp_connection *
+dpp_relay_find_conn(struct dpp_relay_controller *ctrl, const u8 *src)
+{
+	struct dpp_connection *conn;
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+			return conn;
+	}
+
+	return NULL;
+}
+
+
 int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
 			 size_t data_len)
 {
 	struct dpp_relay_controller *ctrl;
-	struct dpp_connection *conn, *found = NULL;
+	struct dpp_connection *conn = NULL;
 	struct wpabuf *msg;
 
 	/* Check if there is a successfully completed authentication for this
@@ -591,19 +681,15 @@
 	 */
 	dl_list_for_each(ctrl, &dpp->controllers,
 			 struct dpp_relay_controller, list) {
-		if (found)
+		conn = dpp_relay_find_conn(ctrl, src);
+		if (conn)
 			break;
-		dl_list_for_each(conn, &ctrl->conn,
-				 struct dpp_connection, list) {
-			if (os_memcmp(src, conn->mac_addr,
-				      ETH_ALEN) == 0) {
-				found = conn;
-				break;
-			}
-		}
 	}
 
-	if (!found)
+	if (!conn && dpp->tmp_controller)
+		conn = dpp_relay_find_conn(dpp->tmp_controller, src);
+
+	if (!conn)
 		return -1;
 
 	msg = wpabuf_alloc(4 + 1 + data_len);
@@ -622,6 +708,12 @@
 }
 
 
+bool dpp_relay_controller_available(struct dpp_global *dpp)
+{
+	return dpp && dl_list_len(&dpp->controllers) > 0;
+}
+
+
 static void dpp_controller_free(struct dpp_controller *ctrl)
 {
 	struct dpp_connection *conn, *tmp;
@@ -799,8 +891,9 @@
 
 	status = dpp_conf_result_rx(auth, hdr, buf, len);
 	if (status == DPP_STATUS_OK && auth->send_conn_status) {
-		wpa_msg(msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"wait_conn_status=1 conf_resp_status=%d",
+			auth->conf_resp_status);
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
 		auth->waiting_conn_status_result = 1;
 		eloop_cancel_timeout(
@@ -812,7 +905,8 @@
 		return 0;
 	}
 	if (status == DPP_STATUS_OK)
-		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_resp_status=%d", auth->conf_resp_status);
 	else
 		wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
 	return -1; /* to remove the completed connection */
@@ -861,12 +955,6 @@
 	struct dpp_authentication *auth;
 	struct dpp_global *dpp = conn->ctrl->global;
 
-	if (conn->auth) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Ignore Presence Announcement during ongoing Authentication");
-		return -1;
-	}
-
 	wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
 
 	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
@@ -885,6 +973,12 @@
 		return -1;
 	}
 
+	if (conn->auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Presence Announcement during ongoing Authentication");
+		return 0;
+	}
+
 	auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
 			     DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
 	if (!auth)
@@ -1017,6 +1111,7 @@
 					      NULL, NULL,
 					      ctrl->pkex_identifier,
 					      ctrl->pkex_code,
+					      os_strlen(ctrl->pkex_code),
 					      buf, len, true);
 	if (!conn->pkex) {
 		wpa_printf(MSG_DEBUG,
@@ -1307,6 +1402,8 @@
 		return -1;
 	}
 
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX);
+
 	pos = msg;
 	end = msg + len;
 
@@ -1911,7 +2008,10 @@
 
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
 		 const struct hostapd_ip_addr *addr, int port, const char *name,
-		 enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
+		 enum dpp_netrole netrole, const char *mud_url,
+		 const char *extra_conf_req_name,
+		 const char *extra_conf_req_value,
+		 void *msg_ctx, void *cb_ctx,
 		 int (*process_conf_obj)(void *ctx,
 					 struct dpp_authentication *auth),
 		 bool (*tcp_msg_sent)(void *ctx,
@@ -1941,6 +2041,12 @@
 	conn->process_conf_obj = process_conf_obj;
 	conn->tcp_msg_sent = tcp_msg_sent;
 	conn->name = os_strdup(name ? name : "Test");
+	if (mud_url)
+		conn->mud_url = os_strdup(mud_url);
+	if (extra_conf_req_name)
+		conn->extra_conf_req_name = os_strdup(extra_conf_req_name);
+	if (extra_conf_req_value)
+		conn->extra_conf_req_value = os_strdup(extra_conf_req_value);
 	conn->netrole = netrole;
 	conn->global = dpp;
 	conn->auth = auth;
@@ -1987,7 +2093,9 @@
 
 int dpp_tcp_auth(struct dpp_global *dpp, void *_conn,
 		 struct dpp_authentication *auth, const char *name,
-		 enum dpp_netrole netrole,
+		 enum dpp_netrole netrole, const char *mud_url,
+		 const char *extra_conf_req_name,
+		 const char *extra_conf_req_value,
 		 int (*process_conf_obj)(void *ctx,
 					 struct dpp_authentication *auth),
 		 bool (*tcp_msg_sent)(void *ctx,
@@ -2001,6 +2109,13 @@
 	conn->tcp_msg_sent = tcp_msg_sent;
 	os_free(conn->name);
 	conn->name = os_strdup(name ? name : "Test");
+	os_free(conn->mud_url);
+	conn->mud_url = mud_url ? os_strdup(mud_url) : NULL;
+	os_free(conn->extra_conf_req_name);
+	conn->extra_conf_req_name = extra_conf_req_name ?
+		os_strdup(extra_conf_req_name) : NULL;
+	conn->extra_conf_req_value = extra_conf_req_value ?
+		os_strdup(extra_conf_req_value) : NULL;
 	conn->netrole = netrole;
 	conn->auth = auth;
 
@@ -2203,6 +2318,35 @@
 }
 
 
+bool dpp_controller_is_own_pkex_req(struct dpp_global *dpp,
+				    const u8 *buf, size_t len)
+{
+	struct dpp_connection *conn;
+	const u8 *attr_key = NULL;
+	u16 attr_key_len = 0;
+
+	dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+		if (!conn->pkex || !conn->pkex->enc_key)
+			continue;
+
+		if (!attr_key) {
+			attr_key = dpp_get_attr(buf, len,
+						DPP_ATTR_ENCRYPTED_KEY,
+						&attr_key_len);
+			if (!attr_key)
+				return false;
+		}
+
+		if (attr_key_len == wpabuf_len(conn->pkex->enc_key) &&
+		    os_memcmp(attr_key, wpabuf_head(conn->pkex->enc_key),
+			      attr_key_len) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
 void dpp_tcp_init_flush(struct dpp_global *dpp)
 {
 	struct dpp_connection *conn, *tmp;
@@ -2216,6 +2360,10 @@
 static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
 {
 	struct dpp_connection *conn, *tmp;
+	char txt[100];
+
+	wpa_printf(MSG_DEBUG, "DPP: Remove Relay connection to Controller %s",
+		   hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
 
 	dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
 			      list)
@@ -2236,6 +2384,204 @@
 		dl_list_del(&ctrl->list);
 		dpp_relay_controller_free(ctrl);
 	}
+
+	if (dpp->tmp_controller) {
+		dpp_relay_controller_free(dpp->tmp_controller);
+		dpp->tmp_controller = NULL;
+	}
+}
+
+
+void dpp_relay_remove_controller(struct dpp_global *dpp,
+				 const struct hostapd_ip_addr *addr)
+{
+	struct dpp_relay_controller *ctrl;
+
+	if (!dpp)
+		return;
+
+	dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+			 list) {
+		if (hostapd_ip_equal(&ctrl->ipaddr, addr)) {
+			dl_list_del(&ctrl->list);
+			dpp_relay_controller_free(ctrl);
+			return;
+		}
+	}
+
+	if (dpp->tmp_controller &&
+	    hostapd_ip_equal(&dpp->tmp_controller->ipaddr, addr)) {
+		dpp_relay_controller_free(dpp->tmp_controller);
+		dpp->tmp_controller = NULL;
+	}
+}
+
+
+static void dpp_relay_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct dpp_global *dpp = eloop_ctx;
+	struct sockaddr_in addr;
+	socklen_t addr_len = sizeof(addr);
+	int fd;
+	struct dpp_relay_controller *ctrl;
+	struct dpp_connection *conn = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: New TCP connection (Relay)");
+
+	fd = accept(dpp->relay_sock, (struct sockaddr *) &addr, &addr_len);
+	if (fd < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to accept new connection: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+	ctrl = dpp_relay_controller_get_addr(dpp, &addr);
+	if (!ctrl && dpp->tmp_controller &&
+	    dl_list_len(&dpp->tmp_controller->conn)) {
+		char txt[100];
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Remove a temporaty Controller entry for %s",
+			   hostapd_ip_txt(&dpp->tmp_controller->ipaddr,
+					  txt, sizeof(txt)));
+		dpp_relay_controller_free(dpp->tmp_controller);
+		dpp->tmp_controller = NULL;
+	}
+	if (!ctrl && !dpp->tmp_controller) {
+		wpa_printf(MSG_DEBUG, "DPP: Add a temporary Controller entry");
+		ctrl = os_zalloc(sizeof(*ctrl));
+		if (!ctrl)
+			goto fail;
+		dl_list_init(&ctrl->conn);
+		ctrl->global = dpp;
+		ctrl->ipaddr.af = AF_INET;
+		ctrl->ipaddr.u.v4.s_addr = addr.sin_addr.s_addr;
+		ctrl->msg_ctx = dpp->relay_msg_ctx;
+		ctrl->cb_ctx = dpp->relay_cb_ctx;
+		ctrl->tx = dpp->relay_tx;
+		ctrl->gas_resp_tx = dpp->relay_gas_resp_tx;
+		dpp->tmp_controller = ctrl;
+	}
+	if (!ctrl) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No Controller found for that address");
+		goto fail;
+	}
+
+	if (dl_list_len(&ctrl->conn) >= 15) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+		goto fail;
+	}
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		goto fail;
+
+	conn->global = ctrl->global;
+	conn->relay = ctrl;
+	conn->msg_ctx = ctrl->msg_ctx;
+	conn->cb_ctx = ctrl->global->cb_ctx;
+	os_memset(conn->mac_addr, 0xff, ETH_ALEN);
+	conn->sock = fd;
+
+	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+				dpp_controller_rx, conn, NULL) < 0)
+		goto fail;
+	conn->read_eloop = 1;
+
+	/* TODO: eloop timeout to expire connections that do not complete in
+	 * reasonable time */
+	dl_list_add(&ctrl->conn, &conn->list);
+	return;
+
+fail:
+	close(fd);
+	os_free(conn);
+}
+
+
+int dpp_relay_listen(struct dpp_global *dpp, int port,
+		     struct dpp_relay_config *config)
+{
+	int s;
+	int on = 1;
+	struct sockaddr_in sin;
+
+	if (dpp->relay_sock >= 0) {
+		wpa_printf(MSG_INFO, "DPP: %s(%d) - relay port already opened",
+			   __func__, port);
+		return -1;
+	}
+
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: socket(SOCK_STREAM) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+			   strerror(errno));
+		/* try to continue anyway */
+	}
+
+	if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) {
+		wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+
+	/* TODO: IPv6 */
+	os_memset(&sin, 0, sizeof(sin));
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	sin.sin_port = htons(port);
+	if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Failed to bind Relay TCP port: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	if (listen(s, 10 /* max backlog */) < 0 ||
+	    fcntl(s, F_SETFL, O_NONBLOCK) < 0 ||
+	    eloop_register_sock(s, EVENT_TYPE_READ, dpp_relay_tcp_cb, dpp,
+				NULL)) {
+		close(s);
+		return -1;
+	}
+
+	dpp->relay_sock = s;
+	dpp->relay_msg_ctx = config->msg_ctx;
+	dpp->relay_cb_ctx = config->cb_ctx;
+	dpp->relay_tx = config->tx;
+	dpp->relay_gas_resp_tx = config->gas_resp_tx;
+	wpa_printf(MSG_DEBUG, "DPP: Relay started on TCP port %d", port);
+	return 0;
+}
+
+
+void dpp_relay_stop_listen(struct dpp_global *dpp)
+{
+	if (!dpp || dpp->relay_sock < 0)
+		return;
+	eloop_unregister_sock(dpp->relay_sock, EVENT_TYPE_READ);
+	close(dpp->relay_sock);
+	dpp->relay_sock = -1;
 }
 
 
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 732124f..584c6d2 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -384,13 +384,16 @@
 			    u8 edmg_channel, int ht_enabled,
 			    int vht_enabled, int he_enabled,
 			    bool eht_enabled, int sec_channel_offset,
-			    int oper_chwidth, int center_segment0,
+			    enum oper_chan_width oper_chwidth,
+			    int center_segment0,
 			    int center_segment1, u32 vht_caps,
 			    struct he_capabilities *he_cap,
 			    struct eht_capabilities *eht_cap)
 {
 	if (!he_cap || !he_cap->he_supported)
 		he_enabled = 0;
+	if (!eht_cap || !eht_cap->eht_supported)
+		eht_enabled = 0;
 	os_memset(data, 0, sizeof(*data));
 	data->mode = mode;
 	data->freq = freq;
@@ -402,11 +405,13 @@
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
-	if (oper_chwidth == CHANWIDTH_80MHZ)
+	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
 		data->bandwidth = 80;
-	else if (oper_chwidth == CHANWIDTH_160MHZ ||
-		 oper_chwidth == CHANWIDTH_80P80MHZ)
+	else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+		 oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
 		data->bandwidth = 160;
+	else if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+		data->bandwidth = 320;
 	else if (sec_channel_offset)
 		data->bandwidth = 40;
 	else
@@ -482,9 +487,8 @@
 		return 0;
 	}
 
-#if 0 /* FIX: Figure out how to handle CHANWIDTH_320MHZ */
 	if (data->eht_enabled) switch (oper_chwidth) {
-	case CHANWIDTH_320MHZ:
+	case CONF_OPER_CHWIDTH_320MHZ:
 		if (!(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
 		      EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
 			wpa_printf(MSG_ERROR,
@@ -492,16 +496,18 @@
 			return -1;
 		}
 		break;
+	default:
+		break;
 	}
-#endif
 
 	if (data->he_enabled || data->eht_enabled) switch (oper_chwidth) {
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (sec_channel_offset == 0)
 			break;
 
 		if (mode == HOSTAPD_MODE_IEEE80211G) {
-			if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+			if (he_cap &&
+			    !(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
 			      HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) {
 				wpa_printf(MSG_ERROR,
 					   "40 MHz channel width is not supported in 2.4 GHz");
@@ -510,9 +516,10 @@
 			break;
 		}
 		/* fall through */
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		if (mode == HOSTAPD_MODE_IEEE80211A) {
-			if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+			if (he_cap &&
+			    !(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
 			      HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
 				wpa_printf(MSG_ERROR,
 					   "40/80 MHz channel width is not supported in 5/6 GHz");
@@ -520,35 +527,39 @@
 			}
 		}
 		break;
-	case CHANWIDTH_80P80MHZ:
-		if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		if (he_cap &&
+		    !(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
 		      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) {
 			wpa_printf(MSG_ERROR,
 				   "80+80 MHz channel width is not supported in 5/6 GHz");
 			return -1;
 		}
 		break;
-	case CHANWIDTH_160MHZ:
-		if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	case CONF_OPER_CHWIDTH_160MHZ:
+		if (he_cap &&
+		    !(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
 		      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) {
 			wpa_printf(MSG_ERROR,
 				   "160 MHz channel width is not supported in 5 / 6GHz");
 			return -1;
 		}
 		break;
-	} else if (data->vht_enabled) switch (oper_chwidth) {
-	case CHANWIDTH_USE_HT:
+	default:
 		break;
-	case CHANWIDTH_80P80MHZ:
+	} else if (data->vht_enabled) switch (oper_chwidth) {
+	case CONF_OPER_CHWIDTH_USE_HT:
+		break;
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
 			wpa_printf(MSG_ERROR,
 				   "80+80 channel width is not supported!");
 			return -1;
 		}
 		/* fall through */
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
 				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
 			wpa_printf(MSG_ERROR,
@@ -556,11 +567,13 @@
 			return -1;
 		}
 		break;
+	default:
+		break;
 	}
 
 	if (data->eht_enabled || data->he_enabled ||
 	    data->vht_enabled) switch (oper_chwidth) {
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		if (center_segment1 ||
 		    (center_segment0 != 0 &&
 		     5000 + center_segment0 * 5 != data->center_freq1 &&
@@ -571,7 +584,7 @@
 			return -1;
 		}
 		break;
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		if (center_segment1 == center_segment0 + 4 ||
 		    center_segment1 == center_segment0 - 4) {
 			wpa_printf(MSG_ERROR,
@@ -580,19 +593,21 @@
 		}
 		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		data->bandwidth = 80;
 		if (!sec_channel_offset) {
 			wpa_printf(MSG_ERROR,
 				   "80/80+80 MHz: no second channel offset");
 			return -1;
 		}
-		if (oper_chwidth == CHANWIDTH_80MHZ && center_segment1) {
+		if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ &&
+		    center_segment1) {
 			wpa_printf(MSG_ERROR,
 				   "80 MHz: center segment 1 configured");
 			return -1;
 		}
-		if (oper_chwidth == CHANWIDTH_80P80MHZ && !center_segment1) {
+		if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ &&
+		    !center_segment1) {
 			wpa_printf(MSG_ERROR,
 				   "80+80 MHz: center segment 1 not configured");
 			return -1;
@@ -631,7 +646,7 @@
 			}
 		}
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		data->bandwidth = 160;
 		if (center_segment1) {
 			wpa_printf(MSG_ERROR,
@@ -662,6 +677,43 @@
 			return -1;
 		}
 		break;
+	case CONF_OPER_CHWIDTH_320MHZ:
+		data->bandwidth = 320;
+		if (!data->eht_enabled || !is_6ghz_freq(freq)) {
+			wpa_printf(MSG_ERROR,
+				   "320 MHz: EHT not enabled or not a 6 GHz channel");
+			return -1;
+		}
+		if (center_segment1) {
+			wpa_printf(MSG_ERROR,
+				   "320 MHz: center segment 1 should not be set");
+			return -1;
+		}
+		if (center_segment0 == channel + 30 ||
+		    center_segment0 == channel + 26 ||
+		    center_segment0 == channel + 22 ||
+		    center_segment0 == channel + 18 ||
+		    center_segment0 == channel + 14 ||
+		    center_segment0 == channel + 10 ||
+		    center_segment0 == channel + 6 ||
+		    center_segment0 == channel + 2 ||
+		    center_segment0 == channel - 2 ||
+		    center_segment0 == channel - 6 ||
+		    center_segment0 == channel - 10 ||
+		    center_segment0 == channel - 14 ||
+		    center_segment0 == channel - 18 ||
+		    center_segment0 == channel - 22 ||
+		    center_segment0 == channel - 26 ||
+		    center_segment0 == channel - 30)
+			data->center_freq1 = 5000 + center_segment0 * 5;
+		else {
+			wpa_printf(MSG_ERROR,
+				   "320 MHz: wrong center segment 0");
+			return -1;
+		}
+		break;
+	default:
+		break;
 	}
 
 	return 0;
@@ -772,6 +824,7 @@
 	case 2:
 	case 4:
 	case 8:
+	case 16:
 		return num_chans * 20;
 	default:
 		return 20;
@@ -805,6 +858,9 @@
 	case 160:
 		bw_mask = HOSTAPD_CHAN_WIDTH_160;
 		break;
+	case 320:
+		bw_mask = HOSTAPD_CHAN_WIDTH_320;
+		break;
 	default:
 		bw_mask = 0;
 		break;
@@ -820,3 +876,70 @@
 	return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
 }
+
+
+/* IEEE P802.11be/D3.0, Table 36-30 - Definition of the Punctured Channel
+ * Information field in the U-SIG for an EHT MU PPDU using non-OFDMA
+ * transmissions */
+static const u16 punct_bitmap_80[] = { 0xF, 0xE, 0xD, 0xB, 0x7 };
+static const u16 punct_bitmap_160[] = {
+	0xFF, 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF,
+	0x7F, 0xFC, 0xF3, 0xCF, 0x3F
+};
+static const u16 punct_bitmap_320[] = {
+	0xFFFF, 0xFFFC, 0xFFF3, 0xFFCF, 0xFF3F, 0xFCFF, 0xF3FF, 0xCFFF,
+	0x3FFF, 0xFFF0, 0xFF0F, 0xF0FF, 0x0FFF, 0xFFC0, 0xFF30, 0xFCF0,
+	0xF3F0, 0xCFF0, 0x3FF0, 0x0FFC, 0x0FF3, 0x0FCF, 0x0F3F, 0x0CFF,
+	0x03FF
+};
+
+
+bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap)
+{
+	u8 i, count;
+	u16 bitmap;
+	const u16 *valid_bitmaps;
+
+	if (!punct_bitmap) /* All channels active */
+		return true;
+
+	bitmap = ~punct_bitmap;
+
+	switch (bw) {
+	case 80:
+		bitmap &= 0xF;
+		valid_bitmaps = punct_bitmap_80;
+		count = ARRAY_SIZE(punct_bitmap_80);
+		break;
+
+	case 160:
+		bitmap &= 0xFF;
+		valid_bitmaps = punct_bitmap_160;
+		count = ARRAY_SIZE(punct_bitmap_160);
+		break;
+
+	case 320:
+		bitmap &= 0xFFFF;
+		valid_bitmaps = punct_bitmap_320;
+		count = ARRAY_SIZE(punct_bitmap_320);
+		break;
+
+	default:
+		return false;
+	}
+
+	if (!bitmap) /* No channel active */
+		return false;
+
+	if (!(bitmap & BIT(pri_ch_bit_pos))) {
+		wpa_printf(MSG_DEBUG, "Primary channel cannot be punctured");
+		return false;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (valid_bitmaps[i] == bitmap)
+			return true;
+	}
+
+	return false;
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index d87a2ca..82e0282 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -41,7 +41,8 @@
 			    int ht_enabled,
 			    int vht_enabled, int he_enabled,
 			    bool eht_enabled, int sec_channel_offset,
-			    int oper_chwidth, int center_segment0,
+			    enum oper_chan_width oper_chwidth,
+			    int center_segment0,
 			    int center_segment1, u32 vht_caps,
 			    struct he_capabilities *he_caps,
 			    struct eht_capabilities *eht_cap);
@@ -53,5 +54,6 @@
 int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
 		    int ht40_plus, int pri);
 int chan_pri_allowed(const struct hostapd_channel_data *chan);
+bool is_punct_bitmap_valid(u16 bw, u16 pri_ch_bit_pos, u16 punct_bitmap);
 
 #endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 44335de..cd1b198 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -199,11 +199,76 @@
 }
 
 
+static int ieee802_11_parse_mle(const u8 *pos, size_t elen, size_t **total_len,
+				struct ieee802_11_elems *elems,
+				int show_errors)
+{
+	u8 mle_type = pos[0] & MULTI_LINK_CONTROL_TYPE_MASK;
+
+	switch (mle_type) {
+	case MULTI_LINK_CONTROL_TYPE_BASIC:
+		elems->basic_mle = pos;
+		elems->basic_mle_len = elen;
+		*total_len = &elems->basic_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+		elems->probe_req_mle = pos;
+		elems->probe_req_mle_len = elen;
+		*total_len = &elems->probe_req_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_RECONF:
+		elems->reconf_mle = pos;
+		elems->reconf_mle_len = elen;
+		*total_len = &elems->reconf_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_TDLS:
+		elems->tdls_mle = pos;
+		elems->tdls_mle_len = elen;
+		*total_len = &elems->tdls_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+		elems->prior_access_mle = pos;
+		elems->prior_access_mle_len = elen;
+		*total_len = &elems->prior_access_mle_len;
+		break;
+	default:
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP,
+				   "Unknown Multi-Link element type %u",
+				   mle_type);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static size_t ieee802_11_fragments_length(struct ieee802_11_elems *elems,
+					  const u8 *start, size_t len)
+{
+	const struct element *elem;
+	size_t frags_len = 0;
+
+	for_each_element(elem, start, len) {
+		if (elem->id != WLAN_EID_FRAGMENT)
+			break;
+
+		frags_len += elem->datalen + 2;
+		elems->num_frag_elems++;
+	}
+
+	return frags_len;
+}
+
+
 static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
 				      struct ieee802_11_elems *elems,
+				      const u8 *start, size_t len,
 				      int show_errors)
 {
 	u8 ext_id;
+	size_t *total_len = NULL;
 
 	if (elen < 1) {
 		if (show_errors) {
@@ -216,8 +281,6 @@
 	ext_id = *pos++;
 	elen--;
 
-	elems->frag_ies.last_eid_ext = 0;
-
 	switch (ext_id) {
 	case WLAN_EID_EXT_ASSOC_DELAY_INFO:
 		if (elen != 1)
@@ -244,6 +307,7 @@
 			break;
 		elems->fils_hlp = pos;
 		elems->fils_hlp_len = elen;
+		total_len = &elems->fils_hlp_len;
 		break;
 	case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
 		if (elen < 1)
@@ -260,6 +324,7 @@
 	case WLAN_EID_EXT_WRAPPED_DATA:
 		elems->wrapped_data = pos;
 		elems->wrapped_data_len = elen;
+		total_len = &elems->wrapped_data_len;
 		break;
 	case WLAN_EID_EXT_FILS_PUBLIC_KEY:
 		if (elen < 1)
@@ -312,13 +377,28 @@
 		elems->pasn_params_len = elen;
 		break;
 	case WLAN_EID_EXT_EHT_CAPABILITIES:
+		if (elen < EHT_CAPABILITIES_IE_MIN_LEN)
+			break;
 		elems->eht_capabilities = pos;
 		elems->eht_capabilities_len = elen;
 		break;
 	case WLAN_EID_EXT_EHT_OPERATION:
+		if (elen < EHT_OPERATION_IE_MIN_LEN)
+			break;
 		elems->eht_operation = pos;
 		elems->eht_operation_len = elen;
 		break;
+	case WLAN_EID_EXT_MULTI_LINK:
+		if (elen < 2)
+			break;
+		if (ieee802_11_parse_mle(pos, elen, &total_len, elems,
+					 show_errors))
+			return -1;
+		break;
+	case WLAN_EID_EXT_KNOWN_BSSID:
+		elems->mbssid_known_bss = pos;
+		elems->mbssid_known_bss_len = elen;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
@@ -328,56 +408,21 @@
 		return -1;
 	}
 
-	if (elen == 254)
-		elems->frag_ies.last_eid_ext = ext_id;
+	if (elen == 254 && total_len)
+		*total_len += ieee802_11_fragments_length(
+			elems, pos + elen, (start + len) - (pos + elen));
 
 	return 0;
 }
 
 
-static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies,
-				      const u8 *pos, u8 elen)
-{
-	if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) {
-		wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip");
-		return;
-	}
-
-	/*
-	 * Note: while EID == 0 is a valid ID (SSID IE), it should not be
-	 * fragmented.
-	 */
-	if (!frag_ies->last_eid) {
-		wpa_printf(MSG_MSGDUMP,
-			   "Fragment without a valid last element - skip");
-		return;
-	}
-
-	frag_ies->frags[frag_ies->n_frags].ie = pos;
-	frag_ies->frags[frag_ies->n_frags].ie_len = elen;
-	frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid;
-	frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext;
-	frag_ies->n_frags++;
-}
-
-
-/**
- * ieee802_11_parse_elems - Parse information elements in management frames
- * @start: Pointer to the start of IEs
- * @len: Length of IE buffer in octets
- * @elems: Data structure for parsed elements
- * @show_errors: Whether to show parsing errors in debug log
- * Returns: Parsing result
- */
-ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
-				struct ieee802_11_elems *elems,
-				int show_errors)
+static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len,
+					 struct ieee802_11_elems *elems,
+					 int show_errors)
 {
 	const struct element *elem;
 	int unknown = 0;
 
-	os_memset(elems, 0, sizeof(*elems));
-
 	if (!start)
 		return ParseOK;
 
@@ -385,6 +430,12 @@
 		u8 id = elem->id, elen = elem->datalen;
 		const u8 *pos = elem->data;
 
+		if (id == WLAN_EID_FRAGMENT && elems->num_frag_elems > 0) {
+			elems->num_frag_elems--;
+			continue;
+		}
+		elems->num_frag_elems = 0;
+
 		switch (id) {
 		case WLAN_EID_SSID:
 			if (elen > SSID_MAX_LEN) {
@@ -588,11 +639,13 @@
 			elems->s1g_capab = pos;
 			break;
 		case WLAN_EID_FRAGMENT:
-			ieee802_11_parse_fragment(&elems->frag_ies, pos, elen);
+			wpa_printf(MSG_MSGDUMP,
+				   "Fragment without a valid last element - skip");
+
 			break;
 		case WLAN_EID_EXTENSION:
-			if (ieee802_11_parse_extension(pos, elen, elems,
-						       show_errors))
+			if (ieee802_11_parse_extension(pos, elen, elems, start,
+						       len, show_errors))
 				unknown++;
 			break;
 		default:
@@ -604,12 +657,6 @@
 				   id, elen);
 			break;
 		}
-
-		if (id != WLAN_EID_FRAGMENT && elen == 255)
-			elems->frag_ies.last_eid = id;
-
-		if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext)
-			elems->frag_ies.last_eid = 0;
 	}
 
 	if (!for_each_element_completed(elem, start, len)) {
@@ -627,6 +674,431 @@
 }
 
 
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+				struct ieee802_11_elems *elems,
+				int show_errors)
+{
+	os_memset(elems, 0, sizeof(*elems));
+
+	return __ieee802_11_parse_elems(start, len, elems, show_errors);
+}
+
+
+/**
+ * ieee802_11_elems_clear_ids - Clear the data for the given element IDs
+ * @ids: Array of element IDs for which data should be cleared.
+ * @num: The number of entries in the array
+ */
+void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems,
+				const u8 *ids, size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		switch (ids[i]) {
+		case WLAN_EID_SSID:
+			elems->ssid = NULL;
+			elems->ssid_len = 0;
+			break;
+		case WLAN_EID_SUPP_RATES:
+			elems->supp_rates = NULL;
+			elems->supp_rates_len = 0;
+			break;
+		case WLAN_EID_DS_PARAMS:
+			elems->ds_params = NULL;
+			break;
+		case WLAN_EID_CHALLENGE:
+			elems->challenge = NULL;
+			elems->challenge_len = 0;
+			break;
+		case WLAN_EID_ERP_INFO:
+			elems->erp_info = NULL;
+			break;
+		case WLAN_EID_EXT_SUPP_RATES:
+			elems->ext_supp_rates = NULL;
+			elems->ext_supp_rates_len = 0;
+			break;
+		case WLAN_EID_RSN:
+			elems->rsn_ie = NULL;
+			elems->rsn_ie_len = 0;
+			break;
+		case WLAN_EID_RSNX:
+			elems->rsnxe = NULL;
+			elems->rsnxe_len = 0;
+			break;
+		case WLAN_EID_PWR_CAPABILITY:
+			elems->power_capab = NULL;
+			elems->power_capab_len = 0;
+			break;
+		case WLAN_EID_SUPPORTED_CHANNELS:
+			elems->supp_channels = NULL;
+			elems->supp_channels_len = 0;
+			break;
+		case WLAN_EID_MOBILITY_DOMAIN:
+			elems->mdie = NULL;
+			elems->mdie_len = 0;
+			break;
+		case WLAN_EID_FAST_BSS_TRANSITION:
+			elems->ftie = NULL;
+			elems->ftie_len = 0;
+			break;
+		case WLAN_EID_TIMEOUT_INTERVAL:
+			elems->timeout_int = NULL;
+			break;
+		case WLAN_EID_HT_CAP:
+			elems->ht_capabilities = NULL;
+			break;
+		case WLAN_EID_HT_OPERATION:
+			elems->ht_operation = NULL;
+			break;
+		case WLAN_EID_MESH_CONFIG:
+			elems->mesh_config = NULL;
+			elems->mesh_config_len = 0;
+			break;
+		case WLAN_EID_MESH_ID:
+			elems->mesh_id = NULL;
+			elems->mesh_id_len = 0;
+			break;
+		case WLAN_EID_PEER_MGMT:
+			elems->peer_mgmt = NULL;
+			elems->peer_mgmt_len = 0;
+			break;
+		case WLAN_EID_VHT_CAP:
+			elems->vht_capabilities = NULL;
+			break;
+		case WLAN_EID_VHT_OPERATION:
+			elems->vht_operation = NULL;
+			break;
+		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
+			elems->vht_opmode_notif = NULL;
+			break;
+		case WLAN_EID_LINK_ID:
+			elems->link_id = NULL;
+			break;
+		case WLAN_EID_INTERWORKING:
+			elems->interworking = NULL;
+			elems->interworking_len = 0;
+			break;
+		case WLAN_EID_QOS_MAP_SET:
+			elems->qos_map_set = NULL;
+			elems->qos_map_set_len = 0;
+			break;
+		case WLAN_EID_EXT_CAPAB:
+			elems->ext_capab = NULL;
+			elems->ext_capab_len = 0;
+			break;
+		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+			elems->bss_max_idle_period = NULL;
+			break;
+		case WLAN_EID_SSID_LIST:
+			elems->ssid_list = NULL;
+			elems->ssid_list_len = 0;
+			break;
+		case WLAN_EID_AMPE:
+			elems->ampe = NULL;
+			elems->ampe_len = 0;
+			break;
+		case WLAN_EID_MIC:
+			elems->mic = NULL;
+			elems->mic_len = 0;
+			break;
+		case WLAN_EID_MULTI_BAND:
+			os_memset(&elems->mb_ies, 0, sizeof(elems->mb_ies));
+			elems->mb_ies.nof_ies = 0;
+			break;
+		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
+			elems->supp_op_classes = NULL;
+			elems->supp_op_classes_len = 0;
+			break;
+		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
+			elems->rrm_enabled = NULL;
+			elems->rrm_enabled_len = 0;
+			break;
+		case WLAN_EID_CAG_NUMBER:
+			elems->cag_number = NULL;
+			elems->cag_number_len = 0;
+			break;
+		case WLAN_EID_AP_CSN:
+			elems->ap_csn = NULL;
+			break;
+		case WLAN_EID_FILS_INDICATION:
+			elems->fils_indic = NULL;
+			elems->fils_indic_len = 0;
+			break;
+		case WLAN_EID_DILS:
+			elems->dils = NULL;
+			elems->dils_len = 0;
+			break;
+		case WLAN_EID_S1G_CAPABILITIES:
+			elems->s1g_capab = NULL;
+			break;
+		}
+	}
+}
+
+
+/**
+ * ieee802_11_elems_clear_ext_ids - Clear the data for the given element
+ * extension IDs
+ * @ids: Array of element extension IDs for which data should be cleared.
+ * @num: The number of entries in the array
+ */
+void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
+				    const u8 *ids, size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		switch (ids[i]) {
+		case WLAN_EID_EXT_ASSOC_DELAY_INFO:
+			elems->assoc_delay_info = NULL;
+			break;
+		case WLAN_EID_EXT_FILS_REQ_PARAMS:
+			elems->fils_req_params = NULL;
+			elems->fils_req_params_len = 0;
+			break;
+		case WLAN_EID_EXT_FILS_KEY_CONFIRM:
+			elems->fils_key_confirm = NULL;
+			elems->fils_key_confirm_len = 0;
+			break;
+		case WLAN_EID_EXT_FILS_SESSION:
+			elems->fils_session = NULL;
+			break;
+		case WLAN_EID_EXT_FILS_HLP_CONTAINER:
+			elems->fils_hlp = NULL;
+			elems->fils_hlp_len = 0;
+			break;
+		case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
+			elems->fils_ip_addr_assign = NULL;
+			elems->fils_ip_addr_assign_len = 0;
+			break;
+		case WLAN_EID_EXT_KEY_DELIVERY:
+			elems->key_delivery = NULL;
+			elems->key_delivery_len = 0;
+			break;
+		case WLAN_EID_EXT_WRAPPED_DATA:
+			elems->wrapped_data = NULL;
+			elems->wrapped_data_len = 0;
+			break;
+		case WLAN_EID_EXT_FILS_PUBLIC_KEY:
+			elems->fils_pk = NULL;
+			elems->fils_pk_len = 0;
+			break;
+		case WLAN_EID_EXT_FILS_NONCE:
+			elems->fils_nonce = NULL;
+			break;
+		case WLAN_EID_EXT_OWE_DH_PARAM:
+			elems->owe_dh = NULL;
+			elems->owe_dh_len = 0;
+			break;
+		case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
+			elems->password_id = NULL;
+			elems->password_id_len = 0;
+			break;
+		case WLAN_EID_EXT_HE_CAPABILITIES:
+			elems->he_capabilities = NULL;
+			elems->he_capabilities_len = 0;
+			break;
+		case WLAN_EID_EXT_HE_OPERATION:
+			elems->he_operation = NULL;
+			elems->he_operation_len = 0;
+			break;
+		case WLAN_EID_EXT_OCV_OCI:
+			elems->oci = NULL;
+			elems->oci_len = 0;
+			break;
+		case WLAN_EID_EXT_SHORT_SSID_LIST:
+			elems->short_ssid_list = NULL;
+			elems->short_ssid_list_len = 0;
+			break;
+		case WLAN_EID_EXT_HE_6GHZ_BAND_CAP:
+			elems->he_6ghz_band_cap = NULL;
+			break;
+		case WLAN_EID_EXT_PASN_PARAMS:
+			elems->pasn_params = NULL;
+			elems->pasn_params_len = 0;
+			break;
+		case WLAN_EID_EXT_MULTI_LINK:
+			elems->basic_mle = NULL;
+			elems->probe_req_mle = NULL;
+			elems->reconf_mle = NULL;
+			elems->tdls_mle = NULL;
+			elems->prior_access_mle = NULL;
+
+			elems->basic_mle_len = 0;
+			elems->probe_req_mle_len = 0;
+			elems->reconf_mle_len = 0;
+			elems->tdls_mle_len = 0;
+			elems->prior_access_mle_len = 0;
+			break;
+		case WLAN_EID_EXT_EHT_CAPABILITIES:
+			elems->eht_capabilities = NULL;
+			elems->eht_capabilities_len = 0;
+			break;
+		case WLAN_EID_EXT_EHT_OPERATION:
+			elems->eht_operation = NULL;
+			elems->eht_operation_len = 0;
+			break;
+		}
+	}
+}
+
+
+ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
+					 struct ieee802_11_elems *elems,
+					 struct wpabuf *mlbuf,
+					 u8 link_id, bool show_errors)
+{
+	const struct ieee80211_eht_ml *ml;
+	const u8 *pos;
+	ParseRes res = ParseFailed;
+
+	pos = wpabuf_head(mlbuf);
+	len = wpabuf_len(mlbuf);
+
+	/* Must have control and common info length */
+	if (len < sizeof(*ml) + 1 || len < sizeof(*ml) + pos[sizeof(*ml)])
+		goto out;
+
+	ml = (const struct ieee80211_eht_ml *) pos;
+
+	/* As we are interested with the Per-STA profile, ignore other types */
+	if ((le_to_host16(ml->ml_control) & MULTI_LINK_CONTROL_TYPE_MASK) !=
+	     MULTI_LINK_CONTROL_TYPE_BASIC)
+		goto out;
+
+	/* Skip the common info */
+	len -= sizeof(*ml) + pos[sizeof(*ml)];
+	pos += sizeof(*ml) + pos[sizeof(*ml)];
+
+	while (len > 2) {
+		size_t sub_elem_len = *(pos + 1);
+		size_t sta_info_len;
+		u16 link_info_control;
+		const u8 *non_inherit;
+
+		wpa_printf(MSG_DEBUG,
+			   "MLD: sub element: len=%zu, sub_elem_len=%zu",
+			   len, sub_elem_len);
+
+		if (2 + sub_elem_len > len) {
+			if (show_errors)
+				wpa_printf(MSG_DEBUG,
+					   "MLD: error: len=%zu, sub_elem_len=%zu",
+					   len, sub_elem_len);
+			goto out;
+		}
+
+		if (*pos != 0) {
+			pos += 2 + sub_elem_len;
+			len -= 2 + sub_elem_len;
+			continue;
+		}
+
+		if (sub_elem_len < 3) {
+			if (show_errors)
+				wpa_printf(MSG_DEBUG,
+					   "MLD: error: sub_elem_len=%zu < 5",
+					   sub_elem_len);
+			goto out;
+		}
+
+		link_info_control = WPA_GET_LE16(pos + 2);
+		if ((link_info_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK) !=
+		    link_id) {
+			pos += 2 + sub_elem_len;
+			len -= 2 + sub_elem_len;
+			continue;
+		}
+
+		sta_info_len = *(pos + 4);
+		if (sub_elem_len < sta_info_len + 3 || sta_info_len < 1) {
+			if (show_errors)
+				wpa_printf(MSG_DEBUG,
+					   "MLD: error: sub_elem_len=%zu, sta_info_len=%zu",
+					   sub_elem_len, sta_info_len);
+			goto out;
+		}
+
+		pos += sta_info_len + 4;
+		sub_elem_len -= sta_info_len + 2;
+
+		if (sub_elem_len < 2) {
+			if (show_errors)
+				wpa_printf(MSG_DEBUG,
+					   "MLD: missing capability info");
+			goto out;
+		}
+
+		pos += 2;
+		sub_elem_len -= 2;
+
+		/* Handle non-inheritance */
+		non_inherit = get_ie_ext(pos, sub_elem_len,
+					 WLAN_EID_EXT_NON_INHERITANCE);
+		if (non_inherit && non_inherit[1] > 1) {
+			u8 non_inherit_len = non_inherit[1] - 1;
+
+			/*
+			 * Do not include the Non-Inheritance element when
+			 * parsing below. It should be the last element in the
+			 * subelement.
+			 */
+			if (3U + non_inherit_len > sub_elem_len)
+				goto out;
+			sub_elem_len -= 3 + non_inherit_len;
+
+			/* Skip the ID, length and extension ID */
+			non_inherit += 3;
+
+			if (non_inherit_len < 1UL + non_inherit[0]) {
+				if (show_errors)
+					wpa_printf(MSG_DEBUG,
+						   "MLD: Invalid inheritance");
+				goto out;
+			}
+
+			ieee802_11_elems_clear_ids(elems, &non_inherit[1],
+						   non_inherit[0]);
+
+			non_inherit_len -= 1 + non_inherit[0];
+			non_inherit += 1 + non_inherit[0];
+
+			if (non_inherit_len < 1UL + non_inherit[0]) {
+				if (show_errors)
+					wpa_printf(MSG_DEBUG,
+						   "MLD: Invalid inheritance");
+				goto out;
+			}
+
+			ieee802_11_elems_clear_ext_ids(elems, &non_inherit[1],
+						       non_inherit[0]);
+		}
+
+		wpa_printf(MSG_DEBUG, "MLD: link: sub_elem_len=%zu",
+			   sub_elem_len);
+
+		if (sub_elem_len)
+			res = __ieee802_11_parse_elems(pos, sub_elem_len,
+						       elems, show_errors);
+		else
+			res = ParseOK;
+		break;
+	}
+
+out:
+	return res;
+}
+
+
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
 {
 	const struct element *elem;
@@ -886,7 +1358,7 @@
 {
 	u8 op_class;
 
-	return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
+	return ieee80211_freq_to_channel_ext(freq, 0, CONF_OPER_CHWIDTH_USE_HT,
 					     &op_class, channel);
 }
 
@@ -896,15 +1368,15 @@
  * for HT40, VHT, and HE. DFS channels are not covered.
  * @freq: Frequency (MHz) to convert
  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
- * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*)
+ * @chanwidth: VHT/EDMG/etc. channel width
  * @op_class: Buffer for returning operating class
  * @channel: Buffer for returning channel number
  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
  */
-enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
-						   int sec_channel,
-						   int chanwidth,
-						   u8 *op_class, u8 *channel)
+enum hostapd_hw_mode
+ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
+			      enum oper_chan_width chanwidth,
+			      u8 *op_class, u8 *channel)
 {
 	u8 vht_opclass;
 
@@ -952,13 +1424,13 @@
 	}
 
 	switch (chanwidth) {
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		vht_opclass = 128;
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		vht_opclass = 129;
 		break;
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		vht_opclass = 130;
 		break;
 	default:
@@ -1057,15 +1529,18 @@
 			return NUM_HOSTAPD_MODES;
 
 		switch (chanwidth) {
-		case CHANWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
 			*op_class = 133;
 			break;
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			*op_class = 134;
 			break;
-		case CHANWIDTH_80P80MHZ:
+		case CONF_OPER_CHWIDTH_80P80MHZ:
 			*op_class = 135;
 			break;
+		case CONF_OPER_CHWIDTH_320MHZ:
+			*op_class = 137;
+			break;
 		default:
 			if (sec_channel)
 				*op_class = 132;
@@ -1090,12 +1565,12 @@
 			return NUM_HOSTAPD_MODES;
 
 		switch (chanwidth) {
-		case CHANWIDTH_USE_HT:
-		case CHANWIDTH_2160MHZ:
+		case CONF_OPER_CHWIDTH_USE_HT:
+		case CONF_OPER_CHWIDTH_2160MHZ:
 			*channel = (freq - 56160) / 2160;
 			*op_class = 180;
 			break;
-		case CHANWIDTH_4320MHZ:
+		case CONF_OPER_CHWIDTH_4320MHZ:
 			/* EDMG channels 9 - 13 */
 			if (freq > 56160 + 2160 * 5)
 				return NUM_HOSTAPD_MODES;
@@ -1103,7 +1578,7 @@
 			*channel = (freq - 56160) / 2160 + 8;
 			*op_class = 181;
 			break;
-		case CHANWIDTH_6480MHZ:
+		case CONF_OPER_CHWIDTH_6480MHZ:
 			/* EDMG channels 17 - 20 */
 			if (freq > 56160 + 2160 * 4)
 				return NUM_HOSTAPD_MODES;
@@ -1111,7 +1586,7 @@
 			*channel = (freq - 56160) / 2160 + 16;
 			*op_class = 182;
 			break;
-		case CHANWIDTH_8640MHZ:
+		case CONF_OPER_CHWIDTH_8640MHZ:
 			/* EDMG channels 25 - 27 */
 			if (freq > 56160 + 2160 * 3)
 				return NUM_HOSTAPD_MODES;
@@ -1140,28 +1615,31 @@
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
-		cw = CHANWIDTH_USE_HT;
+		cw = CONF_OPER_CHWIDTH_USE_HT;
 		break;
 	case CHAN_WIDTH_80:
-		cw = CHANWIDTH_80MHZ;
+		cw = CONF_OPER_CHWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		cw = CHANWIDTH_80P80MHZ;
+		cw = CONF_OPER_CHWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		cw = CHANWIDTH_160MHZ;
+		cw = CONF_OPER_CHWIDTH_160MHZ;
 		break;
 	case CHAN_WIDTH_2160:
-		cw = CHANWIDTH_2160MHZ;
+		cw = CONF_OPER_CHWIDTH_2160MHZ;
 		break;
 	case CHAN_WIDTH_4320:
-		cw = CHANWIDTH_4320MHZ;
+		cw = CONF_OPER_CHWIDTH_4320MHZ;
 		break;
 	case CHAN_WIDTH_6480:
-		cw = CHANWIDTH_6480MHZ;
+		cw = CONF_OPER_CHWIDTH_6480MHZ;
 		break;
 	case CHAN_WIDTH_8640:
-		cw = CHANWIDTH_8640MHZ;
+		cw = CONF_OPER_CHWIDTH_8640MHZ;
+		break;
+	case CHAN_WIDTH_320:
+		cw = CONF_OPER_CHWIDTH_320MHZ;
 		break;
 	}
 
@@ -1263,8 +1741,9 @@
 		if (chan < 25 || chan > 29)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1313,8 +1792,9 @@
 		if (chan != 25)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1369,8 +1849,9 @@
 		if (chan != 25)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1395,8 +1876,9 @@
 		if (chan < 149 || chan > 165)
 			return -1;
 		return 5000 + 5 * chan;
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1458,6 +1940,7 @@
 	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
 	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
 	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+	case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */
 		if (chan < 1 || chan > 233)
 			return -1;
 		return 5950 + chan * 5;
@@ -1481,8 +1964,9 @@
 		if (chan < 25 || chan > 29)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 /**
@@ -1822,6 +2306,9 @@
 	S2S(DENIED_HE_NOT_SUPPORTED)
 	S2S(SAE_HASH_TO_ELEMENT)
 	S2S(SAE_PK)
+	S2S(INVALID_PUBLIC_KEY)
+	S2S(PASN_BASE_AKMP_FAILED)
+	S2S(OCI_MISMATCH)
 	}
 	return "UNKNOWN";
 #undef S2S
@@ -1911,11 +2398,14 @@
 
 	/*
 	 * IEEE Std 802.11ax-2021, Table E-4 actually talks about channel center
-	 * frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing
-	 * of 80 MHz, but currently use the following definition for simplicity
+	 * frequency index for operation classes 128, 129, 130, 132, 133, 134,
+	 * and 135, but currently use the lowest 20 MHz channel for simplicity
 	 * (these center frequencies are not actual channels, which makes
-	 * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take
-	 * care of removing invalid channels.
+	 * wpas_p2p_verify_channel() fail).
+	 * Specially for the operation class 136, it is also defined to use the
+	 * channel center frequency index value, but it happens to be a 20 MHz
+	 * channel and the channel number in the channel set would match the
+	 * value in for the frequency center.
 	 */
 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
@@ -2272,6 +2762,9 @@
 	/* channels 15, 47, 79...*/
 	if ((idx & 0x1f) == 0xf)
 		return 3; /* 160 MHz */
+	/* channels 31, 63, 95, 127, 159, 191 */
+	if ((idx & 0x1f) == 0x1f && idx < 192)
+		return 4; /* 320 MHz */
 
 	return -1;
 }
@@ -2294,7 +2787,7 @@
 
 bool is_6ghz_op_class(u8 op_class)
 {
-	return op_class >= 131 && op_class <= 136;
+	return op_class >= 131 && op_class <= 137;
 }
 
 
@@ -2600,6 +3093,8 @@
 		return 160;
 	case 136: /* UHB channels, 20 MHz: 2 */
 		return 20;
+	case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */
+		return 320;
 	case 180: /* 60 GHz band, channels 1..8 */
 		return 2160;
 	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
@@ -2608,103 +3103,111 @@
 		return 6480;
 	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
 		return 8640;
+	default:
+		return 20;
 	}
-
-	return 20;
 }
 
 
-int op_class_to_ch_width(u8 op_class)
+enum oper_chan_width op_class_to_ch_width(u8 op_class)
 {
 	switch (op_class) {
 	case 81:
 	case 82:
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 83: /* channels 1..9; 40 MHz */
 	case 84: /* channels 5..13; 40 MHz */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 115: /* channels 36,40,44,48; indoor only */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 116: /* channels 36,44; 40 MHz; indoor only */
 	case 117: /* channels 40,48; 40 MHz; indoor only */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 118: /* channels 52,56,60,64; dfs */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 119: /* channels 52,60; 40 MHz; dfs */
 	case 120: /* channels 56,64; 40 MHz; dfs */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 121: /* channels 100-140 */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 122: /* channels 100-142; 40 MHz */
 	case 123: /* channels 104-136; 40 MHz */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 124: /* channels 149,153,157,161 */
 	case 125: /* channels 149,153,157,161,165,169,171 */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 126: /* channels 149,157,165, 173; 40 MHz */
 	case 127: /* channels 153,161,169,177; 40 MHz */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
-		return CHANWIDTH_80MHZ;
+		return CONF_OPER_CHWIDTH_80MHZ;
 	case 129: /* center freqs 50, 114, 163; 160 MHz */
-		return CHANWIDTH_160MHZ;
+		return CONF_OPER_CHWIDTH_160MHZ;
 	case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */
-		return CHANWIDTH_80P80MHZ;
+		return CONF_OPER_CHWIDTH_80P80MHZ;
 	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
-		return CHANWIDTH_80MHZ;
+		return CONF_OPER_CHWIDTH_80MHZ;
 	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
-		return CHANWIDTH_160MHZ;
+		return CONF_OPER_CHWIDTH_160MHZ;
 	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
-		return CHANWIDTH_80P80MHZ;
+		return CONF_OPER_CHWIDTH_80P80MHZ;
 	case 136: /* UHB channels, 20 MHz: 2 */
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
+	case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */
+		return CONF_OPER_CHWIDTH_320MHZ;
 	case 180: /* 60 GHz band, channels 1..8 */
-		return CHANWIDTH_2160MHZ;
+		return CONF_OPER_CHWIDTH_2160MHZ;
 	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
-		return CHANWIDTH_4320MHZ;
+		return CONF_OPER_CHWIDTH_4320MHZ;
 	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
-		return CHANWIDTH_6480MHZ;
+		return CONF_OPER_CHWIDTH_6480MHZ;
 	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
-		return CHANWIDTH_8640MHZ;
+		return CONF_OPER_CHWIDTH_8640MHZ;
+	default:
+		return CONF_OPER_CHWIDTH_USE_HT;
 	}
-	return CHANWIDTH_USE_HT;
 }
 
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
-				       u8 eid, u8 eid_ext,
-				       const u8 *data, u8 len)
-{
-	struct frag_ies_info *frag_ies = &elems->frag_ies;
-	struct wpabuf *buf;
-	unsigned int i;
 
-	if (!elems || !data || !len)
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+				       bool ext_elem)
+{
+	struct wpabuf *buf;
+	const u8 *pos, *end = data + len;
+	size_t min_defrag_len = ext_elem ? 255 : 256;
+
+	if (!data || !len)
 		return NULL;
 
-	buf = wpabuf_alloc_copy(data, len);
+	if (len < min_defrag_len)
+		return wpabuf_alloc_copy(data, len);
+
+	buf = wpabuf_alloc_copy(data, min_defrag_len - 1);
 	if (!buf)
 		return NULL;
 
-	for (i = 0; i < frag_ies->n_frags; i++) {
+	pos = &data[min_defrag_len - 1];
+	len -= min_defrag_len - 1;
+	while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) {
 		int ret;
+		size_t elen = 2 + pos[1];
 
-		if (frag_ies->frags[i].eid != eid ||
-		    frag_ies->frags[i].eid_ext != eid_ext)
-			continue;
-
-		ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len);
+		if (elen > (size_t) (end - pos) || elen > len)
+			break;
+		ret = wpabuf_resize(&buf, pos[1]);
 		if (ret < 0) {
 			wpabuf_free(buf);
 			return NULL;
 		}
 
 		/* Copy only the fragment data (without the EID and length) */
-		wpabuf_put_data(buf, frag_ies->frags[i].ie,
-				frag_ies->frags[i].ie_len);
+		wpabuf_put_data(buf, &pos[2], pos[1]);
+		pos += elen;
+		len -= elen;
 	}
 
 	return buf;
@@ -2715,7 +3218,7 @@
 				  u8 eid, u8 eid_ext)
 {
 	const u8 *data;
-	u8 len;
+	size_t len;
 
 	/*
 	 * TODO: Defragmentation mechanism can be supported for all IEs. For now
@@ -2745,7 +3248,7 @@
 		return NULL;
 	}
 
-	return ieee802_11_defrag_data(elems, eid, eid_ext, data, len);
+	return ieee802_11_defrag_data(data, len, true);
 }
 
 /* Parse HT capabilities to get maximum number of supported spatial streams */
@@ -2837,6 +3340,7 @@
 	struct supported_chan_width supported_width;
 	supported_width.is_160_supported = 0;
 	supported_width.is_80p80_supported = 0;
+	supported_width.is_320_supported = 0;
 	if (elems == NULL)
 		return supported_width;
 
@@ -2844,6 +3348,8 @@
 		(struct ieee80211_vht_capabilities *) elems->vht_capabilities;
 	struct ieee80211_he_capabilities *hecaps =
 		(struct ieee80211_he_capabilities *) elems->he_capabilities;
+	struct ieee80211_eht_capabilities *ehtcaps =
+		(struct ieee80211_eht_capabilities *) elems->eht_capabilities;
 
 	if (vhtcaps) {
 		le32 vht_capabilities_info =
@@ -2861,8 +3367,16 @@
 		if (channel_width_set & HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
 			supported_width.is_80p80_supported = 1;
 	}
-	wpa_printf(MSG_DEBUG, " IE indicate 160 supported: %u, 80+80 supported: %u",
-        supported_width.is_160_supported, supported_width.is_80p80_supported);
+	if (ehtcaps) {
+		if (ehtcaps->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+		    EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)
+			supported_width.is_320_supported = 1;
+	}
+	wpa_printf(MSG_DEBUG,
+		   " IE indicates 320 supported: %u, 160 supported: %u, 80+80 supported: %u",
+		   supported_width.is_320_supported,
+		   supported_width.is_160_supported,
+		   supported_width.is_80p80_supported);
 	return supported_width;
 }
 
@@ -2972,6 +3486,39 @@
 	return channel_width;
 }
 
+/* Parse EHT operation IE to get EHT operation channel width */
+static enum chan_width get_eht_operation_channel_width(
+				struct ieee80211_eht_operation *eht_oper,
+				int eht_oper_len)
+{
+	enum chan_width channel_width = CHAN_WIDTH_UNKNOWN;
+	if (!(eht_oper->oper_params & EHT_OPER_INFO_PRESENT) ||
+	    eht_oper_len < (EHT_OPERATION_IE_MIN_LEN + EHT_OPER_INFO_MIN_LEN))
+		return channel_width;
+
+	switch (eht_oper->oper_info.control & EHT_OPER_CHANNEL_WIDTH_MASK) {
+	case EHT_OPER_CHANNEL_WIDTH_20MHZ:
+		channel_width = CHAN_WIDTH_20;
+		break;
+	case EHT_OPER_CHANNEL_WIDTH_40MHZ:
+		channel_width = CHAN_WIDTH_40;
+		break;
+	case EHT_OPER_CHANNEL_WIDTH_80MHZ:
+		channel_width = CHAN_WIDTH_80;
+		break;
+	case EHT_OPER_CHANNEL_WIDTH_160MHZ:
+		channel_width = CHAN_WIDTH_160;
+		break;
+	case EHT_OPER_CHANNEL_WIDTH_320MHZ:
+		channel_width = CHAN_WIDTH_320;
+		break;
+	default:
+		break;
+	}
+	wpa_printf(MSG_DEBUG, " EHT operation CBW: %u", channel_width);
+	return channel_width;
+}
+
 /* Parse HT/VHT/HE operation IEs to get operation channel width */
 enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems)
 {
@@ -2985,7 +3532,14 @@
 	    (struct ieee80211_vht_operation_info *) elems->vht_operation;
 	struct ieee80211_he_operation *he_oper =
 	    (struct ieee80211_he_operation *) elems->he_operation;
-	if (he_oper)
+	struct ieee80211_eht_operation *eht_oper =
+	    (struct ieee80211_eht_operation *) elems->eht_operation;
+
+	if (eht_oper)
+		channel_width = get_eht_operation_channel_width(
+			eht_oper, elems->eht_operation_len);
+
+	if (channel_width == CHAN_WIDTH_UNKNOWN && he_oper)
 		channel_width = get_he_operation_channel_width(
 			he_oper, elems->he_operation_len);
 
@@ -3009,7 +3563,11 @@
 				enum chan_width ap_operation_chan_width,
 				struct supported_chan_width sta_supported_chan_width)
 {
-	if (ap_operation_chan_width == CHAN_WIDTH_160)
+	if (ap_operation_chan_width == CHAN_WIDTH_320 &&
+	    sta_supported_chan_width.is_320_supported)
+		return CHAN_WIDTH_320;
+	if (ap_operation_chan_width == CHAN_WIDTH_160 ||
+	    ap_operation_chan_width == CHAN_WIDTH_320)
 		return (sta_supported_chan_width.is_160_supported)
 			? CHAN_WIDTH_160 : CHAN_WIDTH_80;
 	if (ap_operation_chan_width == CHAN_WIDTH_80P80)
@@ -3017,3 +3575,75 @@
 			? CHAN_WIDTH_80P80 : CHAN_WIDTH_80;
 	return ap_operation_chan_width;
 }
+
+const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type)
+{
+	const struct element *elem;
+
+	if (!ies)
+		return NULL;
+
+	for_each_element_extid(elem, WLAN_EID_EXT_MULTI_LINK, ies, len) {
+		if (elem->datalen >= 2 &&
+		    (elem->data[1] & MULTI_LINK_CONTROL_TYPE_MASK) == type)
+			return &elem->id;
+	}
+
+	return NULL;
+}
+
+
+const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len)
+{
+	const size_t mld_addr_pos =
+		2 /* Control field */ +
+		1 /* Common Info Length field */;
+	const size_t fixed_len = mld_addr_pos +
+		ETH_ALEN /* MLD MAC Address field */;
+
+	if (len < fixed_len)
+		return NULL;
+
+	if ((buf[0] & MULTI_LINK_CONTROL_TYPE_MASK) !=
+	    MULTI_LINK_CONTROL_TYPE_BASIC)
+		return NULL;
+
+	return &buf[mld_addr_pos];
+}
+
+
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type)
+{
+	const u8 *data;
+	size_t len;
+
+	switch (type) {
+	case MULTI_LINK_CONTROL_TYPE_BASIC:
+		data = elems->basic_mle;
+		len = elems->basic_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_PROBE_REQ:
+		data = elems->probe_req_mle;
+		len = elems->probe_req_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_RECONF:
+		data = elems->reconf_mle;
+		len = elems->reconf_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_TDLS:
+		data = elems->tdls_mle;
+		len = elems->tdls_mle_len;
+		break;
+	case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS:
+		data = elems->prior_access_mle;
+		len = elems->prior_access_mle_len;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "Defragmentation not supported for Multi-Link element type=%u",
+			   type);
+		return NULL;
+	}
+
+	return ieee802_11_defrag_data(data, len, true);
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index e21f7be..d1f7218 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -21,7 +21,6 @@
 struct hostapd_hw_modes;
 
 #define MAX_NOF_MB_IES_SUPPORTED 5
-#define MAX_NUM_FRAG_IES_SUPPORTED 3
 
 struct mb_ies_info {
 	struct {
@@ -31,21 +30,6 @@
 	u8 nof_ies;
 };
 
-struct frag_ies_info {
-	struct {
-		u8 eid;
-		u8 eid_ext;
-		const u8 *ie;
-		u8 ie_len;
-	} frags[MAX_NUM_FRAG_IES_SUPPORTED];
-
-	u8 n_frags;
-
-	/* the last parsed element ID and element extension ID */
-	u8 last_eid;
-	u8 last_eid_ext;
-};
-
 /* Parsed Information Elements */
 struct ieee802_11_elems {
 	const u8 *ssid;
@@ -119,6 +103,12 @@
 	const u8 *pasn_params;
 	const u8 *eht_capabilities;
 	const u8 *eht_operation;
+	const u8 *basic_mle;
+	const u8 *probe_req_mle;
+	const u8 *reconf_mle;
+	const u8 *tdls_mle;
+	const u8 *prior_access_mle;
+	const u8 *mbssid_known_bss;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -157,10 +147,10 @@
 	u8 dils_len;
 	u8 fils_req_params_len;
 	u8 fils_key_confirm_len;
-	u8 fils_hlp_len;
+	size_t fils_hlp_len;
 	u8 fils_ip_addr_assign_len;
 	u8 key_delivery_len;
-	u8 wrapped_data_len;
+	size_t wrapped_data_len;
 	u8 fils_pk_len;
 	u8 owe_dh_len;
 	u8 power_capab_len;
@@ -175,9 +165,20 @@
 	u8 pasn_params_len;
 	u8 eht_capabilities_len;
 	u8 eht_operation_len;
+	size_t basic_mle_len;
+	size_t probe_req_mle_len;
+	size_t reconf_mle_len;
+	size_t tdls_mle_len;
+	size_t prior_access_mle_len;
+	u8 mbssid_known_bss_len;
 
 	struct mb_ies_info mb_ies;
-	struct frag_ies_info frag_ies;
+
+	/*
+	 * The number of fragment elements to be skipped after a known
+	 * fragmented element.
+	 */
+	unsigned int num_frag_elems;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -185,6 +186,14 @@
 ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
 				struct ieee802_11_elems *elems,
 				int show_errors);
+void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems,
+				const u8 *ids, size_t num);
+void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems,
+				    const u8 *ids, size_t num);
+ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len,
+					 struct ieee802_11_elems *elems,
+					 struct wpabuf *mlbuf,
+					 u8 link_id, bool show_errors);
 int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
 					    u32 oui_type);
@@ -215,9 +224,10 @@
 			    const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
 int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
-enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
-						   int sec_channel, int vht,
-						   u8 *op_class, u8 *channel);
+enum hostapd_hw_mode
+ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel,
+			      enum oper_chan_width chanwidth,
+			      u8 *op_class, u8 *channel);
 int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
 				  int sec_channel, u8 *op_class, u8 *channel);
 int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
@@ -279,7 +289,7 @@
 			       unsigned int capab);
 bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab);
 int op_class_to_bandwidth(u8 op_class);
-int op_class_to_ch_width(u8 op_class);
+enum oper_chan_width op_class_to_ch_width(u8 op_class);
 
 /* element iteration helpers */
 #define for_each_element(_elem, _data, _datalen)			\
@@ -337,17 +347,20 @@
 int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
 			    struct ieee80211_edmg_config requested);
 
-struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
-				       u8 eid, u8 eid_ext,
-				       const u8 *data, u8 len);
+struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len,
+				       bool ext_elem);
 struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
 				  u8 eid, u8 eid_ext);
+struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type);
+const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
+const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
 
 int get_max_nss_capability(struct ieee802_11_elems *elems, int parse_for_rx);
 
 struct supported_chan_width {
 	u8 is_160_supported;
 	u8 is_80p80_supported;
+	u8 is_320_supported;
 };
 
 struct supported_chan_width get_supported_channel_width(struct ieee802_11_elems *elems);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 32cbaaa..9846fb4 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -209,6 +209,9 @@
 #define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
 #define WLAN_STATUS_SAE_PK 127
+#define WLAN_STATUS_INVALID_PUBLIC_KEY 136
+#define WLAN_STATUS_PASN_BASE_AKMP_FAILED 137
+#define WLAN_STATUS_OCI_MISMATCH 138
 
 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -481,6 +484,9 @@
 #define WLAN_EID_EXT_SPATIAL_REUSE 39
 #define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
 #define WLAN_EID_EXT_OCV_OCI 54
+#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+#define WLAN_EID_EXT_NON_INHERITANCE 56
+#define WLAN_EID_EXT_KNOWN_BSSID 57
 #define WLAN_EID_EXT_SHORT_SSID_LIST 58
 #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
 #define WLAN_EID_EXT_EDMG_CAPABILITIES 61
@@ -495,6 +501,7 @@
 #define WLAN_EID_EXT_EHT_CAPABILITIES 108
 #define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
 #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
 
 /* Extended Capabilities field */
 #define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -585,7 +592,11 @@
 #define WLAN_RSNX_CAPAB_SAE_PK 6
 #define WLAN_RSNX_CAPAB_SECURE_LTF 8
 #define WLAN_RSNX_CAPAB_SECURE_RTT 9
-#define WLAN_RSNX_CAPAB_PROT_RANGE_NEG 10
+#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
+#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+
+/* Multiple BSSID element subelements */
+#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
 
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
 #define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -610,12 +621,20 @@
 #define WLAN_ACTION_ROBUST_AV_STREAMING 19
 #define WLAN_ACTION_UNPROTECTED_DMG 20
 #define WLAN_ACTION_VHT 21
-#define WLAN_ACTION_S1G 22
-#define WLAN_ACTION_S1G_RELAY 23
+#define WLAN_ACTION_UNPROTECTED_S1G 22
+#define WLAN_ACTION_S1G 23
 #define WLAN_ACTION_FLOW_CONTROL 24
 #define WLAN_ACTION_CTRL_RESP_MCS_NEG 25
 #define WLAN_ACTION_FILS 26
+#define WLAN_ACTION_CDMG 27
+#define WLAN_ACTION_CMMG 28
+#define WLAN_ACTION_GLK 29
+#define WLAN_ACTION_HE 30
+#define WLAN_ACTION_PROTECTED_HE 31
+#define WLAN_ACTION_WUR 32
 #define WLAN_ACTION_PROTECTED_FTM 34
+#define WLAN_ACTION_EHT 36
+#define WLAN_ACTION_PROTECTED_EHT 37
 #define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 /* Note: 128-255 used to report errors by setting category | 0x80 */
@@ -1280,9 +1299,12 @@
 #define HT_OPER_PARAM_PCO_PHASE				((u16) BIT(11))
 /* B36..B39 - Reserved */
 
+#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
+#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
+#define BSS_MEMBERSHIP_SELECTOR_EPD 124
+#define BSS_MEMBERSHIP_SELECTOR_GLK 125
 #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
-#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
 
 /* VHT Defines */
 #define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
@@ -1340,16 +1362,11 @@
 
 #define VHT_RX_NSS_MAX_STREAMS			    8
 
-/* VHT/EDMG channel widths */
+/* VHT operation information - channel widths */
 #define CHANWIDTH_USE_HT	0
 #define CHANWIDTH_80MHZ		1
 #define CHANWIDTH_160MHZ	2
 #define CHANWIDTH_80P80MHZ	3
-#define CHANWIDTH_2160MHZ	4
-#define CHANWIDTH_4320MHZ	5
-#define CHANWIDTH_6480MHZ	6
-#define CHANWIDTH_8640MHZ	7
-#define CHANWIDTH_40MHZ_6GHZ	8
 
 #define HE_NSS_MAX_STREAMS			    8
 
@@ -1948,6 +1965,42 @@
 	WNM_NOTIF_TYPE_VENDOR_SPECIFIC = 221,
 };
 
+struct wnm_event_report_element {
+	u8 eid; /* WLAN_EID_EVENT_REPORT */
+	u8 len;
+	u8 token;
+	u8 type;
+	u8 status;
+	/* Followed by conditional fields */
+	union {
+		struct {
+			u8 tsf[8]; /* Event TSF */
+			u8 color_bitmap[8]; /* Event Report field */
+		} STRUCT_PACKED bss_color_collision;
+		struct {
+			u8 tsf[8]; /* Event TSF */
+			u8 color; /* Event Report field */
+		} STRUCT_PACKED bss_color_in_use;
+	} u;
+} STRUCT_PACKED;
+
+enum wnm_event_report_status {
+	WNM_STATUS_SUCCESSFUL = 0,
+	WNM_STATUS_REQ_FAILED = 1,
+	WNM_STATUS_REQ_REFUSED = 2,
+	WNM_STATUS_REQ_INCAPABLE = 3,
+	WNM_STATUS_FREQUENT_TRANSITION = 4,
+};
+
+enum wnm_event_report_type {
+       WNM_EVENT_TYPE_TRANSITION = 0,
+       WNM_EVENT_TYPE_RSNA = 1,
+       WNM_EVENT_TYPE_P2P_LINK = 2,
+       WNM_EVENT_TYPE_WNM_LOG = 3,
+       WNM_EVENT_TYPE_BSS_COLOR_COLLISION = 4,
+       WNM_EVENT_TYPE_BSS_COLOR_IN_USE = 5,
+};
+
 /* Channel Switch modes (802.11h) */
 #define CHAN_SWITCH_MODE_ALLOW_TX	0
 #define CHAN_SWITCH_MODE_BLOCK_TX	1
@@ -2440,13 +2493,20 @@
 #define RNR_BSS_PARAM_CO_LOCATED                    BIT(6)
 #define RNR_20_MHZ_PSD_MAX_TXPOWER                  255 /* dBm */
 
-/* IEEE P802.11be/D1.5, 9.4.2.311 - EHT Operation element */
+/* IEEE P802.11be/D2.3, 9.4.2.311 - EHT Operation element */
+
+#define EHT_OPERATION_IE_MIN_LEN 1
 
 /* Figure 9-1002b: EHT Operation Parameters field subfields */
 #define EHT_OPER_INFO_PRESENT                          BIT(0)
 #define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT       BIT(1)
+#define EHT_OPER_DEFAULT_PE_DURATION                   BIT(2)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_LIMIT        BIT(3)
+#define EHT_OPER_GROUP_ADDR_BU_INDICATION_EXPONENT     (BIT(4) | BIT(5))
+#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE          2
 
 /* Control subfield: Channel Width subfield; see Table 9-401b */
+#define EHT_OPER_CHANNEL_WIDTH_MASK                    0x7
 #define EHT_OPER_CHANNEL_WIDTH_20MHZ                   0
 #define EHT_OPER_CHANNEL_WIDTH_40MHZ                   1
 #define EHT_OPER_CHANNEL_WIDTH_80MHZ                   2
@@ -2454,6 +2514,8 @@
 #define EHT_OPER_CHANNEL_WIDTH_320MHZ                  4
 
 /* Figure 9-1002c: EHT Operation Information field format */
+#define EHT_OPER_INFO_MIN_LEN 3
+
 struct ieee80211_eht_oper_info {
 	u8 control; /* B0..B2: Channel Width */
 	u8 ccfs0;
@@ -2464,11 +2526,14 @@
 /* Figure 9-1002a: EHT Operation element format */
 struct ieee80211_eht_operation {
 	u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */
+	u8 basic_eht_mcs_nss_set[4];
 	struct ieee80211_eht_oper_info oper_info; /* 0 or 3 or 5 octets */
 } STRUCT_PACKED;
 
 /* IEEE P802.11be/D1.5, 9.4.2.313 - EHT Capabilities element */
 
+#define  EHT_CAPABILITIES_IE_MIN_LEN 11
+
 /* Figure 9-1002af: EHT MAC Capabilities Information field */
 #define EHT_MACCAP_EPCS_PRIO			BIT(0)
 #define EHT_MACCAP_OM_CONTROL			BIT(1)
@@ -2539,6 +2604,141 @@
 	u8 optional[EHT_MCS_NSS_CAPAB_LEN + EHT_PPE_THRESH_CAPAB_LEN];
 } STRUCT_PACKED;
 
+#define IEEE80211_EHT_CAPAB_MIN_LEN (2 + 9)
+
+/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */
+
+/* Figure 9-1002f: Multi-Link Control field */
+#define MULTI_LINK_CONTROL_TYPE_MASK			0x07
+#define MULTI_LINK_CONTROL_LEN				2
+
+/* Table 9-401c: Mult-Link element Type subfield encoding */
+#define MULTI_LINK_CONTROL_TYPE_BASIC			0
+#define MULTI_LINK_CONTROL_TYPE_PROBE_REQ		1
+#define MULTI_LINK_CONTROL_TYPE_RECONF			2
+#define MULTI_LINK_CONTROL_TYPE_TDLS			3
+#define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS		4
+
+/*
+ * IEEE P802.11be/D2.2, Table 9-401c: Optional subelement IDs for Link Info
+ * field of the Multi-Link element
+ */
+#define MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE		0
+#define MULTI_LINK_SUB_ELEM_ID_VENDOR			221
+#define MULTI_LINK_SUB_ELEM_ID_FRAGMENT			254
+
+/* IEEE P802.11be/D2.2, 9.4.2.312.2 - Basic Multi-Link element */
+
+/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+#define BASIC_MULTI_LINK_CTRL_PRES_LINK_ID		0x0010
+#define BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT	0x0020
+#define BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO		0x0040
+#define BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA		0x0080
+#define BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA		0x0100
+#define BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID		0x0200
+
+/*
+ * STA Control field definitions of Per-STA Profile subelement in Basic
+ * Multi-Link element as described in Figure 9-1002n: STA Control field format.
+ */
+#define BASIC_MLE_STA_CTRL_LINK_ID_MASK			0x000F
+#define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE		0x0010
+#define BASIC_MLE_STA_CTRL_PRES_STA_MAC			0x0020
+#define BASIC_MLE_STA_CTRL_PRES_BEACON_INT		0x0040
+#define BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET		0x0080
+#define BASIC_MLE_STA_CTRL_PRES_DTIM_INFO		0x0100
+#define BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR		0x0200
+#define BASIC_MLE_STA_CTRL_NSTR_BITMAP			0x0400
+#define BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT		0x0800
+
+#define BASIC_MLE_STA_PROF_STA_MAC_IDX			3
+
+/* IEEE P802.11be/D2.2, 9.4.2.312.2.3 - Common Info field of the Basic
+ * Multi-Link element */
+struct eht_ml_basic_common_info {
+	u8 len;
+	u8 mld_addr[ETH_ALEN];
+
+	/*
+	 * Followed by optional fields based on the multi link basic presence
+	 * bitmap
+	 *
+	 * Link ID Info: 1 octet
+	 * BSS Parameters Change Count: 1 octet
+	 * Medium Synchronization Delay Information: 2 octets
+	 * EML Capabilities: 2 octets
+	 * MLD Capabilities and Operations: 2 octets
+	 * AP MLD ID: 1 octet
+	 */
+	u8 variable[];
+} STRUCT_PACKED;
+
+#define EHT_ML_LINK_ID_MSK   0x0f
+
+#define EHT_ML_MEDIUM_SYNC_DELAY_DURATION   0x00ff
+#define EHT_ML_MEDIUM_SYNC_DELAY_OFDM_ED_TH 0x0f00
+#define EHT_ML_MEDIUM_SYNC_DELAY_MAX_TXOP   0xf000
+
+#define EHT_ML_EML_CAPA_EMLSR_SUPP               0x0001
+#define EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK 0x000e
+#define EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK   0x0070
+#define EHT_ML_EML_CAPA_EMLMR_SUPP               0x0080
+#define EHT_ML_EML_CAPA_EMLMR_DELAY_MASK         0x0700
+#define EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK  0x7800
+
+#define EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK        0x000f
+#define EHT_ML_MLD_CAPA_SRS_SUPP                      0x0010
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ALL    0x0020
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_ALL_TO_ONE    0x0040
+#define EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK  0x0060
+#define EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK         0x0f80
+#define EHT_ML_MLD_CAPA_AAR_SUPP                      0x1000
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.2.4 - Per-STA Profile subelement format */
+struct ieee80211_eht_per_sta_profile {
+	le16 sta_control;
+
+	/* Followed by STA Info and STA Profile fields */
+	u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.3 - Probe Request Multi-Link element */
+
+#define EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID 0x0001
+
+struct eht_ml_probe_req_common_info {
+	u8 len;
+
+	/*
+	 * Followed by optional fields based on the multi link basic presence
+	 * bitmap
+	 *
+	 * AP MLD ID: 1 octet
+	 */
+	u8 variable[];
+} STRUCT_PACKED;
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.4 - Reconfiguration Multi-Link element */
+
+#define EHT_ML_PRES_BM_RECONFIGURE_MLD_ADDRESS 0x0001
+
+/* IEEE P802.11be/D2.0, 9.4.2.312.1 - Multi-Link element / General */
+
+struct ieee80211_eht_ml {
+	le16 ml_control;
+
+	/* Followed by Common Info and Link Info fields */
+	u8 variable[];
+} STRUCT_PACKED;
+
+/* Table 9-401c - Optional subelement IDs for Link Info field of the
+ * Multi-Link element */
+enum ieee80211_eht_ml_sub_elem {
+	EHT_ML_SUB_ELEM_PER_STA_PROFILE = 0,
+	EHT_ML_SUB_ELEM_VENDOR = 221,
+	EHT_ML_SUB_ELEM_FRAGMENT = 254,
+};
+
 /* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
 #define EDMG_BSS_OPERATING_CHANNELS_OFFSET	6
 #define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET	7
@@ -2622,6 +2822,7 @@
 #define FD_CAP_BSS_CHWIDTH_40				1
 #define FD_CAP_BSS_CHWIDTH_80				2
 #define FD_CAP_BSS_CHWIDTH_160_80_80			3
+#define FD_CAP_BSS_CHWIDTH_320				4
 #define FD_CAP_BSS_CHWIDTH_SHIFT			2
 
 #define FD_CAP_NSS_1					0
@@ -2685,4 +2886,15 @@
 #define IS_CROSS_AKM_ROAM_KEY_MGMT(key_mgmt) \
 	((key_mgmt & WPA_KEY_MGMT_CROSS_AKM_ROAM) == WPA_KEY_MGMT_CROSS_AKM_ROAM)
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+struct ieee80211_neighbor_ap_info {
+	u8 tbtt_info_hdr;
+	u8 tbtt_info_len;
+	u8 op_class;
+	u8 channel;
+
+	/* Followed by the rest of the TBTT Information field contents */
+	u8 data[0];
+} STRUCT_PACKED;
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ocv.c b/src/common/ocv.c
index c9dc14f..d77bc4b 100644
--- a/src/common/ocv.c
+++ b/src/common/ocv.c
@@ -159,11 +159,10 @@
 	}
 
 	/*
-	 * When using a 160 or 80+80 MHz channel to transmit, verify that we use
+	 * When using an 80+80 MHz channel to transmit, verify that we use
 	 * the same segments as the receiver by comparing frequency segment 1.
 	 */
-	if ((ci->chanwidth == CHAN_WIDTH_160 ||
-	     ci->chanwidth == CHAN_WIDTH_80P80) &&
+	if (ci->chanwidth == CHAN_WIDTH_80P80 &&
 	    tx_seg1_idx != oci.seg1_idx) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
 			    "frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index 8fcb135..3b5c0b8 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -51,7 +51,10 @@
 		wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
 			   MAC2STR(e->addr));
 
-		ptksa_cache_free_entry(ptksa, e);
+		if (e->cb && e->ctx)
+			e->cb(e);
+		else
+			ptksa_cache_free_entry(ptksa, e);
 	}
 
 	ptksa_cache_set_expiration(ptksa);
@@ -254,10 +257,14 @@
 /*
  * ptksa_cache_add - Add a PTKSA cache entry
  * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @own_addr: Own MAC address
  * @addr: Peer address
  * @cipher: The cipher used
  * @life_time: The PTK life time in seconds
  * @ptk: The PTK
+ * @life_time_expiry_cb: Callback for alternative expiration handling
+ * @ctx: Context pointer to save into e->ctx for the callback
+ * @akmp: The key management mechanism that was used to derive the PTK
  * Returns: Pointer to the added PTKSA cache entry or %NULL on error
  *
  * This function creates a PTKSA entry and adds it to the PTKSA cache.
@@ -265,12 +272,17 @@
  * this entry will be replaced with the new entry.
  */
 struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
+					   const u8 *own_addr,
 					   const u8 *addr, u32 cipher,
 					   u32 life_time,
-					   const struct wpa_ptk *ptk)
+					   const struct wpa_ptk *ptk,
+					   void (*life_time_expiry_cb)
+					   (struct ptksa_cache_entry *e),
+					   void *ctx, u32 akmp)
 {
 	struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
 	struct os_reltime now;
+	bool set_expiry = false;
 
 	if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
 		return NULL;
@@ -289,6 +301,12 @@
 	dl_list_init(&entry->list);
 	os_memcpy(entry->addr, addr, ETH_ALEN);
 	entry->cipher = cipher;
+	entry->cb = life_time_expiry_cb;
+	entry->ctx = ctx;
+	entry->akmp = akmp;
+
+	if (own_addr)
+		os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
 
 	os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
 
@@ -302,6 +320,8 @@
 		}
 	}
 
+	if (dl_list_empty(&entry->list))
+		set_expiry = true;
 	/*
 	 * If the expiration is later then all other or the list is empty
 	 * entries, add it to the end of the list;
@@ -317,5 +337,8 @@
 		   "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
 		   MAC2STR(addr), cipher);
 
+	if (set_expiry)
+		ptksa_cache_set_expiration(ptksa);
+
 	return entry;
 }
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index 28ef291..6182215 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -23,6 +23,10 @@
 	os_time_t expiration;
 	u32 cipher;
 	u8 addr[ETH_ALEN];
+	u8 own_addr[ETH_ALEN];
+	void (*cb)(struct ptksa_cache_entry *e);
+	void *ctx;
+	u32 akmp;
 };
 
 #ifdef CONFIG_PTKSA_CACHE
@@ -35,9 +39,13 @@
 					   const u8 *addr, u32 cipher);
 int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len);
 struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
+					   const u8 *own_addr,
 					   const u8 *addr, u32 cipher,
 					   u32 life_time,
-					   const struct wpa_ptk *ptk);
+					   const struct wpa_ptk *ptk,
+					   void (*cb)
+					   (struct ptksa_cache_entry *e),
+					   void *ctx, u32 akmp);
 void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
 
 #else /* CONFIG_PTKSA_CACHE */
@@ -64,8 +72,9 @@
 }
 
 static inline struct ptksa_cache_entry *
-ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher,
-		u32 life_time, const struct wpa_ptk *ptk)
+ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
+		u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
+		void (*cb)(struct ptksa_cache_entry *e), void *ctx, u32 akmp)
 {
 	return NULL;
 }
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index d04c8d1..6c25816 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -2,6 +2,7 @@
  * Qualcomm Atheros OUI and vendor specific assignments
  * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2020, The Linux Foundation
+ * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -711,10 +712,10 @@
  *	This event contains Tx VDEV group information, other VDEVs
  *	interface index, and status information.
  *
- * @QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY: Vendor command to
- *	configure the concurrent session policies when multiple STA interfaces
+ * @QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_POLICY: Vendor command to
+ *	configure the concurrent session policies when multiple interfaces
  *	are (getting) active. The attributes used by this command are defined
- *	in enum qca_wlan_vendor_attr_concurrent_sta_policy.
+ *	in enum qca_wlan_vendor_attr_concurrent_policy.
  *
  * @QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS: Userspace can use this command
  *	to query usable channels for different interface types such as STA,
@@ -788,6 +789,116 @@
  *
  *	The attributes used with this command are defined in
  *	enum qca_wlan_vendor_attr_mcc_quota.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_RADIO_COMBINATION_MATRIX: Vendor command to
+ *	get the WLAN radio combinations matrix supported by the device which
+ *	provides the device simultaneous radio configurations such as
+ *	standalone, dual band simultaneous, and single band simultaneous.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_radio_combination_matrix.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DRIVER_READY: Event indicating to the user space
+ *	that the driver is ready for operations again after recovering from
+ *	internal failures. This occurs following a failure that was indicated by
+ *	@QCA_NL80211_VENDOR_SUBCMD_HANG.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_PASN: Subcommand used to offload preassociation
+ *	security negotiation and key generation to user space.
+ *
+ *	When used as an event, the driver requests userspace to trigger the PASN
+ *	authentication or dropping of a PTKSA for the indicated peer devices.
+ *	When used as a command response, userspace indicates a consolidated
+ *	status report for all the peers that were requested for.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_pasn.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT: Subcommand used to set
+ *	secure ranging context such as TK and LTF keyseed for each peer
+ *	requested by the driver with a @QCA_NL80211_VENDOR_SUBCMD_PASN event.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_secure_ranging_ctx.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD: This vendor subcommand is used to
+ *	enable/disable offload processing in firmware during system/runtime
+ *	suspend for CoAP messages (see RFC7252: The Constrained Application
+ *	Protocol) and fetch information of the CoAP messages cached during
+ *	offload processing.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_coap_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG: Subcommand to configure
+ *	(add, remove, or change) a Stream Classification Service (SCS) rule.
+ *
+ *      The attributes used with this event are defined in
+ *      enum qca_wlan_vendor_attr_scs_rule_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY: Fetch SAR capabilities
+ *	supported by the WLAN firmware.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_sar_capability.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SR: Subcommand used to implement Spatial Reuse
+ *	(SR) feature. This command is used by userspace to configure SR
+ *	parameters to the driver and to get the SR related parameters and
+ *	statistics with synchronous responses from the driver.
+ *	The driver also uses this command to send asynchronous events to
+ *	userspace to indicate suspend/resume of SR feature and changes
+ *	in SR parameters.
+ *
+ *	The attributes used with this command are defined in
+ *	enum qca_wlan_vendor_attr_sr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT: Subcommand used to
+ *	notify application layer about the primary netdev of an MLO connection.
+ *	In some implementations, MLO has multiple netdevs out of which one
+ *	netdev is designated as primary to provide a unified interface to the
+ *	bridge. In those implementations this event is sent on every MLO peer
+ *	connection.
+ *
+ *	The attributes used with this event are defined in
+ *	enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT: This vendor command is used by the
+ *	driver to notify different AFC events to userspace. The attributes used
+ *	with this command are defined in enum qca_wlan_vendor_attr_afc_event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE: This vendor command is used by
+ *	userspace to deliver AFC response data to driver. The attributes used
+ *	with this command are defined in enum qca_wlan_vendor_attr_afc_response.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP: Subcommand to configure AP interface to
+ *	operate in doze mode.
+ *
+ *	Userspace uses this command to configure the AP interface to enter or
+ *	exit from doze mode. The driver sends this event after it enters or
+ *	exits the doze mode with the updated AP doze mode settings.
+ *
+ *	The attributes used with this subcommand are defined in
+ *	enum qca_wlan_vendor_attr_dozed_ap.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE: This vendor subcommand is used
+ *	to get the status of local packet capture of monitor mode. The monitor
+ *	mode can be started using QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE
+ *	subcommand.
+ *
+ *	The attributes used with this command are defined in enum
+ *	qca_wlan_vendor_attr_get_monitor_mode.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS: This vendor command is used to
+ *	get roam information from the driver to user space. It provides the
+ *	latest several instances of roam information cached in the driver.
+ *	The command is only used for STA mode. The attributes used with this
+ *	command are defined in enum qca_wlan_vendor_attr_roam_cached_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MLO_LINK_STATE: This vendor subcommand is used to
+ *	configure and fetch the state information of the MLO links affiliated
+ *	with the STA interface. The attributes used with this command are
+ *	defined in enum qca_wlan_vendor_attr_mlo_link_state.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -974,7 +1085,7 @@
 	QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194,
 	QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195,
 	QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196,
-	QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY = 197,
+	QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_POLICY = 197,
 	QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS = 198,
 	QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY = 199,
 	QCA_NL80211_VENDOR_SUBCMD_MDNS_OFFLOAD = 200,
@@ -983,8 +1094,30 @@
 	QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS = 203,
 	QCA_NL80211_VENDOR_SUBCMD_RATEMASK_CONFIG = 204,
 	QCA_NL80211_VENDOR_SUBCMD_MCC_QUOTA = 205,
+	/* 206..212 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_GET_RADIO_COMBINATION_MATRIX = 213,
+	QCA_NL80211_VENDOR_SUBCMD_DRIVER_READY = 214,
+	QCA_NL80211_VENDOR_SUBCMD_PASN = 215,
+	QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT = 216,
+	QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD = 217,
+	QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG = 218,
+	QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY = 219,
+	QCA_NL80211_VENDOR_SUBCMD_SR = 220,
+	QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT = 221,
+	QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT = 222,
+	QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE = 223,
+	QCA_NL80211_VENDOR_SUBCMD_DOZED_AP = 224,
+	QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE = 225,
+	QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS = 226,
+	QCA_NL80211_VENDOR_SUBCMD_MLO_LINK_STATE = 227,
 };
 
+/* Compatibility defines for previously used subcmd names.
+ * These values should not be used in any new implementation.
+ */
+#define QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY \
+	QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_POLICY
+
 enum qca_wlan_vendor_attr {
 	QCA_WLAN_VENDOR_ATTR_INVALID = 0,
 	/* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
@@ -1019,7 +1152,7 @@
 	 * use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
-	/* Dummy (NOP) attribute for 64 bit padding */
+	/* Attribute used for padding for 64-bit alignment */
 	QCA_WLAN_VENDOR_ATTR_PAD = 13,
 	/* Unique FTM session cookie (Unsigned 64 bit). Specified in
 	 * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in
@@ -1249,6 +1382,9 @@
  *
  * @QCA_ROAM_REASON_BT_ACTIVITY: Roam triggered due to Bluetooth connection is
  * established when the station is connected in the 2.4 GHz band.
+ *
+ * @QCA_ROAM_REASON_STA_KICKOUT: Roam triggered due to continuous TX Data frame
+ * failures to the connected AP.
  */
 enum qca_roam_reason {
 	QCA_ROAM_REASON_UNKNOWN,
@@ -1266,10 +1402,14 @@
 	QCA_ROAM_REASON_PERIODIC_TIMER,
 	QCA_ROAM_REASON_BACKGROUND_SCAN,
 	QCA_ROAM_REASON_BT_ACTIVITY,
+	QCA_ROAM_REASON_STA_KICKOUT,
 };
 
 enum qca_wlan_vendor_attr_roam_auth {
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+	/* Indicates BSSID of the roamed AP for non-MLO roaming and MLD address
+	 * of the roamed AP for MLO roaming.
+	 */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
@@ -1314,6 +1454,11 @@
 	 * Defined by enum qca_roam_reason.
 	 */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
+	/* A nested attribute containing per-link information of all the links
+	 * of MLO connection done while roaming. The attributes used inside this
+	 * nested attribute are defined in enum qca_wlan_vendor_attr_mlo_links.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS = 15,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
@@ -1495,6 +1640,12 @@
  * Used with command to configure ACS operation for EHT mode.
  * Disable (flag attribute not present) - EHT disabled and
  * Enable (flag attribute present) - EHT enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME: Optional (u32).
+ * Used with command to configure how older scan can be considered for ACS
+ * scoring. In case scan was performed on a partial set of channels configured
+ * with this command within last QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME
+ * (in ms), scan only the remaining channels.
  */
 enum qca_wlan_vendor_attr_acs_offload {
 	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -1517,6 +1668,7 @@
 	QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL = 17,
 	QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP = 18,
 	QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19,
+	QCA_WLAN_VENDOR_ATTR_ACS_LAST_SCAN_AGEOUT_TIME = 20,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -1600,17 +1752,41 @@
  *	operation is specifically mentioned (against its respective
  *	documentation) to support either of these or both modes.
  * @QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI: Flag indicates
- * 	that the driver requires add/del virtual interface path using the
+ *	that the driver requires add/del virtual interface path using the
  *	generic nl80211 commands for NDP interface create/delete and to
  *	register/unregister the netdev instead of creating/deleting the NDP
  *	interface using the vendor commands
  *	QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE and
  *	QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE. With the latest kernel
- * 	(5.12 version onward), interface creation/deletion is not allowed using
- * 	vendor commands as it leads to a deadlock while acquiring the RTNL_LOCK
- * 	during the register/unregister of netdev. Create and delete NDP
- * 	interface using NL80211_CMD_NEW_INTERFACE and NL80211_CMD_DEL_INTERFACE
- * 	commands respectively if the driver advertises this capability set.
+ *	(5.12 version onward), interface creation/deletion is not allowed using
+ *	vendor commands as it leads to a deadlock while acquiring the RTNL_LOCK
+ *	during the register/unregister of netdev. Create and delete NDP
+ *	interface using NL80211_CMD_NEW_INTERFACE and NL80211_CMD_DEL_INTERFACE
+ *	commands respectively if the driver advertises this capability set.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA: Flag indicates that the device in
+ *	station mode supports secure LTF. If NL80211_EXT_FEATURE_SECURE_LTF is
+ *	set, then QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP: Flag indicates that the device in AP
+ *	mode supports secure LTF. If NL80211_EXT_FEATURE_SECURE_LTF is set, then
+ *	QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA: Flag indicates that the device in
+ *	station mode supports secure RTT measurement exchange. If
+ *	NL80211_EXT_FEATURE_SECURE_RTT is set,
+ *	QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP: Flag indicates that the device in AP
+ *	mode supports secure RTT measurement exchange. If
+ *	NL80211_EXT_FEATURE_SECURE_RTT is set,
+ *	QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA: Flag indicates that
+ *	the device in station mode supports protection of range negotiation and
+ *	measurement management frames. If
+ *	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE is set, then
+ *	QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP: Flag indicates that
+ *	the device in AP mode supports protection of range negotiation and
+ *	measurement management frames. If
+ *	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE is set, then
+ *	QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP will be ignored.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -1622,7 +1798,7 @@
 	QCA_WLAN_VENDOR_FEATURE_OCE_AP                  = 5,
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON            = 6,
 	QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
-	QCA_WLAN_VENDOR_FEATURE_TWT 			= 8,
+	QCA_WLAN_VENDOR_FEATURE_TWT			= 8,
 	QCA_WLAN_VENDOR_FEATURE_11AX			= 9,
 	QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT		= 10,
 	QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG		= 11,
@@ -1630,6 +1806,12 @@
 	QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS = 13,
 	QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT	= 14,
 	QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI = 15,
+	QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA		= 16,
+	QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP		= 17,
+	QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA		= 18,
+	QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP		= 19,
+	QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA = 20,
+	QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP = 21,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -1849,6 +2031,8 @@
  * This attribute is used to provide TSF sync interval and only applicable when
  * TSF command is %QCA_TSF_SYNC_START. If this attribute is not provided, the
  * driver will use the default value. Time unit is in milliseconds.
+ * @QCA_WLAN_VENDOR_ATTR_TSF_PAD: Attribute used for padding for 64-bit
+ * alignment.
  */
 enum qca_vendor_attr_tsf_cmd {
 	QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0,
@@ -1856,6 +2040,7 @@
 	QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
 	QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
 	QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL,
+	QCA_WLAN_VENDOR_ATTR_TSF_PAD,
 	QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_TSF_MAX =
 	QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1
@@ -2030,6 +2215,8 @@
  *	qca_wlan_vendor_scan_priority. This is an optional attribute.
  *	If this attribute is not configured, the driver shall use
  *	QCA_WLAN_VENDOR_SCAN_PRIORITY_HIGH as the priority of vendor scan.
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_PAD: Attribute used for padding for 64-bit
+ *	alignment.
  */
 enum qca_wlan_vendor_attr_scan {
 	QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
@@ -2046,6 +2233,7 @@
 	QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11,
 	QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME = 12,
 	QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY = 13,
+	QCA_WLAN_VENDOR_ATTR_SCAN_PAD = 14,
 	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
 	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
@@ -2437,12 +2625,8 @@
 	 * state, it should not exceed the negotiated channel width. If it is
 	 * configured when STA is in disconnected state, the configured value
 	 * will take effect for the next immediate connection.
-	 * Possible values are:
-	 *   NL80211_CHAN_WIDTH_20
-	 *   NL80211_CHAN_WIDTH_40
-	 *   NL80211_CHAN_WIDTH_80
-	 *   NL80211_CHAN_WIDTH_80P80
-	 *   NL80211_CHAN_WIDTH_160
+	 *
+	 * This uses values defined in enum nl80211_chan_width.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63,
 
@@ -2532,6 +2716,14 @@
 	 * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
 	 * disabled, as BK is of the lowest priority and an upgrade to it does
 	 * not result in any changes for the frames.
+	 *
+	 * If only UDP frames of BE or BK access category needs to be upgraded
+	 * without changing the access category of VO or VI UDP frames, refer to
+	 * attribute QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK.
+	 *
+	 * This attribute is not recommended to be used as it blindly forces all
+	 * UDP packets to a higher access category which could impact the
+	 * traffic pattern of all apps using UDP and can cause unknown behavior.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
 
@@ -2643,6 +2835,88 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_ARP_NS_OFFLOAD = 81,
 
+	/*
+	 * 8-bit unsigned value. This attribute can be used to configure the
+	 * data path mode to be followed for audio traffic. Possible values
+	 * are defined in enum qca_wlan_audio_data_path.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_AUDIO_DATA_PATH = 82,
+
+	/*
+	 * 8-bit unsigned value. This attribute can be used to configure the
+	 * Dedicated Bluetooth Antenna Mode (DBAM) feature. Possible values for
+	 * this attribute are defined in the enum qca_wlan_dbam_config.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DBAM = 83,
+
+	/* 8-bit unsigned value. This attribute takes the QoS/access category
+	 * value represented by the enum qca_wlan_ac_type and expects the driver
+	 * to upgrade the UDP frames of BE or BK access category to this access
+	 * category. This attribute will not modify UDP frames of VO or VI
+	 * access category. The value of QCA_WLAN_AC_ALL is invalid for this
+	 * attribute.
+	 *
+	 * This will override the DSCP value configured in the frame with the
+	 * intention to only upgrade the access category. That said, it is not
+	 * intended to downgrade the access category for the frames.
+	 * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+	 * disabled, as BK is of the lowest priority and an upgrade to it does
+	 * not result in any changes for the frames.
+	 *
+	 * This attribute behavior is similar to
+	 * QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE with the difference that
+	 * only UDP frames of BE or BK access category are upgraded and not
+	 * UDP frames of VI or VO access category.
+	 *
+	 * This attribute is not recommended to be used as it blindly forces all
+	 * UDP packets of BE or BK access category to a higher access category
+	 * which could impact the traffic pattern of all apps using UDP and can
+	 * cause unknown behavior.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE_FOR_BE_BK = 84,
+
+	/* 8-bit unsigned value to configure the driver to enable/disable the
+	 * periodic sounding for Tx beamformer functionality. The default
+	 * behavior uses algorithm to do sounding based on packet stats.
+	 *
+	 * 0 - Default behavior.
+	 * 1 - Enable the periodic sounding for Tx beamformer.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 85,
+
+	/* 8-bit unsigned value, whenever wifi calling (wfc) begins or ends,
+	 * userspace sends this information to the driver/firmware to configure
+	 * wfc state. The driver/firmware uses this information to
+	 * optimize power savings, rate adaption, roaming, etc.
+	 *
+	 * 1 - wfc is on.
+	 * 0 - wfc is off.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_WFC_STATE = 86,
+
+	/* 8-bit unsigned value to configure the driver to enable/disable the
+	 * EHT EML capability in management frame EHT capabilities.
+	 * 1 - Enable, 0 - Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_EML_CAPABILITY = 87,
+
+	/* 8-bit unsigned value to configure the driver with EHT MLO max
+	 * simultaneous links to be used for MLO connection.
+	 * The range of the value is 0 to 14.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_SIMULTANEOUS_LINKS = 88,
+
+	/* 8-bit unsigned value to configure the driver with EHT MLO maximum
+	 * number of links to be used for MLO connection.
+	 * The range of the value is 1 to 16.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MAX_NUM_LINKS = 89,
+
+	/* 8-bit unsigned value to configure the driver with EHT MLO mode.
+	 * Uses enum qca_wlan_eht_mlo_mode values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_EHT_MLO_MODE = 90,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -2658,6 +2932,19 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
 
 /**
+ * enum qca_dbam_config - Specifies DBAM config mode
+ * @QCA_DBAM_DISABLE: Firmware disables DBAM
+ * @QCA_DBAM_ENABLE: Firmware enables DBAM opportunistically when
+ * internal criteria are met.
+ * @QCA_DBAM_FORCE_ENABLE: Firmware enables DBAM forcefully.
+ */
+enum qca_dbam_config {
+	QCA_DBAM_DISABLE = 0,
+	QCA_DBAM_ENABLE = 1,
+	QCA_DBAM_FORCE_ENABLE = 2,
+};
+
+/**
  * enum qca_wlan_ani_setting - ANI setting type
  * @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level
  * @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter
@@ -3157,6 +3444,8 @@
  *    timestamp and calculate transfer delay for the message.
  * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32
  *    Real period for this measurement, unit in us.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PAD: Attribute used for padding for
+ *    64-bit alignment.
  */
 enum qca_wlan_vendor_attr_ll_stats_ext {
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
@@ -3252,6 +3541,7 @@
 
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PAD,
 
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
@@ -3537,7 +3827,8 @@
  * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by
  *	initiator. Not always provided.
  *	See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
- * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Attribute used for padding for 64-bit
+ *	alignment.
  */
 enum qca_wlan_vendor_attr_ftm_meas {
 	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID,
@@ -4070,6 +4361,29 @@
 	 * Possible values are 0-100.
 	 */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_TS_DUTY_CYCLE = 87,
+	/* Unsigned 32 bit value. The number of Beacon frames which are received
+	 * from the associated AP and indicate buffered unicast frame(s) for us
+	 * in the TIM element.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_TIM_BEACON = 88,
+	/* Unsigned 32 bit value. The total number of Beacon frames received
+	 * from the associated AP that have wrongly indicated buffered unicast
+	 * traffic in the TIM element for us.
+	 * Below scenarios will be considered as wrong TIM element beacon:
+	 * 1)	The related TIM element is set in the beacon for STA but STA
+	 *      doesn’t receive any unicast data after this beacon.
+	 * 2)	The related TIM element is still set in the beacon for STA
+	 *	after STA has indicated power save exit by QoS Null Data frame.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_TIM_BEACON_ERR = 89,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PAD = 90,
+
+	/* Signed 32 bit value. It represents the noise floor calibration value.
+	 * Possible values are -120~-50 dBm.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NF_CAL_VAL = 90,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -4683,7 +4997,7 @@
 
 /**
  * enum qca_vendor_attr_roam_control - Attributes to carry roam configuration
- * 	The following attributes are used to set/get/clear the respective
+ *	The following attributes are used to set/get/clear the respective
  *	configurations to/from the driver.
  *	For the get, the attribute for the configuration to be queried shall
  *	carry any of its acceptable values to the driver. In return, the driver
@@ -4885,6 +5199,48 @@
  *	Optional parameter. Scan dwell time for 6G Non Preferred Scanning
  *	Channels. If this attribute is not configured, the driver shall proceed
  *	with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_RX_LINKSPEED_THRESHOLD: u16 value in Mbps.
+ *	Optional parameter. RX link speed threshold to disable roaming.
+ *	If the current RX link speed is above the threshold, roaming is not
+ *	needed. If this attribute is not configured, or if it is set to 0, the
+ *	driver will not consider the RX link speed in the roaming decision.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX: u16 value in milliseconds.
+ *	Optional parameter. This configuration delays hand-off by the
+ *	specified duration to receive pending RX frames from the current BSS.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ: Unsigned 8-bit
+ *	value.
+ *	During the roam scan, if there are no desired APs found in the partial
+ *	frequency list, an immediate full scan on all the supported frequencies
+ *	is initiated as a fallback. This flag controls the frequency list
+ *	creation for the full scan on the following lines.
+ *	1 - Full scan to exclude the frequencies that were already scanned by
+ *	    the previous partial scan.
+ *	0 - Full scan to include all the supported frequencies irrespective of
+ *	    the ones part of the earlier partial scan.
+ *	If this flag is not specified, a full scan shall include all the
+ *	supported frequencies irrespective of the ones part of an earlier
+ *	partial scan.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY: Unsigned 8-bit
+ *	value.
+ *	During the roam scan, if there are no desired APs found in the partial
+ *	frequency list, an immediate full scan on all the supported frequencies
+ *	is initiated as a fallback. This full scan would add the 2.4/5/6 GHz
+ *	frequencies, including all PSC frequencies by default. This attribute
+ *	controls the inclusion of the 6 GHz PSC frequencies for the full scan
+ *	as following.
+ *	1 - Full scan to include the supported 6 GHz PSC frequencies only on the
+ *	   prior discovery of any 6 GHz frequency support in the environment.
+ *	   This discovery can happen through a prior RNR, 11k neighbor
+ *	request, 11v BTM request, host scan, etc.
+ *	0 - Default behavior. Full scan to include all the supported 6 GHz
+ *	   PSC frequencies regardless of whether 6 GHz BSSs have been
+ *	   discovered.
+ *	The default behavior if this flag is not specified is to include all
+ *	the supported 6 GHz PSC frequencies in the roam full scan.
  */
 enum qca_vendor_attr_roam_control {
 	QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -4910,6 +5266,10 @@
 	QCA_ATTR_ROAM_CONTROL_MAXIMUM_AWAY_TIME = 21,
 	QCA_ATTR_ROAM_CONTROL_SCAN_6G_PSC_DWELL_TIME = 22,
 	QCA_ATTR_ROAM_CONTROL_SCAN_6G_NON_PSC_DWELL_TIME = 23,
+	QCA_ATTR_ROAM_CONTROL_LINKSPEED_THRESHOLD = 24,
+	QCA_ATTR_ROAM_CONTROL_HO_DELAY_FOR_RX = 25,
+	QCA_ATTR_ROAM_CONTROL_FULL_SCAN_NO_REUSE_PARTIAL_SCAN_FREQ = 26,
+	QCA_ATTR_ROAM_CONTROL_FULL_SCAN_6GHZ_ONLY_ON_PRIOR_DISCOVERY = 27,
 
 	/* keep last */
 	QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -5502,6 +5862,9 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_DENYLISTED_BSSID = 46,
 
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_PAD = 47,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
@@ -5727,7 +6090,7 @@
 	/* HE 40 with extension channel below */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS     = 1 << 30,
 	/* HE 40 intolerant */
-	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL     = 1 << 31,
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL     = 1U << 31,
 };
 
 /**
@@ -5813,6 +6176,22 @@
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
 
 	/*
+	 * Segment 0 in MHz (u32).
+	 *
+	 * For 20/40/80 MHz bandwidth, this indicates the channel center
+	 * frequency index for the 20/40/80 MHz operating channel.
+	 * For 160 MHz bandwidth, this indicates the channel center
+	 * frequency of the primary 80 MHz channel.
+	 * For 320 MHz bandwidth, indicates the channel center frequency
+	 * of the primary 160 MHz channel.
+	 *
+	 * To maintain backward compatibility,
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
+	 * is also maintained.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 = 12,
+	/* Legacy alias for the Segment 0 attribute.
+	 *
 	 * VHT segment 0 in MHz (u32) and the attribute is mandatory.
 	 * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
 	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
@@ -5830,9 +6209,25 @@
 	 * is still used if either of the driver or user space application
 	 * doesn't support the 6 GHz band.
 	 */
-	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 =
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0,
 
 	/*
+	 * Segment 1 in MHz (u32).
+	 *
+	 * For 20/40/80 MHz bandwidth, this is set to 0.
+	 * For 160 MHz bandwidth, indicates the channel center frequency of the
+	 * 160 MHz channel.
+	 * For 320 MHz bandwidth, indicates the channel center frequency of the
+	 * 320 MHz channel.
+	 *
+	 * To maintain backward compatibility,
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
+	 * is also maintained.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 = 13,
+	/* Legacy alias for the Segment 1 attribute.
+	 *
 	 * VHT segment 1 in MHz (u32) and the attribute is mandatory.
 	 * Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
 	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
@@ -5850,7 +6245,8 @@
 	 * is still used if either of the driver or user space application
 	 * doesn't support the 6 GHz band.
 	 */
-	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 =
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1,
 
 	/*
 	 * 16-bit attribute of bits indicating the AP power modes supported by
@@ -5867,6 +6263,33 @@
 	 * qca_wlan_vendor_external_acs_event_chan_power_info_attr.
 	 */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR	= 15,
+	/*
+	 * This indicates the overlapping 320 MHz center frequency in MHz
+	 * (u32), if the given primary channel supports more than one
+	 * 320 MHz channel bonding.
+	 *
+	 * Example:
+	 * For 6 GHz, channel frequency 6115 MHz (channel number 33) segment 0
+	 * center frequency (primary 160 MHz) is 6185 MHz and there can be two
+	 * possible segment 2 frequencies for this (320 MHz center
+	 * frequencies):
+	 *
+	 * 1) Center frequency 6105 MHz (channel 31): 320 MHz channel bonding
+	 *    from frequency 5945 MHz - 6265 MHz
+	 * 2) Center frequency 6265 MHz (channel 63): 320 MHz channel bonding
+	 *    from frequency 6105 MHz - 6425 MHz
+	 *
+	 * In this case,
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_0 will
+	 * return 6185 MHz.
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_SEG_1 will
+	 * return 6105 MHz.
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+	 * will return 6265 MHz.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_OVERLAP_SEG_1
+									= 16,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
@@ -6119,6 +6542,11 @@
  * VHT seg1 channel frequency in MHz
  * Note: If user-space application has no support of the 6 GHz band, this
  * attribute is optional.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP: Required (u16)
+ * Puncture Bitmap for selected primary channel. Optional if no support
+ * for EHT (IEEE 802.11be). Encoding for this attribute follows the
+ * convention used in the Disabled Subchannel Bitmap field of the EHT Operation
+ * element.
  */
 enum qca_wlan_vendor_attr_external_acs_channels {
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -6154,6 +6582,7 @@
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11,
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP = 14,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
@@ -6626,6 +7055,8 @@
 	 * gain changes.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_RECAPTURE = 31,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PAD = 32,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
@@ -6658,6 +7089,8 @@
 	 * mismatches in search fft report. u64 attribute.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_PAD = 6,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX =
@@ -6936,6 +7369,28 @@
 	QCA_WLAN_HANG_BUS_FAILURE = 26,
 	/* tasklet/credit latency found */
 	QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT = 27,
+	/* MSDU buffers received in REO error ring, exceeding certain
+	 * threshold
+	 */
+	QCA_WLAN_HANG_RX_MSDU_BUF_RCVD_IN_ERR_RING = 28,
+	/* Vdev SM is out of sync and connect req received
+	 * when already connected
+	 */
+	QCA_WLAN_HANG_VDEV_SM_OUT_OF_SYNC = 29,
+	/* Stats request timeout */
+	QCA_WLAN_HANG_STATS_REQ_TIMEOUT = 30,
+	/* Leak in TX descriptor for a packet */
+	QCA_WLAN_HANG_TX_DESC_LEAK = 31,
+	/* Scheduler watchdog timeout */
+	QCA_WLAN_HANG_SCHED_TIMEOUT = 32,
+	/* Failed to send self peer deletion cmd to firmware */
+	QCA_WLAN_HANG_SELF_PEER_DEL_FAIL = 33,
+	/* Received del self sta without del bss */
+	QCA_WLAN_HANG_DEL_SELF_STA_FAIL = 34,
+	/* Recovery needed when sending flush completion to userspace */
+	QCA_WLAN_HANG_FLUSH_LOGS = 35,
+	/* Host wakeup because of page fault */
+	QCA_WLAN_HANG_HOST_WAKEUP_REASON_PAGE_FAULT = 36,
 };
 
 /**
@@ -8066,6 +8521,43 @@
 };
 
 /**
+ * enum eht_mcs_config - EHT MCS support configuration
+ *
+ * Configures the EHT Tx/Rx MCS map in EHT Capability element.
+ * These values are used in the driver to configure the EHT MCS map to advertise
+ * Tx/Rx MCS map in EHT capability and these values are applied for all the
+ * streams supported by the device.
+ * @EHT_MCS0_7: EHT MCS 0 to 7 support
+ * @EHT_MCS0_9: EHT MCS 0 to 9 support
+ * @EHT_MCS0_11: EHT MCS 0 to 11 support
+ * @EHT_MCS0_13: EHT MCS 0 to 13 support
+ */
+enum eht_mcs_config {
+	EHT_MCS0_7 = 0,
+	EHT_MCS0_9 = 1,
+	EHT_MCS0_11 = 2,
+	EHT_MCS0_13 = 3,
+};
+
+/**
+ * enum qca_wlan_eht_mlo_mode: EHT MLO mode configuration.
+ * @QCA_WLAN_EHT_MODE_INVALID: Invalid.
+ * @QCA_WLAN_EHT_MLSR: Multi-link single radio mode
+ * @QCA_WLAN_EHT_EMLSR: Enhanced multi-link single radio mode.
+ * @QCA_WLAN_EHT_NON_STR_MLMR: Non simultaneous transmit and receive
+ * multi-link multi radio mode.
+ * @QCA_WLAN_EHT_STR_MLMR: Simultaneous transmit and receive
+ * multi-link multi radio mode.
+ */
+enum qca_wlan_eht_mlo_mode {
+	QCA_WLAN_EHT_MODE_INVALID = 0,
+	QCA_WLAN_EHT_MLSR = 1,
+	QCA_WLAN_EHT_EMLSR = 2,
+	QCA_WLAN_EHT_NON_STR_MLMR = 3,
+	QCA_WLAN_EHT_STR_MLMR = 4,
+};
+
+/**
  * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
  * HE operating mode control transmit request. These attributes are
  * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
@@ -8647,6 +9139,74 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_11BE_EMLSR_MODE = 58,
 
+	/* 8-bit unsigned value to configure the driver to enable/disable the
+	 * periodic sounding for Tx beamformer functionality. The default
+	 * behavior uses algorithm to do sounding based on packet stats.
+	 *
+	 * 0 - Default behavior.
+	 * 1 - Enable the periodic sounding for Tx beamformer.
+	 * This attribute is used for testing purposes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 59,
+
+	/* 8-bit unsigned value to configure beamformee SS EHT capability
+	 * to indicate the maximum number of spatial streams that the STA
+	 * can receive in an EHT sounding NDP for <= 80 MHz bandwidth.
+	 * The range of the value is 3 to 7.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_80MHZ = 60,
+
+	/* 8-bit unsigned value to configure beamformee SS EHT capability
+	 * to indicate the maximum number of spatial streams that the STA
+	 * can receive in an EHT sounding NDP for 160 MHz bandwidth.
+	 * The range of the value is 3 to 7.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_160MHZ = 61,
+
+	/* 8-bit unsigned value to configure beamformee SS EHT capability
+	 * to indicate the maximum number of spatial streams that the STA
+	 * can receive in an EHT sounding NDP for 320 MHz bandwidth.
+	 * The range of the value is 3 to 7.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_BEAMFORMEE_SS_320MHZ = 62,
+
+	/* 8-bit unsigned value to configure the driver to exclude station
+	 * profile in Probe Request frame Multi-Link element.
+	 * 0 - Default behavior, sends the Probe Request frame with station
+	 * profile data included in the Multi-Link element.
+	 * 1 - Exclude station profile in Probe Request frame Multi-Link
+	 * element.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EXCLUDE_STA_PROF_IN_PROBE_REQ = 63,
+
+	/* 8-bit unsigned value to configure EHT testbed defaults.
+	 * This attribute is used to configure the testbed device.
+	 * 1 - Set the device EHT capabilities to testbed defaults.
+	 * 0 - Reset the device EHT capabilities to supported config.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_EHT_TESTBED_DEFAULTS = 64,
+
+	/* 8-bit unsigned value to indicate the EHT MCS support.
+	 * Uses enum eht_mcs_config values.
+	 * This attribute is used to configure the testbed device to
+	 * allow the advertised hardware capabilities to be downgraded
+	 * for testing purposes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_MCS = 65,
+
+	/* 8-bit unsigned value to configure EHT TB Sounding Feedback
+	 * Rate Limit capability.
+	 * This attribute is used to configure the testbed device.
+	 * 0 - Indicates no maximum supported data rate limitation.
+	 * 1 - Indicates the maximum supported data rate is the lower of
+	 * the 1500 Mb/s and the maximum supported data rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_EHT_TB_SOUNDING_FB_RL = 66,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -8862,12 +9422,15 @@
  * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host
  *	driver for the last received RSSI. Unsigned 64 bit number containing
  *	nanoseconds from the boottime.
+ * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_PAD: Attribute used for padding for
+ *	64-bit alignment.
  */
 enum qca_wlan_vendor_bss_filter_sta_stats {
 	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0,
 	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1,
 	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2,
 	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3,
+	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_PAD = 4,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST,
@@ -9158,6 +9721,9 @@
  * start. The default value in the firmware is 0.
  * This parameter is used for
  * 1. TWT SET Request
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD: Attribute used for padding for 64-bit
+ * alignment.
  */
 enum qca_wlan_vendor_attr_twt_setup {
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -9194,6 +9760,8 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE = 25,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26,
 
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PAD = 27,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
@@ -9359,6 +9927,16 @@
  * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF: Optional (u64)
  * This field contains absolute TSF value of the time at which the TWT
  * session will be resumed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_SP_START_OFFSET: Optional (s32)
+ * This field will be used when device supports Flexible TWT.
+ * This field contains an offset value by which to shift the starting time
+ * of the next service period. The value of offset can be negative or positive.
+ * If provided, this attribute will override
+ * QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME. The units are in microseconds.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_PAD: Attribute used for padding for 64-bit
+ * alignment.
  */
 enum qca_wlan_vendor_attr_twt_nudge {
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0,
@@ -9367,6 +9945,8 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE = 3,
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR = 4,
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF = 5,
+	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_SP_START_OFFSET = 6,
+	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_PAD = 7,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST,
@@ -9868,6 +10448,9 @@
  * to userspace along with QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG as an
  * asynchronous event when the driver is configured to send CFR data using
  * netlink events with %QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD: Attribute used for padding for 64-bit
+ * alignment.
  */
 enum qca_wlan_vendor_peer_cfr_capture_attr {
 	QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
@@ -9899,6 +10482,7 @@
 	QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE = 26,
 	QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID = 27,
 	QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA = 28,
+	QCA_WLAN_VENDOR_ATTR_PEER_CFR_PAD = 29,
 
 	/* Keep last */
 	QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
@@ -10191,6 +10775,8 @@
  * containing buffer of statistics to send to application layer entity.
  * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
  * representing a cookie for peer unique session.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PAD: Attribute used for padding for
+ * 64-bit alignment.
  */
 enum qca_wlan_vendor_attr_peer_stats_cache_params {
 	QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
@@ -10199,6 +10785,7 @@
 	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
 	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
 	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
+	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PAD = 5,
 
 	/* Keep last */
 	QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
@@ -10477,6 +11064,8 @@
 	 * reported.
 	 */
 	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME = 13,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAD = 14,
 
 	/* Keep last */
 	QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST,
@@ -10657,14 +11246,21 @@
  * These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of
  * %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
  *
- * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4G are shared.
- * @QCA_BTC_CHAIN_SEPARATED: chains of BT and WLAN 2.4G are separated.
+ * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4 GHz are shared.
+ * @QCA_BTC_CHAIN_SEPARATED_HYBRID: chains of BT and WLAN 2.4 GHz are
+ * separated, hybrid mode.
+ * @QCA_BTC_CHAIN_SEPARATED_FDD: chains of BT and WLAN 2.4 GHz are
+ * separated, fixed FDD mode.
  */
 enum qca_btc_chain_mode {
 	QCA_BTC_CHAIN_SHARED = 0,
-	QCA_BTC_CHAIN_SEPARATED = 1,
+	QCA_BTC_CHAIN_SEPARATED_HYBRID = 1,
+	QCA_BTC_CHAIN_SEPARATED_FDD = 2,
 };
 
+/* deprecated legacy name */
+#define QCA_BTC_CHAIN_SEPARATED QCA_BTC_CHAIN_SEPARATED_HYBRID
+
 /**
  * enum qca_vendor_attr_btc_chain_mode - Specifies attributes for BT coex
  * chain mode.
@@ -11001,6 +11597,21 @@
  * This represents the average congestion duration of uplink frames in MAC
  * queue in unit of ms. This can be queried either in connected state or
  * disconnected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS: Array of u32 nested
+ * values, used in AP mode. This represents the MPDU packet count per MCS
+ * rate value of TX packets. Every index of this nested attribute corresponds
+ * to MCS index, e.g., Index 0 represents MCS0 TX rate. This can be
+ * queried in connected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS: Array of u32 nested
+ * values, used in AP mode. This represents the MPDU packet count per MCS
+ * rate value of RX packets. Every index of this nested attribute corresponds
+ * to MCS index, e.g., Index 0 represents MCS0 RX rate. This can be
+ * queried in connected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD: Attribute used for padding for
+ * 64-bit alignment.
  */
 enum qca_wlan_vendor_attr_get_sta_info {
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -11054,6 +11665,9 @@
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON = 48,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON = 49,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY = 50,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS = 51,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS = 52,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD = 53,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -11573,24 +12187,62 @@
 };
 
 /**
- * enum qca_wlan_vendor_attr_concurrent_sta_policy - Defines attributes
- * used by QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY vendor command.
+ * enum qca_wlan_concurrent_ap_policy_config - Concurrent AP policies
  *
- * @QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG:
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_UNSPECIFIED: No specific policy for this AP
+ * interface.
+ *
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO: Select interface concurrencies
+ * to meet gaming audio latency requirements.
+ *
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING: Select interface
+ * concurrencies to meet lossless audio streaming requirements.
+ *
+ * @QCA_WLAN_CONCURRENT_AP_POLICY_XR: Select interface concurrencies to meet
+ * XR (eXtended Reality) requirements.
+ */
+enum qca_wlan_concurrent_ap_policy_config {
+	QCA_WLAN_CONCURRENT_AP_POLICY_UNSPECIFIED = 0,
+	QCA_WLAN_CONCURRENT_AP_POLICY_GAMING_AUDIO = 1,
+	QCA_WLAN_CONCURRENT_AP_POLICY_LOSSLESS_AUDIO_STREAMING = 2,
+	QCA_WLAN_CONCURRENT_AP_POLICY_XR = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_concurrent_policy - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_POLICY vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_STA_CONFIG:
  * u8 attribute. Configures the concurrent STA policy configuration.
  * Possible values are defined in enum qca_wlan_concurrent_sta_policy_config.
+
+ * @QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_AP_CONFIG:
+ * u8 attribute. Configures the concurrent AP policy configuration.
+ * Possible values are defined in enum qca_wlan_concurrent_ap_policy_config.
  */
-enum qca_wlan_vendor_attr_concurrent_sta_policy {
-	QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_INVALID = 0,
-	QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG = 1,
+enum qca_wlan_vendor_attr_concurrent_policy {
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_STA_CONFIG = 1,
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_AP_CONFIG = 2,
 
 	/* keep last */
-	QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST,
-	QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX =
-	QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST - 1,
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_AFTER_LAST - 1,
 
 };
 
+/* Compatibility defines for previously used enum
+ * qca_wlan_vendor_attr_concurrent_policy names. These values should not be used
+ * in any new implementation.
+ */
+#define QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG \
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_STA_CONFIG
+#define QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX \
+	QCA_WLAN_VENDOR_ATTR_CONCURRENT_POLICY_MAX
+#define qca_wlan_vendor_attr_concurrent_sta_policy \
+	qca_wlan_vendor_attr_concurrent_policy
+
 /**
  * enum qca_sta_connect_fail_reason_codes - Defines values carried
  * by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor
@@ -11739,6 +12391,8 @@
  *	or radar detection.
  * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED: NLA_FLAG attribute.
  *	This flag indicates radar signal has been detected.
+ * @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_PAD: Attribute used for padding for
+ *	64-bit alignment.
  */
 enum qca_wlan_vendor_attr_radar_history {
 	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_INVALID = 0,
@@ -11747,6 +12401,7 @@
 	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ = 2,
 	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP = 3,
 	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED = 4,
+	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_PAD = 5,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_LAST,
@@ -11762,13 +12417,19 @@
  *	command it clears MCC quota setting and restores adaptive scheduling.
  * @QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_FIXED: Channel time quota is fixed and
  *      will not be changed.
+ *      This quota type is present in command/event.
  * @QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_DYNAMIC: Channel time quota is dynamic
  *      and the target may change the quota based on the data activity.
+ *      This quota type is only present in event.
+ * @QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY: Channel time quota is optimized
+ *      by the target for low latency.
+ *      This quota type is only present in command.
  */
 enum qca_wlan_vendor_mcc_quota_type {
 	QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_CLEAR = 0,
 	QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_FIXED = 1,
 	QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_DYNAMIC = 2,
+	QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY = 3,
 };
 
 /**
@@ -11805,6 +12466,15 @@
  * configurations are applied. This is required in a command only. Only one
  * interface index may be specified. If not specified, the configuration is
  * rejected.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_LOW_LATENCY_MODE_ENABLE
+ * 8-bit unsigned value to enable/disable Multi-Channel Concurrency
+ * low latency mode. The firmware will do optimization for low
+ * latency in Multi-Channel concurrency state if enabled. And all existing
+ * user quota setting will be overwritten by the target.
+ * 0 - disable(default), 1 - enable.
+ * It is only present in a command with quota type of
+ * QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY.
  */
 enum qca_wlan_vendor_attr_mcc_quota {
 	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_INVALID = 0,
@@ -11813,6 +12483,7 @@
 	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_FREQ = 3,
 	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_CHAN_TIME_PERCENTAGE = 4,
 	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_IFINDEX = 5,
+	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_LOW_LATENCY_MODE_ENABLE = 6,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_MCC_QUOTA_LAST,
@@ -11821,6 +12492,512 @@
 };
 
 /**
+ * enum qca_wlan_roam_stats_invoke_reason - Roam invoke reason. These values
+ * are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED: Default value when target
+ *  invoke roam.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE: Neighbor unreachable
+ *  detection failed when the roam trigger.
+ * @QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE: Invoke from user space.
+ */
+
+enum qca_wlan_roam_stats_invoke_reason {
+	QCA_WLAN_ROAM_STATS_INVOKE_REASON_UNDEFINED = 0,
+	QCA_WLAN_ROAM_STATS_INVOKE_REASON_NUD_FAILURE = 1,
+	QCA_WLAN_ROAM_STATS_INVOKE_REASON_USER_SPACE = 2,
+};
+
+/**
+ * enum qca_wlan_roam_stats_tx_failures_reason - Roam TX failures reason. These
+ * values are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED: Default value when
+ *  roam by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY: Excessive retry when roam
+ *  trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY: Station inactivity when
+ *  roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT: IBSS disconnect when
+ *  roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT: TDLS peer has
+ *  disappeared, and all TX is failing when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT: SA query process
+ *   timeout when roam trigger by kickout.
+ * @QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT: Directly connected
+ *  peer has roamed to a repeater.
+ */
+enum qca_wlan_roam_stats_tx_failures_reason {
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_UNSPECIFIED = 0,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_XRETRY = 1,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_INACTIVITY = 2,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_IBSS_DISCONNECT = 3,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_TDLS_DISCONNECT = 4,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_SA_QUERY_TIMEOUT = 5,
+	QCA_WLAN_ROAM_STATS_KICKOUT_REASON_ROAMING_EVENT = 6,
+};
+
+/**
+ * enum qca_wlan_roam_stats_abort_reason - Roam abort reason. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON.
+ *
+ * @QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED: Target did not specify the
+ *  detailed reason for roam scan being aborted.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH: Roam scan is not
+ *  started due to high data RSSI during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD: Roam scan is not
+ *  started due to good link speed during LOW-RSSI roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH: Roam scan is not started
+ *  due to high data RSSI during background roaming.
+ * @QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD: Roam scan is not
+ *  started due to high beacon RSSI during background roaming
+ */
+enum qca_wlan_roam_stats_abort_reason {
+	QCA_WLAN_ROAM_STATS_ABORT_UNSPECIFIED = 0,
+	QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_DATA_RSSI_HIGH = 1,
+	QCA_WLAN_ROAM_STATS_ABORT_LOWRSSI_LINK_SPEED_GOOD = 2,
+	QCA_WLAN_ROAM_STATS_ABORT_BG_DATA_RSSI_HIGH = 3,
+	QCA_WLAN_ROAM_STATS_ABORT_BG_RSSI_ABOVE_THRESHOLD = 4,
+};
+
+/**
+ * enum qca_wlan_roam_stats_scan_type - Roam scan type define.
+ * These values are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_TYPE.
+ *
+ * @QCA_WLAN_ROAM_STATS_SCAN_TYPE_PARTIAL: Partial channel scan
+ * @QCA_WLAN_ROAM_STATS_SCAN_TYPE_FULL: Full channel scan
+ * @QCA_WLAN_ROAM_STATS_SCAN_TYPE_NO_SCAN: No roam scan was triggered.
+ *  This is generally used in BTM events to indicate BTM frame exchange logs.
+ * @QCA_WLAN_ROAM_STATS_SCAN_TYPE_HIGHER_BAND_5GHZ_6GHZ: Higher band roam scan
+ *  from 2.4 GHz to 5 GHz or 6 GHz
+ * @QCA_WLAN_ROAM_STATS_SCAN_TYPE_HIGHER_BAND_6GHZ: Higher band roam scan from
+ *  5 GHz to 6 GHz
+ */
+enum qca_wlan_roam_stats_scan_type {
+	QCA_WLAN_ROAM_STATS_SCAN_TYPE_PARTIAL = 0,
+	QCA_WLAN_ROAM_STATS_SCAN_TYPE_FULL = 1,
+	QCA_WLAN_ROAM_STATS_SCAN_TYPE_NO_SCAN = 2,
+	QCA_WLAN_ROAM_STATS_SCAN_TYPE_HIGHER_BAND_5GHZ_6GHZ = 3,
+	QCA_WLAN_ROAM_STATS_SCAN_TYPE_HIGHER_BAND_6GHZ = 4,
+};
+
+/**
+ * enum qca_wlan_roam_stats_scan_dwell_type - Roam scan dwell type.
+ * These values are used by the attribute
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_DWELL_TYPE.
+ *
+ * @QCA_WLAN_ROAM_STATS_DWELL_TYPE_UNSPECIFIED: Target did not specify the
+ *  detailed roam scan type.
+ * @QCA_WLAN_ROAM_STATS_DWELL_ACTIVE_TYPE: Active scan during roam.
+ * @QCA_WLAN_ROAM_STATS_DWELL_PASSIVE_TYPE: Passive scan during roam.
+ */
+enum qca_wlan_roam_stats_scan_dwell_type {
+	QCA_WLAN_ROAM_STATS_DWELL_TYPE_UNSPECIFIED = 0,
+	QCA_WLAN_ROAM_STATS_DWELL_TYPE_ACTIVE = 1,
+	QCA_WLAN_ROAM_STATS_DWELL_TYPE_PASSIVE = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_scan_chan_info - Attributes used inside
+ * the %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_scan_chan_info {
+	/* 32-bit unsigned value to indicate center frequency of the primary
+	 * channel in MHz for each roam scan channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHANNEL_FREQ = 1,
+	/* 8-bit unsigned value to indicate channel scan type for each
+	 * roam scan channel, values in qca_wlan_roam_stats_scan_dwell_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_DWELL_TYPE = 2,
+	/* 32-bit unsigned value to indicate maximum scan time in milliseconds
+	 * for each roam scan channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX_DWELL_TIME = 3,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_FRAME_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_roam_stats_frame_subtype - Roam frame subtypes. These values
+ * are used by the attribute %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE.
+ *
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH: Pre-authentication frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC: Reassociation frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1: EAPOL-Key M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2: EAPOL-Key M2 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3: EAPOL-Key M3 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4: EAPOL-Key M4 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1: EAPOL-Key GTK M1 frame
+ * @QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2: EAPOL-Key GTK M2 frame
+ */
+enum qca_wlan_roam_stats_frame_subtype {
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_PREAUTH = 1,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_REASSOC = 2,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M1 = 3,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M2 = 4,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M3 = 5,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_M4 = 6,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M1 = 7,
+	QCA_WLAN_ROAM_STATS_FRAME_SUBTYPE_EAPOL_GTK_M2 = 8,
+};
+
+/**
+ * enum roam_frame_status - Specifies the valid values the vendor roam frame
+ * attribute QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS can take.
+ *
+ * @QCA_WLAN_ROAM_FRAME_STATUS_SUCCESS: It indicates the roam frame was
+ *  sent or received successfully.
+ * @QCA_WLAN_ROAM_FRAME_STATUS_FAIL: It indicates the roam frame sending or
+ *  receiving failed.
+ */
+enum qca_wlan_roam_stats_frame_status {
+	QCA_WLAN_ROAM_STATS_FRAME_STATUS_SUCCESS = 0,
+	QCA_WLAN_ROAM_STATS_FRAME_STATUS_FAIL = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_frame_info - Attributes used within the
+ * %QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO nested attribute.
+ */
+enum qca_wlan_vendor_attr_roam_stats_frame_info {
+	/* 8-bit unsigned value to indicate the frame subtype during
+	 * roaming, one of the values in qca_wlan_roam_stats_frame_subtype.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_SUBTYPE = 1,
+	/* 8-bit unsigned value to indicate the frame is successful or failed
+	 * during roaming, one of the values in
+	 * qca_wlan_roam_stats_frame_status.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_STATUS = 2,
+	/* 64-bit unsigned value to indicate the timestamp for frame of
+	 * preauthentication/reassociation/EAPOL-M1/EAPOL-M2/EAPOL-M3/EAPOL-M4
+	 * when sent or received during roaming, timestamp in milliseconds
+	 * from system boot.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_TIMESTAMP = 3,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_PAD = 4,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_stats_info - Used by the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO.
+ */
+enum qca_wlan_vendor_attr_roam_stats_info {
+	/* 64-bit unsigned value to indicate the timestamp when roam was
+	 * triggered by the firmware, timestamp in milliseconds from system
+	 * boot.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_TRIGGER_TIMESTAMP = 1,
+	/* 32-bit unsigned value to indicate the roam trigger reason for the
+	 * last roaming attempted by the firmware. This can be queried either
+	 * in a connected state or disconnected state. The values of this
+	 * attribute represent the roam trigger reason codes, which
+	 * are defined in enum qca_roam_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TRIGGER_REASON = 2,
+	/* 8-bit unsigned value to indicate percentage of packets for which
+	 * the RX rate is lower than the RX rate threshold in total RX packets,
+	 * used for roaming trigger by per.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_RXRATE_THRESHOLD_PERCENT = 3,
+	/* 8-bit unsigned value to indicate percentage of packets for which
+	 * the TX rate is lower than TX rate threshold in total TX packets,
+	 * used for roaming trigger by per.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PER_TXRATE_THRESHOLD_PERCENT = 4,
+	/* 32-bit unsigned value to indicate final beacon miss count for
+	 * trigger reason of beacon miss.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FINAL_BMISS_CNT = 5,
+	/* 32-bit unsigned value to indicate consecutive beacon miss count
+	 * for trigger reason of beacon miss.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONSECUTIVE_BMISS_CNT = 6,
+	/* 8-bit unsigned value to indicate QOS-NULL TX status for trigger
+	 * reason of beacon miss, 0 - success, 1 - fail.
+	 * If QOS-NULL TX status is successful, beacon miss final count and
+	 * consecutive beacon miss count will be reset to zero, and roam will
+	 * not be triggered. If QOS-NULL TX status is failed, beacon miss final
+	 * count and consecutive beacon miss count continue to calculate until
+	 * roaming trigger by beacon miss.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BMISS_QOS_NULL_SUCCESS = 7,
+	/* 8-bit signed value to indicate connected AP RSSI in dBm
+	 * for trigger reason of poor RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_CURRENT_RSSI = 8,
+	/* 8-bit signed value to indicate RSSI threshold value in dBm
+	 * for trigger reason of poor RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_ROAM_RSSI_THRESHOLD = 9,
+	/* 8-bit unsigned value to indicate RX link speed status
+	 * for trigger reason of poor RSSI, 0 - good link speed,
+	 * 1 - bad link speed.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_POOR_RSSI_RX_LINKSPEED_STATUS = 10,
+	/* 8-bit signed value to indicate connected AP RSSI in dBm
+	 * for trigger reason of better RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_CURRENT_RSSI = 11,
+	/* 8-bit signed value to indicate RSSI threshold value in dBm
+	 * for trigger reason of better RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BETTER_RSSI_HIGH_RSSI_THRESHOLD = 12,
+	/* 32-bit unsigned value to indicate RX throughput in bytes per second
+	 * for trigger reason of congestion.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_RX_TPUT = 13,
+	/* 32-bit unsigned value to indicate TX throughput in bytes per second
+	 * for trigger reason of congestion.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_TX_TPUT = 14,
+	/* 8-bit unsigned value to indicate roamable AP count
+	 * for trigger reason of congestion.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_CONGESTION_ROAMABLE_CNT = 15,
+	/* 8-bit unsigned value to indicate invoke reason, one of the values
+	 * defined in qca_wlan_roam_stats_invoke_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_USER_TRIGGER_INVOKE_REASON = 16,
+	/* 8-bit unsigned value to indicate request mode for trigger reason
+	 * of BTM, values are defined in IEEE Std 802.11-2020, 9.6.13.9.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQUEST_MODE = 17,
+	/* 32-bit unsigned value to indicate disassociate time in milliseconds
+	 * for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_DISASSOC_IMMINENT_TIME = 18,
+	/* 32-bit unsigned value to indicate preferred candidate list valid
+	 * interval in milliseconds for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_VALID_INTERNAL = 19,
+	/* 8-bit unsigned value to indicate the number of preferred
+	 * candidates for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_CANDIDATE_LIST_CNT = 20,
+	/* 8-bit unsigned value to indicate response status for trigger
+	 * reason of BTM, values are defined in IEEE Std 802.11-2020,
+	 * Table 9-428 (BTM status code definitions).
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_RESPONSE_STATUS_CODE = 21,
+	/* 32-bit unsigned value to indicate BSS termination timeout value
+	 * in milliseconds for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_BSS_TERMINATION_TIMEOUT = 22,
+	/* 32-bit unsigned value to indicate MBO associate retry timeout
+	 * value in milliseconds for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_MBO_ASSOC_RETRY_TIMEOUT = 23,
+	/* 8-bit unsigned value to indicate dialog token number
+	 * for trigger reason of BTM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BTM_REQ_DIALOG_TOKEN = 24,
+	/* 8-bit unsigned value to indicate percentage of connected AP
+	 * channel congestion utilization for trigger reason of BSS load.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BSS_CU_LOAD = 25,
+	/* 8-bit unsigned value to indicate disconnection type
+	 * for trigger reason of disconnection. 1 - Deauthentication,
+	 * 2 - Disassociation.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_TYPE = 26,
+	/* 16-bit unsigned value to indicate deauthentication or disassociation
+	 * reason for trigger reason of disconnection, values are defined
+	 * in IEEE Std 802.11-2020, Table 9-49 (Reason codes).
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DISCONNECTION_REASON = 27,
+	/* 32-bit unsigned value to indicate milliseconds of roam scan
+	 * periodicity when needing to roam to find a better AP for trigger
+	 * reason of periodic timer.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PERIODIC_TIMER_MS = 28,
+	/* 8-bit signed value to indicate connected AP RSSI in dBm for
+	 * trigger reason of background scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_CURRENT_RSSI = 29,
+	/* 8-bit signed value to indicate data RSSI in dBm for trigger reason
+	 * of background scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI = 30,
+	/* 8-bit signed value to indicate data RSSI threshold in dBm
+	 * for trigger reason of background scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_BACKGROUND_SCAN_DATA_RSSI_THRESH = 31,
+	/* 32-bit unsigned value to indicate consecutive TX failure threshold
+	 * for trigger reason of TX failures.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_THRESHOLD = 32,
+	/* 8-bit unsigned value to indicate TX failure reason for trigger
+	 * reason of TX failures, one of the values defined in
+	 *  qca_wlan_roam_stats_tx_failures_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TX_FAILURES_REASON = 33,
+	/* 8-bit unsigned value to indicate detail abort reason. One of the
+	 * values in enum qca_wlan_roam_stats_abort_reason.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ABORT_REASON = 34,
+	/* 8-bit signed value to indicate data RSSI in dBm when aborting the
+	 * roam scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI = 35,
+	/* 8-bit signed value to indicate data RSSI threshold in dBm when
+	 * aborting the roam scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI_THRESHOLD = 36,
+	/* 8-bit unsigned value to indicate data RSSI threshold in RX link
+	 * speed status when aborting the roam scan.
+	 * 0 - good link speed, 1 - bad link speed
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RX_LINKSPEED_STATUS = 37,
+	/* 8-bit unsigned value to indicate roaming scan type.
+	 * One of the values in enum qca_wlan_roam_stats_scan_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_TYPE = 38,
+	/* 8-bit unsigned value to indicate roaming result, used in STA mode
+	 * only.
+	 * 0-Roaming is successful, 1-Roaming is failed
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_ROAM_STATUS = 39,
+	/* 8-bit unsigned value to indicate the roam fail reason for the
+	 * last failed roaming attempt by the firmware. Different roam failure
+	 * reason codes are specified in enum qca_vendor_roam_fail_reasons.
+	 * This can be queried either in connected state or disconnected state.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FAIL_REASON = 40,
+	/* Nested attribute. Indicate roam scan info for each channel, the
+	 * attributes defined in enum
+	 * qca_wlan_vendor_attr_roam_stats_scan_chan_info are used inside
+	 * this attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_SCAN_CHAN_INFO = 41,
+	/* 32-bit unsigned value to indicate total scan time during roam scan
+	 * all channels, time in milliseconds.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_TOTAL_SCAN_TIME = 42,
+	/* Nested attribute. This attribute shall be used by the driver to
+	 * send roam information of each subtype. The attributes defined in
+	 * enum qca_wlan_vendor_attr_roam_stats_frame_info are used inside
+	 * this attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_FRAME_INFO = 43,
+	/* Attribute used for padding for 64-bit alignment */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_PAD = 44,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_cached_stats - Vendor subcmd attributes to
+ * report cached roam info from the driver to user space, enum values are used
+ * for netlink attributes sent with the
+ * %QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS sub command.
+ */
+enum qca_wlan_vendor_attr_roam_cached_stats {
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INVALID = 0,
+	/* Nested attribute, this attribute contains nested array roam info
+	 * statistics defined in enum qca_wlan_vendor_attr_roam_stats_info.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_INFO = 1,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_CACHED_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_supported_radio_cfg - Attributes for
+ * radio configurations present in each radio combination.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND: u32 attribute indicates
+ * the band info in the radio configuration. Uses the enum qca_set_band values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA: u8 attribute indicates
+ * the number of antennas info in the radio configuration.
+ */
+enum qca_wlan_vendor_attr_supported_radio_cfg {
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_BAND = 1,
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_ANTENNA = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_LAST,
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_MAX =
+	QCA_WLAN_VENDOR_ATTR_SUPPORTED_RADIO_CFG_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_radio_combination - Attributes for
+ * radio combinations supported by the device.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_CFGS: Nested attribute
+ * provides the radio configurations present in the radio combination.
+ * Uses the enum qca_wlan_vendor_attr_supported_radio_cfg attributes.
+ * This attribute provides the values for radio combination matrix.
+ * For standalone config, the number of config values is one and the config
+ * carries the band and antenna information for standalone configuration. For
+ * Dual Band Simultaneous (DBS)/Single Band Simultaneous (SBS) mode
+ * configuration the number of config values is two and the config carries the
+ * band and antenna information for each simultaneous radio.
+ */
+enum qca_wlan_vendor_attr_radio_combination {
+	QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_CFGS = 1,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_LAST,
+	QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_MAX =
+	QCA_WLAN_VENDOR_ATTR_RADIO_COMBINATIONS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_radio_combination_matrix - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_GET_RADIO_COMBINATION_MATRIX
+ *
+ * @QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_SUPPORTED_CFGS: Nested attribute
+ * provides the radio combinations supported by the device.
+ * Uses the enum qca_wlan_vendor_attr_radio_combination attributes.
+ * For example, in the radio combination matrix for a device which has two
+ * radios, where one radio is capable of 2.4 GHz 2X2 only and another radio is
+ * capable of either 5 GHz or 6 GHz 2X2, the possible number of radio
+ * combinations is 5 and the radio combinations are
+ * {{{2.4 GHz 2X2}}, //Standalone 2.4 GHz
+ * {{5 GHz 2X2}}, //Standalone 5 GHz
+ * {{6 GHz 2X2}}, //Standalone 6 GHz
+ * {{2.4 GHz 2X2}, {5 GHz 2X2}}, //2.4 GHz + 5 GHz DBS
+ * {{2.4 GHz 2X2}, {6 GHz 2X2}}} //2.4 GHz + 6 GHz DBS
+ * The band and antenna info together as nested data provides one radio config.
+ * Standalone configuration has one config with band and antenna nested data.
+ * Dual Band Simultaneous (DBS)/Single Band Simultaneous (SBS) configuration
+ * have two nested band and antenna info data.
+ */
+enum qca_wlan_vendor_attr_radio_combination_matrix {
+	QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_SUPPORTED_CFGS = 1,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_LAST,
+	QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_MAX =
+	QCA_WLAN_VENDOR_ATTR_RADIO_MATRIX_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_mdns_offload - Attributes used by
  * %QCA_NL80211_VENDOR_SUBCMD_MDNS_OFFLOAD vendor command.
  *
@@ -12198,4 +13375,1746 @@
 	QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_audio_data_path - Defines the data path to be used for audio
+ * traffic.
+ *
+ * @QCA_WLAN_AUDIO_DATA_PATH_VIA_HOST_PROCESSOR:
+ * Send audio traffic through the host processor.
+ * @QCA_WLAN_AUDIO_DATA_PATH_VIA_LOW_POWER_DSP:
+ * Send audio traffic using the low power DSP to/from the encoder.
+ */
+enum qca_wlan_audio_data_path {
+	QCA_WLAN_AUDIO_DATA_PATH_VIA_HOST_PROCESSOR = 0,
+	QCA_WLAN_AUDIO_DATA_PATH_VIA_LOW_POWER_DSP = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_pasn_action - Action to authenticate (and generate keys
+ *	for) or drop existing PASN security association for the listed the
+ *	peers. Used by QCA_WLAN_VENDOR_ATTR_PASN_ACTION and sent from the driver
+ *	to userspace.
+ *
+ * @QCA_WLAN_VENDOR_PASN_ACTION_AUTH: Initiate PASN handshake with the peer
+ *	devices indicated with %QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR.
+ * @QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT: Indication from
+ *	the driver to userspace to inform that the existing PASN keys of the
+ *	peer devices specified with %QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR are
+ *	not valid anymore.
+ */
+enum qca_wlan_vendor_pasn_action {
+	QCA_WLAN_VENDOR_PASN_ACTION_AUTH,
+	QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_pasn_peer: Defines the nested attributes used in
+ *	QCA_WLAN_VENDOR_ATTR_PASN_PEERS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR: This attribute is optional in the
+ *	event from the driver to userspace and represents the local MAC address
+ *	to be used for PASN handshake. When this attribute is present, userspace
+ *	shall use the source address specified in this attribute by the driver
+ *	for PASN handshake with peer device.
+ *	This attribute is required in a command response from userspace to the
+ *	driver and represents the MAC address that was used in PASN handshake
+ *	with the peer device.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR: Indicates the MAC address of the
+ *	peer device to which PASN handshake is requested in an event from the
+ *	driver to userspace when QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	QCA_WLAN_VENDOR_PASN_ACTION_AUTH.
+ *	Indicates the MAC address of the peer device for which the keys are to
+ *	be invalidated in an event from the driver to userspace when
+ *	QCA_WLAN_VENDOR_ATTR_PASN_ACTION is set to
+ *	QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT.
+ *	Indicates the MAC address of the peer device for which the status is
+ *	being sent in a status report from userspace to the driver.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED: NLA_FLAG attribute used
+ *	in the event from the driver to userspace. When set, userspace is
+ *	required to derive LTF key seed from KDK and set it to the driver.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS: NLA_FLAG attribute. This
+ *	attribute is used in the command response from userspace to the driver.
+ *	If present, it indicates the successful PASN handshake with the peer. If
+ *	this flag is not present, it indicates that the PASN handshake with the
+ *	peer device failed.
+ */
+enum qca_wlan_vendor_attr_pasn_peer {
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR = 2,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED = 3,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS = 4,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX =
+	QCA_WLAN_VENDOR_ATTR_PASN_PEER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_pasn: Defines the attributes used in the
+ *	QCA_NL80211_VENDOR_SUBCMD_PASN command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PASN_ACTION: u32 attribute, possible values are
+ *	defined in enum qca_wlan_vendor_pasn_action and used only in an event
+ *	from the driver to userspace.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_PEERS: Nested attribute, used to pass PASN peer
+ *	details for each peer and used in both an event and a command response.
+ *	The nested attributes used inside QCA_WLAN_VENDOR_ATTR_PASN_PEERS are
+ *	defined in enum qca_wlan_vendor_attr_pasn_peer.
+ */
+enum qca_wlan_vendor_attr_pasn {
+	QCA_WLAN_VENDOR_ATTR_PASN_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_PASN_ACTION = 1,
+	QCA_WLAN_VENDOR_ATTR_PASN_PEERS = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_PASN_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PASN_MAX =
+	QCA_WLAN_VENDOR_ATTR_PASN_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_secure_ranging_ctx_action - Used to add or delete
+ *	the ranging security context derived from PASN for each peer. Used in
+ *	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION.
+ *
+ * @QCA_WLAN_VENDOR_SECURE_RANGING_CTX_ACTION_ADD: Add the secure ranging
+ *	context for the peer.
+ * @QCA_WLAN_VENDOR_SECURE_RANGING_CTX_ACTION_DELETE: Delete the secure ranging
+ *	context for the peer.
+ */
+enum qca_wlan_vendor_secure_ranging_ctx_action {
+	QCA_WLAN_VENDOR_SECURE_RANGING_CTX_ACTION_ADD,
+	QCA_WLAN_VENDOR_SECURE_RANGING_CTX_ACTION_DELETE,
+};
+
+/**
+ * enum qca_wlan_vendor_sha_type - SHA types. Used to configure the SHA type
+ *	used for deriving PASN keys to the driver. Used in
+ *	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE
+ * @QCA_WLAN_VENDOR_SHA_256: SHA-256
+ * @QCA_WLAN_VENDOR_SHA_384: SHA-384
+ */
+enum qca_wlan_vendor_sha_type {
+	QCA_WLAN_VENDOR_SHA_256,
+	QCA_WLAN_VENDOR_SHA_384,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_secure_ranging_ctx: Defines the attributes used
+ *	to set security context for the PASN peer from userspace to the driver.
+ *	Used with QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION: u32 attribute, possible
+ *	values are defined in enum qca_wlan_vendor_secure_ranging_ctx_action
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR: The local MAC address that
+ *	was used during the PASN handshake.
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR: The MAC address of
+ *	the peer device for which secure ranging context is being configured.
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE: u32 attribute, defines the
+ *	hash algorithm to be used, possible values are defined in enum
+ *	qca_wlan_vendor_sha_type.
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK: Variable length attribute, holds
+ *	the temporal key generated from the PASN handshake. The length of this
+ *	attribute is dependent on the value of
+ *	%QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER.
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER: cipher suite to use with the
+ *	TK, u32, as defined in IEEE Std 802.11-2020, 9.4.2.24.2 (Cipher suites)
+ *	(e.g., 0x000FAC04).
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED: Variable length
+ *	attribute, holds the LTF keyseed derived from KDK of PASN handshake.
+ *	The length of this attribute is dependent on the value of
+ *	%QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE.
+
+ */
+enum qca_wlan_vendor_attr_secure_ranging_ctx {
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION = 1,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR = 2,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR = 3,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE = 4,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK = 5,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER = 6,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED = 7,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_MAX =
+	QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_filter - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER
+ * nested attribute. The packets that match a filter will be replied with
+ * attributes configured in %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4:
+ * u32 attribute. Destination IPv4 address in network byte order, the
+ * IPv4 packets with different address will be filtered out.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4_IS_BC:
+ * Flag attribute. If it's present, indicates that
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 is a broadcast
+ * address; while if not, indicates that the address is a unicast/multicast
+ * address.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Destination IPv6 address in network byte order, the IPv6 packets
+ * with different destination address will be filtered out.
+ * This attribute is optional.
+ *
+ * At least one of %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6 must be configured.
+ * Packets on both IPv4 and IPv6 will be processed if both are configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_PORT:
+ * u16 attribute. Destination UDP port, the packets with different destination
+ * UDP port will be filtered out.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET:
+ * u32 attribute. Represents the offset (in UDP payload) of the data
+ * to be matched.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_DATA:
+ * NLA_BINARY attribute, the maximum allowed size is 16 bytes.
+ * Binary data that is compared bit-by-bit against the data (specified
+ * by %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET) in UDP
+ * payload, the packets don't match will be filtered out.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_filter {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4_IS_BC = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6 = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_PORT = 4,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET = 5,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_DATA = 6,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_reply - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4:
+ * u32 attribute. Source address (in network byte order) for replying
+ * the matching broadcast/multicast IPv4 packets.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source address (in network byte order) for replying the matching
+ * multicast IPv6 packets.
+ * This attribute is optional.
+ *
+ * For broadcast/multicast offload reply, one of
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6 or both must be
+ * configured according to version of the IP address(es) configured in
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER;
+ * while for unicast case, firmware will take the destination IP address
+ * in the received matching packet as the source address for replying.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER:
+ * Nested attribute. Filter for the received UDP packets, only the matching
+ * packets will be replied and cached.
+ * See enum qca_wlan_vendor_attr_coap_offload_filter for list of supported
+ * attributes.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * CoAP message (UDP payload) to be sent upon receiving matching packets.
+ * Firmware is responsible for adding any necessary protocol headers.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_CACHE_EXPTIME:
+ * u32 attribute. Expiration time in milliseconds of the cached CoAP messages.
+ * A cached message will be dropped by firmware if it's expired.
+ * This attribute is optional. A default value of 40000 will be used in the
+ * absence of it.
+ */
+enum qca_wlan_vendor_attr_coap_offload_reply {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4 = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6 = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MSG = 4,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_CACHE_EXPTIME = 5,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 - Represents parameters for
+ * CoAP message (UDP) transmitting on IPv4.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_ADDR:
+ * u32 attribute. Source address (in network byte order) for transmitting
+ * packets on IPv4.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_PORT:
+ * u16 attribute. Source UDP port.
+ * This attribute is optional, a random port is taken if it's not present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR:
+ * u32 attribute. Destination IPv4 address (in network byte order).
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_IS_BC:
+ * Flag attribute. If it's present, indicates that
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR is a broadcast
+ * address; while if not, indicates that the address is unicast/multicast
+ * address.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_PORT:
+ * u16 attribute. Destination UDP port.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_ADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_PORT = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_IS_BC = 4,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_PORT = 5,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 - Represents parameters for
+ * CoAP message (UDP) transmitting on IPv6.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_ADDR:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source address (in network byte order) for transmitting packets on IPv6.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_PORT:
+ * u16 attribute. Source UDP port.
+ * This attribute is optional, a random port is taken if it's not present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_ADDR:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Destination IPv6 address (in network byte order).
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_PORT:
+ * u16 attribute. Destination UDP port.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_ADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_PORT = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_ADDR = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_PORT = 4,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_periodic_tx - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4:
+ * Nested attribute. The IPv4 source/destination address/port for offload
+ * transmitting. See enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 for the list
+ * of supported attributes.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6:
+ * Nested attribute. The IPv6 source/destination address/port for offload
+ * transmitting. See enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 for the list
+ * of supported attributes.
+ * This attribute is optional.
+ *
+ * At least one of %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6 must be configured.
+ * Firmware will transmit the packets on both IPv4 and IPv6 if both are
+ * configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_PERIOD:
+ * u32 attribute. Period in milliseconds for the periodic transmitting.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * CoAP message (UDP payload) to be periodically transmitted. Firmware
+ * is responsible for adding any necessary protocol headers.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_periodic_tx {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4 = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6 = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_PERIOD = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MSG = 4,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_cache_info - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS:
+ * u64 attribute. Received time (since system booted in microseconds) of
+ * the cached CoAP message.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4:
+ * u32 attribute. Source IPv4 address (in network byte order) of the cached
+ * CoAP message.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source IPv6 address (in network byte order) of the cached CoAP message.
+ * This attribute is optional.
+ *
+ * At most and at least one of
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6 is given for
+ * an entry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * The cached CoAP message (UDP payload). If the actual message size is
+ * greater than the maximum size, it will be truncated and leaving only
+ * the first 1152 bytes.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_PAD: Attribute used for
+ * padding for 64-bit alignment
+ */
+enum qca_wlan_vendor_attr_coap_offload_cache_info {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6 = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG = 4,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_PAD = 5,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_coap_offload_action - Actions for
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE:
+ * Enable CoAP offload reply.
+ * If it's enabled, firmware will start offload processing on each suspend
+ * and stop on each resume.
+ *
+ * Offload reply on match works as follows:
+ * Reply the packets that match the filter with the given CoAP
+ * message (with necessary protocol headers), increase the CoAP message
+ * ID in the given CoAP message by one for the next use after each successful
+ * transmission, and try to store the information of the received packet,
+ * including the received time, source IP address, and CoAP message (UDP
+ * payload).
+ *
+ * Firmware has a limit to the maximum stored entries, it takes the source IP
+ * address as the key of an entry, and keeps at most one entry for each key.
+ * A packet won't be stored if no entry for the same key is present and the
+ * total number of the existing unexpired entries reaches the maximum value.
+ *
+ * If any configured item is changed, user space should disable offload reply
+ * first and then issue a new enable request.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE:
+ * Disable CoAP offload reply and return information of any cached CoAP
+ * messages.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE:
+ * Enable CoAP offload periodic transmitting.
+ * If it's enabled, firmware will start offload periodic transmitting on
+ * each suspend and stop on each resume.
+ *
+ * Offload periodic transmitting works as follows:
+ * Send the given CoAP message (with necessary protocol headers) with the given
+ * source/destination IP address/UDP port periodically based on the given
+ * period and increase the CoAP message ID in the given CoAP message by one
+ * for the next use after each successful transmission.
+ *
+ * If any configured item is changed, user space should disable offload
+ * periodic transmitting first and then issue a new enable request.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE:
+ * Disable CoAP offload periodic transmitting.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET:
+ * Get information of the CoAP messages cached during offload reply
+ * processing. The cache is cleared after retrieval.
+ */
+enum qca_wlan_vendor_coap_offload_action {
+	QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE = 0,
+	QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE = 1,
+	QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE = 2,
+	QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE = 3,
+	QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD.
+ * This is used to set parameters for CoAP offload processing, or get
+ * cached CoAP messages from firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION:
+ * u32 attribute. Action to take in this vendor command.
+ * See enum qca_wlan_vendor_coap_offload_action for supported actions.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REQ_ID:
+ * u32 attribute. Represents the Request ID for the CoAP offload
+ * configuration, which can help to identify the user entity starting
+ * the CoAP offload processing and accordingly stop the respective
+ * ones/get the cached CoAP messages with the matching ID.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY:
+ * Nested attribute. Parameters for offload reply.
+ * See enum qca_wlan_vendor_attr_coap_offload_reply for the list of
+ * supported attributes.
+ * This attribute is mandatory if %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION
+ * is QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE, and is ignored
+ * otherwise.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX:
+ * Nested attribute. Parameters for offload periodic transmitting.
+ * See enum qca_wlan_vendor_attr_coap_offload_periodic_tx for the list of
+ * supported attributes.
+ * This attribute is mandatory if %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION is
+ * QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE, and is ignored
+ * otherwise.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES:
+ * Array of nested attributes. Information of the cached CoAP messages,
+ * where each entry is taken from
+ * enum qca_wlan_vendor_attr_coap_offload_cache_info.
+ * This attribute is used for reporting the cached CoAP messages
+ * in reply for command in which %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION
+ * is QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET or
+ * QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE. It means there is no
+ * cached item if this attribute is not present.
+ */
+enum qca_wlan_vendor_attr_coap_offload {
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION = 1,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REQ_ID = 2,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY = 3,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX = 4,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES = 5,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_MAX =
+	QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scs_rule_config - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG to configure Stream Classification
+ * Service (SCS) rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_RULE_ID: Mandatory u32 attribute.
+ * Represents the unique id of SCS rule to be configured.
+
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_REQUEST_TYPE: Mandatory u8 attribute.
+ * Represents the request type: add, remove, or change.
+ * Values as defined in IEEE Std 802.11-2020, Table 9-246 (SCS Request
+ * Type definitions).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_OUTPUT_TID: Mandatory u8 attribute
+ * in case of add/change request type.
+ * Represents the output traffic identifier (TID) to be assigned to the flow
+ * matching the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_CLASSIFIER_TYPE: Mandatory u8
+ * attribute in case of add/change request type.
+ * Represents type of classifier parameters present in SCS rule.
+ * Refer IEEE Std 802.11-2020 Table 9-164 (Frame classifier type).
+ * Only classifier types 4 and 10 are supported for SCS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_VERSION: Mandatory u8 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the IP version (4: IPv4, 6: IPv6) of the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV4_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv4.
+ * Represents the source IPv4 address in the rule which is to be compared
+ * against the source IP address in the IPv4 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV4_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv4.
+ * Represents the destination IPv4 address in the rule which is to be compared
+ * against the destination IP address in the IPv4 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV6_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv6.
+ * Represents the source IPv6 address in the rule which is to be compared
+ * against the source IP address in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV6_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv6.
+ * Represents the destination IPv6 address in the rule which is to be compared
+ * against the destination IP address in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_PORT: Optional u16 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the source port number in the rule which is to be compared against
+ * the source port number in the protocol header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_PORT: Optional u16 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the destination port number in the rule which is to be compared
+ * against the destination port number in the protocol header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DSCP: Optional u8 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the DSCP value in the rule which is to be compared against the
+ * DSCP field present in the IP header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_NEXT_HEADER: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS4.
+ * Represents the protocol/next header in the rule which is to be compared
+ * against the protocol/next header field present in the IPv4/IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_FLOW_LABEL: Optional
+ * attribute of size 3 bytes present in case of add/change request type
+ * when classifier type is TCLAS4 and version is IPv6.
+ * Represents the flow label value in the rule which is to be compared against
+ * the flow label field present in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_PROTOCOL_INSTANCE: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS10.
+ * Represents the protocol instance number in the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS10.
+ * Represents the protocol/next header in the rule which is to be compared
+ * against the protocol/next header field present in the IPv4/IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK: Optional
+ * attribute of variable length present when request type is add/change and
+ * classifier type is TCLAS10.
+ * Represents the mask to be used for masking the header contents of the header
+ * specified by QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER
+ * attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE: Optional
+ * attribute of variable length present when request type is add/change and
+ * classifier type is TCLAS10.
+ * Represents the value to be compared against after masking the header contents
+ * of the header specified by the
+ * QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER attribute with the
+ * filter mask specified by the
+ * QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID: Optional u16
+ * attribute.
+ * Represents the service class id of the configured SCS rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR: Optional 6 bytes
+ * MAC address.
+ * Represents the destination MAC address in the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX: Optional u32 attribute
+ * Represents the netdevice interface index in the rule.
+ */
+enum qca_wlan_vendor_attr_scs_rule_config {
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_RULE_ID = 1,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_REQUEST_TYPE = 2,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_OUTPUT_TID = 3,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_CLASSIFIER_TYPE = 4,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_VERSION = 5,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV4_ADDR = 6,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV4_ADDR = 7,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV6_ADDR = 8,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV6_ADDR = 9,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_PORT = 10,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_PORT = 11,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DSCP = 12,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_NEXT_HEADER = 13,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_FLOW_LABEL = 14,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_PROTOCOL_INSTANCE = 15,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER = 16,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK = 17,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE = 18,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID = 19,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR = 20,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX = 21,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mlo_links - Definition of attributes used inside
+ * nested attribute QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID: u8 attribute, link ID of this MLO link.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR: Own MAC address of this MLO link.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID: AP link MAC address of this MLO link.
+ */
+enum qca_wlan_vendor_attr_mlo_links {
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID = 1,
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR = 2,
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID = 3,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX =
+	QCA_WLAN_VENDOR_ATTR_MLO_LINK_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_sar_version - This describes the current SAR version
+ * used in the firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_1: The firmware supports legacy SAR.
+ * In legacy SAR, the firmware supports 5 static and 1 user defined SAR limits.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_2: The firmware supports SAR version 2,
+ * i.e., SAR Non DBS mode. In SAR version 2, the firmware has 6 SAR tables for
+ * each CTL group. So user can select up to 6 SAR indexes from the current CTL
+ * groups.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_3: The firmware supports SAR version 3,
+ * i.e., SAR DBS mode. In SAR version 3, the firmware has 6 SAR tables for each
+ * CTL group but user can choose up to 3 SAR set index only, as the top half
+ * of the SAR index (0 to 2) is used for non DBS purpose and the bottom half of
+ * the SAR index (3 to 5) is used for DBS mode.
+ */
+enum qca_wlan_vendor_sar_version {
+	QCA_WLAN_VENDOR_SAR_VERSION_INVALID = 0,
+	QCA_WLAN_VENDOR_SAR_VERSION_1 = 1,
+	QCA_WLAN_VENDOR_SAR_VERSION_2 = 2,
+	QCA_WLAN_VENDOR_SAR_VERSION_3 = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_sar_ctl_group_state - This describes whether
+ * CTL grouping is enabled or disabled in the firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_ENABLED: CTL grouping
+ * is enabled in firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_DISABLED: CTL grouping
+ * is disabled in firmware.
+ *
+ */
+enum qca_wlan_vendor_sar_ctl_group_state {
+	QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_INVALID = 0,
+	QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_ENABLED = 1,
+	QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_DISABLED = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sar_capability - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY to get SAR capabilities
+ * supported by the firmware.
+
+ * @QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION:
+ * u32 attribute. This field describes current SAR version supported by the
+ * firmware.
+ * See enum qca_wlan_vendor_sar_version for more information.
+ * This attribute is mandatory.
+
+ * @QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_CTL_GROUP_STATE:
+ * u32 attribute. This field describes whether CTL groups are enabled
+ * or disabled in the firmware.
+ * See enum qca_wlan_vendor_sar_ctl_group_state for more information.
+ * This attribute is optional.
+ */
+
+enum qca_wlan_vendor_attr_sar_capability {
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION = 1,
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_CTL_GROUP_STATE = 2,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX =
+	QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr_stats - Attributes for Spatial Reuse statistics.
+ * These statistics are sent from the driver in a response when userspace
+ * queries to get the statistics using the operation
+ * %QCA_WLAN_SR_OPERATION_GET_STATS. These statistics are reset
+ * by the driver when the SR feature is enabled, when the driver receives
+ * %QCA_WLAN_SR_OPERATION_CLEAR_STATS operation, or when disconnected.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_OPPORTUNITIES_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG TX opportunities.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_TRIED_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG PPDUs tried to transmit.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_SUCCESS_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG PPDUs successfully transmitted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_OPPORTUNITIES_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG TX opportunities.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_TRIED_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG PPDUs tried to transmit.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_SUCCESS_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG PPDUs successfully transmitted.
+ */
+enum qca_wlan_vendor_attr_sr_stats {
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_OPPORTUNITIES_COUNT = 1,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_TRIED_COUNT = 2,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_SUCCESS_COUNT = 3,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_OPPORTUNITIES_COUNT = 4,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_TRIED_COUNT = 5,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_SUCCESS_COUNT = 6,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_MAX =
+	QCA_WLAN_VENDOR_ATTR_SR_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_sr_reason_code - Defines the different reason codes used in
+ * Spatial Reuse feature.
+ *
+ * @QCA_WLAN_SR_REASON_CODE_ROAMING: The SR feature is disabled/enabled due to
+ * roaming to an AP that doesn't support/supports SR feature, respectively.
+ *
+ * @QCA_WLAN_SR_REASON_CODE_CONCURRENCY: The SR feature is disabled/enabled due
+ * to change in concurrent interfaces that are supported by the driver.
+ */
+enum qca_wlan_sr_reason_code {
+	QCA_WLAN_SR_REASON_CODE_ROAMING = 0,
+	QCA_WLAN_SR_REASON_CODE_CONCURRENCY = 1,
+};
+
+/**
+ * enum qca_wlan_sr_operation - Defines the different types of SR operations.
+ * The values are used inside attribute %QCA_WLAN_VENDOR_ATTR_SR_OPERATION.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_ENABLE: Userspace sends this operation to the
+ * driver to enable the Spatial Reuse feature. Attributes
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD and
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD are used with this
+ * operation.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_DISABLE: Userspace sends this operation to the
+ * driver to disable the Spatial Reuse feature.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_SUSPEND: The driver uses this operation in an
+ * asynchronous event sent to userspace when the SR feature is disabled.
+ * The disable reason is encoded in QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE
+ * and sent along with the asynchronous event.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_RESUME: The driver uses this operation in an
+ * asynchronous event when the SR feature is enabled again after the SR feature
+ * was suspended by the driver earlier. The enable reason is
+ * encoded in QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE. Attributes used are
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD and
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD.
+ *
+ * @QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT: This operation is
+ * used to prohibit PSR-based spatial reuse and non-SRG OBSS PD-based spatial
+ * reuse transmissions. Userspace sends this operation to the driver.
+ * The driver/firmware upon receiving this operation shall prohibit PSR-based
+ * spatial reuse and non-SRG OBSS PD-based spatial reuse transmissions.
+ *
+ * @QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_ALLOW: This operation is
+ * used to allow PSR-based spatial reuse and non-SRG OBSS PD-based spatial
+ * reuse transmissions. Userspace sends this operation to the driver.
+ * The driver/firmware upon receiving this operation shall allow PSR-based
+ * spatial reuse and non-SRG OBSS PD-based spatial reuse transmissions.
+ *
+ * @QCA_WLAN_SR_OPERATION_GET_STATS: Userspace sends this operation to the
+ * driver to get the SR statistics and the driver sends a synchronous response
+ * with the attributes defined in enum qca_wlan_vendor_attr_sr_stats using the
+ * nested attribute %QCA_WLAN_VENDOR_ATTR_SR_STATS.
+ *
+ * @QCA_WLAN_SR_OPERATION_CLEAR_STATS: Userspace sends this operation to the
+ * driver to clear the SR statistics and upon receiving this operation
+ * the driver/firmware shall clear the SR statistics.
+ *
+ * @QCA_WLAN_SR_OPERATION_GET_PARAMS: Userspace sends this operation to the
+ * driver to get the SR parameters and the driver sends the synchronous response
+ * with the following required attributes:
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW.
+ *
+ * @QCA_WLAN_SR_OPERATION_UPDATE_PARAMS: The driver uses this operation in an
+ * asynchronous event to userspace to update any changes in SR parameters.
+ * The following attributes are used with this operation:
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW.
+ */
+enum qca_wlan_sr_operation {
+	QCA_WLAN_SR_OPERATION_SR_ENABLE = 0,
+	QCA_WLAN_SR_OPERATION_SR_DISABLE = 1,
+	QCA_WLAN_SR_OPERATION_SR_SUSPEND = 2,
+	QCA_WLAN_SR_OPERATION_SR_RESUME = 3,
+	QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT = 4,
+	QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_ALLOW = 5,
+	QCA_WLAN_SR_OPERATION_GET_STATS = 6,
+	QCA_WLAN_SR_OPERATION_CLEAR_STATS = 7,
+	QCA_WLAN_SR_OPERATION_GET_PARAMS = 8,
+	QCA_WLAN_SR_OPERATION_UPDATE_PARAMS = 9,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr_params - Defines attributes for SR configuration
+ * parameters used by attribute %QCA_WLAN_VENDOR_ATTR_SR_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE: Flag attribute.
+ * This attribute is optionally set in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS and in request when operation is set to
+ * %QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT. Refer IEEE Std
+ * 802.11ax-2021 Figure 9-788r-SR Control field format to understand more
+ * about HESIGA_Spatial_reuse_value15_allowed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW: Flag attribute.
+ * This attribute is used in response to %QCA_WLAN_SR_OPERATION_GET_PARAMS
+ * operation. This indicates whether non-SRG OBSS PD SR transmissions are
+ * allowed or not at non-AP STAs that are associated with the AP. If present
+ * non-SRG OBSS PD SR transmissions are not allowed else are allowed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the SRG OBSS PD
+ * Min Offset field which contains an unsigned integer that is added to -82 dBm
+ * to generate the value of the SRG OBSS PD Min parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the SRG OBSS PD
+ * Max Offset field which contains an unsigned integer that is added to -82 dBm
+ * to generate the value of the SRG OBSS PD Max parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the Non-SRG OBSS
+ * PD Max Offset field which contains an unsigned integer that is added to -82
+ * dBm to generate the value of the Non-SRG OBSS PD Max parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD: s32 attribute (in dBm).
+ * Userspace optionally sends this attribute with
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE operation to the driver to specify the
+ * preferred SRG PD threshold. The driver shall send this attribute to
+ * userspace in SR resume event to indicate the PD threshold being used for SR.
+ * When there is change in SRG PD threshold (for example, due to roaming, etc.)
+ * the driver shall indicate the userspace the newly configured SRG PD threshold
+ * using an asynchronous event.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD: s32 attribute (in dBm).
+ * Userspace optionally sends this attribute with
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE operation to the driver to specify the
+ * preferred non-SRG PD threshold. The driver shall send this attribute to
+ * userspace in SR resume event to indicate the PD threshold being used for SR.
+ * When there is change in non-SRG PD threshold (for example, due to roaming,
+ * etc.) the driver shall indicate the userspace the newly configured non-SRG PD
+ * threshold using an asynchronous event.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE: u32 attribute. The possible
+ * values are defined in enum qca_wlan_sr_reason_code. This
+ * attribute is used with %QCA_WLAN_SR_OPERATION_SR_RESUME and
+ * %QCA_WLAN_SR_OPERATION_SR_SUSPEND operations.
+ */
+enum qca_wlan_vendor_attr_sr_params {
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE = 1,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW = 2,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET = 3,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET = 4,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET = 5,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD = 6,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD = 7,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE = 8,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_MAX =
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr - Defines the attributes used by the vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_SR.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_OPERATION: Mandatory u8 attribute for all requests
+ * from userspace to the driver. Possible values are defined in enum
+ * qca_wlan_sr_operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS: Nested attribute, contains the SR
+ * configuration parameters. The possible attributes inside this attribute are
+ * defined in enum qca_wlan_vendor_attr_sr_params.
+ * This attribute is used when QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE in requests from userspace to the driver and
+ * also in response from the driver to userspace when the response is sent for
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS.
+ * The driver uses this attribute in asynchronous events in which the operation
+ * is set to %QCA_WLAN_SR_OPERATION_SR_RESUME,
+ * %QCA_WLAN_SR_OPERATION_SR_SUSPEND or %QCA_WLAN_SR_OPERATION_UPDATE_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS: Nested attribute, contains the SR
+ * statistics. These attributes used inside this are defined in enum
+ * qca_wlan_vendor_attr_sr_stats.
+ * This attribute is used in response from the driver to a command in which
+ * %QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to
+ * %QCA_WLAN_SR_OPERATION_GET_STATS.
+ */
+enum qca_wlan_vendor_attr_sr {
+	QCA_WLAN_VENDOR_ATTR_SR_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SR_OPERATION = 1,
+	QCA_WLAN_VENDOR_ATTR_SR_PARAMS = 2,
+	QCA_WLAN_VENDOR_ATTR_SR_STATS = 3,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SR_MAX =
+	QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event - Defines the attributes
+ * used in the QCA_NL80211_VENDOR_SUBCMD_MLO_PEER_PRIM_NETDEV_EVENT subcommand.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR: 6 byte MAC address
+ * used by the peer on the link that corresponds to the link used for sending
+ * the event notification.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR: 6 byte
+ * MLD MAC address of the peer.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX: u32 attribute,
+ * used to pass ifindex of the primary netdev.
+ */
+enum qca_wlan_vendor_attr_mlo_peer_prim_netdev_event {
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MACADDR = 1,
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MLD_MAC_ADDR = 2,
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_PRIM_IFINDEX = 3,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_MAX =
+	QCA_WLAN_VENDOR_ATTR_MLO_PEER_PRIM_NETDEV_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info: This enum is used with
+ * nested attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST to update the frequency range
+ * and PSD information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START: Required and type is
+ * u32. This attribute is used to indicate the start of the queried frequency
+ * range in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END: Required and type is u32.
+ * This attribute is used to indicate the end of the queried frequency range
+ * in MHz.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD: Required and type is u32.
+ * This attribute will contain the PSD information for a single range as
+ * specified by the QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START and
+ * QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END attributes.
+ *
+ * The PSD power info (dBm/MHz) from user space should be multiplied
+ * by a factor of 100 when sending to the driver to preserve granularity
+ * up to 2 decimal places.
+ * Example:
+ *     PSD power value: 10.21 dBm/MHz
+ *     Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_PSD_INFO: 1021.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD attribute will be used only
+ * with nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO and with
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * The following set of attributes will be used to exchange frequency and
+ * corresponding PSD information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_freq_psd_info {
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_START = 1,
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_RANGE_END = 2,
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_PSD = 3,
+
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_AFC_FREQ_PSD_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info: This enum is used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_CHAN_LIST_INFO to update the
+ * channel list and corresponding EIRP information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM: Required and type is u8.
+ * This attribute is used to indicate queried channel from
+ * the operating class indicated in QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP: Optional and type is u32.
+ * This attribute is used to configure the EIRP power info corresponding
+ * to the channel number indicated in QCA_WLAN_VENDOR_ATTR_AFC_CHAN_NUM.
+ * The EIRP power info(dBm) from user space should be multiplied
+ * by a factor of 100 when sending to Driver to preserve granularity up to
+ * 2 decimal places.
+ * Example:
+ *     EIRP power value: 34.23 dBm
+ *     Value to be updated in QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO: 3423.
+ *
+ * Note: QCA_WLAN_VENDOR_ATTR_AFC_EIRP_INFO attribute will only be used with
+ * nested attribute QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * with QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE:
+ *
+ * The following set of attributes will be used to exchange Channel and
+ * corresponding EIRP information for AFC between the user space and Driver.
+ */
+enum qca_wlan_vendor_attr_afc_chan_eirp_info {
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM = 1,
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP = 2,
+
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_opclass_info: This enum is used with nested
+ * attributes QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_REQ_OPCLASS_CHAN_INFO to update the operating class,
+ * channel, and EIRP related information.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS: Required and type is u8.
+ * This attribute is used to indicate the operating class, as listed under
+ * IEEE Std 802.11-2020 Annex E Table E-4, for the queried channel list.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST: Array of nested attributes
+ * for updating the channel number and EIRP power information.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info.
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE_EXPIRY.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of queried channels for the corresponding operating class.
+ *
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ *      .....
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ *  ....
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ *      ....
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ *
+ * Operating class information packing format for
+ * QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO and
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_INFO when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE.
+ *
+ * m - Total number of operating classes.
+ * n, j - Number of channels for the corresponding operating class.
+ *
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[0]
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[0]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ *      .....
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[n - 1]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[n - 1]
+ *  ....
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS[m]
+ *  QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST[m]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[0]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[0]
+ *      ....
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_CHAN_NUM[j - 1]
+ *      QCA_WLAN_VENDOR_ATTR_AFC_CHAN_EIRP_INFO_EIRP[j - 1]
+ *
+ * The following set of attributes will be used to exchange operating class
+ * information for AFC between the user space and the driver.
+ */
+enum qca_wlan_vendor_attr_afc_opclass_info {
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_OPCLASS = 1,
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_CHAN_LIST = 2,
+
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_AFC_OPCLASS_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_event_type: Defines values for AFC event type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY: AFC expiry event sent from the
+ * driver to userspace in order to query the new AFC power values.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE: Power update
+ * complete event will be sent from the driver to userspace to indicate
+ * processing of the AFC response.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET: AFC payload reset event
+ * will be sent from the driver to userspace to indicate last received
+ * AFC response data has been cleared on the AP due to invalid data
+ * in the QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE.
+ *
+ * The following enum defines the different event types that will be
+ * used by the driver to help trigger corresponding AFC functionality in user
+ * space.
+ */
+enum qca_wlan_vendor_afc_event_type {
+	QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY = 0,
+	QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE = 1,
+	QCA_WLAN_VENDOR_AFC_EVENT_TYPE_PAYLOAD_RESET = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_ap_deployment_type: Defines values for AP
+ * deployment type.
+ * Attribute used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN: Unknown AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR: Indoor AP deployment.
+ *
+ * @QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR: Outdoor AP deployment.
+ *
+ * The following enum defines different deployment modes that the AP might
+ * come up in. This information will be essential to retrieve deployment-type
+ * specific SP power values for AFC operation.
+ */
+enum qca_wlan_vendor_afc_ap_deployment_type {
+	QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_UNKNOWN = 0,
+	QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_INDOOR = 1,
+	QCA_WLAN_VENDOR_AFC_AP_DEPLOYMENT_TYPE_OUTDOOR = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_afc_evt_status_code: Defines values AP will use to
+ * indicate AFC response status.
+ * Enum used by QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE attribute.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS: Success
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT: Indicates AFC indication
+ * command was not received within the expected time of the AFC expiry event
+ * being triggered.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR: Indicates AFC data
+ * parsing error by the driver.
+ *
+ * @QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR: Indicates any other local
+ * error.
+ *
+ * The following enum defines the status codes that the driver will use to
+ * indicate whether the AFC data is valid or not.
+ */
+enum qca_wlan_vendor_afc_evt_status_code {
+	QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_SUCCESS = 0,
+	QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_TIMEOUT = 1,
+	QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_PARSING_ERROR = 2,
+	QCA_WLAN_VENDOR_AFC_EVT_STATUS_CODE_LOCAL_ERROR = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_event: Defines attributes to be used with
+ * vendor event QCA_NL80211_VENDOR_SUBCMD_AFC_EVENT. These attributes will
+ * support sending only a single request to the user space at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE: Required u8 attribute.
+ * Used with event to notify the type of AFC event received.
+ * Valid values are defined in enum qca_wlan_vendor_afc_event_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ *
+ * This attribute is used to indicate the AP deployment type in the AFC request.
+ * Valid values are defined in enum qca_wlan_vendor_afc_ap_deployment_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID: Required u32 attribute.
+ * Unique request identifier generated by the AFC client for every
+ * AFC expiry event trigger. See also QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID.
+ * The user space application is responsible for ensuring no duplicate values
+ * are in-flight with the server, e.g., by delaying a request, should the same
+ * value be received from different radios in parallel.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION: u32 attribute. Optional.
+ * It is used when the QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY, otherwise unused.
+ *
+ * This attribute indicates the AFC spec version information. This will
+ * indicate the AFC version AFC client must use to query the AFC data.
+ * Bits 15:0  - Minor version
+ * Bits 31:16 - Major version
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER: u16 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY,
+ * otherwise unused.
+ * This attribute indicates the minimum desired power (in dBm) for
+ * the queried spectrum.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE: u8 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * Valid values are defined in enum qca_wlan_vendor_afc_evt_status_code.
+ * This attribute is used to indicate if there were any errors parsing the
+ * AFC response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE: s32 attribute. Required
+ * when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ *            for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0   - DD (Day 1-31)
+ *              bits 15:8  - MM (Month 1-12)
+ *              bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME: u32 attribute. Required when
+ * QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0   - SS (Seconds 0-59)
+ *              bits 15:8  - MM (Minutes 0-59)
+ *              bits 23:16 - HH (Hours 0-23)
+ *              bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST: Array of nested attributes
+ * for updating the list of frequency ranges to be queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in
+ * enum qca_wlan_vendor_attr_afc_freq_psd_info.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST: Array of nested attributes
+ * for updating the list of operating classes and corresponding channels to be
+ * queried.
+ * Required when QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE is
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_EXPIRY or
+ * QCA_WLAN_VENDOR_AFC_EVENT_TYPE_POWER_UPDATE_COMPLETE, otherwise unused.
+ * It uses the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info.
+ */
+enum qca_wlan_vendor_attr_afc_event {
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AP_DEPLOYMENT = 2,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID = 3,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFC_WFA_VERSION = 4,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MIN_DES_POWER = 5,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_STATUS_CODE = 6,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_SERVER_RESP_CODE = 7,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_DATE = 8,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_EXP_TIME = 9,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_FREQ_RANGE_LIST = 10,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_OPCLASS_CHAN_LIST = 11,
+
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_MAX =
+	QCA_WLAN_VENDOR_ATTR_AFC_EVENT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_afc_response: Defines attributes to be used
+ * with vendor command QCA_NL80211_VENDOR_SUBCMD_AFC_RESPONSE. These attributes
+ * will support sending only a single AFC response to the driver at a time.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA: Type is NLA_STRING. Required attribute.
+ * This attribute will be used to send a single Spectrum Inquiry response object
+ * from the 'availableSpectrumInquiryResponses' array object from the response
+ * JSON.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE: Required u32 attribute.
+ *
+ * This attribute indicates the period (in seconds) for which the response
+ * data received is valid for.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID: Required u32 attribute.
+ *
+ * This attribute indicates the request ID for which the corresponding
+ * response is being sent for. See also QCA_WLAN_VENDOR_ATTR_AFC_EVENT_REQ_ID.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE: Required u32 attribute.
+ *
+ * This attribute indicates the date until which the current response is
+ * valid for in UTC format.
+ * Date format: bits 7:0   - DD (Day 1-31)
+ *              bits 15:8  - MM (Month 1-12)
+ *              bits 31:16 - YYYY (Year)
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME: Required u32 attribute.
+ *
+ * This attribute indicates the time until which the current response is
+ * valid for in UTC format.
+ * Time format: bits 7:0   - SS (Seconds 0-59)
+ *              bits 15:8  - MM (Minutes 0-59)
+ *              bits 23:16 - HH (Hours 0-23)
+ *              bits 31:24 - Reserved
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE: Required s32 attribute.
+ *
+ * This attribute indicates the AFC response code. The AFC response codes are
+ * in the following categories:
+ * -1: General Failure.
+ * 0: Success.
+ * 100 - 199: General errors related to protocol.
+ * 300 - 399: Error events specific to message exchange
+ *            for the Available Spectrum Inquiry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO: Array of nested attributes
+ * for PSD info of all the queried frequency ranges. It uses the attributes
+ * defined in enum qca_wlan_vendor_attr_afc_freq_psd_info. Required attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO: Array of nested
+ * attributes for EIRP info of all queried operating class/channels. It uses
+ * the attributes defined in enum qca_wlan_vendor_attr_afc_opclass_info and
+ * enum qca_wlan_vendor_attr_afc_chan_eirp_info. Required attribute.
+ *
+ */
+enum qca_wlan_vendor_attr_afc_response {
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_DATA = 1,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_TIME_TO_LIVE = 2,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_REQ_ID = 3,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_DATE = 4,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_EXP_TIME = 5,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFC_SERVER_RESP_CODE = 6,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_FREQ_PSD_INFO = 7,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_OPCLASS_CHAN_EIRP_INFO = 8,
+
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_MAX =
+	QCA_WLAN_VENDOR_ATTR_AFC_RESP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_dozed_ap_state - Doze states for AP interface
+ *
+ * @QCA_WLAN_DOZED_AP_DISABLE: Disable doze state on the AP interface.
+ *
+ * @QCA_WLAN_DOZED_AP_ENABLE: Enable doze state on the AP interface. AP starts
+ * beaconing at higher beacon interval with Rx disabled.
+ */
+enum qca_wlan_dozed_ap_state {
+	QCA_WLAN_DOZED_AP_DISABLE = 0,
+	QCA_WLAN_DOZED_AP_ENABLE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dozed_ap - Used by the vendor command
+ * @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP to configure or receive dozed AP mode
+ * configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE: u8 attribute.
+ * Configures the doze state for an AP interface. Possible values are defined
+ * in enum qca_wlan_dozed_ap_state. @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP event
+ * gets triggered asynchronously to provide updated AP interface configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE: Unsigned 64-bit cookie provided by
+ * the driver in the response to specific @QCA_NL80211_VENDOR_SUBCMD_DOZED_AP
+ * command, which is used later to maintain synchronization between commands
+ * and asynchronous events.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF: u64 attribute.
+ * Used in event to indicate the next TBTT TSF timer value after applying the
+ * doze mode configuration. Next TBTT TSF is the time at which the AP sends
+ * the first beacon after entering or exiting dozed mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER: u16 attribute.
+ * Used with event to inform the periodicity of beacon transmission that would
+ * be skipped at all TBTTs in between.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DOZED_AP_PAD: Attribute used for padding for 64-bit
+ * alignment.
+ */
+enum qca_wlan_vendor_attr_dozed_ap {
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_STATE = 1,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_COOKIE = 2,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_NEXT_TSF = 3,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_BI_MULTIPLIER = 4,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_PAD = 5,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_MAX =
+	QCA_WLAN_VENDOR_ATTR_DOZED_AP_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_monitor_mode_status - Represents the status codes
+ * used with QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING: Used to indicate no
+ * capture running status.
+ * @QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING: Used to indicate
+ * capture running status.
+ **/
+
+enum qca_wlan_vendor_monitor_mode_status {
+	QCA_WLAN_VENDOR_MONITOR_MODE_NO_CAPTURE_RUNNING = 0,
+	QCA_WLAN_VENDOR_MONITOR_MODE_CAPTURE_RUNNING = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_monitor_mode - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_MONITOR_MODE to report
+ * information regarding the local packet capture over the monitor mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS: u32 attribute. This attribute
+ * represents the status of the start capture commands. The values used with
+ * this attribute are defined in enum qca_wlan_vendor_monitor_mode_status. This
+ * is returned by the driver in the response to the command.
+ */
+
+enum qca_wlan_vendor_attr_get_monitor_mode {
+	QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_STATUS = 1,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_MAX =
+	QCA_WLAN_VENDOR_ATTR_GET_MONITOR_MODE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_link_state_op_types - Defines different types of
+ * operations for which %QCA_NL80211_VENDOR_SUBCMD_MLO_LINK_STATE can be used.
+ * Will be used with %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE attribute.
+ *
+ * @QCA_WLAN_VENDOR_LINK_STATE_OP_GET - Get the MLO links state information.
+ * @QCA_WLAN_VENDOR_LINK_STATE_OP_SET - Set the MLO links state information.
+ */
+enum qca_wlan_vendor_link_state_op_types {
+	QCA_WLAN_VENDOR_LINK_STATE_OP_GET = 0,
+	QCA_WLAN_VENDOR_LINK_STATE_OP_SET = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_link_state_control_modes - Represents the types of MLO
+ * links state control modes. This enum is used by
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONTROL_MODE attribute.
+ *
+ * @QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_DEFAULT: MLO links state controlled
+ * by the driver.
+ * @QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_USER: MLO links state controlled by
+ * user space.
+ * @QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_MIXED: User space provides the
+ * desired number of MLO links to operate in active state at any given time.
+ * The driver will choose which MLO links should operate in the active state.
+ * See enum qca_wlan_vendor_link_state for active state definition.
+ */
+enum qca_wlan_vendor_link_state_control_modes {
+	QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_DEFAULT = 0,
+	QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_USER = 1,
+	QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_MIXED = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_link_state_operation_modes - Represents the types of MLO
+ * links state operation modes. This enum is used by
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OPERATION_MODE attribute.
+ *
+ * @QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_DEFAULT: In the default operation
+ * mode, the driver selects the operating mode of the links, without any
+ * guidance from the user space.
+ * @QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_LOW_LATENCY: In the low latency
+ * operation mode the driver should select MLO links that will achieve low
+ * latency.
+ * @QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_HIGH_THROUGHPUT: In the high
+ * throughput operation mode the driver should select MLO links that will
+ * achieve higher throughput.
+ * @QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_LOW_POWER: In the low power
+ * operation mode the driver should select MLO links that will achieve low
+ * power.
+ */
+enum qca_wlan_vendor_link_state_operation_modes {
+	QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_DEFAULT = 0,
+	QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_LOW_LATENCY = 1,
+	QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_HIGH_THROUGHPUT = 2,
+	QCA_WLAN_VENDOR_LINK_STATE_OPERATION_MODE_LOW_POWER = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_link_state - Represents the possible link states of an
+ * MLO link.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_INACTIVE: In this state, the link will not
+ * be used for data transmission but it can have TIDs mapped to it. It will be
+ * in doze state always and does not monitor the beacons.
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_ACTIVE: In this state, the link will be
+ * used for data TX/RX and monitors the beacons to check TIM bit indication.
+ * It may enter doze state and comes out based on the transmit data traffic and
+ * TIM bit indication in the beacon.
+ */
+enum qca_wlan_vendor_link_state {
+	QCA_WLAN_VENDOR_LINK_STATE_INACTIVE = 0,
+	QCA_WLAN_VENDOR_LINK_STATE_ACTIVE = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_link_state_config - Definition of attributes used
+ * inside nested attribute %QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_LINK_ID: u8 attribute, link ID of the
+ * MLO link.
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_STATE: u32 attribute. See
+ * enum qca_wlan_vendor_link_state for possible MLO link states.
+ */
+
+enum qca_wlan_vendor_attr_link_state_config {
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_LINK_ID = 1,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_STATE = 2,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mlo_link_state - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_MLO_LINK_STATE vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE: u32 attribute. Indicates the type
+ * of the operation %QCA_NL80211_VENDOR_SUBCMD_MLO_LINK_STATE intended for.
+ * Required only in a command. Possible values for this attribute are defined in
+ * enum qca_wlan_vendor_link_state_op_types.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONTROL_MODE: u32 attribute. Indicates MLO
+ * links control mode type. Optional attribute in a command when
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_OP_SET. Required attribute in a response when
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_OP_GET.
+ * See enum qca_wlan_vendor_link_state_control_modes for possible control modes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG: Array of nested attributes.
+ * Indicates the state of the each MLO link affiliated with the interface.
+ * Required attribute in a command when %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE
+ * is set to %QCA_WLAN_VENDOR_LINK_STATE_OP_SET and
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONTROL_MODE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_USER. Required attribute in a
+ * response when %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_OP_GET.
+ * See enum qca_wlan_vendor_attr_link_state_config for the nested attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_MIXED_MODE_ACTIVE_NUM_LINKS: u8 attribute.
+ * Represents the number of active state links. See enum
+ * qca_wlan_vendor_link_state for active state definition.
+ * Required attribute in a command when %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE
+ * is set to %QCA_WLAN_VENDOR_LINK_STATE_OP_SET and
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONTROL_MODE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_CONTROL_MODE_MIXED.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LINK_STATE_OPERATION_MODE: u32 attribute. Indicates MLO
+ * links operation mode type. Optional attribute in a command when
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_OP_SET. Required attribute in a response when
+ * %QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE is set to
+ * %QCA_WLAN_VENDOR_LINK_STATE_OP_GET.
+ * See enum qca_wlan_vendor_link_state_operation_modes for possible operation
+ * modes.
+ */
+enum qca_wlan_vendor_attr_mlo_link_state {
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_OP_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONTROL_MODE = 2,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_CONFIG = 3,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_MIXED_MODE_ACTIVE_NUM_LINKS = 4,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_OPERATION_MODE = 5,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_MAX =
+	QCA_WLAN_VENDOR_ATTR_LINK_STATE_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index c0f154e..d4a196f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
+#include "common/wpa_common.h"
 #include "utils/const_time.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
@@ -601,9 +603,9 @@
 	case 30:
 		*z = 7;
 		return 0;
+	default:
+		return -1;
 	}
-
-	return -1;
 }
 
 
@@ -1520,10 +1522,11 @@
 	const u8 *salt;
 	struct wpabuf *rejected_groups = NULL;
 	u8 keyseed[SAE_MAX_HASH_LEN];
-	u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
+	u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN_MAX];
 	struct crypto_bignum *tmp;
 	int ret = -1;
 	size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
+	size_t pmk_len;
 	const u8 *addr[1];
 	size_t len[1];
 
@@ -1545,6 +1548,14 @@
 		hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
 	else
 		hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+	if (wpa_key_mgmt_sae_ext_key(sae->akmp))
+		pmk_len = hash_len;
+	else
+		pmk_len = SAE_PMK_LEN;
+	wpa_printf(MSG_DEBUG, "SAE: Derive keys - H2E=%d AKMP=0x%x = %08x (%s)",
+		   sae->h2e, sae->akmp,
+		   wpa_akm_to_suite(sae->akmp),
+		   wpa_key_mgmt_txt(sae->akmp, WPA_PROTO_RSN));
 	if (sae->h2e && (sae->tmp->own_rejected_groups ||
 			 sae->tmp->peer_rejected_groups)) {
 		struct wpabuf *own, *peer;
@@ -1595,32 +1606,35 @@
 	 * (commit-scalar + peer-commit-scalar) mod r part as a bit string by
 	 * zero padding it from left to the length of the order (in full
 	 * octets). */
-	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
+	if (crypto_bignum_to_bin(tmp, val, sizeof(val),
+				 sae->tmp->order_len) < 0)
+		goto fail;
 	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
 
 #ifdef CONFIG_SAE_PK
 	if (sae->pk) {
 		if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
 				 val, sae->tmp->order_len,
-				 keys, 2 * hash_len + SAE_PMK_LEN) < 0)
+				 keys, 2 * hash_len + pmk_len) < 0)
 			goto fail;
 	} else {
 		if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
 				 val, sae->tmp->order_len,
-				 keys, hash_len + SAE_PMK_LEN) < 0)
+				 keys, hash_len + pmk_len) < 0)
 			goto fail;
 	}
 #else /* CONFIG_SAE_PK */
 	if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
 			 val, sae->tmp->order_len,
-			 keys, hash_len + SAE_PMK_LEN) < 0)
+			 keys, hash_len + pmk_len) < 0)
 		goto fail;
 #endif /* !CONFIG_SAE_PK */
 
 	forced_memzero(keyseed, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, hash_len);
 	sae->tmp->kck_len = hash_len;
-	os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
+	os_memcpy(sae->pmk, keys + hash_len, pmk_len);
+	sae->pmk_len = pmk_len;
 	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
 #ifdef CONFIG_SAE_PK
 	if (sae->pk) {
@@ -1634,7 +1648,7 @@
 	forced_memzero(keys, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
 			sae->tmp->kck, sae->tmp->kck_len);
-	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, sae->pmk_len);
 
 	ret = 0;
 fail:
@@ -1726,6 +1740,17 @@
 				token);
 	}
 
+	if (wpa_key_mgmt_sae_ext_key(sae->akmp)) {
+		u32 suite = wpa_akm_to_suite(sae->akmp);
+
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		wpabuf_put_u8(buf, 1 + RSN_SELECTOR_LEN);
+		wpabuf_put_u8(buf, WLAN_EID_EXT_AKM_SUITE_SELECTOR);
+		RSN_SELECTOR_PUT(wpabuf_put(buf, RSN_SELECTOR_LEN), suite);
+		wpa_printf(MSG_DEBUG, "SAE: AKM Suite Selector: %08x", suite);
+		sae->own_akm_suite_selector = suite;
+	}
+
 	return 0;
 }
 
@@ -1802,6 +1827,16 @@
 }
 
 
+static int sae_is_akm_suite_selector_elem(const u8 *pos, const u8 *end)
+{
+	return end - pos >= 2 + 1 + RSN_SELECTOR_LEN &&
+		pos[0] == WLAN_EID_EXTENSION &&
+		pos[1] >= 1 + RSN_SELECTOR_LEN &&
+		end - pos - 2 >= pos[1] &&
+		pos[2] == WLAN_EID_EXT_AKM_SUITE_SELECTOR;
+}
+
+
 static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
 				   const u8 *end, const u8 **token,
 				   size_t *token_len, int h2e)
@@ -1934,8 +1969,10 @@
 	crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
 	sae->tmp->peer_commit_element_ecc =
 		crypto_ec_point_from_bin(sae->tmp->ec, *pos);
-	if (sae->tmp->peer_commit_element_ecc == NULL)
+	if (!sae->tmp->peer_commit_element_ecc) {
+		wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
 
 	if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
 					 sae->tmp->peer_commit_element_ecc)) {
@@ -2089,9 +2126,38 @@
 }
 
 
+static int sae_parse_akm_suite_selector(struct sae_data *sae,
+					const u8 **pos, const u8 *end)
+{
+	const u8 *epos;
+	u8 len;
+
+	wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+		    *pos, end - *pos);
+	if (!sae_is_akm_suite_selector_elem(*pos, end))
+		return WLAN_STATUS_SUCCESS;
+
+	epos = *pos;
+	epos++; /* skip IE type */
+	len = *epos++; /* IE length */
+	if (len > end - epos || len < 1)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	epos++; /* skip ext ID */
+	len--;
+
+	if (len < RSN_SELECTOR_LEN)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	sae->peer_akm_suite_selector = RSN_SELECTOR_GET(epos);
+	wpa_printf(MSG_DEBUG, "SAE: Received AKM Suite Selector: %08x",
+		   sae->peer_akm_suite_selector);
+	*pos = epos + len;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 		     const u8 **token, size_t *token_len, int *allowed_groups,
-		     int h2e)
+		     int h2e, int *ie_offset)
 {
 	const u8 *pos = data, *end = data + len;
 	u16 res;
@@ -2117,6 +2183,9 @@
 	if (res != WLAN_STATUS_SUCCESS)
 		return res;
 
+	if (ie_offset)
+		*ie_offset = pos - data;
+
 	/* Optional Password Identifier element */
 	res = sae_parse_password_identifier(sae, &pos, end);
 	if (res != WLAN_STATUS_SUCCESS)
@@ -2133,6 +2202,31 @@
 	if (h2e)
 		sae_parse_token_container(sae, pos, end, token, token_len);
 
+	/* Conditional AKM Suite Selector element */
+	if (h2e) {
+		res = sae_parse_akm_suite_selector(sae, &pos, end);
+		if (res != WLAN_STATUS_SUCCESS)
+			return res;
+	}
+
+	if (sae->own_akm_suite_selector &&
+	    sae->own_akm_suite_selector != sae->peer_akm_suite_selector) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: AKM suite selector mismatch: own=%08x peer=%08x",
+			   sae->own_akm_suite_selector,
+			   sae->peer_akm_suite_selector);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (!sae->akmp) {
+		if (sae->peer_akm_suite_selector ==
+		    RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+			sae->akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
+		else if (sae->peer_akm_suite_selector ==
+		    RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+			sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+	}
+
 	/*
 	 * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
 	 * the values we sent which would be evidence of a reflection attack.
@@ -2285,7 +2379,8 @@
 }
 
 
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+		      int *ie_offset)
 {
 	u8 verifier[SAE_MAX_HASH_LEN];
 	size_t hash_len;
@@ -2341,6 +2436,10 @@
 		return -1;
 #endif /* CONFIG_SAE_PK */
 
+	/* 2 bytes are for send-confirm, then the hash, followed by IEs */
+	if (ie_offset)
+		*ie_offset = 2 + hash_len;
+
 	return 0;
 }
 
diff --git a/src/common/sae.h b/src/common/sae.h
index 93fc5fb..c446da3 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -11,6 +11,7 @@
 
 #define SAE_KCK_LEN 32
 #define SAE_PMK_LEN 32
+#define SAE_PMK_LEN_MAX 64
 #define SAE_PMKID_LEN 16
 #define SAE_MAX_PRIME_LEN 512
 #define SAE_MAX_ECC_PRIME_LEN 66
@@ -104,7 +105,11 @@
 struct sae_data {
 	enum sae_state state;
 	u16 send_confirm;
-	u8 pmk[SAE_PMK_LEN];
+	u8 pmk[SAE_PMK_LEN_MAX];
+	size_t pmk_len;
+	int akmp; /* WPA_KEY_MGMT_* used in key derivation */
+	u32 own_akm_suite_selector;
+	u32 peer_akm_suite_selector;
 	u8 pmkid[SAE_PMKID_LEN];
 	struct crypto_bignum *peer_commit_scalar;
 	struct crypto_bignum *peer_commit_scalar_accepted;
@@ -131,9 +136,10 @@
 		     const struct wpabuf *token, const char *identifier);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 		     const u8 **token, size_t *token_len, int *allowed_groups,
-		     int h2e);
+		     int h2e, int *ie_offset);
 int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
-int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
+		      int *ie_offset);
 u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 const char * sae_state_txt(enum sae_state state);
 size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 27336c9..15ebcab 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -36,6 +36,9 @@
 		return pmk_len / 2;
 	case WPA_KEY_MGMT_OWE:
 		return pmk_len / 2;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+	case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
@@ -72,6 +75,9 @@
 		return pmk_len <= 32 ? 16 : 32;
 	case WPA_KEY_MGMT_OWE:
 		return pmk_len <= 32 ? 16 : 32;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+	case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+		return pmk_len <= 32 ? 16 : 32;
 	default:
 		return 16;
 	}
@@ -108,6 +114,9 @@
 		return pmk_len / 2;
 	case WPA_KEY_MGMT_OWE:
 		return pmk_len / 2;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+	case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+		return pmk_len / 2;
 	default:
 		return 16;
 	}
@@ -143,7 +152,8 @@
 		akmp == WPA_KEY_MGMT_DPP ||
 		wpa_key_mgmt_ft(akmp) ||
 		wpa_key_mgmt_sha256(akmp) ||
-		wpa_key_mgmt_sae(akmp) ||
+		(wpa_key_mgmt_sae(akmp) &&
+		 !wpa_key_mgmt_sae_ext_key(akmp)) ||
 		wpa_key_mgmt_suite_b(akmp);
 }
 
@@ -223,6 +233,32 @@
 			wpa_printf(MSG_DEBUG,
 				   "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
 			return omac1_aes_128(key, buf, len, mic);
+		case WPA_KEY_MGMT_SAE_EXT_KEY:
+		case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+			wpa_printf(MSG_DEBUG,
+				   "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)",
+				   (unsigned int) key_len * 8 * 2);
+			if (key_len == 128 / 8) {
+				if (hmac_sha256(key, key_len, buf, len, hash))
+					return -1;
+#ifdef CONFIG_SHA384
+			} else if (key_len == 192 / 8) {
+				if (hmac_sha384(key, key_len, buf, len, hash))
+					return -1;
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+			} else if (key_len == 256 / 8) {
+				if (hmac_sha512(key, key_len, buf, len, hash))
+					return -1;
+#endif /* CONFIG_SHA512 */
+			} else {
+				wpa_printf(MSG_INFO,
+					   "SAE: Unsupported KCK length: %u",
+					   (unsigned int) key_len);
+				return -1;
+			}
+			os_memcpy(mic, hash, key_len);
+			break;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_HS20
 		case WPA_KEY_MGMT_OSEN:
@@ -473,6 +509,36 @@
 			   (unsigned int) pmk_len);
 		return -1;
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_SAE
+	} else if (wpa_key_mgmt_sae_ext_key(akmp)) {
+		if (pmk_len == 32) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: PTK derivation using PRF(SHA256)");
+			if (sha256_prf(pmk, pmk_len, label, data, data_len,
+				       tmp, ptk_len) < 0)
+				return -1;
+#ifdef CONFIG_SHA384
+		} else if (pmk_len == 48) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: PTK derivation using PRF(SHA384)");
+			if (sha384_prf(pmk, pmk_len, label, data, data_len,
+				       tmp, ptk_len) < 0)
+				return -1;
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+		} else if (pmk_len == 64) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: PTK derivation using PRF(SHA512)");
+			if (sha512_prf(pmk, pmk_len, label, data, data_len,
+				       tmp, ptk_len) < 0)
+				return -1;
+#endif /* CONFIG_SHA512 */
+		} else {
+			wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u",
+				   (unsigned int) pmk_len);
+			return -1;
+		}
+#endif /* CONFIG_SAE */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
 		if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
@@ -816,7 +882,7 @@
 
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
 	       const u8 *ap_addr, u8 transaction_seqnum,
 	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
@@ -828,8 +894,9 @@
 	const u8 *addr[10];
 	size_t len[10];
 	size_t i, num_elem = 0;
-	u8 zero_mic[24];
+	u8 zero_mic[32];
 	size_t mic_len, fte_fixed_len;
+	int res;
 
 	if (kck_len == 16) {
 		mic_len = 16;
@@ -837,6 +904,10 @@
 	} else if (kck_len == 24) {
 		mic_len = 24;
 #endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	} else if (kck_len == 32) {
+		mic_len = 32;
+#endif /* CONFIG_SHA512 */
 	} else {
 		wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
 			   (unsigned int) kck_len);
@@ -901,6 +972,17 @@
 
 	for (i = 0; i < num_elem; i++)
 		wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (kck_len == 32) {
+		u8 hash[SHA512_MAC_LEN];
+
+		if (hmac_sha512_vector(kck, kck_len, num_elem, addr, len, hash))
+			return -1;
+		os_memcpy(mic, hash, 32);
+		res = 0;
+	}
+#endif /* CONFIG_SHA384 */
 #ifdef CONFIG_SHA384
 	if (kck_len == 24) {
 		u8 hash[SHA384_MAC_LEN];
@@ -908,26 +990,34 @@
 		if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
 			return -1;
 		os_memcpy(mic, hash, 24);
+		res = 0;
 	}
 #endif /* CONFIG_SHA384 */
-	if (kck_len == 16 &&
-	    omac1_aes_128_vector(kck, num_elem, addr, len, mic))
-		return -1;
+	if (kck_len == 16 && key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		u8 hash[SHA256_MAC_LEN];
 
-	return 0;
+		if (hmac_sha256_vector(kck, kck_len, num_elem, addr, len, hash))
+			return -1;
+		os_memcpy(mic, hash, 16);
+		res = 0;
+	}
+	if (kck_len == 16 && key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    omac1_aes_128_vector(kck, num_elem, addr, len, mic) == 0)
+		res = 0;
+
+	return res;
 }
 
 
 static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
-			     struct wpa_ft_ies *parse, int use_sha384)
+			     struct wpa_ft_ies *parse, const u8 *opt)
 {
 	const u8 *end, *pos;
 
 	parse->ftie = ie;
 	parse->ftie_len = ie_len;
 
-	pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
-		    sizeof(struct rsn_ftie));
+	pos = opt;
 	end = ie + ie_len;
 	wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
 
@@ -938,7 +1028,7 @@
 		len = *pos++;
 		if (len > end - pos) {
 			wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
-			break;
+			return -1;
 		}
 
 		switch (id) {
@@ -950,8 +1040,11 @@
 				return -1;
 			}
 			parse->r1kh_id = pos;
+			wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+				    parse->r1kh_id, FT_R1KH_ID_LEN);
 			break;
 		case FTIE_SUBELEM_GTK:
+			wpa_printf(MSG_DEBUG, "FT: GTK");
 			parse->gtk = pos;
 			parse->gtk_len = len;
 			break;
@@ -964,8 +1057,11 @@
 			}
 			parse->r0kh_id = pos;
 			parse->r0kh_id_len = len;
+			wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+				    parse->r0kh_id, parse->r0kh_id_len);
 			break;
 		case FTIE_SUBELEM_IGTK:
+			wpa_printf(MSG_DEBUG, "FT: IGTK");
 			parse->igtk = pos;
 			parse->igtk_len = len;
 			break;
@@ -973,9 +1069,12 @@
 		case FTIE_SUBELEM_OCI:
 			parse->oci = pos;
 			parse->oci_len = len;
+			wpa_hexdump(MSG_DEBUG, "FT: OCI",
+				    parse->oci, parse->oci_len);
 			break;
 #endif /* CONFIG_OCV */
 		case FTIE_SUBELEM_BIGTK:
+			wpa_printf(MSG_DEBUG, "FT: BIGTK");
 			parse->bigtk = pos;
 			parse->bigtk_len = len;
 			break;
@@ -991,20 +1090,73 @@
 }
 
 
-int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
-		     struct wpa_ft_ies *parse, int use_sha384)
+static int wpa_ft_parse_fte(int key_mgmt, const u8 *ie, size_t len,
+			    struct wpa_ft_ies *parse)
+{
+	size_t mic_len;
+	u8 mic_len_info;
+	const u8 *pos = ie;
+	const u8 *end = pos + len;
+
+	wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", pos, 2);
+	parse->fte_rsnxe_used = pos[0] & FTE_MIC_CTRL_RSNXE_USED;
+	mic_len_info = (pos[0] & FTE_MIC_CTRL_MIC_LEN_MASK) >>
+		FTE_MIC_CTRL_MIC_LEN_SHIFT;
+	parse->fte_elem_count = pos[1];
+	pos += 2;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		switch (mic_len_info) {
+		case FTE_MIC_LEN_16:
+			mic_len = 16;
+			break;
+		case FTE_MIC_LEN_24:
+			mic_len = 24;
+			break;
+		case FTE_MIC_LEN_32:
+			mic_len = 32;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "FT: Unknown MIC Length subfield value %u",
+				   mic_len_info);
+			return -1;
+		}
+	} else {
+		mic_len = wpa_key_mgmt_sha384(key_mgmt) ? 24 : 16;
+	}
+	if (mic_len > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "FT: No room for %zu octet MIC in FTE",
+			   mic_len);
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", pos, mic_len);
+	parse->fte_mic = pos;
+	parse->fte_mic_len = mic_len;
+	pos += mic_len;
+
+	if (2 * WPA_NONCE_LEN > end - pos)
+		return -1;
+	parse->fte_anonce = pos;
+	wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
+		    parse->fte_anonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+	parse->fte_snonce = pos;
+	wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
+		    parse->fte_snonce, WPA_NONCE_LEN);
+	pos += WPA_NONCE_LEN;
+
+	return wpa_ft_parse_ftie(ie, len, parse, pos);
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
+		     int key_mgmt)
 {
 	const u8 *end, *pos;
 	struct wpa_ie_data data;
 	int ret;
-	const struct rsn_ftie *ftie;
 	int prot_ie_count = 0;
-	int update_use_sha384 = 0;
-
-	if (use_sha384 < 0) {
-		use_sha384 = 0;
-		update_use_sha384 = 1;
-	}
 
 	os_memset(parse, 0, sizeof(*parse));
 	if (ies == NULL)
@@ -1038,11 +1190,8 @@
 				parse->rsn_pmkid = data.pmkid;
 			parse->key_mgmt = data.key_mgmt;
 			parse->pairwise_cipher = data.pairwise_cipher;
-			if (update_use_sha384) {
-				use_sha384 =
-					wpa_key_mgmt_sha384(parse->key_mgmt);
-				update_use_sha384 = 0;
-			}
+			if (!key_mgmt)
+				key_mgmt = parse->key_mgmt;
 			break;
 		case WLAN_EID_RSNX:
 			wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len);
@@ -1060,47 +1209,19 @@
 			break;
 		case WLAN_EID_FAST_BSS_TRANSITION:
 			wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
-			if (use_sha384) {
-				const struct rsn_ftie_sha384 *ftie_sha384;
+			/* The first two octets (MIC Control field) is in the
+			 * same offset for all cases, but the second field (MIC)
+			 * has variable length with three different values.
+			 * In particular the FT-SAE-EXT-KEY is inconvinient to
+			 * parse, so try to handle this in pieces instead of
+			 * using the struct rsn_ftie* definitions. */
 
-				if (len < sizeof(*ftie_sha384))
-					return -1;
-				ftie_sha384 =
-					(const struct rsn_ftie_sha384 *) pos;
-				wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
-					    ftie_sha384->mic_control, 2);
-				wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
-					    ftie_sha384->mic,
-					    sizeof(ftie_sha384->mic));
-				parse->fte_anonce = ftie_sha384->anonce;
-				wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
-					    ftie_sha384->anonce,
-					    WPA_NONCE_LEN);
-				parse->fte_snonce = ftie_sha384->snonce;
-				wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
-					    ftie_sha384->snonce,
-					    WPA_NONCE_LEN);
-				prot_ie_count = ftie_sha384->mic_control[1];
-				if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
-					return -1;
-				break;
-			}
-
-			if (len < sizeof(*ftie))
+			if (len < 2)
 				return -1;
-			ftie = (const struct rsn_ftie *) pos;
-			wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
-				    ftie->mic_control, 2);
-			wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
-				    ftie->mic, sizeof(ftie->mic));
-			parse->fte_anonce = ftie->anonce;
-			wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
-				    ftie->anonce, WPA_NONCE_LEN);
-			parse->fte_snonce = ftie->snonce;
-			wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
-				    ftie->snonce, WPA_NONCE_LEN);
-			prot_ie_count = ftie->mic_control[1];
-			if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
+			prot_ie_count = pos[1]; /* Element Count field in
+						 * MIC Control */
+
+			if (wpa_ft_parse_fte(key_mgmt, pos, len, parse) < 0)
 				return -1;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
@@ -1183,7 +1304,7 @@
  * PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers
  * 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used.
  */
-static bool pasn_use_sha384(int akmp, int cipher)
+bool pasn_use_sha384(int akmp, int cipher)
 {
 	return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
 					      cipher == WPA_CIPHER_GCMP_256)) ||
@@ -1318,6 +1439,62 @@
 
 
 /**
+ * wpa_ltf_keyseed - Compute LTF keyseed from KDK
+ * @ptk: Buffer that holds pairwise transient key
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher)
+{
+	u8 *buf;
+	size_t buf_len;
+	u8 hash[SHA384_MAC_LEN];
+	const u8 *kdk = ptk->kdk;
+	size_t kdk_len = ptk->kdk_len;
+	const char *label = "Secure LTF key seed";
+
+	if (!kdk || !kdk_len) {
+		wpa_printf(MSG_ERROR, "WPA: No KDK for LTF keyseed generation");
+		return -1;
+	}
+
+	buf = (u8 *)label;
+	buf_len = os_strlen(label);
+
+	if (pasn_use_sha384(akmp, cipher)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Secure LTF keyseed using HMAC-SHA384");
+
+		if (hmac_sha384(kdk, kdk_len, buf, buf_len, hash)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: HMAC-SHA384 compute failed");
+			return -1;
+		}
+		os_memcpy(ptk->ltf_keyseed, hash, SHA384_MAC_LEN);
+		ptk->ltf_keyseed_len = SHA384_MAC_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ",
+				ptk->ltf_keyseed, ptk->ltf_keyseed_len);
+
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: LTF keyseed using HMAC-SHA256");
+
+		if (hmac_sha256(kdk, kdk_len, buf, buf_len, hash)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: HMAC-SHA256 compute failed");
+			return -1;
+		}
+		os_memcpy(ptk->ltf_keyseed, hash, SHA256_MAC_LEN);
+		ptk->ltf_keyseed_len = SHA256_MAC_LEN;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ",
+				ptk->ltf_keyseed, ptk->ltf_keyseed_len);
+	}
+
+	return 0;
+}
+
+
+/**
  * pasn_mic - Calculate PASN MIC
  * @kck: The key confirmation key for the PASN PTKSA
  * @akmp: Negotiated AKM
@@ -1479,8 +1656,12 @@
 #ifdef CONFIG_SAE
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
 		return WPA_KEY_MGMT_SAE;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+		return WPA_KEY_MGMT_SAE_EXT_KEY;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
 		return WPA_KEY_MGMT_FT_SAE;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+		return WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
@@ -1862,30 +2043,40 @@
 		      const u8 *ssid, size_t ssid_len,
 		      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
 		      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
-		      int use_sha384)
+		      int key_mgmt)
 {
 	u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
 	       FT_R0KH_ID_MAX_LEN + ETH_ALEN];
-	u8 *pos, r0_key_data[64], hash[48];
+	u8 *pos, r0_key_data[64 + 16], hash[64];
 	const u8 *addr[2];
 	size_t len[2];
-	size_t q = use_sha384 ? 48 : 32;
-	size_t r0_key_data_len = q + 16;
+	size_t q, r0_key_data_len;
+	int res;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    (xxkey_len == SHA256_MAC_LEN || xxkey_len == SHA384_MAC_LEN ||
+	     xxkey_len == SHA512_MAC_LEN))
+		q = xxkey_len;
+	else if (wpa_key_mgmt_sha384(key_mgmt))
+		q = SHA384_MAC_LEN;
+	else
+		q = SHA256_MAC_LEN;
+	r0_key_data_len = q + 16;
 
 	/*
-	 * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+	 * R0-Key-Data = KDF-Hash-Length(XXKey, "FT-R0",
 	 *                       SSIDlength || SSID || MDID || R0KHlength ||
 	 *                       R0KH-ID || S0KH-ID)
 	 * XXKey is either the second 256 bits of MSK or PSK; or the first
-	 * 384 bits of MSK for FT-EAP-SHA384.
+	 * 384 bits of MSK for FT-EAP-SHA384; or PMK from SAE.
 	 * PMK-R0 = L(R0-Key-Data, 0, Q)
 	 * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
-	 * Q = 384 for FT-EAP-SHA384; otherwise, 256
+	 * Q = 384 for FT-EAP-SHA384; the length of the digest generated by H()
+	 * for FT-SAE-EXT-KEY; or otherwise, 256
 	 */
 	if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
 		return -1;
-	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
-		   use_sha384 ? "SHA384" : "SHA256");
+	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-SHA%zu", q * 8);
 	wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
 	wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
 	wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
@@ -1903,30 +2094,43 @@
 	os_memcpy(pos, s0kh_id, ETH_ALEN);
 	pos += ETH_ALEN;
 
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (q == SHA512_MAC_LEN) {
+		if (xxkey_len != SHA512_MAC_LEN) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Unexpected XXKey length %d (expected %d)",
+				   (int) xxkey_len, SHA512_MAC_LEN);
+			return -1;
+		}
+		res = sha512_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+				 r0_key_data, r0_key_data_len);
+	}
+#endif /* CONFIG_SHA512 */
 #ifdef CONFIG_SHA384
-	if (use_sha384) {
+	if (q == SHA384_MAC_LEN) {
 		if (xxkey_len != SHA384_MAC_LEN) {
 			wpa_printf(MSG_ERROR,
 				   "FT: Unexpected XXKey length %d (expected %d)",
 				   (int) xxkey_len, SHA384_MAC_LEN);
 			return -1;
 		}
-		if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
-			       r0_key_data, r0_key_data_len) < 0)
-			return -1;
+		res = sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+				 r0_key_data, r0_key_data_len);
 	}
 #endif /* CONFIG_SHA384 */
-	if (!use_sha384) {
+	if (q == SHA256_MAC_LEN) {
 		if (xxkey_len != PMK_LEN) {
 			wpa_printf(MSG_ERROR,
 				   "FT: Unexpected XXKey length %d (expected %d)",
 				   (int) xxkey_len, PMK_LEN);
 			return -1;
 		}
-		if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
-			       r0_key_data, r0_key_data_len) < 0)
-			return -1;
+		res = sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+				 r0_key_data, r0_key_data_len);
 	}
+	if (res < 0)
+		return res;
 	os_memcpy(pmk_r0, r0_key_data, q);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
@@ -1939,12 +2143,23 @@
 	addr[1] = &r0_key_data[q];
 	len[1] = 16;
 
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (q == SHA512_MAC_LEN)
+		res = sha512_vector(2, addr, len, hash);
+#endif /* CONFIG_SHA512 */
 #ifdef CONFIG_SHA384
-	if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
-		return -1;
+	if (q == SHA384_MAC_LEN)
+		res = sha384_vector(2, addr, len, hash);
 #endif /* CONFIG_SHA384 */
-	if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
-		return -1;
+	if (q == SHA256_MAC_LEN)
+		res = sha256_vector(2, addr, len, hash);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Failed to derive PMKR0Name (PMK-R0 len %zu)",
+			   q);
+		return res;
+	}
 	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
 	forced_memzero(r0_key_data, sizeof(r0_key_data));
@@ -1958,11 +2173,14 @@
  * IEEE Std 802.11r-2008 - 8.5.1.5.4
  */
 int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
-			   const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
+			   const u8 *s1kh_id, u8 *pmk_r1_name,
+			   size_t pmk_r1_len)
 {
-	u8 hash[48];
+	u8 hash[64];
 	const u8 *addr[4];
 	size_t len[4];
+	int res;
+	const char *title;
 
 	/*
 	 * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
@@ -1977,14 +2195,31 @@
 	addr[3] = s1kh_id;
 	len[3] = ETH_ALEN;
 
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (pmk_r1_len == SHA512_MAC_LEN) {
+		title = "FT: PMKR1Name (using SHA512)";
+		res = sha512_vector(4, addr, len, hash);
+	}
+#endif /* CONFIG_SHA512 */
 #ifdef CONFIG_SHA384
-	if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
-		return -1;
+	if (pmk_r1_len == SHA384_MAC_LEN) {
+		title = "FT: PMKR1Name (using SHA384)";
+		res = sha384_vector(4, addr, len, hash);
+	}
 #endif /* CONFIG_SHA384 */
-	if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
-		return -1;
+	if (pmk_r1_len == SHA256_MAC_LEN) {
+		title = "FT: PMKR1Name (using SHA256)";
+		res = sha256_vector(4, addr, len, hash);
+	}
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Failed to derive PMKR1Name (PMK-R1 len %zu)",
+			   pmk_r1_len);
+		return res;
+	}
 	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, title, pmk_r1_name, WPA_PMK_NAME_LEN);
 	return 0;
 }
 
@@ -2001,10 +2236,11 @@
 {
 	u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
 	u8 *pos;
+	int res;
 
-	/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
-	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
-		   pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
+	/* PMK-R1 = KDF-Hash(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-SHA%zu",
+		   pmk_r0_len * 8);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
 	wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
 	wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
@@ -2014,26 +2250,28 @@
 	os_memcpy(pos, s1kh_id, ETH_ALEN);
 	pos += ETH_ALEN;
 
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (pmk_r0_len == SHA512_MAC_LEN)
+		res = sha512_prf(pmk_r0, pmk_r0_len, "FT-R1",
+				 buf, pos - buf, pmk_r1, pmk_r0_len);
+#endif /* CONFIG_SHA512 */
 #ifdef CONFIG_SHA384
-	if (pmk_r0_len == SHA384_MAC_LEN &&
-	    sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
-		       buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
-		return -1;
+	if (pmk_r0_len == SHA384_MAC_LEN)
+		res = sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
+				 buf, pos - buf, pmk_r1, pmk_r0_len);
 #endif /* CONFIG_SHA384 */
-	if (pmk_r0_len == PMK_LEN &&
-	    sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
-		       buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
-		return -1;
-	if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
-		wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
-			   (int) pmk_r0_len);
-		return -1;
+	if (pmk_r0_len == SHA256_MAC_LEN)
+		res = sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
+				 buf, pos - buf, pmk_r1, pmk_r0_len);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "FT: Failed to derive PMK-R1");
+		return res;
 	}
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
 
 	return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
-				      pmk_r1_name,
-				      pmk_r0_len == SHA384_MAC_LEN);
+				      pmk_r1_name, pmk_r0_len);
 }
 
 
@@ -2056,7 +2294,8 @@
 	u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
 	       WPA_KDK_MAX_LEN];
 	size_t ptk_len, offset;
-	int use_sha384 = wpa_key_mgmt_sha384(akmp);
+	size_t key_len;
+	int res;
 
 	if (kdk_len > WPA_KDK_MAX_LEN) {
 		wpa_printf(MSG_ERROR,
@@ -2065,12 +2304,20 @@
 		return -1;
 	}
 
+	if (akmp == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    (pmk_r1_len == SHA256_MAC_LEN || pmk_r1_len == SHA384_MAC_LEN ||
+	     pmk_r1_len == SHA512_MAC_LEN))
+		key_len = pmk_r1_len;
+	else if (wpa_key_mgmt_sha384(akmp))
+		key_len = SHA384_MAC_LEN;
+	else
+		key_len = SHA256_MAC_LEN;
+
 	/*
 	 * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
 	 *                  BSSID || STA-ADDR)
 	 */
-	wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
-		   use_sha384 ? "SHA384" : "SHA256");
+	wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-SHA%zu", key_len * 8);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
 	wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
@@ -2086,39 +2333,52 @@
 	os_memcpy(pos, sta_addr, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
+	ptk->kck_len = wpa_kck_len(akmp, key_len);
 	ptk->kck2_len = wpa_kck2_len(akmp);
-	ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
+	ptk->kek_len = wpa_kek_len(akmp, key_len);
 	ptk->kek2_len = wpa_kek2_len(akmp);
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk->kdk_len = kdk_len;
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
 		ptk->kck2_len + ptk->kek2_len + ptk->kdk_len;
 
+	res = -1;
+#ifdef CONFIG_SHA512
+	if (key_len == SHA512_MAC_LEN) {
+		if (pmk_r1_len != SHA512_MAC_LEN) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Unexpected PMK-R1 length %d (expected %d)",
+				   (int) pmk_r1_len, SHA512_MAC_LEN);
+			return -1;
+		}
+		res = sha512_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+				 buf, pos - buf, tmp, ptk_len);
+	}
+#endif /* CONFIG_SHA512 */
 #ifdef CONFIG_SHA384
-	if (use_sha384) {
+	if (key_len == SHA384_MAC_LEN) {
 		if (pmk_r1_len != SHA384_MAC_LEN) {
 			wpa_printf(MSG_ERROR,
 				   "FT: Unexpected PMK-R1 length %d (expected %d)",
 				   (int) pmk_r1_len, SHA384_MAC_LEN);
 			return -1;
 		}
-		if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
-			       buf, pos - buf, tmp, ptk_len) < 0)
-			return -1;
+		res = sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+				 buf, pos - buf, tmp, ptk_len);
 	}
 #endif /* CONFIG_SHA384 */
-	if (!use_sha384) {
+	if (key_len == SHA256_MAC_LEN) {
 		if (pmk_r1_len != PMK_LEN) {
 			wpa_printf(MSG_ERROR,
 				   "FT: Unexpected PMK-R1 length %d (expected %d)",
 				   (int) pmk_r1_len, PMK_LEN);
 			return -1;
 		}
-		if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
-			       buf, pos - buf, tmp, ptk_len) < 0)
-			return -1;
+		res = sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
+				 buf, pos - buf, tmp, ptk_len);
 	}
+	if (res < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
 
 	/*
@@ -2187,7 +2447,7 @@
  * @akmp: Negotiated key management protocol
  *
  * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
- * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
+ * AKM: 00-0F-AC:3, 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
  * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
  * AKM: 00-0F-AC:11
  * See rsn_pmkid_suite_b()
@@ -2379,8 +2639,12 @@
 		return "WPS";
 	case WPA_KEY_MGMT_SAE:
 		return "SAE";
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+		return "SAE-EXT-KEY";
 	case WPA_KEY_MGMT_FT_SAE:
 		return "FT-SAE";
+	case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+		return "FT-SAE-EXT-KEY";
 	case WPA_KEY_MGMT_OSEN:
 		return "OSEN";
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
@@ -2441,8 +2705,12 @@
 		return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
 	if (akm & WPA_KEY_MGMT_SAE)
 		return RSN_AUTH_KEY_MGMT_SAE;
+	if (akm & WPA_KEY_MGMT_SAE_EXT_KEY)
+		return RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
 	if (akm & WPA_KEY_MGMT_FT_SAE)
 		return RSN_AUTH_KEY_MGMT_FT_SAE;
+	if (akm & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		return RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
 	if (akm & WPA_KEY_MGMT_OWE)
 		return RSN_AUTH_KEY_MGMT_OWE;
 	if (akm & WPA_KEY_MGMT_DPP)
@@ -2602,9 +2870,9 @@
 		return 16;
 	case WPA_CIPHER_TKIP:
 		return 32;
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
@@ -2617,9 +2885,9 @@
 	case WPA_CIPHER_GCMP:
 	case WPA_CIPHER_TKIP:
 		return 6;
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
@@ -2644,8 +2912,9 @@
 		return WPA_ALG_BIP_GMAC_256;
 	case WPA_CIPHER_BIP_CMAC_256:
 		return WPA_ALG_BIP_CMAC_256;
+	default:
+		return WPA_ALG_NONE;
 	}
-	return WPA_ALG_NONE;
 }
 
 
@@ -3025,120 +3294,190 @@
  */
 static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
 {
-	if (pos[1] == 0)
+	u8 len = pos[1];
+	size_t dlen = 2 + len;
+	u32 selector;
+	const u8 *p;
+	size_t left;
+	u8 link_id;
+	char title[50];
+	int ret;
+
+	if (len == 0)
 		return 1;
 
-	if (pos[1] >= 6 &&
-	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
-	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
-	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+	if (len < RSN_SELECTOR_LEN)
+		return 2;
+
+	p = pos + 2;
+	selector = RSN_SELECTOR_GET(p);
+	p += RSN_SELECTOR_LEN;
+	left = len - RSN_SELECTOR_LEN;
+
+	if (left >= 2 && selector == WPA_OUI_TYPE && p[0] == 1 && p[1] == 0) {
 		ie->wpa_ie = pos;
-		ie->wpa_ie_len = pos[1] + 2;
+		ie->wpa_ie_len = dlen;
 		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
 			    ie->wpa_ie, ie->wpa_ie_len);
 		return 0;
 	}
 
-	if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+	if (selector == OSEN_IE_VENDOR_TYPE) {
 		ie->osen = pos;
-		ie->osen_len = pos[1] + 2;
+		ie->osen_len = dlen;
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
-		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
-			    pos, pos[1] + 2);
+	if (left >= PMKID_LEN && selector == RSN_KEY_DATA_PMKID) {
+		ie->pmkid = p;
+		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_KEYID) {
-		ie->key_id = pos + 2 + RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key",
-			    pos, pos[1] + 2);
+	if (left >= 2 && selector == RSN_KEY_DATA_KEYID) {
+		ie->key_id = p;
+		wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key", pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
-		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
-				pos, pos[1] + 2);
+	if (left > 2 && selector == RSN_KEY_DATA_GROUPKEY) {
+		ie->gtk = p;
+		ie->gtk_len = left;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
-		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
-		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
-			    pos, pos[1] + 2);
+	if (left >= ETH_ALEN && selector == RSN_KEY_DATA_MAC_ADDR) {
+		ie->mac_addr = p;
+		wpa_printf(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key: " MACSTR,
+			   MAC2STR(ie->mac_addr));
 		return 0;
 	}
 
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
-		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+	if (left > 2 && selector == RSN_KEY_DATA_IGTK) {
+		ie->igtk = p;
+		ie->igtk_len = left;
 		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
-				pos, pos[1] + 2);
+				pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_BIGTK) {
-		ie->bigtk = pos + 2 + RSN_SELECTOR_LEN;
-		ie->bigtk_len = pos[1] - RSN_SELECTOR_LEN;
+	if (left > 2 && selector == RSN_KEY_DATA_BIGTK) {
+		ie->bigtk = p;
+		ie->bigtk_len = left;
 		wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK in EAPOL-Key",
-				pos, pos[1] + 2);
+				pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
-		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+	if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = p;
 		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
-			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+			    ie->ip_addr_req, left);
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
-		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+	if (left >= 3 * 4 && selector == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = p;
 		wpa_hexdump(MSG_DEBUG,
 			    "WPA: IP Address Allocation in EAPOL-Key",
-			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+			    ie->ip_addr_alloc, left);
 		return 0;
 	}
 
-	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
-		ie->oci = pos + 2 + RSN_SELECTOR_LEN;
-		ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
+	if (left > 2 && selector == RSN_KEY_DATA_OCI) {
+		ie->oci = p;
+		ie->oci_len = left;
 		wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key",
-			    pos, pos[1] + 2);
+			    pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_TRANSITION_DISABLE) {
-		ie->transition_disable = pos + 2 + RSN_SELECTOR_LEN;
-		ie->transition_disable_len = pos[1] - RSN_SELECTOR_LEN;
+	if (left >= 1 && selector == WFA_KEY_DATA_TRANSITION_DISABLE) {
+		ie->transition_disable = p;
+		ie->transition_disable_len = left;
 		wpa_hexdump(MSG_DEBUG,
 			    "WPA: Transition Disable KDE in EAPOL-Key",
-			    pos, pos[1] + 2);
+			    pos, dlen);
 		return 0;
 	}
 
-	if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
-	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) {
-		ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN;
-		ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN;
-		wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key",
-			    pos, pos[1] + 2);
+	if (left >= 2 && selector == WFA_KEY_DATA_DPP) {
+		ie->dpp_kde = p;
+		ie->dpp_kde_len = left;
+		wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key", pos, dlen);
+		return 0;
+	}
+
+	if (left >= RSN_MLO_GTK_KDE_PREFIX_LENGTH &&
+	    selector == RSN_KEY_DATA_MLO_GTK) {
+		link_id = (p[0] & RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK) >>
+			RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT;
+		if (link_id >= MAX_NUM_MLO_LINKS)
+			return 2;
+
+		ie->valid_mlo_gtks |= BIT(link_id);
+		ie->mlo_gtk[link_id] = p;
+		ie->mlo_gtk_len[link_id] = left;
+		ret = os_snprintf(title, sizeof(title),
+				  "RSN: Link ID %u - MLO GTK KDE in EAPOL-Key",
+				  link_id);
+		if (!os_snprintf_error(sizeof(title), ret))
+			wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+		return 0;
+	}
+
+	if (left >= RSN_MLO_IGTK_KDE_PREFIX_LENGTH &&
+	    selector == RSN_KEY_DATA_MLO_IGTK) {
+		link_id = (p[8] & RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK) >>
+			  RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT;
+		if (link_id >= MAX_NUM_MLO_LINKS)
+			return 2;
+
+		ie->valid_mlo_igtks |= BIT(link_id);
+		ie->mlo_igtk[link_id] = p;
+		ie->mlo_igtk_len[link_id] = left;
+		ret = os_snprintf(title, sizeof(title),
+				  "RSN: Link ID %u - MLO IGTK KDE in EAPOL-Key",
+				  link_id);
+		if (!os_snprintf_error(sizeof(title), ret))
+			wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+		return 0;
+	}
+
+	if (left >= RSN_MLO_BIGTK_KDE_PREFIX_LENGTH &&
+	    selector == RSN_KEY_DATA_MLO_BIGTK) {
+		link_id = (p[8] & RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK) >>
+			  RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT;
+		if (link_id >= MAX_NUM_MLO_LINKS)
+			return 2;
+
+		ie->valid_mlo_bigtks |= BIT(link_id);
+		ie->mlo_bigtk[link_id] = p;
+		ie->mlo_bigtk_len[link_id] = left;
+		ret = os_snprintf(title, sizeof(title),
+				  "RSN: Link ID %u - MLO BIGTK KDE in EAPOL-Key",
+				  link_id);
+		if (!os_snprintf_error(sizeof(title), ret))
+			wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+		return 0;
+	}
+
+	if (left >= RSN_MLO_LINK_KDE_FIXED_LENGTH &&
+	    selector == RSN_KEY_DATA_MLO_LINK) {
+		link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >>
+			  RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT;
+		if (link_id >= MAX_NUM_MLO_LINKS)
+			return 2;
+
+		ie->valid_mlo_links |= BIT(link_id);
+		ie->mlo_link[link_id] = p;
+		ie->mlo_link_len[link_id] = left;
+		ret = os_snprintf(title, sizeof(title),
+				  "RSN: Link ID %u - MLO Link KDE in EAPOL-Key",
+				  link_id);
+		if (!os_snprintf_error(sizeof(title), ret))
+			wpa_hexdump(MSG_DEBUG, title, pos, dlen);
 		return 0;
 	}
 
@@ -3157,15 +3496,17 @@
 {
 	const u8 *pos, *end;
 	int ret = 0;
+	size_t dlen = 0;
 
 	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
+	for (pos = buf, end = pos + len; end - pos > 1; pos += dlen) {
 		if (pos[0] == 0xdd &&
 		    ((pos == buf + len - 1) || pos[1] == 0)) {
 			/* Ignore padding */
 			break;
 		}
-		if (2 + pos[1] > end - pos) {
+		dlen = 2 + pos[1];
+		if ((int) dlen > end - pos) {
 			wpa_printf(MSG_DEBUG,
 				   "WPA: EAPOL-Key Key Data underflow (ie=%d len=%d pos=%d)",
 				   pos[0], pos[1], (int) (pos - buf));
@@ -3175,22 +3516,22 @@
 		}
 		if (*pos == WLAN_EID_RSN) {
 			ie->rsn_ie = pos;
-			ie->rsn_ie_len = pos[1] + 2;
+			ie->rsn_ie_len = dlen;
 			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
 				    ie->rsn_ie, ie->rsn_ie_len);
 		} else if (*pos == WLAN_EID_RSNX) {
 			ie->rsnxe = pos;
-			ie->rsnxe_len = pos[1] + 2;
+			ie->rsnxe_len = dlen;
 			wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key",
 				    ie->rsnxe, ie->rsnxe_len);
 		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
 			ie->mdie = pos;
-			ie->mdie_len = pos[1] + 2;
+			ie->mdie_len = dlen;
 			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
 				    ie->mdie, ie->mdie_len);
 		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
 			ie->ftie = pos;
-			ie->ftie_len = pos[1] + 2;
+			ie->ftie_len = dlen;
 			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
 				    ie->ftie, ie->ftie_len);
 		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
@@ -3198,31 +3539,31 @@
 				ie->reassoc_deadline = pos;
 				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
 					    "in EAPOL-Key",
-					    ie->reassoc_deadline, pos[1] + 2);
+					    ie->reassoc_deadline, dlen);
 			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
 				ie->key_lifetime = pos;
 				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
 					    "in EAPOL-Key",
-					    ie->key_lifetime, pos[1] + 2);
+					    ie->key_lifetime, dlen);
 			} else {
 				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
 					    "EAPOL-Key Key Data IE",
-					    pos, 2 + pos[1]);
+					    pos, dlen);
 			}
 		} else if (*pos == WLAN_EID_LINK_ID) {
 			if (pos[1] >= 18) {
 				ie->lnkid = pos;
-				ie->lnkid_len = pos[1] + 2;
+				ie->lnkid_len = dlen;
 			}
 		} else if (*pos == WLAN_EID_EXT_CAPAB) {
 			ie->ext_capab = pos;
-			ie->ext_capab_len = pos[1] + 2;
+			ie->ext_capab_len = dlen;
 		} else if (*pos == WLAN_EID_SUPP_RATES) {
 			ie->supp_rates = pos;
-			ie->supp_rates_len = pos[1] + 2;
+			ie->supp_rates_len = dlen;
 		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
 			ie->ext_supp_rates = pos;
-			ie->ext_supp_rates_len = pos[1] + 2;
+			ie->ext_supp_rates_len = dlen;
 		} else if (*pos == WLAN_EID_HT_CAP &&
 			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
 			ie->ht_capabilities = pos + 2;
@@ -3276,7 +3617,7 @@
 		} else {
 			wpa_hexdump(MSG_DEBUG,
 				    "WPA: Unrecognized EAPOL-Key Key Data IE",
-				    pos, 2 + pos[1]);
+				    pos, dlen);
 		}
 	}
 
@@ -3375,6 +3716,9 @@
 	case WPA_KEY_MGMT_SAE:
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
 		break;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+		break;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 	case WPA_KEY_MGMT_FILS_SHA256:
@@ -3588,6 +3932,7 @@
 	switch (data->key_mgmt) {
 #ifdef CONFIG_SAE
 	case WPA_KEY_MGMT_SAE:
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
 	/* fall through */
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
@@ -3741,4 +4086,27 @@
 		wpabuf_put_u8(buf, capab);
 }
 
+
+/*
+ * wpa_pasn_add_extra_ies - Add protocol specific IEs in Authentication
+ * frame for PASN.
+ *
+ * @buf: Buffer in which the elements will be added
+ * @extra_ies: Protocol specific elements to add
+ * @len: Length of the elements
+ * Returns: 0 on success, -1 on failure
+ */
+
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len)
+{
+	if (!len || !extra_ies || !buf)
+		return 0;
+
+	if (wpabuf_tailroom(buf) < sizeof(len))
+		return -1;
+
+	wpabuf_put_data(buf, extra_ies, len);
+	return 0;
+}
+
 #endif /* CONFIG_PASN */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c28c55d..05b1a8a 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -81,8 +81,14 @@
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
 #define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
 #define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
-
+#define RSN_AUTH_KEY_MGMT_FT_PSK_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 20)
 #define RSN_AUTH_KEY_MGMT_PASN RSN_SELECTOR(0x00, 0x0f, 0xac, 21)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384_UNRESTRICTED \
+	RSN_SELECTOR(0x00, 0x0f, 0xac, 22)
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 23)
+#define RSN_AUTH_KEY_MGMT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 24)
+#define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
 
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
@@ -126,6 +132,10 @@
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 #define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 #define RSN_KEY_DATA_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
+#define RSN_KEY_DATA_MLO_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
+#define RSN_KEY_DATA_MLO_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
+#define RSN_KEY_DATA_MLO_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
+#define RSN_KEY_DATA_MLO_LINK RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
 
 #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
 #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
@@ -176,6 +186,18 @@
 #define FT_R1KH_ID_LEN 6
 #define WPA_PMK_NAME_LEN 16
 
+/* FTE - MIC Control - RSNXE Used */
+#define FTE_MIC_CTRL_RSNXE_USED BIT(0)
+#define FTE_MIC_CTRL_MIC_LEN_MASK (BIT(1) | BIT(2) | BIT(3))
+#define FTE_MIC_CTRL_MIC_LEN_SHIFT 1
+
+/* FTE - MIC Length subfield values */
+enum ft_mic_len_subfield {
+	FTE_MIC_LEN_16 = 0,
+	FTE_MIC_LEN_24 = 1,
+	FTE_MIC_LEN_32 = 2,
+};
+
 
 /* IEEE 802.11, 8.5.2 EAPOL-Key frames */
 #define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
@@ -222,6 +244,7 @@
 #define FILS_FT_MAX_LEN 48
 #define WPA_PASN_KCK_LEN 32
 #define WPA_PASN_MIC_MAX_LEN 24
+#define WPA_LTF_KEYSEED_MAX_LEN 48
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -234,12 +257,14 @@
 	u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
 	u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
 	u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */
+	u8 ltf_keyseed[WPA_LTF_KEYSEED_MAX_LEN]; /* LTF Key seed */
 	size_t kck_len;
 	size_t kek_len;
 	size_t tk_len;
 	size_t kck2_len;
 	size_t kek2_len;
 	size_t kdk_len;
+	size_t ltf_keyseed_len;
 	int installed; /* 1 if key has already been installed to driver */
 };
 
@@ -330,6 +355,40 @@
 	u8 bigtk[WPA_BIGTK_MAX_LEN];
 } STRUCT_PACKED;
 
+#define RSN_MLO_GTK_KDE_PREFIX_LENGTH		(1 + 6)
+#define RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK	0x03
+#define RSN_MLO_GTK_KDE_PREFIX0_TX		0x04
+#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT	4
+#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK	0xF0
+
+#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH		(2 + 6 + 1)
+#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT	4
+#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK	0xF0
+struct rsn_mlo_igtk_kde {
+	u8 keyid[2];
+	u8 pn[6];
+	u8 prefix8;
+	u8 igtk[WPA_IGTK_MAX_LEN];
+} STRUCT_PACKED;
+
+#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH		(2 + 6 + 1)
+#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT	4
+#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK	0xF0
+struct rsn_mlo_bigtk_kde {
+	u8 keyid[2];
+	u8 pn[6];
+	u8 prefix8;
+	u8 bigtk[WPA_BIGTK_MAX_LEN];
+} STRUCT_PACKED;
+
+#define RSN_MLO_LINK_KDE_FIXED_LENGTH		(1 + 6)
+#define RSN_MLO_LINK_KDE_LINK_INFO_INDEX	0
+#define RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT	0
+#define RSN_MLO_LINK_KDE_LI_LINK_ID_MASK	0x0F
+#define RSN_MLO_LINK_KDE_LI_RSNE_INFO		0x10
+#define RSN_MLO_LINK_KDE_LI_RSNXE_INFO		0x20
+#define RSN_MLO_LINK_KDE_LINK_MAC_INDEX		1
+
 struct rsn_mdie {
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 ft_capab;
@@ -354,6 +413,14 @@
 	/* followed by optional parameters */
 } STRUCT_PACKED;
 
+struct rsn_ftie_sha512 {
+	u8 mic_control[2];
+	u8 mic[32];
+	u8 anonce[WPA_NONCE_LEN];
+	u8 snonce[WPA_NONCE_LEN];
+	/* followed by optional parameters */
+} STRUCT_PACKED;
+
 #define FTIE_SUBELEM_R1KH_ID 1
 #define FTIE_SUBELEM_GTK 2
 #define FTIE_SUBELEM_R0KH_ID 3
@@ -408,7 +475,7 @@
 		     size_t *key_auth_len);
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+int wpa_ft_mic(int key_mgmt, const u8 *kck, size_t kck_len, const u8 *sta_addr,
 	       const u8 *ap_addr, u8 transaction_seqnum,
 	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
@@ -420,9 +487,10 @@
 		      const u8 *ssid, size_t ssid_len,
 		      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
 		      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
-		      int use_sha384);
+		      int key_mgmt);
 int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
-			   const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
+			   const u8 *s1kh_id, u8 *pmk_r1_name,
+			   size_t pmk_r1_len);
 int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
 		      const u8 *pmk_r0_name,
 		      const u8 *r1kh_id, const u8 *s1kh_id,
@@ -497,6 +565,10 @@
 	size_t r0kh_id_len;
 	const u8 *fte_anonce;
 	const u8 *fte_snonce;
+	bool fte_rsnxe_used;
+	unsigned int fte_elem_count;
+	const u8 *fte_mic;
+	size_t fte_mic_len;
 	const u8 *rsn;
 	size_t rsn_len;
 	u16 rsn_capab;
@@ -552,7 +624,7 @@
 #define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
-		     int use_sha384);
+		     int key_mgmt);
 
 struct wpa_eapol_ie_parse {
 	const u8 *wpa_ie;
@@ -564,7 +636,6 @@
 	const u8 *gtk;
 	size_t gtk_len;
 	const u8 *mac_addr;
-	size_t mac_addr_len;
 	const u8 *igtk;
 	size_t igtk_len;
 	const u8 *bigtk;
@@ -608,6 +679,19 @@
 	u16 aid;
 	const u8 *wmm;
 	size_t wmm_len;
+#define MAX_NUM_MLO_LINKS 15
+	u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
+	const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
+	size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
+	u16 valid_mlo_igtks; /* bitmap of valid link IGTK KDEs */
+	const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
+	size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+	u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK KDEs */
+	const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
+	size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+	u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
+	const u8 *mlo_link[MAX_NUM_MLO_LINKS];
+	size_t mlo_link_len[MAX_NUM_MLO_LINKS];
 };
 
 int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
@@ -638,6 +722,7 @@
 int wpa_use_aes_key_wrap(int akmp);
 int fils_domain_name_hash(const char *domain, u8 *hash);
 
+bool pasn_use_sha384(int akmp, int cipher);
 int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		    const u8 *spa, const u8 *bssid,
 		    const u8 *dhss, size_t dhss_len,
@@ -651,6 +736,8 @@
 	     const u8 *data, size_t data_len,
 	     const u8 *frame, size_t frame_len, u8 *mic);
 
+int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher);
+
 int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
 			 u8 *hash);
 
@@ -674,5 +761,6 @@
 				struct wpa_pasn_params_data *pasn_params);
 
 void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab);
+int wpa_pasn_add_extra_ies(struct wpabuf *buf, const u8 *extra_ies, size_t len);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 055bf73..4ab2a1b 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -92,6 +92,15 @@
 #define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
 /** Channel switch (followed by freq=<MHz> and other channel parameters) */
 #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
+/** MLO link channel switch started (followed by freq=<MHz> and other channel
+ * parameters)
+ */
+#define WPA_EVENT_LINK_CHANNEL_SWITCH_STARTED \
+	"CTRL-EVENT-STARTED-LINK-CHANNEL-SWITCH "
+/** MLO link channel switch (followed by freq=<MHz> and other channel
+ * parameters)
+ */
+#define WPA_EVENT_LINK_CHANNEL_SWITCH "CTRL-EVENT-LINK-CHANNEL-SWITCH "
 /** SAE authentication failed due to unknown password identifier */
 #define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
 	"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
@@ -213,6 +222,9 @@
 #define DPP_EVENT_CSR "DPP-CSR "
 #define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
 #define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED "
+#define DPP_EVENT_PB_STATUS "DPP-PB-STATUS "
+#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT "
+#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER "
 
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -454,6 +466,8 @@
 #define WPA_BSS_MASK_UPDATE_IDX		BIT(22)
 #define WPA_BSS_MASK_BEACON_IE		BIT(23)
 #define WPA_BSS_MASK_FILS_INDICATION	BIT(24)
+#define WPA_BSS_MASK_RNR		BIT(25)
+#define WPA_BSS_MASK_ML			BIT(26)
 
 
 /* VENDOR_ELEM_* frame id values */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index e4f3eb3..ff0869c 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1011,6 +1011,16 @@
 struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
 
 /**
+ * crypto_ec_key_set_priv - Initialize EC key pair from raw key data
+ * @group: Identifying number for the ECC group
+ * @raw: Raw key data
+ * @raw_len: Length of @raw buffer
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+					      const u8 *raw, size_t raw_len);
+
+/**
  * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1
  * @der: DER encoding of ASN.1 SubjectPublicKeyInfo
  * @der_len: Length of @der buffer
@@ -1058,7 +1068,8 @@
 /**
  * crypto_ec_key_get_subject_public_key - Get SubjectPublicKeyInfo ASN.1 for an EC key
  * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
- * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo or %NULL on failure
+ * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo using
+ * compressed point format, or %NULL on failure
  */
 struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
 
@@ -1086,16 +1097,20 @@
  * crypto_ec_key_get_public_key - Get EC public key as an EC point
  * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
  * Returns: Public key as an EC point or %NULL on failure
+ *
+ * The caller needs to free the returned value with crypto_ec_point_deinit().
  */
-const struct crypto_ec_point *
+struct crypto_ec_point *
 crypto_ec_key_get_public_key(struct crypto_ec_key *key);
 
 /**
  * crypto_ec_key_get_private_key - Get EC private key as a bignum
  * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
  * Returns: Private key as a bignum or %NULL on failure
+ *
+ * The caller needs to free the returned value with crypto_bignum_deinit().
  */
-const struct crypto_bignum *
+struct crypto_bignum *
 crypto_ec_key_get_private_key(struct crypto_ec_key *key);
 
 /**
@@ -1309,6 +1324,58 @@
  */
 void crypto_rsa_key_free(struct crypto_rsa_key *key);
 
+enum hpke_mode {
+	HPKE_MODE_BASE = 0x00,
+	HPKE_MODE_PSK = 0x01,
+	HPKE_MODE_AUTH = 0x02,
+	HPKE_MODE_AUTH_PSK = 0x03,
+};
+
+enum hpke_kem_id {
+	HPKE_DHKEM_P256_HKDF_SHA256 = 0x0010,
+	HPKE_DHKEM_P384_HKDF_SHA384 = 0x0011,
+	HPKE_DHKEM_P521_HKDF_SHA512 = 0x0012,
+	HPKE_DHKEM_X5519_HKDF_SHA256 = 0x0020,
+	HPKE_DHKEM_X448_HKDF_SHA512 = 0x0021,
+};
+
+enum hpke_kdf_id {
+	HPKE_KDF_HKDF_SHA256 = 0x0001,
+	HPKE_KDF_HKDF_SHA384 = 0x0002,
+	HPKE_KDF_HKDF_SHA512 = 0x0003,
+};
+
+enum hpke_aead_id {
+	HPKE_AEAD_AES_128_GCM = 0x0001,
+	HPKE_AEAD_AES_256_GCM = 0x0002,
+	HPKE_AEAD_CHACHA20POLY1305 = 0x0003,
+};
+
+/**
+ * hpke_base_seal - HPKE base mode single-shot encrypt
+ * Returns: enc | ct; or %NULL on failure
+ */
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len);
+
+/**
+ * hpke_base_open - HPKE base mode single-shot decrypt
+ * @enc_ct: enc | ct
+ * Returns: pt; or %NULL on failure
+ */
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len);
+
 /**
  * crypto_unload - Unload crypto resources
  *
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index fafb688..4147f41 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -2190,6 +2190,285 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static const struct hpke_test {
+	const char *name;
+	enum hpke_mode mode;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
+	const char *info;
+	int sk_r_group;
+	const char *pk_r;
+	const char *sk_r;
+	const char *enc;
+	const char *pt;
+	const char *aad;
+	const char *ct;
+} hpke_tests[] = {
+	{
+		.name = "A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA256,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 19,
+		.pk_r = "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0",
+		.sk_r = "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2",
+		.enc = "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434",
+	},
+	{
+		.name = "A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 19,
+		.pk_r = "04085aa5b665dc3826f9650ccbcc471be268c8ada866422f739e2d531d4a8818a9466bc6b449357096232919ec4fe9070ccbac4aac30f4a1a53efcf7af90610edd",
+		.sk_r = "3ac8530ad1b01885960fab38cf3cdc4f7aef121eaa239f222623614b4079fb38",
+		.enc = "0493ed86735bdfb978cc055c98b45695ad7ce61ce748f4dd63c525a3b8d53a15565c6897888070070c1579db1f86aaa56deb8297e64db7e8924e72866f9a472580",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "d3cf4984931484a080f74c1bb2a6782700dc1fef9abe8442e44a6f09044c88907200b332003543754eb51917ba",
+	},
+	{
+		.name = "A.6. DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 21,
+		.pk_r = "0401b45498c1714e2dce167d3caf162e45e0642afc7ed435df7902ccae0e84ba0f7d373f646b7738bbbdca11ed91bdeae3cdcba3301f2457be452f271fa6837580e661012af49583a62e48d44bed350c7118c0d8dc861c238c72a2bda17f64704f464b57338e7f40b60959480c0e58e6559b190d81663ed816e523b6b6a418f66d2451ec64",
+		.sk_r = "01462680369ae375e4b3791070a7458ed527842f6a98a79ff5e0d4cbde83c27196a3916956655523a6a2556a7af62c5cadabe2ef9da3760bb21e005202f7b2462847",
+		.enc = "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a",
+	},
+	{ /* self-generated test vector for P-384 */
+		.name = "custom DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+		.kdf_id = HPKE_KDF_HKDF_SHA384,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 20,
+		.pk_r = "049c0e4dcbbb3c80715cafaa1839d0bc3c3adcc95eb8062f84175f9c3cec115e6b799061c65a0605907785c25b3571564706a8ba6a204452b38c7c205db17d328f2353df05d5f1c568e7503331178c36c2d37bbed48401295407face3f8dae5ed8",
+		.sk_r = "cabffb07d20ffcfdaa043e1de49e1654659e0f0aba5de56523e8b73dc80c579a9e5c89ed3810ec21c4bafcf74ad2a245",
+		.enc = "04b30bea96d0e51582033b02a4d676d0464a5eb2d858be86cda1c4e6f8b2aa9fb80f5365483f781b1b3a8b3b8efd50b0f7bca16f06d0435fa3da1d671ea0a318b40fe170a074923c651e5dc824966b7b98d0e36bdf932875dae7130369a793cecc",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "ae7feccfea0f8fcd620d15369a28db8701cdc90d55c20efff6296bd441697b0da34671d1f3c4864183e86d27fc",
+	},
+	{ /* self-generated test vector for BP-256 */
+		.name = "custom PB-256 using DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+		.kdf_id = HPKE_KDF_HKDF_SHA256,
+		.aead_id = HPKE_AEAD_AES_128_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 28,
+		.pk_r = "04a2cb9c4cae90cdc1c27516e9f84b6b166e4b1dcc517286268239ddb0bf74cca6390fed092ac4423ab2192b8bb41a4824d908d2053b93fc813830bebac5ce19b9",
+		.sk_r = "11d9db41c4341166ca52f5a1775595c0bdb4934350daeb7bce659c4b7a40e314",
+		.enc = "047a25e309c7ee50ec27f13d44734a3ccd8c703e3affcc728513df416511ef9bf02f5e7750e7415de8b5f306ebd3fc88ea9b9368523eb1733a8d82c1a877e5a0f4",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "17c84b3f07f6ffe08ff2be45c709ea782229504aa5b2253876725c6c39f8d8c992304fc5877994f79d6c10d462",
+	},
+	{ /* self-generated test vector for BP-384 */
+		.name = "custom PB-384 using DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+		.kdf_id = HPKE_KDF_HKDF_SHA384,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 29,
+		.pk_r = "041f4199ad28835908079c45d165d55630098be53eb4beede9921f5b2204fa396111f99ac54c56411f7cb2c43ec18d8e604d895027228cf975f5a4b598f189d8fb03e3fefe020258c40d4d1b15fd7587d209925d67a41f9659a8ed6f662fb441e4",
+		.sk_r = "7017cf8a5a9a81ad4e0d755ccbea27a378b787561f8d5662639850805fefcbaab6b9a15729872abb7dc53d19a6cf77e4",
+		.enc = "0415d49dedc5bc1ffe9f8de9022c266bb605ec6cd7b77b6ce68974095398856f8aefa4b7abbfbd496b99a2dda3a9c65f1a71b9d40255aa1c7c4205a8b4ef611b96ed29fd2d7b0cde4c0e82058805e6276025cc4fc606f6e5771c31bd9704e9ba0b",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "5f5e9f82bedadec0e9b01a1b304cb48b05c0d6d397b1c8a95ed541218ec54f634a41cbc4066910a409e47b254e",
+	},
+	{ /* self-generated test vector for BP-512 */
+		.name = "custom PB-512 using DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+		.mode = HPKE_MODE_BASE,
+		.kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+		.kdf_id = HPKE_KDF_HKDF_SHA512,
+		.aead_id = HPKE_AEAD_AES_256_GCM,
+		.info = "4f6465206f6e2061204772656369616e2055726e",
+		.sk_r_group = 30,
+		.pk_r = "049e81046a531365a3b5215ac37e7b38f5fa34f86c4eb2e03113b197390a26c555bb007596e131c2541f336eb24a45f44283b5b53fedddfa5642675602fdec17d34120a35efffb44952e32dee7732f2f3245c3314269996b610703a63fb8555a75ca5092690a1125ae8712c1e31fd77aee42bd052e71f9f9459814d6f4065bcea0",
+		.sk_r = "483b6882608182b296843fa7dfffbdd61ed0372574d4aa32a035c8e33a493927aaf00d42bd9124ebe4df26010b38124668c02b35a749e74845d565734310cfe9",
+		.enc = "04158d18473aeb3b283d3345b1a87d3de2b192ff9e41b5a98f91daacfb24be72e698cbc04c33078681e507bf346c0ea70c927083a22ca9ea027f420067ee42285b798d95fea51002d097ce28371883202bfd300fb64943669e32c6f1a348087368bb480b757892ebd199a9389978c92cbc44076626d705a771fbbd90c030a6767e",
+		.pt = "4265617574792069732074727574682c20747275746820626561757479",
+		.aad = "436f756e742d30",
+		.ct = "033d91c4514857da5b833635180c1acc09f175cbf44777a7b71e177705cfd17437b1c85d671dd767bb4fe20e2e",
+	},
+};
+
+
+static int run_hpke_test(const struct hpke_test *test)
+{
+	struct wpabuf *info, *pk_r, *sk_r, *enc, *pt, *aad, *ct;
+	struct wpabuf *res_pt = NULL, *enc_ct = NULL, *res_ct = NULL;
+	struct crypto_ec_key *own_priv = NULL, *peer_pub = NULL;
+	int res = -1;
+	size_t coord_len;
+
+	wpa_printf(MSG_INFO, "- %s", test->name);
+
+	info = wpabuf_parse_bin(test->info);
+	pk_r = wpabuf_parse_bin(test->pk_r);
+	sk_r = wpabuf_parse_bin(test->sk_r);
+	enc = wpabuf_parse_bin(test->enc);
+	pt = wpabuf_parse_bin(test->pt);
+	aad = wpabuf_parse_bin(test->aad);
+	ct = wpabuf_parse_bin(test->ct);
+	if (!info || !pk_r || !sk_r || !enc || !pt || !aad || !ct) {
+		wpa_printf(MSG_ERROR, "Could not parse test data");
+		goto fail;
+	}
+
+	/* Receiver - decryption against the test vector */
+
+	enc_ct = wpabuf_concat(enc, ct);
+	enc = NULL;
+	ct = NULL;
+	if (!enc_ct)
+		goto fail;
+
+	own_priv = crypto_ec_key_set_priv(test->sk_r_group, wpabuf_head(sk_r),
+					  wpabuf_len(sk_r));
+	if (!own_priv) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed to set private key");
+		goto fail;
+	}
+
+	res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+				own_priv,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(enc_ct), wpabuf_len(enc_ct));
+	if (!res_pt) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt");
+		wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+		goto fail;
+	}
+	if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+	    os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+		      wpabuf_len(pt)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed - decryption mismatch");
+		goto fail;
+	}
+
+	/* Sender - encryption (randomized algorithm) */
+
+	if (test->sk_r_group == 19)
+		coord_len = 32;
+	else if (test->sk_r_group == 20)
+		coord_len = 48;
+	else if (test->sk_r_group == 21)
+		coord_len = 66;
+	else if (test->sk_r_group == 28)
+		coord_len = 32;
+	else if (test->sk_r_group == 29)
+		coord_len = 48;
+	else if (test->sk_r_group == 30)
+		coord_len = 64;
+	else
+		goto fail;
+	if (wpabuf_len(pk_r) != 1 + 2 * coord_len) {
+		wpa_printf(MSG_ERROR, "Unexpected pkR length (%zu != %zu)",
+			   wpabuf_len(pk_r), 1 + 2 * coord_len);
+		goto fail;
+	}
+	peer_pub = crypto_ec_key_set_pub(test->sk_r_group,
+					 wpabuf_head_u8(pk_r) + 1,
+					 wpabuf_head_u8(pk_r) + 1 + coord_len,
+					 coord_len);
+	if (!peer_pub) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed to set public key");
+		goto fail;
+	}
+
+	res_ct = hpke_base_seal(test->kem_id, test->kdf_id, test->aead_id,
+				peer_pub,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(pt), wpabuf_len(pt));
+	if (!res_ct) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to encrypt");
+		goto fail;
+	}
+
+	/* Receiver - decryption (to verify own encryption) */
+
+	wpabuf_free(res_pt);
+	res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+				own_priv,
+				wpabuf_head(info), wpabuf_len(info),
+				wpabuf_head(aad), wpabuf_len(aad),
+				wpabuf_head(res_ct), wpabuf_len(res_ct));
+	if (!res_pt) {
+		wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt own encrypted version");
+		goto fail;
+	}
+	if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+	    os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+		      wpabuf_len(pt)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "HPKE base open - failed - decryption mismatch for own encrypted version");
+		wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+		goto fail;
+	}
+
+	res = 0;
+fail:
+	wpabuf_free(info);
+	wpabuf_free(pk_r);
+	wpabuf_free(sk_r);
+	wpabuf_free(enc);
+	wpabuf_free(pt);
+	wpabuf_free(aad);
+	wpabuf_free(ct);
+	wpabuf_free(enc_ct);
+	wpabuf_free(res_pt);
+	wpabuf_free(res_ct);
+	crypto_ec_key_deinit(own_priv);
+	return res;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+static int test_hpke(void)
+{
+#ifdef CONFIG_DPP3
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "RFC 9180 - HPKE");
+	for (i = 0; i < ARRAY_SIZE(hpke_tests); i++) {
+		if (run_hpke_test(&hpke_tests[i]) < 0)
+			return -1;
+	}
+
+	wpa_printf(MSG_INFO, "HPKE base open test cases passed");
+#endif /* CONFIG_DPP3 */
+	return 0;
+}
+
+
 static int test_ms_funcs(void)
 {
 #ifndef CONFIG_FIPS
@@ -2310,6 +2589,7 @@
 	    test_sha384() ||
 	    test_fips186_2_prf() ||
 	    test_extract_expand_hkdf() ||
+	    test_hpke() ||
 	    test_ms_funcs())
 		ret = -1;
 
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index c6e065f..22f6ab4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -16,6 +16,7 @@
 #include <openssl/dh.h>
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
+#include <openssl/rsa.h>
 #include <openssl/pem.h>
 #ifdef CONFIG_ECC
 #include <openssl/ec.h>
@@ -25,6 +26,8 @@
 #include <openssl/provider.h>
 #include <openssl/core_names.h>
 #include <openssl/param_build.h>
+#include <openssl/encoder.h>
+#include <openssl/decoder.h>
 #else /* OpenSSL version >= 3.0 */
 #include <openssl/cmac.h>
 #endif /* OpenSSL version >= 3.0 */
@@ -117,6 +120,19 @@
 {
 	return ASN1_STRING_data((ASN1_STRING *) x);
 }
+
+
+static const ASN1_TIME * X509_get0_notBefore(const X509 *x)
+{
+	return X509_get_notBefore(x);
+}
+
+
+static const ASN1_TIME * X509_get0_notAfter(const X509 *x)
+{
+	return X509_get_notAfter(x);
+}
+
 #endif /* OpenSSL version < 1.1.0 */
 
 
@@ -166,7 +182,6 @@
 
 
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
-static OSSL_PROVIDER *openssl_default_provider = NULL;
 static OSSL_PROVIDER *openssl_legacy_provider = NULL;
 #endif /* OpenSSL version >= 3.0 */
 
@@ -176,9 +191,7 @@
 	if (openssl_legacy_provider)
 		return;
 
-	openssl_legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
-	if (openssl_legacy_provider && !openssl_default_provider)
-		openssl_default_provider = OSSL_PROVIDER_load(NULL, "default");
+	openssl_legacy_provider = OSSL_PROVIDER_try_load(NULL, "legacy", 1);
 #endif /* OpenSSL version >= 3.0 */
 }
 
@@ -190,10 +203,6 @@
 		OSSL_PROVIDER_unload(openssl_legacy_provider);
 		openssl_legacy_provider = NULL;
 	}
-	if (openssl_default_provider) {
-		OSSL_PROVIDER_unload(openssl_default_provider);
-		openssl_default_provider = NULL;
-	}
 #endif /* OpenSSL version >= 3.0 */
 }
 
@@ -304,12 +313,12 @@
 
 
 #ifndef CONFIG_FIPS
+
 int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	openssl_load_legacy_provider();
 	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
 }
-#endif /* CONFIG_FIPS */
 
 
 int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
@@ -388,11 +397,11 @@
 #endif /* CONFIG_NO_RC4 */
 
 
-#ifndef CONFIG_FIPS
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
 }
+
 #endif /* CONFIG_FIPS */
 
 
@@ -438,9 +447,9 @@
 		return EVP_aes_192_ecb();
 	case 32:
 		return EVP_aes_256_ecb();
+	default:
+		return NULL;
 	}
-
-	return NULL;
 }
 
 
@@ -1347,21 +1356,22 @@
 
 	ctx = os_zalloc(sizeof(*ctx));
 	if (!ctx)
-		return NULL;
+		goto fail;
 	ctx->ctx = EVP_MAC_CTX_new(mac);
 	if (!ctx->ctx) {
-		EVP_MAC_free(mac);
 		os_free(ctx);
-		return NULL;
+		ctx = NULL;
+		goto fail;
 	}
 
 	if (EVP_MAC_init(ctx->ctx, key, key_len, params) != 1) {
 		EVP_MAC_CTX_free(ctx->ctx);
 		bin_clear_free(ctx, sizeof(*ctx));
-		EVP_MAC_free(mac);
-		return NULL;
+		ctx = NULL;
+		goto fail;
 	}
 
+fail:
 	EVP_MAC_free(mac);
 	return ctx;
 #else /* OpenSSL version >= 3.0 */
@@ -2139,9 +2149,7 @@
 int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
 			 struct crypto_bignum *r)
 {
-	/* Note: BN_rshift() does not modify the first argument even though it
-	 * has not been marked const. */
-	return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
+	return BN_rshift((BIGNUM *) r, (const BIGNUM *) a, n) == 1 ? 0 : -1;
 }
 
 
@@ -2216,6 +2224,7 @@
 struct crypto_ec {
 	EC_GROUP *group;
 	int nid;
+	int iana_group;
 	BN_CTX *bnctx;
 	BIGNUM *prime;
 	BIGNUM *order;
@@ -2260,6 +2269,44 @@
 }
 
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static const char * crypto_ec_group_2_name(int group)
+{
+	/* Map from IANA registry for IKE D-H groups to OpenSSL group name */
+	switch (group) {
+	case 19:
+		return "prime256v1";
+	case 20:
+		return "secp384r1";
+	case 21:
+		return "secp521r1";
+	case 25:
+		return "prime192v1";
+	case 26:
+		return "secp224r1";
+#ifdef NID_brainpoolP224r1
+	case 27:
+		return "brainpoolP224r1";
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+	case 28:
+		return "brainpoolP256r1";
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case 29:
+		return "brainpoolP384r1";
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case 30:
+		return "brainpoolP512r1";
+#endif /* NID_brainpoolP512r1 */
+	default:
+		return NULL;
+	}
+}
+#endif /* OpenSSL version >= 3.0 */
+
+
 struct crypto_ec * crypto_ec_init(int group)
 {
 	struct crypto_ec *e;
@@ -2274,6 +2321,7 @@
 		return NULL;
 
 	e->nid = nid;
+	e->iana_group = group;
 	e->bnctx = BN_CTX_new();
 	e->group = EC_GROUP_new_by_curve_name(nid);
 	e->prime = BN_new();
@@ -2401,14 +2449,16 @@
 	    EC_POINT_get_affine_coordinates(e->group, (EC_POINT *) point,
 					    x_bn, y_bn, e->bnctx)) {
 		if (x) {
-			crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
-					     x, len, len);
+			ret = crypto_bignum_to_bin(
+				(struct crypto_bignum *) x_bn, x, len, len);
 		}
-		if (y) {
-			crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
-					     y, len, len);
+		if (ret >= 0 && y) {
+			ret = crypto_bignum_to_bin(
+				(struct crypto_bignum *) y_bn, y, len, len);
 		}
-		ret = 0;
+
+		if (ret > 0)
+			ret = 0;
 	}
 
 	BN_clear_free(x_bn);
@@ -2936,6 +2986,27 @@
 
 struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_DECODER_CTX *ctx;
+
+	ctx = OSSL_DECODER_CTX_new_for_pkey(
+		&pkey, "DER", NULL, "EC",
+		OSSL_KEYMGMT_SELECT_KEYPAIR |
+		OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+		NULL, NULL);
+	if (!ctx ||
+	    OSSL_DECODER_from_data(ctx, &der, &der_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: Decoding EC private key (DER) failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	return (struct crypto_ec_key *) pkey;
+fail:
+	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
+	return NULL;
+#else /* OpenSSL version >= 3.0 */
 	EVP_PKEY *pkey = NULL;
 	EC_KEY *eckey;
 
@@ -2957,6 +3028,142 @@
 fail:
 	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
 	return NULL;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+					      const u8 *raw, size_t raw_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	const char *group_name;
+	OSSL_PARAM params[4];
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL;
+	BIGNUM *priv;
+	EC_POINT *pub = NULL;
+	EC_GROUP *ec_group = NULL;
+	size_t len;
+	u8 *pub_bin = NULL;
+	u8 *priv_bin = NULL;
+	int priv_bin_len;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	priv = BN_bin2bn(raw, raw_len, NULL);
+	if (!priv)
+		return NULL;
+	priv_bin = os_malloc(raw_len);
+	if (!priv_bin)
+		goto fail;
+	priv_bin_len = BN_bn2lebinpad(priv, priv_bin, raw_len);
+	if (priv_bin_len < 0)
+		goto fail;
+
+	ec_group = EC_GROUP_new_by_curve_name(crypto_ec_group_2_nid(group));
+	if (!ec_group)
+		goto fail;
+	pub = EC_POINT_new(ec_group);
+	if (!pub ||
+	    EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1)
+		goto fail;
+	len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+				 NULL, 0, NULL);
+	if (len == 0)
+		goto fail;
+	pub_bin = os_malloc(len);
+	if (!pub_bin)
+		goto fail;
+	len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+				 pub_bin, len, NULL);
+	if (len == 0)
+		goto fail;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_PRIV_KEY,
+					    priv_bin, priv_bin_len);
+	params[2] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+						      pub_bin, len);
+	params[3] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx ||
+	    EVP_PKEY_fromdata_init(ctx) <= 0 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0)
+		goto fail;
+
+out:
+	bin_clear_free(priv_bin, raw_len);
+	os_free(pub_bin);
+	BN_clear_free(priv);
+	EVP_PKEY_CTX_free(ctx);
+	EC_POINT_free(pub);
+	EC_GROUP_free(ec_group);
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+#else /* OpenSSL version >= 3.0 */
+	EC_KEY *eckey = NULL;
+	EVP_PKEY *pkey = NULL;
+	BIGNUM *priv = NULL;
+	int nid;
+	const EC_GROUP *ec_group;
+	EC_POINT *pub = NULL;
+
+	nid = crypto_ec_group_2_nid(group);
+	if (nid < 0) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+		return NULL;
+	}
+
+	eckey = EC_KEY_new_by_curve_name(nid);
+	priv = BN_bin2bn(raw, raw_len, NULL);
+	if (!eckey || !priv ||
+	    EC_KEY_set_private_key(eckey, priv) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	ec_group = EC_KEY_get0_group(eckey);
+	if (!ec_group)
+		goto fail;
+	pub = EC_POINT_new(ec_group);
+	if (!pub ||
+	    EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1 ||
+	    EC_KEY_set_public_key(eckey, pub) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set EC_KEY(pub): %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+		goto fail;
+	}
+
+out:
+	BN_clear_free(priv);
+	EC_POINT_free(pub);
+	return (struct crypto_ec_key *) pkey;
+
+fail:
+	EC_KEY_free(eckey);
+	EVP_PKEY_free(pkey);
+	pkey = NULL;
+	goto out;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -2972,8 +3179,13 @@
 	}
 
 	/* Ensure this is an EC key */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (!EVP_PKEY_is_a(pkey, "EC"))
+		goto fail;
+#else /* OpenSSL version >= 3.0 */
 	if (!EVP_PKEY_get0_EC_KEY(pkey))
 		goto fail;
+#endif /* OpenSSL version >= 3.0 */
 	return (struct crypto_ec_key *) pkey;
 fail:
 	crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
@@ -2984,6 +3196,47 @@
 struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *buf_x,
 					     const u8 *buf_y, size_t len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	const char *group_name;
+	OSSL_PARAM params[3];
+	u8 *pub;
+	EVP_PKEY_CTX *ctx;
+	EVP_PKEY *pkey = NULL;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	pub = os_malloc(1 + len * 2);
+	if (!pub)
+		return NULL;
+	pub[0] = 0x04; /* uncompressed */
+	os_memcpy(pub + 1, buf_x, len);
+	os_memcpy(pub + 1 + len, buf_y, len);
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+						      pub, 1 + len * 2);
+	params[2] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx) {
+		os_free(pub);
+		return NULL;
+	}
+	if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) {
+		os_free(pub);
+		EVP_PKEY_CTX_free(ctx);
+		return NULL;
+	}
+
+	os_free(pub);
+	EVP_PKEY_CTX_free(ctx);
+
+	return (struct crypto_ec_key *) pkey;
+#else /* OpenSSL version >= 3.0 */
 	EC_KEY *eckey = NULL;
 	EVP_PKEY *pkey = NULL;
 	EC_GROUP *ec_group = NULL;
@@ -3058,6 +3311,7 @@
 	EVP_PKEY_free(pkey);
 	pkey = NULL;
 	goto out;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -3065,6 +3319,24 @@
 crypto_ec_key_set_pub_point(struct crypto_ec *ec,
 			    const struct crypto_ec_point *pub)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	int len = BN_num_bytes(ec->prime);
+	struct crypto_ec_key *key;
+	u8 *buf;
+
+	buf = os_malloc(2 * len);
+	if (!buf)
+		return NULL;
+	if (crypto_ec_point_to_bin(ec, pub, buf, buf + len) < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	key = crypto_ec_key_set_pub(ec->iana_group, buf, buf + len, len);
+	os_free(buf);
+
+	return key;
+#else /* OpenSSL version >= 3.0 */
 	EC_KEY *eckey;
 	EVP_PKEY *pkey = NULL;
 
@@ -3093,11 +3365,41 @@
 	EC_KEY_free(eckey);
 	pkey = NULL;
 	goto out;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct crypto_ec_key * crypto_ec_key_gen(int group)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY_CTX *ctx;
+	OSSL_PARAM params[2];
+	const char *group_name;
+	EVP_PKEY *pkey = NULL;
+
+	group_name = crypto_ec_group_2_name(group);
+	if (!group_name)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     (char *) group_name, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+	if (!ctx ||
+	    EVP_PKEY_keygen_init(ctx) != 1 ||
+	    EVP_PKEY_CTX_set_params(ctx, params) != 1 ||
+	    EVP_PKEY_generate(ctx, &pkey) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: failed to generate EC keypair: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		pkey = NULL;
+	}
+
+	EVP_PKEY_CTX_free(ctx);
+
+	return (struct crypto_ec_key *) pkey;
+#else /* OpenSSL version >= 3.0 */
 	EVP_PKEY_CTX *kctx = NULL;
 	EC_KEY *ec_params = NULL, *eckey;
 	EVP_PKEY *params = NULL, *key = NULL;
@@ -3145,6 +3447,7 @@
 	EVP_PKEY_free(params);
 	EVP_PKEY_CTX_free(kctx);
 	return (struct crypto_ec_key *) key;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -3183,6 +3486,54 @@
 
 struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
 {
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_ENCODER_CTX *ctx;
+	int selection;
+	unsigned char *pdata = NULL;
+	size_t pdata_len = 0;
+	EVP_PKEY *copy = NULL;
+	struct wpabuf *buf = NULL;
+
+	if (EVP_PKEY_get_ec_point_conv_form(pkey) !=
+	    POINT_CONVERSION_COMPRESSED) {
+		copy = EVP_PKEY_dup(pkey);
+		if (!copy)
+			return NULL;
+		if (EVP_PKEY_set_utf8_string_param(
+			    copy, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+			    OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED) !=
+		    1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set compressed format");
+			EVP_PKEY_free(copy);
+			return NULL;
+		}
+		pkey = copy;
+	}
+
+	selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS |
+		OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+
+	ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
+					    "SubjectPublicKeyInfo",
+					    NULL);
+	if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to encode SubjectPublicKeyInfo: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		pdata = NULL;
+	}
+	OSSL_ENCODER_CTX_free(ctx);
+	if (pdata) {
+		buf = wpabuf_alloc_copy(pdata, pdata_len);
+		OPENSSL_free(pdata);
+	}
+
+	EVP_PKEY_free(copy);
+
+	return buf;
+#else /* OpenSSL version >= 3.0 */
 #ifdef OPENSSL_IS_BORINGSSL
 	unsigned char *der = NULL;
 	int der_len;
@@ -3196,7 +3547,7 @@
 	int nid;
 
 	ctx = BN_CTX_new();
-	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
 	if (!ctx || !eckey)
 		goto fail;
 
@@ -3249,33 +3600,16 @@
 	int der_len;
 	struct wpabuf *buf;
 	EC_KEY *eckey;
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-	EVP_PKEY *tmp;
-#endif /* OpenSSL version >= 3.0 */
 
-	eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
 	if (!eckey)
 		return NULL;
 
 	/* For now, all users expect COMPRESSED form */
 	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
 
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-	tmp = EVP_PKEY_new();
-	if (!tmp)
-		return NULL;
-	if (EVP_PKEY_set1_EC_KEY(tmp, eckey) != 1) {
-		EVP_PKEY_free(tmp);
-		return NULL;
-	}
-	key = (struct crypto_ec_key *) tmp;
-#endif /* OpenSSL version >= 3.0 */
-
 	der_len = i2d_PUBKEY((EVP_PKEY *) key, &der);
 	EC_KEY_free(eckey);
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-	EVP_PKEY_free(tmp);
-#endif /* OpenSSL version >= 3.0 */
 	if (der_len <= 0) {
 		wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
@@ -3286,19 +3620,58 @@
 	OPENSSL_free(der);
 	return buf;
 #endif /* OPENSSL_IS_BORINGSSL */
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
 						bool include_pub)
 {
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	OSSL_ENCODER_CTX *ctx;
+	int selection;
+	unsigned char *pdata = NULL;
+	size_t pdata_len = 0;
+	struct wpabuf *buf;
+	EVP_PKEY *copy = NULL;
+
+	selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS |
+		OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
+	if (include_pub) {
+		selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+	} else {
+		/* Not including OSSL_KEYMGMT_SELECT_PUBLIC_KEY does not seem
+		 * to really be sufficient, so clone the key and explicitly
+		 * mark it not to include the public key. */
+		copy = EVP_PKEY_dup(pkey);
+		if (!copy)
+			return NULL;
+		EVP_PKEY_set_int_param(copy, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC,
+				       0);
+		pkey = copy;
+	}
+
+	ctx = OSSL_ENCODER_CTX_new_for_pkey(pkey, selection, "DER",
+					    "type-specific", NULL);
+	if (!ctx || OSSL_ENCODER_to_data(ctx, &pdata, &pdata_len) != 1) {
+		OSSL_ENCODER_CTX_free(ctx);
+		EVP_PKEY_free(copy);
+		return NULL;
+	}
+	OSSL_ENCODER_CTX_free(ctx);
+	buf = wpabuf_alloc_copy(pdata, pdata_len);
+	OPENSSL_free(pdata);
+	EVP_PKEY_free(copy);
+	return buf;
+#else /* OpenSSL version >= 3.0 */
 	EC_KEY *eckey;
 	unsigned char *der = NULL;
 	int der_len;
 	struct wpabuf *buf;
 	unsigned int key_flags;
 
-	eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
 	if (!eckey)
 		return NULL;
 
@@ -3319,18 +3692,53 @@
 	OPENSSL_free(der);
 
 	return buf;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
 					       int prefix)
 {
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct wpabuf *buf;
+	unsigned char *pos;
+	size_t pub_len = OSSL_PARAM_UNMODIFIED;
+
+	buf = NULL;
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    NULL, 0, &pub_len) < 0 ||
+	    pub_len == OSSL_PARAM_UNMODIFIED ||
+	    !(buf = wpabuf_alloc(pub_len)) ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    wpabuf_put(buf, pub_len),
+					    pub_len, NULL) != 1 ||
+	    wpabuf_head_u8(buf)[0] != 0x04) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to get encoded public key: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (!prefix) {
+		/* Remove 0x04 prefix if requested */
+		pos = wpabuf_mhead(buf);
+		os_memmove(pos, pos + 1, pub_len - 1);
+		buf->used--;
+	}
+
+	return buf;
+#else /* OpenSSL version >= 3.0 */
 	int len, res;
 	EC_KEY *eckey;
 	struct wpabuf *buf;
 	unsigned char *pos;
 
-	eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
+	eckey = EVP_PKEY_get1_EC_KEY(pkey);
 	if (!eckey)
 		return NULL;
 	EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
@@ -3367,30 +3775,92 @@
 	}
 
 	return buf;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
-const struct crypto_ec_point *
+struct crypto_ec_point *
 crypto_ec_key_get_public_key(struct crypto_ec_key *key)
 {
-	const EC_KEY *eckey;
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char group[64];
+	unsigned char pub[256];
+	size_t len;
+	EC_POINT *point = NULL;
+	EC_GROUP *grp;
+	int res = 0;
+	OSSL_PARAM params[2];
 
-	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
+					   group, sizeof(group), &len) != 1 ||
+	    EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
+					    pub, sizeof(pub), &len) != 1)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     group, 0);
+	params[1] = OSSL_PARAM_construct_end();
+	grp = EC_GROUP_new_from_params(params, NULL, NULL);
+	if (!grp)
+		goto fail;
+	point = EC_POINT_new(grp);
+	if (!point)
+		goto fail;
+	res = EC_POINT_oct2point(grp, point, pub, len, NULL);
+
+fail:
+	if (res != 1) {
+		EC_POINT_free(point);
+		point = NULL;
+	}
+
+	EC_GROUP_free(grp);
+
+	return (struct crypto_ec_point *) point;
+#else /* OpenSSL version >= 3.0 */
+	const EC_KEY *eckey;
+	const EC_POINT *point;
+	const EC_GROUP *group;
+
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
 	if (!eckey)
 		return NULL;
-	return (const struct crypto_ec_point *) EC_KEY_get0_public_key(eckey);
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		return NULL;
+	point = EC_KEY_get0_public_key(eckey);
+	if (!point)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_dup(point, group);
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
-const struct crypto_bignum *
+struct crypto_bignum *
 crypto_ec_key_get_private_key(struct crypto_ec_key *key)
 {
-	const EC_KEY *eckey;
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	BIGNUM *bn = NULL;
 
-	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	if (!EVP_PKEY_is_a(pkey, "EC") ||
+	    EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bn) != 1)
+		return NULL;
+	return (struct crypto_bignum *) bn;
+#else /* OpenSSL version >= 3.0 */
+	const EC_KEY *eckey;
+	const BIGNUM *bn;
+
+	eckey = EVP_PKEY_get0_EC_KEY(pkey);
 	if (!eckey)
 		return NULL;
-	return (const struct crypto_bignum *) EC_KEY_get0_private_key(eckey);
+	bn = EC_KEY_get0_private_key(eckey);
+	if (!bn)
+		return NULL;
+	return (struct crypto_bignum *) BN_dup(bn);
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -3613,10 +4083,12 @@
 	case NID_brainpoolP512r1:
 		return 30;
 #endif /* NID_brainpoolP512r1 */
+	default:
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Unsupported curve (nid=%d) in EC key",
+			   nid);
+		return -1;
 	}
-	wpa_printf(MSG_ERROR, "OpenSSL: Unsupported curve (nid=%d) in EC key",
-		   nid);
-	return -1;
 }
 
 
@@ -3943,6 +4415,8 @@
 {
 	EVP_PKEY *pkey;
 	X509 *x509;
+	const ASN1_TIME *not_before, *not_after;
+	int res_before, res_after;
 
 	pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
 	if (pkey)
@@ -3953,17 +4427,36 @@
 	if (!x509)
 		return NULL;
 
+	not_before = X509_get0_notBefore(x509);
+	not_after = X509_get0_notAfter(x509);
+	if (!not_before || !not_after)
+		goto fail;
+	res_before = X509_cmp_current_time(not_before);
+	res_after = X509_cmp_current_time(not_after);
+	if (!res_before || !res_after)
+		goto fail;
+	if (res_before > 0 || res_after < 0) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Certificate for RSA public key is not valid at this time (%d %d)",
+			   res_before, res_after);
+		goto fail;
+	}
+
 	pkey = X509_get_pubkey(x509);
 	X509_free(x509);
 
 	if (!pkey)
 		return NULL;
 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
+		wpa_printf(MSG_INFO, "OpenSSL: No RSA public key found");
 		EVP_PKEY_free(pkey);
 		return NULL;
 	}
 
 	return pkey;
+fail:
+	X509_free(x509);
+	return NULL;
 }
 
 
@@ -4068,6 +4561,772 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+#define HPKE_MAX_SHARED_SECRET_LEN 66
+#define HPKE_MAX_HASH_LEN 64
+#define HPKE_MAX_KEY_LEN 32
+#define HPKE_MAX_NONCE_LEN 12
+#define HPKE_MAX_PUB_LEN (1 + 2 * 66)
+
+struct hpke_context {
+	/* KEM */
+	enum hpke_kem_id kem_id;
+	int kem_nid;
+	int iana_group;
+	size_t n_pk;
+	size_t n_secret;
+	const EVP_MD *kem_h;
+	size_t kem_n_h;
+
+	/* KDF */
+	enum hpke_kdf_id kdf_id;
+	const EVP_MD *kdf_h;
+	size_t n_h;
+
+	/* AEAD */
+	enum hpke_aead_id aead_id;
+	const EVP_CIPHER *cipher;
+	size_t n_k;
+	size_t n_n;
+	size_t n_t;
+	u8 key[HPKE_MAX_KEY_LEN];
+	u8 base_nonce[HPKE_MAX_NONCE_LEN];
+};
+
+
+static void hpke_free_context(struct hpke_context *ctx)
+{
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+static struct hpke_context * hpke_get_context(enum hpke_kem_id kem_id,
+					      enum hpke_kdf_id kdf_id,
+					      enum hpke_aead_id aead_id,
+					      struct crypto_ec_key *key)
+{
+	struct hpke_context *ctx;
+	int group;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	ctx->kem_id = kem_id;
+	switch (kem_id) {
+	case HPKE_DHKEM_P256_HKDF_SHA256:
+		ctx->kem_nid = NID_X9_62_prime256v1;
+		ctx->iana_group = 19;
+		ctx->n_pk = 65;
+		ctx->n_secret = 32;
+		ctx->kem_h = EVP_sha256();
+		ctx->kem_n_h = 32;
+		break;
+	case HPKE_DHKEM_P384_HKDF_SHA384:
+		ctx->kem_nid = NID_secp384r1;
+		ctx->iana_group = 20;
+		ctx->n_pk = 97;
+		ctx->n_secret = 48;
+		ctx->kem_h = EVP_sha384();
+		ctx->kem_n_h = 48;
+		break;
+	case HPKE_DHKEM_P521_HKDF_SHA512:
+		ctx->kem_nid = NID_secp521r1;
+		ctx->iana_group = 21;
+		ctx->n_pk = 133;
+		ctx->n_secret = 64;
+		ctx->kem_h = EVP_sha512();
+		ctx->kem_n_h = 64;
+		break;
+	default:
+		goto fail;
+	}
+
+	ctx->kdf_id = kdf_id;
+	switch (kdf_id) {
+	case HPKE_KDF_HKDF_SHA256:
+		ctx->kdf_h = EVP_sha256();
+		ctx->n_h = 32;
+		break;
+	case HPKE_KDF_HKDF_SHA384:
+		ctx->kdf_h = EVP_sha384();
+		ctx->n_h = 48;
+		break;
+	case HPKE_KDF_HKDF_SHA512:
+		ctx->kdf_h = EVP_sha512();
+		ctx->n_h = 64;
+		break;
+	default:
+		goto fail;
+	}
+
+	ctx->aead_id = aead_id;
+	switch (aead_id) {
+	case HPKE_AEAD_AES_128_GCM:
+		ctx->cipher = EVP_aes_128_gcm();
+		ctx->n_k = 16;
+		ctx->n_n = 12;
+		ctx->n_t = 16;
+		break;
+	case HPKE_AEAD_AES_256_GCM:
+		ctx->cipher = EVP_aes_256_gcm();
+		ctx->n_k = 32;
+		ctx->n_n = 12;
+		ctx->n_t = 16;
+		break;
+	default:
+		goto fail;
+	}
+
+	/* Convert BP-256/384/512 to P-256/384/521 for DPP */
+	group = crypto_ec_key_group(key);
+	if (group == 28 && ctx->iana_group == 19) {
+		ctx->iana_group = 28;
+	} else if (group == 29 && ctx->iana_group == 20) {
+		ctx->iana_group = 29;
+	} else if (group == 30 && ctx->iana_group == 21) {
+		ctx->iana_group = 30;
+		ctx->n_pk = 129;
+	}
+	if (group != ctx->iana_group) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:group mismatch (%d != %d)",
+			   __func__, group, ctx->iana_group);
+		goto fail;
+	}
+
+	return ctx;
+fail:
+	hpke_free_context(ctx);
+	return NULL;
+}
+
+
+static size_t hpke_suite_id(struct hpke_context *ctx, bool kem, u8 *suite_id)
+{
+	size_t suite_id_len;
+
+	if (kem) {
+		os_memcpy(suite_id, "KEM", 3);
+		WPA_PUT_BE16(&suite_id[3], ctx->kem_id);
+		suite_id_len = 5;
+	} else {
+		os_memcpy(suite_id, "HPKE", 4);
+		WPA_PUT_BE16(&suite_id[4], ctx->kem_id);
+		WPA_PUT_BE16(&suite_id[6], ctx->kdf_id);
+		WPA_PUT_BE16(&suite_id[8], ctx->aead_id);
+		suite_id_len = 10;
+	}
+	return suite_id_len;
+}
+
+
+static int hpke_labeled_extract(struct hpke_context *ctx, bool kem,
+				const u8 *salt, size_t salt_len,
+				const char *label,
+				const u8 *ikm, size_t ikm_len, u8 *prk)
+{
+	u8 zero[HPKE_MAX_HASH_LEN];
+	u8 suite_id[10];
+	size_t suite_id_len;
+	unsigned int mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *hctx;
+	size_t mlen;
+	int res;
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX *hctx;
+	int res;
+#endif /* OpenSSL version >= 3.0 */
+
+	if (!salt || !salt_len) {
+		salt_len = mdlen;
+		os_memset(zero, 0, salt_len);
+		salt = zero;
+	}
+
+	suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+
+	/* labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
+	 * return Extract(salt, labeled_ikm) */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		return -1;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(
+		"digest",
+		(char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	hctx = EVP_MAC_CTX_new(hmac);
+	EVP_MAC_free(hmac);
+	if (!hctx)
+		return -1;
+
+	if (EVP_MAC_init(hctx, salt, salt_len, params) != 1)
+		goto fail;
+
+	if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 ||
+	    EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 ||
+	    EVP_MAC_update(hctx, (const unsigned char *) label,
+			   os_strlen(label)) != 1 ||
+	    EVP_MAC_update(hctx, ikm, ikm_len) != 1)
+		goto fail;
+
+	res = EVP_MAC_final(hctx, prk, &mlen, mdlen);
+	EVP_MAC_CTX_free(hctx);
+
+	return res == 1 ? 0 : -1;
+fail:
+	EVP_MAC_CTX_free(hctx);
+	return -1;
+#else /* OpenSSL version >= 3.0 */
+	hctx = HMAC_CTX_new();
+	if (!hctx)
+		return -1;
+	res = HMAC_Init_ex(hctx, salt, salt_len, kem ? ctx->kem_h : ctx->kdf_h,
+			   NULL);
+	if (res != 1)
+		goto done;
+
+	HMAC_Update(hctx, (const unsigned char *) "HPKE-v1", 7);
+	HMAC_Update(hctx, suite_id, suite_id_len);
+	HMAC_Update(hctx, (const unsigned char *) label, os_strlen(label));
+	HMAC_Update(hctx, ikm, ikm_len);
+
+	res = HMAC_Final(hctx, prk, &mdlen);
+done:
+	HMAC_CTX_free(hctx);
+
+	return res == 1 ? 0 : -1;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static int
+hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk,
+		    const char *label, const u8 *info, size_t info_len,
+		    u8 *out, size_t out_len)
+{
+	u8 suite_id[10];
+	size_t suite_id_len;
+	u8 hash[HPKE_MAX_HASH_LEN];
+	u8 iter = 0;
+	size_t label_len = os_strlen(label);
+	u8 *pos;
+	size_t left = out_len, clen;
+	int res = -1;
+	u8 *labeled_info;
+	size_t labeled_info_len;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *hctx = NULL;
+	size_t mdlen;
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX *hctx;
+	unsigned int mdlen;
+#endif /* OpenSSL version >= 3.0 */
+
+	/* labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
+	 *                       label, info)
+	 * return Expand(prk, labeled_info, L) */
+	suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+	labeled_info_len = 2 + 7 + suite_id_len + label_len + info_len;
+	labeled_info = os_malloc(labeled_info_len);
+	if (!labeled_info)
+		return -1;
+	pos = labeled_info;
+	WPA_PUT_BE16(pos, out_len);
+	pos += 2;
+	os_memcpy(pos, "HPKE-v1", 7);
+	pos += 7;
+	os_memcpy(pos, suite_id, suite_id_len);
+	pos += suite_id_len;
+	os_memcpy(pos, label, label_len);
+	pos += label_len;
+	if (info && info_len)
+		os_memcpy(pos, info, info_len);
+
+	pos = out;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		return -1;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(
+		"digest",
+		(char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+	params[1] = OSSL_PARAM_construct_end();
+#else /* OpenSSL version >= 3.0 */
+	hctx = HMAC_CTX_new();
+	if (!hctx)
+		return -1;
+#endif /* OpenSSL version >= 3.0 */
+
+	while (left > 0) {
+		mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+		EVP_MAC_CTX_free(hctx);
+		hctx = EVP_MAC_CTX_new(hmac);
+		if (!hctx)
+			return -1;
+
+		if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
+			goto fail;
+
+		if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1)
+			goto fail;
+		if (iter == 255)
+			goto fail;
+		iter++;
+
+		if (EVP_MAC_update(hctx, labeled_info, labeled_info_len) != 1 ||
+		    EVP_MAC_update(hctx, &iter, sizeof(iter)) != 1)
+			goto fail;
+
+		if (EVP_MAC_final(hctx, hash, &mdlen, mdlen) != 1)
+			goto fail;
+#else /* OpenSSL version >= 3.0 */
+		if (HMAC_Init_ex(hctx, prk, mdlen,
+				 kem ? ctx->kem_h : ctx->kdf_h,
+				 NULL) != 1)
+			goto fail;
+
+		if (iter > 0)
+			HMAC_Update(hctx, hash, mdlen);
+		if (iter == 255)
+			goto fail;
+		iter++;
+		HMAC_Update(hctx, labeled_info, labeled_info_len);
+		HMAC_Update(hctx, &iter, sizeof(iter));
+
+		if (HMAC_Final(hctx, hash, &mdlen) != 1)
+			goto fail;
+		HMAC_CTX_reset(hctx);
+#endif /* OpenSSL version >= 3.0 */
+
+		clen = left > mdlen ? mdlen : left;
+		os_memcpy(pos, hash, clen);
+		pos += clen;
+		left -= clen;
+	}
+	res = 0;
+fail:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_free(hmac);
+	EVP_MAC_CTX_free(hctx);
+#else /* OpenSSL version >= 3.0 */
+	HMAC_CTX_free(hctx);
+#endif /* OpenSSL version >= 3.0 */
+	os_free(labeled_info);
+
+	return res;
+}
+
+
+static int hpke_extract_and_expand(struct hpke_context *ctx,
+				   const u8 *dhss, size_t dhss_len,
+				   const u8 *enc, size_t enc_len,
+				   const u8 *pk_rm, size_t pk_rm_len,
+				   u8 *shared_secret)
+{
+	u8 kem_context[2 * HPKE_MAX_PUB_LEN];
+	u8 eae_prk[HPKE_MAX_HASH_LEN];
+
+	/* eae_prk = LabeledExtract("", "eae_prk", dh) */
+	if (hpke_labeled_extract(ctx, true, NULL, 0, "eae_prk", dhss, dhss_len,
+				 eae_prk) < 0)
+		return -1;
+
+	if (enc_len > HPKE_MAX_PUB_LEN || pk_rm_len > HPKE_MAX_PUB_LEN)
+		return -1;
+	/* kem_context = concat(enc, pkRm) */
+	os_memcpy(kem_context, enc, enc_len);
+	os_memcpy(&kem_context[enc_len], pk_rm, pk_rm_len);
+
+	/* shared_secret = LabeledExpand(eae_prk, "shared_secret",
+	 *                               kem_context, Nsecret) */
+	if (hpke_labeled_expand(ctx, true, eae_prk, "shared_secret",
+				kem_context, enc_len + pk_rm_len,
+				shared_secret, ctx->n_secret) < 0)
+		return -1;
+
+	forced_memzero(eae_prk, sizeof(eae_prk));
+	return 0;
+}
+
+
+static int hpke_key_schedule(struct hpke_context *ctx, const u8 *shared_secret,
+			     const u8 *info, size_t info_len)
+{
+	u8 key_schedule_context[1 + 2 * HPKE_MAX_HASH_LEN];
+	u8 secret[HPKE_MAX_HASH_LEN];
+	int res = -1;
+
+	/* key_schedule_context = concat(mode, psk_id_hash, info_hash) */
+	key_schedule_context[0] = HPKE_MODE_BASE;
+
+	/* psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) */
+	if (hpke_labeled_extract(ctx, false, NULL, 0, "psk_id_hash",
+				 NULL, 0, &key_schedule_context[1]) < 0)
+		goto fail;
+
+	/* info_hash = LabeledExtract("", "info_hash", info) */
+	if (hpke_labeled_extract(ctx, false, NULL, 0, "info_hash",
+				 info, info_len,
+				 &key_schedule_context[1 + ctx->n_h]) < 0)
+		goto fail;
+
+	/* secret = LabeledExtract(shared_secret, "secret", psk) */
+	if (hpke_labeled_extract(ctx, false, shared_secret, ctx->n_secret,
+				 "secret", NULL, 0, secret) < 0)
+		goto fail;
+
+	/* key = LabeledExpand(secret, "key", key_schedule_context, Nk) */
+	if (hpke_labeled_expand(ctx, false, secret, "key",
+				key_schedule_context, 1 + 2 * ctx->n_h,
+				ctx->key, ctx->n_k) < 0)
+		goto fail;
+
+	/* base_nonce = LabeledExpand(secret, "base_nonce",
+	 *                            key_schedule_context, Nn) */
+	if (hpke_labeled_expand(ctx, false, secret, "base_nonce",
+				key_schedule_context, 1 + 2 * ctx->n_h,
+				ctx->base_nonce, ctx->n_n) < 0)
+		goto fail;
+	res = 0;
+fail:
+	forced_memzero(key_schedule_context, sizeof(key_schedule_context));
+	forced_memzero(secret, sizeof(secret));
+	return res;
+}
+
+
+static int hpke_encap(struct hpke_context *ctx, struct crypto_ec_key *pk_r,
+		      u8 *shared_secret, u8 *enc)
+{
+	EVP_PKEY_CTX *pctx = NULL;
+	struct crypto_ec_key *sk_e;
+	int res = -1;
+	u8 *dhss = NULL;
+	size_t dhss_len = 0;
+	struct wpabuf *enc_buf = NULL, *pk_rm = NULL;
+
+	/* skE, pkE = GenerateKeyPair() */
+	sk_e = crypto_ec_key_gen(ctx->iana_group);
+	if (!sk_e) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not generate key pair",
+			   __func__);
+		goto fail;
+	}
+
+	/* dh = DH(skE, pkR) */
+	dhss_len = sizeof(dhss);
+	pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_e, NULL);
+	if (!pctx ||
+	    EVP_PKEY_derive_init(pctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 ||
+	    EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+	    !(dhss = os_malloc(dhss_len)) ||
+	    EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+	    dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: hpke_encap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+			   dhss_len, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	/* enc = SerializePublicKey(pkE) */
+	enc_buf = crypto_ec_key_get_pubkey_point(sk_e, 1);
+	if (!enc_buf)
+		goto fail;
+	os_memcpy(enc, wpabuf_head(enc_buf), wpabuf_len(enc_buf));
+
+	/* pkRm = SerializePublicKey(pkR) */
+	pk_rm = crypto_ec_key_get_pubkey_point(pk_r, 1);
+	if (!pk_rm)
+		goto fail;
+
+	/* kem_context = concat(enc, pkRm) */
+	/* shared_secret = ExtractAndExpand(dh, kem_context) */
+	/* return shared_secret, enc */
+	res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+				      wpabuf_head(pk_rm),
+				      wpabuf_len(pk_rm), shared_secret);
+fail:
+	bin_clear_free(dhss, dhss_len);
+	crypto_ec_key_deinit(sk_e);
+	EVP_PKEY_CTX_free(pctx);
+	wpabuf_free(enc_buf);
+	wpabuf_free(pk_rm);
+	return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_seal(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+	       const u8 *pt, size_t pt_len)
+{
+	EVP_CIPHER_CTX *cctx;
+	int len = 0;
+	struct wpabuf *ct = NULL;
+
+	/* No need to xor in sequence number since we support only the
+	 * single-shot API, i.e., base_nonce can be used as-is. */
+
+	cctx = EVP_CIPHER_CTX_new();
+	if (!cctx ||
+	    EVP_EncryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+			       ctx->base_nonce) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+			   __func__);
+		goto fail;
+	}
+	if (aad && aad_len &&
+	    EVP_EncryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate(AAD) failed",
+			   __func__);
+		goto fail;
+	}
+	ct = wpabuf_alloc(pt_len + AES_BLOCK_SIZE + ctx->n_t);
+	if (!ct)
+		goto fail;
+	if (EVP_EncryptUpdate(cctx, wpabuf_put(ct, 0), &len, pt, pt_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate failed",
+			   __func__);
+		goto fail;
+	}
+	wpabuf_put(ct, len);
+
+	if (EVP_EncryptFinal(cctx, wpabuf_put(ct, 0), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+			   __func__);
+		wpabuf_free(ct);
+		ct = NULL;
+		goto fail;
+	}
+
+	if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG, ctx->n_t,
+				wpabuf_put(ct, ctx->n_t)) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not get tag",
+			   __func__);
+		wpabuf_free(ct);
+		ct = NULL;
+		goto fail;
+	}
+fail:
+	EVP_CIPHER_CTX_free(cctx);
+	return ct;
+}
+
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *peer_pub,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *pt, size_t pt_len)
+{
+	struct hpke_context *ctx;
+	u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+	u8 enc[1 + 2 * HPKE_MAX_PUB_LEN];
+	struct wpabuf *ct = NULL, *enc_ct = NULL;
+
+	ctx = hpke_get_context(kem_id, kdf_id, aead_id, peer_pub);
+	if (!ctx)
+		return NULL;
+
+	/* shared_secret, enc = Encap(pkR) */
+	if (hpke_encap(ctx, peer_pub, shared_secret, enc) < 0)
+		goto fail;
+
+	/* KeyScheduleS(mode_base, shared_secret, info,
+	 *              default_psk, default_psk_id) */
+	if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+		goto fail;
+
+	/* ct = ctx.Seal(aad, pt) */
+	ct = hpke_aead_seal(ctx, aad, aad_len, pt, pt_len);
+	if (!ct)
+		goto fail;
+
+	/* return enc, ct */
+	enc_ct = wpabuf_alloc(ctx->n_pk + wpabuf_len(ct));
+	if (!enc_ct)
+		goto fail;
+	wpabuf_put_data(enc_ct, enc, ctx->n_pk);
+	wpabuf_put_buf(enc_ct, ct);
+
+fail:
+	forced_memzero(shared_secret, sizeof(shared_secret));
+	hpke_free_context(ctx);
+	wpabuf_free(ct);
+	return enc_ct;
+}
+
+
+static int hpke_decap(struct hpke_context *ctx, const u8 *enc,
+		      size_t enc_ct_len, struct crypto_ec_key *sk_r,
+		      u8 *shared_secret)
+{
+	EVP_PKEY_CTX *pctx = NULL;
+	struct wpabuf *pk_rm = NULL;
+	size_t len;
+	int res = -1;
+	struct crypto_ec_key *pk_e = NULL;
+	u8 *dhss = NULL;
+	size_t dhss_len = 0;
+
+	/* pkE = DeserializePublicKey(enc) */
+	if (enc_ct_len < ctx->n_pk)
+		return -1; /* not enough room for enc */
+	if (enc[0] != 0x04)
+		return -1; /* not in uncompressed form */
+	len = (ctx->n_pk - 1) / 2;
+	pk_e = crypto_ec_key_set_pub(ctx->iana_group, &enc[1],
+				     &enc[1 + len], len);
+	if (!pk_e)
+		return -1; /* invalid public key point */
+	/* dh = DH(skR, pkE) */
+	pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL);
+	if (!pctx ||
+	    EVP_PKEY_derive_init(pctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 ||
+	    EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 ||
+	    !(dhss = os_malloc(dhss_len)) ||
+	    EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+	    dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: hpke_decap: EVP_PKEY_derive failed (dhss_len=%zu): %s",
+			   dhss_len, ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	/* pkRm = SerializePublicKey(pk(skR)) */
+	pk_rm = crypto_ec_key_get_pubkey_point(sk_r, 1);
+	if (!pk_rm)
+		goto fail;
+
+	/* kem_context = concat(enc, pkRm) */
+	/* shared_secret = ExtractAndExpand(dh, kem_context) */
+	res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+				      wpabuf_head(pk_rm),
+				      wpabuf_len(pk_rm), shared_secret);
+fail:
+	bin_clear_free(dhss, dhss_len);
+	crypto_ec_key_deinit(pk_e);
+	EVP_PKEY_CTX_free(pctx);
+	wpabuf_free(pk_rm);
+	return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_open(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+	       const u8 *ct, size_t ct_len)
+{
+	EVP_CIPHER_CTX *cctx;
+	int len = 0;
+	const u8 *tag;
+	struct wpabuf *pt = NULL;
+
+	if (ct_len < ctx->n_t)
+		return NULL;
+	tag = ct + ct_len - ctx->n_t;
+	ct_len -= ctx->n_t;
+
+	/* No need to xor in sequence number since we support only the
+	 * single-shot API, i.e., base_nonce can be used as-is. */
+
+	cctx = EVP_CIPHER_CTX_new();
+	if (!cctx ||
+	    EVP_DecryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+			       ctx->base_nonce) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+			   __func__);
+		goto fail;
+	}
+	if (aad && aad_len &&
+	    EVP_DecryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate(AAD) failed",
+			   __func__);
+		goto fail;
+	}
+	pt = wpabuf_alloc(ct_len + AES_BLOCK_SIZE);
+	if (!pt)
+		goto fail;
+	if (EVP_DecryptUpdate(cctx, wpabuf_put(pt, 0), &len, ct, ct_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate failed",
+			   __func__);
+		goto fail;
+	}
+	wpabuf_put(pt, len);
+
+	if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, ctx->n_t,
+				(void *) tag) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:Could not set tag",
+			   __func__);
+		wpabuf_free(pt);
+		pt = NULL;
+		goto fail;
+	}
+
+	if (EVP_DecryptFinal(cctx, wpabuf_put(pt, 0), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+			   __func__);
+		wpabuf_free(pt);
+		pt = NULL;
+	}
+fail:
+	EVP_CIPHER_CTX_free(cctx);
+	return pt;
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+			       enum hpke_kdf_id kdf_id,
+			       enum hpke_aead_id aead_id,
+			       struct crypto_ec_key *own_priv,
+			       const u8 *info, size_t info_len,
+			       const u8 *aad, size_t aad_len,
+			       const u8 *enc_ct, size_t enc_ct_len)
+{
+	struct hpke_context *ctx;
+	u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+	struct wpabuf *pt = NULL;
+
+	ctx = hpke_get_context(kem_id, kdf_id, aead_id, own_priv);
+	if (!ctx)
+		return NULL;
+
+	/* shared_secret = Decap(enc, skR) */
+	if (hpke_decap(ctx, enc_ct, enc_ct_len, own_priv, shared_secret) < 0)
+		goto fail;
+
+	/* KeyScheduleR(mode_base, shared_secret, info,
+	 *              default_psk, default_psk_id) */
+	if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+		goto fail;
+
+	/* return ctx.Open(aad, ct) */
+	pt = hpke_aead_open(ctx, aad, aad_len,
+			    &enc_ct[ctx->n_pk], enc_ct_len - ctx->n_pk);
+
+fail:
+	forced_memzero(shared_secret, sizeof(shared_secret));
+	hpke_free_context(ctx);
+	return pt;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 void crypto_unload(void)
 {
 	openssl_unload_legacy_provider();
diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c
index a4bf50a..f9347d0 100644
--- a/src/crypto/fips_prf_internal.c
+++ b/src/crypto/fips_prf_internal.c
@@ -17,10 +17,11 @@
 int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
 {
 	u8 xkey[64];
-	u32 t[5], _t[5];
+	u32 _t[5];
 	int i, j, m, k;
 	u8 *xpos = x;
 	u32 carry;
+	struct SHA1Context ctx;
 
 	if (seed_len < sizeof(xkey))
 		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
@@ -30,11 +31,7 @@
 	/* FIPS 186-2 + change notice 1 */
 
 	os_memcpy(xkey, seed, seed_len);
-	t[0] = 0x67452301;
-	t[1] = 0xEFCDAB89;
-	t[2] = 0x98BADCFE;
-	t[3] = 0x10325476;
-	t[4] = 0xC3D2E1F0;
+	SHA1Init(&ctx);
 
 	m = xlen / 40;
 	for (j = 0; j < m; j++) {
@@ -43,7 +40,7 @@
 			/* XVAL = (XKEY + XSEED_j) mod 2^b */
 
 			/* w_i = G(t, XVAL) */
-			os_memcpy(_t, t, 20);
+			os_memcpy(_t, ctx.state, 20);
 			SHA1Transform(_t, xkey);
 			_t[0] = host_to_be32(_t[0]);
 			_t[1] = host_to_be32(_t[1]);
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index 4697e04..484f772 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -7,6 +7,19 @@
  */
 
 #include "includes.h"
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+/* OpenSSL 3.0 has deprecated the low-level SHA1 functions and does not
+ * include an upper layer interface that could be used to use the
+ * SHA1_Transform() function. Use the internal SHA-1 implementation instead
+ * as a workaround. */
+#include "sha1-internal.c"
+#include "fips_prf_internal.c"
+
+#else /* OpenSSL version >= 3.0 */
+
 #include <openssl/sha.h>
 
 #include "common.h"
@@ -97,3 +110,5 @@
 
 	return 0;
 }
+
+#endif /* OpenSSL version >= 3.0 */
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index 17af964..1ad1068 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -30,6 +30,7 @@
 	unsigned char tk[32];
 	const u8 *_addr[11];
 	size_t _len[11], i;
+	int ret;
 
 	if (num_elem > 10) {
 		/*
@@ -70,8 +71,9 @@
 		_addr[i + 1] = addr[i];
 		_len[i + 1] = len[i];
 	}
-	if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
-		return -1;
+	ret = sha256_vector(1 + num_elem, _addr, _len, mac);
+	if (ret < 0)
+		goto fail;
 
 	os_memset(k_pad, 0, sizeof(k_pad));
 	os_memcpy(k_pad, key, key_len);
@@ -84,7 +86,14 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA256_MAC_LEN;
-	return sha256_vector(2, _addr, _len, mac);
+
+	ret = sha256_vector(2, _addr, _len, mac);
+
+fail:
+	forced_memzero(k_pad, sizeof(k_pad));
+	forced_memzero(tk, sizeof(tk));
+
+	return ret;
 }
 
 
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 7a2ee32..c201dcd 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -353,7 +353,9 @@
  * tls_connection_set_verify - Set certificate verification options
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
- * @verify_peer: 1 = verify peer certificate
+ * @verify_peer: 0 = do not verify peer certificate, 1 = verify peer
+ *	certificate (require it to be provided), 2 = verify peer certificate if
+ *	provided
  * @flags: Connection flags (TLS_CONN_*)
  * @session_ctx: Session caching context or %NULL to use default
  * @session_ctx_len: Length of @session_ctx in bytes.
@@ -682,4 +684,13 @@
  */
 bool tls_connection_get_own_cert_used(struct tls_connection *conn);
 
+/**
+ * tls_register_cert_callback - Register a callback to retrieve certificates
+ * @cb: Callback object to register
+ */
+typedef ssize_t (*tls_get_certificate_cb)
+(void* ctx, const char* alias, uint8_t** value);
+
+void tls_register_cert_callback(tls_get_certificate_cb cb);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index a1b5166..23bbe68 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -126,109 +126,6 @@
 }
 #endif
 
-#ifdef ANDROID
-#include <openssl/pem.h>
-#include <keystore/keystore_get.h>
-
-#include <log/log.h>
-#include <log/log_event_list.h>
-
-#define CERT_VALIDATION_FAILURE 210033
-#define ANDROID_KEYSTORE_PREFIX "keystore://"
-#define ANDROID_KEYSTORE_PREFIX_LEN os_strlen(ANDROID_KEYSTORE_PREFIX)
-#define ANDROID_KEYSTORE_ENCODED_PREFIX "keystores://"
-#define ANDROID_KEYSTORE_ENCODED_PREFIX_LEN os_strlen(ANDROID_KEYSTORE_ENCODED_PREFIX)
-
-static void log_cert_validation_failure(const char *reason)
-{
-	android_log_context ctx = create_android_logger(CERT_VALIDATION_FAILURE);
-	android_log_write_string8(ctx, reason);
-	android_log_write_list(ctx, LOG_ID_SECURITY);
-	android_log_destroy(&ctx);
-}
-
-
-static BIO* BIO_from_keystore(const char *alias)
-{
-	BIO *bio = NULL;
-	uint8_t *value = NULL;
-	int length = keystore_get(alias, strlen(alias), &value);
-	if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
-		BIO_write(bio, value, length);
-	free(value);
-	return bio;
-}
-
-static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *alias)
-{
-	BIO *bio = BIO_from_keystore(alias);
-	STACK_OF(X509_INFO) *stack = NULL;
-	stack_index_t i;
-	int ret = 0;
-
-	if (!bio) {
-		wpa_printf(MSG_ERROR, "OpenSSL: Failed to parse certificate: %s",
-				alias);
-		return -1;
-	}
-
-	// Keystore returns X.509 certificates in PEM encoding
-	stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
-	BIO_free(bio);
-
-	if (!stack) {
-		wpa_printf(MSG_ERROR, "OpenSSL: Failed to parse certificate: %s",
-				alias);
-		return -1;
-	}
-
-	for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
-		X509_INFO *info = sk_X509_INFO_value(stack, i);
-
-		if (info->x509)
-			if (!X509_STORE_add_cert(ctx, info->x509)) {
-				wpa_printf(MSG_ERROR,
-						"OpenSSL: Failed to add Root CA certificate");
-				ret = -1;
-				break;
-			}
-		if (info->crl)
-			X509_STORE_add_crl(ctx, info->crl);
-	}
-
-	sk_X509_INFO_pop_free(stack, X509_INFO_free);
-	return ret;
-}
-
-
-static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
-					    const char *encoded_alias)
-{
-	int rc = -1;
-	int len = os_strlen(encoded_alias);
-	unsigned char *decoded_alias;
-
-	if (len & 1) {
-		wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
-			   encoded_alias);
-		return rc;
-	}
-
-	decoded_alias = os_malloc(len / 2 + 1);
-	if (decoded_alias) {
-		if (!hexstr2bin(encoded_alias, decoded_alias, len / 2)) {
-			decoded_alias[len / 2] = '\0';
-			rc = tls_add_ca_from_keystore(
-				ctx, (const char *) decoded_alias);
-		}
-		os_free(decoded_alias);
-	}
-
-	return rc;
-}
-
-#endif /* ANDROID */
-
 static int tls_openssl_ref_count = 0;
 static int tls_ex_idx_session = -1;
 
@@ -246,9 +143,6 @@
 	struct dl_list sessions; /* struct tls_session_data */
 };
 
-static struct tls_context *tls_global = NULL;
-
-
 struct tls_data {
 	SSL_CTX *ssl;
 	unsigned int tls_session_lifetime;
@@ -305,6 +199,121 @@
 	int server_dh_prime_len;
 };
 
+static struct tls_context *tls_global = NULL;
+static tls_get_certificate_cb certificate_callback_global = NULL;
+
+#ifdef ANDROID
+#include <openssl/pem.h>
+
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#define CERT_VALIDATION_FAILURE 210033
+#define ANDROID_KEYSTORE_PREFIX "keystore://"
+#define ANDROID_KEYSTORE_PREFIX_LEN os_strlen(ANDROID_KEYSTORE_PREFIX)
+#define ANDROID_KEYSTORE_ENCODED_PREFIX "keystores://"
+#define ANDROID_KEYSTORE_ENCODED_PREFIX_LEN os_strlen(ANDROID_KEYSTORE_ENCODED_PREFIX)
+
+static void log_cert_validation_failure(const char *reason)
+{
+	android_log_context ctx = create_android_logger(CERT_VALIDATION_FAILURE);
+	android_log_write_string8(ctx, reason);
+	android_log_write_list(ctx, LOG_ID_SECURITY);
+	android_log_destroy(&ctx);
+}
+
+
+static BIO* BIO_from_keystore(const char *alias, struct tls_connection *conn)
+{
+	BIO *bio = NULL;
+	uint8_t *value = NULL;
+
+	void *cb_ctx = NULL;
+	if (conn != NULL && conn->context != NULL) {
+		cb_ctx = conn->context->cb_ctx;
+	}
+
+	if (cb_ctx != NULL && certificate_callback_global != NULL) {
+		wpa_printf(MSG_INFO, "Retrieving certificate using callback");
+		int length = (*certificate_callback_global)(cb_ctx, alias, &value);
+		if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+			BIO_write(bio, value, length);
+		free(value);
+	}
+	return bio;
+}
+
+static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *alias, struct tls_connection *conn)
+{
+	BIO *bio = BIO_from_keystore(alias, conn);
+	STACK_OF(X509_INFO) *stack = NULL;
+	stack_index_t i;
+	int ret = 0;
+
+	if (!bio) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Failed to parse certificate: %s",
+				alias);
+		return -1;
+	}
+
+	// Keystore returns X.509 certificates in PEM encoding
+	stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+	BIO_free(bio);
+
+	if (!stack) {
+		wpa_printf(MSG_ERROR, "OpenSSL: Failed to parse certificate: %s",
+				alias);
+		return -1;
+	}
+
+	for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+		X509_INFO *info = sk_X509_INFO_value(stack, i);
+
+		if (info->x509)
+			if (!X509_STORE_add_cert(ctx, info->x509)) {
+				wpa_printf(MSG_ERROR,
+						"OpenSSL: Failed to add Root CA certificate");
+				ret = -1;
+				break;
+			}
+		if (info->crl)
+			X509_STORE_add_crl(ctx, info->crl);
+	}
+
+	sk_X509_INFO_pop_free(stack, X509_INFO_free);
+	return ret;
+}
+
+
+static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
+						const char *encoded_alias,
+						struct tls_connection *conn)
+{
+	int rc = -1;
+	int len = os_strlen(encoded_alias);
+	unsigned char *decoded_alias;
+
+	if (len & 1) {
+		wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
+			   encoded_alias);
+		return rc;
+	}
+
+	decoded_alias = os_malloc(len / 2 + 1);
+	if (decoded_alias) {
+		if (!hexstr2bin(encoded_alias, decoded_alias, len / 2)) {
+			decoded_alias[len / 2] = '\0';
+			rc = tls_add_ca_from_keystore(
+				ctx, (const char *) decoded_alias, conn);
+		}
+		os_free(decoded_alias);
+	}
+
+	return rc;
+}
+
+#endif /* ANDROID */
+
 
 static struct tls_context * tls_context_new(const struct tls_config *conf)
 {
@@ -1584,6 +1593,15 @@
 	struct tls_connection *conn = arg;
 	const u8 *pos = buf;
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if ((SSL_version(ssl) == TLS1_VERSION ||
+	     SSL_version(ssl) == TLS1_1_VERSION) &&
+	    SSL_get_security_level(ssl) > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Drop security level to 0 to allow TLS 1.0/1.1 use of MD5-SHA1 signature algorithm");
+		SSL_set_security_level(ssl, 0);
+	}
+#endif /* OpenSSL version >= 3.0 */
 	if (write_p == 2) {
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: session ver=0x%x content_type=%d",
@@ -2569,6 +2587,7 @@
 			u8 hash[32];
 			const u8 *addr[1];
 			size_t len[1];
+
 			addr[0] = wpabuf_head(cert);
 			len[0] = wpabuf_len(cert);
 			if (sha256_vector(1, addr, len, hash) < 0 ||
@@ -2590,29 +2609,30 @@
 	}
 #endif /* CONFIG_SHA256 */
 
-	openssl_tls_cert_event(conn, err_cert, depth, buf);
-
 	if (!preverify_ok) {
-		if (depth > 0) {
-			/* Send cert event for the peer certificate so that
-			 * the upper layers get information about it even if
-			 * validation of a CA certificate fails. */
-			STACK_OF(X509) *chain;
+		/* Send cert events for the peer certificate chain so that
+		 * the upper layers get information about it even if
+		 * validation of a CA certificate fails. */
+		STACK_OF(X509) *chain;
+		int num_of_certs;
 
-			chain = X509_STORE_CTX_get1_chain(x509_ctx);
-			if (chain && sk_X509_num(chain) > 0) {
-				char buf2[256];
-				X509 *cert;
+		chain = X509_STORE_CTX_get1_chain(x509_ctx);
+		num_of_certs = sk_X509_num(chain);
+		if (chain && num_of_certs > 0) {
+			char buf2[256];
+			X509 *cert;
+			int cur_depth;
 
-				cert = sk_X509_value(chain, 0);
+			for (cur_depth = num_of_certs - 1; cur_depth >= 0; cur_depth--) {
+				cert = sk_X509_value(chain, cur_depth);
 				X509_NAME_oneline(X509_get_subject_name(cert),
 						  buf2, sizeof(buf2));
 
-				openssl_tls_cert_event(conn, cert, 0, buf2);
+				openssl_tls_cert_event(conn, cert, cur_depth, buf2);
 			}
-			if (chain)
-				sk_X509_pop_free(chain, X509_free);
 		}
+		if (chain)
+			sk_X509_pop_free(chain, X509_free);
 
 		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
 			   " error %d (%s) depth %d for '%s'", err, err_str,
@@ -2622,6 +2642,8 @@
 		return preverify_ok;
 	}
 
+	openssl_tls_cert_event(conn, err_cert, depth, buf);
+
 	wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
 		   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
 		   preverify_ok, err, err_str,
@@ -2891,7 +2913,7 @@
 	if (ca_cert && os_strncmp(ANDROID_KEYSTORE_PREFIX, ca_cert,
 					ANDROID_KEYSTORE_PREFIX_LEN) == 0) {
 		if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
-				&ca_cert[ANDROID_KEYSTORE_PREFIX_LEN]) < 0)
+				&ca_cert[ANDROID_KEYSTORE_PREFIX_LEN], conn) < 0)
 			return -1;
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 		return 0;
@@ -2912,7 +2934,7 @@
 		alias = strtok_r(aliases, delim, &savedptr);
 		for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
 			if (tls_add_ca_from_keystore_encoded(
-					SSL_CTX_get_cert_store(ssl_ctx), alias)) {
+					SSL_CTX_get_cert_store(ssl_ctx), alias, conn)) {
 				wpa_printf(MSG_ERROR,
 						"OpenSSL: Failed to add ca_cert %s from keystore",
 						alias);
@@ -3261,7 +3283,11 @@
 		EC_KEY_free(ecdh);
 #endif
 	}
-	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+	if ((flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH))
+#ifdef EAP_TLSV1_3
+			&& (flags & TLS_CONN_DISABLE_TLSv1_3)
+#endif
+												 ) {
 #ifdef OPENSSL_IS_BORINGSSL
 		uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
 
@@ -3472,7 +3498,7 @@
 #ifdef ANDROID
 	if (os_strncmp(ANDROID_KEYSTORE_PREFIX, client_cert,
 			ANDROID_KEYSTORE_PREFIX_LEN) == 0) {
-		BIO *bio = BIO_from_keystore(&client_cert[ANDROID_KEYSTORE_PREFIX_LEN]);
+		BIO *bio = BIO_from_keystore(&client_cert[ANDROID_KEYSTORE_PREFIX_LEN], conn);
 		X509 *x509 = NULL;
 		if (!bio) {
 			return -1;
@@ -4180,8 +4206,10 @@
 			   "TLS: Failed to decode domain parameters from '%s': %s",
 			   dh_file, ERR_error_string(ERR_get_error(), NULL));
 		BIO_free(bio);
+		OSSL_DECODER_CTX_free(ctx);
 		return -1;
 	}
+	OSSL_DECODER_CTX_free(ctx);
 	BIO_free(bio);
 
 	if (!tmpkey) {
@@ -5404,6 +5432,12 @@
 			   __func__, ERR_error_string(err, NULL));
 	}
 
+	if (tls_set_conn_flags(conn, params->flags,
+			       params->openssl_ciphers) < 0) {
+		wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
+		return -1;
+	}
+
 	if (engine_id) {
 		wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine %s",
 			   engine_id);
@@ -5508,12 +5542,6 @@
 #endif /* OPENSSL_IS_BORINGSSL */
 	}
 
-	if (tls_set_conn_flags(conn, params->flags,
-			       params->openssl_ciphers) < 0) {
-		wpa_printf(MSG_ERROR, "TLS: Failed to set connection flags");
-		return -1;
-	}
-
 #ifdef OPENSSL_IS_BORINGSSL
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
 		SSL_enable_ocsp_stapling(conn->ssl);
@@ -5586,8 +5614,9 @@
 		return "DH";
 	case EVP_PKEY_EC:
 		return "EC";
+	default:
+		return "?";
 	}
-	return "?";
 }
 
 
@@ -6014,3 +6043,8 @@
 		return SSL_get_certificate(conn->ssl) != NULL;
 	return false;
 }
+
+void tls_register_cert_callback(tls_get_certificate_cb cb)
+{
+	certificate_callback_global = cb;
+}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 3392562..1477ecc 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -41,23 +41,12 @@
 #define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
 #define HOSTAPD_CHAN_DFS_MASK 0x00000300
 
-#define HOSTAPD_CHAN_VHT_10_70 0x00000800
-#define HOSTAPD_CHAN_VHT_30_50 0x00001000
-#define HOSTAPD_CHAN_VHT_50_30 0x00002000
-#define HOSTAPD_CHAN_VHT_70_10 0x00004000
+#define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
+#define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
 
 #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
 
-#define HOSTAPD_CHAN_VHT_10_150 0x00100000
-#define HOSTAPD_CHAN_VHT_30_130 0x00200000
-#define HOSTAPD_CHAN_VHT_50_110 0x00400000
-#define HOSTAPD_CHAN_VHT_70_90  0x00800000
-#define HOSTAPD_CHAN_VHT_90_70  0x01000000
-#define HOSTAPD_CHAN_VHT_110_50 0x02000000
-#define HOSTAPD_CHAN_VHT_130_30 0x04000000
-#define HOSTAPD_CHAN_VHT_150_10 0x08000000
-
 /* Allowed bandwidth mask */
 enum hostapd_chan_width_attr {
 	HOSTAPD_CHAN_WIDTH_10   = BIT(0),
@@ -66,6 +55,7 @@
 	HOSTAPD_CHAN_WIDTH_40M  = BIT(3),
 	HOSTAPD_CHAN_WIDTH_80   = BIT(4),
 	HOSTAPD_CHAN_WIDTH_160  = BIT(5),
+	HOSTAPD_CHAN_WIDTH_320  = BIT(6),
 };
 
 /* Filter gratuitous ARP */
@@ -180,6 +170,11 @@
 	 * wmm_rules - WMM regulatory rules
 	 */
 	struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM];
+
+	/**
+	 * punct_bitmap - RU puncturing bitmap
+	 */
+	u16 punct_bitmap;
 };
 
 #define HE_MAC_CAPAB_0		0
@@ -211,6 +206,7 @@
 
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
 #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
 
 
 enum ieee80211_op_mode {
@@ -667,6 +663,16 @@
 	 */
 	unsigned int p2p_include_6ghz:1;
 
+	/**
+	 * non_coloc_6ghz - Force scanning of non-PSC 6 GHz channels
+	 *
+	 * If this is set, the driver should scan non-PSC channels from the
+	 * scan request even if neighbor reports from 2.4/5 GHz APs did not
+	 * report a co-located AP on these channels. The default is to scan
+	 * non-PSC channels only if a co-located AP was reported on the channel.
+	 */
+	unsigned int non_coloc_6ghz:1;
+
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -709,6 +715,21 @@
 	 * auth_data_len - Length of auth_data buffer in octets
 	 */
 	size_t auth_data_len;
+
+	/**
+	 * mld - Establish an MLD connection
+	 */
+	bool mld;
+
+	/**
+	 * mld_link_id - The link ID of the MLD AP to which we are associating
+	 */
+	u8 mld_link_id;
+
+	/**
+	 * The MLD AP address
+	 */
+	const u8 *ap_mld_addr;
 };
 
 /**
@@ -872,6 +893,38 @@
 	size_t fils_kek_len;
 };
 
+struct wpa_driver_mld_params {
+	/**
+	 * mld_addr - AP's MLD address
+	 */
+	const u8 *mld_addr;
+
+	/**
+	 * valid_links - The valid links including the association link
+	 */
+	u16 valid_links;
+
+	/**
+	 * assoc_link_id - The link on which the association is performed
+	 */
+	u8 assoc_link_id;
+
+	/**
+	 * mld_links - Link information
+	 *
+	 * Should include information on all the requested links for association
+	 * including the link on which the association should take place.
+	 * For the association link, the ies and ies_len should be NULL and
+	 * 0 respectively.
+	 */
+	struct {
+		int freq;
+		const u8 *bssid;
+		const u8 *ies;
+		size_t ies_len;
+	} mld_links[MAX_NUM_MLD_LINKS];
+};
+
 /**
  * struct wpa_driver_associate_params - Association parameters
  * Data for struct wpa_driver_ops::associate().
@@ -989,6 +1042,17 @@
 	unsigned int key_mgmt_suite;
 
 	/**
+	 * allowed_key_mgmts - Bitfield of allowed key management suites
+	 * (WPA_KEY_MGMT_*) other than @key_mgmt_suite for the current
+	 * connection
+	 *
+	 * SME in the driver may choose key_mgmt from this list for the initial
+	 * connection or roaming. The driver which doesn't support this
+	 * ignores this parameter.
+	 */
+	unsigned int allowed_key_mgmts;
+
+	/**
 	 * auth_alg - Allowed authentication algorithms
 	 * Bit field of WPA_AUTH_ALG_*
 	 */
@@ -1228,13 +1292,24 @@
 	 * 1 = hash-to-element only
 	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
 	 */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
+
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	/**
 	 * td_policy - Transition Disable Policy
 	 */
 	u32 td_policy;
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
+	/**
+	 * disable_eht - Disable EHT for this connection
+	 */
+	int disable_eht;
+
+	/*
+	 * mld_params - MLD association parameters
+	 */
+	struct wpa_driver_mld_params mld_params;
 };
 
 enum hide_ssid {
@@ -1578,7 +1653,7 @@
 	 * 1 = hash-to-element only
 	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
 	 */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 
 	/**
 	 * FILS Discovery frame minimum interval in TUs
@@ -1614,6 +1689,50 @@
 	 * Unsolicited broadcast Probe Response template length
 	 */
 	size_t unsol_bcast_probe_resp_tmpl_len;
+
+	/**
+	 * mbssid_tx_iface - Transmitting interface of the MBSSID set
+	 */
+	const char *mbssid_tx_iface;
+
+	/**
+	 * mbssid_index - The index of this BSS in the MBSSID set
+	 */
+	unsigned int mbssid_index;
+
+	/**
+	 * mbssid_elem - Buffer containing all MBSSID elements
+	 */
+	u8 *mbssid_elem;
+
+	/**
+	 * mbssid_elem_len - Total length of all MBSSID elements
+	 */
+	size_t mbssid_elem_len;
+
+	/**
+	 * mbssid_elem_count - The number of MBSSID elements
+	 */
+	u8 mbssid_elem_count;
+
+	/**
+	 * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+	 * Kernel will use these offsets to generate multiple BSSID beacons.
+	 */
+	u8 **mbssid_elem_offset;
+
+	/**
+	 * ema - Enhanced MBSSID advertisements support.
+	 */
+	bool ema;
+
+	/**
+	 * punct_bitmap - Preamble puncturing bitmap
+	 * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+	 * channel with the lowest frequency. A bit set to 1 indicates that the
+	 * subchannel is punctured, otherwise active.
+	 */
+	u16 punct_bitmap;
 };
 
 struct wpa_driver_mesh_bss_params {
@@ -1767,6 +1886,12 @@
 	 * %KEY_FLAG_RX_TX
 	 *  RX/TX key. */
 	enum key_flag key_flag;
+
+	/**
+	 * link_id - MLO Link ID
+	 *
+	 * Set to a valid Link ID (0-14) when applicable, otherwise -1. */
+	int link_id;
 };
 
 enum wpa_driver_if_type {
@@ -1864,6 +1989,8 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384	0x00200000
 #define WPA_DRIVER_CAPA_KEY_MGMT_CCKM		0x00400000
 #define WPA_DRIVER_CAPA_KEY_MGMT_OSEN		0x00800000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY	0x01000000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY	0x02000000
 	/** Bitfield of supported key management suites */
 	unsigned int key_mgmt;
 	unsigned int key_mgmt_iftype[WPA_IF_MAX];
@@ -2042,15 +2169,15 @@
 #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX	0x0000000000000001ULL
 /** Driver supports TX status reports for EAPOL frames through control port */
 #define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
-/** Driver supports secure LTF */
-#define WPA_DRIVER_FLAGS2_SEC_LTF		0x0000000000000004ULL
-/** Driver supports secure RTT measurement exchange */
-#define WPA_DRIVER_FLAGS2_SEC_RTT		0x0000000000000008ULL
+/** Driver supports secure LTF in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_AP		0x0000000000000004ULL
+/** Driver supports secure RTT measurement exchange in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_AP		0x0000000000000008ULL
 /**
  * Driver supports protection of range negotiation and measurement management
- * frames
+ * frames in AP mode
  */
-#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG	0x0000000000000010ULL
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP	0x0000000000000010ULL
 /** Driver supports Beacon frame TX rate configuration (HE rates) */
 #define WPA_DRIVER_FLAGS2_BEACON_RATE_HE	0x0000000000000020ULL
 /** Driver supports Beacon protection only in client mode */
@@ -2063,6 +2190,17 @@
 #define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP	0x0000000000000200ULL
 /** Driver supports background radar/CAC detection */
 #define WPA_DRIVER_RADAR_BACKGROUND		0x0000000000000400ULL
+/** Driver supports secure LTF in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_STA		0x0000000000000800ULL
+/** Driver supports secure RTT measurement exchange in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_STA		0x0000000000001000ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in STA mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA	0x0000000000002000ULL
+/** Driver supports MLO in station/AP mode */
+#define WPA_DRIVER_FLAGS2_MLO			0x0000000000004000ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2175,11 +2313,26 @@
 
 	/* Maximum number of supported CSA counters */
 	u16 max_csa_counters;
+
+	/* Maximum number of supported AKM suites in commands */
+	unsigned int max_num_akms;
+
+	/* Maximum number of interfaces supported for MBSSID advertisement */
+	unsigned int mbssid_max_interfaces;
+	/* Maximum profile periodicity for enhanced MBSSID advertisement */
+	unsigned int ema_max_periodicity;
 };
 
 
 struct hostapd_data;
 
+enum guard_interval {
+	GUARD_INTERVAL_0_4 = 1,
+	GUARD_INTERVAL_0_8 = 2,
+	GUARD_INTERVAL_1_6 = 3,
+	GUARD_INTERVAL_3_2 = 4,
+};
+
 #define STA_DRV_DATA_TX_MCS BIT(0)
 #define STA_DRV_DATA_RX_MCS BIT(1)
 #define STA_DRV_DATA_TX_VHT_MCS BIT(2)
@@ -2190,14 +2343,23 @@
 #define STA_DRV_DATA_RX_SHORT_GI BIT(7)
 #define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
 #define STA_DRV_DATA_CONN_TIME BIT(9)
+#define STA_DRV_DATA_TX_HE_MCS BIT(10)
+#define STA_DRV_DATA_RX_HE_MCS BIT(11)
+#define STA_DRV_DATA_TX_HE_NSS BIT(12)
+#define STA_DRV_DATA_RX_HE_NSS BIT(13)
+#define STA_DRV_DATA_TX_HE_DCM BIT(14)
+#define STA_DRV_DATA_RX_HE_DCM BIT(15)
+#define STA_DRV_DATA_TX_HE_GI BIT(16)
+#define STA_DRV_DATA_RX_HE_GI BIT(17)
 
 struct hostap_sta_driver_data {
 	unsigned long rx_packets, tx_packets;
 	unsigned long long rx_bytes, tx_bytes;
 	unsigned long long rx_airtime, tx_airtime;
+	unsigned long long beacons_count;
 	int bytes_64bit; /* whether 64-bit byte counters are supported */
-	unsigned long current_tx_rate;
-	unsigned long current_rx_rate;
+	unsigned long current_tx_rate; /* in kbps */
+	unsigned long current_rx_rate; /* in kbps */
 	unsigned long inactive_msec;
 	unsigned long connected_sec;
 	unsigned long flags; /* bitfield of STA_DRV_DATA_* */
@@ -2207,13 +2369,27 @@
 	s8 last_ack_rssi;
 	unsigned long backlog_packets;
 	unsigned long backlog_bytes;
-	s8 signal;
+	unsigned long fcs_error_count;
+	unsigned long beacon_loss_count;
+	unsigned long expected_throughput;
+	unsigned long rx_drop_misc;
+	unsigned long rx_mpdus;
+	int signal; /* dBm; or -WPA_INVALID_NOISE */
+	u8 rx_hemcs;
+	u8 tx_hemcs;
 	u8 rx_vhtmcs;
 	u8 tx_vhtmcs;
 	u8 rx_mcs;
 	u8 tx_mcs;
+	u8 rx_he_nss;
+	u8 tx_he_nss;
 	u8 rx_vht_nss;
 	u8 tx_vht_nss;
+	s8 avg_signal; /* dBm */
+	s8 avg_beacon_signal; /* dBm */
+	s8 avg_ack_signal; /* dBm */
+	enum guard_interval rx_guard_interval, tx_guard_interval;
+	u8 rx_dcm, tx_dcm;
 };
 
 struct hostapd_sta_add_params {
@@ -2247,6 +2423,10 @@
 	const u8 *supp_oper_classes;
 	size_t supp_oper_classes_len;
 	int support_p2p_ps;
+
+	bool mld_link_sta;
+	s8 mld_link_id;
+	const u8 *mld_link_addr;
 };
 
 struct mac_address {
@@ -2340,11 +2520,8 @@
  * @frequency: control frequency
  * @above_threshold: true if the above threshold was crossed
  *	(relevant for a CQM event)
- * @current_signal: in dBm
- * @avg_signal: in dBm
- * @avg_beacon_signal: in dBm
+ * @data: STA information
  * @current_noise: %WPA_INVALID_NOISE if not supported
- * @current_txrate: current TX rate
  * @chanwidth: channel width
  * @center_frq1: center frequency for the first segment
  * @center_frq2: center frequency for the second segment (if relevant)
@@ -2352,16 +2529,18 @@
 struct wpa_signal_info {
 	u32 frequency;
 	int above_threshold;
-	int current_signal;
-	int avg_signal;
-	int avg_beacon_signal;
+	struct hostap_sta_driver_data data;
 	int current_noise;
-	int current_txrate;
 	enum chan_width chanwidth;
 	int center_frq1;
 	int center_frq2;
 };
 
+struct wpa_mlo_signal_info {
+	u16 valid_links;
+	struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
+};
+
 /**
  * struct wpa_channel_info - Information about the current channel
  * @frequency: Center frequency of the primary 20 MHz channel
@@ -2422,6 +2601,7 @@
  * @beacon_after: Next beacon/probe resp/asooc resp info
  * @counter_offset_beacon: Offset to the count field in beacon's tail
  * @counter_offset_presp: Offset to the count field in probe resp.
+ * @punct_bitmap - Preamble puncturing bitmap
  */
 struct csa_settings {
 	u8 cs_count;
@@ -2433,6 +2613,8 @@
 
 	u16 counter_offset_beacon[2];
 	u16 counter_offset_presp[2];
+
+	u16 punct_bitmap;
 };
 
 /**
@@ -2492,6 +2674,7 @@
 enum drv_br_port_attr {
 	DRV_BR_PORT_ATTR_PROXYARP,
 	DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+	DRV_BR_PORT_ATTR_MCAST2UCAST,
 };
 
 enum drv_br_net_param {
@@ -2580,6 +2763,7 @@
  *	the real status code for failures. Used only for the request interface
  *	from user space to the driver.
  * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
+ * @mld_addr: AP's MLD address or %NULL if MLO is not used
  */
 struct external_auth {
 	enum {
@@ -2592,6 +2776,91 @@
 	unsigned int key_mgmt_suite;
 	u16 status;
 	const u8 *pmkid;
+	const u8 *mld_addr;
+};
+
+#define WPAS_MAX_PASN_PEERS 10
+
+enum pasn_status {
+	PASN_STATUS_SUCCESS = 0,
+	PASN_STATUS_FAILURE = 1,
+};
+
+/**
+ * struct pasn_peer - PASN peer parameters
+ *
+ * Used to process the PASN authentication event from the driver to
+ * userspace and to send a response back.
+ * @own_addr: Own MAC address specified by the driver to use for PASN
+ *	handshake.
+ * @peer_addr: MAC address of the peer with which PASN authentication is to be
+ *	performed.
+ * @network_id: Unique id for the network.
+ *	This identifier is used as a unique identifier for each network
+ *	block when using the control interface. Each network is allocated an
+ *	id when it is being created, either when reading the configuration
+ *	file or when a new network is added through the control interface.
+ * @akmp: Authentication key management protocol type supported.
+ * @cipher: Cipher suite.
+ * @group: Finite cyclic group. Default group used is 19 (ECC).
+ * @ltf_keyseed_required: Indicates whether LTF keyseed generation is required
+ * @status: PASN response status, %PASN_STATUS_SUCCESS for successful
+ *	authentication, use %PASN_STATUS_FAILURE if PASN authentication
+ *	fails or if wpa_supplicant fails to set the security ranging context to
+ *	the driver
+ */
+struct pasn_peer {
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+	int network_id;
+	int akmp;
+	int cipher;
+	int group;
+	bool ltf_keyseed_required;
+	enum pasn_status status;
+};
+
+/**
+ * struct pasn_auth - PASN authentication trigger parameters
+ *
+ * These are used across the PASN authentication event from the driver to
+ * userspace and to send a response to it.
+ * @action: Action type. Only significant for the event interface.
+ * @num_peers: The number of peers for which the PASN handshake is requested
+ *	for.
+ * @peer: Holds the peer details.
+ */
+struct pasn_auth {
+	enum {
+		PASN_ACTION_AUTH,
+		PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT,
+	} action;
+	unsigned int num_peers;
+	struct pasn_peer peer[WPAS_MAX_PASN_PEERS];
+};
+
+/**
+ * struct secure_ranging_params - Parameters required to set secure ranging
+ *	context for a peer.
+ *
+ * @action: Add or delete a security context to the driver.
+ * @own_addr: Own MAC address used during key derivation.
+ * @peer_addr: Address of the peer device.
+ * @cipher: Cipher suite.
+ * @tk_len: Length of temporal key.
+ * @tk: Temporal key buffer.
+ * @ltf_keyseed_len: Length of LTF keyseed.
+ * @ltf_keyeed: LTF keyseed buffer.
+ */
+struct secure_ranging_params {
+	u32 action;
+	const u8 *own_addr;
+	const u8 *peer_addr;
+	u32 cipher;
+	u8 tk_len;
+	const u8 *tk;
+	u8 ltf_keyseed_len;
+	const u8 *ltf_keyseed;
 };
 
 /* enum nested_attr - Used to specify if subcommand uses nested attributes */
@@ -2601,6 +2870,36 @@
 	NESTED_ATTR_UNSPECIFIED = 2,
 };
 
+/* Preferred channel list information */
+
+/* GO role */
+#define WEIGHTED_PCL_GO BIT(0)
+/* P2P Client role */
+#define WEIGHTED_PCL_CLI BIT(1)
+/* Must be considered for operating channel */
+#define WEIGHTED_PCL_MUST_CONSIDER BIT(2)
+/* Should be excluded in GO negotiation */
+#define WEIGHTED_PCL_EXCLUDE BIT(3)
+
+/* Preferred channel list with weight */
+struct weighted_pcl {
+	u32 freq; /* MHz */
+	u8 weight;
+	u32 flag; /* bitmap for WEIGHTED_PCL_* */
+};
+
+struct driver_sta_mlo_info {
+	u16 req_links; /* bitmap of requested link IDs */
+	u16 valid_links; /* bitmap of accepted link IDs */
+	u8 assoc_link_id;
+	u8 ap_mld_addr[ETH_ALEN];
+	struct {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		unsigned int freq;
+	} links[MAX_NUM_MLD_LINKS];
+};
+
 /**
  * struct wpa_driver_ops - Driver interface API definition
  *
@@ -2821,9 +3120,9 @@
 	 * poll - Poll driver for association information
 	 * @priv: private driver interface data
 	 *
-	 * This is an option callback that can be used when the driver does not
-	 * provide event mechanism for association events. This is called when
-	 * receiving WPA EAPOL-Key messages that require association
+	 * This is an optional callback that can be used when the driver does
+	 * not provide event mechanism for association events. This is called
+	 * when receiving WPA/RSN EAPOL-Key messages that require association
 	 * information. The driver interface is supposed to generate associnfo
 	 * event before returning from this callback function. In addition, the
 	 * driver interface should generate an association event after having
@@ -3848,6 +4147,14 @@
 	int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
 
 	/**
+	 * mlo_signal_poll - Get current MLO connection information
+	 * @priv: Private driver interface data
+	 * @mlo_signal_info: MLO connection info structure
+	 */
+	int (*mlo_signal_poll)(void *priv,
+			       struct wpa_mlo_signal_info *mlo_signal_info);
+
+	/**
 	 * channel_info - Get parameters of the current operating channel
 	 * @priv: Private driver interface data
 	 * @channel_info: Channel info structure
@@ -4234,6 +4541,16 @@
 	int (*set_replay_protect)(void *priv, bool enabled, u32 window);
 
 	/**
+	 * set_offload - Set MACsec hardware offload
+	 * @priv: Private driver interface data
+	 * @offload: 0 = MACSEC_OFFLOAD_OFF
+	 *           1 = MACSEC_OFFLOAD_PHY
+	 *           2 = MACSEC_OFFLOAD_MAC
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_offload)(void *priv, u8 offload);
+
+	/**
 	 * set_current_cipher_suite - Set current cipher suite
 	 * @priv: Private driver interface data
 	 * @cs: EUI64 identifier
@@ -4445,14 +4762,17 @@
 	 * @priv: Private driver interface data
 	 * @if_type: Interface type
 	 * @num: Number of channels
-	 * @freq_list: Preferred channel frequency list encoded in MHz values
+	 * @freq_list: Weighted preferred channel list
 	 * Returns 0 on success, -1 on failure
 	 *
 	 * This command can be used to query the preferred frequency list from
-	 * the driver specific to a particular interface type.
+	 * the driver specific to a particular interface type. The freq_list
+	 * array needs to have room for *num entries. *num will be updated to
+	 * indicate the number of entries fetched from the driver.
 	 */
 	int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
-				  unsigned int *num, unsigned int *freq_list);
+				  unsigned int *num,
+				  struct weighted_pcl *freq_list);
 
 	/**
 	 * set_prob_oper_freq - Indicate probable P2P operating channel
@@ -4641,6 +4961,46 @@
 	 */
 	int (*dpp_listen)(void *priv, bool enable);
 
+	/**
+	 * set_secure_ranging_ctx - Add or delete secure ranging parameters of
+	 * the specified peer to the driver.
+	 * @priv: Private driver interface data
+	 * @params: Secure ranging parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 */
+	int (*set_secure_ranging_ctx)(void *priv,
+				      struct secure_ranging_params *params);
+
+	/**
+	 * send_pasn_resp - Send PASN response for a set of peers to the
+	 * driver.
+	 * @priv: Private driver interface data
+	 * @params: Parameters holding peers and respective status.
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_pasn_resp)(void *priv, struct pasn_auth *params);
+
+	/**
+	 * get_sta_mlo_info - Get the current multi-link association info
+	 * @priv: Private driver interface data
+	 * @mlo: Pointer to fill multi-link association info
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is used to fetch multi-link of the current association.
+	 */
+	int (*get_sta_mlo_info)(void *priv,
+				struct driver_sta_mlo_info *mlo_info);
+
+	/**
+	 * link_add - Add a link to the AP MLD interface
+	 * @priv: Private driver interface data
+	 * @link_id: The link ID
+	 * @addr: The MAC address to use for the link
+	 * Returns: 0 on success, negative value on failure
+	 */
+	int (*link_add)(void *priv, u8 link_id, const u8 *addr);
+
 #ifdef CONFIG_TESTING_OPTIONS
 	int (*register_frame)(void *priv, u16 type,
 			      const u8 *match, size_t match_len,
@@ -5234,6 +5594,30 @@
 	 * EVENT_CCA_NOTIFY - Notification that CCA has completed
 	 */
 	EVENT_CCA_NOTIFY,
+
+	/**
+	 * EVENT_PASN_AUTH - This event is used by the driver that requests
+	 * PASN authentication and secure ranging context for multiple peers.
+	 */
+	EVENT_PASN_AUTH,
+
+	/**
+	 * EVENT_LINK_CH_SWITCH - MLD AP link decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch.
+	 *
+	 */
+	EVENT_LINK_CH_SWITCH,
+
+	/**
+	 * EVENT_LINK_CH_SWITCH_STARTED - MLD AP link started to switch channels
+	 *
+	 * This is a pre-switch event indicating the shortly following switch
+	 * of operating channels.
+	 *
+	 * Described in wpa_event_data.ch_switch.
+	 */
+	EVENT_LINK_CH_SWITCH_STARTED,
 };
 
 
@@ -5757,6 +6141,12 @@
 		 * ssi_signal - Signal strength in dBm (or 0 if not available)
 		 */
 		int ssi_signal;
+
+		/**
+		 * link_id - MLO link on which the frame was received or -1 for
+		 * non MLD.
+		 */
+		int link_id;
 	} rx_mgmt;
 
 	/**
@@ -5856,6 +6246,8 @@
 		const u8 *src;
 		const u8 *data;
 		size_t data_len;
+		enum frame_encryption encrypted;
+		int link_id;
 	} eapol_rx;
 
 	/**
@@ -5948,6 +6340,8 @@
 	 * @ch_width: Channel width
 	 * @cf1: Center frequency 1
 	 * @cf2: Center frequency 2
+	 * @link_id: Link ID of the MLO link
+	 * @punct_bitmap: Puncturing bitmap
 	 */
 	struct ch_switch {
 		int freq;
@@ -5956,6 +6350,8 @@
 		enum chan_width ch_width;
 		int cf1;
 		int cf2;
+		int link_id;
+		u16 punct_bitmap;
 	} ch_switch;
 
 	/**
@@ -6133,6 +6529,19 @@
 	struct bss_color_collision {
 		u64 bitmap;
 	} bss_color_collision;
+
+	/**
+	 * struct pasn_auth - Data for EVENT_PASN_AUTH
+	 */
+	struct pasn_auth pasn_auth;
+
+	/**
+	 * struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+	 */
+	struct port_authorized {
+		const u8 *td_bitmap;
+		size_t td_bitmap_len;
+	} port_authorized;
 };
 
 /**
@@ -6194,6 +6603,23 @@
 	event.eapol_rx.src = src;
 	event.eapol_rx.data = data;
 	event.eapol_rx.data_len = data_len;
+	event.eapol_rx.encrypted = FRAME_ENCRYPTION_UNKNOWN;
+	event.eapol_rx.link_id = -1;
+	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+static inline void drv_event_eapol_rx2(void *ctx, const u8 *src, const u8 *data,
+				       size_t data_len,
+				       enum frame_encryption encrypted,
+				       int link_id)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_rx.src = src;
+	event.eapol_rx.data = data;
+	event.eapol_rx.data_len = data_len;
+	event.eapol_rx.encrypted = encrypted;
+	event.eapol_rx.link_id = link_id;
 	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
 }
 
@@ -6210,6 +6636,8 @@
 
 int ht_supported(const struct hostapd_hw_modes *mode);
 int vht_supported(const struct hostapd_hw_modes *mode);
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+		  enum ieee80211_op_mode op_mode);
 
 struct wowlan_triggers *
 wpa_get_wowlan_triggers(const char *wowlan_triggers,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 8db7861..bbd1a7c 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -95,6 +95,9 @@
 	E2S(CCA_STARTED_NOTIFY);
 	E2S(CCA_ABORTED_NOTIFY);
 	E2S(CCA_NOTIFY);
+	E2S(PASN_AUTH);
+	E2S(LINK_CH_SWITCH);
+	E2S(LINK_CH_SWITCH_STARTED);
 	}
 
 	return "UNKNOWN";
@@ -117,6 +120,8 @@
 		return "80+80 MHz";
 	case CHAN_WIDTH_160:
 		return "160 MHz";
+	case CHAN_WIDTH_320:
+		return "320 MHz";
 	default:
 		return "unknown";
 	}
@@ -136,6 +141,8 @@
 	case CHAN_WIDTH_80P80:
 	case CHAN_WIDTH_160:
 		return 160;
+	case CHAN_WIDTH_320:
+		return 320;
 	default:
 		return 0;
 	}
@@ -178,6 +185,21 @@
 }
 
 
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+		  enum ieee80211_op_mode op_mode)
+{
+	if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_HE_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports HE. Assume
+		 * it does to avoid connection issues.
+		 */
+		return true;
+	}
+
+	return hw_mode->he_capab[op_mode].he_supported;
+}
+
+
 static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
 				    int capa_trigger, u8 *param_trigger)
 {
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index b609bbf..c79e873 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -32,6 +32,10 @@
 
 #define UNUSED_SCI 0xffffffffffffffff
 
+#if LIBNL_VER_NUM >= LIBNL_VER(3, 6)
+#define LIBNL_HAS_OFFLOAD
+#endif
+
 struct cb_arg {
 	struct macsec_drv_data *drv;
 	u32 *pn;
@@ -73,6 +77,11 @@
 	bool replay_protect;
 	bool replay_protect_set;
 
+#ifdef LIBNL_HAS_OFFLOAD
+	enum macsec_offload offload;
+	bool offload_set;
+#endif /* LIBNL_HAS_OFFLOAD */
+
 	u32 replay_window;
 
 	u8 encoding_sa;
@@ -228,6 +237,15 @@
 						    drv->replay_window);
 	}
 
+#ifdef LIBNL_HAS_OFFLOAD
+	if (drv->offload_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit offload=%d",
+			   drv->ifname, drv->offload);
+		rtnl_link_macsec_set_offload(drv->link, drv->offload);
+	}
+#endif /* LIBNL_HAS_OFFLOAD */
+
 	if (drv->encoding_sa_set) {
 		wpa_printf(MSG_DEBUG, DRV_PREFIX
 			   "%s: try_commit encoding_sa=%d",
@@ -456,6 +474,36 @@
 
 
 /**
+ * macsec_drv_set_offload - Set offload status
+ * @priv: Private driver interface data
+ * @offload: 0 = MACSEC_OFFLOAD_OFF
+ *           1 = MACSEC_OFFLOAD_PHY
+ *           2 = MACSEC_OFFLOAD_MAC
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_offload(void *priv, u8 offload)
+{
+#ifdef LIBNL_HAS_OFFLOAD
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %02" PRIx8, __func__, offload);
+
+	drv->offload_set = true;
+	drv->offload = offload;
+
+	return try_commit(drv);
+#else /* LIBNL_HAS_OFFLOAD */
+	if (offload == 0)
+		return 0;
+	wpa_printf(MSG_INFO,
+		   "%s: libnl version does not include support for MACsec offload",
+		   __func__);
+	return -1;
+#endif /* LIBNL_HAS_OFFLOAD */
+}
+
+
+/**
  * macsec_drv_set_current_cipher_suite - Set current cipher suite
  * @priv: Private driver interface data
  * @cs: EUI64 identifier
@@ -1648,6 +1696,7 @@
 	.enable_protect_frames = macsec_drv_enable_protect_frames,
 	.enable_encrypt = macsec_drv_enable_encrypt,
 	.set_replay_protect = macsec_drv_set_replay_protect,
+	.set_offload = macsec_drv_set_offload,
 	.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
 	.enable_controlled_port = macsec_drv_enable_controlled_port,
 	.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 6bd88be..cc87e72 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -31,6 +31,8 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_common.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
 #include "netlink.h"
 #include "linux_defines.h"
 #include "linux_ioctl.h"
@@ -173,6 +175,8 @@
 				  const u16 *csa_offs, size_t csa_offs_len);
 static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
 					       int report);
+static int nl80211_put_freq_params(struct nl_msg *msg,
+				   const struct hostapd_freq_params *freq);
 
 #define IFIDX_ANY -1
 
@@ -219,8 +223,11 @@
 		return CHAN_WIDTH_80P80;
 	case NL80211_CHAN_WIDTH_160:
 		return CHAN_WIDTH_160;
+	case NL80211_CHAN_WIDTH_320:
+		return CHAN_WIDTH_320;
+	default:
+		return CHAN_WIDTH_UNKNOWN;
 	}
-	return CHAN_WIDTH_UNKNOWN;
 }
 
 
@@ -270,8 +277,17 @@
 	if (drv->associated)
 		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
 	drv->associated = 0;
+	os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
 	os_memset(drv->bssid, 0, ETH_ALEN);
-	drv->first_bss->freq = 0;
+	drv->first_bss->flink->freq = 0;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	os_free(drv->pending_roam_data);
+	drv->pending_roam_data = NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	drv->auth_mld = false;
+	drv->auth_mld_link_id = -1;
+	os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN);
 }
 
 
@@ -901,7 +917,7 @@
 	dl_list_init(&w->drvs);
 
 	/* Beacon frames not supported in IEEE 802.11ad */
-	if (ieee80211_freq_to_chan(bss->freq, &channel) !=
+	if (ieee80211_freq_to_chan(bss->flink->freq, &channel) !=
 	    HOSTAPD_MODE_IEEE80211AD) {
 		w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
 		if (!w->nl_cb) {
@@ -1015,6 +1031,74 @@
 }
 
 
+static int get_mlo_info(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+	static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+		[NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+		[NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN },
+	};
+	struct driver_sta_mlo_info *info = arg;
+	int rem;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_MLO_LINKS])
+		return NL_SKIP;
+
+	info->valid_links = 0;
+	nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+		u8 link_id;
+
+		if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+				     link_attr, link_policy) != 0)
+			continue;
+
+		if (!link_data[NL80211_ATTR_MLO_LINK_ID] ||
+		    !link_data[NL80211_ATTR_MAC])
+			continue;
+
+		link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+		info->valid_links |= BIT(link_id);
+		os_memcpy(info->links[link_id].addr,
+			  nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+		if (link_data[NL80211_ATTR_WIPHY_FREQ])
+			info->links[link_id].freq =
+				nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_sta_mlo_info(void *priv,
+				    struct driver_sta_mlo_info *mlo_info)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!drv->associated)
+		return -1;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		struct nl_msg *msg;
+
+		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+		if (send_and_recv_msgs(drv, msg, get_mlo_info,
+				       &drv->sta_mlo_info, NULL, NULL))
+			return -1;
+	}
+
+	os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
+	return 0;
+}
+
+
 static void wpa_driver_nl80211_event_newlink(
 	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
 	int ifindex, const char *ifname)
@@ -1169,6 +1253,7 @@
 			   MACSTR " to " MACSTR,
 			   ifindex, bss->ifname,
 			   MAC2STR(bss->addr), MAC2STR(addr));
+		os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
 		os_memcpy(bss->addr, addr, ETH_ALEN);
 		if (notify)
 			wpa_supplicant_event(drv->ctx,
@@ -1440,6 +1525,8 @@
 	u8 assoc_bssid[ETH_ALEN];
 	u8 assoc_ssid[SSID_MAX_LEN];
 	u8 assoc_ssid_len;
+	u8 bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+	unsigned int freq[MAX_NUM_MLD_LINKS];
 };
 
 static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
@@ -1452,9 +1539,11 @@
 		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
 		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 },
 	};
 	struct nl80211_get_assoc_freq_arg *ctx = arg;
 	enum nl80211_bss_status status;
+	struct wpa_driver_nl80211_data *drv = ctx->drv;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -1467,9 +1556,25 @@
 	status = nla_get_u32(bss[NL80211_BSS_STATUS]);
 	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
 	    bss[NL80211_BSS_FREQUENCY]) {
-		ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-		wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
-			   ctx->assoc_freq);
+		int link_id = -1;
+		u32 freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+
+		if (bss[NL80211_BSS_MLO_LINK_ID])
+			link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+		if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+			ctx->freq[link_id] = freq;
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLO link %d associated on %u MHz",
+				   link_id, ctx->freq[link_id]);
+		}
+
+		if (!drv->sta_mlo_info.valid_links ||
+		    drv->sta_mlo_info.assoc_link_id == link_id) {
+			ctx->assoc_freq = freq;
+			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+				   ctx->assoc_freq);
+		}
 	}
 	if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
 	    bss[NL80211_BSS_FREQUENCY]) {
@@ -1479,10 +1584,26 @@
 	}
 	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
 	    bss[NL80211_BSS_BSSID]) {
-		os_memcpy(ctx->assoc_bssid,
-			  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
-		wpa_printf(MSG_DEBUG, "nl80211: Associated with "
-			   MACSTR, MAC2STR(ctx->assoc_bssid));
+		int link_id = -1;
+		const u8 *bssid = nla_data(bss[NL80211_BSS_BSSID]);
+
+		if (bss[NL80211_BSS_MLO_LINK_ID])
+			link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+		if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+			os_memcpy(ctx->bssid[link_id], bssid, ETH_ALEN);
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLO link %d associated with "
+				   MACSTR, link_id, MAC2STR(bssid));
+		}
+
+		if (!drv->sta_mlo_info.valid_links ||
+		    drv->sta_mlo_info.assoc_link_id == link_id) {
+			os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+				   MACSTR, MAC2STR(bssid));
+		}
+
 	}
 
 	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
@@ -1568,6 +1689,14 @@
 			   "associated BSS from scan results: %u MHz", freq);
 		if (freq)
 			drv->assoc_freq = freq;
+
+		if (drv->sta_mlo_info.valid_links) {
+			int i;
+
+			for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+				drv->sta_mlo_info.links[i].freq = arg.freq[i];
+		}
+
 		return drv->assoc_freq;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -1575,87 +1704,6 @@
 	return drv->assoc_freq;
 }
 
-
-static int get_link_signal(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
-	static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
-		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
-		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
-		[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
-	};
-	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
-	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
-		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
-		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
-		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
-		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
-	};
-	struct wpa_signal_info *sig_change = arg;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-	if (!tb[NL80211_ATTR_STA_INFO] ||
-	    nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
-			     tb[NL80211_ATTR_STA_INFO], policy))
-		return NL_SKIP;
-	if (!sinfo[NL80211_STA_INFO_SIGNAL])
-		return NL_SKIP;
-
-	sig_change->current_signal =
-		(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
-
-	if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
-		sig_change->avg_signal =
-			(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
-	else
-		sig_change->avg_signal = 0;
-
-	if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
-		sig_change->avg_beacon_signal =
-			(s8)
-			nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
-	else
-		sig_change->avg_beacon_signal = 0;
-
-	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
-		if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
-				     sinfo[NL80211_STA_INFO_TX_BITRATE],
-				     rate_policy)) {
-			sig_change->current_txrate = 0;
-		} else {
-			if (rinfo[NL80211_RATE_INFO_BITRATE]) {
-				sig_change->current_txrate =
-					nla_get_u16(rinfo[
-					     NL80211_RATE_INFO_BITRATE]) * 100;
-			}
-		}
-	}
-
-	return NL_SKIP;
-}
-
-
-int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
-			    struct wpa_signal_info *sig)
-{
-	struct nl_msg *msg;
-
-	sig->current_signal = -WPA_INVALID_NOISE;
-	sig->current_txrate = 0;
-
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
-	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
-		nlmsg_free(msg);
-		return -ENOBUFS;
-	}
-
-	return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
-}
-
-
 static int get_link_noise(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1858,6 +1906,16 @@
 		return -ENOMEM;
 
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+		/* put wiphy idx to get the interface specific country code
+		 * instead of the global one. */
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
 	alpha2[0] = '\0';
 	ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
 				 NULL, NULL);
@@ -2190,6 +2248,7 @@
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
+	unsigned int i;
 
 	if (global_priv == NULL)
 		return NULL;
@@ -2268,6 +2327,17 @@
 		drv->in_interface_list = 1;
 	}
 
+	/*
+	 * Set the default link to be the first one, and set its address to that
+	 * of the interface.
+	 */
+	bss->flink = &bss->links[0];
+	bss->n_links = 1;
+	os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+		bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
 	return bss;
 
 failed:
@@ -2385,8 +2455,7 @@
 
 #ifdef CONFIG_PASN
 	/* register for PASN Authentication frames */
-	if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-	    nl80211_register_frame(bss, bss->nl_mgmt, type,
+	if (nl80211_register_frame(bss, bss->nl_mgmt, type,
 				   (u8 *) "\x07\x00", 2, false))
 		ret = -1;
 #endif /* CONFIG_PASN */
@@ -2936,21 +3005,50 @@
 }
 
 
-static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss)
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
+					 struct i802_link *link)
 {
 	struct nl_msg *msg;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
+	if (!link->beacon_set)
+		return 0;
+
 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
 		   drv->ifindex);
-	bss->beacon_set = 0;
-	bss->freq = 0;
+	link->beacon_set = 0;
+	link->freq = 0;
+
 	nl80211_put_wiphy_data_ap(bss);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+	if (!msg)
+		return -ENOBUFS;
+
+	if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: stop beaconing on link=%u",
+			   link->link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       link->link_id)) {
+			nlmsg_free(msg);
+			return -ENOBUFS;
+		}
+	}
+
 	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
+static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
+{
+	unsigned int i;
+
+	for (i = 0; i < bss->n_links; i++)
+		wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+}
+
+
 /**
  * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
  * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
@@ -3000,7 +3098,7 @@
 	nl80211_remove_monitor_interface(drv);
 
 	if (is_ap_interface(drv->nlmode))
-		wpa_driver_nl80211_del_beacon(bss);
+		wpa_driver_nl80211_del_beacon_all(bss);
 
 	if (drv->eapol_sock >= 0) {
 		eloop_unregister_read_sock(drv->eapol_sock);
@@ -3130,9 +3228,9 @@
 		return RSN_CIPHER_SUITE_WEP40;
 	case WPA_CIPHER_GTK_NOT_USED:
 		return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
@@ -3176,7 +3274,9 @@
 	__AKM(IEEE8021X_SHA256, 802_1X_SHA256);
 	__AKM(PSK_SHA256, PSK_SHA256);
 	__AKM(SAE, SAE);
+	__AKM(SAE_EXT_KEY, SAE_EXT_KEY);
 	__AKM(FT_SAE, FT_SAE);
+	__AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
 	__AKM(CCKM, CCKM);
 	__AKM(OSEN, OSEN);
 	__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
@@ -3383,6 +3483,7 @@
 	size_t key_len = params->key_len;
 	int vlan_id = params->vlan_id;
 	enum key_flag key_flag = params->key_flag;
+	int link_id = params->link_id;
 
 	/* Ignore for P2P Device */
 	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
@@ -3390,9 +3491,10 @@
 
 	ifindex = if_nametoindex(ifname);
 	wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
-		   "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x",
+		   "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
 		   __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
-		   (unsigned long) seq_len, (unsigned long) key_len, key_flag);
+		   (unsigned long) seq_len, (unsigned long) key_len, key_flag,
+		   link_id);
 
 	if (check_key_flag(key_flag)) {
 		wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
@@ -3526,6 +3628,12 @@
 			goto fail;
 	}
 
+	if (link_id != -1) {
+		wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
 		ret = 0;
@@ -3588,6 +3696,13 @@
 			goto fail;
 	}
 
+	if (link_id != -1) {
+		wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d",
+			   link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG,
@@ -3811,6 +3926,15 @@
 		}
 	}
 
+	if (params->mld && params->ap_mld_addr) {
+		drv->auth_mld = params->mld;
+		drv->auth_mld_link_id = params->mld_link_id;
+		os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN);
+	} else {
+		drv->auth_mld = false;
+		drv->auth_mld_link_id = -1;
+	}
+
 	os_free(drv->auth_data);
 	drv->auth_data = NULL;
 	drv->auth_data_len = 0;
@@ -3915,6 +4039,7 @@
 	os_memset(&p, 0, sizeof(p));
 	p.ifname = bss->ifname;
 	p.alg = WPA_ALG_WEP;
+	p.link_id = -1;
 	for (i = 0; i < 4; i++) {
 		if (!params->wep_key[i])
 			continue;
@@ -3973,6 +4098,17 @@
 			goto fail;
 	}
 
+	if (params->mld && params->ap_mld_addr) {
+		wpa_printf(MSG_DEBUG, "  * MLD: link_id=%u, MLD addr=" MACSTR,
+			   params->mld_link_id, MAC2STR(params->ap_mld_addr));
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       params->mld_link_id) ||
+		    nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+			    params->ap_mld_addr))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -4076,6 +4212,10 @@
 	params.ie_len = drv->auth_ie_len;
 	params.auth_data = drv->auth_data;
 	params.auth_data_len = drv->auth_data_len;
+	params.mld = drv->auth_mld;
+	params.mld_link_id = drv->auth_mld_link_id;
+	if (drv->auth_mld)
+		params.ap_mld_addr = drv->auth_ap_mld_addr;
 
 	for (i = 0; i < 4; i++) {
 		if (drv->auth_wep_key_len[i]) {
@@ -4135,10 +4275,10 @@
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
 		if (freq == 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
-				   bss->freq);
-			freq = bss->freq;
+				   bss->flink->freq);
+			freq = bss->flink->freq;
 		}
-		if ((int) freq == bss->freq)
+		if ((int) freq == bss->flink->freq)
 			wait_time = 0;
 		goto send_frame_cmd;
 	}
@@ -4200,14 +4340,14 @@
 	}
 	if (freq == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
-			   bss->freq);
-		freq = bss->freq;
+			   bss->flink->freq);
+		freq = bss->flink->freq;
 	}
 
 	if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
-			   freq, bss->freq);
+			   freq, bss->flink->freq);
 		return nl80211_send_monitor(drv, data, data_len, encrypt,
 					    noack);
 	}
@@ -4563,18 +4703,18 @@
 
 
 #ifdef CONFIG_SAE
-static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe)
+static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
 {
 	u8 sae_pwe;
 
 	wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
-	if (pwe == 0)
+	if (pwe == SAE_PWE_HUNT_AND_PECK)
 		sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
-	else if (pwe == 1)
+	else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
 		sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
-	else if (pwe == 2)
+	else if (pwe == SAE_PWE_BOTH)
 		sae_pwe = NL80211_SAE_PWE_BOTH;
-	else if (pwe == 3)
+	else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK)
 		return 0; /* special test mode */
 	else
 		return -1;
@@ -4617,6 +4757,7 @@
 
 
 #ifdef CONFIG_IEEE80211AX
+
 static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
 					  struct nl_msg *msg,
 					  struct wpa_driver_ap_params *params)
@@ -4646,6 +4787,60 @@
 	nla_nest_end(msg, attr);
 	return 0;
 }
+
+
+static int nl80211_mbssid(struct nl_msg *msg,
+			 struct wpa_driver_ap_params *params)
+{
+	struct nlattr *config, *elems;
+	int ifidx;
+
+	if (!params->mbssid_tx_iface)
+		return 0;
+
+	config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+	if (!config ||
+	    nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX,
+		       params->mbssid_index))
+		return -1;
+
+	if (params->mbssid_tx_iface) {
+		ifidx = if_nametoindex(params->mbssid_tx_iface);
+		if (ifidx <= 0 ||
+		    nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+				ifidx))
+			return -1;
+	}
+
+	if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
+		return -1;
+
+	nla_nest_end(msg, config);
+
+	if (params->mbssid_elem_count && params->mbssid_elem_len &&
+	    params->mbssid_elem_offset && *params->mbssid_elem_offset) {
+		u8 i, **offs = params->mbssid_elem_offset;
+
+		elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+		if (!elems)
+			return -1;
+
+		for (i = 0; i < params->mbssid_elem_count - 1; i++) {
+			if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+				return -1;
+		}
+
+		if (nla_put(msg, i + 1,
+			    *offs + params->mbssid_elem_len - offs[i],
+			    offs[i]))
+			return -1;
+
+		nla_nest_end(msg, elems);
+	}
+
+	return 0;
+}
+
 #endif /* CONFIG_IEEE80211AX */
 
 
@@ -4665,7 +4860,7 @@
 	struct wpa_driver_mesh_bss_params mesh_params;
 #endif /* CONFIG_MESH */
 
-	beacon_set = params->reenable ? 0 : bss->beacon_set;
+	beacon_set = params->reenable ? 0 : bss->flink->beacon_set;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
 		   beacon_set);
@@ -4775,7 +4970,8 @@
 	if (drv->device_ap_sme) {
 		u32 flags = 0;
 
-		if (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) {
+		if (params->key_mgmt_suites & (WPA_KEY_MGMT_SAE |
+					       WPA_KEY_MGMT_SAE_EXT_KEY)) {
 			/* Add the previously used flag attribute to support
 			 * older kernel versions and the newer flag bit for
 			 * newer kernels. */
@@ -4918,6 +5114,9 @@
 		nla_nest_end(msg, spr);
 	}
 
+	if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+		goto fail;
+
 	if (params->freq && params->freq->he_enabled) {
 		struct nlattr *bss_color;
 
@@ -4943,11 +5142,13 @@
 	if (params->unsol_bcast_probe_resp_interval &&
 	    nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
 		goto fail;
+
+	if (nl80211_mbssid(msg, params) < 0)
+		goto fail;
 #endif /* CONFIG_IEEE80211AX */
 
 #ifdef CONFIG_SAE
-	if (((params->key_mgmt_suites & WPA_KEY_MGMT_SAE) ||
-	     (params->key_mgmt_suites & WPA_KEY_MGMT_FT_SAE)) &&
+	if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
 	    nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
 		goto fail;
 #endif /* CONFIG_SAE */
@@ -4957,22 +5158,30 @@
 		goto fail;
 #endif /* CONFIG_FILS */
 
+	if (params->punct_bitmap) {
+		wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x",
+			   params->punct_bitmap);
+		if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+				params->punct_bitmap))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
 			   ret, strerror(-ret));
 	} else {
-		bss->beacon_set = 1;
+		bss->flink->beacon_set = 1;
 		nl80211_set_bss(bss, params->cts_protect, params->preamble,
 				params->short_slot_time, params->ht_opmode,
 				params->isolate, params->basic_rates);
 		nl80211_set_multicast_to_unicast(bss,
 						 params->multicast_to_unicast);
 		if (beacon_set && params->freq &&
-		    params->freq->bandwidth != bss->bandwidth) {
+		    params->freq->bandwidth != bss->flink->bandwidth) {
 			wpa_printf(MSG_DEBUG,
 				   "nl80211: Update BSS %s bandwidth: %d -> %d",
-				   bss->ifname, bss->bandwidth,
+				   bss->ifname, bss->flink->bandwidth,
 				   params->freq->bandwidth);
 			ret = nl80211_set_channel(bss, params->freq, 1);
 			if (ret) {
@@ -4982,7 +5191,7 @@
 			} else {
 				wpa_printf(MSG_DEBUG,
 					   "nl80211: Frequency set succeeded for ht2040 coex");
-				bss->bandwidth = params->freq->bandwidth;
+				bss->flink->bandwidth = params->freq->bandwidth;
 			}
 		} else if (!beacon_set && params->freq) {
 			/*
@@ -4990,7 +5199,7 @@
 			 * mode only at the point when beaconing is started, so
 			 * set the initial value here.
 			 */
-			bss->bandwidth = params->freq->bandwidth;
+			bss->flink->bandwidth = params->freq->bandwidth;
 		}
 	}
 
@@ -5055,6 +5264,9 @@
 		case 160:
 			cw = NL80211_CHAN_WIDTH_160;
 			break;
+		case 320:
+			cw = NL80211_CHAN_WIDTH_320;
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -5107,8 +5319,9 @@
 				NL80211_CHAN_NO_HT))
 			return -ENOBUFS;
 	}
-	if (freq->radar_background)
-		nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND);
+	if (freq->radar_background &&
+	    nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND))
+		return -ENOBUFS;
 
 	return 0;
 }
@@ -5136,7 +5349,7 @@
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret == 0) {
-		bss->freq = freq->freq;
+		bss->flink->freq = freq->freq;
 		return 0;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
@@ -5203,16 +5416,29 @@
 	struct nl_msg *msg;
 	struct nl80211_sta_flag_update upd;
 	int ret = -ENOBUFS;
+	u8 cmd;
+	const char *cmd_string;
 
 	if ((params->flags & WPA_STA_TDLS_PEER) &&
 	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
 		return -EOPNOTSUPP;
 
+	if (params->mld_link_sta) {
+		cmd = params->set ? NL80211_CMD_MODIFY_LINK_STA :
+			NL80211_CMD_ADD_LINK_STA;
+		cmd_string = params->set ? "NL80211_CMD_MODIFY_LINK_STA" :
+			"NL80211_CMD_ADD_LINK_STA";
+	} else {
+		cmd = params->set ? NL80211_CMD_SET_STATION :
+			NL80211_CMD_NEW_STATION;
+		cmd_string = params->set ? "NL80211_CMD_SET_STATION" :
+			"NL80211_CMD_NEW_STATION";
+	}
+
 	wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
-		   params->set ? "Set" : "Add", MAC2STR(params->addr));
-	msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION :
-			      NL80211_CMD_NEW_STATION);
-	if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+		   cmd_string, MAC2STR(params->addr));
+	msg = nl80211_bss_msg(bss, 0, cmd);
+	if (!msg)
 		goto fail;
 
 	/*
@@ -5430,12 +5656,43 @@
 		nla_nest_end(msg, wme);
 	}
 
+	/* In case we are an AP MLD need to always specify the link ID */
+	if (params->mld_link_id >= 0) {
+		wpa_printf(MSG_DEBUG, "  * mld_link_id=%d",
+			   params->mld_link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       params->mld_link_id))
+			goto fail;
+
+		/*
+		 * If the link address is specified the station is a non-AP MLD
+		 * and thus need to provide the MLD address as the station
+		 * address, and the non-AP MLD link address as the link address.
+		 */
+		if (params->mld_link_addr) {
+			wpa_printf(MSG_DEBUG, "  * mld_link_addr=" MACSTR,
+				   MAC2STR(params->mld_link_addr));
+
+			if (nla_put(msg, NL80211_ATTR_MLD_ADDR,
+				    ETH_ALEN, params->addr) ||
+			    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->mld_link_addr))
+				goto fail;
+		} else {
+			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->addr))
+				goto fail;
+		}
+	} else {
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret)
-		wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
-			   "result: %d (%s)", params->set ? "SET" : "NEW", ret,
-			   strerror(-ret));
+		wpa_printf(MSG_DEBUG, "nl80211: %s result: %d (%s)",
+			   cmd_string, ret, strerror(-ret));
 	if (ret == -EEXIST)
 		ret = 0;
 fail:
@@ -5764,7 +6021,7 @@
 		nl80211_mgmt_unsubscribe(bss, "AP teardown");
 
 	nl80211_put_wiphy_data_ap(bss);
-	bss->beacon_set = 0;
+	bss->flink->beacon_set = 0;
 }
 
 
@@ -6100,6 +6357,12 @@
 	}
 #endif /* CONFIG_HE_OVERRIDES */
 
+	if (params->disable_eht) {
+		wpa_printf(MSG_DEBUG, "  * EHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT))
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -6239,14 +6502,100 @@
 }
 
 
+static unsigned int num_bits_set(u32 val)
+{
+	unsigned int c;
+
+	for (c = 0; val; c++)
+		val &= val - 1;
+
+	return c;
+}
+
+
 static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 				  struct wpa_driver_associate_params *params,
 				  struct nl_msg *msg)
 {
+	if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
+		struct wpa_driver_mld_params *mld_params = &params->mld_params;
+		struct nlattr *links, *attr;
+		int i;
+		u8 link_id;
+
+		wpa_printf(MSG_DEBUG, "  * MLD: MLD addr=" MACSTR,
+			   MAC2STR(mld_params->mld_addr));
+
+		if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+			    mld_params->mld_addr) ||
+		    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       mld_params->assoc_link_id))
+			return -1;
+
+		links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+		if (!links)
+			return -1;
+
+		attr = nla_nest_start(msg, 0);
+		if (!attr)
+			return -1;
+
+		/* First add the association link ID */
+		link_id = mld_params->assoc_link_id;
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+		    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+			    mld_params->mld_links[link_id].bssid) ||
+		    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+				mld_params->mld_links[link_id].freq))
+			return -1;
+
+		os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+			  mld_params->mld_links[link_id].bssid, ETH_ALEN);
+
+		nla_nest_end(msg, attr);
+
+		for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
+		     link_id++) {
+			if (!(mld_params->valid_links & BIT(link_id)) ||
+			    link_id == mld_params->assoc_link_id)
+				continue;
+
+			attr = nla_nest_start(msg, i);
+			if (!attr)
+				return -1;
+
+			if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+				       link_id) ||
+			    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    mld_params->mld_links[link_id].bssid) ||
+			    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+					mld_params->mld_links[link_id].freq) ||
+			    (mld_params->mld_links[link_id].ies &&
+			     mld_params->mld_links[i].ies_len &&
+			     nla_put(msg, NL80211_ATTR_IE,
+				     mld_params->mld_links[link_id].ies_len,
+				     mld_params->mld_links[link_id].ies)))
+				return -1;
+
+			os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+				  mld_params->mld_links[link_id].bssid,
+				  ETH_ALEN);
+			nla_nest_end(msg, attr);
+			i++;
+		}
+
+		nla_nest_end(msg, links);
+
+		os_memcpy(drv->sta_mlo_info.ap_mld_addr,
+			  params->mld_params.mld_addr, ETH_ALEN);
+		drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id;
+		drv->sta_mlo_info.req_links = mld_params->valid_links;
+	}
+
 	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
 		return -1;
 
-	if (params->bssid) {
+	if (params->bssid && !params->mld_params.mld_addr) {
 		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
 			   MAC2STR(params->bssid));
 		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
@@ -6261,7 +6610,7 @@
 			return -1;
 	}
 
-	if (params->freq.freq) {
+	if (params->freq.freq && !params->mld_params.mld_addr) {
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
 		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
 				params->freq.freq))
@@ -6358,7 +6707,9 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE_EXT_KEY ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE_EXT_KEY ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
@@ -6368,71 +6719,111 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
-		int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		u32 *mgmt;
+		unsigned int akm_count = 1, i;
+
+		/*
+		 * Make sure the driver has capability to handle default AKM in
+		 * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts.
+		 */
+		if (drv->capa.max_num_akms <=
+		    num_bits_set(params->allowed_key_mgmts)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)",
+				   drv->capa.max_num_akms,
+				   num_bits_set(params->allowed_key_mgmts));
+			return -1;
+		}
+
+		mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms);
+		if (!mgmt)
+			return -1;
+
+		mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 
 		switch (params->key_mgmt_suite) {
 		case WPA_KEY_MGMT_CCKM:
-			mgmt = RSN_AUTH_KEY_MGMT_CCKM;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X:
-			mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 			break;
 		case WPA_KEY_MGMT_FT_IEEE8021X:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X;
 			break;
 		case WPA_KEY_MGMT_FT_PSK:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SHA256:
-			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 			break;
 		case WPA_KEY_MGMT_PSK_SHA256:
-			mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
 			break;
 		case WPA_KEY_MGMT_OSEN:
-			mgmt = RSN_AUTH_KEY_MGMT_OSEN;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
 			break;
 		case WPA_KEY_MGMT_SAE:
-			mgmt = RSN_AUTH_KEY_MGMT_SAE;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
+			break;
+		case WPA_KEY_MGMT_SAE_EXT_KEY:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
 			break;
 		case WPA_KEY_MGMT_FT_SAE:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_SAE;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE;
+			break;
+		case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
-			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
-			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 			break;
 		case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
 			break;
 		case WPA_KEY_MGMT_FILS_SHA256:
-			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256;
 			break;
 		case WPA_KEY_MGMT_FILS_SHA384:
-			mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384;
 			break;
 		case WPA_KEY_MGMT_FT_FILS_SHA256:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
 			break;
 		case WPA_KEY_MGMT_FT_FILS_SHA384:
-			mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
 			break;
 		case WPA_KEY_MGMT_OWE:
-			mgmt = RSN_AUTH_KEY_MGMT_OWE;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_OWE;
 			break;
 		case WPA_KEY_MGMT_DPP:
-			mgmt = RSN_AUTH_KEY_MGMT_DPP;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
 			break;
 		case WPA_KEY_MGMT_PSK:
 		default:
-			mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+			mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 			break;
 		}
-		wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
-		if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
+
+		if (drv->capa.max_num_akms > 1) {
+			akm_count += wpa_key_mgmt_to_suites(
+				params->allowed_key_mgmts, &mgmt[1],
+				drv->capa.max_num_akms - 1);
+		}
+
+		for (i = 0; i < akm_count; i++)
+			wpa_printf(MSG_DEBUG, "  * akm[%d]=0x%x", i, mgmt[i]);
+
+		if (nla_put(msg, NL80211_ATTR_AKM_SUITES,
+			    akm_count * sizeof(u32), mgmt)) {
+			os_free(mgmt);
 			return -1;
+		}
+
+		os_free(mgmt);
 	}
 
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
@@ -6513,17 +6904,21 @@
 	    nl80211_put_fils_connect_params(drv, params, msg) != 0)
 		return -1;
 
-	if ((
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-	     (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+	if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+	     (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) &&
 #else
-	     params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
 	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
 	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
 		return -1;
 
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
+		return -1;
+
 	return 0;
 }
 
@@ -6540,9 +6935,8 @@
 
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	if (params->req_key_mgmt_offload && params->psk &&
-	    (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
-	     params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
-	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+	    (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
 		wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
 		ret = issue_key_mgmt_set_key(drv, params->psk, 32);
 		if (ret)
@@ -6569,13 +6963,13 @@
 		goto fail;
 
 #ifdef CONFIG_SAE
-	if ((
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-	     (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+	if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+	     (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) &&
 #else
-	     params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
 	    nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
 		goto fail;
 #endif /* CONFIG_SAE */
@@ -6683,13 +7077,13 @@
 
 		if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
 			return -1;
-		if (
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-		    (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+		if ((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+		    (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE))
 #else
-		    params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+		if (wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+		    wpa_key_mgmt_sae(params->allowed_key_mgmts))
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-		    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)
 			bss->use_nl_connect = 1;
 		else
 			bss->use_nl_connect = 0;
@@ -7003,14 +7397,17 @@
 	struct nl_msg *msg;
 	struct nl80211_sta_flag_update upd;
 	int ret;
+	const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+		drv->sta_mlo_info.ap_mld_addr : drv->bssid;
 
-	if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+	if (!drv->associated && is_zero_ether_addr(connected_addr) &&
+	    !authorized) {
 		wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
 		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
-		   MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
+		   MACSTR, authorized ? "" : "un", MAC2STR(connected_addr));
 
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -7018,7 +7415,7 @@
 		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
 
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
-	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) ||
 	    nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -7211,16 +7608,26 @@
 		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
 		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
 		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
-		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
-		[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8},
 		[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 },
+		[NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
-		[NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
 	};
 	struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
 	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -7230,6 +7637,10 @@
 		[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
 		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
 		[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_GI] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_DCM] = { .type = NLA_U8 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -7272,34 +7683,62 @@
 			nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
 		data->bytes_64bit = 1;
 	}
+	if (stats[NL80211_STA_INFO_SIGNAL])
+		data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
 	if (stats[NL80211_STA_INFO_RX_PACKETS])
 		data->rx_packets =
 			nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
 	if (stats[NL80211_STA_INFO_TX_PACKETS])
 		data->tx_packets =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
-	if (stats[NL80211_STA_INFO_RX_DURATION])
-		data->rx_airtime =
-			nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
-	if (stats[NL80211_STA_INFO_TX_DURATION])
-		data->tx_airtime =
-			nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
+	if (stats[NL80211_STA_INFO_TX_RETRIES])
+		data->tx_retry_count =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]);
 	if (stats[NL80211_STA_INFO_TX_FAILED])
 		data->tx_retry_failed =
 			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
-	if (stats[NL80211_STA_INFO_SIGNAL])
-		data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
-	if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
-		data->last_ack_rssi =
-			nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
-		data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
-	}
-
+	if (stats[NL80211_STA_INFO_SIGNAL_AVG])
+		data->avg_signal =
+			(s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
 	if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
 		data->connected_sec =
 			nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
 		data->flags |= STA_DRV_DATA_CONN_TIME;
 	}
+	if (stats[NL80211_STA_INFO_BEACON_LOSS])
+		data->beacon_loss_count =
+			nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]);
+	if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+		data->expected_throughput =
+			nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+	if (stats[NL80211_STA_INFO_RX_DROP_MISC])
+		data->rx_drop_misc =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]);
+	if (stats[NL80211_STA_INFO_BEACON_RX])
+		data->beacons_count =
+			nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]);
+	if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+		data->avg_beacon_signal =
+			(s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+	if (stats[NL80211_STA_INFO_RX_DURATION])
+		data->rx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+	if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+		data->last_ack_rssi =
+			nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+		data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+	}
+	if (stats[NL80211_STA_INFO_ACK_SIGNAL_AVG])
+		data->avg_ack_signal =
+			nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]);
+	if (stats[NL80211_STA_INFO_RX_MPDUS])
+		data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]);
+	if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT])
+		data->fcs_error_count =
+			nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]);
+	if (stats[NL80211_STA_INFO_TX_DURATION])
+		data->tx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
 
 	if (stats[NL80211_STA_INFO_TX_BITRATE] &&
 	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
@@ -7312,6 +7751,10 @@
 			data->current_tx_rate =
 				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
 
+		/* Convert from 100 kbps to kbps; it's a more convenient unit.
+		 * It's also safe up until ~1Tbps. */
+		data->current_tx_rate = data->current_tx_rate * 100;
+
 		if (rate[NL80211_RATE_INFO_MCS]) {
 			data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
 			data->flags |= STA_DRV_DATA_TX_MCS;
@@ -7321,13 +7764,44 @@
 				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
 			data->flags |= STA_DRV_DATA_TX_VHT_MCS;
 		}
-		if (rate[NL80211_RATE_INFO_SHORT_GI])
+		if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+			data->tx_guard_interval = GUARD_INTERVAL_0_4;
 			data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+		}
 		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
 			data->tx_vht_nss =
 				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
 			data->flags |= STA_DRV_DATA_TX_VHT_NSS;
 		}
+		if (rate[NL80211_RATE_INFO_HE_MCS]) {
+			data->tx_hemcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+			data->flags |= STA_DRV_DATA_TX_HE_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_NSS]) {
+			data->tx_he_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+			data->flags |= STA_DRV_DATA_TX_HE_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_GI]) {
+			switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+			case NL80211_RATE_INFO_HE_GI_0_8:
+				data->tx_guard_interval = GUARD_INTERVAL_0_8;
+				break;
+			case NL80211_RATE_INFO_HE_GI_1_6:
+				data->tx_guard_interval = GUARD_INTERVAL_1_6;
+				break;
+			case NL80211_RATE_INFO_HE_GI_3_2:
+				data->tx_guard_interval = GUARD_INTERVAL_3_2;
+				break;
+			}
+			data->flags |= STA_DRV_DATA_TX_HE_GI;
+		}
+		if (rate[NL80211_RATE_INFO_HE_DCM]) {
+			data->tx_dcm =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+			data->flags |= STA_DRV_DATA_TX_HE_DCM;
+		}
 	}
 
 	if (stats[NL80211_STA_INFO_RX_BITRATE] &&
@@ -7341,9 +7815,12 @@
 			data->current_rx_rate =
 				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
 
+		/* Convert from 100 kbps to kbps; it's a more convenient unit.
+		 * It's also safe up until ~1Tbps. */
+		data->current_rx_rate = data->current_rx_rate * 100;
+
 		if (rate[NL80211_RATE_INFO_MCS]) {
-			data->rx_mcs =
-				nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->rx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
 			data->flags |= STA_DRV_DATA_RX_MCS;
 		}
 		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
@@ -7351,13 +7828,44 @@
 				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
 			data->flags |= STA_DRV_DATA_RX_VHT_MCS;
 		}
-		if (rate[NL80211_RATE_INFO_SHORT_GI])
+		if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+			data->rx_guard_interval = GUARD_INTERVAL_0_4;
 			data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+		}
 		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
 			data->rx_vht_nss =
 				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
 			data->flags |= STA_DRV_DATA_RX_VHT_NSS;
 		}
+		if (rate[NL80211_RATE_INFO_HE_MCS]) {
+			data->rx_hemcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+			data->flags |= STA_DRV_DATA_RX_HE_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_NSS]) {
+			data->rx_he_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+			data->flags |= STA_DRV_DATA_RX_HE_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_GI]) {
+			switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+			case NL80211_RATE_INFO_HE_GI_0_8:
+				data->rx_guard_interval = GUARD_INTERVAL_0_8;
+				break;
+			case NL80211_RATE_INFO_HE_GI_1_6:
+				data->rx_guard_interval = GUARD_INTERVAL_1_6;
+				break;
+			case NL80211_RATE_INFO_HE_GI_3_2:
+				data->rx_guard_interval = GUARD_INTERVAL_3_2;
+				break;
+			}
+			data->flags |= STA_DRV_DATA_RX_HE_GI;
+		}
+		if (rate[NL80211_RATE_INFO_HE_DCM]) {
+			data->rx_dcm =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+			data->flags |= STA_DRV_DATA_RX_HE_DCM;
+		}
 	}
 
 	if (stats[NL80211_STA_INFO_TID_STATS])
@@ -7366,6 +7874,26 @@
 	return NL_SKIP;
 }
 
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    const u8 *bssid,
+			    struct hostap_sta_driver_data *data)
+{
+	struct nl_msg *msg;
+
+	data->signal = -WPA_INVALID_NOISE;
+	data->current_tx_rate = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+}
+
+
 static int i802_read_sta_data(struct i802_bss *bss,
 			      struct hostap_sta_driver_data *data,
 			      const u8 *addr)
@@ -7513,7 +8041,7 @@
 	struct ieee80211_mgmt mgmt;
 	u8 channel;
 
-	if (ieee80211_freq_to_chan(bss->freq, &channel) ==
+	if (ieee80211_freq_to_chan(bss->flink->freq, &channel) ==
 	    HOSTAPD_MODE_IEEE80211AD) {
 		/* Deauthentication is not used in DMG/IEEE 802.11ad;
 		 * disassociate the STA instead. */
@@ -8106,7 +8634,7 @@
 	if (!addr &&
 	    (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
 	     type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
-	     type == WPA_IF_STATION)) {
+	     type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) {
 		/* Enforce unique address */
 		u8 new_addr[ETH_ALEN];
 
@@ -8136,12 +8664,18 @@
 
 	if (type == WPA_IF_AP_BSS && setup_ap) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+		unsigned int i;
+
 		if (new_bss == NULL) {
 			if (added)
 				nl80211_remove_iface(drv, ifidx);
 			return -1;
 		}
 
+		/* Initialize here before any failure path */
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+			new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
 		if (bridge &&
 		    i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
 			wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
@@ -8165,7 +8699,11 @@
 		new_bss->ifindex = ifidx;
 		new_bss->drv = drv;
 		new_bss->next = drv->first_bss->next;
-		new_bss->freq = drv->first_bss->freq;
+		new_bss->flink = &new_bss->links[0];
+		new_bss->n_links = 1;
+		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+
+		new_bss->flink->freq = drv->first_bss->flink->freq;
 		new_bss->ctx = bss_ctx;
 		new_bss->added_if = added;
 		drv->first_bss->next = new_bss;
@@ -8257,7 +8795,7 @@
 		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
 		nl80211_teardown_ap(bss);
 		if (!bss->added_if && !drv->first_bss->next)
-			wpa_driver_nl80211_del_beacon(bss);
+			wpa_driver_nl80211_del_beacon_all(bss);
 		nl80211_destroy_bss(bss);
 		if (!bss->added_if)
 			i802_set_iface_flags(bss, 0);
@@ -8372,8 +8910,8 @@
 	struct ieee80211_hdr *hdr;
 	int offchanok = 1;
 
-	if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq &&
-	    bss->beacon_set)
+	if (is_ap_interface(drv->nlmode) && (int) freq == bss->flink->freq &&
+	    bss->flink->beacon_set)
 		offchanok = 0;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
@@ -8409,7 +8947,7 @@
 		modes = nl80211_get_hw_feature_data(bss, &num_modes,
 						    &flags, &dfs_domain);
 		if (dfs_domain != HOSTAPD_DFS_REGION_ETSI &&
-		    ieee80211_is_dfs(bss->freq, modes, num_modes))
+		    ieee80211_is_dfs(bss->flink->freq, modes, num_modes))
 			offchanok = 0;
 		if (modes) {
 			for (i = 0; i < num_modes; i++) {
@@ -8423,7 +8961,7 @@
 
 	if (is_ap_interface(drv->nlmode) &&
 	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
-	     (int) freq == bss->freq || drv->device_ap_sme ||
+	     (int) freq == bss->flink->freq || drv->device_ap_sme ||
 	     !drv->use_monitor))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, offchanok,
@@ -8653,13 +9191,71 @@
 }
 
 
+static void nl80211_remove_links(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u8 link_id;
+
+	while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
+		struct i802_link *link = &bss->links[0];
+
+		wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
+			   link->link_id);
+
+		wpa_driver_nl80211_del_beacon(bss, link);
+
+		link_id = link->link_id;
+
+		/* First remove the link locally */
+		if (bss->n_links == 1) {
+			bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
+			os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+		} else {
+			struct i802_link *other = &bss->links[bss->n_links - 1];
+
+			os_memcpy(link, other, sizeof(*link));
+			other->link_id = NL80211_DRV_LINK_ID_NA;
+			os_memset(other->addr, 0, ETH_ALEN);
+
+			bss->n_links--;
+		}
+
+		/* Remove the link from the kernel */
+		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
+		if (!msg ||
+		    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+			nlmsg_free(msg);
+			wpa_printf(MSG_ERROR,
+				   "nl80211: remove link (%d) failed",
+				   link_id);
+			return;
+		}
+
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: remove link (%d) failed. ret=%d (%s)",
+				   link_id, ret, strerror(-ret));
+			return;
+		}
+	}
+}
+
+
 static int wpa_driver_nl80211_deinit_ap(void *priv)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
-	wpa_driver_nl80211_del_beacon(bss);
+
+	/* Stop beaconing */
+	wpa_driver_nl80211_del_beacon(bss, bss->flink);
+
+	nl80211_remove_links(bss);
 
 	/*
 	 * If the P2P GO interface was dynamically added, then it is
@@ -8676,9 +9272,12 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
-	wpa_driver_nl80211_del_beacon(bss);
+
+	wpa_driver_nl80211_del_beacon_all(bss);
+
 	return 0;
 }
 
@@ -8782,12 +9381,12 @@
 	int res;
 
 	os_memset(si, 0, sizeof(*si));
-	res = nl80211_get_link_signal(drv, si);
+	res = nl80211_get_link_signal(drv, drv->bssid, &si->data);
 	if (res) {
 		if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
 		    drv->nlmode != NL80211_IFTYPE_MESH_POINT)
 			return res;
-		si->current_signal = 0;
+		si->data.signal = 0;
 	}
 
 	res = nl80211_get_channel_width(drv, si);
@@ -8798,6 +9397,163 @@
 }
 
 
+static int get_links_noise(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_mlo_signal_info *mlo_sig = arg;
+	int i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to parse nested attributes");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_sig->valid_links & BIT(i)))
+			continue;
+
+		if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+		    mlo_sig->links[i].frequency)
+			continue;
+
+		mlo_sig->links[i].current_noise =
+			(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		break;
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
+				   struct wpa_mlo_signal_info *mlo_sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
+				  NULL, NULL);
+}
+
+
+static int get_links_channel_width(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_mlo_signal_info *mlo_sig = arg;
+	struct nlattr *link;
+	int rem_links;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_MLO_LINKS])
+		return NL_SKIP;
+
+	nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) {
+		struct nlattr *tb2[NL80211_ATTR_MAX + 1];
+		int link_id;
+
+		nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+			  NULL);
+
+		if (!tb2[NL80211_ATTR_MLO_LINK_ID])
+			continue;
+
+		link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		if (!tb2[NL80211_ATTR_CHANNEL_WIDTH])
+			continue;
+		mlo_sig->links[link_id].chanwidth = convert2width(
+			nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH]));
+		if (tb2[NL80211_ATTR_CENTER_FREQ1])
+			mlo_sig->links[link_id].center_frq1 =
+				nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]);
+		if (tb2[NL80211_ATTR_CENTER_FREQ2])
+			mlo_sig->links[link_id].center_frq2 =
+				nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
+					   struct wpa_mlo_signal_info *mlo_sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
+				  NULL, NULL);
+}
+
+
+static int nl80211_mlo_signal_poll(void *priv,
+				   struct wpa_mlo_signal_info *mlo_si)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+	int i;
+
+	if (drv->nlmode != NL80211_IFTYPE_STATION ||
+	    !drv->sta_mlo_info.valid_links)
+		return -1;
+
+	os_memset(mlo_si, 0, sizeof(*mlo_si));
+	mlo_si->valid_links = drv->sta_mlo_info.valid_links;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_si->valid_links & BIT(i)))
+			continue;
+
+		res = nl80211_get_link_signal(drv,
+					      drv->sta_mlo_info.links[i].bssid,
+					      &mlo_si->links[i].data);
+		if (res != 0)
+			return res;
+
+		mlo_si->links[i].center_frq1 = -1;
+		mlo_si->links[i].center_frq2 = -1;
+		mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN;
+		mlo_si->links[i].current_noise = WPA_INVALID_NOISE;
+		mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
+	}
+
+	res = nl80211_get_links_channel_width(drv, mlo_si);
+	if (res != 0)
+		return res;
+
+	return nl80211_get_links_noise(drv, mlo_si);
+}
+
+
 static int nl80211_set_param(void *priv, const char *param)
 {
 	struct i802_bss *bss = priv;
@@ -8947,10 +9703,14 @@
 
 
 static int nl80211_pmkid(struct i802_bss *bss, int cmd,
-			 struct wpa_pmkid_params *params)
+			 struct wpa_pmkid_params *params, bool skip_pmk)
 {
 	struct nl_msg *msg;
-	const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
+
+	if (cmd == NL80211_CMD_SET_PMKSA)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu",
+			   skip_pmk ? "true" : "false", params->pmk_len);
 
 	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
 	    (params->pmkid &&
@@ -8969,7 +9729,7 @@
 	     nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
 			params->pmk_reauth_threshold)) ||
 	    (cmd != NL80211_CMD_DEL_PMKSA &&
-	     params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+	     params->pmk_len && !skip_pmk &&
 	     nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
 		nl80211_nlmsg_clear(msg);
 		nlmsg_free(msg);
@@ -8983,6 +9743,9 @@
 static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
 {
 	struct i802_bss *bss = priv;
+	const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */
+	const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */
+	bool skip_pmk = params->pmk_len > PMK_MAX_LEN;
 	int ret;
 
 	if (params->bssid)
@@ -8995,7 +9758,15 @@
 			   wpa_ssid_txt(params->ssid, params->ssid_len));
 	}
 
-	ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
+	ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk);
+	/*
+	 * Try again by skipping PMK if the first attempt failed with ERANGE
+	 * error, PMK was not skipped, and PMK length is greater than the
+	 * legacy kernel maximum allowed limit.
+	 */
+	if (ret == -ERANGE && !skip_pmk &&
+	    params->pmk_len > LEGACY_PMK_MAX_LEN)
+		ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true);
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
@@ -9021,7 +9792,7 @@
 			   wpa_ssid_txt(params->ssid, params->ssid_len));
 	}
 
-	ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
+	ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true);
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
@@ -9808,8 +10579,8 @@
 			  bss->ifname,
 			  bss->brname,
 			  MAC2STR(bss->addr),
-			  bss->freq,
-			  bss->beacon_set ? "beacon_set=1\n" : "",
+			  bss->flink->freq,
+			  bss->flink->beacon_set ? "beacon_set=1\n" : "",
 			  bss->added_if_into_bridge ?
 			  "added_if_into_bridge=1\n" : "",
 			  bss->already_in_bridge ? "already_in_bridge=1\n" : "",
@@ -9886,6 +10657,34 @@
 		return pos - buf;
 	pos += res;
 
+	if (drv->sta_mlo_info.valid_links) {
+		int i;
+		struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+		res = os_snprintf(pos, end - pos,
+				  "ap_mld_addr=" MACSTR "\n",
+				   MAC2STR(mlo->ap_mld_addr));
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo->valid_links & BIT(i)))
+				continue;
+
+			res = os_snprintf(pos, end - pos,
+					  "link_addr[%u]=" MACSTR "\n"
+					  "link_bssid[%u]=" MACSTR "\n"
+					  "link_freq[%u]=%u\n",
+					  i, MAC2STR(mlo->links[i].addr),
+					  i, MAC2STR(mlo->links[i].bssid),
+					  i, mlo->links[i].freq);
+			if (os_snprintf_error(end - pos, res))
+				return pos - buf;
+			pos += res;
+		}
+	}
+
 	if (drv->has_capability) {
 		res = os_snprintf(pos, end - pos,
 				  "capa.key_mgmt=0x%x\n"
@@ -9909,7 +10708,9 @@
 				  "capa.max_conc_chan_5_0=%u\n"
 				  "capa.max_sched_scan_plans=%u\n"
 				  "capa.max_sched_scan_plan_interval=%u\n"
-				  "capa.max_sched_scan_plan_iterations=%u\n",
+				  "capa.max_sched_scan_plan_iterations=%u\n"
+				  "capa.mbssid_max_interfaces=%u\n"
+				  "capa.ema_max_periodicity=%u\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
@@ -9931,7 +10732,9 @@
 				  drv->capa.max_conc_chan_5_0,
 				  drv->capa.max_sched_scan_plans,
 				  drv->capa.max_sched_scan_plan_interval,
-				  drv->capa.max_sched_scan_plan_iterations);
+				  drv->capa.max_sched_scan_plan_iterations,
+				  drv->capa.mbssid_max_interfaces,
+				  drv->capa.ema_max_periodicity);
 		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
@@ -9995,7 +10798,7 @@
 	int i;
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d%s%s%s)",
+		   "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x%s%s%s)",
 		   settings->cs_count, settings->block_tx,
 		   settings->freq_params.freq,
 		   settings->freq_params.channel,
@@ -10003,6 +10806,7 @@
 		   settings->freq_params.bandwidth,
 		   settings->freq_params.center_freq1,
 		   settings->freq_params.center_freq2,
+		   settings->punct_bitmap,
 		   settings->freq_params.ht_enabled ? " ht" : "",
 		   settings->freq_params.vht_enabled ? " vht" : "",
 		   settings->freq_params.he_enabled ? " he" : "");
@@ -10073,7 +10877,10 @@
 			settings->cs_count) ||
 	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
 	    (settings->block_tx &&
-	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)))
+	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
+	    (settings->punct_bitmap &&
+	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+			 settings->punct_bitmap)))
 		goto error;
 
 	/* beacon_after params */
@@ -10727,6 +11534,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
 		bss->ifname, MAC2STR(addr));
 	drv->addr_changed = new_addr;
+	os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
 	os_memcpy(bss->addr, addr, ETH_ALEN);
 
 	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
@@ -10871,7 +11679,7 @@
 		goto fail;
 	}
 	ret = 0;
-	drv->assoc_freq = bss->freq = params->freq.freq;
+	drv->assoc_freq = bss->flink->freq = params->freq.freq;
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
 
 fail:
@@ -10927,7 +11735,7 @@
 	} else {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: mesh leave request send successfully");
-		drv->first_bss->freq = 0;
+		drv->first_bss->flink->freq = 0;
 	}
 
 	if (drv->start_mode_sta &&
@@ -11168,6 +11976,8 @@
 		return "proxyarp_wifi";
 	case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
 		return "hairpin_mode";
+	case DRV_BR_PORT_ATTR_MCAST2UCAST:
+		return "multicast_to_unicast";
 	}
 
 	return NULL;
@@ -11441,9 +12251,33 @@
 
 struct nl80211_pcl {
 	unsigned int num;
-	unsigned int *freq_list;
+	struct weighted_pcl *freq_list;
 };
 
+static void get_pcl_attr_values(struct weighted_pcl *wpcl, struct nlattr *nl[])
+{
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ])
+		wpcl->freq = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]);
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT])
+		wpcl->weight = nla_get_u8(nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]);
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]) {
+		u32 flags = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]);
+
+		wpcl->flag = 0;
+		if (flags & BIT(0))
+			wpcl->flag |= WEIGHTED_PCL_GO;
+		if (flags & BIT(1))
+			wpcl->flag |= WEIGHTED_PCL_CLI;
+		if (flags & BIT(2))
+			wpcl->flag |= WEIGHTED_PCL_MUST_CONSIDER;
+		if (flags & BIT(3))
+			wpcl->flag |= WEIGHTED_PCL_EXCLUDE;
+	} else {
+		wpcl->flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+	}
+}
+
+
 static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -11452,6 +12286,7 @@
 	struct nlattr *nl_vend, *attr;
 	enum qca_iface_type iface_type;
 	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *nl_pcl[QCA_WLAN_VENDOR_ATTR_PCL_MAX + 1];
 	unsigned int num, max_num;
 	u32 *freqs;
 
@@ -11477,26 +12312,69 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
 		   iface_type);
 
-	attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
-	if (!attr) {
+	attr = tb_vendor[
+		QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL];
+	if (attr) {
+		int rem;
+		struct nlattr *wpcl = attr;
+		unsigned int i;
+
+		num = 0;
+		nla_for_each_nested(attr, wpcl, rem) {
+			if (num == param->num)
+				break; /* not enough room for all entries */
+			if (nla_parse(nl_pcl, QCA_WLAN_VENDOR_ATTR_PCL_MAX,
+				      nla_data(attr), nla_len(attr), NULL)) {
+				wpa_printf(MSG_ERROR,
+					   "nl80211: Failed to parse PCL info");
+				param->num = 0;
+				return NL_SKIP;
+			}
+			get_pcl_attr_values(&param->freq_list[num], nl_pcl);
+			num++;
+		}
+		param->num = num;
+
+		/* Sort frequencies based on their weight */
+		for (i = 0; i < num; i++) {
+			unsigned int j;
+
+			for (j = i + 1; j < num; j++) {
+				if (param->freq_list[i].weight <
+				    param->freq_list[j].weight) {
+					struct weighted_pcl tmp;
+
+					tmp = param->freq_list[i];
+					param->freq_list[i] =
+						param->freq_list[j];
+					param->freq_list[j] = tmp;
+				}
+			}
+		}
+	} else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not provide weighted PCL; use the non-weighted variant");
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+		/*
+		 * param->num has the maximum number of entries for which there
+		 * is room in the freq_list provided by the caller.
+		 */
+		freqs = nla_data(attr);
+		max_num = nla_len(attr) / sizeof(u32);
+		if (max_num > param->num)
+			max_num = param->num;
+		for (num = 0; num < max_num; num++) {
+			param->freq_list[num].freq = freqs[num];
+			param->freq_list[num].flag =
+				WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+		}
+		param->num = num;
+	} else {
 		wpa_printf(MSG_ERROR,
 			   "nl80211: preferred_freq_list couldn't be found");
 		param->num = 0;
 		return NL_SKIP;
 	}
-
-	/*
-	 * param->num has the maximum number of entries for which there
-	 * is room in the freq_list provided by the caller.
-	 */
-	freqs = nla_data(attr);
-	max_num = nla_len(attr) / sizeof(u32);
-	if (max_num > param->num)
-		max_num = param->num;
-	for (num = 0; num < max_num; num++)
-		param->freq_list[num] = freqs[num];
-	param->num = num;
-
 	return NL_SKIP;
 }
 
@@ -11504,7 +12382,7 @@
 static int nl80211_get_pref_freq_list(void *priv,
 				      enum wpa_driver_if_type if_type,
 				      unsigned int *num,
-				      unsigned int *freq_list)
+				      struct weighted_pcl *freq_list)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -11561,7 +12439,8 @@
 	}
 	nla_nest_end(msg, params);
 
-	os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+	if (freq_list)
+		os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl));
 	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param,
 				 NULL, NULL);
 	if (ret) {
@@ -11573,8 +12452,10 @@
 	*num = param.num;
 
 	for (i = 0; i < *num; i++) {
-		wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
-			   i, freq_list[i]);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x",
+			   i, freq_list[i].freq, freq_list[i].weight,
+			   freq_list[i].flag);
 	}
 
 	return 0;
@@ -12007,6 +12888,170 @@
 
 #endif /* CONFIG_MBO */
 
+
+#ifdef CONFIG_PASN
+
+static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
+{
+	unsigned int i;
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *nlpeers, *attr, *attr1;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG,
+		"nl80211: PASN authentication response for %d entries",
+		params->num_peers);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_PASN))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	nlpeers = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEERS);
+	if (!nlpeers)
+		goto fail;
+
+	for (i = 0; i < params->num_peers; i++) {
+		attr1 = nla_nest_start(msg, i);
+		if (!attr1 ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR,
+			    ETH_ALEN, params->peer[i].own_addr) ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR,
+			    ETH_ALEN, params->peer[i].peer_addr))
+			goto fail;
+
+		if (params->peer[i].status == 0 &&
+		    nla_put_flag(msg,
+				 QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
+			goto fail;
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Own address[%u]: " MACSTR
+			   " Peer address[%u]: " MACSTR " Status: %s",
+			   i, MAC2STR(params->peer[i].own_addr), i,
+			   MAC2STR(params->peer[i].peer_addr),
+			   params->peer[i].status ? "Fail" : "Success");
+		nla_nest_end(msg, attr1);
+	}
+
+	nla_nest_end(msg, nlpeers);
+	nla_nest_end(msg, attr);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static u32 wpa_ltf_keyseed_len_to_sha_type(size_t len)
+{
+	if (len == SHA384_MAC_LEN)
+		return QCA_WLAN_VENDOR_SHA_384;
+	if (len == SHA256_MAC_LEN)
+		return QCA_WLAN_VENDOR_SHA_256;
+
+	wpa_printf(MSG_ERROR, "nl80211: Unexpected LTF keyseed len %zu", len);
+	return (u32) -1;
+}
+
+
+static int nl80211_set_secure_ranging_ctx(void *priv,
+					  struct secure_ranging_params *params)
+{
+	int ret;
+	u32 suite;
+	struct nlattr *attr;
+	struct nl_msg *msg = NULL;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/* Configure secure ranging context only to the drivers that support it.
+	 */
+	if (!drv->secure_ranging_ctx_vendor_cmd_avail)
+		return 0;
+
+	if (!params->peer_addr || !params->own_addr)
+		return -1;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG,
+		"nl80211: Secure ranging context for " MACSTR,
+		MAC2STR(params->peer_addr));
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR,
+		    ETH_ALEN, params->peer_addr) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR,
+		    ETH_ALEN, params->own_addr) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION,
+			params->action))
+		goto fail;
+
+	if (params->cipher) {
+		suite = wpa_cipher_to_cipher_suite(params->cipher);
+		if (!suite ||
+		    nla_put_u32(msg,
+				QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER,
+				suite))
+			goto fail;
+	}
+
+	if (params->tk_len && params->tk) {
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK,
+			    params->tk_len, params->tk))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: TK",
+				params->tk, params->tk_len);
+	}
+
+	if (params->ltf_keyseed_len && params->ltf_keyseed) {
+		u32 sha_type = wpa_ltf_keyseed_len_to_sha_type(
+			params->ltf_keyseed_len);
+
+		if (sha_type == (u32) -1 ||
+		    nla_put_u32(
+			    msg,
+			    QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE,
+			    sha_type) ||
+		    nla_put(msg,
+			    QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED,
+			    params->ltf_keyseed_len, params->ltf_keyseed))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: LTF keyseed",
+				params->ltf_keyseed, params->ltf_keyseed_len);
+	}
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set secure ranging context failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
@@ -12390,6 +13435,77 @@
 }
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	unsigned int idx, i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
+		   link_id, MAC2STR(addr));
+
+	if (drv->nlmode != NL80211_IFTYPE_AP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: cannot add link to iftype=%u",
+			   drv->nlmode);
+		return -EINVAL;
+	}
+
+	if (bss->n_links >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
+			   bss->n_links);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < bss->n_links; i++) {
+		if (bss->links[i].link_id == link_id &&
+		    bss->links[i].beacon_set) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLD: link already set");
+			return -EINVAL;
+		}
+	}
+
+	/* try using the first link entry, assuming it is not beaconing yet */
+	if (bss->n_links == 1 &&
+	    bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+		if (bss->flink->beacon_set) {
+			wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
+			return -EINVAL;
+		}
+
+		idx = 0;
+	} else {
+		idx = bss->n_links;
+	}
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: add link failed. ret=%d (%s)",
+			   ret, strerror(-ret));
+		return ret;
+	}
+
+	bss->links[idx].link_id = link_id;
+	os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+
+	bss->n_links = idx + 1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+	return 0;
+}
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 static int testing_nl80211_register_frame(void *priv, u16 type,
@@ -12496,6 +13612,7 @@
 	.resume = wpa_driver_nl80211_resume,
 	.signal_monitor = nl80211_signal_monitor,
 	.signal_poll = nl80211_signal_poll,
+	.mlo_signal_poll = nl80211_mlo_signal_poll,
 	.channel_info = nl80211_channel_info,
 	.set_param = nl80211_set_param,
 	.get_radio_name = nl80211_get_radio_name,
@@ -12566,6 +13683,10 @@
 #endif /* CONFIG_MBO */
 	.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
 	.add_sta_node = nl80211_add_sta_node,
+#ifdef CONFIG_PASN
+	.send_pasn_resp = nl80211_send_pasn_resp,
+	.set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx,
+#endif /* CONFIG_PASN */
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.do_acs = nl80211_do_acs,
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
@@ -12576,6 +13697,8 @@
 #ifdef CONFIG_DPP
 	.dpp_listen = nl80211_dpp_listen,
 #endif /* CONFIG_DPP */
+	.get_sta_mlo_info = nl80211_get_sta_mlo_info,
+	.link_add = nl80211_link_add,
 #ifdef CONFIG_TESTING_OPTIONS
 	.register_frame = testing_nl80211_register_frame,
 	.radio_disable = testing_nl80211_radio_disable,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 8ad7184..bd35e52 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -49,15 +49,31 @@
 	int wiphy_idx;
 };
 
+#define NL80211_DRV_LINK_ID_NA (-1)
+
+struct i802_link {
+	unsigned int beacon_set:1;
+
+	s8 link_id;
+	int freq;
+	int bandwidth;
+	u8 addr[ETH_ALEN];
+	void *ctx;
+};
+
 struct i802_bss {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *next;
+
+	size_t n_links;
+	struct i802_link links[MAX_NUM_MLD_LINKS];
+	struct i802_link *flink;
+
 	int ifindex;
 	int br_ifindex;
 	u64 wdev_id;
 	char ifname[IFNAMSIZ + 1];
 	char brname[IFNAMSIZ];
-	unsigned int beacon_set:1;
 	unsigned int added_if_into_bridge:1;
 	unsigned int already_in_bridge:1;
 	unsigned int added_bridge:1;
@@ -68,9 +84,8 @@
 	unsigned int use_nl_connect:1;
 
 	u8 addr[ETH_ALEN];
+	u8 prev_addr[ETH_ALEN];
 
-	int freq;
-	int bandwidth;
 	int if_dynamic;
 
 	void *ctx;
@@ -128,6 +143,7 @@
 	u8 bssid[ETH_ALEN];
 	u8 prev_bssid[ETH_ALEN];
 	int associated;
+	struct driver_sta_mlo_info sta_mlo_info;
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	enum nl80211_iftype nlmode;
@@ -180,6 +196,9 @@
 	unsigned int unsol_bcast_probe_resp:1;
 	unsigned int qca_do_acs:1;
 	unsigned int brcm_do_acs:1;
+	unsigned int uses_6ghz:1;
+	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+	unsigned int puncturing:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -221,6 +240,9 @@
 	int auth_wep_tx_keyidx;
 	int auth_local_state_change;
 	int auth_p2p;
+	bool auth_mld;
+	u8 auth_mld_link_id;
+	u8 auth_ap_mld_addr[ETH_ALEN];
 
 	/*
 	 * Tells whether the last scan issued from wpa_supplicant was a normal
@@ -235,7 +257,6 @@
 	bool roam_indication_done;
 	u8 *pending_roam_data;
 	size_t pending_roam_data_len;
-	struct os_reltime pending_roam_ind_time;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 };
 
@@ -269,7 +290,8 @@
 int is_sta_interface(enum nl80211_iftype nlmode);
 int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
 int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
-			    struct wpa_signal_info *sig);
+			    const u8 *bssid,
+			    struct hostap_sta_driver_data *data);
 int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
 			   struct wpa_signal_info *sig_change);
 int nl80211_get_wiphy_index(struct i802_bss *bss);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 75df36c..3152529 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -294,6 +294,9 @@
 		case RSN_AUTH_KEY_MGMT_FT_SAE:
 			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
 			break;
+		case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY;
+			break;
 		case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
 			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
 			break;
@@ -330,6 +333,9 @@
 		case RSN_AUTH_KEY_MGMT_SAE:
 			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
 			break;
+		case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY;
+			break;
 		}
 	}
 
@@ -657,6 +663,9 @@
 			      NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP))
 		info->drv->unsol_bcast_probe_resp = 1;
 
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_PUNCT))
+		info->drv->puncturing = 1;
+
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
 		capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT;
@@ -668,6 +677,25 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_RADAR_BACKGROUND))
 		capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SECURE_LTF)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+	}
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SECURE_RTT)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+	}
+
+	if (ext_feature_isset(
+		    ext_features, len,
+		    NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+	}
 }
 
 
@@ -849,6 +877,30 @@
 }
 
 
+static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
+{
+	struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+
+	if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
+			     NULL))
+		return;
+
+	if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
+		return;
+
+	cap->mbssid_max_interfaces =
+		nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
+
+	if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
+		cap->ema_max_periodicity =
+			nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
+
+	wpa_printf(MSG_DEBUG,
+		   "mbssid: max interfaces %u, max profile periodicity %u",
+		   cap->mbssid_max_interfaces, cap->ema_max_periodicity);
+}
+
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1029,6 +1081,9 @@
 				case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO:
 					drv->get_sta_info_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT:
+					drv->secure_ranging_ctx_vendor_cmd_avail = 1;
+					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
@@ -1086,6 +1141,16 @@
 	if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
 		capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
 
+	if (tb[NL80211_ATTR_MAX_NUM_AKM_SUITES])
+		capa->max_num_akms =
+			nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+
+	if (tb[NL80211_ATTR_MBSSID_CONFIG])
+		wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
+
+	if (tb[NL80211_ATTR_MLO_SUPPORT])
+		capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+
 	return NL_SKIP;
 }
 
@@ -1164,6 +1229,13 @@
 	if (info->update_ft_ies_supported)
 		drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES;
 
+	if (!drv->capa.max_num_akms)
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+		drv->capa.max_num_akms = 1;
+#else
+		drv->capa.max_num_akms = NL80211_MAX_NR_AKM_SUITES;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
 	return 0;
 }
 
@@ -1326,6 +1398,22 @@
 		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+	if (check_feature(
+		    QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA,
+		    &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+	if (check_feature(
+		    QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP,
+		    &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
 	os_free(info.flags);
 }
 
@@ -1948,7 +2036,8 @@
 		os_memset(mode, 0, sizeof(*mode));
 		mode->mode = NUM_HOSTAPD_MODES;
 		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
-			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN |
+			HOSTAPD_MODE_FLAG_HE_INFO_KNOWN;
 
 		/*
 		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
@@ -2241,43 +2330,15 @@
 
 	for (c = 0; c < mode->num_channels; c++) {
 		struct hostapd_channel_data *chan = &mode->channels[c];
-		if (chan->freq - 10 >= start && chan->freq + 70 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_10_70;
 
-		if (chan->freq - 30 >= start && chan->freq + 50 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_30_50;
+		if (chan->freq - 10 < start || chan->freq + 10 > end)
+			continue;
 
-		if (chan->freq - 50 >= start && chan->freq + 30 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_50_30;
+		if (max_bw >= 80)
+			chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
 
-		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
-
-		if (max_bw >= 160) {
-			if (chan->freq - 10 >= start && chan->freq + 150 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_10_150;
-
-			if (chan->freq - 30 >= start && chan->freq + 130 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_30_130;
-
-			if (chan->freq - 50 >= start && chan->freq + 110 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_50_110;
-
-			if (chan->freq - 70 >= start && chan->freq + 90 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_70_90;
-
-			if (chan->freq - 90 >= start && chan->freq + 70 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_90_70;
-
-			if (chan->freq - 110 >= start && chan->freq + 50 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_110_50;
-
-			if (chan->freq - 130 >= start && chan->freq + 30 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_130_30;
-
-			if (chan->freq - 150 >= start && chan->freq + 10 <= end)
-				chan->flag |= HOSTAPD_CHAN_VHT_150_10;
-		}
+		if (max_bw >= 160)
+			chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
 	}
 }
 
@@ -2474,7 +2535,8 @@
 }
 
 
-static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes,
+static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
+				   struct hostapd_hw_modes *modes,
 				   u16 num_modes)
 {
 	int i;
@@ -2492,6 +2554,9 @@
 		for (j = 0; j < mode->num_channels; j++) {
 			struct hostapd_channel_data *chan = &mode->channels[j];
 
+			if (chan->freq >= 5925 && chan->freq <= 7125 &&
+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+				drv->uses_6ghz = true;
 			res = os_snprintf(pos, end - pos, " %d%s%s%s",
 					  chan->freq,
 					  (chan->flag & HOSTAPD_CHAN_DISABLED) ?
@@ -2563,7 +2628,7 @@
 
 		modes = wpa_driver_nl80211_postprocess_modes(result.modes,
 							     num_modes);
-		nl80211_dump_chan_list(modes, *num_modes);
+		nl80211_dump_chan_list(drv, modes, *num_modes);
 		return modes;
 	}
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 29893a4..14d7842 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -178,6 +178,12 @@
 	C2S(NL80211_CMD_COLOR_CHANGE_COMPLETED)
 	C2S(NL80211_CMD_SET_FILS_AAD)
 	C2S(NL80211_CMD_ASSOC_COMEBACK)
+	C2S(NL80211_CMD_ADD_LINK)
+	C2S(NL80211_CMD_REMOVE_LINK)
+	C2S(NL80211_CMD_ADD_LINK_STA)
+	C2S(NL80211_CMD_MODIFY_LINK_STA)
+	C2S(NL80211_CMD_REMOVE_LINK_STA)
+	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
 	C2S(__NL80211_CMD_AFTER_LAST)
 	}
 #undef C2S
@@ -321,7 +327,7 @@
 	}
 
 	event.assoc_info.freq = drv->assoc_freq;
-	drv->first_bss->freq = drv->assoc_freq;
+	drv->first_bss->flink->freq = drv->assoc_freq;
 
 	nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
 
@@ -414,11 +420,302 @@
 	}
 }
 
+
+static void
+nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo,
+				       struct nlattr *mlo_links)
+{
+	struct nlattr *link;
+	int rem_links;
+
+	nla_for_each_nested(link, mlo_links, rem_links) {
+		struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX + 1];
+		int link_id;
+
+		nla_parse(tb,QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX, nla_data(link),
+			  nla_len(link), NULL);
+
+		if (!tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID])
+			continue;
+
+		link_id = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		mlo->valid_links |= BIT(link_id);
+		os_memcpy(mlo->links[link_id].addr,
+			  nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR]),
+			  ETH_ALEN);
+		os_memcpy(mlo->links[link_id].bssid,
+			  nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID]),
+			  ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+			   " bssid " MACSTR,
+			   link_id, MAC2STR(mlo->links[link_id].addr),
+			   MAC2STR(mlo->links[link_id].bssid));
+	}
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
+static void nl80211_parse_mlo_link_info(struct driver_sta_mlo_info *mlo,
+					struct nlattr *mlo_links)
+{
+	struct nlattr *link;
+	int rem_links;
+
+	nla_for_each_nested(link, mlo_links, rem_links) {
+		struct nlattr *tb[NL80211_ATTR_MAX + 1];
+		int link_id;
+
+		nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+			  NULL);
+
+		if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] ||
+		    !tb[NL80211_ATTR_BSSID])
+			continue;
+
+		link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		if (tb[NL80211_ATTR_STATUS_CODE]) {
+			/* Set requested links only when status indicated */
+			mlo->req_links |= BIT(link_id);
+			if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) ==
+			    WLAN_STATUS_SUCCESS)
+				mlo->valid_links |= BIT(link_id);
+		} else {
+			mlo->valid_links |= BIT(link_id);
+		}
+
+		os_memcpy(mlo->links[link_id].addr,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+		os_memcpy(mlo->links[link_id].bssid,
+			  nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+			   " bssid " MACSTR,
+			   link_id, MAC2STR(mlo->links[link_id].addr),
+			   MAC2STR(mlo->links[link_id].bssid));
+	}
+}
+
+
+struct links_info {
+	/* bitmap of link IDs in Per-STA profile subelements */
+	u16 non_assoc_links;
+	u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN];
+};
+
+
+static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len,
+					     struct links_info *info)
+{
+	size_t rem_len;
+	const u8 *pos;
+
+	if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+	    mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN])
+		return;
+
+	/* Skip Common Info */
+	pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+	rem_len = mle_len -
+		(MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+	/* Parse Subelements */
+	while (rem_len > 2) {
+		size_t ie_len = 2 + pos[1];
+
+		if (rem_len < ie_len)
+			break;
+
+		if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+			u8 link_id;
+			const u8 *sta_profile;
+			u16 sta_ctrl;
+
+			if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)
+				goto next_subelem;
+
+			sta_profile = &pos[2];
+			sta_ctrl = WPA_GET_LE16(sta_profile);
+			link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+			if (link_id >= MAX_NUM_MLD_LINKS)
+				goto next_subelem;
+
+			if (!(sta_ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC))
+				goto next_subelem;
+
+			info->non_assoc_links |= BIT(link_id);
+			os_memcpy(info->addr[link_id],
+				  &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX],
+				  ETH_ALEN);
+		}
+next_subelem:
+		pos += ie_len;
+		rem_len -= ie_len;
+	}
+}
+
+
+static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo,
+					      struct nlattr *req_ie,
+					      struct nlattr *resp_ie)
+{
+	int i;
+	struct wpabuf *mle;
+	struct ieee802_11_elems req_elems, resp_elems;
+	struct links_info req_info, resp_info;
+
+	if (!req_ie || !resp_ie) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: (Re)Association Request/Response frame elements not available");
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(nla_data(req_ie), nla_len(req_ie),
+				   &req_elems, 0) == ParseFailed ||
+	    ieee802_11_parse_elems(nla_data(resp_ie), nla_len(resp_ie),
+				   &resp_elems, 0) == ParseFailed) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: Failed to parse (Re)Association Request/Response elements");
+		return -1;
+	}
+
+	mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mle) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: Basic Multi-Link element not found in Association Request");
+		return -1;
+	}
+	os_memset(&req_info, 0, sizeof(req_info));
+	nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+					 &req_info);
+	wpabuf_free(mle);
+
+	mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mle) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Basic Multi-Link element not found in Association Response");
+		return -1;
+	}
+	os_memset(&resp_info, 0, sizeof(resp_info));
+	nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+					 &resp_info);
+	wpabuf_free(mle);
+
+	if (req_info.non_assoc_links != resp_info.non_assoc_links) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Association Request and Response links bitmaps not equal (0x%x != 0x%x)",
+			   req_info.non_assoc_links,
+			   resp_info.non_assoc_links);
+		return -1;
+	}
+
+	mlo->req_links = BIT(mlo->assoc_link_id) | req_info.non_assoc_links;
+	if ((mlo->req_links & mlo->valid_links) != mlo->valid_links) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Accepted links are not a subset of requested links (req_links=0x%x valid_links=0x%x non_assoc_links=0x%x assoc_link_id=0x%x)",
+			   mlo->req_links, mlo->valid_links,
+			   req_info.non_assoc_links, BIT(mlo->assoc_link_id));
+		return -1;
+	}
+
+	/* Get MLO links info for rejected links */
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+			continue;
+
+		os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
+		os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
+{
+	u16 control;
+
+	if (len < 2)
+		return -1;
+
+	control = WPA_GET_LE16(data);
+	if (!(control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
+		return -1;
+
+#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
+		(2 + /* Multi-Link Control field */ \
+		 1 + /* Common Info Length field (Basic) */ \
+		 ETH_ALEN) /* MLD MAC Address field (Basic) */
+	if (len <= BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX)
+		return -1;
+
+	return data[BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX] & 0x0F;
+}
+
+
+static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv,
+				   bool qca_roam_auth,
+				   struct nlattr *addr,
+				   struct nlattr *mlo_links,
+				   struct nlattr *req_ie,
+				   struct nlattr *resp_ie)
+{
+	const u8 *ml_ie;
+	struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+	int res;
+
+	if (!addr || !mlo_links || !resp_ie)
+		return;
+
+	ml_ie = get_ml_ie(nla_data(resp_ie), nla_len(resp_ie),
+			  MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!ml_ie)
+		return;
+
+	res = nl80211_get_assoc_link_id(&ml_ie[3], ml_ie[1] - 1);
+	if (res < 0 || res >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not find a valid association Link ID (res=%d)",
+			   res);
+		return;
+	}
+	drv->sta_mlo_info.assoc_link_id = res;
+
+	os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
+		   MAC2STR(mlo->ap_mld_addr));
+
+	if (!qca_roam_auth)
+		nl80211_parse_mlo_link_info(mlo, mlo_links);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (qca_roam_auth)
+		nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	if (!(mlo->valid_links & BIT(mlo->assoc_link_id)) ||
+	    (!mlo->req_links &&
+	     nl80211_update_rejected_links_info(mlo, req_ie, resp_ie))) {
+		wpa_printf(MSG_INFO, "nl80211: Invalid MLO connection info");
+		mlo->valid_links = 0;
+		return;
+	}
+
+	os_memcpy(drv->bssid, mlo->links[drv->sta_mlo_info.assoc_link_id].bssid,
+		  ETH_ALEN);
+	os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+}
+
+
 static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
-			       enum nl80211_commands cmd, struct nlattr *status,
+			       enum nl80211_commands cmd, bool qca_roam_auth,
+			       struct nlattr *status,
 			       struct nlattr *addr, struct nlattr *req_ie,
 			       struct nlattr *resp_ie,
 			       struct nlattr *timed_out,
@@ -430,7 +727,8 @@
 			       struct nlattr *subnet_status,
 			       struct nlattr *fils_erp_next_seq_num,
 			       struct nlattr *fils_pmk,
-			       struct nlattr *fils_pmkid)
+			       struct nlattr *fils_pmkid,
+			       struct nlattr *mlo_links)
 {
 	union wpa_event_data event;
 	const u8 *ssid = NULL;
@@ -522,7 +820,10 @@
 	}
 
 	drv->associated = 1;
-	if (addr) {
+	os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+	nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, req_ie,
+			       resp_ie);
+	if (!drv->sta_mlo_info.valid_links && addr) {
 		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
 		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
 	}
@@ -551,7 +852,7 @@
 	}
 
 	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
-	drv->first_bss->freq = drv->assoc_freq;
+	drv->first_bss->flink->freq = drv->assoc_freq;
 
 	if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
 	    (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
@@ -673,6 +974,9 @@
 	case CHAN_WIDTH_80P80:
 		freq1 = cf1 - 30;
 		break;
+	case CHAN_WIDTH_320:
+		freq1 = cf1 - 150;
+		break;
 	case CHAN_WIDTH_UNKNOWN:
 	case CHAN_WIDTH_2160:
 	case CHAN_WIDTH_4320:
@@ -687,9 +991,11 @@
 
 
 static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr *ifindex, struct nlattr *freq,
-				 struct nlattr *type, struct nlattr *bw,
-				 struct nlattr *cf1, struct nlattr *cf2,
+				 struct nlattr *ifindex, struct nlattr *link,
+				 struct nlattr *freq, struct nlattr *type,
+				 struct nlattr *bw, struct nlattr *cf1,
+				 struct nlattr *cf2,
+				 struct nlattr *punct_bitmap,
 				 int finished)
 {
 	struct i802_bss *bss;
@@ -745,6 +1051,8 @@
 	data.ch_switch.freq = nla_get_u32(freq);
 	data.ch_switch.ht_enabled = ht_enabled;
 	data.ch_switch.ch_offset = chan_offset;
+	if (punct_bitmap)
+		data.ch_switch.punct_bitmap = (u16) nla_get_u32(punct_bitmap);
 	if (bw)
 		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
 	if (cf1)
@@ -753,7 +1061,26 @@
 		data.ch_switch.cf2 = nla_get_u32(cf2);
 
 	if (finished)
-		bss->freq = data.ch_switch.freq;
+		bss->flink->freq = data.ch_switch.freq;
+
+	if (link) {
+		u8 link_id = nla_get_u8(link);
+
+		if (link_id < MAX_NUM_MLD_LINKS &&
+		    drv->sta_mlo_info.valid_links & BIT(link_id)) {
+			data.ch_switch.link_id = link_id;
+			drv->sta_mlo_info.links[link_id].freq =
+				data.ch_switch.freq;
+			wpa_supplicant_event(
+				bss->ctx,
+				finished ? EVENT_LINK_CH_SWITCH :
+				EVENT_LINK_CH_SWITCH_STARTED, &data);
+		}
+
+		if (link_id != drv->sta_mlo_info.assoc_link_id)
+			return;
+	}
+
 	drv->assoc_freq = data.ch_switch.freq;
 
 	wpa_supplicant_event(bss->ctx, finished ?
@@ -788,7 +1115,8 @@
 
 static void mlme_event_mgmt(struct i802_bss *bss,
 			    struct nlattr *freq, struct nlattr *sig,
-			    const u8 *frame, size_t len)
+			    const u8 *frame, size_t len,
+			    int link_id)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	const struct ieee80211_mgmt *mgmt;
@@ -826,6 +1154,8 @@
 	event.rx_mgmt.frame_len = len;
 	event.rx_mgmt.ssi_signal = ssi_signal;
 	event.rx_mgmt.drv_priv = bss;
+	event.rx_mgmt.link_id = link_id;
+
 	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
 }
 
@@ -923,8 +1253,11 @@
 			 * ignore_next_local_deauth as well, to avoid next local
 			 * deauth event be wrongly ignored.
 			 */
-			if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
-				       ETH_ALEN)) {
+			if (os_memcmp(mgmt->sa, drv->first_bss->addr,
+				      ETH_ALEN) == 0 ||
+			    (!is_zero_ether_addr(drv->first_bss->prev_addr) &&
+			     os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
+				       ETH_ALEN) == 0)) {
 				wpa_printf(MSG_DEBUG,
 					   "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
 				drv->ignore_next_local_deauth = 0;
@@ -1077,11 +1410,14 @@
 		       struct nlattr *addr, struct nlattr *timed_out,
 		       struct nlattr *freq, struct nlattr *ack,
 		       struct nlattr *cookie, struct nlattr *sig,
-		       struct nlattr *wmm, struct nlattr *req_ie)
+		       struct nlattr *wmm, struct nlattr *req_ie,
+		       struct nlattr *link)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 stype = 0, auth_type = 0;
 	const u8 *data;
 	size_t len;
+	int link_id;
 
 	if (timed_out && addr) {
 		mlme_timeout_event(drv, cmd, addr);
@@ -1095,6 +1431,11 @@
 		return;
 	}
 
+	if (link)
+		link_id = nla_get_u8(link);
+	else
+		link_id = -1;
+
 	data = nla_data(frame);
 	len = nla_len(frame);
 	if (len < 4 + 2 * ETH_ALEN) {
@@ -1105,15 +1446,38 @@
 		return;
 	}
 	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
-		   ") A1=" MACSTR " A2=" MACSTR, cmd,
+		   ") A1=" MACSTR " A2=" MACSTR " on link_id=%d", cmd,
 		   nl80211_command_to_string(cmd), bss->ifname,
 		   MAC2STR(bss->addr), MAC2STR(data + 4),
-		   MAC2STR(data + 4 + ETH_ALEN));
-	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
-	    (is_zero_ether_addr(bss->rand_addr) ||
-	     os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
-	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+		   MAC2STR(data + 4 + ETH_ALEN), link_id);
+
+	/* PASN Authentication frame can be received with a different source MAC
+	 * address. Allow NL80211_CMD_FRAME event with foreign addresses also.
+	 */
+	if (cmd == NL80211_CMD_FRAME && len >= 24) {
+		const struct ieee80211_mgmt *mgmt;
+		u16 fc;
+
+		mgmt = (const struct ieee80211_mgmt *) data;
+		fc = le_to_host16(mgmt->frame_control);
+		stype = WLAN_FC_GET_STYPE(fc);
+		auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	}
+
+	if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH &&
+	    auth_type == host_to_le16(WLAN_AUTH_PASN)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: %s: Allow PASN frame for foreign address",
+			   bss->ifname);
+	} else if (cmd != NL80211_CMD_FRAME_TX_STATUS  &&
+		   !(data[4] & 0x01) &&
+		   os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+		   (is_zero_ether_addr(bss->rand_addr) ||
+		    os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
+		   os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+		   (is_zero_ether_addr(drv->first_bss->prev_addr) ||
+		    os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
+			      ETH_ALEN) != 0)) {
 		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
 			   "for foreign address", bss->ifname);
 		return;
@@ -1139,7 +1503,7 @@
 		break;
 	case NL80211_CMD_FRAME:
 		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
-				nla_len(frame));
+				nla_len(frame), link_id);
 		break;
 	case NL80211_CMD_FRAME_TX_STATUS:
 		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
@@ -1219,7 +1583,7 @@
 	if (freq) {
 		wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
 			   freq);
-		drv->first_bss->freq = freq;
+		drv->first_bss->flink->freq = freq;
 	}
 
 	os_memset(&event, 0, sizeof(event));
@@ -1335,7 +1699,7 @@
 	struct nlattr *nl;
 	int rem;
 	struct scan_info *info;
-#define MAX_REPORT_FREQS 100
+#define MAX_REPORT_FREQS 110
 	int freqs[MAX_REPORT_FREQS];
 	int num_freqs = 0;
 
@@ -1367,7 +1731,7 @@
 		}
 	}
 	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-		char msg[500], *pos, *end;
+		char msg[MAX_REPORT_FREQS * 5 + 1], *pos, *end;
 		int res;
 
 		pos = msg;
@@ -1382,11 +1746,12 @@
 			if (!os_snprintf_error(end - pos, res))
 				pos += res;
 			num_freqs++;
-			if (num_freqs == MAX_REPORT_FREQS - 1)
+			if (num_freqs == MAX_REPORT_FREQS)
 				break;
 		}
 		info->freqs = freqs;
 		info->num_freqs = num_freqs;
+		msg[sizeof(msg) - 1] = '\0';
 		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
 			   msg);
 	}
@@ -1490,11 +1855,11 @@
 	 * nl80211_get_link_signal() and nl80211_get_link_noise() set default
 	 * values in case querying the driver fails.
 	 */
-	res = nl80211_get_link_signal(drv, &ed.signal_change);
+	res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data);
 	if (res == 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
-			   ed.signal_change.current_signal,
-			   ed.signal_change.current_txrate);
+		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %lu",
+			   ed.signal_change.data.signal,
+			   ed.signal_change.data.current_tx_rate);
 	} else {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Querying the driver for signal info failed");
@@ -2092,7 +2457,7 @@
 	bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
 	wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
 
-	mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+	mlme_event_connect(drv, NL80211_CMD_ROAM, true, NULL,
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
@@ -2104,7 +2469,16 @@
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
-			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+	wpa_driver_nl80211_driver_event(
+		drv, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH,
+		data, len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
 }
 
 
@@ -2115,7 +2489,6 @@
 	if (!drv->roam_indication_done) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Pending roam indication, delay processing roam+auth vendor event");
-		os_get_reltime(&drv->pending_roam_ind_time);
 
 		os_free(drv->pending_roam_data);
 		drv->pending_roam_data = os_memdup(data, len);
@@ -2371,6 +2744,82 @@
 	wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
 }
 
+
+#ifdef CONFIG_PASN
+
+static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv,
+				  u8 *data, size_t len)
+{
+	int ret = -EINVAL;
+	struct nlattr *attr;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX + 1];
+	unsigned int n_peers = 0, idx = 0;
+	int rem_conf;
+	enum qca_wlan_vendor_pasn_action action;
+	union wpa_event_data event;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PASN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]) {
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	action = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]);
+	switch (action) {
+	case QCA_WLAN_VENDOR_PASN_ACTION_AUTH:
+		event.pasn_auth.action = PASN_ACTION_AUTH;
+		break;
+	case QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT:
+		event.pasn_auth.action =
+			PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
+		break;
+	default:
+		return;
+	}
+
+	nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS], rem_conf)
+		n_peers++;
+
+	if (n_peers > WPAS_MAX_PASN_PEERS) {
+		wpa_printf(MSG_DEBUG, "nl80211: PASN auth: too many peers (%d)",
+			    n_peers);
+		return;
+	}
+
+	nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS],
+			    rem_conf) {
+		struct nlattr *nl_src, *nl_peer;
+
+		ret = nla_parse_nested(cfg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX,
+				       attr, NULL);
+		if (ret)
+			return;
+		nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
+		nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
+		if (nl_src)
+			os_memcpy(event.pasn_auth.peer[idx].own_addr,
+				  nla_data(nl_src), ETH_ALEN);
+		if (nl_peer)
+			os_memcpy(event.pasn_auth.peer[idx].peer_addr,
+				  nla_data(nl_peer), ETH_ALEN);
+		if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
+			event.pasn_auth.peer[idx].ltf_keyseed_required = true;
+		idx++;
+	}
+	event.pasn_auth.num_peers = n_peers;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: PASN auth action: %u, num_bssids: %d",
+		   event.pasn_auth.action,
+		   event.pasn_auth.num_peers);
+	wpa_supplicant_event(drv->ctx, EVENT_PASN_AUTH, &event);
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -2407,6 +2856,11 @@
 	case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
 		qca_nl80211_p2p_lo_stop_event(drv, data, len);
 		break;
+#ifdef CONFIG_PASN
+	case QCA_NL80211_VENDOR_SUBCMD_PASN:
+		qca_nl80211_pasn_auth(drv, data, len);
+		break;
+#endif /* CONFIG_PASN */
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -2534,7 +2988,13 @@
 
 #ifdef ANDROID
 #ifdef ANDROID_LIB_EVENT
-       wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data, len);
+	/* Postpone QCA roam+auth event indication to the point when both that
+	 * and the NL80211_CMD_ROAM event have been received (see calls to
+	 * qca_nl80211_key_mgmt_auth() and drv->pending_roam_data). */
+	if (!(vendor_id == OUI_QCA &&
+	      subcmd == QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH))
+		wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data,
+						len);
 #endif /* ANDROID_LIB_EVENT */
 #endif /* ANDROID */
 
@@ -2670,6 +3130,7 @@
 {
 	union wpa_event_data event;
 	enum nl80211_external_auth_action act;
+	char mld_addr[50];
 
 	if (!tb[NL80211_ATTR_AKM_SUITES] ||
 	    !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
@@ -2700,10 +3161,21 @@
 
 	event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
 
+	mld_addr[0] = '\0';
+	if (tb[NL80211_ATTR_MLD_ADDR]) {
+		event.external_auth.mld_addr =
+			nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+		os_snprintf(mld_addr, sizeof(mld_addr), ", MLD ADDR: " MACSTR,
+			    MAC2STR(event.external_auth.mld_addr));
+	}
+
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: External auth action: %u, AKM: 0x%x",
+		   "nl80211: External auth action: %u, AKM: 0x%x, SSID: %s, BSSID: " MACSTR "%s",
 		   event.external_auth.action,
-		   event.external_auth.key_mgmt_suite);
+		   event.external_auth.key_mgmt_suite,
+		   wpa_ssid_txt(event.external_auth.ssid,
+				event.external_auth.ssid_len),
+		   MAC2STR(event.external_auth.bssid), mld_addr);
 	wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
 }
 
@@ -2712,6 +3184,11 @@
 				    struct nlattr **tb)
 {
 	const u8 *addr;
+	union wpa_event_data event;
+	const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+				drv->sta_mlo_info.ap_mld_addr : drv->bssid;
+
+	os_memset(&event, 0, sizeof(event));
 
 	if (!tb[NL80211_ATTR_MAC] ||
 	    nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
@@ -2721,15 +3198,23 @@
 	}
 
 	addr = nla_data(tb[NL80211_ATTR_MAC]);
-	if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+	if (os_memcmp(addr, connected_addr, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Ignore port authorized event for " MACSTR
 			   " (not the currently connected BSSID " MACSTR ")",
-			   MAC2STR(addr), MAC2STR(drv->bssid));
+			   MAC2STR(addr), MAC2STR(connected_addr));
 		return;
 	}
 
-	wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL);
+	if (tb[NL80211_ATTR_TD_BITMAP]) {
+		event.port_authorized.td_bitmap_len =
+			nla_len(tb[NL80211_ATTR_TD_BITMAP]);
+		if (event.port_authorized.td_bitmap_len > 0)
+			event.port_authorized.td_bitmap =
+				nla_data(tb[NL80211_ATTR_TD_BITMAP]);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event);
 }
 
 
@@ -2789,6 +3274,9 @@
 		case NL80211_CHAN_WIDTH_160:
 			ed.sta_opmode.chan_width = CHAN_WIDTH_160;
 			break;
+		case NL80211_CHAN_WIDTH_320:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_320;
+			break;
 		default:
 			ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
 			break;
@@ -2808,6 +3296,8 @@
 {
 	u8 *src_addr;
 	u16 ethertype;
+	enum frame_encryption encrypted;
+	int link_id;
 
 	if (!tb[NL80211_ATTR_MAC] ||
 	    !tb[NL80211_ATTR_FRAME] ||
@@ -2816,6 +3306,13 @@
 
 	src_addr = nla_data(tb[NL80211_ATTR_MAC]);
 	ethertype = nla_get_u16(tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+	encrypted = nla_get_flag(tb[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) ?
+		FRAME_NOT_ENCRYPTED : FRAME_ENCRYPTED;
+
+	if (tb[NL80211_ATTR_MLO_LINK_ID])
+		link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+	else
+		link_id = -1;
 
 	switch (ethertype) {
 	case ETH_P_RSN_PREAUTH:
@@ -2824,9 +3321,10 @@
 			   MAC2STR(src_addr));
 		break;
 	case ETH_P_PAE:
-		drv_event_eapol_rx(drv->ctx, src_addr,
-				   nla_data(tb[NL80211_ATTR_FRAME]),
-				   nla_len(tb[NL80211_ATTR_FRAME]));
+		drv_event_eapol_rx2(drv->ctx, src_addr,
+				    nla_data(tb[NL80211_ATTR_FRAME]),
+				    nla_len(tb[NL80211_ATTR_FRAME]),
+				    encrypted, link_id);
 		break;
 	default:
 		wpa_printf(MSG_INFO,
@@ -2975,17 +3473,10 @@
 	if (cmd == NL80211_CMD_ROAM &&
 	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
 		if (drv->pending_roam_data) {
-			struct os_reltime now, age;
-
-			os_get_reltime(&now);
-			os_reltime_sub(&now, &drv->pending_roam_ind_time, &age);
-			if (age.sec == 0 && age.usec < 100000) {
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: Process pending roam+auth vendor event");
-				qca_nl80211_key_mgmt_auth(
-					drv, drv->pending_roam_data,
-					drv->pending_roam_data_len);
-			}
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Process pending roam+auth vendor event");
+			qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data,
+						  drv->pending_roam_data_len);
 			os_free(drv->pending_roam_data);
 			drv->pending_roam_data = NULL;
 			return;
@@ -3083,11 +3574,12 @@
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
 			   tb[NL80211_ATTR_STA_WME],
-			   tb[NL80211_ATTR_REQ_IE]);
+			   tb[NL80211_ATTR_REQ_IE],
+			   tb[NL80211_ATTR_MLO_LINK_ID]);
 		break;
 	case NL80211_CMD_CONNECT:
 	case NL80211_CMD_ROAM:
-		mlme_event_connect(drv, cmd,
+		mlme_event_connect(drv, cmd, false,
 				   tb[NL80211_ATTR_STATUS_CODE],
 				   tb[NL80211_ATTR_MAC],
 				   tb[NL80211_ATTR_REQ_IE],
@@ -3099,26 +3591,31 @@
 				   NULL,
 				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
 				   tb[NL80211_ATTR_PMK],
-				   tb[NL80211_ATTR_PMKID]);
+				   tb[NL80211_ATTR_PMKID],
+				   tb[NL80211_ATTR_MLO_LINKS]);
 		break;
 	case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
 		mlme_event_ch_switch(drv,
 				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_MLO_LINK_ID],
 				     tb[NL80211_ATTR_WIPHY_FREQ],
 				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
 				     tb[NL80211_ATTR_CHANNEL_WIDTH],
 				     tb[NL80211_ATTR_CENTER_FREQ1],
 				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     tb[NL80211_ATTR_PUNCT_BITMAP],
 				     0);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
 				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_MLO_LINK_ID],
 				     tb[NL80211_ATTR_WIPHY_FREQ],
 				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
 				     tb[NL80211_ATTR_CHANNEL_WIDTH],
 				     tb[NL80211_ATTR_CENTER_FREQ1],
 				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     tb[NL80211_ATTR_PUNCT_BITMAP],
 				     1);
 		break;
 	case NL80211_CMD_DISCONNECT:
@@ -3306,7 +3803,8 @@
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
 			   tb[NL80211_ATTR_COOKIE],
 			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
-			   tb[NL80211_ATTR_STA_WME], NULL);
+			   tb[NL80211_ATTR_STA_WME], NULL,
+			   tb[NL80211_ATTR_MLO_LINK_ID]);
 		break;
 	case NL80211_CMD_UNEXPECTED_FRAME:
 		nl80211_spurious_frame(bss, tb, 0);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index b82e5af..7a7890c 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -203,6 +203,21 @@
 				goto fail;
 		}
 		nla_nest_end(msg, ssids);
+
+		/*
+		 * If allowed, scan for 6 GHz APs that are reported by other
+		 * APs. Note that if the flag is not set and 6 GHz channels are
+		 * to be scanned, it is highly likely that non-PSC channels
+		 * would be scanned passively (due to the Probe Request frame
+		 * transmission restrictions mandated in IEEE Std 802.11ax-2021,
+		 * 26.17.2.3 (Scanning in the 6 GHz band). Passive scanning of
+		 * all non-PSC channels would take a significant amount of time.
+		 */
+		if (!params->non_coloc_6ghz) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Scan co-located APs on 6 GHz");
+			scan_flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+		}
 	} else {
 		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
 	}
@@ -397,7 +412,7 @@
 	drv->scan_state = SCAN_REQUESTED;
 	/* Not all drivers generate "scan completed" wireless event, so try to
 	 * read results after a timeout. */
-	timeout = 10;
+	timeout = drv->uses_6ghz ? 15 : 10;
 	if (drv->scan_complete_events) {
 		/*
 		 * The driver seems to deliver events to notify when scan is
@@ -896,8 +911,21 @@
 			   "nl80211: Local state (associated with " MACSTR
 			   ") does not match with BSS state",
 			   MAC2STR(drv->bssid));
-		clear_state_mismatch(drv, r->bssid);
-		clear_state_mismatch(drv, drv->bssid);
+
+		if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
+			      ETH_ALEN) != 0) {
+			clear_state_mismatch(drv, r->bssid);
+
+			if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
+				clear_state_mismatch(
+					drv, drv->sta_mlo_info.ap_mld_addr);
+			else
+				clear_state_mismatch(drv, drv->bssid);
+
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: BSSID is the MLD address");
+		}
 	}
 }
 
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 0f0ad1f..cf201fe 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -2424,7 +2424,7 @@
 	struct iwreq iwr;
 
 	os_memset(si, 0, sizeof(*si));
-	si->current_signal = -WPA_INVALID_NOISE;
+	si->data.signal = -WPA_INVALID_NOISE;
 	si->current_noise = WPA_INVALID_NOISE;
 	si->chanwidth = CHAN_WIDTH_UNKNOWN;
 
@@ -2440,7 +2440,7 @@
 		return -1;
 	}
 
-	si->current_signal = stats.qual.level -
+	si->data.signal = stats.qual.level -
 		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
 	si->current_noise = stats.qual.noise -
 		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 0e960f4..7780479 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -147,8 +147,9 @@
 		return "kernel-control";
 	case 1:
 		return "userspace-control";
+	default:
+		return "?";
 	}
-	return "?";
 }
 
 
@@ -161,8 +162,9 @@
 		return "IF_OPER_DORMANT";
 	case IF_OPER_UP:
 		return "IF_OPER_UP";
+	default:
+		return "?";
 	}
-	return "?";
 }
 
 
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 0568a79..9a0ac03 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -324,6 +324,17 @@
  */
 
 /**
+ * DOC: Multi-Link Operation
+ *
+ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * links. To use this in nl80211, various commands and responses now need
+ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
+ * Additionally, various commands that need to operate on a specific link
+ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
+ * use %NL80211_CMD_START_AP or similar functions.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -366,14 +377,22 @@
  *	the non-transmitting interfaces are deleted as well.
  *
  * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
- *	by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ *	by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
+ *	represents peer's MLD address for MLO pairwise key. For MLO group key,
+ *	the link is identified by %NL80211_ATTR_MLO_LINK_ID.
  * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
  *	%NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ *	For MLO connection, the link to set default key is identified by
+ *	%NL80211_ATTR_MLO_LINK_ID.
  * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
  *	%NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
- *	and %NL80211_ATTR_KEY_SEQ attributes.
+ *	and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
+ *	peer's MLD address for MLO pairwise key. The link to add MLO
+ *	group key is identified by %NL80211_ATTR_MLO_LINK_ID.
  * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
- *	or %NL80211_ATTR_MAC.
+ *	or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
+ *	for MLO pairwise key. The link to delete group key is identified by
+ *	%NL80211_ATTR_MLO_LINK_ID.
  *
  * @NL80211_CMD_GET_BEACON: (not used)
  * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
@@ -405,7 +424,8 @@
  *	interface identified by %NL80211_ATTR_IFINDEX.
  * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
  *	or, if no MAC address given, all stations, on the interface identified
- *	by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ *	by %NL80211_ATTR_IFINDEX. For MLD station, MLD address is used in
+ *	%NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and
  *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
  *	of disconnection indication should be sent to the station
  *	(Deauthentication or Disassociation frame and reason code for that
@@ -753,6 +773,13 @@
  *	%NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
  *	counters which will be updated to the current value. This attribute
  *	is used during CSA period.
+ *	For TX on an MLD, the frequency can be omitted and the link ID be
+ *	specified, or if transmitting to a known peer MLD (with MLD addresses
+ *	in the frame) both can be omitted and the link will be selected by
+ *	lower layers.
+ *	For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ *	indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
+ *	be included to indicate the ack TX timestamp.
  * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
  *	command may be used with the corresponding cookie to cancel the wait
  *	time if it is known that it is no longer necessary.  This command is
@@ -763,7 +790,9 @@
  *	transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
  *	the TX command and %NL80211_ATTR_FRAME includes the contents of the
  *	frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
- *	the frame.
+ *	the frame. %NL80211_ATTR_TX_HW_TIMESTAMP may be included to indicate the
+ *	tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ *	indicate the ack RX timestamp.
  * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
  *	backward compatibility.
  *
@@ -1108,6 +1137,12 @@
  *	has been received. %NL80211_ATTR_FRAME is used to specify the
  *	frame contents.  The frame is the raw EAPoL data, without ethernet or
  *	802.11 headers.
+ *	For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
+ *	its effect will depend on the destination: If the destination is known
+ *	to be an MLD, this will be used as a hint to select the link to transmit
+ *	the frame on. If the destination is not an MLD, this will select both
+ *	the link to transmit on and the source address will be set to the link
+ *	address of that link.
  *	When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
  *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
  *	indicating the protocol type of the received frame; whether the frame
@@ -1132,6 +1167,23 @@
  *	%NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
  *	command interface.
  *
+ *	Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in
+ *	%NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO
+ *	during the authentication offload in STA mode while connecting to MLD
+ *	APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability
+ *	in %NL80211_CMD_CONNECT to know whether the user space supports enabling
+ *	MLO during the authentication offload or not.
+ *	User space should enable MLO during the authentication only when it
+ *	receives the AP MLD address in authentication offload request. User
+ *	space shouldn't enable MLO when the authentication offload request
+ *	doesn't indicate the AP MLD address even if the AP is MLO capable.
+ *	User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and
+ *	interface address identified by %NL80211_ATTR_IFINDEX as self MLD
+ *	address. User space and host driver to use MLD addresses in RA, TA and
+ *	BSSID fields of the frames between them, and host driver translates the
+ *	MLD addresses to/from link addresses based on the link chosen for the
+ *	authentication.
+ *
  *	Host driver reports this status on an authentication failure to the
  *	user space through the connect result as the user space would have
  *	initiated the connection through the connect request.
@@ -1237,6 +1289,26 @@
  *      to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
  *      specify the timeout value.
  *
+ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
+ *	%NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
+ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
+ *	without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
+ *	in preparation for e.g. roaming to a regular (non-MLO) AP.
+ *
+ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
+ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
+ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
+ *
+ * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing
+ *	measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC
+ *	is included, enable/disable HW timestamping only for frames to/from the
+ *	specified MAC address. Otherwise enable/disable HW timestamping for
+ *	all TM/FTM frames (including ones that were enabled with specific MAC
+ *	address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable
+ *	HW timestamping.
+ *	The number of peers that HW timestamping can be enabled for concurrently
+ *	is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1481,6 +1553,15 @@
 
 	NL80211_CMD_ASSOC_COMEBACK,
 
+	NL80211_CMD_ADD_LINK,
+	NL80211_CMD_REMOVE_LINK,
+
+	NL80211_CMD_ADD_LINK_STA,
+	NL80211_CMD_MODIFY_LINK_STA,
+	NL80211_CMD_REMOVE_LINK_STA,
+
+	NL80211_CMD_SET_HW_TIMESTAMP,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2340,8 +2421,10 @@
  *
  * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
  *	%NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
- *	%NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
- *	interface type.
+ *	%NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
+ *	other interface-type specific capabilities per interface type. For MLO,
+ *	%NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
+ *	present.
  *
  * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
  *	groupID for monitor mode.
@@ -2663,6 +2746,54 @@
  *	association request when used with NL80211_CMD_NEW_STATION). Can be set
  *	only if %NL80211_STA_FLAG_WME is set.
  *
+ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
+ *	various commands that need a link ID to operate.
+ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
+ *	per-link information and a link ID.
+ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
+ *	authenticate/associate.
+ *
+ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
+ *	connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
+ *	included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
+ *
+ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
+ *	AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
+ *	%NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
+ *	attribute is not present userspace shall consider maximum number of AKM
+ *	suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
+ *	number prior to the introduction of this attribute.
+ *
+ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
+ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
+ *
+ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
+ *	nanoseconds (u64). This is the device clock timestamp so it will
+ *	probably reset when the device is stopped or the firmware is reset.
+ *	When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
+ *	timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ *	the ack TX timestamp.
+ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
+ *	nanoseconds (u64). This is the device clock timestamp so it will
+ *	probably reset when the device is stopped or the firmware is reset.
+ *	When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
+ *	timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ *	the incoming frame RX timestamp.
+ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
+ *	(re)associations.
+ *
+ * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest
+ *	bit corresponds to the lowest 20 MHz channel. Each bit set to 1
+ *	indicates that the sub-channel is punctured. Higher 16 bits are
+ *	reserved.
+ *
+ * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW
+ *	timestamping can be enabled for concurrently (u16), a wiphy attribute.
+ *	A value of 0xffff indicates setting for all peers (i.e. not specifying
+ *	an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported.
+ * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should
+ *	be enabled or not (flag attribute).
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3175,6 +3306,28 @@
 
 	NL80211_ATTR_EHT_CAPABILITY,
 
+	NL80211_ATTR_DISABLE_EHT,
+
+	NL80211_ATTR_MLO_LINKS,
+	NL80211_ATTR_MLO_LINK_ID,
+	NL80211_ATTR_MLD_ADDR,
+
+	NL80211_ATTR_MLO_SUPPORT,
+
+	NL80211_ATTR_MAX_NUM_AKM_SUITES,
+
+	NL80211_ATTR_EML_CAPABILITY,
+	NL80211_ATTR_MLD_CAPA_AND_OPS,
+
+	NL80211_ATTR_TX_HW_TIMESTAMP,
+	NL80211_ATTR_RX_HW_TIMESTAMP,
+	NL80211_ATTR_TD_BITMAP,
+
+	NL80211_ATTR_PUNCT_BITMAP,
+
+	NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
+	NL80211_ATTR_HW_TIMESTAMP_ENABLED,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3229,6 +3382,11 @@
 #define NL80211_HE_MIN_CAPABILITY_LEN           16
 #define NL80211_HE_MAX_CAPABILITY_LEN           54
 #define NL80211_MAX_NR_CIPHER_SUITES		5
+
+/*
+ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
+ * present in %NL80211_CMD_GET_WIPHY response.
+ */
 #define NL80211_MAX_NR_AKM_SUITES		2
 #define NL80211_EHT_MIN_CAPABILITY_LEN          13
 #define NL80211_EHT_MAX_CAPABILITY_LEN          51
@@ -4851,6 +5009,8 @@
  *	Contains a nested array of signal strength attributes (u8, dBm),
  *	using the nesting index as the antenna number.
  * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -4876,6 +5036,8 @@
 	NL80211_BSS_PARENT_BSSID,
 	NL80211_BSS_CHAIN_SIGNAL,
 	NL80211_BSS_FREQUENCY_OFFSET,
+	NL80211_BSS_MLO_LINK_ID,
+	NL80211_BSS_MLD_ADDR,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -5755,6 +5917,7 @@
 #define NL80211_KEK_LEN			16
 #define NL80211_KCK_EXT_LEN		24
 #define NL80211_KEK_EXT_LEN		32
+#define NL80211_KCK_EXT_LEN_32		32
 #define NL80211_REPLAY_CTR_LEN		8
 
 /**
@@ -5872,7 +6035,7 @@
  * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
  *	the connected inactive stations in AP mode.
  * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
- *	to work properly to suppport receiving regulatory hints from
+ *	to work properly to support receiving regulatory hints from
  *	cellular base stations.
  * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
  *	here to reserve the value for API/ABI compatibility)
@@ -6172,6 +6335,23 @@
  * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
  *	detection.
  *
+ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
+ *	change without having to bring the underlying network device down
+ *	first. For example, in station mode this can be used to vary the
+ *	origin MAC address prior to a connection to a new AP for privacy
+ *	or other reasons. Note that certain driver specific restrictions
+ *	might apply, e.g. no scans in progress, no offchannel operations
+ *	in progress, and no active connections.
+ *
+ * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables
+ *	authentication, data encryption and message integrity.
+ *
+ * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA
+ *	in authentication and deauthentication frames sent to unassociated peer
+ *	using @NL80211_CMD_FRAME.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -6239,6 +6419,10 @@
 	NL80211_EXT_FEATURE_BSS_COLOR,
 	NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
 	NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+	NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
+	NL80211_EXT_FEATURE_PUNCT,
+	NL80211_EXT_FEATURE_SECURE_NAN,
+	NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -6353,8 +6537,14 @@
  * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
  *	%NL80211_ATTR_SCAN_FREQ_KHZ. This also means
  *	%NL80211_ATTR_SCAN_FREQUENCIES will not be included.
- * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by
- *	2.4/5 GHz APs
+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by
+ *	2.4/5 GHz APs. When the flag is set, the scan logic will use the
+ *	information from the RNR element found in beacons/probe responses
+ *	received on the 2.4/5 GHz channels to actively scan only the 6GHz
+ *	channels on which APs are expected to be found. Note that when not set,
+ *	the scan logic would scan all 6GHz channels, but since transmission of
+ *	probe requests on non PSC channels is limited, it is highly likely that
+ *	these channels would passively be scanned.
  */
 enum nl80211_scan_flags {
 	NL80211_SCAN_FLAG_LOW_PRIORITY				= 1<<0,
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 0e3a7b2..ff8ad8d 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -356,9 +356,19 @@
 		return -1;
 	}
 	eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
-	crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(peer_scalar, cruft, order_len,
+				 order_len) < 0) {
+		os_free(cruft);
+		return -1;
+	}
+
 	eap_pwd_h_update(hash, cruft, order_len);
-	crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(server_scalar, cruft, order_len,
+				 order_len) < 0) {
+		os_free(cruft);
+		return -1;
+	}
+
 	eap_pwd_h_update(hash, cruft, order_len);
 	eap_pwd_h_final(hash, &session_id[1]);
 
@@ -368,7 +378,12 @@
 		os_free(cruft);
 		return -1;
 	}
-	crypto_bignum_to_bin(k, cruft, prime_len, prime_len);
+
+	if (crypto_bignum_to_bin(k, cruft, prime_len, prime_len) < 0) {
+		os_free(cruft);
+		return -1;
+	}
+
 	eap_pwd_h_update(hash, cruft, prime_len);
 	os_free(cruft);
 	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index 8ee9e32..a4256e2 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -164,26 +164,33 @@
 
 	os_memset(attr, 0, sizeof(*attr));
 	while (pos < end) {
+		u8 attr_id, attr_len;
+
 		if (end - pos < 2) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
 			return -1;
 		}
 
-		if (pos[1] < 2) {
-			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
-				   "length (%d)", pos[1]);
+		attr_id = *pos++;
+		attr_len = *pos++;
+		/* Attribute length value includes the Type and Length fields */
+		if (attr_len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-SAKE: Invalid attribute length (%d)",
+				   attr_len);
 			return -1;
 		}
+		attr_len -= 2;
 
-		if (pos + pos[1] > end) {
+		if (attr_len > end - pos) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
 			return -1;
 		}
 
-		if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2))
+		if (eap_sake_parse_add_attr(attr, attr_id, attr_len, pos))
 			return -1;
 
-		pos += pos[1];
+		pos += attr_len;
 	}
 
 	return 0;
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 269d719..8338c47 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -267,6 +267,7 @@
 	sm->reauthInit = false;
 	sm->erp_seq = (u32) -1;
 	sm->use_machine_cred = 0;
+	sm->eap_fast_mschapv2 = false;
 }
 
 
@@ -1707,7 +1708,7 @@
 		identity_len = config->machine_identity_len;
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using machine identity",
 				  identity, identity_len);
-	} else if (config->imsi_privacy_key && config->identity &&
+	} else if (config->imsi_privacy_cert && config->identity &&
 		   config->identity_len > 0) {
 		const u8 *pos = config->identity;
 		const u8 *end = config->identity + config->identity_len;
@@ -1731,6 +1732,9 @@
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP: using IMSI privacy anonymous identity",
 				  identity, identity_len);
+	} else if (config->strict_conservative_peer_mode) {
+		wpa_printf(MSG_DEBUG, "EAP: never use real identity in conservative peer mode.");
+		return NULL;
 	} else {
 		identity = config->identity;
 		identity_len = config->identity_len;
@@ -2193,6 +2197,15 @@
 	os_free(hash_hex);
 }
 
+ssize_t tls_certificate_callback(void* ctx, const char* alias, uint8_t** value) {
+	if (alias == NULL || ctx == NULL || value == NULL) return -1;
+	struct eap_sm *sm = (struct eap_sm*) ctx;
+	wpa_printf(MSG_INFO, "tls_certificate_callback: received sm=%p", (void*)sm);
+	if (sm->eapol_cb && sm->eapol_cb->get_certificate) {
+		return sm->eapol_cb->get_certificate(sm->eapol_ctx, alias, value);
+	}
+	return -1;
+}
 
 /**
  * eap_peer_sm_init - Allocate and initialize EAP peer state machine
@@ -2216,6 +2229,7 @@
 	struct tls_config tlsconf;
 
 	sm = os_zalloc(sizeof(*sm));
+	wpa_printf(MSG_INFO, "Init sm=%p", (void*)sm);
 	if (sm == NULL)
 		return NULL;
 	sm->eapol_ctx = eapol_ctx;
@@ -2236,6 +2250,7 @@
 	tlsconf.event_cb = eap_peer_sm_tls_event;
 	tlsconf.cb_ctx = sm;
 	tlsconf.cert_in_cb = conf->cert_in_cb;
+	tls_register_cert_callback(&tls_certificate_callback);
 	sm->ssl_ctx = tls_init(&tlsconf);
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
@@ -2264,6 +2279,7 @@
  */
 void eap_peer_sm_deinit(struct eap_sm *sm)
 {
+	wpa_printf(MSG_INFO, "Deinit sm=%p", (void*)sm);
 	if (sm == NULL)
 		return;
 	eap_deinit_prev_method(sm, "EAP deinit");
@@ -2816,6 +2832,24 @@
 	return config->identity;
 }
 
+
+/**
+ * eap_get_config_strict_conservative_peer_mode - get the value of
+ * strict conservative peer mode in eap_peer_config.
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+*/
+int eap_get_config_strict_conservative_peer_mode(struct eap_sm *sm)
+{
+	struct eap_peer_config *config;
+	config = eap_get_config(sm);
+	if (config) {
+		return config->strict_conservative_peer_mode;
+	}
+
+	return 0;
+}
+
+
 static const u8 * strnchr(const u8 *str, size_t len, u8 needle) {
 	const u8 *cur = str;
 
@@ -3080,6 +3114,19 @@
 	return sm ? sm->eapKeyAvailable : 0;
 }
 
+/**
+ * eap_notify_permanent_id_req_denied - Notify that the AT_PERMANENT_ID_REQ
+ * is denied from eap_peer when the strict conservative mode is enabled.
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+*/
+void eap_notify_permanent_id_req_denied(struct eap_sm *sm)
+{
+	if (!sm || !sm->eapol_cb->notify_permanent_id_req_denied)
+		return;
+
+	sm->eapol_cb->notify_permanent_id_req_denied(sm->eapol_ctx);
+}
+
 
 /**
  * eap_notify_success - Notify EAP state machine about external success trigger
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index aae1a41..b98e878 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -233,6 +233,15 @@
 			    const char *cert_hash);
 
 	/**
+	 * notify_permanent_id_req_denied - Notify that the
+	 * AT_PERMANENT_ID_REQ from the server was denied. This
+	 * notification happens when the peer is in strict
+	 * conservative mode.
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	*/
+	void (*notify_permanent_id_req_denied)(void* ctx);
+
+	/**
 	 * notify_status - Notification of the current EAP state
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 * @status: Step in the process of EAP authentication
@@ -295,6 +304,16 @@
 	 * @reason_string: Information to log about the event
 	 */
 	void (*notify_open_ssl_failure)(void *ctx, const char* reason_string);
+
+	/**
+	 * get_certificate - Retrieve a certificate from the certificate store
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @alias: key into the certificate key-value store
+	 * @value: pointer reference - pointer to the retrieved certificate will
+	 *         be stored here on success
+	 * Returns: size of the retrieved certificate or -1 on error
+	 */
+	ssize_t (*get_certificate)(void* ctx, const char* alias, uint8_t** value);
 };
 
 /**
@@ -366,11 +385,13 @@
 void eap_set_force_disabled(struct eap_sm *sm, int disabled);
 void eap_set_external_sim(struct eap_sm *sm, int external_sim);
 int eap_key_available(struct eap_sm *sm);
+void eap_notify_permanent_id_req_denied(struct eap_sm *sm);
 void eap_notify_success(struct eap_sm *sm);
 void eap_notify_lower_layer_success(struct eap_sm *sm);
 const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
 struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+int eap_get_config_strict_conservative_peer_mode(struct eap_sm *sm);
 void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
 void eap_invalidate_cached_session(struct eap_sm *sm);
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index e721efe..49338cf 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -103,20 +103,20 @@
 
 	data->eap_method = EAP_TYPE_AKA;
 
-	if (config && config->imsi_privacy_key) {
+	if (config && config->imsi_privacy_cert) {
 #ifdef CRYPTO_RSA_OAEP_SHA256
 		data->imsi_privacy_key = crypto_rsa_key_read(
-			config->imsi_privacy_key, false);
+			config->imsi_privacy_cert, false);
 		if (!data->imsi_privacy_key) {
 			wpa_printf(MSG_ERROR,
-				   "EAP-AKA: Failed to read/parse IMSI privacy key %s",
-				   config->imsi_privacy_key);
+				   "EAP-AKA: Failed to read/parse IMSI privacy certificate %s",
+				   config->imsi_privacy_cert);
 			os_free(data);
 			return NULL;
 		}
 #else /* CRYPTO_RSA_OAEP_SHA256 */
 		wpa_printf(MSG_ERROR,
-			   "EAP-AKA: No support for imsi_privacy_key in the build");
+			   "EAP-AKA: No support for imsi_privacy_cert in the build");
 		os_free(data);
 		return NULL;
 #endif /* CRYPTO_RSA_OAEP_SHA256 */
@@ -639,11 +639,12 @@
 #ifdef CRYPTO_RSA_OAEP_SHA256
 static struct wpabuf *
 eap_aka_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key,
-			 const u8 *identity, size_t identity_len)
+			 const u8 *identity, size_t identity_len,
+			 const char *attr)
 {
 	struct wpabuf *imsi_buf, *enc;
 	char *b64;
-	size_t b64_len;
+	size_t b64_len, len;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypt permanent identity",
 			  identity, identity_len);
@@ -661,7 +662,10 @@
 	if (!b64)
 		return NULL;
 
-	enc = wpabuf_alloc(1 + b64_len);
+	len = 1 + b64_len;
+	if (attr)
+		len += 1 + os_strlen(attr);
+	enc = wpabuf_alloc(len);
 	if (!enc) {
 		os_free(b64);
 		return NULL;
@@ -669,6 +673,10 @@
 	wpabuf_put_u8(enc, '\0');
 	wpabuf_put_data(enc, b64, b64_len);
 	os_free(b64);
+	if (attr) {
+		wpabuf_put_u8(enc, ',');
+		wpabuf_put_str(enc, attr);
+	}
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypted permanent identity",
 			  wpabuf_head(enc), wpabuf_len(enc));
 
@@ -700,6 +708,12 @@
 		identity_len = data->pseudonym_len;
 		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
+		if (id_req == PERMANENT_ID && eap_get_config_strict_conservative_peer_mode(sm)) {
+			wpa_printf(MSG_INFO, "EAP-AKA: permanent_id_req is denied in "
+				   "the strict conservative peer mode");
+			eap_notify_permanent_id_req_denied(sm);
+			return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+		}
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
@@ -712,9 +726,15 @@
 		}
 #ifdef CRYPTO_RSA_OAEP_SHA256
 		if (identity && data->imsi_privacy_key) {
+			struct eap_peer_config *config;
+			const char *attr = NULL;
+
+			config = eap_get_config(sm);
+			if (config)
+				attr = config->imsi_privacy_attr;
 			enc_identity = eap_aka_encrypt_identity(
 				data->imsi_privacy_key,
-				identity, identity_len);
+				identity, identity_len, attr);
 			if (!enc_identity) {
 				wpa_printf(MSG_INFO,
 					   "EAP-AKA: Failed to encrypt permanent identity");
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index eaf514b..59fac90 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -318,14 +318,36 @@
 	size_t imsi_identity_len;
 
 	/**
-	 * imsi_privacy_key - IMSI privacy key (PEM encoded X.509v3 certificate)
+	 * imsi_privacy_cert - IMSI privacy certificate
 	 *
 	 * This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
-	 * identity (IMSI) to improve privacy. The X.509v3 certificate needs to
-	 * include a 2048-bit RSA public key and this is from the operator who
-	 * authenticates the SIM/USIM.
+	 * identity (IMSI) to improve privacy. The referenced PEM-encoded
+	 * X.509v3 certificate needs to include a 2048-bit RSA public key and
+	 * this is from the operator who authenticates the SIM/USIM.
 	 */
-	char *imsi_privacy_key;
+	char *imsi_privacy_cert;
+
+	/**
+	 * imsi_privacy_attr - IMSI privacy attribute
+	 *
+	 * This field is used to help the EAP-SIM/AKA/AKA' server to identify
+	 * the used certificate (and as such, the matching private key). This
+	 * is set to an attribute in name=value format if the operator needs
+	 * this information.
+	 */
+	char *imsi_privacy_attr;
+
+	/**
+	 * strict_conservative_peer_mode - Whether the strict conservative peer
+	 * mode is enabled or not
+	 *
+	 * This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+	 * for EAP-SIM/AKA/AKA', in conservative peer mode, a client error would
+	 * be sent to the server, but it allows to send the permanent identity
+	 * in some special cases according to 4.6.2 of RFC 4187; With the strict
+	 * mode, it never sends the permanent identity to server for privacy concern.
+	 */
+	int strict_conservative_peer_mode;
 
 	/**
 	 * machine_identity - EAP Identity for machine credential
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index b12cfee..d7677ca 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -354,6 +354,7 @@
 		sm->auth_challenge = data->key_block_p->server_challenge;
 		sm->peer_challenge = data->key_block_p->client_challenge;
 	}
+	sm->eap_fast_mschapv2 = true;
 	sm->init_phase2 = 1;
 	data->phase2_priv = data->phase2_method->init(sm);
 	sm->init_phase2 = 0;
@@ -693,18 +694,7 @@
 
 	if (key_len > isk_len)
 		key_len = isk_len;
-	if (key_len == 32 &&
-	    data->phase2_method->vendor == EAP_VENDOR_IETF &&
-	    data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
-		/*
-		 * EAP-FAST uses reverse order for MS-MPPE keys when deriving
-		 * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
-		 * ISK for EAP-FAST cryptobinding.
-		 */
-		os_memcpy(isk, key + 16, 16);
-		os_memcpy(isk + 16, key, 16);
-	} else
-		os_memcpy(isk, key, key_len);
+	os_memcpy(isk, key, key_len);
 	os_free(key);
 
 	return 0;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 652b67e..3fe5f77 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -365,6 +365,11 @@
 	/* Optional challenges generated in Phase 1 (EAP-FAST) */
 	u8 *peer_challenge, *auth_challenge;
 
+	/* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+	 * That variant is otherwise identical, but it generates the MSK using
+	 * MS-MPPE keys in reverse order. */
+	bool eap_fast_mschapv2;
+
 	int num_rounds;
 	int num_rounds_short;
 	int force_disabled;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 8ad4d18..e84ae16 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -108,6 +108,12 @@
 	if (data == NULL)
 		return NULL;
 
+	wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+		   sm->eap_fast_mschapv2 ? "FAST-" : "",
+		   sm->peer_challenge && sm->auth_challenge ?
+		   " with preset challenges" : "",
+		   sm->init_phase2 ? " for Phase 2" : "");
+
 	if (sm->peer_challenge) {
 		data->peer_challenge = os_memdup(sm->peer_challenge,
 						 MSCHAPV2_CHAL_LEN);
@@ -844,6 +850,7 @@
 	struct eap_mschapv2_data *data = priv;
 	u8 *key;
 	int key_len;
+	bool first_is_send;
 
 	if (!data->master_key_valid || !data->success)
 		return NULL;
@@ -854,12 +861,25 @@
 	if (key == NULL)
 		return NULL;
 
-	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
-	 *	peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
-	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
-				    0) < 0 ||
+	/*
+	 * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+	 * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+	 * On a Peer:
+	 * MS-MPPE-Recv-Key = MasterSendKey
+	 * MS-MPPE-Send-Key = MasterReceiveKey
+	 *
+	 * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+	 * MSK = MasterSendKey + MasterReceiveKey
+	 * (i.e., reverse order and no padding)
+	 *
+	 * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+	 * starts with Receive key.
+	 */
+	first_is_send = !sm->eap_fast_mschapv2;
+	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+				    first_is_send, 0) < 0 ||
 	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
-				    MSCHAPV2_KEY_LEN, 0, 0) < 0) {
+				    MSCHAPV2_KEY_LEN, !first_is_send, 0) < 0) {
 		os_free(key);
 		return NULL;
 	}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 605feb2..97f4dd2 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -666,7 +666,10 @@
 	 * sufficiently smaller than the prime or order might need pre-pending
 	 * with zeros.
 	 */
-	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
 				   element + prime_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
@@ -742,7 +745,9 @@
 	 * zero the memory each time because this is mod prime math and some
 	 * value may start with a few zeros and the previous one did not.
 	 */
-	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, prime_len);
 
 	/* server element: x, y */
@@ -755,7 +760,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* server scalar */
-	crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* my element: x, y */
@@ -768,7 +776,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* my scalar */
-	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* the ciphersuite */
@@ -796,7 +807,9 @@
 		goto fin;
 
 	/* k */
-	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, prime_len);
 
 	/* my element */
@@ -809,7 +822,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* my scalar */
-	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* server element: x, y */
@@ -822,7 +838,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* server scalar */
-	crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->server_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* the ciphersuite */
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 954c585..6f18ebf 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -101,20 +101,20 @@
 		return NULL;
 	}
 
-	if (config && config->imsi_privacy_key) {
+	if (config && config->imsi_privacy_cert) {
 #ifdef CRYPTO_RSA_OAEP_SHA256
 		data->imsi_privacy_key = crypto_rsa_key_read(
-			config->imsi_privacy_key, false);
+			config->imsi_privacy_cert, false);
 		if (!data->imsi_privacy_key) {
 			wpa_printf(MSG_ERROR,
-				   "EAP-SIM: Failed to read/parse IMSI privacy key %s",
-				   config->imsi_privacy_key);
+				   "EAP-SIM: Failed to read/parse IMSI privacy certificate %s",
+				   config->imsi_privacy_cert);
 			os_free(data);
 			return NULL;
 		}
 #else /* CRYPTO_RSA_OAEP_SHA256 */
 		wpa_printf(MSG_ERROR,
-			   "EAP-SIM: No support for imsi_privacy_key in the build");
+			   "EAP-SIM: No support for imsi_privacy_cert in the build");
 		os_free(data);
 		return NULL;
 #endif /* CRYPTO_RSA_OAEP_SHA256 */
@@ -133,6 +133,9 @@
 					   "sim_min_num_chal configuration "
 					   "(%lu, expected 2 or 3)",
 					   (unsigned long) data->min_num_chal);
+#ifdef CRYPTO_RSA_OAEP_SHA256
+				crypto_rsa_key_free(data->imsi_privacy_key);
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 				os_free(data);
 				return NULL;
 			}
@@ -504,11 +507,12 @@
 #ifdef CRYPTO_RSA_OAEP_SHA256
 static struct wpabuf *
 eap_sim_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key,
-			 const u8 *identity, size_t identity_len)
+			 const u8 *identity, size_t identity_len,
+			 const char *attr)
 {
 	struct wpabuf *imsi_buf, *enc;
 	char *b64;
-	size_t b64_len;
+	size_t b64_len, len;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypt permanent identity",
 			  identity, identity_len);
@@ -526,7 +530,10 @@
 	if (!b64)
 		return NULL;
 
-	enc = wpabuf_alloc(1 + b64_len);
+	len = 1 + b64_len;
+	if (attr)
+		len += 1 + os_strlen(attr);
+	enc = wpabuf_alloc(len);
 	if (!enc) {
 		os_free(b64);
 		return NULL;
@@ -534,6 +541,10 @@
 	wpabuf_put_u8(enc, '\0');
 	wpabuf_put_data(enc, b64, b64_len);
 	os_free(b64);
+	if (attr) {
+		wpabuf_put_u8(enc, ',');
+		wpabuf_put_str(enc, attr);
+	}
 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypted permanent identity",
 			  wpabuf_head(enc), wpabuf_len(enc));
 
@@ -565,6 +576,12 @@
 		identity_len = data->pseudonym_len;
 		eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
 	} else if (id_req != NO_ID_REQ) {
+		if (id_req == PERMANENT_ID && eap_get_config_strict_conservative_peer_mode(sm)) {
+			wpa_printf(MSG_INFO, "EAP-SIM: permanent_id_req is denied in "
+				   "the strict conservative peer mode");
+			eap_notify_permanent_id_req_denied(sm);
+			return  eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+		}
 		identity = eap_get_config_identity(sm, &identity_len);
 		if (identity) {
 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
@@ -577,9 +594,15 @@
 		}
 #ifdef CRYPTO_RSA_OAEP_SHA256
 		if (identity && data->imsi_privacy_key) {
+			struct eap_peer_config *config;
+			const char *attr = NULL;
+
+			config = eap_get_config(sm);
+			if (config)
+				attr = config->imsi_privacy_attr;
 			enc_identity = eap_sim_encrypt_identity(
 				data->imsi_privacy_key,
-				identity, identity_len);
+				identity, identity_len, attr);
 			if (!enc_identity) {
 				wpa_printf(MSG_INFO,
 					   "EAP-SIM: Failed to encrypt permanent identity");
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index bc7f6f4..ced7b16 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -319,6 +319,13 @@
 	if (!data->phase2_method)
 		return -1;
 
+	/* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+	 * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+	 * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+	 * to interoperate.
+	 */
+	sm->eap_fast_mschapv2 = true;
+
 	sm->init_phase2 = 1;
 	data->phase2_priv = data->phase2_method->init(sm);
 	sm->init_phase2 = 0;
@@ -1298,6 +1305,33 @@
 		goto done;
 	}
 
+	if (tlv.crypto_binding) {
+		if (tlv.iresult != TEAP_STATUS_SUCCESS &&
+		    tlv.result != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
+			failed = 1;
+			error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
+			goto done;
+		}
+
+		tmp = eap_teap_process_crypto_binding(sm, data, ret,
+						      tlv.crypto_binding,
+						      tlv.crypto_binding_len);
+		if (!tmp) {
+			failed = 1;
+			error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
+		} else {
+			resp = wpabuf_concat(resp, tmp);
+			if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
+				data->result_success_done = 1;
+			if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
+				data->inner_method_done = 0;
+				data->iresult_verified = 1;
+			}
+		}
+	}
+
 	if (tlv.identity_type == TEAP_IDENTITY_TYPE_MACHINE) {
 		struct eap_peer_config *config = eap_get_config(sm);
 
@@ -1353,33 +1387,6 @@
 		}
 	}
 
-	if (tlv.crypto_binding) {
-		if (tlv.iresult != TEAP_STATUS_SUCCESS &&
-		    tlv.result != TEAP_STATUS_SUCCESS) {
-			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
-			failed = 1;
-			error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
-			goto done;
-		}
-
-		tmp = eap_teap_process_crypto_binding(sm, data, ret,
-						      tlv.crypto_binding,
-						      tlv.crypto_binding_len);
-		if (!tmp) {
-			failed = 1;
-			error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
-		} else {
-			resp = wpabuf_concat(resp, tmp);
-			if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
-				data->result_success_done = 1;
-			if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
-				data->inner_method_done = 0;
-				data->iresult_verified = 1;
-			}
-		}
-	}
-
 	if (data->result_success_done && data->session_ticket_used &&
 	    eap_teap_derive_msk(data) == 0) {
 		/* Assume the server might accept authentication without going
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 2894cfb..3696e1d 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -209,6 +209,7 @@
 		EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4,
 		EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5,
 	} eap_teap_id;
+	int eap_teap_method_sequence;
 
 	/**
 	 * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 28bb564..1c59bb0 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -176,9 +176,15 @@
 		METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
 	} method_pending;
 
+	/* Optional challenges generated in Phase 1 (EAP-FAST) */
 	u8 *auth_challenge;
 	u8 *peer_challenge;
 
+	/* Whether to use the EAP-FAST-MSCHAPv2 instantiation of EAP-MSCHAPv2.
+	 * That variant is otherwise identical, but it generates the MSK using
+	 * MS-MPPE keys in reverse order. */
+	bool eap_fast_mschapv2;
+
 	struct wpabuf *assoc_wps_ie;
 	struct wpabuf *assoc_p2p_ie;
 
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 55d48d9..0596334 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -357,18 +357,7 @@
 
 	if (key_len > isk_len)
 		key_len = isk_len;
-	if (key_len == 32 &&
-	    data->phase2_method->vendor == EAP_VENDOR_IETF &&
-	    data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
-		/*
-		 * EAP-FAST uses reverse order for MS-MPPE keys when deriving
-		 * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
-		 * ISK for EAP-FAST cryptobinding.
-		 */
-		os_memcpy(isk, key + 16, 16);
-		os_memcpy(isk + 16, key, 16);
-	} else
-		os_memcpy(isk, key, key_len);
+	os_memcpy(isk, key, key_len);
 	os_free(key);
 
 	return 0;
@@ -961,6 +950,7 @@
 		sm->auth_challenge = data->key_block_p->server_challenge;
 		sm->peer_challenge = data->key_block_p->client_challenge;
 	}
+	sm->eap_fast_mschapv2 = true;
 	sm->init_phase2 = 1;
 	data->phase2_priv = data->phase2_method->init(sm);
 	sm->init_phase2 = 0;
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 9b3eb26..3dc6a39 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -64,6 +64,12 @@
 		return NULL;
 	data->state = CHALLENGE;
 
+	wpa_printf(MSG_DEBUG, "EAP-%sMSCHAPv2 init%s%s",
+		   sm->eap_fast_mschapv2 ? "FAST-" : "",
+		   sm->peer_challenge && sm->auth_challenge ?
+		   " with preset challenges" : "",
+		   sm->init_phase2 ? " for Phase 2" : "");
+
 	if (sm->auth_challenge) {
 		os_memcpy(data->auth_challenge, sm->auth_challenge,
 			  CHALLENGE_LEN);
@@ -542,6 +548,7 @@
 {
 	struct eap_mschapv2_data *data = priv;
 	u8 *key;
+	bool first_is_send;
 
 	if (data->state != SUCCESS || !data->master_key_valid)
 		return NULL;
@@ -550,11 +557,26 @@
 	key = os_malloc(*len);
 	if (key == NULL)
 		return NULL;
+	/*
+	 * [MS-CHAP], 3.1.5.1 (Master Session Key (MSK) Derivation
+	 * MSK = MasterReceiveKey + MasterSendKey + 32 bytes zeros (padding)
+	 * On an Authenticator:
+	 * MS-MPPE-Recv-Key = MasterReceiveKey
+	 * MS-MPPE-Send-Key = MasterSendKey
+	 *
+	 * RFC 5422, 3.2.3 (Authenticating Using EAP-FAST-MSCHAPv2)
+	 * MSK = MasterSendKey + MasterReceiveKey
+	 * (i.e., reverse order and no padding)
+	 *
+	 * On Peer, EAP-MSCHAPv2 starts with Send key and EAP-FAST-MSCHAPv2
+	 * starts with Receive key.
+	 */
+	first_is_send = sm->eap_fast_mschapv2;
 	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
-	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
-				    1) < 0 ||
+	if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN,
+				    first_is_send, 1) < 0 ||
 	    get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
-				    MSCHAPV2_KEY_LEN, 1, 1) < 0) {
+				    MSCHAPV2_KEY_LEN, !first_is_send, 1) < 0) {
 		os_free(key);
 		return NULL;
 	}
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 81cddca..afafaef 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -293,7 +293,10 @@
 	/* We send the element as (x,y) followed by the scalar */
 	element = wpabuf_put(data->outbuf, 2 * prime_len);
 	scalar = wpabuf_put(data->outbuf, order_len);
-	crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, scalar, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
 				   element + prime_len) < 0) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
@@ -349,7 +352,9 @@
 	 *
 	 * First is k
 	 */
-	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, prime_len);
 
 	/* server element: x, y */
@@ -362,7 +367,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* server scalar */
-	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* peer element: x, y */
@@ -375,7 +383,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* peer scalar */
-	crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* ciphersuite */
@@ -785,7 +796,9 @@
 		goto fin;
 
 	/* k */
-	crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len);
+	if (crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, prime_len);
 
 	/* peer element: x, y */
@@ -798,7 +811,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* peer scalar */
-	crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->peer_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* server element: x, y */
@@ -811,7 +827,10 @@
 	eap_pwd_h_update(hash, cruft, prime_len * 2);
 
 	/* server scalar */
-	crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len);
+	if (crypto_bignum_to_bin(data->my_scalar, cruft, order_len,
+				 order_len) < 0)
+		goto fin;
+
 	eap_pwd_h_update(hash, cruft, order_len);
 
 	/* ciphersuite */
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index 691b44a..e32c6e4 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -74,11 +74,15 @@
 
 	enum teap_error_codes error_code;
 	enum teap_identity_types cur_id_type;
+
+	bool check_crypto_binding;
 };
 
 
 static int eap_teap_process_phase2_start(struct eap_sm *sm,
 					 struct eap_teap_data *data);
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+				int vendor, enum eap_type eap_type);
 
 
 static const char * eap_teap_state_txt(int state)
@@ -704,6 +708,8 @@
 	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
 		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
 
+	data->check_crypto_binding = true;
+
 	return buf;
 }
 
@@ -889,6 +895,7 @@
 	struct eap_teap_data *data = priv;
 	struct wpabuf *req = NULL;
 	int piggyback = 0;
+	bool move_to_method = true;
 
 	if (data->ssl.state == FRAG_ACK) {
 		return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
@@ -940,6 +947,21 @@
 		break;
 	case CRYPTO_BINDING:
 		req = eap_teap_build_crypto_binding(sm, data);
+		if (req && sm->cfg->eap_teap_auth == 0 &&
+		    data->inner_eap_not_done &&
+		    !data->phase2_method &&
+		    sm->cfg->eap_teap_method_sequence == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Continue with inner EAP authentication for second credential (optimized)");
+			eap_teap_state(data, PHASE2_ID);
+			if (eap_teap_phase2_init(sm, data, EAP_VENDOR_IETF,
+						 EAP_TYPE_IDENTITY) < 0) {
+				eap_teap_state(data, FAILURE);
+				wpabuf_free(req);
+				return NULL;
+			}
+			move_to_method = false;
+		}
 		if (data->phase2_method) {
 			/*
 			 * Include the start of the next EAP method in the
@@ -950,7 +972,8 @@
 
 			eap = eap_teap_build_phase2_req(sm, data, id);
 			req = wpabuf_concat(req, eap);
-			eap_teap_state(data, PHASE2_METHOD);
+			if (move_to_method)
+				eap_teap_state(data, PHASE2_METHOD);
 		}
 		break;
 	case REQUEST_PAC:
@@ -1008,6 +1031,13 @@
 	if (!data->phase2_method)
 		return -1;
 
+	/* While RFC 7170 does not describe this, EAP-TEAP has been deployed
+	 * with implementations that use the EAP-FAST-MSCHAPv2, instead of the
+	 * EAP-MSCHAPv2, way of deriving the MSK for IMSK. Use that design here
+	 * to interoperate.
+	 */
+	sm->eap_fast_mschapv2 = true;
+
 	sm->init_phase2 = 1;
 	data->phase2_priv = data->phase2_method->init(sm);
 	sm->init_phase2 = 0;
@@ -1503,7 +1533,8 @@
 					 struct wpabuf *in_data)
 {
 	struct eap_teap_tlv_parse tlv;
-	int check_crypto_binding = data->state == CRYPTO_BINDING;
+	bool check_crypto_binding = data->state == CRYPTO_BINDING ||
+		data->check_crypto_binding;
 
 	if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
 		wpa_printf(MSG_DEBUG,
@@ -1586,6 +1617,7 @@
 
 		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Valid Crypto-Binding TLV received");
+		data->check_crypto_binding = false;
 		if (data->final_result) {
 			wpa_printf(MSG_DEBUG,
 				   "EAP-TEAP: Authentication completed successfully");
@@ -1664,7 +1696,8 @@
 			   "EAP-TEAP: Continue with basic password authentication for second credential");
 		eap_teap_state(data, PHASE2_BASIC_AUTH);
 	} else if (check_crypto_binding && data->state == CRYPTO_BINDING &&
-		   sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done) {
+		   sm->cfg->eap_teap_auth == 0 && data->inner_eap_not_done &&
+		   sm->cfg->eap_teap_method_sequence == 1) {
 		wpa_printf(MSG_DEBUG,
 			   "EAP-TEAP: Continue with inner EAP authentication for second credential");
 		eap_teap_state(data, PHASE2_ID);
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index b893522..b21f12d 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -445,7 +445,8 @@
 			sizeof(data->mschapv2_auth_response));
 	} else {
 		pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
-				       RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
+				       RADIUS_VENDOR_ID_MICROSOFT, 1, 7);
+		*pos++ = data->mschapv2_ident;
 		os_memcpy(pos, "Failed", 6);
 		pos += 6;
 		AVP_PAD(req, pos);
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 4e66369..334ce6c 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1281,11 +1281,12 @@
  * @src: Source MAC address of the EAPOL packet
  * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
  * @len: Length of the EAPOL frame
+ * @encrypted: Whether the frame was encrypted
  * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
  * -1 failure
  */
 int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
-		      size_t len)
+		      size_t len, enum frame_encryption encrypted)
 {
 	const struct ieee802_1x_hdr *hdr;
 	const struct ieee802_1x_eapol_key *key;
@@ -1295,6 +1296,14 @@
 
 	if (sm == NULL)
 		return 0;
+
+	if (encrypted == FRAME_NOT_ENCRYPTED && sm->ctx->encryption_required &&
+	    sm->ctx->encryption_required(sm->ctx->ctx)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAPOL: Discard unencrypted EAPOL frame when encryption since encryption was expected");
+		return 0;
+	}
+
 	sm->dot1xSuppEapolFramesRx++;
 	if (len < sizeof(*hdr)) {
 		sm->dot1xSuppInvalidEapolFramesRx++;
@@ -2022,6 +2031,13 @@
 		sm->ctx->cert_cb(sm->ctx->ctx, cert, cert_hash);
 }
 
+static void eapol_sm_notify_permanent_id_req_denied(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+	if (sm->ctx->permanent_id_req_denied_cb)
+		sm->ctx->permanent_id_req_denied_cb(sm->ctx->ctx);
+}
+
 
 static void eapol_sm_notify_status(void *ctx, const char *status,
 				   const char *parameter)
@@ -2096,6 +2112,18 @@
 		sm->ctx->open_ssl_failure_cb(sm->ctx->ctx, reason_string);
 }
 
+static ssize_t
+eapol_sm_get_certificate(void *ctx, const char* alias, uint8_t** value)
+{
+	struct eapol_sm *sm = ctx;
+	wpa_printf(MSG_INFO, "eapol_sm_get_certificate");
+
+	if (sm->ctx->get_certificate_cb) {
+		return sm->ctx->get_certificate_cb(alias, value);
+	}
+	return -1;
+}
+
 static const struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
@@ -2109,6 +2137,7 @@
 	eapol_sm_notify_pending,
 	eapol_sm_eap_param_needed,
 	eapol_sm_notify_cert,
+	eapol_sm_notify_permanent_id_req_denied,
 	eapol_sm_notify_status,
 	eapol_sm_notify_eap_error,
 #ifdef CONFIG_EAP_PROXY
@@ -2118,7 +2147,8 @@
 #endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id,
 	eapol_sm_notify_eap_method_selected,
-	eapol_sm_notify_open_ssl_failure
+	eapol_sm_notify_open_ssl_failure,
+	eapol_sm_get_certificate
 };
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 630a38e..fe34ec9 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -255,6 +255,14 @@
 			const char *cert_hash);
 
 	/**
+	 * permanent_id_req_denied_cb - Notify that the AT_PERMANENT_ID_REQ
+	 * from the server was denied. This notification happens when the
+	 * peer is in the strict conservative mode.
+	 * @ctx: Callback context (ctx)
+	*/
+	void (*permanent_id_req_denied_cb)(void *ctx);
+
+	/**
 	 * cert_in_cb - Include server certificates in callback
 	 */
 	int cert_in_cb;
@@ -321,6 +329,22 @@
 	 * @reason_string: Information to log about the event
 	 */
 	void (*open_ssl_failure_cb)(void *ctx, const char* reason_string);
+
+	/**
+	 * encryption_required - Check whether encryption is required
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * Returns: Whether the current session requires encryption
+	 */
+	bool (*encryption_required)(void *ctx);
+
+	/**
+	 * get_certificate_cb - Retrieve a certificate from the certificate store
+	 * @alias: key into the certificate key-value store
+	 * @value: pointer reference - pointer to the retrieved certificate will
+	 *         be stored here on success
+	 * Returns: size of the retrieved certificate or -1 on error
+	 */
+	ssize_t (*get_certificate_cb)(const char* alias, uint8_t** value);
 };
 
 
@@ -337,7 +361,7 @@
 void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
 			int startPeriod, int maxStart);
 int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
-		      size_t len);
+		      size_t len, enum frame_encryption encrypted);
 void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
 void eapol_sm_notify_portEnabled(struct eapol_sm *sm, bool enabled);
 void eapol_sm_notify_portValid(struct eapol_sm *sm, bool valid);
@@ -403,7 +427,8 @@
 {
 }
 static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src,
-				    const u8 *buf, size_t len)
+				    const u8 *buf, size_t len,
+				    enum frame_encryption encrypted)
 {
 	return 0;
 }
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
index d1c4014..255d0fd 100644
--- a/src/fst/fst_group.c
+++ b/src/fst/fst_group.c
@@ -28,8 +28,13 @@
 	while (s >= 2) {
 		const struct multi_band_ie *mbie =
 			(const struct multi_band_ie *) p;
+		size_t len;
+
 		WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
 		WPA_ASSERT(2U + mbie->len >= sizeof(*mbie));
+		len = 2 + mbie->len;
+		if (len > s)
+			break;
 
 		fst_printf(MSG_WARNING,
 			   "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
@@ -45,8 +50,8 @@
 			   mbie->mb_connection_capability,
 			   mbie->fst_session_tmout);
 
-		p += 2 + mbie->len;
-		s -= 2 + mbie->len;
+		p += len;
+		s -= len;
 	}
 }
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index cd04008..a1fe121 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -441,6 +441,7 @@
 		return NULL;
 	dl_list_add(&p2p->devices, &dev->list);
 	os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+	dev->support_6ghz = false;
 
 	return dev;
 }
@@ -611,6 +612,8 @@
 		dev->info.group_capab = msg->capability[1];
 	}
 
+	p2p_update_peer_6ghz_capab(dev, msg);
+
 	if (msg->ext_listen_timing) {
 		dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
 		dev->ext_listen_interval =
@@ -634,6 +637,15 @@
 }
 
 
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+				const struct p2p_message *msg)
+{
+	if (msg->capability &&
+	    (msg->capability[0] & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE))
+		dev->support_6ghz = true;
+}
+
+
 static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
 					 size_t ies_len)
 {
@@ -1803,6 +1815,7 @@
 		os_memcpy(params->passphrase, p2p->passphrase, os_strlen(p2p->passphrase));
 	} else {
 		p2p_random(params->passphrase, p2p->cfg->passphrase_len);
+		params->passphrase[p2p->cfg->passphrase_len] = '\0';
 	}
 	p2p->passphrase_set = 0;
 	return 0;
@@ -1837,6 +1850,7 @@
 		os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
 		res.ssid_len = p2p->ssid_len;
 		p2p_random(res.passphrase, p2p->cfg->passphrase_len);
+		res.passphrase[p2p->cfg->passphrase_len] = '\0';
 	} else {
 		res.freq = peer->oper_freq;
 		if (p2p->ssid_len) {
@@ -2083,6 +2097,7 @@
 			}
 		}
 
+		p2p_update_peer_6ghz_capab(dev, &msg);
 		os_get_reltime(&dev->last_seen);
 		p2p_parse_free(&msg);
 		return; /* already known */
@@ -2945,6 +2960,14 @@
 }
 
 
+bool is_p2p_dfs_chan_enabled(struct p2p_data *p2p)
+{
+	if (p2p)
+		return p2p->cfg->p2p_dfs_chan_enable;
+	return false;
+}
+
+
 struct p2p_data * p2p_init(const struct p2p_config *cfg)
 {
 	struct p2p_data *p2p;
@@ -4098,9 +4121,11 @@
 		/*
 		 * Better remain on operating channel instead of listen channel
 		 * when running a group.
+		 * Wait 120 ms to let the P2P GO to send its beacon on the
+		 * intended TBTT.
 		 */
 		p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel");
-		p2p_set_timeout(p2p, 0, 100000);
+		p2p_set_timeout(p2p, 0, 120000);
 		return;
 	}
 	p2p_listen_in_find(p2p, 0);
@@ -5540,7 +5565,7 @@
 
 
 void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
-				const unsigned int *pref_freq_list,
+				const struct weighted_pcl *pref_freq_list,
 				unsigned int size)
 {
 	unsigned int i;
@@ -5548,10 +5573,11 @@
 	if (size > P2P_MAX_PREF_CHANNELS)
 		size = P2P_MAX_PREF_CHANNELS;
 	p2p->num_pref_freq = size;
+	os_memcpy(p2p->pref_freq_list, pref_freq_list,
+		  size * sizeof(struct weighted_pcl));
 	for (i = 0; i < size; i++) {
-		p2p->pref_freq_list[i] = pref_freq_list[i];
 		p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
-			i, p2p->pref_freq_list[i]);
+			i, p2p->pref_freq_list[i].freq);
 	}
 }
 
@@ -5603,7 +5629,7 @@
 	if (!dev)
 		return false;
 
-	return !!(dev->info.dev_capab & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE);
+	return dev->support_6ghz;
 }
 
 
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index f606fbb..7784128 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -12,6 +12,8 @@
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
 
+struct weighted_pcl;
+
 /* P2P ASP Setup Capability */
 #define P2PS_SETUP_NONE 0
 #define P2PS_SETUP_NEW BIT(0)
@@ -506,6 +508,11 @@
 	bool p2p_6ghz_disable;
 
 	/**
+	 * p2p_dfs_chan_enable - Enable p2p Go to operate on dfs channel
+	 */
+	bool p2p_dfs_chan_enable;
+
+	/**
 	 * pri_dev_type - Primary Device Type (see WPS)
 	 */
 	u8 pri_dev_type[8];
@@ -1132,7 +1139,8 @@
 	 * the driver specific to a particular interface type.
 	 */
 	int (*get_pref_freq_list)(void *ctx, int go,
-				  unsigned int *len, unsigned int *freq_list);
+				  unsigned int *len,
+				  struct weighted_pcl *freq_list);
 };
 
 
@@ -2111,6 +2119,7 @@
 			     const struct p2p_channels *cli_chan);
 
 bool is_p2p_6ghz_disabled(struct p2p_data *p2p);
+bool is_p2p_dfs_chan_enabled(struct p2p_data *p2p);
 
 /**
  * p2p_set_best_channels - Update best channel information
@@ -2338,6 +2347,8 @@
 					   const u8 *go_dev_addr,
 					   const u8 *ssid, size_t ssid_len);
 
+bool p2p_pref_freq_allowed(const struct weighted_pcl *freq_list, bool go);
+
 struct p2p_nfc_params {
 	int sel;
 	const u8 *wsc_attr;
@@ -2397,7 +2408,7 @@
 void p2p_expire_peers(struct p2p_data *p2p);
 
 void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
-				const unsigned int *pref_freq_list,
+				const struct weighted_pcl *pref_freq_list,
 				unsigned int size);
 void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
 				   u8 chan);
@@ -2422,6 +2433,7 @@
 bool p2p_wfd_enabled(struct p2p_data *p2p);
 bool is_p2p_allow_6ghz(struct p2p_data *p2p);
 void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value);
-int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size);
+int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size);
+int p2p_channel_to_freq(int op_class, int channel);
 
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 4229d9b..e4f40fe 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/qca-vendor.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
@@ -111,7 +112,7 @@
 
 
 void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
-				   const unsigned int *preferred_freq_list,
+				   const struct weighted_pcl *pref_freq_list,
 				   unsigned int size)
 {
 	unsigned int i, count = 0;
@@ -126,8 +127,9 @@
 	 * of the vendor IE size.
 	 */
 	for (i = 0; i < size; i++) {
-		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
-					&op_channel) == 0)
+		if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
+					&op_channel) == 0 &&
+		    !(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE))
 			count++;
 	}
 
@@ -136,10 +138,11 @@
 	wpabuf_put_be24(buf, OUI_QCA);
 	wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
 	for (i = 0; i < size; i++) {
-		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
-					&op_channel) < 0) {
+		if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
+					&op_channel) < 0 ||
+		    (pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) {
 			wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
-				   preferred_freq_list[i]);
+				   pref_freq_list[i].freq);
 			continue;
 		}
 		wpabuf_put_u8(buf, op_class);
@@ -149,7 +152,7 @@
 
 
 void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
-			      struct p2p_channels *chan)
+			      struct p2p_channels *chan, bool is_6ghz_capab)
 {
 	u8 *len;
 	size_t i;
@@ -161,6 +164,9 @@
 
 	for (i = 0; i < chan->reg_classes; i++) {
 		struct p2p_reg_class *c = &chan->reg_class[i];
+
+		if (is_6ghz_op_class(c->reg_class) && !is_6ghz_capab)
+			continue;
 		wpabuf_put_u8(buf, c->reg_class);
 		wpabuf_put_u8(buf, c->channels);
 		wpabuf_put_data(buf, c->channel, c->channels);
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 1d53d52..55db8a5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p_i.h"
@@ -142,6 +143,7 @@
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
+	bool is_6ghz_capab;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
@@ -179,7 +181,22 @@
 		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
 	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
-	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+		p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
+	if (p2p->num_pref_freq) {
+		bool go = p2p->go_intent == 15;
+		struct p2p_channels pref_chanlist;
+
+		p2p_pref_channel_filter(&p2p->channels, p2p->pref_freq_list,
+					p2p->num_pref_freq, &pref_chanlist, go);
+		p2p_channels_dump(p2p, "channel list after filtering",
+				  &pref_chanlist);
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &pref_chanlist, is_6ghz_capab);
+	} else {
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->channels, is_6ghz_capab);
+	}
 	p2p_buf_add_device_info(buf, p2p, peer);
 	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 				      p2p->op_reg_class, p2p->op_channel);
@@ -278,6 +295,8 @@
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
+	bool is_6ghz_capab;
+	struct p2p_channels pref_chanlist;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -328,17 +347,35 @@
 					      p2p->op_channel);
 	}
 	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	if (p2p->num_pref_freq) {
+		bool go = (peer && peer->go_state == LOCAL_GO) ||
+			p2p->go_intent == 15;
+
+		p2p_pref_channel_filter(&p2p->channels, p2p->pref_freq_list,
+					p2p->num_pref_freq, &pref_chanlist, go);
+		p2p_channels_dump(p2p, "channel list after filtering",
+				  &pref_chanlist);
+	} else {
+		p2p_copy_channels(&pref_chanlist, &p2p->channels,
+				  p2p->allow_6ghz);
+	}
 	if (status || peer == NULL) {
 		p2p_buf_add_channel_list(buf, p2p->cfg->country,
-					 &p2p->channels);
+					 &pref_chanlist, false);
 	} else if (peer->go_state == REMOTE_GO) {
+		is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+			p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
 		p2p_buf_add_channel_list(buf, p2p->cfg->country,
-					 &p2p->channels);
+					 &pref_chanlist, is_6ghz_capab);
 	} else {
 		struct p2p_channels res;
-		p2p_channels_intersect(&p2p->channels, &peer->channels,
+
+		is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+			p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
+		p2p_channels_intersect(&pref_chanlist, &peer->channels,
 				       &res);
-		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res,
+				       is_6ghz_capab);
 	}
 	p2p_buf_add_device_info(buf, p2p, peer);
 	if (peer && peer->go_state == LOCAL_GO) {
@@ -562,7 +599,8 @@
 static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
 					struct p2p_device *dev,
 					struct p2p_message *msg,
-					unsigned freq_list[], unsigned int size)
+					const struct weighted_pcl freq_list[],
+					unsigned int size)
 {
 	u8 op_class, op_channel;
 	unsigned int oper_freq = 0, i, j;
@@ -577,10 +615,11 @@
 	 */
 	for (i = 0; i < size && !found; i++) {
 		/* Make sure that the common frequency is supported by peer. */
-		oper_freq = freq_list[i];
+		oper_freq = freq_list[i].freq;
 		if (p2p_freq_to_channel(oper_freq, &op_class,
-					&op_channel) < 0)
-			continue; /* cannot happen due to earlier check */
+					&op_channel) < 0 ||
+		    !p2p_pref_freq_allowed(&freq_list[i], go))
+			continue;
 		for (j = 0; j < msg->channel_list_len; j++) {
 			if (!msg->channel_list ||
 			    op_channel != msg->channel_list[j])
@@ -609,7 +648,8 @@
 static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
 				     struct p2p_device *dev,
 				     struct p2p_message *msg,
-				     unsigned freq_list[], unsigned int size)
+				     const struct weighted_pcl freq_list[],
+				     unsigned int size)
 {
 	u8 op_class, op_channel;
 	unsigned int oper_freq = 0, i, j;
@@ -625,11 +665,13 @@
 			oper_freq = p2p_channel_to_freq(
 				msg->pref_freq_list[2 * j],
 				msg->pref_freq_list[2 * j + 1]);
-			if (freq_list[i] != oper_freq)
+			if (freq_list[i].freq != oper_freq)
 				continue;
 			if (p2p_freq_to_channel(oper_freq, &op_class,
 						&op_channel) < 0)
 				continue; /* cannot happen */
+			if (!p2p_pref_freq_allowed(&freq_list[i], go))
+				break;
 			p2p->op_reg_class = op_class;
 			p2p->op_channel = op_channel;
 			os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -652,10 +694,11 @@
 void p2p_check_pref_chan(struct p2p_data *p2p, int go,
 			 struct p2p_device *dev, struct p2p_message *msg)
 {
-	unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
+	unsigned int size;
 	unsigned int i;
 	u8 op_class, op_channel;
 	char txt[100], *pos, *end;
+	bool is_6ghz_capab;
 	int res;
 
 	/*
@@ -669,25 +712,34 @@
 
 	/* Obtain our preferred frequency list from driver based on P2P role. */
 	size = P2P_MAX_PREF_CHANNELS;
-	if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
-					 freq_list))
+	if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go,
+					 &p2p->num_pref_freq,
+					 p2p->pref_freq_list))
+		return;
+	size = p2p->num_pref_freq;
+	if (!size)
 		return;
 	/* Filter out frequencies that are not acceptable for P2P use */
+	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+		p2p_is_peer_6ghz_capab(p2p, dev->info.p2p_device_addr);
 	i = 0;
 	while (i < size) {
-		if (p2p_freq_to_channel(freq_list[i], &op_class,
-					&op_channel) < 0 ||
+		if (p2p_freq_to_channel(p2p->pref_freq_list[i].freq,
+					&op_class, &op_channel) < 0 ||
 		    (!p2p_channels_includes(&p2p->cfg->channels,
 					    op_class, op_channel) &&
 		     (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
-						   op_class, op_channel)))) {
+						   op_class, op_channel))) ||
+		    (is_6ghz_freq(p2p->pref_freq_list[i].freq) &&
+		     !is_6ghz_capab)) {
 			p2p_dbg(p2p,
 				"Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
-				freq_list[i], go);
+				p2p->pref_freq_list[i].freq, go);
 			if (size - i - 1 > 0)
-				os_memmove(&freq_list[i], &freq_list[i + 1],
+				os_memmove(&p2p->pref_freq_list[i],
+					   &p2p->pref_freq_list[i + 1],
 					   (size - i - 1) *
-					   sizeof(unsigned int));
+					   sizeof(struct weighted_pcl));
 			size--;
 			continue;
 		}
@@ -699,7 +751,8 @@
 	pos = txt;
 	end = pos + sizeof(txt);
 	for (i = 0; i < size; i++) {
-		res = os_snprintf(pos, end - pos, " %u", freq_list[i]);
+		res = os_snprintf(pos, end - pos, " %u",
+				  p2p->pref_freq_list[i].freq);
 		if (os_snprintf_error(end - pos, res))
 			break;
 		pos += res;
@@ -713,11 +766,14 @@
 	 * our preferred channel list.
 	 */
 	for (i = 0; i < size; i++) {
-		if (freq_list[i] == (unsigned int) dev->oper_freq)
+		if (p2p->pref_freq_list[i].freq ==
+		    (unsigned int) dev->oper_freq &&
+		    p2p_pref_freq_allowed(&p2p->pref_freq_list[i], go))
 			break;
 	}
 	if (i != size &&
-	    p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) {
+	    p2p_freq_to_channel(p2p->pref_freq_list[i].freq, &op_class,
+				&op_channel) == 0) {
 		/* Peer operating channel preference matches our preference */
 		p2p->op_reg_class = op_class;
 		p2p->op_channel = op_channel;
@@ -735,9 +791,11 @@
 	  * _not_ included in the GO Negotiation Request or Invitation Request.
 	  */
 	if (msg->pref_freq_list_len == 0)
-		p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
+		p2p_check_pref_chan_no_recv(p2p, go, dev, msg,
+					    p2p->pref_freq_list, size);
 	else
-		p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
+		p2p_check_pref_chan_recv(p2p, go, dev, msg,
+					 p2p->pref_freq_list, size);
 }
 
 
@@ -852,6 +910,9 @@
 		p2p_add_dev_info(p2p, sa, dev, &msg);
 	}
 
+	if (dev)
+		p2p_update_peer_6ghz_capab(dev, &msg);
+
 	if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
 		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 
@@ -1085,6 +1146,7 @@
 	struct p2p_channels res;
 	u8 group_capab;
 	size_t extra = 0;
+	bool is_6ghz_capab;
 
 	p2p_dbg(p2p, "Building GO Negotiation Confirm");
 
@@ -1128,7 +1190,9 @@
 		p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
 					      resp_chan[3], resp_chan[4]);
 	p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
-	p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+		p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, is_6ghz_capab);
 	if (go) {
 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
 				     p2p->ssid_len);
@@ -1175,6 +1239,7 @@
 		return;
 	}
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+	p2p_update_peer_6ghz_capab(dev, &msg);
 
 	if (msg.dialog_token != dev->dialog_token) {
 		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
@@ -1470,6 +1535,8 @@
 		return;
 	}
 
+	p2p_update_peer_6ghz_capab(dev, &msg);
+
 	if (dev->go_state == REMOTE_GO && msg.group_id) {
 		/* Store SSID for Provisioning step */
 		p2p->ssid_len = msg.group_id_len - ETH_ALEN;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index b5e5c2b..235467e 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -10,6 +10,7 @@
 #define P2P_I_H
 
 #include "utils/list.h"
+#include "drivers/driver.h"
 #include "p2p.h"
 #include "ap/ap_config.h"
 
@@ -149,6 +150,7 @@
 	struct wpabuf *go_neg_conf;
 
 	int sd_pending_bcast_queries;
+	bool support_6ghz;
 };
 
 struct p2p_sd_query {
@@ -553,7 +555,7 @@
 
 	struct wpabuf **vendor_elem;
 
-	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
 	unsigned int num_pref_freq;
 
 	/* Override option for preferred operating channel in GO Negotiation */
@@ -690,7 +692,6 @@
 
 /* p2p_utils.c */
 int p2p_random(char *buf, size_t len);
-int p2p_channel_to_freq(int op_class, int channel);
 int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
 void p2p_channels_intersect(const struct p2p_channels *a,
 			    const struct p2p_channels *b,
@@ -769,7 +770,7 @@
 void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
 				   u8 reg_class, u8 channel);
 void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
-			      struct p2p_channels *chan);
+			      struct p2p_channels *chan, bool is_6ghz_capab);
 void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
 				u8 client_timeout);
 void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
@@ -800,7 +801,7 @@
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
 void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
-				   const unsigned int *preferred_freq_list,
+				   const struct weighted_pcl *pref_freq_list,
 				   unsigned int size);
 
 /* p2p_sd.c */
@@ -875,6 +876,8 @@
 struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 						const u8 *addr,
 						struct p2p_message *msg);
+void p2p_update_peer_6ghz_capab(struct p2p_device *dev,
+				const struct p2p_message *msg);
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg);
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
@@ -902,6 +905,10 @@
 void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
 int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
 			  u8 *status);
+void p2p_pref_channel_filter(const struct p2p_channels *a,
+			     const struct weighted_pcl *freq_list,
+			     unsigned int num_channels,
+			     struct p2p_channels *res, bool go);
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index ab00722..941ec00 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -24,6 +24,7 @@
 	u8 *len;
 	const u8 *dev_addr;
 	size_t extra = 0;
+	bool is_6ghz_capab;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
@@ -74,7 +75,10 @@
 					      p2p->op_channel);
 	if (p2p->inv_bssid_set)
 		p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
-	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+		p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels,
+				 is_6ghz_capab);
 	if (go_dev_addr)
 		dev_addr = go_dev_addr;
 	else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
@@ -155,8 +159,14 @@
 					      reg_class, channel);
 	if (group_bssid)
 		p2p_buf_add_group_bssid(buf, group_bssid);
-	if (channels)
-		p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+	if (channels) {
+		bool is_6ghz_capab;
+
+		is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+			p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, channels,
+					 is_6ghz_capab);
+	}
 	p2p_buf_update_ie_hdr(buf, len);
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -588,9 +598,14 @@
 	p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
 	p2p->invite_peer = dev;
 	dev->invitation_reqs++;
+
+	/* In case of an active P2P GO use a shorter wait time to avoid
+	 * issues if not sending out multiple consecutive Beacon frames. */
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
-			    wpabuf_head(req), wpabuf_len(req), 500) < 0) {
+			    wpabuf_head(req), wpabuf_len(req),
+			    p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO ?
+			    150 : 500) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		/* Use P2P find to recover and retry */
 		p2p_set_timeout(p2p, 0, 0);
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 338b47e..f75cee8 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -124,9 +124,15 @@
 		}
 
 		if (shared_group ||
-		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
+		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) {
+			bool is_6ghz_capab;
+
+			is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
+				p2p_is_peer_6ghz_capab(
+					p2p, dev->info.p2p_device_addr);
 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
-						 &p2p->channels);
+						 &p2p->channels, is_6ghz_capab);
+		}
 
 		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
 		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
@@ -356,9 +362,15 @@
 		}
 
 		if (persist ||
-		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) {
+			bool is_6ghz_capab;
+
+			is_6ghz_capab = is_p2p_6ghz_capable(p2p) && dev &&
+				p2p_is_peer_6ghz_capab(
+					p2p, dev->info.p2p_device_addr);
 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
-						 &p2p->channels);
+						 &p2p->channels, is_6ghz_capab);
+		}
 
 		if (!persist && conncap)
 			p2p_buf_add_connection_capability(buf, conncap);
@@ -607,6 +619,8 @@
 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
+	p2p_update_peer_6ghz_capab(dev, &msg);
+
 	if (!msg.adv_id) {
 		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
 		if (!(msg.wps_config_methods & allowed_config_methods)) {
@@ -1355,6 +1369,8 @@
 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
+	p2p_update_peer_6ghz_capab(dev, &msg);
+
 	if (dev->dialog_token != msg.dialog_token) {
 		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
 			msg.dialog_token, dev->dialog_token);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index a203606..c1f0084 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -519,14 +519,14 @@
 }
 
 
-int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size)
+int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size)
 {
 	int i;
 
 	for (i = 0; i < size; i++) {
-		if (is_6ghz_freq(pref_freq_list[i])) {
+		if (is_6ghz_freq(pref_freq_list[i].freq)) {
 			wpa_printf(MSG_DEBUG, "P2P: Remove 6 GHz channel %d",
-				   pref_freq_list[i]);
+				   pref_freq_list[i].freq);
 			size--;
 			os_memmove(&pref_freq_list[i], &pref_freq_list[i + 1],
 				   (size - i) * sizeof(pref_freq_list[0]));
@@ -535,3 +535,79 @@
 	}
 	return i;
 }
+
+
+/**
+ * p2p_pref_freq_allowed - Based on the flags set, check if the preferred
+ * frequency is allowed
+ * @freq_list: Weighted preferred channel list
+ * @go: Whether the local device is the group owner
+ * Returns: Whether the preferred frequency is allowed
+ */
+bool p2p_pref_freq_allowed(const struct weighted_pcl *freq_list, bool go)
+{
+	if (freq_list->flag & WEIGHTED_PCL_EXCLUDE)
+		return false;
+	if (!(freq_list->flag & WEIGHTED_PCL_CLI) && !go)
+		return false;
+	if (!(freq_list->flag & WEIGHTED_PCL_GO) && go)
+		return false;
+	return true;
+}
+
+
+static int p2p_check_pref_channel(int channel, u8 op_class,
+				  const struct weighted_pcl *freq_list,
+				  unsigned int num_channels, bool go)
+{
+	unsigned int i;
+
+	/* If the channel is present in the preferred channel list, check if it
+	 * has appropriate flags for the role.
+	 */
+	for (i = 0; i < num_channels; i++) {
+		if (p2p_channel_to_freq(op_class, channel) !=
+		    (int) freq_list[i].freq)
+			continue;
+		if (!p2p_pref_freq_allowed(&freq_list[i], go))
+			return -1;
+		break;
+	}
+
+	return 0;
+}
+
+
+void p2p_pref_channel_filter(const struct p2p_channels *p2p_chan,
+			     const struct weighted_pcl *freq_list,
+			     unsigned int num_channels,
+			     struct p2p_channels *res, bool go)
+{
+	size_t i, j;
+
+	os_memset(res, 0, sizeof(*res));
+
+	for (i = 0; i < p2p_chan->reg_classes; i++) {
+		const struct p2p_reg_class *reg = &p2p_chan->reg_class[i];
+		struct p2p_reg_class *res_reg = &res->reg_class[i];
+
+		if (num_channels > 0) {
+			for (j = 0; j < reg->channels; j++) {
+				if (p2p_check_pref_channel(reg->channel[j],
+							   reg->reg_class,
+							   freq_list,
+							   num_channels,
+							   go) < 0)
+					continue;
+
+				res_reg->channel[res_reg->channels++] =
+					reg->channel[j];
+			}
+		}
+
+		if (res_reg->channels == 0)
+			continue;
+		res->reg_classes++;
+		res_reg->reg_class = reg->reg_class;
+	}
+}
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index 2bf3e8e..d06ad86 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -84,6 +84,7 @@
 
 	/* not defined IEEE Std 802.1X-2010 */
 	struct ieee802_1x_kay *kay;
+	u8 offload;
 };
 
 static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
@@ -188,6 +189,7 @@
 	sm->protect_frames = false;
 	sm->replay_protect = false;
 	sm->validate_frames = Checked;
+	sm->offload = sm->kay->macsec_offload;
 
 	sm->port_valid = false;
 	sm->controlled_port_enabled = true;
@@ -197,6 +199,7 @@
 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+	secy_cp_control_offload(sm->kay, sm->offload);
 }
 
 
@@ -208,6 +211,7 @@
 
 	sm->protect_frames = sm->kay->macsec_protect;
 	sm->replay_protect = sm->kay->macsec_replay_protect;
+	sm->offload = sm->kay->macsec_offload;
 	sm->validate_frames = sm->kay->macsec_validate;
 
 	sm->current_cipher_suite = sm->cipher_suite;
@@ -223,6 +227,7 @@
 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+	secy_cp_control_offload(sm->kay, sm->offload);
 }
 
 
@@ -462,6 +467,7 @@
 	sm->validate_frames = kay->macsec_validate;
 	sm->replay_protect = kay->macsec_replay_protect;
 	sm->replay_window = kay->macsec_replay_window;
+	sm->offload = kay->macsec_offload;
 
 	sm->controlled_port_enabled = false;
 
@@ -491,6 +497,7 @@
 	secy_cp_control_confidentiality_offset(sm->kay,
 					       sm->confidentiality_offset);
 	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
+	secy_cp_control_offload(sm->kay, sm->offload);
 
 	SM_STEP_RUN(CP);
 
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 3c55c5a..66c65aa 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -3477,8 +3477,8 @@
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
-		    u16 port, u8 priority, u32 macsec_csindex,
-		    const char *ifname, const u8 *addr)
+		    u8 macsec_offload, u16 port, u8 priority,
+		    u32 macsec_csindex, const char *ifname, const u8 *addr)
 {
 	struct ieee802_1x_kay *kay;
 
@@ -3537,6 +3537,7 @@
 		kay->macsec_validate = Disabled;
 		kay->macsec_replay_protect = false;
 		kay->macsec_replay_window = 0;
+		kay->macsec_offload = 0;
 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
 		kay->mka_hello_time = MKA_HELLO_TIME;
 	} else {
@@ -3553,6 +3554,7 @@
 		kay->macsec_validate = Strict;
 		kay->macsec_replay_protect = macsec_replay_protect;
 		kay->macsec_replay_window = macsec_replay_window;
+		kay->macsec_offload = macsec_offload;
 		kay->mka_hello_time = MKA_HELLO_TIME;
 	}
 
@@ -3757,6 +3759,7 @@
 	secy_cp_control_protect_frames(kay, kay->macsec_protect);
 	secy_cp_control_replay(kay, kay->macsec_replay_protect,
 			       kay->macsec_replay_window);
+	secy_cp_control_offload(kay, kay->macsec_offload);
 	if (secy_create_transmit_sc(kay, participant->txsc))
 		goto fail;
 
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 525679f..545a99b 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -166,6 +166,7 @@
 	int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa);
 	int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa);
 	int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*set_offload)(void *ctx, u8 offload);
 };
 
 struct ieee802_1x_kay {
@@ -206,6 +207,7 @@
 	bool is_key_server;
 	bool is_obliged_key_server;
 	char if_name[IFNAMSIZ];
+	u8 macsec_offload;
 
 	unsigned int macsec_csindex;  /* MACsec cipher suite table index */
 	int mka_algindex;  /* MKA alg table index */
@@ -240,8 +242,8 @@
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
-		    u16 port, u8 priority, u32 macsec_csindex,
-		    const char *ifname, const u8 *addr);
+		    u8 macsec_offload, u16 port, u8 priority,
+		    u32 macsec_csindex, const char *ifname, const u8 *addr);
 void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
 
 struct ieee802_1x_mka_participant *
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index 0f36e6b..f35baad 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -85,6 +85,26 @@
 }
 
 
+int secy_cp_control_offload(struct ieee802_1x_kay *kay, u8 offload)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->set_offload) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy set_offload operation not supported");
+		return -1;
+	}
+
+	return ops->set_offload(ops->ctx, offload);
+}
+
+
 int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs)
 {
 	struct ieee802_1x_kay_ctx *ops;
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index 18c06f6..b82507b 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -23,6 +23,7 @@
 int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, bool flag);
 int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, bool enabled);
 int secy_cp_control_replay(struct ieee802_1x_kay *kay, bool flag, u32 win);
+int secy_cp_control_offload(struct ieee802_1x_kay *kay, u8 offload);
 int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs);
 int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
 					   enum confidentiality_offset co);
diff --git a/src/pasn/Makefile b/src/pasn/Makefile
new file mode 100644
index 0000000..a5b2c6b
--- /dev/null
+++ b/src/pasn/Makefile
@@ -0,0 +1,16 @@
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_FILS
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_SAE_PK
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_SHA512
+CFLAGS += -DCONFIG_PASN
+
+LIB_OBJS= \
+	pasn_initiator.o \
+	pasn_responder.o
+
+include ../lib.rules
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
new file mode 100644
index 0000000..9c2f397
--- /dev/null
+++ b/src/pasn/pasn_common.h
@@ -0,0 +1,184 @@
+/*
+ * PASN info for initiator and responder
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (c) 2022, Jouni Malinen <j@w1.fi>
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PASN_COMMON_H
+#define PASN_COMMON_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_PASN
+
+enum pasn_fils_state {
+	PASN_FILS_STATE_NONE = 0,
+	PASN_FILS_STATE_PENDING_AS,
+	PASN_FILS_STATE_COMPLETE
+};
+
+struct pasn_fils {
+	u8 state;
+	u8 nonce[FILS_NONCE_LEN];
+	u8 anonce[FILS_NONCE_LEN];
+	u8 session[FILS_SESSION_LEN];
+	u8 erp_pmkid[PMKID_LEN];
+	bool completed;
+	struct wpabuf *erp_resp;
+};
+
+struct pasn_data {
+	int akmp;
+	int cipher;
+	u16 group;
+	bool secure_ltf;
+	int freq;
+	size_t kdk_len;
+
+	u8 trans_seq;
+	u8 status;
+
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	size_t pmk_len;
+	u8 pmk[PMK_LEN_MAX];
+	bool using_pmksa;
+
+	u8 hash[SHA384_MAC_LEN];
+
+	struct wpabuf *beacon_rsne_rsnxe;
+	struct wpa_ptk ptk;
+	struct crypto_ecdh *ecdh;
+
+	struct wpabuf *comeback;
+	u16 comeback_after;
+
+#ifdef CONFIG_SAE
+	struct sae_data sae;
+	struct sae_pt *pt;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	bool fils_eapol;
+	bool fils_wd_valid;
+	struct pasn_fils fils;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211R
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+#endif /* CONFIG_IEEE80211R */
+	/* Note that this pointers to RSN PMKSA cache are actually defined
+	 * differently for the PASN initiator (using RSN Supplicant
+	 * implementation) and PASN responser (using RSN Authenticator
+	 * implementation). Functions cannot be mixed between those cases. */
+	struct rsn_pmksa_cache *pmksa;
+	struct rsn_pmksa_cache_entry *pmksa_entry;
+	struct eapol_sm *eapol;
+	int fast_reauth;
+#ifdef CONFIG_TESTING_OPTIONS
+	int corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+	void *cb_ctx;
+	u16 rsnxe_capab;
+	int network_id;
+
+	u8 wrapped_data_format;
+	struct wpabuf *secret;
+
+	/* Reponder */
+	int wpa_key_mgmt;
+	int rsn_pairwise;
+	bool derive_kdk;
+	const char *password;
+	int disable_pmksa_caching;
+	int *pasn_groups;
+	struct wpabuf *wrapped_data;
+	int use_anti_clogging;
+	const u8 *rsn_ie;
+	const u8 *rsnxe_ie;
+	size_t rsn_ie_len;
+
+	u8 *comeback_key;
+	struct os_reltime last_comeback_key_update;
+	u16 comeback_idx;
+	u16 *comeback_pending_idx;
+
+	bool custom_pmkid_valid;
+	u8 custom_pmkid[PMKID_LEN];
+
+	/**
+	 * Extra elements to add into Authentication frames. These can be used,
+	 * e.g., for Wi-Fi Aware use cases.
+	 */
+	const u8 *extra_ies;
+	size_t extra_ies_len;
+
+	/**
+	 * send_mgmt - Function handler to transmit a Management frame
+	 * @ctx: Callback context from cb_ctx
+	 * @frame_buf : Frame to transmit
+	 * @frame_len: Length of frame to transmit
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @wait_dur: How many milliseconds to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_mgmt)(void *ctx, const u8 *data, size_t data_len, int noack,
+			 unsigned int freq, unsigned int wait);
+	/**
+	 * validate_custom_pmkid - Handler to validate vendor specific PMKID
+	 * @ctx: Callback context from cb_ctx
+	 * @addr : MAC address of the peer
+	 * @pmkid: Custom PMKID
+	 * Returns: 0 on success (valid PMKID), -1 on failure
+	 */
+	int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
+				     const u8 *pmkid);
+};
+
+/* Initiator */
+
+void wpa_pasn_reset(struct pasn_data *pasn);
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+		    const u8 *peer_addr, const u8 *bssid,
+		    int akmp, int cipher, u16 group,
+		    int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+		    const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+		    const struct wpabuf *comeback);
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+		    const u8 *peer_addr, const u8 *bssid,
+		    int akmp, int cipher, u16 group,
+		    int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+		    const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+		    const struct wpabuf *comeback);
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+		     struct wpa_pasn_params_data *pasn_params);
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+			    const u8 *data, size_t data_len, u8 acked);
+
+/* Responder */
+int handle_auth_pasn_1(struct pasn_data *pasn,
+		       const u8 *own_addr, const u8 *peer_addr,
+		       const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+		       const u8 *peer_addr,
+		       const struct ieee80211_mgmt *mgmt, size_t len);
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+			  const u8 *peer_addr,
+			  struct rsn_pmksa_cache_entry *pmksa, u16 status);
+
+#endif /* CONFIG_PASN */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* PASN_COMMON_H */
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
new file mode 100644
index 0000000..1f9a508
--- /dev/null
+++ b/src/pasn/pasn_initiator.c
@@ -0,0 +1,1393 @@
+/*
+ * PASN initiator processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/dragonfly.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "pasn_common.h"
+
+
+#ifdef CONFIG_SAE
+
+static struct wpabuf * wpas_pasn_wd_sae_commit(struct pasn_data *pasn)
+{
+	struct wpabuf *buf = NULL;
+	int ret;
+
+	ret = sae_set_group(&pasn->sae, pasn->group);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+		return NULL;
+	}
+
+	ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt,
+				    pasn->own_addr, pasn->peer_addr,
+				    NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+		return NULL;
+	}
+
+	/* Need to add the entire Authentication frame body */
+	buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+		return NULL;
+	}
+
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+	sae_write_commit(&pasn->sae, buf, NULL, 0);
+	pasn->sae.state = SAE_COMMITTED;
+
+	return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+	const u8 *data;
+	size_t buf_len;
+	u16 len, res, alg, seq, status;
+	int groups[] = { pasn->group, 0 };
+	int ret;
+
+	if (!wd)
+		return -1;
+
+	data = wpabuf_head_u8(wd);
+	buf_len = wpabuf_len(wd);
+
+	/* first handle the commit message */
+	if (buf_len < 2) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+		return -1;
+	}
+
+	len = WPA_GET_LE16(data);
+	if (len < 6 || buf_len - 2 < len) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+		return -1;
+	}
+
+	buf_len -= 2;
+	data += 2;
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 1 ||
+	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+		return -1;
+	}
+
+	res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+			       1, NULL);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+		return -1;
+	}
+
+	/* Process the commit message and derive the PMK */
+	ret = sae_process_commit(&pasn->sae);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+		return -1;
+	}
+
+	buf_len -= len;
+	data += len;
+
+	/* Handle the confirm message */
+	if (buf_len < 2) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+		return -1;
+	}
+
+	len = WPA_GET_LE16(data);
+	if (len < 6 || buf_len - 2 < len) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+		return -1;
+	}
+
+	buf_len -= 2;
+	data += 2;
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+		return -1;
+	}
+
+	res = sae_check_confirm(&pasn->sae, data + 6, len - 6, NULL);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+	pasn->sae.state = SAE_ACCEPTED;
+
+	return 0;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_sae_confirm(struct pasn_data *pasn)
+{
+	struct wpabuf *buf = NULL;
+
+	/* Need to add the entire authentication frame body */
+	buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+		return NULL;
+	}
+
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	sae_write_confirm(&pasn->sae, buf);
+	pasn->sae.state = SAE_CONFIRMED;
+
+	return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn)
+{
+	struct wpabuf *buf = NULL;
+	struct wpabuf *erp_msg;
+	int ret;
+
+	erp_msg = eapol_sm_build_erp_reauth_start(pasn->eapol);
+	if (!erp_msg) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
+		return NULL;
+	}
+
+	if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
+	    random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
+		goto fail;
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
+		    FILS_NONCE_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
+		    FILS_SESSION_LEN);
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	/* Add the authentication algorithm */
+	wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+	/* Authentication Transaction seq# */
+	wpabuf_put_le16(buf, 1);
+
+	/* Status Code */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	/* Own RSNE */
+	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+
+	/* FILS Nonce */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN);
+
+	/* Wrapped Data (ERP) */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+	wpabuf_put_buf(buf, erp_msg);
+
+	/*
+	 * Calculate pending PMKID here so that we do not need to maintain a
+	 * copy of the EAP-Initiate/Reauth message.
+	 */
+	ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
+			     wpabuf_len(erp_msg),
+			     pasn->fils.erp_pmkid);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
+		goto fail;
+	}
+
+	wpabuf_free(erp_msg);
+	erp_msg = NULL;
+
+	wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
+	return buf;
+fail:
+	wpabuf_free(erp_msg);
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_fils_auth(struct pasn_data *pasn)
+{
+	wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
+		   pasn->fils.completed);
+
+	/* Nothing to add as we are done */
+	if (pasn->fils.completed)
+		return NULL;
+
+	if (!pasn->fils_eapol) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Missing Indication IE or PFS");
+		return NULL;
+	}
+
+	return wpas_pasn_fils_build_auth(pasn);
+}
+
+
+static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd)
+{
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsne_data;
+	u8 rmsk[ERP_MAX_KEY_LEN];
+	size_t rmsk_len;
+	u8 anonce[FILS_NONCE_LEN];
+	const u8 *data;
+	size_t buf_len;
+	struct wpabuf *fils_wd = NULL;
+	u16 alg, seq, status;
+	int ret;
+
+	if (!wd)
+		return -1;
+
+	data = wpabuf_head(wd);
+	buf_len = wpabuf_len(wd);
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
+		    data, buf_len);
+
+	/* first handle the header */
+	if (buf_len < 6) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
+		return -1;
+	}
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
+	    status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Dropping peer authentication");
+		return -1;
+	}
+
+	data += 6;
+	buf_len -= 6;
+
+	if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
+		return -1;
+	}
+
+	if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+	    !elems.wrapped_data) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
+		return -1;
+	}
+
+	ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+			       &rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
+		return -1;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+		return -1;
+	}
+
+	if (rsne_data.num_pmkid) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Not expecting PMKID in RSNE");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce,
+		    FILS_NONCE_LEN);
+	os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+
+	if (os_memcmp(pasn->fils.session, elems.fils_session,
+		      FILS_SESSION_LEN)) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
+		return -1;
+	}
+
+	fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+				    WLAN_EID_EXT_WRAPPED_DATA);
+
+	if (!fils_wd) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Failed getting wrapped data");
+		return -1;
+	}
+
+	eapol_sm_process_erp_finish(pasn->eapol, wpabuf_head(fils_wd),
+				    wpabuf_len(fils_wd));
+
+	wpabuf_free(fils_wd);
+	fils_wd = NULL;
+
+	if (eapol_sm_failed(pasn->eapol)) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
+		return -1;
+	}
+
+	rmsk_len = ERP_MAX_KEY_LEN;
+	ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+
+	if (ret == PMK_LEN) {
+		rmsk_len = PMK_LEN;
+		ret = eapol_sm_get_key(pasn->eapol, rmsk, rmsk_len);
+	}
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
+		return -1;
+	}
+
+	ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
+			       pasn->fils.nonce, anonce, NULL, 0,
+			       pasn->pmk, &pasn->pmk_len);
+
+	forced_memzero(rmsk, sizeof(rmsk));
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
+		    PMKID_LEN);
+
+	wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
+
+	pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+					    pasn->pmk_len, pasn->fils.erp_pmkid,
+					    NULL, 0, pasn->peer_addr,
+					    pasn->own_addr, NULL,
+					    pasn->akmp, 0);
+
+	pasn->fils.completed = true;
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
+static struct wpabuf * wpas_pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+	if (pasn->using_pmksa)
+		return NULL;
+
+	switch (pasn->akmp) {
+	case WPA_KEY_MGMT_PASN:
+		/* no wrapped data */
+		return NULL;
+	case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+		if (pasn->trans_seq == 0)
+			return wpas_pasn_wd_sae_commit(pasn);
+		if (pasn->trans_seq == 2)
+			return wpas_pasn_wd_sae_confirm(pasn);
+#endif /* CONFIG_SAE */
+		wpa_printf(MSG_ERROR,
+			   "PASN: SAE: Cannot derive wrapped data");
+		return NULL;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+		return wpas_pasn_wd_fils_auth(pasn);
+#endif /* CONFIG_FILS */
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		/*
+		 * Wrapped data with these AKMs is optional and is only needed
+		 * for further validation of FT security parameters. For now do
+		 * not use them.
+		 */
+		return NULL;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "PASN: TODO: Wrapped data for akmp=0x%x",
+			   pasn->akmp);
+		return NULL;
+	}
+}
+
+
+static u8 wpas_pasn_get_wrapped_data_format(struct pasn_data *pasn)
+{
+	if (pasn->using_pmksa)
+		return WPA_PASN_WRAPPED_DATA_NO;
+
+	/* Note: Valid AKMP is expected to already be validated */
+	switch (pasn->akmp) {
+	case WPA_KEY_MGMT_SAE:
+		return WPA_PASN_WRAPPED_DATA_SAE;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+		return WPA_PASN_WRAPPED_DATA_FILS_SK;
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		/*
+		 * Wrapped data with these AKMs is optional and is only needed
+		 * for further validation of FT security parameters. For now do
+		 * not use them.
+		 */
+		return WPA_PASN_WRAPPED_DATA_NO;
+	case WPA_KEY_MGMT_PASN:
+	default:
+		return WPA_PASN_WRAPPED_DATA_NO;
+	}
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
+					      const struct wpabuf *comeback,
+					      bool verify)
+{
+	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+	const u8 *pmkid;
+	u8 wrapped_data;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
+
+	if (pasn->trans_seq)
+		return NULL;
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	/* Get public key */
+	pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+	pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
+	if (!pubkey) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+		goto fail;
+	}
+
+	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid,
+				   pasn->own_addr, pasn->peer_addr,
+				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+	pmkid = NULL;
+	if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+		pmkid = pasn->pmk_r1_name;
+#else /* CONFIG_IEEE80211R */
+		goto fail;
+#endif /* CONFIG_IEEE80211R */
+	} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+		struct rsn_pmksa_cache_entry *pmksa;
+
+		pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+					pasn->own_addr, NULL, NULL, pasn->akmp);
+		if (pmksa && pasn->custom_pmkid_valid)
+			pmkid = pasn->custom_pmkid;
+		else if (pmksa)
+			pmkid = pmksa->pmkid;
+
+		/*
+		 * Note: Even when PMKSA is available, also add wrapped data as
+		 * it is possible that the PMKID is no longer valid at the AP.
+		 */
+		if (!verify)
+			wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+	}
+
+	if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
+		goto fail;
+
+	if (!wrapped_data_buf)
+		wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+				  pubkey, true, comeback, -1);
+
+	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+		goto fail;
+
+	wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab);
+
+	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+				   wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
+				   wpabuf_len(buf) - IEEE80211_HDRLEN,
+				   pasn->hash);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+		goto fail;
+	}
+
+	pasn->trans_seq++;
+
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+
+	wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
+	return buf;
+fail:
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn)
+{
+	struct wpabuf *buf, *wrapped_data_buf = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len, data_len;
+	const u8 *data;
+	u8 *ptr;
+	u8 wrapped_data;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+
+	if (pasn->trans_seq != 2)
+		return NULL;
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid,
+				   pasn->own_addr, pasn->peer_addr,
+				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+	wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+
+	if (!wrapped_data_buf)
+		wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+				  NULL, false, NULL, -1);
+
+	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+		goto fail;
+	wpabuf_free(wrapped_data_buf);
+	wrapped_data_buf = NULL;
+
+	/* Add the MIC */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, mic_len);
+	ptr = wpabuf_put(buf, mic_len);
+
+	os_memset(ptr, 0, mic_len);
+
+	data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+	data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+		       pasn->own_addr, pasn->peer_addr,
+		       pasn->hash, mic_len * 2, data, data_len, mic);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
+		goto fail;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (pasn->corrupt_mic) {
+		wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
+		mic[0] = ~mic[0];
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_memcpy(ptr, mic, mic_len);
+
+	pasn->trans_seq++;
+
+	wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
+	return buf;
+fail:
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+void wpa_pasn_reset(struct pasn_data *pasn)
+{
+	wpa_printf(MSG_DEBUG, "PASN: Reset");
+
+	crypto_ecdh_deinit(pasn->ecdh);
+	pasn->ecdh = NULL;
+
+
+	pasn->akmp = 0;
+	pasn->cipher = 0;
+	pasn->group = 0;
+	pasn->trans_seq = 0;
+	pasn->pmk_len = 0;
+	pasn->using_pmksa = false;
+
+	forced_memzero(pasn->pmk, sizeof(pasn->pmk));
+	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+	forced_memzero(&pasn->hash, sizeof(pasn->hash));
+
+	wpabuf_free(pasn->beacon_rsne_rsnxe);
+	pasn->beacon_rsne_rsnxe = NULL;
+
+	wpabuf_free(pasn->comeback);
+	pasn->comeback = NULL;
+	pasn->comeback_after = 0;
+
+#ifdef CONFIG_SAE
+	sae_clear_data(&pasn->sae);
+	if (pasn->pt) {
+		sae_deinit_pt(pasn->pt);
+		pasn->pt = NULL;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	pasn->fils_eapol = false;
+	os_memset(&pasn->fils, 0, sizeof(pasn->fils));
+#endif /* CONFIG_FILS*/
+
+#ifdef CONFIG_IEEE80211R
+	forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+	pasn->pmk_r1_len = 0;
+	os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	pasn->pmksa_entry = NULL;
+#ifdef CONFIG_TESTING_OPTIONS
+	pasn->corrupt_mic = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	pasn->network_id = 0;
+	pasn->derive_kdk = false;
+	pasn->rsn_ie = NULL;
+	pasn->rsn_ie_len = 0;
+	pasn->rsnxe_ie = NULL;
+	pasn->custom_pmkid_valid = false;
+}
+
+
+static int wpas_pasn_set_pmk(struct pasn_data *pasn,
+			     struct wpa_ie_data *rsn_data,
+			     struct wpa_pasn_params_data *pasn_data,
+			     struct wpabuf *wrapped_data)
+{
+	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+
+	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+	pasn->pmk_len = 0;
+
+	if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+		wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+		pasn->pmk_len = WPA_PASN_PMK_LEN;
+		os_memcpy(pasn->pmk, pasn_default_pmk,
+			  sizeof(pasn_default_pmk));
+		return 0;
+	}
+
+	if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+		wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
+		pasn->pmk_len = pasn->pmk_r1_len;
+		os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+		pasn->using_pmksa = true;
+		return 0;
+#else /* CONFIG_IEEE80211R */
+		wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+		return -1;
+#endif /* CONFIG_IEEE80211R */
+	}
+
+	if (rsn_data->num_pmkid) {
+		int ret;
+		struct rsn_pmksa_cache_entry *pmksa;
+		const u8 *pmkid = NULL;
+
+		if (pasn->custom_pmkid_valid) {
+			ret = pasn->validate_custom_pmkid(pasn->cb_ctx,
+							  pasn->peer_addr,
+							  rsn_data->pmkid);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: Failed custom PMKID validation");
+				return -1;
+			}
+		} else {
+			pmkid = rsn_data->pmkid;
+		}
+
+		pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
+					pasn->own_addr,
+					pmkid, NULL, pasn->akmp);
+		if (pmksa) {
+			wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
+
+			pasn->pmk_len = pmksa->pmk_len;
+			os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+			pasn->using_pmksa = true;
+
+			return 0;
+		}
+	}
+
+#ifdef CONFIG_SAE
+	if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+		int ret;
+
+		ret = wpas_pasn_wd_sae_rx(pasn, wrapped_data);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed processing SAE wrapped data");
+			pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
+		pasn->pmk_len = PMK_LEN;
+		os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+		pasn->pmksa_entry = pmksa_cache_add(pasn->pmksa, pasn->pmk,
+						    pasn->pmk_len,
+						    pasn->sae.pmkid,
+						    NULL, 0, pasn->peer_addr,
+						    pasn->own_addr, NULL,
+						    pasn->akmp, 0);
+		return 0;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+	    pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+		int ret;
+
+		ret = wpas_pasn_wd_fils_rx(pasn, wrapped_data);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed processing FILS wrapped data");
+			pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			return -1;
+		}
+
+		return 0;
+	}
+#endif	/* CONFIG_FILS */
+
+	/* TODO: Derive PMK based on wrapped data */
+	wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	return -1;
+}
+
+
+static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr,
+				 const u8 *peer_addr, const u8 *bssid, int akmp,
+				 int cipher, u16 group, int freq,
+				 const u8 *beacon_rsne, u8 beacon_rsne_len,
+				 const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+				 const struct wpabuf *comeback, bool verify)
+{
+	struct wpabuf *frame;
+	int ret;
+
+	pasn->ecdh = crypto_ecdh_init(group);
+	if (!pasn->ecdh) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+		goto fail;
+	}
+
+	if (beacon_rsne && beacon_rsne_len) {
+		pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
+						       beacon_rsnxe_len);
+		if (!pasn->beacon_rsne_rsnxe) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed storing beacon RSNE/RSNXE");
+			goto fail;
+		}
+
+		wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne,
+				beacon_rsne_len);
+		if (beacon_rsnxe && beacon_rsnxe_len)
+			wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
+					beacon_rsnxe_len);
+	}
+
+	pasn->akmp = akmp;
+	pasn->cipher = cipher;
+	pasn->group = group;
+	pasn->freq = freq;
+
+	os_memcpy(pasn->own_addr, own_addr, ETH_ALEN);
+	os_memcpy(pasn->peer_addr, peer_addr, ETH_ALEN);
+	os_memcpy(pasn->bssid, bssid, ETH_ALEN);
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: Init%s: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
+		   verify ? " (verify)" : "",
+		   MAC2STR(pasn->peer_addr), pasn->akmp, pasn->cipher,
+		   pasn->group);
+
+	frame = wpas_pasn_build_auth_1(pasn, comeback, verify);
+	if (!frame) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
+		goto fail;
+	}
+
+	ret = pasn->send_mgmt(pasn->cb_ctx,
+			      wpabuf_head(frame), wpabuf_len(frame), 0,
+			      pasn->freq, 1000);
+
+	wpabuf_free(frame);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return -1;
+}
+
+
+int wpas_pasn_start(struct pasn_data *pasn, const u8 *own_addr,
+		    const u8 *peer_addr, const u8 *bssid,
+		    int akmp, int cipher, u16 group,
+		    int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+		    const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+		    const struct wpabuf *comeback)
+{
+	/* TODO: Currently support only ECC groups */
+	if (!dragonfly_suitable_group(group, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Reject unsuitable group %u", group);
+		return -1;
+	}
+
+	switch (akmp) {
+	case WPA_KEY_MGMT_PASN:
+		break;
+#ifdef CONFIG_SAE
+	case WPA_KEY_MGMT_SAE:
+
+		if (beacon_rsnxe &&
+		    !ieee802_11_rsnx_capab(beacon_rsnxe,
+					   WLAN_RSNX_CAPAB_SAE_H2E)) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: AP does not support SAE H2E");
+			return -1;
+		}
+
+		pasn->sae.state = SAE_NOTHING;
+		pasn->sae.send_confirm = 0;
+		break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+		break;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		break;
+#endif /* CONFIG_IEEE80211R */
+	default:
+		wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
+		return -1;
+	}
+
+	return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+				     cipher, group,
+				     freq, beacon_rsne, beacon_rsne_len,
+				     beacon_rsnxe, beacon_rsnxe_len, comeback,
+				     false);
+}
+
+/*
+ * Wi-Fi Aware uses PASN handshake to authenticate peer devices.
+ * Devices can simply verify each other for subsequent sessions using
+ * pairing verification procedure.
+ *
+ * In pairing verification, Wi-Fi aware devices use PASN authentication
+ * frames with a custom PMKID and Wi-Fi Aware R4 specific verification IEs.
+ * It does not use wrapped data in the Authentication frames. This function
+ * provides support to construct PASN Authentication frames for pairing
+ * verification.
+ */
+int wpa_pasn_verify(struct pasn_data *pasn, const u8 *own_addr,
+		    const u8 *peer_addr, const u8 *bssid,
+		    int akmp, int cipher, u16 group,
+		    int freq, const u8 *beacon_rsne, u8 beacon_rsne_len,
+		    const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+		    const struct wpabuf *comeback)
+{
+	return wpas_pasn_send_auth_1(pasn, own_addr, peer_addr, bssid, akmp,
+				     cipher, group, freq, beacon_rsne,
+				     beacon_rsne_len, beacon_rsnxe,
+				     beacon_rsnxe_len, comeback, true);
+}
+
+
+static bool is_pasn_auth_frame(struct pasn_data *pasn,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t len, bool rx)
+{
+	u16 fc;
+
+	if (!mgmt || len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+		return false;
+
+	/* Not an Authentication frame; do nothing */
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH)
+		return false;
+
+	/* Not our frame; do nothing */
+	if (os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
+		return false;
+
+	if (rx && (os_memcmp(mgmt->da, pasn->own_addr, ETH_ALEN) != 0 ||
+		   os_memcmp(mgmt->sa, pasn->peer_addr, ETH_ALEN) != 0))
+		return false;
+
+	if (!rx && (os_memcmp(mgmt->sa, pasn->own_addr, ETH_ALEN) != 0 ||
+		    os_memcmp(mgmt->da, pasn->peer_addr, ETH_ALEN) != 0))
+		return false;
+
+	/* Not PASN; do nothing */
+	if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+		return false;
+
+	return true;
+}
+
+
+int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
+		     struct wpa_pasn_params_data *pasn_params)
+
+{
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn_data;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+	struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len;
+	u16 status;
+	int ret, inc_y;
+	u8 *copy = NULL;
+	size_t mic_offset, copy_len;
+
+	if (!is_pasn_auth_frame(pasn, mgmt, len, true))
+		return -2;
+
+	if (mgmt->u.auth.auth_transaction !=
+	    host_to_le16(pasn->trans_seq + 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: RX: Invalid transaction sequence: (%u != %u)",
+			   le_to_host16(mgmt->u.auth.auth_transaction),
+			   pasn->trans_seq + 1);
+		return -3;
+	}
+
+	status = le_to_host16(mgmt->u.auth.status_code);
+
+	if (status != WLAN_STATUS_SUCCESS &&
+	    status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Authentication rejected - status=%u", status);
+		goto fail;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		goto fail;
+	}
+
+	/* Check that the MIC IE exists. Save it and zero out the memory */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	if (status == WLAN_STATUS_SUCCESS) {
+		if (!elems.mic || elems.mic_len != mic_len) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Invalid MIC. Expecting len=%u",
+				   mic_len);
+			goto fail;
+		}
+		os_memcpy(mic, elems.mic, mic_len);
+	}
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Missing PASN Parameters IE");
+		goto fail;
+	}
+
+	if (!pasn_params) {
+		wpa_printf(MSG_DEBUG, "PASN: pasn_params == NULL");
+		goto fail;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  true, pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed validation PASN of Parameters IE");
+		goto fail;
+	}
+
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Authentication temporarily rejected");
+
+		if (pasn_params->comeback && pasn_params->comeback_len) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Comeback token available. After=%u",
+				   pasn_params->after);
+
+			if (!pasn_params->after)
+				return 1;
+
+			pasn->comeback = wpabuf_alloc_copy(
+				pasn_params->comeback,
+				pasn_params->comeback_len);
+			if (pasn->comeback)
+				pasn->comeback_after = pasn_params->after;
+		}
+
+		pasn->status = status;
+		goto fail;
+	}
+
+	if (!elems.rsn_ie) {
+		wpa_printf(MSG_DEBUG, "PASN: Missing RSNE");
+		goto fail;
+	}
+
+	ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+			       &rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+		goto fail;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+		goto fail;
+	}
+
+	if (pasn->akmp != rsn_data.key_mgmt ||
+	    pasn->cipher != rsn_data.pairwise_cipher) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+		goto fail;
+	}
+
+	if (pasn->group != pasn_params->group) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
+		goto fail;
+	}
+
+	if (!pasn_params->pubkey || !pasn_params->pubkey_len) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+		goto fail;
+	}
+
+	if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+		inc_y = 1;
+	} else if (pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+		   pasn_params->pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+		inc_y = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid first octet in pubkey=0x%x",
+			   pasn_params->pubkey[0]);
+		goto fail;
+	}
+
+	secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+					 pasn_params->pubkey + 1,
+					 pasn_params->pubkey_len - 1);
+
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+		goto fail;
+	}
+
+	if (pasn_params->wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+			goto fail;
+		}
+	}
+
+	ret = wpas_pasn_set_pmk(pasn, &rsn_data, pasn_params, wrapped_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
+		goto fail;
+	}
+
+	ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
+			      pasn->own_addr, pasn->peer_addr,
+			      wpabuf_head(secret), wpabuf_len(secret),
+			      &pasn->ptk, pasn->akmp, pasn->cipher,
+			      pasn->kdk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+		goto fail;
+	}
+
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed to derive LTF keyseed");
+			goto fail;
+		}
+	}
+
+	wpabuf_free(wrapped_data);
+	wrapped_data = NULL;
+	wpabuf_free(secret);
+	secret = NULL;
+
+	/* Use a copy of the message since we need to clear the MIC field */
+	if (!elems.mic)
+		goto fail;
+	mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+	copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+	if (mic_offset + mic_len > copy_len)
+		goto fail;
+	copy = os_memdup(&mgmt->u.auth, copy_len);
+	if (!copy)
+		goto fail;
+	os_memset(copy + mic_offset, 0, mic_len);
+
+	if (pasn->beacon_rsne_rsnxe) {
+		/* Verify the MIC */
+		ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+			       pasn->peer_addr, pasn->own_addr,
+			       wpabuf_head(pasn->beacon_rsne_rsnxe),
+			       wpabuf_len(pasn->beacon_rsne_rsnxe),
+			       copy, copy_len, out_mic);
+	} else {
+		u8 *rsne_rsnxe;
+		size_t rsne_rsnxe_len = 0;
+
+		/*
+		 * Note: When Beacon rsne_rsnxe is not initialized, it is likely
+		 * that this is for Wi-Fi Aware using PASN handshake for which
+		 * Beacon RSNE/RSNXE are same as RSNE/RSNXE in the
+		 * Authentication frame
+		 */
+		if (elems.rsn_ie && elems.rsn_ie_len)
+			rsne_rsnxe_len += elems.rsn_ie_len + 2;
+		if (elems.rsnxe && elems.rsnxe_len)
+			rsne_rsnxe_len += elems.rsnxe_len + 2;
+
+		rsne_rsnxe = os_zalloc(rsne_rsnxe_len);
+		if (!rsne_rsnxe)
+			goto fail;
+
+		if (elems.rsn_ie && elems.rsn_ie_len)
+			os_memcpy(rsne_rsnxe, elems.rsn_ie - 2,
+				  elems.rsn_ie_len + 2);
+		if (elems.rsnxe && elems.rsnxe_len)
+			os_memcpy(rsne_rsnxe + elems.rsn_ie_len + 2,
+				  elems.rsnxe - 2, elems.rsnxe_len + 2);
+
+		wpa_hexdump_key(MSG_DEBUG, "PASN: RSN + RSNXE buf",
+				rsne_rsnxe, rsne_rsnxe_len);
+
+		/* Verify the MIC */
+		ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+			       pasn->peer_addr, pasn->own_addr,
+			       rsne_rsnxe,
+			       rsne_rsnxe_len,
+			       copy, copy_len, out_mic);
+
+		os_free(rsne_rsnxe);
+	}
+	os_free(copy);
+	copy = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
+	if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
+		goto fail;
+	}
+
+	pasn->trans_seq++;
+
+	wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
+
+	frame = wpas_pasn_build_auth_3(pasn);
+	if (!frame) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
+		goto fail;
+	}
+
+	ret = pasn->send_mgmt(pasn->cb_ctx,
+			      wpabuf_head(frame), wpabuf_len(frame), 0,
+			      pasn->freq, 100);
+	wpabuf_free(frame);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
+
+	pasn->status = WLAN_STATUS_SUCCESS;
+
+	return 0;
+fail:
+	wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
+	wpabuf_free(wrapped_data);
+	wpabuf_free(secret);
+	os_free(copy);
+
+	/*
+	 * TODO: In case of an error the standard allows to silently drop
+	 * the frame and terminate the authentication exchange. However, better
+	 * reply to the AP with an error status.
+	 */
+	if (status == WLAN_STATUS_SUCCESS)
+		pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	else
+		pasn->status = status;
+
+	return -1;
+}
+
+
+int wpa_pasn_auth_tx_status(struct pasn_data *pasn,
+			    const u8 *data, size_t data_len, u8 acked)
+
+{
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+
+	if (!is_pasn_auth_frame(pasn, mgmt, data_len, false))
+		return -1;
+
+	if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
+		wpa_printf(MSG_ERROR,
+			   "PASN: Invalid transaction sequence: (%u != %u)",
+			   pasn->trans_seq,
+			   le_to_host16(mgmt->u.auth.auth_transaction));
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR,
+		   "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
+		   acked);
+
+	/*
+	 * Even if the frame was not acked, do not treat this is an error, and
+	 * try to complete the flow, relying on the PASN timeout callback to
+	 * clean up.
+	 */
+	if (pasn->trans_seq == 3) {
+		wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
+			   MAC2STR(pasn->peer_addr));
+		/*
+		 * Either frame was not ACKed or it was ACKed but the trans_seq
+		 * != 1, i.e., not expecting an RX frame, so we are done.
+		 */
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
new file mode 100644
index 0000000..3b1912d
--- /dev/null
+++ b/src/pasn/pasn_responder.c
@@ -0,0 +1,1016 @@
+/*
+ * PASN responder processing
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Copyright (C) 2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_common.h"
+#include "common/sae.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha384.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/crypto.h"
+#include "ap/hostapd.h"
+#include "ap/comeback_token.h"
+#include "ap/ieee802_1x.h"
+#include "ap/pmksa_cache_auth.h"
+#include "pasn_common.h"
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_SAE
+
+static int pasn_wd_handle_sae_commit(struct pasn_data *pasn,
+				     const u8 *own_addr, const u8 *peer_addr,
+				     struct wpabuf *wd)
+{
+	const u8 *data;
+	size_t buf_len;
+	u16 res, alg, seq, status;
+	int groups[] = { pasn->group, 0 };
+	int ret;
+
+	if (!wd)
+		return -1;
+
+	data = wpabuf_head_u8(wd);
+	buf_len = wpabuf_len(wd);
+
+	if (buf_len < 6) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
+			   buf_len);
+		return -1;
+	}
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 1 ||
+	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
+		return -1;
+	}
+
+	sae_clear_data(&pasn->sae);
+	pasn->sae.state = SAE_NOTHING;
+
+	ret = sae_set_group(&pasn->sae, pasn->group);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+		return -1;
+	}
+
+	if (!pasn->password || !pasn->pt) {
+		wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
+		return -1;
+	}
+
+	ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr,
+				    NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+		return -1;
+	}
+
+	res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
+			       groups, 0, NULL);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
+		return -1;
+	}
+
+	/* Process the commit message and derive the PMK */
+	ret = sae_process_commit(&pasn->sae);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+		return -1;
+	}
+
+	pasn->sae.state = SAE_COMMITTED;
+
+	return 0;
+}
+
+
+static int pasn_wd_handle_sae_confirm(struct pasn_data *pasn,
+				      const u8 *peer_addr, struct wpabuf *wd)
+{
+	const u8 *data;
+	size_t buf_len;
+	u16 res, alg, seq, status;
+
+	if (!wd)
+		return -1;
+
+	data = wpabuf_head_u8(wd);
+	buf_len = wpabuf_len(wd);
+
+	if (buf_len < 6) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
+			   buf_len);
+		return -1;
+	}
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+		return -1;
+	}
+
+	res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6, NULL);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+		return -1;
+	}
+
+	pasn->sae.state = SAE_ACCEPTED;
+
+	/*
+	 * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
+	 * PASN/SAE should only be allowed with future PASN only. For now do not
+	 * restrict this only for PASN.
+	 */
+	if (pasn->disable_pmksa_caching)
+		return 0;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE",
+			pasn->sae.pmk, pasn->sae.pmk_len);
+	if (!pasn->sae.akmp)
+		pasn->sae.akmp = WPA_KEY_MGMT_SAE;
+
+	pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len,
+			     pasn->sae.pmkid, NULL, 0, pasn->own_addr,
+			     peer_addr, 0, NULL, pasn->sae.akmp);
+	return 0;
+}
+
+
+static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn)
+{
+	struct wpabuf *buf = NULL;
+	u8 *len_ptr;
+	size_t len;
+
+	/* Need to add the entire Authentication frame body */
+	buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+		return NULL;
+	}
+
+	/* Need to add the entire authentication frame body for the commit */
+	len_ptr = wpabuf_put(buf, 2);
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+	/* Write the actual commit and update the length accordingly */
+	sae_write_commit(&pasn->sae, buf, NULL, 0);
+	len = wpabuf_len(buf);
+	WPA_PUT_LE16(len_ptr, len - 2);
+
+	/* Need to add the entire Authentication frame body for the confirm */
+	len_ptr = wpabuf_put(buf, 2);
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	sae_write_confirm(&pasn->sae, buf);
+	WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
+
+	pasn->sae.state = SAE_CONFIRMED;
+
+	return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn)
+{
+	struct pasn_fils *fils = &pasn->fils;
+	struct wpabuf *buf = NULL;
+
+	if (!fils->erp_resp) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		return NULL;
+
+	/* Add the authentication algorithm */
+	wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+	/* Authentication Transaction seq# */
+	wpabuf_put_le16(buf, 2);
+
+	/* Status Code */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	/* Own RSNE */
+	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+
+	/* FILS Nonce */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
+
+	/* Wrapped Data */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+	wpabuf_put_buf(buf, fils->erp_resp);
+
+	return buf;
+}
+
+#endif /* CONFIG_FILS */
+
+static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn)
+{
+	switch (pasn->akmp) {
+	case WPA_KEY_MGMT_PASN:
+		/* no wrapped data */
+		return NULL;
+	case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+		return pasn_get_sae_wd(pasn);
+#else /* CONFIG_SAE */
+		wpa_printf(MSG_ERROR,
+			   "PASN: SAE: Cannot derive wrapped data");
+		return NULL;
+#endif /* CONFIG_SAE */
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+		return pasn_get_fils_wd(pasn);
+#endif /* CONFIG_FILS */
+		/* fall through */
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+	default:
+		wpa_printf(MSG_ERROR,
+			   "PASN: TODO: Wrapped data for akmp=0x%x",
+			   pasn->akmp);
+		return NULL;
+	}
+}
+
+
+static int
+pasn_derive_keys(struct pasn_data *pasn,
+		 const u8 *own_addr, const u8 *peer_addr,
+		 const u8 *cached_pmk, size_t cached_pmk_len,
+		 struct wpa_pasn_params_data *pasn_data,
+		 struct wpabuf *wrapped_data,
+		 struct wpabuf *secret)
+{
+	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+	u8 pmk[PMK_LEN_MAX];
+	u8 pmk_len;
+	int ret;
+
+	os_memset(pmk, 0, sizeof(pmk));
+	pmk_len = 0;
+
+	if (!cached_pmk || !cached_pmk_len)
+		wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
+
+	if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+		wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+		pmk_len = WPA_PASN_PMK_LEN;
+		os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
+	} else if (cached_pmk && cached_pmk_len) {
+		wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
+
+		pmk_len = cached_pmk_len;
+		os_memcpy(pmk, cached_pmk, cached_pmk_len);
+	} else {
+		switch (pasn->akmp) {
+#ifdef CONFIG_SAE
+		case WPA_KEY_MGMT_SAE:
+			if (pasn->sae.state == SAE_COMMITTED) {
+				pmk_len = PMK_LEN;
+				os_memcpy(pmk, pasn->sae.pmk, PMK_LEN);
+				break;
+			}
+#endif /* CONFIG_SAE */
+			/* fall through */
+		default:
+			/* TODO: Derive PMK based on wrapped data */
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Missing PMK derivation");
+			return -1;
+		}
+	}
+
+	ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr,
+			      wpabuf_head(secret), wpabuf_len(secret),
+			      &pasn->ptk, pasn->akmp,
+			      pasn->cipher, pasn->kdk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+		return -1;
+	}
+
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp,
+				      pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed to derive LTF keyseed");
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+	return 0;
+}
+
+
+static void handle_auth_pasn_comeback(struct pasn_data *pasn,
+				      const u8 *own_addr, const u8 *peer_addr,
+				      u16 group)
+{
+	struct wpabuf *buf, *comeback;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: Building comeback frame 2. Comeback after=%u",
+		   pasn->comeback_after);
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		return;
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2,
+				   WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
+
+	/*
+	 * Do not include the group as a part of the token since it is not going
+	 * to be used.
+	 */
+	comeback = auth_build_token_req(&pasn->last_comeback_key_update,
+					pasn->comeback_key, pasn->comeback_idx,
+					pasn->comeback_pending_idx,
+					sizeof(u16) * COMEBACK_PENDING_IDX_SIZE,
+					0, peer_addr, 0);
+	if (!comeback) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed sending auth with comeback");
+		wpabuf_free(buf);
+		return;
+	}
+
+	wpa_pasn_add_parameter_ie(buf, group,
+				  WPA_PASN_WRAPPED_DATA_NO,
+				  NULL, 0, comeback,
+				  pasn->comeback_after);
+	wpabuf_free(comeback);
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr));
+
+	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+			      wpabuf_len(buf), 0, 0, 0);
+	if (ret)
+		wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
+
+	wpabuf_free(buf);
+}
+
+
+int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
+			  const u8 *peer_addr,
+			  struct rsn_pmksa_cache_entry *pmksa, u16 status)
+{
+	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+	struct wpabuf *rsn_buf = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len;
+	u8 *ptr;
+	const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
+	u8 *data_buf = NULL;
+	size_t frame_len, data_len;
+	int ret;
+	const u8 *pmkid = NULL;
+
+	wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2,
+				   status);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		goto done;
+
+	if (pmksa && pasn->custom_pmkid_valid)
+		pmkid = pasn->custom_pmkid;
+	else if (pmksa) {
+		pmkid = pmksa->pmkid;
+#ifdef CONFIG_SAE
+	} else if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+		wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
+		pmkid = pasn->sae.pmkid;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	} else if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+		   pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+		wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
+		pmkid = pasn->fils.erp_pmkid;
+#endif /* CONFIG_FILS */
+	}
+
+	if (wpa_pasn_add_rsne(buf, pmkid,
+			      pasn->akmp, pasn->cipher) < 0)
+		goto fail;
+
+	/* No need to derive PMK if PMKSA is given */
+	if (!pmksa)
+		wrapped_data_buf = pasn_get_wrapped_data(pasn);
+	else
+		pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
+
+	/* Get public key */
+	pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+	pubkey = wpabuf_zeropad(pubkey,
+				crypto_ecdh_prime_len(pasn->ecdh));
+	if (!pubkey) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+		goto fail;
+	}
+
+	wpa_pasn_add_parameter_ie(buf, pasn->group,
+				  pasn->wrapped_data_format,
+				  pubkey, true, NULL, 0);
+
+	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+		goto fail;
+
+	wpabuf_free(wrapped_data_buf);
+	wrapped_data_buf = NULL;
+	wpabuf_free(pubkey);
+	pubkey = NULL;
+
+	/* Add RSNXE if needed */
+	rsnxe_ie = pasn->rsnxe_ie;
+	if (rsnxe_ie)
+		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
+
+	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
+	/* Add the mic */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, mic_len);
+	ptr = wpabuf_put(buf, mic_len);
+
+	os_memset(ptr, 0, mic_len);
+
+	frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+	frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+	if (pasn->rsn_ie && pasn->rsn_ie_len) {
+		rsn_ie = pasn->rsn_ie;
+	} else {
+		/*
+		 * Note: when pasn->rsn_ie is NULL, it is likely that Beacon
+		 * frame RSNE is not initialized. This is possible in case of
+		 * PASN authentication used for Wi-Fi Aware for which Beacon
+		 * frame RSNE and RSNXE are same as RSNE and RSNXE in the
+		 * Authentication frame.
+		 */
+		rsn_buf = wpabuf_alloc(500);
+		if (!rsn_buf)
+			goto fail;
+
+		if (wpa_pasn_add_rsne(rsn_buf, pmkid,
+				      pasn->akmp, pasn->cipher) < 0)
+			goto fail;
+
+		rsn_ie = wpabuf_head_u8(rsn_buf);
+	}
+
+	/*
+	 * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
+	 * MDE, etc. Thus, do not use the returned length but instead use the
+	 * length specified in the IE header.
+	 */
+	data_len = rsn_ie[1] + 2;
+	if (rsnxe_ie) {
+		data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
+		if (!data_buf)
+			goto fail;
+
+		os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
+		os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
+		data_len += rsnxe_ie[1] + 2;
+		data = data_buf;
+	} else {
+		data = rsn_ie;
+	}
+
+	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+		       own_addr, peer_addr, data, data_len,
+		       frame, frame_len, mic);
+	os_free(data_buf);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
+		goto fail;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (pasn->corrupt_mic) {
+		wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
+		mic[0] = ~mic[0];
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_memcpy(ptr, mic, mic_len);
+
+done:
+	wpa_printf(MSG_DEBUG,
+		   "PASN: Building frame 2: success; resp STA=" MACSTR,
+		   MAC2STR(peer_addr));
+
+	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
+			      wpabuf_len(buf), 0, 0, 0);
+	if (ret)
+		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
+
+	wpabuf_free(rsn_buf);
+	wpabuf_free(buf);
+	return ret;
+fail:
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+	wpabuf_free(rsn_buf);
+	wpabuf_free(buf);
+	return -1;
+}
+
+
+int handle_auth_pasn_1(struct pasn_data *pasn,
+		       const u8 *own_addr, const u8 *peer_addr,
+		       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn_data;
+	struct wpa_pasn_params_data pasn_params;
+	struct rsn_pmksa_cache_entry *pmksa = NULL;
+	const u8 *cached_pmk = NULL;
+	size_t cached_pmk_len = 0;
+	struct wpabuf *wrapped_data = NULL, *secret = NULL;
+	const int *groups = pasn->pasn_groups;
+	static const int default_groups[] = { 19, 0 };
+	u16 status = WLAN_STATUS_SUCCESS;
+	int ret, inc_y;
+	bool derive_keys;
+	u32 i;
+
+	if (!groups)
+		groups = default_groups;
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	if (!elems.rsn_ie) {
+		wpa_printf(MSG_DEBUG, "PASN: No RSNE");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				   &rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+	    !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+		status = WLAN_STATUS_INVALID_RSNIE;
+		goto send_resp;
+	}
+
+	pasn->akmp = rsn_data.key_mgmt;
+	pasn->cipher = rsn_data.pairwise_cipher;
+
+	if (pasn->derive_kdk &&
+	    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+				      WLAN_RSNX_CAPAB_SECURE_LTF))
+		pasn->secure_ltf = true;
+
+	if (pasn->derive_kdk)
+		pasn->kdk_len = WPA_KDK_MAX_LEN;
+	else
+		pasn->kdk_len = 0;
+
+	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: No PASN Parameters element found");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto send_resp;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  false, &pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed validation of PASN Parameters IE");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto send_resp;
+	}
+
+	for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
+		;
+
+	if (!pasn_params.group || groups[i] != pasn_params.group) {
+		wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
+			   pasn_params.group);
+		status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		goto send_resp;
+	}
+
+	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+		status = WLAN_STATUS_INVALID_PARAMETERS;
+		goto send_resp;
+	}
+
+	if (pasn_params.comeback) {
+		wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
+
+		ret = check_comeback_token(pasn->comeback_key,
+					   pasn->comeback_pending_idx,
+					   peer_addr,
+					   pasn_params.comeback,
+					   pasn_params.comeback_len);
+
+		if (ret) {
+			wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
+			status = WLAN_STATUS_INVALID_PARAMETERS;
+			goto send_resp;
+		}
+	} else if (pasn->use_anti_clogging) {
+		wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
+		handle_auth_pasn_comeback(pasn, own_addr, peer_addr,
+					  pasn_params.group);
+		return -1;
+	}
+
+	pasn->ecdh = crypto_ecdh_init(pasn_params.group);
+	if (!pasn->ecdh) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	pasn->group = pasn_params.group;
+
+	if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+		inc_y = 1;
+	} else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+		   pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+		inc_y = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid first octet in pubkey=0x%x",
+			   pasn_params.pubkey[0]);
+		status = WLAN_STATUS_INVALID_PUBLIC_KEY;
+		goto send_resp;
+	}
+
+	secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+					 pasn_params.pubkey + 1,
+					 pasn_params.pubkey_len - 1);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	derive_keys = true;
+	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto send_resp;
+		}
+
+#ifdef CONFIG_SAE
+		if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+			ret = pasn_wd_handle_sae_commit(pasn, own_addr,
+							peer_addr,
+							wrapped_data);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: Failed processing SAE commit");
+				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto send_resp;
+			}
+		}
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+		if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+		    pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+			if (!pasn->fils_wd_valid) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: Invalid FILS wrapped data");
+				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto send_resp;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FILS: Pending AS response");
+
+			/*
+			 * With PASN/FILS, keys can be derived only after a
+			 * response from the AS is processed.
+			 */
+			derive_keys = false;
+		}
+#endif /* CONFIG_FILS */
+	}
+
+	pasn->wrapped_data_format = pasn_params.wrapped_data_format;
+
+	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+				   len - IEEE80211_HDRLEN, pasn->hash);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto send_resp;
+	}
+
+	if (!derive_keys) {
+		wpa_printf(MSG_DEBUG, "PASN: Storing secret");
+		pasn->secret = secret;
+		wpabuf_free(wrapped_data);
+		return 0;
+	}
+
+	if (rsn_data.num_pmkid) {
+		if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R_AP
+			wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
+
+			if (!pasn->pmk_r1_len) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: FT: Failed getting PMK-R1");
+				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto send_resp;
+			}
+			cached_pmk = pasn->pmk_r1;
+			cached_pmk_len = pasn->pmk_r1_len;
+#else /* CONFIG_IEEE80211R_AP */
+			wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto send_resp;
+#endif /* CONFIG_IEEE80211R_AP */
+		} else {
+			wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
+
+			if (pasn->pmksa) {
+				const u8 *pmkid = NULL;
+
+				if (pasn->custom_pmkid_valid) {
+					ret = pasn->validate_custom_pmkid(
+						pasn->cb_ctx, peer_addr,
+						rsn_data.pmkid);
+					if (ret) {
+						wpa_printf(MSG_DEBUG,
+							   "PASN: Failed custom PMKID validation");
+						status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+						goto send_resp;
+					}
+				} else {
+					pmkid = rsn_data.pmkid;
+				}
+
+				pmksa = pmksa_cache_auth_get(pasn->pmksa,
+							     peer_addr,
+							     pmkid);
+				if (pmksa) {
+					cached_pmk = pmksa->pmk;
+					cached_pmk_len = pmksa->pmk_len;
+				}
+			}
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
+	}
+
+	ret = pasn_derive_keys(pasn, own_addr, peer_addr,
+			       cached_pmk, cached_pmk_len,
+			       &pasn_params, wrapped_data, secret);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
+		status = WLAN_STATUS_PASN_BASE_AKMP_FAILED;
+		goto send_resp;
+	}
+
+	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
+				   len - IEEE80211_HDRLEN, pasn->hash);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+send_resp:
+	ret = handle_auth_pasn_resp(pasn, own_addr, peer_addr, pmksa, status);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Success handling transaction == 1");
+	}
+
+	wpabuf_free(secret);
+	wpabuf_free(wrapped_data);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return -1;
+
+	return 0;
+}
+
+
+int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
+		       const u8 *peer_addr,
+		       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wpa_pasn_params_data pasn_params;
+	struct wpabuf *wrapped_data = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len;
+	int ret;
+	u8 *copy = NULL;
+	size_t copy_len, mic_offset;
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		goto fail;
+	}
+
+	/* Check that the MIC IE exists. Save it and zero out the memory. */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	if (!elems.mic || elems.mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid MIC. Expecting len=%u", mic_len);
+		goto fail;
+	}
+	os_memcpy(mic, elems.mic, mic_len);
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: No PASN Parameters element found");
+		goto fail;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  false, &pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed validation of PASN Parameters IE");
+		goto fail;
+	}
+
+	if (pasn_params.pubkey || pasn_params.pubkey_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Public key should not be included");
+		goto fail;
+	}
+
+	/* Verify the MIC */
+	copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+	mic_offset = elems.mic - (const u8 *) &mgmt->u.auth;
+	copy_len = len - offsetof(struct ieee80211_mgmt, u.auth);
+	if (mic_offset + mic_len > copy_len)
+		goto fail;
+	copy = os_memdup(&mgmt->u.auth, copy_len);
+	if (!copy)
+		goto fail;
+	os_memset(copy + mic_offset, 0, mic_len);
+	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+		       peer_addr, own_addr,
+		       pasn->hash, mic_len * 2,
+		       copy, copy_len, out_mic);
+	os_free(copy);
+	copy = NULL;
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
+	if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
+		goto fail;
+	}
+
+	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+			goto fail;
+		}
+
+#ifdef CONFIG_SAE
+		if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+			ret = pasn_wd_handle_sae_confirm(pasn, peer_addr,
+							 wrapped_data);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: Failed processing SAE confirm");
+				wpabuf_free(wrapped_data);
+				goto fail;
+			}
+		}
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+		if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+		    pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+			if (wrapped_data) {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: FILS: Ignore wrapped data");
+			}
+		}
+#endif /* CONFIG_FILS */
+		wpabuf_free(wrapped_data);
+	}
+
+	wpa_printf(MSG_INFO,
+		   "PASN: Success handling transaction == 3. Store PTK");
+	return 0;
+
+fail:
+	os_free(copy);
+	return -1;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/src/radius/radius.c b/src/radius/radius.c
index a642280..be59a94 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -176,6 +176,7 @@
 	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
+	{ RADIUS_ATTR_FILTER_ID, "Filter-Id", RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 177c64a..571c159 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -62,6 +62,7 @@
        RADIUS_ATTR_NAS_PORT = 5,
        RADIUS_ATTR_SERVICE_TYPE = 6,
        RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
+       RADIUS_ATTR_FILTER_ID = 11,
        RADIUS_ATTR_FRAMED_MTU = 12,
        RADIUS_ATTR_REPLY_MESSAGE = 18,
        RADIUS_ATTR_STATE = 24,
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 93cc9a7..e7b4d54 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -30,6 +30,7 @@
 			enum pmksa_free_reason reason);
 	bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
 			      void *ctx);
+	void (*notify_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
 	void *ctx;
 };
 
@@ -47,16 +48,46 @@
 				   struct rsn_pmksa_cache_entry *entry,
 				   enum pmksa_free_reason reason)
 {
-	wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
-			    entry->pmkid,
-			    entry->fils_cache_id_set ? entry->fils_cache_id :
-			    NULL);
+	if (pmksa->sm)
+		wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+				    entry->pmkid,
+				    entry->fils_cache_id_set ?
+				    entry->fils_cache_id : NULL);
 	pmksa->pmksa_count--;
-	pmksa->free_cb(entry, pmksa->ctx, reason);
+	if (pmksa->free_cb)
+		pmksa->free_cb(entry, pmksa->ctx, reason);
 	_pmksa_cache_free_entry(entry);
 }
 
 
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+			struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *e;
+
+	e = pmksa->pmksa;
+	while (e) {
+		if (e == entry) {
+			pmksa->pmksa = entry->next;
+			break;
+		}
+		if (e->next == entry) {
+			e->next = entry->next;
+			break;
+		}
+	}
+
+	if (!e) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Could not remove PMKSA cache entry %p since it is not in the list",
+			   entry);
+		return;
+	}
+
+	pmksa_cache_free_entry(pmksa, entry, PMKSA_FREE);
+}
+
+
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
@@ -66,7 +97,7 @@
 
 	os_get_reltime(&now);
 	while (entry && entry->expiration <= now.sec) {
-		if (wpa_key_mgmt_sae(entry->akmp) &&
+		if (wpa_key_mgmt_sae(entry->akmp) && pmksa->is_current_cb &&
 		    pmksa->is_current_cb(entry, pmksa->ctx)) {
 			/* Do not expire the currently used PMKSA entry for SAE
 			 * since there is no convenient mechanism for
@@ -99,6 +130,10 @@
 static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+
+	if (!pmksa->sm)
+		return;
+
 	pmksa->sm->cur_pmksa = NULL;
 	eapol_sm_request_reauth(pmksa->sm->eapol);
 }
@@ -119,6 +154,7 @@
 	if (sec < 0) {
 		sec = 0;
 		if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
+		    pmksa->is_current_cb &&
 		    pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
 			/* Do not continue polling for the current PMKSA entry
 			 * from SAE to expire every second. Use the expiration
@@ -139,8 +175,11 @@
 	}
 	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
 
+	if (!pmksa->sm)
+		return;
+
 	entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
-		pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
+		pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, NULL, 0);
 	if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
 		sec = pmksa->pmksa->reauth_time - now.sec;
 		if (sec < 0)
@@ -179,6 +218,8 @@
 {
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
+	unsigned int pmk_lifetime = 43200;
+	unsigned int pmk_reauth_threshold = 70;
 
 	if (pmk_len > PMK_LEN_MAX)
 		return NULL;
@@ -200,15 +241,21 @@
 	else
 		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
 	os_get_reltime(&now);
-	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
-	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
-		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+	if (pmksa->sm) {
+		pmk_lifetime = pmksa->sm->dot11RSNAConfigPMKLifetime;
+		pmk_reauth_threshold =
+			pmksa->sm->dot11RSNAConfigPMKReauthThreshold;
+	}
+	entry->expiration = now.sec + pmk_lifetime;
+	entry->reauth_time = now.sec +
+		pmk_lifetime * pmk_reauth_threshold / 100;
 	entry->akmp = akmp;
 	if (cache_id) {
 		entry->fils_cache_id_set = 1;
 		os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
 	}
 	os_memcpy(entry->aa, aa, ETH_ALEN);
+	os_memcpy(entry->spa, spa, ETH_ALEN);
 	entry->network_ctx = network_ctx;
 
 	return pmksa_cache_add_entry(pmksa, entry);
@@ -226,7 +273,8 @@
 	pos = pmksa->pmksa;
 	prev = NULL;
 	while (pos) {
-		if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
+		if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0 &&
+		    os_memcmp(entry->spa, pos->spa, ETH_ALEN) == 0) {
 			if (pos->pmk_len == entry->pmk_len &&
 			    os_memcmp_const(pos->pmk, entry->pmk,
 					    entry->pmk_len) == 0 &&
@@ -269,7 +317,7 @@
 		/* Remove the oldest entry to make room for the new entry */
 		pos = pmksa->pmksa;
 
-		if (pos == pmksa->sm->cur_pmksa) {
+		if (pmksa->sm && pos == pmksa->sm->cur_pmksa) {
 			/*
 			 * Never remove the current PMKSA cache entry, since
 			 * it's in use, and removing it triggers a needless
@@ -308,9 +356,16 @@
 	}
 	pmksa->pmksa_count++;
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
-		   " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
+		   " spa=" MACSTR " network_ctx=%p akmp=0x%x",
+		   MAC2STR(entry->aa), MAC2STR(entry->spa),
 		   entry->network_ctx, entry->akmp);
-	wpas_notify_pmk_cache_added((struct wpa_supplicant *)pmksa->sm->ctx->ctx, entry);
+
+	if (!pmksa->sm)
+		return entry;
+
+	if (pmksa->notify_cb)
+		pmksa->notify_cb(entry, pmksa->ctx);
+
 	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
 			 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
 			 entry->pmk, entry->pmk_len,
@@ -398,13 +453,15 @@
  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
  */
 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
-					       const u8 *aa, const u8 *pmkid,
+					       const u8 *aa, const u8 *spa,
+					       const u8 *pmkid,
 					       const void *network_ctx,
 					       int akmp)
 {
 	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 	while (entry) {
 		if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+		    (!spa || os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
 		    (pmkid == NULL ||
 		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
 		    (!akmp || akmp == entry->akmp) &&
@@ -426,6 +483,9 @@
 	os_time_t old_reauth_time = old_entry->reauth_time;
 	const u8 *pmkid = NULL;
 
+	if (!pmksa->sm)
+		return NULL;
+
 	if (wpa_key_mgmt_sae(old_entry->akmp) ||
 	    wpa_key_mgmt_fils(old_entry->akmp))
 		pmkid = old_entry->pmkid;
@@ -577,11 +637,11 @@
 
 	sm->cur_pmksa = NULL;
 	if (pmkid)
-		sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
-						network_ctx, akmp);
+		sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, sm->own_addr,
+						pmkid, network_ctx, akmp);
 	if (sm->cur_pmksa == NULL && bssid)
-		sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
-						network_ctx, akmp);
+		sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, sm->own_addr,
+						NULL, network_ctx, akmp);
 	if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
 		sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
 							      network_ctx,
@@ -700,6 +760,8 @@
 				 void *ctx, enum pmksa_free_reason reason),
 		 bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
 				       void *ctx),
+		 void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx),
 		 void *ctx, struct wpa_sm *sm)
 {
 	struct rsn_pmksa_cache *pmksa;
@@ -708,6 +770,7 @@
 	if (pmksa) {
 		pmksa->free_cb = free_cb;
 		pmksa->is_current_cb = is_current_cb;
+		pmksa->notify_cb = notify_cb;
 		pmksa->ctx = ctx;
 		pmksa->sm = sm;
 	}
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index b801268..48c9e04 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -20,6 +20,7 @@
 	os_time_t expiration;
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 aa[ETH_ALEN];
+	u8 spa[ETH_ALEN];
 
 	/*
 	 * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
@@ -61,10 +62,13 @@
 				 void *ctx, enum pmksa_free_reason reason),
 		 bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
 				       void *ctx),
+		 void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx),
 		 void *ctx, struct wpa_sm *sm);
 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
-					       const u8 *aa, const u8 *pmkid,
+					       const u8 *aa, const u8 *spa,
+					       const u8 *pmkid,
 					       const void *network_ctx,
 					       int akmp);
 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
@@ -88,6 +92,8 @@
 			      void *network_ctx, const u8 *aa, int akmp);
 void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
 		       const u8 *pmk, size_t pmk_len, bool external_only);
+void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+			struct rsn_pmksa_cache_entry *entry);
 void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
 
 #else /* IEEE8021X_EAPOL */
@@ -97,6 +103,8 @@
 				 void *ctx, enum pmksa_free_reason reason),
 		 bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
 				       void *ctx),
+		 void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx),
 		 void *ctx, struct wpa_sm *sm)
 {
 	return (void *) -1;
@@ -107,8 +115,8 @@
 }
 
 static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
-		const void *network_ctx, int akmp)
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+		const u8 *pmkid, const void *network_ctx, int akmp)
 {
 	return NULL;
 }
@@ -168,6 +176,11 @@
 {
 }
 
+static inline void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
+				      struct rsn_pmksa_cache_entry *entry)
+{
+}
+
 static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
 {
 }
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 1a38bf6..8f86820 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -75,7 +75,8 @@
 		return;
 	}
 
-	eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+	eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len,
+			  FRAME_ENCRYPTION_UNKNOWN);
 }
 
 
@@ -329,7 +330,8 @@
 	dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
 			      struct rsn_pmksa_candidate, list) {
 		struct rsn_pmksa_cache_entry *p = NULL;
-		p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0);
+		p = pmksa_cache_get(sm->pmksa, candidate->bssid, sm->own_addr,
+				    NULL, NULL, 0);
 		if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
 		    (p == NULL || p->opportunistic)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
@@ -490,7 +492,7 @@
 	if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
 		return;
 
-	pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0);
+	pmksa = pmksa_cache_get(sm->pmksa, bssid, sm->own_addr, NULL, NULL, 0);
 	if (pmksa && (!pmksa->opportunistic ||
 		      !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
 		return;
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index c26a63d..1531f51 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -180,7 +180,7 @@
 
 static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
-	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+	if (wpa_sm_set_key(sm, -1, WPA_ALG_NONE, peer->addr,
 			   0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
 			   "the driver");
@@ -230,7 +230,7 @@
 
 	wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
 		   MAC2STR(peer->addr));
-	if (wpa_sm_set_key(sm, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
+	if (wpa_sm_set_key(sm, -1, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
 			   peer->tpk.tk, key_len,
 			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
@@ -465,23 +465,26 @@
  * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
  * @kck: TPK-KCK
  * @lnkid: Pointer to the beginning of Link Identifier IE
- * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @rsne: Pointer to the beginning of RSNE used for handshake
+ * @rsne_len: Length of RSNE in octets
  * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
  * @mic: Pointer for writing MIC
  *
  * Calculate MIC for TDLS frame.
  */
 static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
-			     const u8 *rsnie, const u8 *timeoutie,
-			     const u8 *ftie, u8 *mic)
+			     const u8 *rsne, size_t rsne_len,
+			     const u8 *timeoutie,
+			     const u8 *fte, size_t fte_len, u8 *mic)
 {
 	u8 *buf, *pos;
 	struct wpa_tdls_ftie *_ftie;
 	const struct wpa_tdls_lnkid *_lnkid;
 	int ret;
-	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
-		2 + timeoutie[1] + 2 + ftie[1];
+	int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + rsne_len +
+		2 + timeoutie[1] + fte_len;
 	buf = os_zalloc(len);
 	if (!buf) {
 		wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
@@ -502,16 +505,16 @@
 	os_memcpy(pos, lnkid, 2 + lnkid[1]);
 	pos += 2 + lnkid[1];
 	/* 5) RSN IE */
-	os_memcpy(pos, rsnie, 2 + rsnie[1]);
-	pos += 2 + rsnie[1];
+	os_memcpy(pos, rsne, rsne_len);
+	pos += rsne_len;
 	/* 6) Timeout Interval IE */
 	os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
 	pos += 2 + timeoutie[1];
 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
-	os_memcpy(pos, ftie, 2 + ftie[1]);
+	os_memcpy(pos, fte, fte_len);
 	_ftie = (struct wpa_tdls_ftie *) pos;
 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
-	pos += 2 + ftie[1];
+	pos += fte_len;
 
 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -529,14 +532,15 @@
  * @rcode: Reason code for Teardown
  * @dtoken: Dialog Token used for that particular link
  * @lnkid: Pointer to the beginning of Link Identifier IE
- * @ftie: Pointer to the beginning of FT IE
+ * @fte: Pointer to the beginning of FTE
+ * @fre_len: Length of FTE in octets
  * @mic: Pointer for writing MIC
  *
  * Calculate MIC for TDLS frame.
  */
 static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
 				     u8 dtoken, const u8 *lnkid,
-				     const u8 *ftie, u8 *mic)
+				     const u8 *fte, size_t fte_len, u8 *mic)
 {
 	u8 *buf, *pos;
 	struct wpa_tdls_ftie *_ftie;
@@ -547,7 +551,7 @@
 		return -1;
 
 	len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
-		sizeof(trans_seq) + 2 + ftie[1];
+		sizeof(trans_seq) + fte_len;
 
 	buf = os_zalloc(len);
 	if (!buf) {
@@ -567,10 +571,10 @@
 	/* 4) Transaction Sequence number */
 	*pos++ = trans_seq;
 	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
-	os_memcpy(pos, ftie, 2 + ftie[1]);
+	os_memcpy(pos, fte, fte_len);
 	_ftie = (struct wpa_tdls_ftie *) pos;
 	os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
-	pos += 2 + ftie[1];
+	pos += fte_len;
 
 	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
 	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
@@ -584,14 +588,15 @@
 static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
 					  struct wpa_tdls_peer *peer,
 					  const u8 *lnkid, const u8 *timeoutie,
-					  const struct wpa_tdls_ftie *ftie)
+					  const struct wpa_tdls_ftie *ftie,
+					  size_t fte_len)
 {
 	u8 mic[16];
 
 	if (peer->tpk_set) {
 		wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
-				  peer->rsnie_p, timeoutie, (u8 *) ftie,
-				  mic);
+				  peer->rsnie_p, peer->rsnie_p_len, timeoutie,
+				  (const u8 *) ftie, fte_len, mic);
 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
 				   "dropping packet");
@@ -612,13 +617,14 @@
 
 static int wpa_supplicant_verify_tdls_mic_teardown(
 	u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
-	const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+	const u8 *lnkid, const struct wpa_tdls_ftie *ftie, size_t fte_len)
 {
 	u8 mic[16];
 
 	if (peer->tpk_set) {
 		wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
-					  dtoken, lnkid, (u8 *) ftie, mic);
+					  dtoken, lnkid, (const u8 *) ftie,
+					  fte_len, mic);
 		if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 			wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
 				   "dropping packet");
@@ -830,7 +836,8 @@
 	/* compute MIC before sending */
 	wpa_tdls_linkid(sm, peer, &lnkid);
 	wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
-				  dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+				  dialog_token, (const u8 *) &lnkid,
+				  (const u8 *) ftie, 2 + ftie->ie_len,
 				  ftie->mic);
 
 skip_ies:
@@ -1000,7 +1007,8 @@
 	/* Process MIC check to see if TDLS Teardown is right */
 	if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
 						    peer->dtoken, peer,
-						    (u8 *) lnkid, ftie) < 0) {
+						    (const u8 *) lnkid,
+						    ftie, kde.ftie_len) < 0) {
 		wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
 			   "Teardown Request from " MACSTR, MAC2STR(src_addr));
 		return -1;
@@ -1320,8 +1328,9 @@
 		   lifetime);
 
 	/* compute MIC before sending */
-	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
-			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+	wpa_tdls_ftie_mic(peer->tpk.kck, 2, (const u8 *) lnkid, peer->rsnie_p,
+			  peer->rsnie_p_len, (const u8 *) &timeoutie,
+			  (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
 #ifdef CONFIG_TDLS_TESTING
 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -1410,8 +1419,9 @@
 		   lifetime);
 
 	/* compute MIC before sending */
-	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
-			  (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+	wpa_tdls_ftie_mic(peer->tpk.kck, 3, (const u8 *) lnkid, peer->rsnie_p,
+			  peer->rsnie_p_len, (const u8 *) &timeoutie,
+			  (const u8 *) ftie, 2 + ftie->ie_len, ftie->mic);
 #ifdef CONFIG_TDLS_TESTING
 	if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
@@ -2483,8 +2493,9 @@
 	wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
 
 	/* Process MIC check to see if TPK M2 is right */
-	if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
-					   (u8 *) timeoutie, ftie) < 0) {
+	if (wpa_supplicant_verify_tdls_mic(2, peer, (const u8 *) lnkid,
+					   (const u8 *) timeoutie, ftie,
+					   kde.ftie_len) < 0) {
 		/* Discard the frame */
 		wpa_tdls_del_key(sm, peer);
 		wpa_tdls_disable_peer_link(sm, peer);
@@ -2657,8 +2668,9 @@
 		goto error;
 	}
 
-	if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
-					   (u8 *) timeoutie, ftie) < 0) {
+	if (wpa_supplicant_verify_tdls_mic(3, peer, (const u8 *) lnkid,
+					   (const u8 *) timeoutie, ftie,
+					   kde.ftie_len) < 0) {
 		wpa_tdls_del_key(sm, peer);
 		goto error;
 	}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 00b7e84..3a39886 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -38,6 +38,45 @@
 static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 
 
+static void _wpa_hexdump_link(int level, u8 link_id, const char *title,
+			      const void *buf, size_t len, bool key)
+{
+	char *link_title = NULL;
+
+	if (link_id >= MAX_NUM_MLD_LINKS)
+		goto out;
+
+	link_title = os_malloc(os_strlen(title) + 20);
+	if (!link_title)
+		goto out;
+
+	os_snprintf(link_title, os_strlen(title) + 20, "MLO link[%u]: %s",
+		    link_id, title);
+
+out:
+	if (key)
+		wpa_hexdump_key(level, link_title ? link_title : title, buf,
+				len);
+	else
+		wpa_hexdump(level, link_title ? link_title : title, buf, len);
+	os_free(link_title);
+}
+
+
+static void wpa_hexdump_link(int level, u8 link_id, const char *title,
+			     const void *buf, size_t len)
+{
+	_wpa_hexdump_link(level, link_id, title, buf, len, false);
+}
+
+
+static void wpa_hexdump_link_key(int level, u8 link_id, const char *title,
+				 const void *buf, size_t len)
+{
+	_wpa_hexdump_link(level, link_id, title, buf, len, true);
+}
+
+
 /**
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -187,7 +226,7 @@
 	u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
 
 	if (pairwise && sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
-	    wpa_sm_get_state(sm) == WPA_COMPLETED) {
+	    wpa_sm_get_state(sm) == WPA_COMPLETED && !error) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: PTK0 rekey not allowed, reconnecting");
 		wpa_sm_reconnect(sm);
@@ -280,7 +319,8 @@
 		 * not have enough time to get the association information
 		 * event before receiving this 1/4 message, so try to find a
 		 * matching PMKSA cache entry here. */
-		sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
+		sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr,
+						sm->own_addr, pmkid,
 						NULL, 0);
 		if (sm->cur_pmksa) {
 			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -302,7 +342,8 @@
 #ifdef CONFIG_IEEE80211R
 		sm->xxkey_len = 0;
 #ifdef CONFIG_SAE
-		if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
+		if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+		     sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) &&
 		    sm->pmk_len == PMK_LEN) {
 			/* Need to allow FT key derivation to proceed with
 			 * PMK from SAE being used as the XXKey in cases where
@@ -395,8 +436,8 @@
 						     fils_cache_id);
 			}
 			if (!sm->cur_pmksa && pmkid &&
-			    pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
-				    0)) {
+			    pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr,
+					    pmkid, NULL, 0)) {
 				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 					"RSN: the new PMK matches with the "
 					"PMKID");
@@ -553,6 +594,8 @@
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = ver | WPA_KEY_INFO_KEY_TYPE;
+	if (sm->ptk_set && sm->proto != WPA_PROTO_WPA)
+		key_info |= WPA_KEY_INFO_SECURE;
 	if (mic_len)
 		key_info |= WPA_KEY_INFO_MIC;
 	else
@@ -583,6 +626,7 @@
 static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
 			  const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
+	int ret;
 	const u8 *z = NULL;
 	size_t z_len = 0, kdk_len;
 	int akmp;
@@ -616,11 +660,23 @@
 	else
 		kdk_len = 0;
 
-	return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
-			      sm->own_addr, sm->bssid, sm->snonce,
-			      key->key_nonce, ptk, akmp,
-			      sm->pairwise_cipher, z, z_len,
-			      kdk_len);
+	ret = wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+			     sm->own_addr, wpa_sm_get_auth_addr(sm), sm->snonce,
+			     key->key_nonce, ptk, akmp,
+			     sm->pairwise_cipher, z, z_len,
+			     kdk_len);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "WPA: PTK derivation failed");
+		return ret;
+	}
+
+#ifdef CONFIG_PASN
+	if (sm->secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+		ret = wpa_ltf_keyseed(ptk, akmp, sm->pairwise_cipher);
+#endif /* CONFIG_PASN */
+
+	return ret;
 }
 
 
@@ -669,59 +725,106 @@
 }
 
 
-static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
-					  const unsigned char *src_addr,
-					  const struct wpa_eapol_key *key,
-					  u16 ver, const u8 *key_data,
-					  size_t key_data_len)
+static u8 * rsn_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = RSN_SELECTOR_LEN + data_len;
+	RSN_SELECTOR_PUT(pos, kde);
+	pos += RSN_SELECTOR_LEN;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+
+	return pos;
+}
+
+
+static size_t wpa_mlo_link_kde_len(struct wpa_sm *sm)
+{
+	int i;
+	unsigned int num_links = 0;
+
+	for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+		if (sm->mlo.assoc_link_id != i && (sm->mlo.req_links & BIT(i)))
+			num_links++;
+	}
+
+	return num_links * (RSN_SELECTOR_LEN + 1 + ETH_ALEN + 2);
+}
+
+
+static u8 * wpa_mlo_link_kde(struct wpa_sm *sm, u8 *pos)
+{
+	int i;
+	u8 hdr[1 + ETH_ALEN];
+
+	for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+		if (sm->mlo.assoc_link_id == i || !(sm->mlo.req_links & BIT(i)))
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "MLO: Add MLO Link %d KDE in EAPOL-Key 2/4", i);
+		hdr[0] = i & 0xF; /* LinkID; no RSNE or RSNXE */
+		os_memcpy(&hdr[1], sm->mlo.links[i].addr, ETH_ALEN);
+		pos = rsn_add_kde(pos, RSN_KEY_DATA_MLO_LINK, hdr, sizeof(hdr));
+	}
+
+	return pos;
+}
+
+
+static bool is_valid_ap_mld_mac_kde(struct wpa_sm *sm, const u8 *mac_kde)
+{
+	return mac_kde &&
+		os_memcmp(mac_kde, sm->mlo.ap_mld_addr, ETH_ALEN) == 0;
+}
+
+
+static void wpas_swap_tkip_mic_keys(struct wpa_ptk *ptk)
+{
+	u8 buf[8];
+
+	/* Supplicant: swap tx/rx Mic keys */
+	os_memcpy(buf, &ptk->tk[16], 8);
+	os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+	os_memcpy(&ptk->tk[24], buf, 8);
+	forced_memzero(buf, sizeof(buf));
+}
+
+
+static void wpa_supplicant_process_1_of_4_wpa(struct wpa_sm *sm,
+					      const unsigned char *src_addr,
+					      const struct wpa_eapol_key *key,
+					      u16 ver, const u8 *key_data,
+					      size_t key_data_len,
+					      enum frame_encryption encrypted)
 {
 	struct wpa_eapol_ie_parse ie;
 	struct wpa_ptk *ptk;
 	int res;
-	u8 *kde, *kde_buf = NULL;
-	size_t kde_len;
 
 	if (wpa_sm_get_network_ctx(sm) == NULL) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
-			"found (msg 1 of 4)");
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: No SSID info found (msg 1 of 4)");
 		return;
 	}
 
-	if (sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
-	    wpa_sm_get_state(sm) == WPA_COMPLETED) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: PTK0 rekey not allowed, reconnecting");
-		wpa_sm_reconnect(sm);
-		return;
-	}
-
-	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
-	wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RX message 1 of 4-Way "
-		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: RX message 1 of 4-Way Handshake from " MACSTR
+		" (ver=%d)", MAC2STR(src_addr), ver);
 
 	os_memset(&ie, 0, sizeof(ie));
 
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
-		/* RSN: msg 1/4 should contain PMKID for the selected PMK */
-		wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
-			    key_data, key_data_len);
-		if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
-			goto failed;
-		if (ie.pmkid) {
-			wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
-				    "Authenticator", ie.pmkid, PMKID_LEN);
-		}
-	}
-
 	res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
 	if (res == -2) {
-		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
-			"msg 1/4 - requesting full EAP authentication");
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Do not reply to msg 1/4 - requesting full EAP authentication");
 		return;
 	}
 	if (res)
 		goto failed;
 
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+
 	if (sm->renew_snonce) {
 		if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -738,23 +841,125 @@
 	ptk = &sm->tptk;
 	if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
 		goto failed;
-	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
-		u8 buf[8];
-		/* Supplicant: swap tx/rx Mic keys */
-		os_memcpy(buf, &ptk->tk[16], 8);
-		os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
-		os_memcpy(&ptk->tk[24], buf, 8);
-		forced_memzero(buf, sizeof(buf));
-	}
+	if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+		wpas_swap_tkip_mic_keys(ptk);
 	sm->tptk_set = 1;
 
+	if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+				       sm->snonce, sm->assoc_wpa_ie,
+				       sm->assoc_wpa_ie_len, ptk) < 0)
+		goto failed;
+
+	os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+	return;
+
+failed:
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+					  const unsigned char *src_addr,
+					  const struct wpa_eapol_key *key,
+					  u16 ver, const u8 *key_data,
+					  size_t key_data_len,
+					  enum frame_encryption encrypted)
+{
+	struct wpa_eapol_ie_parse ie;
+	struct wpa_ptk *ptk;
+	int res;
+	u8 *kde, *kde_buf = NULL;
+	size_t kde_len;
+	size_t mlo_kde_len = 0;
+
+	if (encrypted == FRAME_NOT_ENCRYPTED && sm->tk_set &&
+	    wpa_sm_pmf_enabled(sm)) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Discard unencrypted EAPOL-Key msg 1/4 when TK is set and PMF is enabled");
+		return;
+	}
+
+	if (wpa_sm_get_network_ctx(sm) == NULL) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
+			"found (msg 1 of 4)");
+		return;
+	}
+
+	if (sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
+	    wpa_sm_get_state(sm) == WPA_COMPLETED) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: PTK0 rekey not allowed, reconnecting");
+		wpa_sm_reconnect(sm);
+		return;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RX message 1 of 4-Way "
+		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+	os_memset(&ie, 0, sizeof(ie));
+
+	/* RSN: msg 1/4 should contain PMKID for the selected PMK */
+	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Discard EAPOL-Key msg 1/4 with invalid IEs/KDEs");
+		return;
+	}
+	if (ie.pmkid) {
+		wpa_hexdump(MSG_DEBUG, "RSN: PMKID from Authenticator",
+			    ie.pmkid, PMKID_LEN);
+	}
+
+	if (sm->mlo.valid_links && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+		wpa_printf(MSG_INFO,
+			   "RSN: Discard EAPOL-Key msg 1/4 with invalid AP MLD MAC address KDE");
+		return;
+	}
+
+	res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+	if (res == -2) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
+			"msg 1/4 - requesting full EAP authentication");
+		return;
+	}
+	if (res)
+		goto failed;
+
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+
+	if (sm->renew_snonce) {
+		if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Failed to get random data for SNonce");
+			goto failed;
+		}
+		sm->renew_snonce = 0;
+		wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+			    sm->snonce, WPA_NONCE_LEN);
+	}
+
+	/* Calculate PTK which will be stored as a temporary PTK until it has
+	 * been verified when processing message 3/4. */
+	ptk = &sm->tptk;
+	if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+		goto failed;
+	if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+		wpas_swap_tkip_mic_keys(ptk);
+	sm->tptk_set = 1;
+
+	/* Add MLO Link KDE and MAC KDE in M2 for ML connection */
+	if (sm->mlo.valid_links)
+		mlo_kde_len = wpa_mlo_link_kde_len(sm) +
+			RSN_SELECTOR_LEN + ETH_ALEN + 2;
+
 	kde = sm->assoc_wpa_ie;
 	kde_len = sm->assoc_wpa_ie_len;
 	kde_buf = os_malloc(kde_len +
 			    2 + RSN_SELECTOR_LEN + 3 +
 			    sm->assoc_rsnxe_len +
 			    2 + RSN_SELECTOR_LEN + 1 +
-			    2 + RSN_SELECTOR_LEN + 2);
+			    2 + RSN_SELECTOR_LEN + 2 + mlo_kde_len);
+
 	if (!kde_buf)
 		goto failed;
 	os_memcpy(kde_buf, kde, kde_len);
@@ -828,8 +1033,23 @@
 	}
 #endif /* CONFIG_DPP2 */
 
-	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       kde, kde_len, ptk) < 0)
+	if (sm->mlo.valid_links) {
+		u8 *pos;
+
+		/* Add MAC KDE */
+		wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 2/4");
+		pos = kde + kde_len;
+		pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+				  ETH_ALEN);
+
+		/* Add MLO Link KDE */
+		wpa_printf(MSG_DEBUG, "Add MLO Link KDE(s) into EAPOL-Key 2/4");
+		pos = wpa_mlo_link_kde(sm, pos);
+		kde_len = pos - kde;
+	}
+
+	if (wpa_supplicant_send_2_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+				       sm->snonce, kde, kde_len, ptk) < 0)
 		goto failed;
 
 	os_free(kde_buf);
@@ -951,17 +1171,31 @@
 		wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
 	}
 
-	if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
-			   rsclen, sm->ptk.tk, keylen,
-			   KEY_FLAG_PAIRWISE | key_flag) < 0) {
+	if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm),
+			   sm->keyidx_active, 1, key_rsc, rsclen, sm->ptk.tk,
+			   keylen, KEY_FLAG_PAIRWISE | key_flag) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+			"WPA: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
 			MACSTR " idx=%d key_flag=0x%x)",
-			alg, keylen, MAC2STR(sm->bssid),
+			alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)),
 			sm->keyidx_active, key_flag);
 		return -1;
 	}
 
+#ifdef CONFIG_PASN
+	if (sm->secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_sm_set_ltf_keyseed(sm, sm->own_addr, sm->bssid,
+				   sm->ptk.ltf_keyseed_len,
+				   sm->ptk.ltf_keyseed) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to set LTF keyseed to the driver (keylen=%zu bssid="
+			MACSTR ")", sm->ptk.ltf_keyseed_len,
+			MAC2STR(sm->bssid));
+		return -1;
+	}
+#endif /* CONFIG_PASN */
+
 	wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
 			 sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
 
@@ -969,6 +1203,7 @@
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
 	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
+	sm->tk_set = true;
 
 	if (sm->wpa_ptk_rekey) {
 		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -982,14 +1217,16 @@
 static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-		"WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
-		sm->keyidx_active, MAC2STR(sm->bssid));
+		"WPA: Activate PTK (idx=%d auth_addr=" MACSTR ")",
+		sm->keyidx_active, MAC2STR(wpa_sm_get_auth_addr(sm)));
 
-	if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, 0, NULL, 0,
-			   NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
+	if (wpa_sm_set_key(sm, -1, 0, wpa_sm_get_auth_addr(sm),
+			   sm->keyidx_active, 0, NULL, 0, NULL, 0,
+			   KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to activate PTK for TX (idx=%d bssid="
-			MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
+			"WPA: Failed to activate PTK for TX (idx=%d auth_addr="
+			MACSTR ")", sm->keyidx_active,
+			MAC2STR(wpa_sm_get_auth_addr(sm)));
 		return -1;
 	}
 	return 0;
@@ -1064,7 +1301,7 @@
 		_gtk = gtk_buf;
 	}
 	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
-		if (wpa_sm_set_key(sm, gd->alg, NULL,
+		if (wpa_sm_set_key(sm, -1, gd->alg, NULL,
 				   gd->keyidx, 1, key_rsc, gd->key_rsc_len,
 				   _gtk, gd->gtk_len,
 				   KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) {
@@ -1074,7 +1311,7 @@
 			forced_memzero(gtk_buf, sizeof(gtk_buf));
 			return -1;
 		}
-	} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
+	} else if (wpa_sm_set_key(sm, -1, gd->alg, broadcast_ether_addr,
 				  gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
 				  _gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1099,6 +1336,56 @@
 }
 
 
+static int wpa_supplicant_install_mlo_gtk(struct wpa_sm *sm, u8 link_id,
+					  const struct wpa_gtk_data *gd,
+					  const u8 *key_rsc, int wnm_sleep)
+{
+	const u8 *gtk = gd->gtk;
+
+	/* Detect possible key reinstallation */
+	if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len &&
+	     os_memcmp(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+		       sm->mlo.links[link_id].gtk.gtk_len) == 0) ||
+	    (sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len ==
+	     (size_t) gd->gtk_len &&
+	     os_memcmp(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+		       sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len) == 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Not reinstalling already in-use GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+			link_id, gd->keyidx, gd->tx, gd->gtk_len);
+		return 0;
+	}
+
+	wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: Group Key", gd->gtk,
+			     gd->gtk_len);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: Installing GTK to the driver (link_id=%d keyidx=%d tx=%d len=%d)",
+		link_id, gd->keyidx, gd->tx, gd->gtk_len);
+	wpa_hexdump_link(MSG_DEBUG, link_id, "RSN: RSC",
+			 key_rsc, gd->key_rsc_len);
+	if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr,
+			   gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk,
+			   gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)",
+			link_id, gd->alg, gd->gtk_len, gd->keyidx);
+		return -1;
+	}
+
+	if (wnm_sleep) {
+		sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len;
+		os_memcpy(sm->mlo.links[link_id].gtk_wnm_sleep.gtk, gd->gtk,
+			  sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len);
+	} else {
+		sm->mlo.links[link_id].gtk.gtk_len = gd->gtk_len;
+		os_memcpy(sm->mlo.links[link_id].gtk.gtk, gd->gtk,
+			  sm->mlo.links[link_id].gtk.gtk_len);
+	}
+
+	return 0;
+}
+
+
 static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
 						int tx)
 {
@@ -1147,6 +1434,84 @@
 }
 
 
+static int wpa_supplicant_mlo_gtk(struct wpa_sm *sm, u8 link_id, const u8 *gtk,
+				  size_t gtk_len, int key_info)
+{
+	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
+	int ret;
+
+	/*
+	 * MLO GTK KDE format:
+	 * KeyID[bits 0-1], Tx [bit 2], Reserved [bit 3], link id [4-7]
+	 * PN
+	 * GTK
+	 */
+	os_memset(&gd, 0, sizeof(gd));
+	wpa_hexdump_link_key(MSG_DEBUG, link_id,
+			     "RSN: received GTK in pairwise handshake",
+			     gtk, gtk_len);
+
+	if (gtk_len < RSN_MLO_GTK_KDE_PREFIX_LENGTH ||
+	    gtk_len - RSN_MLO_GTK_KDE_PREFIX_LENGTH > sizeof(gd.gtk))
+		return -1;
+
+	gd.keyidx = gtk[0] & 0x3;
+	gtk += 1;
+	gtk_len -= 1;
+
+	key_rsc = gtk;
+
+	gtk += 6;
+	gtk_len -= 6;
+
+	os_memcpy(gd.gtk, gtk, gtk_len);
+	gd.gtk_len = gtk_len;
+
+	ret = 0;
+	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len,
+					      gtk_len, &gd.key_rsc_len,
+					      &gd.alg) ||
+	     wpa_supplicant_install_mlo_gtk(sm, link_id, &gd, key_rsc, 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Failed to install GTK for MLO Link ID %u",
+			link_id);
+		ret = -1;
+		goto out;
+	}
+
+out:
+	forced_memzero(&gd, sizeof(gd));
+	return ret;
+}
+
+
+static int wpa_supplicant_pairwise_mlo_gtk(struct wpa_sm *sm,
+					   const struct wpa_eapol_key *key,
+					   struct wpa_eapol_ie_parse *ie,
+					   int key_info)
+{
+	u8 i;
+
+	for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+		if (!(sm->mlo.valid_links & BIT(i)))
+			continue;
+
+		if (!ie->mlo_gtk[i]) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"MLO RSN: GTK not found for link ID %u", i);
+			return -1;
+		}
+
+		if (wpa_supplicant_mlo_gtk(sm, i, ie->mlo_gtk[i],
+					   ie->mlo_gtk_len[i], key_info))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
 				       const struct wpa_eapol_key *key,
 				       const u8 *gtk, size_t gtk_len,
@@ -1227,7 +1592,7 @@
 			"WPA: Invalid IGTK KeyID %d", keyidx);
 		return -1;
 	}
-	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+	if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
 			   broadcast_ether_addr,
 			   keyidx, 0, igtk->pn, sizeof(igtk->pn),
 			   igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1296,7 +1661,7 @@
 			"WPA: Invalid BIGTK KeyID %d", keyidx);
 		return -1;
 	}
-	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+	if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
 			   broadcast_ether_addr,
 			   keyidx, 0, bigtk->pn, sizeof(bigtk->pn),
 			   bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) {
@@ -1318,6 +1683,180 @@
 }
 
 
+static int wpa_supplicant_install_mlo_igtk(struct wpa_sm *sm, u8 link_id,
+					   const struct rsn_mlo_igtk_kde *igtk,
+					   int wnm_sleep)
+{
+	size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+	u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+	/* Detect possible key reinstallation */
+	if ((sm->mlo.links[link_id].igtk.igtk_len == len &&
+	     os_memcmp(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+		       sm->mlo.links[link_id].igtk.igtk_len) == 0) ||
+	    (sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len == len &&
+	     os_memcmp(sm->mlo.links[link_id].igtk_wnm_sleep.igtk, igtk->igtk,
+		       sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len) == 0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Not reinstalling already in-use IGTK to the driver (link_id=%d keyidx=%d)",
+			link_id, keyidx);
+		return 0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: MLO Link %u IGTK keyid %d pn " COMPACT_MACSTR,
+		link_id, keyidx, MAC2STR(igtk->pn));
+	wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: IGTK", igtk->igtk, len);
+	if (keyidx > 4095) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Invalid MLO Link %d IGTK KeyID %d", link_id,
+			keyidx);
+		return -1;
+	}
+	if (wpa_sm_set_key(sm, link_id,
+			   wpa_cipher_to_alg(sm->mgmt_group_cipher),
+			   broadcast_ether_addr, keyidx, 0, igtk->pn,
+			   sizeof(igtk->pn), igtk->igtk, len,
+			   KEY_FLAG_GROUP_RX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to configure MLO Link %d IGTK to the driver",
+			link_id);
+		return -1;
+	}
+
+	if (wnm_sleep) {
+		sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len = len;
+		os_memcpy(sm->mlo.links[link_id].igtk_wnm_sleep.igtk,
+			  igtk->igtk,
+			  sm->mlo.links[link_id].igtk_wnm_sleep.igtk_len);
+	} else {
+		sm->mlo.links[link_id].igtk.igtk_len = len;
+		os_memcpy(sm->mlo.links[link_id].igtk.igtk, igtk->igtk,
+			  sm->mlo.links[link_id].igtk.igtk_len);
+	}
+
+	return 0;
+}
+
+
+static int
+wpa_supplicant_install_mlo_bigtk(struct wpa_sm *sm, u8 link_id,
+				 const struct rsn_mlo_bigtk_kde *bigtk,
+				 int wnm_sleep)
+{
+	size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+	u16 keyidx = WPA_GET_LE16(bigtk->keyid);
+
+	/* Detect possible key reinstallation */
+	if ((sm->mlo.links[link_id].bigtk.bigtk_len == len &&
+	     os_memcmp(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+		       sm->mlo.links[link_id].bigtk.bigtk_len) == 0) ||
+	    (sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len == len &&
+	     os_memcmp(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+		       bigtk->bigtk,
+		       sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len) ==
+	     0)) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Not reinstalling already in-use BIGTK to the driver (link_id=%d keyidx=%d)",
+			link_id, keyidx);
+		return  0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: MLO Link %u BIGTK keyid %d pn " COMPACT_MACSTR,
+		link_id, keyidx, MAC2STR(bigtk->pn));
+	wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: BIGTK", bigtk->bigtk,
+			     len);
+	if (keyidx < 6 || keyidx > 7) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Invalid MLO Link %d BIGTK KeyID %d", link_id,
+			keyidx);
+		return -1;
+	}
+	if (wpa_sm_set_key(sm, link_id,
+			   wpa_cipher_to_alg(sm->mgmt_group_cipher),
+			   broadcast_ether_addr, keyidx, 0, bigtk->pn,
+			   sizeof(bigtk->pn), bigtk->bigtk, len,
+			   KEY_FLAG_GROUP_RX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to configure MLO Link %d BIGTK to the driver",
+			link_id);
+		return -1;
+	}
+
+	if (wnm_sleep) {
+		sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len = len;
+		os_memcpy(sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk,
+			  bigtk->bigtk,
+			  sm->mlo.links[link_id].bigtk_wnm_sleep.bigtk_len);
+	} else {
+		sm->mlo.links[link_id].bigtk.bigtk_len = len;
+		os_memcpy(sm->mlo.links[link_id].bigtk.bigtk, bigtk->bigtk,
+			  sm->mlo.links[link_id].bigtk.bigtk_len);
+	}
+
+	return 0;
+}
+
+
+static int _mlo_ieee80211w_set_keys(struct wpa_sm *sm, u8 link_id,
+				    struct wpa_eapol_ie_parse *ie)
+{
+	size_t len;
+
+	if (ie->mlo_igtk[link_id]) {
+		len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+		if (ie->mlo_igtk_len[link_id] !=
+		    RSN_MLO_IGTK_KDE_PREFIX_LENGTH + len)
+			return -1;
+
+		if (wpa_supplicant_install_mlo_igtk(
+			    sm, link_id,
+			    (const struct rsn_mlo_igtk_kde *)
+			    ie->mlo_igtk[link_id],
+			    0) < 0)
+			return -1;
+	}
+
+	if (ie->mlo_bigtk[link_id] && sm->beacon_prot) {
+		len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+		if (ie->mlo_bigtk_len[link_id] !=
+		    RSN_MLO_BIGTK_KDE_PREFIX_LENGTH + len)
+			return -1;
+
+		if (wpa_supplicant_install_mlo_bigtk(
+			    sm, link_id,
+			    (const struct rsn_mlo_bigtk_kde *)
+			    ie->mlo_bigtk[link_id],
+			    0) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int mlo_ieee80211w_set_keys(struct wpa_sm *sm,
+				   struct wpa_eapol_ie_parse *ie)
+{
+	u8 i;
+
+	if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) ||
+	    sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
+		return 0;
+
+	for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+		if (!(sm->mlo.valid_links & BIT(i)))
+			continue;
+
+		if (_mlo_ieee80211w_set_keys(sm, i, ie))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static int ieee80211w_set_keys(struct wpa_sm *sm,
 			       struct wpa_eapol_ie_parse *ie)
 {
@@ -1621,13 +2160,32 @@
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
 	u8 *rbuf, *key_mic;
+	u8 *kde = NULL;
+	size_t kde_len = 0;
+
+	if (sm->mlo.valid_links) {
+		u8 *pos;
+
+		kde = os_malloc(RSN_SELECTOR_LEN + ETH_ALEN + 2);
+		if (!kde)
+			return -1;
+
+		/* Add MAC KDE */
+		wpa_printf(MSG_DEBUG, "MLO: Add MAC KDE into EAPOL-Key 4/4");
+		pos = kde;
+		pos = rsn_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->own_addr,
+				  ETH_ALEN);
+		kde_len = pos - kde;
+	}
 
 	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  hdrlen, &rlen, (void *) &reply);
-	if (rbuf == NULL)
+				  hdrlen + kde_len, &rlen, (void *) &reply);
+	if (!rbuf) {
+		os_free(kde);
 		return -1;
+	}
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
@@ -1647,7 +2205,11 @@
 		  WPA_REPLAY_COUNTER_LEN);
 
 	key_mic = (u8 *) (reply + 1);
-	WPA_PUT_BE16(key_mic + mic_len, 0);
+	WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data length */
+	if (kde) {
+		os_memcpy(key_mic + mic_len + 2, kde, kde_len); /* Key Data */
+		os_free(kde);
+	}
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key 4/4");
 	return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
@@ -1655,6 +2217,201 @@
 }
 
 
+static int wpa_supplicant_validate_link_kde(struct wpa_sm *sm, u8 link_id,
+					    const u8 *link_kde,
+					    size_t link_kde_len)
+{
+	size_t rsne_len = 0, rsnxe_len = 0;
+	const u8 *rsne = NULL, *rsnxe = NULL;
+
+	if (!link_kde ||
+	    link_kde_len < RSN_MLO_LINK_KDE_LINK_MAC_INDEX + ETH_ALEN) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: MLO Link KDE is not found for link ID %d",
+			link_id);
+		return -1;
+	}
+
+	if (os_memcmp(sm->mlo.links[link_id].bssid,
+		      &link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX],
+		      ETH_ALEN) != 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: MLO Link %u MAC address (" MACSTR
+			") not matching association response (" MACSTR ")",
+			link_id,
+			MAC2STR(&link_kde[RSN_MLO_LINK_KDE_LINK_MAC_INDEX]),
+			MAC2STR(sm->mlo.links[link_id].bssid));
+		return -1;
+	}
+
+	if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNE_INFO) {
+		rsne = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH;
+		if (link_kde_len < RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 ||
+		    link_kde_len <
+		    (size_t) (RSN_MLO_LINK_KDE_FIXED_LENGTH + 2 + rsne[1])) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"RSN: No room for link %u RSNE in MLO Link KDE",
+				link_id);
+			return -1;
+		}
+
+		rsne_len = rsne[1] + 2;
+	}
+
+	if (!rsne) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: RSNE not present in MLO Link %u KDE", link_id);
+		return -1;
+	}
+
+	if (link_kde[0] & RSN_MLO_LINK_KDE_LI_RSNXE_INFO) {
+		rsnxe = link_kde + RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len;
+		if (link_kde_len <
+		    (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2) ||
+		    link_kde_len <
+		    (RSN_MLO_LINK_KDE_FIXED_LENGTH + rsne_len + 2 + rsnxe[1])) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"RSN: No room for link %u RSNXE in MLO Link KDE",
+				link_id);
+			return -1;
+		}
+
+		rsnxe_len = rsnxe[1] + 2;
+	}
+
+	if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+			       sm->mlo.links[link_id].ap_rsne,
+			       sm->mlo.links[link_id].ap_rsne_len,
+			       rsne, rsne_len)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN MLO: IE in 3/4 msg does not match with IE in Beacon/ProbeResp for link ID %u",
+			link_id);
+		wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
+			    sm->mlo.links[link_id].ap_rsne,
+			    sm->mlo.links[link_id].ap_rsne_len);
+		wpa_hexdump(MSG_INFO, "RSNE in EAPOL-Key msg 3/4",
+			    rsne, rsne_len);
+		return -1;
+	}
+
+	if ((sm->mlo.links[link_id].ap_rsnxe && !rsnxe) ||
+	    (!sm->mlo.links[link_id].ap_rsnxe && rsnxe) ||
+	    (sm->mlo.links[link_id].ap_rsnxe && rsnxe &&
+	     (sm->mlo.links[link_id].ap_rsnxe_len != rsnxe_len ||
+	      os_memcmp(sm->mlo.links[link_id].ap_rsnxe, rsnxe,
+			sm->mlo.links[link_id].ap_rsnxe_len) != 0))) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN MLO: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4 for link ID %u",
+			link_id);
+		wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp",
+			    sm->mlo.links[link_id].ap_rsnxe,
+			    sm->mlo.links[link_id].ap_rsnxe_len);
+		wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4",
+			    rsnxe, rsnxe_len);
+		wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_validate_mlo_ieee80211w_kdes(struct wpa_sm *sm,
+					    u8 link_id,
+					    struct wpa_eapol_ie_parse *ie)
+{
+	if (ie->mlo_igtk[link_id] &&
+	    ie->mlo_igtk_len[link_id] != RSN_MLO_IGTK_KDE_PREFIX_LENGTH +
+	    (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN MLO: Invalid IGTK KDE length %lu for link ID %u",
+			(unsigned long) ie->mlo_igtk_len, link_id);
+		return -1;
+	}
+
+	if (!sm->beacon_prot)
+		return 0;
+
+	if (ie->mlo_bigtk[link_id] &&
+	    ie->mlo_bigtk_len[link_id] != RSN_MLO_BIGTK_KDE_PREFIX_LENGTH +
+	    (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN MLO: Invalid BIGTK KDE length %lu for link ID %u",
+			(unsigned long) ie->mlo_bigtk_len, link_id);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4_wpa(struct wpa_sm *sm,
+					      const struct wpa_eapol_key *key,
+					      u16 ver, const u8 *key_data,
+					      size_t key_data_len)
+{
+	u16 key_info, keylen;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: RX message 3 of 4-Way Handshake from " MACSTR
+		" (ver=%d)", MAC2STR(sm->bssid), ver);
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+		goto failed;
+
+	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+		goto failed;
+
+	if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake - drop packet (src="
+			MACSTR ")", MAC2STR(sm->bssid));
+		goto failed;
+	}
+
+	keylen = WPA_GET_BE16(key->key_length);
+	if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Invalid %s key length %d (src=" MACSTR ")",
+			wpa_cipher_txt(sm->pairwise_cipher), keylen,
+			MAC2STR(sm->bssid));
+		goto failed;
+	}
+
+	if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+				       key_info, &sm->ptk) < 0)
+		goto failed;
+
+	/* SNonce was successfully used in msg 3/4, so mark it to be renewed
+	 * for the next 4-Way Handshake. If msg 3 is received again, the old
+	 * SNonce will still be used to avoid changing PTK. */
+	sm->renew_snonce = 1;
+
+	if ((key_info & WPA_KEY_INFO_INSTALL) &&
+	    wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX))
+		goto failed;
+
+	if (key_info & WPA_KEY_INFO_SECURE) {
+		wpa_sm_mlme_setprotection(
+			sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+		eapol_sm_notify_portValid(sm->eapol, true);
+	}
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	sm->msg_3_of_4_ok = 1;
+	return;
+
+failed:
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
 static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 					  const struct wpa_eapol_key *key,
 					  u16 ver, const u8 *key_data,
@@ -1662,28 +2419,83 @@
 {
 	u16 key_info, keylen;
 	struct wpa_eapol_ie_parse ie;
+	bool mlo = sm->mlo.valid_links;
+	int i;
 
 	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
-	wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, "WPA: RX message 3 of 4-Way "
-		"Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+		"RSN: RX message 3 of 4-Way Handshake from " MACSTR
+		" (ver=%d)%s", MAC2STR(sm->bssid), ver, mlo ? " (MLO)" : "");
 
 	key_info = WPA_GET_BE16(key->key_info);
 
 	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
 	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
 		goto failed;
-	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+
+	if (mlo && !ie.valid_mlo_gtks) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
+		goto failed;
+	}
+	if (mlo &&
+	    (key_info &
+	     (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+	      WPA_KEY_INFO_SECURE)) !=
+	    (WPA_KEY_INFO_ENCR_KEY_DATA | WPA_KEY_INFO_INSTALL |
+	     WPA_KEY_INFO_SECURE)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN MLO: Invalid key info (0x%x) in EAPOL-Key msg 3/4",
+			key_info);
+		goto failed;
+	}
+
+	if (mlo && !is_valid_ap_mld_mac_kde(sm, ie.mac_addr)) {
+		wpa_printf(MSG_DEBUG, "RSN: Invalid AP MLD MAC address KDE");
+		goto failed;
+	}
+
+	for (i = 0; mlo && i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(sm->mlo.req_links & BIT(i)))
+			continue;
+
+		if (wpa_supplicant_validate_link_kde(sm, i, ie.mlo_link[i],
+						     ie.mlo_link_len[i]) < 0)
+			goto failed;
+
+		if (!(sm->mlo.valid_links & BIT(i)))
+			continue;
+
+		if (!ie.mlo_gtk[i]) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"RSN: GTK not found for link ID %u", i);
+			goto failed;
+		}
+
+		if (sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+		    wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+		    wpa_validate_mlo_ieee80211w_kdes(sm, i, &ie) < 0)
+			goto failed;
+	}
+
+#ifdef CONFIG_IEEE80211R
+	if (mlo && wpa_key_mgmt_ft(sm->key_mgmt) &&
+	    wpa_supplicant_validate_ie_ft(sm, sm->bssid, &ie) < 0)
+		goto failed;
+#endif /* CONFIG_IEEE80211R */
+
+	if (!mlo && ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: GTK IE in unencrypted key data");
 		goto failed;
 	}
-	if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+	if (!mlo && ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: IGTK KDE in unencrypted key data");
 		goto failed;
 	}
 
-	if (ie.igtk &&
+	if (!mlo && ie.igtk &&
 	    sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
 	    wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
 	    ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
@@ -1694,7 +2506,7 @@
 		goto failed;
 	}
 
-	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+	if (!mlo && wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
 		goto failed;
 
 	if (wpa_handle_ext_key_id(sm, &ie))
@@ -1764,10 +2576,9 @@
 	    wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
 		goto failed;
 
-	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
-				       &sm->ptk) < 0) {
+	if (wpa_supplicant_send_4_of_4(sm, wpa_sm_get_auth_addr(sm), key, ver,
+				       key_info, &sm->ptk) < 0)
 		goto failed;
-	}
 
 	/* SNonce was successfully used in msg 3/4, so mark it to be renewed
 	 * for the next 4-Way Handshake. If msg 3 is received again, the old
@@ -1794,7 +2605,14 @@
 	}
 	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 
-	if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+	if (mlo) {
+		if (wpa_supplicant_pairwise_mlo_gtk(sm, key, &ie,
+						    key_info) < 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"MLO RSN: Failed to configure MLO GTKs");
+			goto failed;
+		}
+	} else if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
 		/* No GTK to be set to the driver */
 	} else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1808,17 +2626,18 @@
 		goto failed;
 	}
 
-	if (ieee80211w_set_keys(sm, &ie) < 0) {
+	if ((mlo && mlo_ieee80211w_set_keys(sm, &ie) < 0) ||
+	    (!mlo && ieee80211w_set_keys(sm, &ie) < 0)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Failed to configure IGTK");
 		goto failed;
 	}
 
-	if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
+	if (mlo || sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
 		wpa_supplicant_key_neg_complete(sm, sm->bssid,
 						key_info & WPA_KEY_INFO_SECURE);
 
-	if (ie.gtk)
+	if (mlo || ie.gtk)
 		wpa_sm_set_rekey_offload(sm);
 
 	/* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
@@ -1832,7 +2651,7 @@
 
 		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
 				     sm->ptk.kck, sm->ptk.kck_len,
-				     sm->bssid, sm->own_addr,
+				     wpa_sm_get_auth_addr(sm), sm->own_addr,
 				     sm->network_ctx, sm->key_mgmt, NULL);
 		if (!sm->cur_pmksa)
 			sm->cur_pmksa = sa;
@@ -1848,173 +2667,6 @@
 }
 
 
-static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
-					     const u8 *keydata,
-					     size_t keydatalen,
-					     u16 key_info,
-					     struct wpa_gtk_data *gd)
-{
-	int maxkeylen;
-	struct wpa_eapol_ie_parse ie;
-	u16 gtk_len;
-
-	wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
-			keydata, keydatalen);
-	if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
-		return -1;
-	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: GTK IE in unencrypted key data");
-		return -1;
-	}
-	if (ie.gtk == NULL) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: No GTK IE in Group Key msg 1/2");
-		return -1;
-	}
-	gtk_len = ie.gtk_len;
-	if (gtk_len < 2) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
-			gtk_len);
-		return -1;
-	}
-	gtk_len -= 2;
-	if (gtk_len > sizeof(gd->gtk)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
-		return -1;
-	}
-	maxkeylen = gd->gtk_len = gtk_len;
-
-#ifdef CONFIG_OCV
-	if (wpa_sm_ocv_enabled(sm)) {
-		struct wpa_channel_info ci;
-
-		if (wpa_sm_channel_info(sm, &ci) != 0) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
-			return -1;
-		}
-
-		if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
-					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != OCI_SUCCESS) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
-				"addr=" MACSTR " frame=eapol-key-g1 error=%s",
-				MAC2STR(sm->bssid), ocv_errorstr);
-			return -1;
-		}
-	}
-#endif /* CONFIG_OCV */
-
-	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-					      gtk_len, maxkeylen,
-					      &gd->key_rsc_len, &gd->alg))
-		return -1;
-
-	wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
-			ie.gtk, 2 + gtk_len);
-	gd->keyidx = ie.gtk[0] & 0x3;
-	gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
-						      !!(ie.gtk[0] & BIT(2)));
-	os_memcpy(gd->gtk, ie.gtk + 2, gtk_len);
-
-	if (ieee80211w_set_keys(sm, &ie) < 0)
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Failed to configure IGTK");
-
-	return 0;
-}
-
-
-static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
-					     const struct wpa_eapol_key *key,
-					     const u8 *key_data,
-					     size_t key_data_len, u16 key_info,
-					     u16 ver, struct wpa_gtk_data *gd)
-{
-	size_t maxkeylen;
-	u16 gtk_len;
-
-	gtk_len = WPA_GET_BE16(key->key_length);
-	maxkeylen = key_data_len;
-	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-		if (maxkeylen < 8) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-				"WPA: Too short maxkeylen (%lu)",
-				(unsigned long) maxkeylen);
-			return -1;
-		}
-		maxkeylen -= 8;
-	}
-
-	if (gtk_len > maxkeylen ||
-	    wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-					      gtk_len, maxkeylen,
-					      &gd->key_rsc_len, &gd->alg))
-		return -1;
-
-	gd->gtk_len = gtk_len;
-	gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
-		WPA_KEY_INFO_KEY_INDEX_SHIFT;
-	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: RC4 not supported in the build");
-		return -1;
-#else /* CONFIG_NO_RC4 */
-		u8 ek[32];
-		if (key_data_len > sizeof(gd->gtk)) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: RC4 key data too long (%lu)",
-				(unsigned long) key_data_len);
-			return -1;
-		}
-		os_memcpy(ek, key->key_iv, 16);
-		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
-		os_memcpy(gd->gtk, key_data, key_data_len);
-		if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
-			forced_memzero(ek, sizeof(ek));
-			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
-				"WPA: RC4 failed");
-			return -1;
-		}
-		forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
-	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-		if (maxkeylen % 8) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Unsupported AES-WRAP len %lu",
-				(unsigned long) maxkeylen);
-			return -1;
-		}
-		if (maxkeylen > sizeof(gd->gtk)) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: AES-WRAP key data "
-				"too long (keydatalen=%lu maxkeylen=%lu)",
-				(unsigned long) key_data_len,
-				(unsigned long) maxkeylen);
-			return -1;
-		}
-		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
-			       key_data, gd->gtk)) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: AES unwrap failed - could not decrypt "
-				"GTK");
-			return -1;
-		}
-	} else {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Unsupported key_info type %d", ver);
-		return -1;
-	}
-	gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
-		sm, !!(key_info & WPA_KEY_INFO_TXRX));
-	return 0;
-}
-
-
 static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
 				      const struct wpa_eapol_key *key,
 				      int ver, u16 key_info)
@@ -2098,19 +2750,121 @@
 }
 
 
-static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
-					  const unsigned char *src_addr,
-					  const struct wpa_eapol_key *key,
-					  const u8 *key_data,
-					  size_t key_data_len, u16 ver)
+static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm,
+					      const unsigned char *src_addr,
+					      const struct wpa_eapol_key *key,
+					      const u8 *key_data,
+					      size_t key_data_len, u16 ver)
 {
 	u16 key_info;
-	int rekey, ret;
-	struct wpa_gtk_data gd;
-	const u8 *key_rsc;
+	u8 i;
+	struct wpa_eapol_ie_parse ie;
 
 	if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"MLO RSN: Group Key Handshake started prior to completion of 4-way handshake");
+		goto failed;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "MLO RSN: RX message 1 of Group "
+		"Key Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr),
+		ver);
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	wpa_hexdump_key(MSG_DEBUG, "MLO RSN: msg 1/2 key data", key_data,
+			key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+		goto failed;
+
+	if (!ie.valid_mlo_gtks) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"MLO RSN: No MLO GTK KDE in Group Key msg 1/2");
+		goto failed;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"MLO RSN: MLO GTK KDE in unencrypted key data");
+		goto failed;
+	}
+
+#ifdef CONFIG_OCV
+	if (wpa_sm_ocv_enabled(sm)) {
+		struct wpa_channel_info ci;
+
+		if (wpa_sm_channel_info(sm, &ci) != 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+			goto failed;
+		}
+
+		if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+					 channel_width_to_int(ci.chanwidth),
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=eapol-key-g1 error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
+			goto failed;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	if (mlo_ieee80211w_set_keys(sm, &ie) < 0)
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"MLO RSN: Failed to configure MLO IGTK");
+
+	for (i = 0; i < MAX_NUM_MLO_LINKS; i++) {
+		if (!(sm->mlo.valid_links & BIT(i)))
+			continue;
+
+		/*
+		 * AP may send group keys for subset of the all links during
+		 * rekey
+		 */
+		if (!ie.mlo_gtk[i])
+			continue;
+
+		if (wpa_supplicant_mlo_gtk(sm, i, ie.mlo_gtk[i],
+					   ie.mlo_gtk_len[i], key_info))
+			goto failed;
+	}
+
+	if (wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+		goto failed;
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "MLO RSN: Group rekeying completed "
+		"with " MACSTR " [GTK=%s]", MAC2STR(sm->mlo.ap_mld_addr),
+		wpa_cipher_txt(sm->group_cipher));
+	wpa_sm_cancel_auth_timeout(sm);
+	wpa_sm_set_state(sm, WPA_COMPLETED);
+
+	wpa_sm_set_rekey_offload(sm);
+
+	return;
+
+failed:
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+					      const unsigned char *src_addr,
+					      const struct wpa_eapol_key *key,
+					      const u8 *key_data,
+					      size_t key_data_len, u16 ver)
+{
+	u16 key_info;
+	int rekey;
+	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
+	size_t maxkeylen;
+	u16 gtk_len;
+
+	if (!sm->msg_3_of_4_ok) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Group Key Handshake started prior to completion of 4-way handshake");
 		goto failed;
 	}
@@ -2118,25 +2872,88 @@
 	os_memset(&gd, 0, sizeof(gd));
 
 	rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
-	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
-		"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: RX message 1 of Group Key Handshake from " MACSTR
+		" (ver=%d)", MAC2STR(src_addr), ver);
 
 	key_info = WPA_GET_BE16(key->key_info);
 
-	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
-		ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
-							key_data_len, key_info,
-							&gd);
-	} else {
-		ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
-							key_data_len,
-							key_info, ver, &gd);
+	gtk_len = WPA_GET_BE16(key->key_length);
+	maxkeylen = key_data_len;
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		if (maxkeylen < 8) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Too short maxkeylen (%lu)",
+				(unsigned long) maxkeylen);
+			goto failed;
+		}
+		maxkeylen -= 8;
 	}
 
+	if (gtk_len > maxkeylen ||
+	    wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gtk_len, maxkeylen,
+					      &gd.key_rsc_len, &gd.alg))
+		goto failed;
+
 	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 
-	if (ret)
+	gd.gtk_len = gtk_len;
+	gd.keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+		WPA_KEY_INFO_KEY_INDEX_SHIFT;
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: RC4 not supported in the build");
 		goto failed;
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
+		u8 ek[32];
+		if (key_data_len > sizeof(gd.gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: RC4 key data too long (%lu)",
+				(unsigned long) key_data_len);
+			goto failed;
+		}
+		os_memcpy(ek, key->key_iv, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+		os_memcpy(gd.gtk, key_data, key_data_len);
+		if (rc4_skip(ek, 32, 256, gd.gtk, key_data_len)) {
+			forced_memzero(ek, sizeof(ek));
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: RC4 failed");
+			goto failed;
+		}
+		forced_memzero(ek, sizeof(ek));
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
+	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		if (maxkeylen % 8) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Unsupported AES-WRAP len %lu",
+				(unsigned long) maxkeylen);
+			goto failed;
+		}
+		if (maxkeylen > sizeof(gd.gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: AES-WRAP key data "
+				"too long (keydatalen=%lu maxkeylen=%lu)",
+				(unsigned long) key_data_len,
+				(unsigned long) maxkeylen);
+			goto failed;
+		}
+		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+			       key_data, gd.gtk)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: AES unwrap failed - could not decrypt "
+				"GTK");
+			goto failed;
+		}
+	} else {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Unsupported key_info type %d", ver);
+		goto failed;
+	}
+	gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+		sm, !!(key_info & WPA_KEY_INFO_TXRX));
 
 	key_rsc = key->key_rsc;
 	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
@@ -2148,15 +2965,15 @@
 	forced_memzero(&gd, sizeof(gd));
 
 	if (rekey) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
-			"completed with " MACSTR " [GTK=%s]",
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Group rekeying completed with " MACSTR
+			" [GTK=%s]",
 			MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
 		wpa_sm_cancel_auth_timeout(sm);
 		wpa_sm_set_state(sm, WPA_COMPLETED);
 	} else {
 		wpa_supplicant_key_neg_complete(sm, sm->bssid,
-						key_info &
-						WPA_KEY_INFO_SECURE);
+						key_info & WPA_KEY_INFO_SECURE);
 	}
 
 	wpa_sm_set_rekey_offload(sm);
@@ -2169,6 +2986,127 @@
 }
 
 
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+					  const unsigned char *src_addr,
+					  const struct wpa_eapol_key *key,
+					  const u8 *key_data,
+					  size_t key_data_len, u16 ver)
+{
+	u16 key_info;
+	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
+	int maxkeylen;
+	struct wpa_eapol_ie_parse ie;
+	u16 gtk_len;
+
+	if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Group Key Handshake started prior to completion of 4-way handshake");
+		goto failed;
+	}
+
+	os_memset(&gd, 0, sizeof(gd));
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: RX message 1 of Group Key Handshake from " MACSTR
+		" (ver=%d)", MAC2STR(src_addr), ver);
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
+			key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+		goto failed;
+
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: GTK KDE in unencrypted key data");
+		goto failed;
+	}
+	if (!ie.gtk) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: No GTK KDE in Group Key msg 1/2");
+		goto failed;
+	}
+	gtk_len = ie.gtk_len;
+	if (gtk_len < 2) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
+			gtk_len);
+		goto failed;
+	}
+	gtk_len -= 2;
+	if (gtk_len > sizeof(gd.gtk)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
+		goto failed;
+	}
+	maxkeylen = gd.gtk_len = gtk_len;
+
+#ifdef CONFIG_OCV
+	if (wpa_sm_ocv_enabled(sm)) {
+		struct wpa_channel_info ci;
+
+		if (wpa_sm_channel_info(sm, &ci) != 0) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+			goto failed;
+		}
+
+		if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+					 channel_width_to_int(ci.chanwidth),
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=eapol-key-g1 error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
+			goto failed;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gtk_len, maxkeylen,
+					      &gd.key_rsc_len, &gd.alg))
+		goto failed;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+			ie.gtk, 2 + gtk_len);
+	gd.keyidx = ie.gtk[0] & 0x3;
+	gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+						      !!(ie.gtk[0] & BIT(2)));
+	os_memcpy(gd.gtk, ie.gtk + 2, gtk_len);
+
+	if (ieee80211w_set_keys(sm, &ie) < 0)
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure IGTK");
+
+	key_rsc = key->key_rsc;
+	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+		key_rsc = null_rsc;
+
+	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
+	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
+		goto failed;
+	forced_memzero(&gd, sizeof(gd));
+
+	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+		"RSN: Group rekeying completed with " MACSTR " [GTK=%s]",
+		MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+	wpa_sm_cancel_auth_timeout(sm);
+	wpa_sm_set_state(sm, WPA_COMPLETED);
+
+	wpa_sm_set_rekey_offload(sm);
+
+	return;
+
+failed:
+	forced_memzero(&gd, sizeof(gd));
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
 					       struct wpa_eapol_key *key,
 					       u16 ver,
@@ -2267,11 +3205,11 @@
 	/* Decrypt key data here so that this operation does not need
 	 * to be implemented separately for each message type. */
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
-#ifdef CONFIG_NO_RC4
+#if defined(CONFIG_NO_RC4) || defined(CONFIG_FIPS)
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: RC4 not supported in the build");
 		return -1;
-#else /* CONFIG_NO_RC4 */
+#else /* CONFIG_NO_RC4 || CONFIG_FIPS */
 		u8 ek[32];
 
 		wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
@@ -2284,7 +3222,7 @@
 			return -1;
 		}
 		forced_memzero(ek, sizeof(ek));
-#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_NO_RC4 || CONFIG_FIPS */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
 		   wpa_use_aes_key_wrap(sm->key_mgmt)) {
@@ -2470,12 +3408,95 @@
 #endif /* CONFIG_FILS */
 
 
+static int wpa_sm_rx_eapol_wpa(struct wpa_sm *sm, const u8 *src_addr,
+			       struct wpa_eapol_key *key,
+			       enum frame_encryption encrypted,
+			       const u8 *tmp, size_t data_len,
+			       u8 *key_data, size_t key_data_len)
+{
+	u16 key_info, ver;
+
+	key_info = WPA_GET_BE16(key->key_info);
+
+	if (key->type != EAPOL_KEY_TYPE_WPA) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Unsupported EAPOL-Key type %d", key->type);
+		return -1;
+	}
+
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Unsupported EAPOL-Key descriptor version %d",
+			ver);
+		return -1;
+	}
+
+	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+			ver);
+		if (sm->group_cipher != WPA_CIPHER_CCMP &&
+		    !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+			/* Earlier versions of IEEE 802.11i did not explicitly
+			 * require version 2 descriptor for all EAPOL-Key
+			 * packets, so allow group keys to use version 1 if
+			 * CCMP is not used for them. */
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Backwards compatibility: allow invalid version for non-CCMP group keys");
+		} else
+			return -1;
+	}
+
+	if ((key_info & WPA_KEY_INFO_MIC) &&
+	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+		return -1;
+
+	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+		if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index");
+			return -1;
+		}
+		if (key_info & (WPA_KEY_INFO_MIC |
+				WPA_KEY_INFO_ENCR_KEY_DATA)) {
+			/* 3/4 4-Way Handshake */
+			wpa_supplicant_process_3_of_4_wpa(sm, key, ver,
+							  key_data,
+							  key_data_len);
+		} else {
+			/* 1/4 4-Way Handshake */
+			wpa_supplicant_process_1_of_4_wpa(sm, src_addr, key,
+							  ver, key_data,
+							  key_data_len,
+							  encrypted);
+		}
+	} else {
+		if (key_info & WPA_KEY_INFO_MIC) {
+			/* 1/2 Group Key Handshake */
+			wpa_supplicant_process_1_of_2_wpa(sm, src_addr, key,
+							  key_data,
+							  key_data_len,
+							  ver);
+		} else {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: EAPOL-Key (Group) without Mic/Encr bit - dropped");
+		}
+	}
+
+	return 1;
+}
+
+
 /**
  * wpa_sm_rx_eapol - Process received WPA EAPOL frames
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @src_addr: Source MAC address of the EAPOL packet
  * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
  * @len: Length of the EAPOL frame
+ * @encrypted: Whether the frame was encrypted
  * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
  *
  * This function is called for each received EAPOL frame. Other than EAPOL-Key
@@ -2487,7 +3508,7 @@
  * successful key handshake.
  */
 int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
-		    const u8 *buf, size_t len)
+		    const u8 *buf, size_t len, enum frame_encryption encrypted)
 {
 	size_t plen, data_len, key_data_len;
 	const struct ieee802_1x_hdr *hdr;
@@ -2580,19 +3601,77 @@
 		goto out;
 	}
 
+	if (sm->rx_replay_counter_set &&
+	    os_memcmp(key->replay_counter, sm->rx_replay_counter,
+		      WPA_REPLAY_COUNTER_LEN) <= 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: EAPOL-Key Replay Counter did not increase - dropping packet");
+		goto out;
+	}
+
 	eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+
 	key_info = WPA_GET_BE16(key->key_info);
+
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Unsupported SMK bit in key_info");
+		goto out;
+	}
+
+	if (!(key_info & WPA_KEY_INFO_ACK)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: No Ack bit in key_info");
+		goto out;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: EAPOL-Key with Request bit - dropped");
+		goto out;
+	}
+
+	if (sm->proto == WPA_PROTO_WPA) {
+		ret = wpa_sm_rx_eapol_wpa(sm, src_addr, key, encrypted,
+					  tmp, data_len,
+					  key_data, key_data_len);
+		goto out;
+	}
+
+	if (key->type != EAPOL_KEY_TYPE_RSN) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Unsupported EAPOL-Key type %d", key->type);
+		goto out;
+	}
+
 	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
 	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_use_akm_defined(sm->key_mgmt)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: Unsupported EAPOL-Key descriptor version %d",
+			"RSN: Unsupported EAPOL-Key descriptor version %d",
 			ver);
 		goto out;
 	}
 
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    sm->pairwise_cipher != WPA_CIPHER_TKIP) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: EAPOL-Key descriptor version %d not allowed without TKIP as the pairwise cipher",
+			ver);
+		goto out;
+	}
+
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+	    (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+	     sm->key_mgmt != WPA_KEY_MGMT_PSK)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: EAPOL-Key descriptor version %d not allowed due to negotiated AKM (0x%x)",
+			ver, sm->key_mgmt);
+		goto out;
+	}
+
 	if (wpa_use_akm_defined(sm->key_mgmt) &&
 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -2616,63 +3695,28 @@
 		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 		    !wpa_use_akm_defined(sm->key_mgmt)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-				"WPA: AP did not use the "
-				"negotiated AES-128-CMAC");
+				"RSN: AP did not use the negotiated AES-128-CMAC");
 			goto out;
 		}
 	} else if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
 		   !wpa_use_akm_defined(sm->key_mgmt) &&
 		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: CCMP is used, but EAPOL-Key "
-			"descriptor version (%d) is not 2", ver);
-		if (sm->group_cipher != WPA_CIPHER_CCMP &&
-		    !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
-			/* Earlier versions of IEEE 802.11i did not explicitly
-			 * require version 2 descriptor for all EAPOL-Key
-			 * packets, so allow group keys to use version 1 if
-			 * CCMP is not used for them. */
+			"RSN: CCMP is used, but EAPOL-Key descriptor version (%d) is not 2", ver);
+		if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-				"WPA: Backwards compatibility: allow invalid "
-				"version for non-CCMP group keys");
-		} else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+				"RSN: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
+		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-				"WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
-		} else
+				"RSN: Unexpected descriptor version %u", ver);
 			goto out;
+		}
 	} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
 		   !wpa_use_akm_defined(sm->key_mgmt) &&
 		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: GCMP is used, but EAPOL-Key "
-			"descriptor version (%d) is not 2", ver);
-		goto out;
-	}
-
-	if (sm->rx_replay_counter_set &&
-	    os_memcmp(key->replay_counter, sm->rx_replay_counter,
-		      WPA_REPLAY_COUNTER_LEN) <= 0) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: EAPOL-Key Replay Counter did not increase - "
-			"dropping packet");
-		goto out;
-	}
-
-	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: Unsupported SMK bit in key_info");
-		goto out;
-	}
-
-	if (!(key_info & WPA_KEY_INFO_ACK)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: No Ack bit in key_info");
-		goto out;
-	}
-
-	if (key_info & WPA_KEY_INFO_REQUEST) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"WPA: EAPOL-Key with Request bit - dropped");
+			"RSN: GCMP is used, but EAPOL-Key descriptor version (%d) is not 2",
+			ver);
 		goto out;
 	}
 
@@ -2709,8 +3753,7 @@
 	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
 		if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: Ignored EAPOL-Key (Pairwise) with "
-				"non-zero key index");
+				"RSN: Ignored EAPOL-Key (Pairwise) with non-zero key index");
 			goto out;
 		}
 		if (key_info & (WPA_KEY_INFO_MIC |
@@ -2722,19 +3765,26 @@
 			/* 1/4 4-Way Handshake */
 			wpa_supplicant_process_1_of_4(sm, src_addr, key,
 						      ver, key_data,
-						      key_data_len);
+						      key_data_len,
+						      encrypted);
 		}
 	} else {
 		if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
 		    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
 			/* 1/2 Group Key Handshake */
-			wpa_supplicant_process_1_of_2(sm, src_addr, key,
-						      key_data, key_data_len,
-						      ver);
+			if (sm->mlo.valid_links)
+				wpa_supplicant_process_mlo_1_of_2(sm, src_addr,
+								  key, key_data,
+								  key_data_len,
+								  ver);
+			else
+				wpa_supplicant_process_1_of_2(sm, src_addr, key,
+							      key_data,
+							      key_data_len,
+							      ver);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: EAPOL-Key (Group) without Mic/Encr bit - "
-				"dropped");
+				"RSN: EAPOL-Key (Group) without Mic/Encr bit - dropped");
 		}
 	}
 
@@ -2920,6 +3970,15 @@
 }
 
 
+static void wpa_sm_pmksa_notify_cb(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx)
+{
+	struct wpa_sm *sm = ctx;
+
+	wpa_sm_notify_pmksa_cache_entry(sm, entry);
+}
+
+
 /**
  * wpa_sm_init - Initialize WPA state machine
  * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
@@ -2944,7 +4003,8 @@
 	sm->dot11RSNAConfigSATimeout = 60;
 
 	sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb,
-				     wpa_sm_pmksa_is_current_cb, sm, sm);
+				     wpa_sm_pmksa_is_current_cb,
+				     wpa_sm_pmksa_notify_cb, sm, sm);
 	if (sm->pmksa == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 			"RSN: PMKSA cache initialization failed");
@@ -2962,6 +4022,8 @@
  */
 void wpa_sm_deinit(struct wpa_sm *sm)
 {
+	int i;
+
 	if (sm == NULL)
 		return;
 	pmksa_cache_deinit(sm->pmksa);
@@ -2972,6 +4034,10 @@
 	os_free(sm->ap_wpa_ie);
 	os_free(sm->ap_rsn_ie);
 	os_free(sm->ap_rsnxe);
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		os_free(sm->mlo.links[i].ap_rsne);
+		os_free(sm->mlo.links[i].ap_rsnxe);
+	}
 	wpa_sm_drop_sa(sm);
 	os_free(sm->ctx);
 #ifdef CONFIG_IEEE80211R
@@ -2996,6 +4062,32 @@
 }
 
 
+static void wpa_sm_clear_ptk(struct wpa_sm *sm)
+{
+	int i;
+
+	sm->ptk_set = 0;
+	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+	sm->tptk_set = 0;
+	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+	os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+	os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+	os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+	os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+	sm->tk_set = false;
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		os_memset(&sm->mlo.links[i].gtk, 0,
+			  sizeof(sm->mlo.links[i].gtk));
+		os_memset(&sm->mlo.links[i].gtk_wnm_sleep, 0,
+			  sizeof(sm->mlo.links[i].gtk_wnm_sleep));
+		os_memset(&sm->mlo.links[i].igtk, 0,
+			  sizeof(sm->mlo.links[i].igtk));
+		os_memset(&sm->mlo.links[i].igtk_wnm_sleep, 0,
+			  sizeof(sm->mlo.links[i].igtk_wnm_sleep));
+	}
+}
+
+
 /**
  * wpa_sm_notify_assoc - Notify WPA state machine about association
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3055,14 +4147,7 @@
 		 * this is not part of a Fast BSS Transition.
 		 */
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
-		sm->ptk_set = 0;
-		os_memset(&sm->ptk, 0, sizeof(sm->ptk));
-		sm->tptk_set = 0;
-		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
-		os_memset(&sm->gtk, 0, sizeof(sm->gtk));
-		os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
-		os_memset(&sm->igtk, 0, sizeof(sm->igtk));
-		os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+		wpa_sm_clear_ptk(sm);
 	}
 
 #ifdef CONFIG_TDLS
@@ -3259,6 +4344,85 @@
 }
 
 
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
+{
+	int i;
+
+	if (!sm)
+		return -1;
+
+	os_memcpy(sm->mlo.ap_mld_addr, mlo->ap_mld_addr, ETH_ALEN);
+	sm->mlo.assoc_link_id =  mlo->assoc_link_id;
+	sm->mlo.valid_links = mlo->valid_links;
+	sm->mlo.req_links = mlo->req_links;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		const u8 *ie;
+		size_t len;
+
+		if (sm->mlo.req_links & BIT(i)) {
+			if (!mlo->links[i].ap_rsne ||
+			    mlo->links[i].ap_rsne_len == 0) {
+				wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+					"RSN: No RSNE for AP MLO link %d with BSSID "
+					MACSTR,
+					i, MAC2STR(mlo->links[i].bssid));
+				return -1;
+
+			}
+			os_memcpy(sm->mlo.links[i].addr, mlo->links[i].addr,
+				  ETH_ALEN);
+			os_memcpy(sm->mlo.links[i].bssid, mlo->links[i].bssid,
+				  ETH_ALEN);
+		}
+
+		ie = mlo->links[i].ap_rsne;
+		len = mlo->links[i].ap_rsne_len;
+		os_free(sm->mlo.links[i].ap_rsne);
+		if (!ie || len == 0) {
+			if (sm->mlo.links[i].ap_rsne)
+				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+					"RSN: Clearing MLO link[%u] AP RSNE",
+					i);
+			sm->mlo.links[i].ap_rsne = NULL;
+			sm->mlo.links[i].ap_rsne_len = 0;
+		} else {
+			wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNE",
+					 ie, len);
+			sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
+			if (!sm->mlo.links[i].ap_rsne) {
+				sm->mlo.links[i].ap_rsne_len = 0;
+				return -1;
+			}
+			sm->mlo.links[i].ap_rsne_len = len;
+		}
+
+		ie = mlo->links[i].ap_rsnxe;
+		len = mlo->links[i].ap_rsnxe_len;
+		os_free(sm->mlo.links[i].ap_rsnxe);
+		if (!ie || len == 0) {
+			if (sm->mlo.links[i].ap_rsnxe)
+				wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+					"RSN: Clearing MLO link[%u] AP RSNXE",
+					i);
+			sm->mlo.links[i].ap_rsnxe = NULL;
+			sm->mlo.links[i].ap_rsnxe_len = 0;
+		} else {
+			wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXE", ie,
+					 len);
+			sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
+			if (!sm->mlo.links[i].ap_rsnxe) {
+				sm->mlo.links[i].ap_rsnxe_len = 0;
+				return -1;
+			}
+			sm->mlo.links[i].ap_rsnxe_len = len;
+		}
+	}
+
+	return 0;
+}
+
+
 /**
  * wpa_sm_set_own_addr - Set own MAC address
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3828,10 +4992,11 @@
 }
 
 
-int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
 			const void *network_ctx)
 {
-	return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
+	return pmksa_cache_get(sm->pmksa, bssid, own_addr, NULL, network_ctx,
+			       0) != NULL;
 }
 
 
@@ -3841,23 +5006,25 @@
 						      const void *network_ctx,
 						      int akmp)
 {
-	return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp);
+	return pmksa_cache_get(sm->pmksa, aa, sm->own_addr, pmkid, network_ctx,
+			       akmp);
+}
+
+
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+			       struct rsn_pmksa_cache_entry *entry)
+{
+	if (sm && sm->pmksa)
+		pmksa_cache_remove(sm->pmksa, entry);
 }
 
 
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
-	sm->ptk_set = 0;
-	sm->tptk_set = 0;
+	wpa_sm_clear_ptk(sm);
 	sm->pmk_len = 0;
 	os_memset(sm->pmk, 0, sizeof(sm->pmk));
-	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
-	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
-	os_memset(&sm->gtk, 0, sizeof(sm->gtk));
-	os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
-	os_memset(&sm->igtk, 0, sizeof(sm->igtk));
-	os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
 #ifdef CONFIG_IEEE80211R
 	os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 	sm->xxkey_len = 0;
@@ -3874,19 +5041,28 @@
 }
 
 
-int wpa_sm_has_ptk(struct wpa_sm *sm)
+#ifdef CONFIG_IEEE80211R
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md)
 {
-	if (sm == NULL)
-		return 0;
+	if (!sm)
+		return false;
+	if (!wpa_key_mgmt_ft(sm->key_mgmt) ||
+	    os_memcmp(md, sm->key_mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		/* Do not allow FT protocol to be used even if we were to have
+		 * an PTK since the mobility domain has changed. */
+		return false;
+	}
 	return sm->ptk_set;
 }
+#endif /* CONFIG_IEEE80211R */
 
 
 int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
 {
 	if (!sm)
 		return 0;
-	return sm->ptk.installed;
+	return sm->tk_set || sm->ptk.installed;
 }
 
 
@@ -3911,7 +5087,7 @@
 void wpa_sm_install_pmk(struct wpa_sm *sm)
 {
 	/* In case the driver wants to handle re-assocs, pass it down the PMK. */
-	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
+	if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->pairwise_cipher), NULL, 0, 0, NULL, 0,
 		(u8*)sm->pmk, sm->pmk_len, KEY_FLAG_PMK) < 0) {
 		wpa_hexdump(MSG_DEBUG, "PSK: Install PMK to the driver for driver reassociations",
 			(u8*)sm->pmk, sm->pmk_len);
@@ -4080,6 +5256,12 @@
 }
 
 
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm)
+{
+	return sm->mlo.valid_links ? sm->mlo.ap_mld_addr : sm->bssid;
+}
+
+
 #ifdef CONFIG_FILS
 
 struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
@@ -4315,7 +5497,7 @@
 		}
 
 		if (wpa_ft_parse_ies(pos, end - pos, &parse,
-				     wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
+				     sm->key_mgmt) < 0) {
 			wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
 			goto fail;
 		}
@@ -4462,7 +5644,8 @@
 	else
 		kdk_len = 0;
 
-	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr,
+			    wpa_sm_get_auth_addr(sm),
 			    sm->fils_nonce, sm->fils_anonce,
 			    dh_ss ? wpabuf_head(dh_ss) : NULL,
 			    dh_ss ? wpabuf_len(dh_ss) : 0,
@@ -4474,6 +5657,15 @@
 		goto fail;
 	}
 
+#ifdef CONFIG_PASN
+	if (sm->secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_ltf_keyseed(&sm->ptk, sm->key_mgmt, sm->pairwise_cipher)) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to derive LTF keyseed");
+		goto fail;
+	}
+#endif /* CONFIG_PASN */
+
 	wpabuf_clear_free(dh_ss);
 	dh_ss = NULL;
 
@@ -4595,21 +5787,27 @@
 	if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
 			      sm->ssid_len, sm->mobility_domain,
 			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
-			      sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) {
+			      sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 0) {
 		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
 		return -1;
 	}
-	sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+	if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+		sm->pmk_r0_len = sm->fils_ft_len;
+	else
+		sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 	wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
 		   MAC2STR(sm->r1kh_id));
 	pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
 	if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
-				   sm->pmk_r1_name, use_sha384) < 0) {
+				   sm->pmk_r1_name, sm->fils_ft_len) < 0) {
 		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
 		return -1;
 	}
 	os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
+	os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+
 	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
 		/* Management Group Cipher Suite */
 		pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
@@ -5015,12 +6213,13 @@
 	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
 			sm->ptk.tk, keylen);
-	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+	if (wpa_sm_set_key(sm, -1, alg, wpa_sm_get_auth_addr(sm), 0, 1,
+			   null_rsc, rsclen,
 			   sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d auth_addr="
 			MACSTR ")",
-			alg, keylen, MAC2STR(sm->bssid));
+			alg, keylen, MAC2STR(wpa_sm_get_auth_addr(sm)));
 		goto fail;
 	}
 
@@ -5033,6 +6232,7 @@
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
 	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
+	sm->tk_set = true;
 
 	/* FILS HLP Container */
 	fils_process_hlp_container(sm, ie_start, end - ie_start);
@@ -5302,13 +6502,17 @@
 
 
 #ifdef CONFIG_PASN
-void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
-			      const u8 *pmkid, const u8 *bssid, int key_mgmt)
+
+void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2)
 {
-	sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
-					bssid, sm->own_addr, NULL,
-					key_mgmt, 0);
+	if (flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
+		sm->secure_ltf = 1;
+	if (flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
+		sm->secure_rtt = 1;
+	if (flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
+		sm->prot_range_neg = 1;
 }
+
 #endif /* CONFIG_PASN */
 
 
@@ -5317,3 +6521,17 @@
 	if (sm)
 		pmksa_cache_reconfig(sm->pmksa);
 }
+
+
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm)
+{
+	return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+			  struct rsn_pmksa_cache_entry *entry)
+{
+	if (sm)
+		sm->cur_pmksa = entry;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 00fa0bc..cd3ef3e 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -19,6 +19,8 @@
 struct wpa_config_blob;
 struct hostapd_freq_params;
 struct wpa_channel_info;
+struct rsn_pmksa_cache_entry;
+enum frame_encryption;
 
 struct wpa_sm_ctx {
 	void *ctx; /* pointer to arbitrary upper level context */
@@ -28,7 +30,7 @@
 	enum wpa_states (*get_state)(void *ctx);
 	void (*deauthenticate)(void * ctx, u16 reason_code);
 	void (*reconnect)(void *ctx);
-	int (*set_key)(void *ctx, enum wpa_alg alg,
+	int (*set_key)(void *ctx, int link_id, enum wpa_alg alg,
 		       const u8 *addr, int key_idx, int set_tx,
 		       const u8 *seq, size_t seq_len,
 		       const u8 *key, size_t key_len, enum key_flag key_flag);
@@ -90,8 +92,15 @@
 			    const u8 *pkt, size_t pkt_len);
 	int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
 	void (*transition_disable)(void *ctx, u8 bitmap);
-	void (*store_ptk)(void *ctx, u8 *addr, int cipher,
+	void (*store_ptk)(void *ctx, const u8 *addr, int cipher,
 			  u32 life_time, const struct wpa_ptk *ptk);
+#ifdef CONFIG_PASN
+	int (*set_ltf_keyseed)(void *ctx, const u8 *own_addr,
+			       const u8 *peer_addr, size_t ltf_keyseed_len,
+			       const u8 *ltf_keyseed);
+#endif /* CONFIG_PASN */
+	void (*notify_pmksa_cache_entry)(void *ctx,
+					 struct rsn_pmksa_cache_entry *entry);
 };
 
 
@@ -139,6 +148,27 @@
 	bool force_kdk_derivation;
 };
 
+struct wpa_sm_link {
+	u8 addr[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u8 *ap_rsne, *ap_rsnxe;
+	size_t ap_rsne_len, ap_rsnxe_len;
+	struct wpa_gtk gtk;
+	struct wpa_gtk gtk_wnm_sleep;
+	struct wpa_igtk igtk;
+	struct wpa_igtk igtk_wnm_sleep;
+	struct wpa_bigtk bigtk;
+	struct wpa_bigtk bigtk_wnm_sleep;
+};
+
+struct wpa_sm_mlo {
+	u8 ap_mld_addr[ETH_ALEN];
+	u8 assoc_link_id;
+	u16 valid_links; /* bitmap of accepted links */
+	u16 req_links; /* bitmap of requested links */
+	struct wpa_sm_link links[MAX_NUM_MLD_LINKS];
+};
+
 #ifndef CONFIG_NO_WPA
 
 struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
@@ -184,7 +214,7 @@
 void wpa_sm_aborted_cached(struct wpa_sm *sm);
 void wpa_sm_aborted_external_cached(struct wpa_sm *sm);
 int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
-		    const u8 *buf, size_t len);
+		    const u8 *buf, size_t len, enum frame_encryption encrypted);
 int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
 int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
 struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm);
@@ -194,7 +224,7 @@
 void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
 			    const u8 *pmkid, const u8 *bssid,
 			    const u8 *fils_cache_id);
-int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, const u8 *own_addr,
 			const void *network_ctx);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm,
@@ -202,7 +232,9 @@
 						      const u8 *pmkid,
 						      const void *network_ctx,
 						      int akmp);
-int wpa_sm_has_ptk(struct wpa_sm *sm);
+void wpa_sm_pmksa_cache_remove(struct wpa_sm *sm,
+			       struct rsn_pmksa_cache_entry *entry);
+bool wpa_sm_has_ft_keys(struct wpa_sm *sm, const u8 *md);
 int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
 
 void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
@@ -223,6 +255,7 @@
 			    const u8 *ptk_kek, size_t ptk_kek_len);
 int wpa_fils_is_completed(struct wpa_sm *sm);
 void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
+int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
 
 #else /* CONFIG_NO_WPA */
 
@@ -368,7 +401,8 @@
 }
 
 static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
-				  const u8 *buf, size_t len)
+				  const u8 *buf, size_t len,
+				  enum frame_encryption encrypted)
 {
 	return -1;
 }
@@ -436,6 +470,12 @@
 {
 }
 
+static inline int wpa_sm_set_mlo_params(struct wpa_sm *sm,
+					const struct wpa_sm_mlo *mlo)
+{
+	return 0;
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_IEEE80211R
@@ -454,7 +494,7 @@
 int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 				 size_t ies_len, const u8 *src_addr);
 int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
-			 const u8 *mdie);
+			 const u8 *mdie, bool force);
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 int wpa_ft_is_ft_protocol(struct wpa_sm *sm);
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -522,8 +562,9 @@
 
 #ifdef CONFIG_PASN
 
-int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
-			      u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+static inline int
+wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+			  u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
 {
 	return -1;
 }
@@ -578,7 +619,11 @@
 void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
 void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
 void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
-void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
-			      const u8 *pmkid, const u8 *bssid, int key_mgmt);
+void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2);
+struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm);
+
+void wpa_sm_set_cur_pmksa(struct wpa_sm *sm,
+			  struct rsn_pmksa_cache_entry *entry);
+const u8 * wpa_sm_get_auth_addr(struct wpa_sm *sm);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 9d3b538..56a30c8 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -41,6 +42,7 @@
 	int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
 	const u8 *mpmk;
 	size_t mpmk_len, kdk_len;
+	int ret = 0;
 
 	if (sm->xxkey_len > 0) {
 		mpmk = sm->xxkey;
@@ -54,11 +56,14 @@
 		return -1;
 	}
 
-	sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+	if (wpa_key_mgmt_sae_ext_key(sm->key_mgmt))
+		sm->pmk_r0_len = mpmk_len;
+	else
+		sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 	if (wpa_derive_pmk_r0(mpmk, mpmk_len, sm->ssid,
 			      sm->ssid_len, sm->mobility_domain,
 			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
-			      sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
+			      sm->pmk_r0, sm->pmk_r0_name, sm->key_mgmt) < 0)
 		return -1;
 	sm->pmk_r1_len = sm->pmk_r0_len;
 	if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
@@ -75,10 +80,25 @@
 	else
 		kdk_len = 0;
 
-	return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
-				 sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
-				 ptk_name, sm->key_mgmt, sm->pairwise_cipher,
-				 kdk_len);
+	ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
+				anonce, sm->own_addr, wpa_sm_get_auth_addr(sm),
+				sm->pmk_r1_name, ptk, ptk_name, sm->key_mgmt,
+				sm->pairwise_cipher, kdk_len);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+		return ret;
+	}
+
+	os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+
+#ifdef CONFIG_PASN
+	if (sm->secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+		ret = wpa_ltf_keyseed(ptk, sm->key_mgmt, sm->pairwise_cipher);
+#endif /* CONFIG_PASN */
+
+	return ret;
 }
 
 
@@ -92,7 +112,6 @@
 int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
 {
 	struct wpa_ft_ies ft;
-	int use_sha384;
 
 	if (sm == NULL)
 		return 0;
@@ -108,8 +127,7 @@
 		return 0;
 	}
 
-	use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
-	if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0)
+	if (wpa_ft_parse_ies(ies, ies_len, &ft, sm->key_mgmt) < 0)
 		return -1;
 
 	if (ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
@@ -171,7 +189,7 @@
  * @len: Buffer for returning the length of the IEs
  * @anonce: ANonce or %NULL if not yet available
  * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
- * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck: KCK for MIC or %NULL if no MIC is used
  * @kck_len: KCK length in octets
  * @target_ap: Target AP address
  * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
@@ -198,12 +216,13 @@
 	size_t rsnxe_len;
 	int rsnxe_used;
 	int res;
+	u8 mic_control;
 
 	sm->ft_completed = 0;
 	sm->ft_reassoc_completed = 0;
 
 	buf_len = 2 + sizeof(struct rsn_mdie) + 2 +
-		sizeof(struct rsn_ftie_sha384) +
+		sizeof(struct rsn_ftie_sha512) +
 		2 + sm->r0kh_id_len + ric_ies_len + 100;
 	buf = os_zalloc(buf_len);
 	if (buf == NULL)
@@ -257,6 +276,8 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
 #ifdef CONFIG_FILS
 	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
@@ -319,7 +340,8 @@
 	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 	ftie_len = pos++;
 	rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) && anonce &&
-		(sm->sae_pwe == 1 || sm->sae_pwe == 2);
+		(sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		 sm->sae_pwe == SAE_PWE_BOTH);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (anonce && sm->ft_rsnxe_used) {
 		rsnxe_used = sm->ft_rsnxe_used == 1;
@@ -327,11 +349,28 @@
 			   rsnxe_used);
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+	mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->pmk_r0_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *ftie;
+
+		ftie = (struct rsn_ftie_sha512 *) pos;
+		mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		ftie->mic_control[0] = mic_control;
+		fte_mic = ftie->mic;
+		elem_count = &ftie->mic_control[1];
+		pos += sizeof(*ftie);
+		os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+		if (anonce)
+			os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+	} else if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    sm->pmk_r0_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(sm->key_mgmt)) {
 		struct rsn_ftie_sha384 *ftie;
 
 		ftie = (struct rsn_ftie_sha384 *) pos;
-		ftie->mic_control[0] = !!rsnxe_used;
+		mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		ftie->mic_control[0] = mic_control;
 		fte_mic = ftie->mic;
 		elem_count = &ftie->mic_control[1];
 		pos += sizeof(*ftie);
@@ -342,7 +381,8 @@
 		struct rsn_ftie *ftie;
 
 		ftie = (struct rsn_ftie *) pos;
-		ftie->mic_control[0] = !!rsnxe_used;
+		mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		ftie->mic_control[0] = mic_control;
 		fte_mic = ftie->mic;
 		elem_count = &ftie->mic_control[1];
 		pos += sizeof(*ftie);
@@ -426,7 +466,8 @@
 		*elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len);
 		if (rsnxe_len)
 			*elem_count += 1;
-		if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
+		if (wpa_ft_mic(sm->key_mgmt, kck, kck_len,
+			       sm->own_addr, target_ap, 5,
 			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
 			       ftie_pos, 2 + *ftie_len,
 			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
@@ -461,14 +502,16 @@
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 
-	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
+	/* TODO: AP MLD address for MLO */
+	if (wpa_sm_set_key(sm, -1, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
 			   (u8 *) sm->ptk.tk, keylen,
 			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 		return -1;
 	}
+	sm->tk_set = true;
 
-	wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
+	wpa_sm_store_ptk(sm, bssid, sm->pairwise_cipher,
 			 sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
 	return 0;
 }
@@ -547,8 +590,6 @@
 	const u8 *bssid;
 	const u8 *kck;
 	size_t kck_len, kdk_len;
-	int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
-	const u8 *anonce, *snonce;
 
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 	wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
@@ -574,7 +615,7 @@
 		return -1;
 	}
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
 		return -1;
 	}
@@ -587,34 +628,15 @@
 		return -1;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
-
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return -1;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return -1;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
+	if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTE");
+		return -1;
 	}
 
-	if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
-			    snonce, WPA_NONCE_LEN);
+			    parse.fte_snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->snonce, WPA_NONCE_LEN);
 		return -1;
@@ -659,8 +681,8 @@
 	os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
-	os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: ANonce", parse.fte_anonce, WPA_NONCE_LEN);
+	os_memcpy(sm->anonce, parse.fte_anonce, WPA_NONCE_LEN);
 	if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
 			      sm->r1kh_id, sm->own_addr, sm->pmk_r1,
 			      sm->pmk_r1_name) < 0)
@@ -678,13 +700,26 @@
 	else
 		kdk_len = 0;
 
+	/* TODO: AP MLD address for MLO */
 	if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
-			      anonce, sm->own_addr, bssid,
+			      parse.fte_anonce, sm->own_addr, bssid,
 			      sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
 			      sm->pairwise_cipher,
 			      kdk_len) < 0)
 		return -1;
 
+	os_memcpy(sm->key_mobility_domain, sm->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+
+#ifdef CONFIG_PASN
+	if (sm->secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_ltf_keyseed(&sm->ptk, sm->key_mgmt, sm->pairwise_cipher)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+		return -1;
+	}
+#endif /* CONFIG_PASN */
+
 	if (wpa_key_mgmt_fils(sm->key_mgmt)) {
 		kck = sm->ptk.kck2;
 		kck_len = sm->ptk.kck2_len;
@@ -692,7 +727,7 @@
 		kck = sm->ptk.kck;
 		kck_len = sm->ptk.kck_len;
 	}
-	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce,
+	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, parse.fte_anonce,
 				    sm->pmk_r1_name,
 				    kck, kck_len, bssid,
 				    ric_ies, ric_ies_len,
@@ -841,7 +876,7 @@
 		os_memcpy(gtk + 16, gtk + 24, 8);
 		os_memcpy(gtk + 24, tmp, 8);
 	}
-	if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+	if (wpa_sm_set_key(sm, -1, alg, broadcast_ether_addr, keyidx, 0,
 			   gtk_elem + 3, rsc_len, gtk, keylen,
 			   KEY_FLAG_GROUP_RX) < 0) {
 		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
@@ -908,7 +943,7 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
 			igtk_len);
-	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+	if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
 			   broadcast_ether_addr, keyidx, 0,
 			   igtk_elem + 2, 6, igtk, igtk_len,
 			   KEY_FLAG_GROUP_RX) < 0) {
@@ -976,7 +1011,7 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk,
 			bigtk_len);
-	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+	if (wpa_sm_set_key(sm, -1, wpa_cipher_to_alg(sm->mgmt_group_cipher),
 			   broadcast_ether_addr, keyidx, 0,
 			   bigtk_elem + 2, 6, bigtk, bigtk_len,
 			   KEY_FLAG_GROUP_RX) < 0) {
@@ -1000,10 +1035,8 @@
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 	const u8 *kck;
 	size_t kck_len;
-	int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
-	const u8 *anonce, *snonce, *fte_mic;
-	u8 fte_elem_count;
-	int own_rsnxe_used, rsnxe_used;
+	int own_rsnxe_used;
+	size_t mic_len;
 
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 
@@ -1018,7 +1051,7 @@
 		return 0;
 	}
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->key_mgmt) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
 		return -1;
 	}
@@ -1031,49 +1064,37 @@
 		return -1;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->pmk_r1_len == SHA512_MAC_LEN)
+		mic_len = 32;
+	else if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		  sm->pmk_r1_len == SHA384_MAC_LEN) ||
+		 wpa_key_mgmt_sha384(sm->key_mgmt))
+		mic_len = 24;
+	else
+		mic_len = 16;
 
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return -1;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return -1;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
+	if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+	    parse.fte_mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+			   parse.fte_mic_len, mic_len);
+		return -1;
 	}
 
-	if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
-			    snonce, WPA_NONCE_LEN);
+			    parse.fte_snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->snonce, WPA_NONCE_LEN);
 		return -1;
 	}
 
-	if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
-			    anonce, WPA_NONCE_LEN);
+			    parse.fte_anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->anonce, WPA_NONCE_LEN);
 		return -1;
@@ -1120,10 +1141,10 @@
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
 	if (parse.rsnxe)
 		count++;
-	if (fte_elem_count != count) {
+	if (parse.fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
-			   fte_elem_count, count);
+			   parse.fte_elem_count, count);
 		return -1;
 	}
 
@@ -1135,7 +1156,7 @@
 		kck_len = sm->ptk.kck_len;
 	}
 
-	if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6,
+	if (wpa_ft_mic(sm->key_mgmt, kck, kck_len, sm->own_addr, src_addr, 6,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
@@ -1147,14 +1168,15 @@
 		return -1;
 	}
 
-	if (os_memcmp_const(mic, fte_mic, 16) != 0) {
+	if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
-		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16);
-		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+			    parse.fte_mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
 		return -1;
 	}
 
-	if (rsnxe_used && !sm->ap_rsnxe) {
+	if (parse.fte_rsnxe_used && !sm->ap_rsnxe) {
 		wpa_printf(MSG_INFO,
 			   "FT: FTE indicated that AP uses RSNXE, but RSNXE was not included in Beacon/Probe Response frames");
 		return -1;
@@ -1188,7 +1210,8 @@
 	}
 
 	own_rsnxe_used = wpa_key_mgmt_sae(sm->key_mgmt) &&
-		(sm->sae_pwe == 1 || sm->sae_pwe == 2);
+		(sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		 sm->sae_pwe == SAE_PWE_BOTH);
 	if ((sm->ap_rsnxe && !parse.rsnxe && own_rsnxe_used) ||
 	    (!sm->ap_rsnxe && parse.rsnxe) ||
 	    (sm->ap_rsnxe && parse.rsnxe &&
@@ -1260,14 +1283,24 @@
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @target_ap: Target AP Address
  * @mdie: Mobility Domain IE from the target AP
+ * @force: Whether to force the request and ignore mobility domain differences
+ *	(only for testing purposes)
  * Returns: 0 on success, -1 on failure
  */
 int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
-			 const u8 *mdie)
+			 const u8 *mdie, bool force)
 {
 	u8 *ft_ies;
 	size_t ft_ies_len;
 
+	if (!force &&
+	    (!mdie || mdie[1] < 3 || !wpa_sm_has_ft_keys(sm, mdie + 2))) {
+		wpa_printf(MSG_DEBUG, "FT: Cannot use over-the DS with " MACSTR
+			   " - no keys matching the mobility domain",
+			   MAC2STR(target_ap));
+		return -1;
+	}
+
 	wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
 		   MAC2STR(target_ap));
 
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 579616f..a3c13b1 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -27,6 +27,7 @@
 	size_t pmk_len;
 	struct wpa_ptk ptk, tptk;
 	int ptk_set, tptk_set;
+	bool tk_set; /* Whether any TK is configured to the driver */
 	unsigned int msg_3_of_4_ok:1;
 	u8 snonce[WPA_NONCE_LEN];
 	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
@@ -105,7 +106,7 @@
 	int rsn_enabled; /* Whether RSN is enabled in configuration */
 	int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
 	int ocv; /* Operating Channel Validation */
-	int sae_pwe; /* SAE PWE generation options */
+	enum sae_pwe sae_pwe; /* SAE PWE generation options */
 
 	unsigned int sae_pk:1; /* whether SAE-PK is used */
 	unsigned int secure_ltf:1;
@@ -149,6 +150,7 @@
 	size_t pmk_r1_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 key_mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
 	size_t r0kh_id_len;
 	u8 r1kh_id[FT_R1KH_ID_LEN];
@@ -217,6 +219,7 @@
 	struct wpabuf *dpp_z;
 	int dpp_pfs;
 #endif /* CONFIG_DPP2 */
+	struct wpa_sm_mlo mlo;
 };
 
 
@@ -238,15 +241,15 @@
 	sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
 }
 
-static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
-				 const u8 *addr, int key_idx, int set_tx,
-				 const u8 *seq, size_t seq_len,
+static inline int wpa_sm_set_key(struct wpa_sm *sm, int link_id,
+				 enum wpa_alg alg, const u8 *addr, int key_idx,
+				 int set_tx, const u8 *seq, size_t seq_len,
 				 const u8 *key, size_t key_len,
 				 enum key_flag key_flag)
 {
 	WPA_ASSERT(sm->ctx->set_key);
-	return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
-				seq, seq_len, key, key_len, key_flag);
+	return sm->ctx->set_key(sm->ctx->ctx, link_id, alg, addr, key_idx,
+				set_tx, seq, seq_len, key, key_len, key_flag);
 }
 
 static inline void wpa_sm_reconnect(struct wpa_sm *sm)
@@ -474,7 +477,7 @@
 }
 
 static inline void wpa_sm_store_ptk(struct wpa_sm *sm,
-				    u8 *addr, int cipher,
+				    const u8 *addr, int cipher,
 				    u32 life_time, struct wpa_ptk *ptk)
 {
 	if (sm->ctx->store_ptk)
@@ -482,6 +485,26 @@
 				   ptk);
 }
 
+#ifdef CONFIG_PASN
+static inline int wpa_sm_set_ltf_keyseed(struct wpa_sm *sm, const u8 *own_addr,
+					 const u8 *peer_addr,
+					 size_t ltf_keyseed_len,
+					 const u8 *ltf_keyseed)
+{
+	WPA_ASSERT(sm->ctx->set_ltf_keyseed);
+	return sm->ctx->set_ltf_keyseed(sm->ctx->ctx, own_addr, peer_addr,
+					ltf_keyseed_len, ltf_keyseed);
+}
+#endif /* CONFIG_PASN */
+
+static inline void
+wpa_sm_notify_pmksa_cache_entry(struct wpa_sm *sm,
+				struct rsn_pmksa_cache_entry *entry)
+{
+	if (sm->ctx->notify_pmksa_cache_entry)
+		sm->ctx->notify_pmksa_cache_entry(sm->ctx->ctx, entry);
+}
+
 int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
 		       int ver, const u8 *dest, u16 proto,
 		       u8 *msg, size_t msg_len, u8 *key_mic);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 3ba722f..50bd2b2 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -191,8 +191,12 @@
 #ifdef CONFIG_SAE
 	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+	} else if (key_mgmt == WPA_KEY_MGMT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
 #endif /* CONFIG_SAE */
 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
@@ -358,7 +362,8 @@
 	size_t flen;
 
 	if (wpa_key_mgmt_sae(sm->key_mgmt) &&
-	    (sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) {
+	    (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     sm->sae_pwe == SAE_PWE_BOTH || sm->sae_pk)) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 		if (sm->sae_pk)
@@ -371,7 +376,7 @@
 	if (sm->secure_rtt)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (sm->prot_range_neg)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 
 	flen = (capab & 0xff00) ? 2 : 1;
 	if (!capab)
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 3825a73..9df56c2 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -771,7 +771,8 @@
 			hlen = tls_key_x_server_params_hash(
 				conn->rl.tls_version, conn->client_random,
 				conn->server_random, server_params,
-				server_params_end - server_params, hash);
+				server_params_end - server_params, hash,
+				sizeof(hash));
 		}
 
 		if (hlen < 0)
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index e178915..0dd8e27 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -378,7 +378,7 @@
 int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
 				 const u8 *server_random,
 				 const u8 *server_params,
-				 size_t server_params_len, u8 *hash)
+				 size_t server_params_len, u8 *hash, size_t hsz)
 {
 	u8 *hpos;
 	size_t hlen;
@@ -393,6 +393,8 @@
 	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
 	crypto_hash_update(ctx, server_params, server_params_len);
 	hlen = MD5_MAC_LEN;
+	if (hsz < hlen)
+		return -1;
 	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
 		return -1;
 	hpos += hlen;
@@ -403,7 +405,7 @@
 	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
 	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
 	crypto_hash_update(ctx, server_params, server_params_len);
-	hlen = hash + sizeof(hash) - hpos;
+	hlen = hsz - hlen;
 	if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
 		return -1;
 	hpos += hlen;
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index e30b15a..4cfdc2d 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -267,7 +267,8 @@
 int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
 				 const u8 *server_random,
 				 const u8 *server_params,
-				 size_t server_params_len, u8 *hash);
+				 size_t server_params_len,
+				 u8 *hash, size_t hsz);
 int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
 			 const u8 *data, size_t data_len,
 			 const u8 *pos, size_t len, u8 *alert);
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 8d36cf1..545abae 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -620,7 +620,7 @@
 			hlen = tls_key_x_server_params_hash(
 				conn->rl.tls_version, conn->client_random,
 				conn->server_random, server_params,
-				pos - server_params, hash);
+				pos - server_params, hash, sizeof(hash));
 		}
 
 		if (hlen < 0) {
diff --git a/src/utils/browser.c b/src/utils/browser.c
index c0f4380..b5d5ba7 100644
--- a/src/utils/browser.c
+++ b/src/utils/browser.c
@@ -370,11 +370,21 @@
 
 #ifdef USE_WEBKIT2
 	if (ignore_tls) {
+#if WEBKIT_CHECK_VERSION(2, 32, 0)
+		WebKitWebContext *wkctx;
+		WebKitWebsiteDataManager *wkmgr;
+
+		wkctx = webkit_web_context_get_default();
+		wkmgr = webkit_web_context_get_website_data_manager(wkctx);
+		webkit_website_data_manager_set_tls_errors_policy(
+			wkmgr, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+#else
 		WebKitWebContext *wkctx;
 
 		wkctx = webkit_web_context_get_default();
 		webkit_web_context_set_tls_errors_policy(
 			wkctx, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
+#endif
 	}
 #endif /* USE_WEBKIT2 */
 
diff --git a/src/utils/crc32.c b/src/utils/crc32.c
index 12d9e2a..3712549 100644
--- a/src/utils/crc32.c
+++ b/src/utils/crc32.c
@@ -72,7 +72,7 @@
 };
 
 
-u32 crc32(const u8 *frame, size_t frame_len)
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len)
 {
 	size_t i;
 	u32 crc;
diff --git a/src/utils/crc32.h b/src/utils/crc32.h
index dc31399..71a19dc 100644
--- a/src/utils/crc32.h
+++ b/src/utils/crc32.h
@@ -9,6 +9,6 @@
 #ifndef CRC32_H
 #define CRC32_H
 
-u32 crc32(const u8 *frame, size_t frame_len);
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len);
 
 #endif /* CRC32_H */
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
index 92a3590..a971f72 100644
--- a/src/utils/ip_addr.c
+++ b/src/utils/ip_addr.c
@@ -51,3 +51,22 @@
 
 	return -1;
 }
+
+
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+		      const struct hostapd_ip_addr *b)
+{
+	if (a->af != b->af)
+		return false;
+
+	if (a->af == AF_INET && a->u.v4.s_addr == b->u.v4.s_addr)
+		return true;
+
+#ifdef CONFIG_IPV6
+	if (a->af == AF_INET6 &&
+	    os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) == 0)
+		return true;
+#endif /* CONFIG_IPV6 */
+
+	return false;
+}
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
index 0670411..1d35e0b 100644
--- a/src/utils/ip_addr.h
+++ b/src/utils/ip_addr.h
@@ -23,5 +23,7 @@
 const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
 			    size_t buflen);
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+		      const struct hostapd_ip_addr *b);
 
 #endif /* IP_ADDR_H */
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index a338a20..4469712 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -596,13 +596,21 @@
 }
 
 
-void wpa_debug_close_file(void)
+void wpa_debug_stop_log(void)
 {
 #ifdef CONFIG_DEBUG_FILE
 	if (!out_file)
 		return;
 	fclose(out_file);
 	out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+	wpa_debug_stop_log();
 	os_free(last_path);
 	last_path = NULL;
 #endif /* CONFIG_DEBUG_FILE */
@@ -621,12 +629,17 @@
 
 #ifndef CONFIG_NO_WPA_MSG
 static wpa_msg_cb_func wpa_msg_cb = NULL;
+static wpa_msg_cb_func wpa_msg_aidl_cb = NULL;
 
 void wpa_msg_register_cb(wpa_msg_cb_func func)
 {
 	wpa_msg_cb = func;
 }
 
+void wpa_msg_register_aidl_cb(wpa_msg_cb_func func)
+{
+	wpa_msg_aidl_cb = func;
+}
 
 static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
 
@@ -670,6 +683,8 @@
 	wpa_printf(level, "%s%s", prefix, buf);
 	if (wpa_msg_cb)
 		wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
 	bin_clear_free(buf, buflen);
 }
 
@@ -681,7 +696,7 @@
 	int buflen;
 	int len;
 
-	if (!wpa_msg_cb)
+	if (!wpa_msg_cb && !wpa_msg_aidl_cb)
 		return;
 
 	va_start(ap, fmt);
@@ -697,7 +712,10 @@
 	va_start(ap, fmt);
 	len = vsnprintf(buf, buflen, fmt, ap);
 	va_end(ap);
-	wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
 	bin_clear_free(buf, buflen);
 }
 
@@ -725,6 +743,8 @@
 	wpa_printf(level, "%s", buf);
 	if (wpa_msg_cb)
 		wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
 	bin_clear_free(buf, buflen);
 }
 
@@ -736,7 +756,7 @@
 	int buflen;
 	int len;
 
-	if (!wpa_msg_cb)
+	if (!wpa_msg_cb && !wpa_msg_aidl_cb)
 		return;
 
 	va_start(ap, fmt);
@@ -752,7 +772,10 @@
 	va_start(ap, fmt);
 	len = vsnprintf(buf, buflen, fmt, ap);
 	va_end(ap);
-	wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	if (wpa_msg_cb)
+		wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
 	bin_clear_free(buf, buflen);
 }
 
@@ -780,6 +803,9 @@
 	wpa_printf(level, "%s", buf);
 	if (wpa_msg_cb)
 		wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+
 	bin_clear_free(buf, buflen);
 }
 
@@ -807,6 +833,8 @@
 	wpa_printf(level, "%s", buf);
 	if (wpa_msg_cb)
 		wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
+	if (wpa_msg_aidl_cb)
+		wpa_msg_aidl_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
 	os_free(buf);
 }
 
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index c6d5cc6..291aa07 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -49,6 +49,7 @@
 int wpa_debug_reopen_file(void);
 void wpa_debug_close_file(void);
 void wpa_debug_setup_stdout(void);
+void wpa_debug_stop_log(void);
 
 /**
  * wpa_debug_printf_timestamp - Print timestamp for debug output
@@ -167,6 +168,7 @@
 #define wpa_msg_no_global(args...) do { } while (0)
 #define wpa_msg_global_only(args...) do { } while (0)
 #define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_aidl_cb(f) do { } while (0)
 #define wpa_msg_register_ifname_cb(f) do { } while (0)
 #else /* CONFIG_NO_WPA_MSG */
 /**
@@ -274,7 +276,7 @@
  * @func: Callback function (%NULL to unregister)
  */
 void wpa_msg_register_cb(wpa_msg_cb_func func);
-
+void wpa_msg_register_aidl_cb(wpa_msg_cb_func func);
 typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
 void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
 
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index bb3c055..63f0d52 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -63,12 +63,18 @@
 	} else
 		record->id_length = 0;
 
+	if (record->type_length > data + size - pos)
+		return -1;
 	record->type = record->type_length == 0 ? NULL : pos;
 	pos += record->type_length;
 
+	if (record->id_length > data + size - pos)
+		return -1;
 	record->id = record->id_length == 0 ? NULL : pos;
 	pos += record->id_length;
 
+	if (record->payload_length > (size_t) (data + size - pos))
+		return -1;
 	record->payload = record->payload_length == 0 ? NULL : pos;
 	pos += record->payload_length;
 
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 25b5b31..dd4423a 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -67,14 +67,14 @@
     defaults: ["wpa_supplicant_cflags_defaults"],
     srcs: [":wpa_supplicant_srcs"],
     shared_libs: [
-        "android.hardware.wifi.supplicant-V1-ndk",
+        "android.hardware.wifi.supplicant-V2-ndk",
+        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libc",
         "libcrypto",
         "libcutils",
         "libkeystore-engine-wifi-hidl",
-        "libkeystore-wifi-hidl",
         "liblog",
         "libnl",
         "libssl",
@@ -279,6 +279,8 @@
         "src/ap/ap_config.c",
         "src/ap/ap_drv_ops.c",
         "src/ap/ap_list.c",
+        "src/ap/comeback_token.c",
+        "src/pasn/pasn_responder.c",
         "src/ap/ap_mlme.c",
         "src/ap/authsrv.c",
         "src/ap/beacon.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index d1436e2..03dc209 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -108,6 +108,7 @@
 INCLUDES += $(LOCAL_PATH)/src/tls
 INCLUDES += $(LOCAL_PATH)/src/utils
 INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += $(LOCAL_PATH)/src/pasn
 INCLUDES += system/security/keystore/include
 ifdef CONFIG_DRIVER_NL80211
 ifneq ($(wildcard external/libnl),)
@@ -276,6 +277,7 @@
 OBJS += src/common/sae.c
 ifdef CONFIG_SAE_PK
 L_CFLAGS += -DCONFIG_SAE_PK
+NEED_AES_SIV=y
 OBJS += src/common/sae_pk.c
 endif
 NEED_ECC=y
@@ -419,6 +421,7 @@
 NEED_SHA256=y
 NEED_SHA384=y
 OBJS += src/common/ptksa_cache.c
+OBJS += src/pasn/pasn_initiator.c
 OBJS += pasn_supplicant.c
 endif
 
@@ -984,6 +987,8 @@
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
+OBJS += src/ap/comeback_token.c
+OBJS += src/pasn/pasn_responder.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
 OBJS += src/ap/dfs.c
@@ -1756,6 +1761,142 @@
 LDO=$(CC)
 endif
 
+PASNOBJS =
+PASNOBJS += src/utils/$(CONFIG_ELOOP).c
+PASNOBJS += src/utils/wpa_debug.c
+PASNOBJS += src/utils/wpabuf.c
+PASNOBJS += src/utils/os_$(CONFIG_OS).c
+PASNOBJS += src/utils/config.c
+PASNOBJS += src/utils/common.c
+
+ifdef NEED_BASE64
+PASNOBJS += src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+PASNOBJS += src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+PASNOBJS += src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+PASNOBJS += src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+PASNOBJS += src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+PASNOBJS += src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+PASNOBJS += src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/common/wpa_common.c
+endif
+
+PASNOBJS += src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+PASNOBJS += src/common/dragonfly.c
+endif
+
+PASNOBJS += src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+PASNOBJS += src/rsn_supp/pmksa_cache.c
+PASNOBJS += src/rsn_supp/wpa_ie.c
+endif
+
+PASNOBJS += src/ap/comeback_token.c
+PASNOBJS += src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+PASNOBJS += src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+PASNOBJS += src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+PASNOBJS += src/eap_peer/eap.c
+PASNOBJS += src/eap_peer/eap_methods.c
+PASNOBJS += src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+PASNOBJS += src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_openssl.c
+#PASNOBJS += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+PASNOBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+PASNOBJS += src/crypto/tls_gnutls.c
+PASNOBJS += -lgnutls -lgpg-error
+PASNOBJS += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+PASNOBJS += src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+PASNOBJS += src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+PASNOBJS += src/crypto/sha384-prf.c
+endif
+
+PASNOBJS += src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+PASNOBJS += src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+PASNOBJS += src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+PASNOBJS += src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+PASNOBJS += src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+PASNOBJS += src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+PASNOBJS += src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+PASNOBJS += src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+PASNOBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+PASNOBJS += src/pasn/pasn_initiator.c
+PASNOBJS += src/pasn/pasn_responder.c
+
 ########################
 
 include $(CLEAR_VARS)
@@ -1790,7 +1931,7 @@
 LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
 endif
 ifeq ($(CONFIG_TLS), openssl)
-LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore-wifi-hidl
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
 endif
 
 # With BoringSSL we need libkeystore-engine in order to provide access to
@@ -1811,7 +1952,8 @@
 LOCAL_SHARED_LIBRARIES += libdbus
 endif
 ifeq ($(WPA_SUPPLICANT_USE_AIDL), y)
-LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant-V1-ndk
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant-V2-ndk
+LOCAL_SHARED_LIBRARIES += android.system.keystore2-V1-ndk
 LOCAL_SHARED_LIBRARIES += libutils libbase
 LOCAL_SHARED_LIBRARIES += libbinder_ndk
 LOCAL_STATIC_LIBRARIES += libwpa_aidl
@@ -1876,6 +2018,7 @@
 LOCAL_SRC_FILES := \
     aidl/aidl.cpp \
     aidl/aidl_manager.cpp \
+    aidl/certificate_utils.cpp \
     aidl/iface_config_utils.cpp \
     aidl/p2p_iface.cpp \
     aidl/p2p_network.cpp \
@@ -1883,7 +2026,8 @@
     aidl/sta_network.cpp \
     aidl/supplicant.cpp
 LOCAL_SHARED_LIBRARIES := \
-    android.hardware.wifi.supplicant-V1-ndk \
+    android.hardware.wifi.supplicant-V2-ndk \
+    android.system.keystore2-V1-ndk \
     libbinder_ndk \
     libbase \
     libutils \
@@ -1893,3 +2037,14 @@
     $(LOCAL_PATH)/aidl
 include $(BUILD_STATIC_LIBRARY)
 endif # WPA_SUPPLICANT_USE_AIDL == y
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE = libpasn
+#LOCAL_CFLAGS = $(L_CFLAGS)
+#LOCAL_SRC_FILES = $(PASNOBJS)
+#LOCAL_C_INCLUDES = $(INCLUDES)
+#LOCAL_SHARED_LIBRARIES := libc libcutils liblog
+#ifeq ($(CONFIG_TLS), openssl)
+#LOCAL_SHARED_LIBRARIES := libcrypto libssl
+#endif
+#include $(BUILD_SHARED_LIBRARY)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 1c6911b..c682f73 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -12,6 +12,12 @@
 CONFIG_FILE=.config
 include ../src/build.rules
 
+ifdef CONFIG_BUILD_PASN_SO
+# add the dependency this way to allow CONFIG_BUILD_PASN_SO
+# being set in the config which is read by build.rules
+_all: libpasn.so
+endif
+
 ifdef CONFIG_BUILD_WPA_CLIENT_SO
 # add the dependency this way to allow CONFIG_BUILD_WPA_CLIENT_SO
 # being set in the config which is read by build.rules
@@ -76,6 +82,11 @@
 ifndef CONFIG_NO_WPA_PASSPHRASE
 	install -D wpa_passphrase $(DESTDIR)/$(BINDIR)/wpa_passphrase
 endif
+
+ifdef CONFIG_BUILD_PASN_SO
+	install -m 0644 -D libpasn.so $(DESTDIR)/$(LIBDIR)/libpasn.so
+endif
+
 ifdef CONFIG_BUILD_WPA_CLIENT_SO
 	install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
 	install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
@@ -266,6 +277,7 @@
 OBJS += ../src/common/sae.o
 ifdef CONFIG_SAE_PK
 CFLAGS += -DCONFIG_SAE_PK
+NEED_AES_SIV=y
 OBJS += ../src/common/sae_pk.o
 endif
 NEED_ECC=y
@@ -417,6 +429,7 @@
 NEED_SHA256=y
 NEED_SHA384=y
 OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/pasn/pasn_initiator.o
 OBJS += pasn_supplicant.o
 endif
 
@@ -986,6 +999,8 @@
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/comeback_token.o
+OBJS += ../src/pasn/pasn_responder.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
 OBJS += ../src/ap/dfs.o
@@ -2092,6 +2107,157 @@
 	lcov -c -d $(BUILDDIR) > lcov.info
 	genhtml lcov.info --output-directory lcov-html
 
+PASN_CFLAGS := $(CFLAGS)
+PASN_CFLAGS += -DCONFIG_PASN
+
+LIBPASNSO := ../src/utils/$(CONFIG_ELOOP).c
+LIBPASNSO += ../src/utils/wpa_debug.c
+LIBPASNSO += ../src/utils/wpabuf.c
+LIBPASNSO += ../src/utils/os_$(CONFIG_OS).c
+LIBPASNSO += ../src/utils/config.c
+LIBPASNSO += ../src/utils/common.c
+
+ifdef NEED_BASE64
+LIBPASNSO += ../src/utils/base64.c
+endif
+
+ifdef CONFIG_WPA_TRACE
+LIBPASNSO += ../src/utils/trace.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_FILE
+LIBPASNSO += ../src/utils/ext_password_file.c
+endif
+
+ifdef CONFIG_EXT_PASSWORD_TEST
+LIBPASNSO += ../src/utils/ext_password_test.c
+endif
+
+ifdef NEED_EXT_PASSWORD
+LIBPASNSO += ../src/utils/ext_password.c
+endif
+
+ifdef CONFIG_SAE
+LIBPASNSO += ../src/common/sae.c
+endif
+
+ifdef CONFIG_SAE_PK
+LIBPASNSO += ../src/common/sae_pk.c
+endif
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/common/wpa_common.c
+endif
+
+LIBPASNSO += ../src/common/ieee802_11_common.c
+
+ifdef NEED_DRAGONFLY
+LIBPASNSO += ../src/common/dragonfly.c
+endif
+
+LIBPASNSO += ../src/common/ptksa_cache.c
+
+ifndef CONFIG_NO_WPA
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
+LIBPASNSO += ../src/rsn_supp/wpa_ie.c
+endif
+
+LIBPASNSO += ../src/ap/comeback_token.c
+LIBPASNSO += ../src/ap/pmksa_cache_auth.c
+
+ifdef NEED_EAP_COMMON
+LIBPASNSO += ../src/eap_common/eap_common.c
+endif
+
+ifdef CHAP
+LIBPASNSO += ../src/eap_common/chap.c
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+LIBPASNSO += ../src/eap_peer/eap.c
+LIBPASNSO += ../src/eap_peer/eap_methods.c
+LIBPASNSO += ../src/eapol_supp/eapol_supp_sm.c
+endif
+
+ifeq ($(CONFIG_TLS), wolfssl)
+LIBPASNSO += ../src/crypto/crypto_wolfssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_wolfssl.c
+NEED_TLS_PRF_SHA256=y
+LIBPASNSO += -lwolfssl -lm
+endif
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+LIBPASNSO += ../src/crypto/crypto_openssl.c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_openssl.c
+LIBPASNSO += -lssl -lcrypto
+NEED_TLS_PRF_SHA256=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+LIBPASNSO += ../src/crypto/crypto_$(CONFIG_CRYPTO).c
+ifdef TLS_FUNCS
+LIBPASNSO += ../src/crypto/tls_gnutls.c
+LIBPASNSO += -lgnutls -lgpg-error
+LIBPASNSO += -lgcrypt
+endif
+endif
+
+ifdef NEED_TLS_PRF_SHA256
+LIBPASNSO += ../src/crypto/sha256-tlsprf.c
+endif
+
+ifdef NEED_SHA512
+LIBPASNSO += ../src/crypto/sha512-prf.c
+endif
+
+ifdef NEED_SHA384
+LIBPASNSO += ../src/crypto/sha384-prf.c
+endif
+
+LIBPASNSO += ../src/crypto/sha256-prf.c
+
+ifdef NEED_HMAC_SHA512_KDF
+LIBPASNSO += ../src/crypto/sha512-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA384_KDF
+LIBPASNSO += ../src/crypto/sha384-kdf.c
+endif
+
+ifdef NEED_HMAC_SHA256_KDF
+LIBPASNSO += ../src/crypto/sha256-kdf.c
+endif
+
+ifdef NEED_DH_GROUPS
+LIBPASNSO += ../src/crypto/dh_groups.c
+endif
+
+ifdef NEED_AES_SIV
+LIBPASNSO += ../src/crypto/aes-siv.c
+endif
+
+ifdef NEED_AES_CTR
+LIBPASNSO += ../src/crypto/aes-ctr.c
+endif
+
+ifdef NEED_SHA1
+LIBPASNSO += ../src/crypto/sha1-prf.c
+ifdef NEED_TLS_PRF
+LIBPASNSO += ../src/crypto/sha1-tlsprf.c
+endif
+endif
+
+LIBPASNSO += ../src/pasn/pasn_initiator.c
+LIBPASNSO += ../src/pasn/pasn_responder.c
+
+libpasn.so: $(LIBPASNSO)
+	@$(E) "  CC  $@ ($^)"
+	$(Q)$(CC) $(LDFLAGS) -o $@ $(PASN_CFLAGS) -shared -fPIC -lcrypto $^
+
 clean: common-clean
 	$(MAKE) -C ../src clean
 	$(MAKE) -C dbus clean
@@ -2102,6 +2268,7 @@
 	rm -f lcov.info
 	rm -rf lcov-html
 	rm -f libwpa_client.a
+	rm -f libpasn.so
 	rm -f libwpa_client.so
 	rm -f libwpa_test1 libwpa_test2
 	rm -f wpa_passphrase
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index a099a85..7d30e23 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -168,11 +168,16 @@
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #	format
 #
-# imsi_privacy_key: IMSI privacy key (PEM encoded X.509v3 certificate)
+# imsi_privacy_cert: IMSI privacy certificate (PEM encoded X.509v3 certificate)
 #	This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
 #	identity (IMSI) to improve privacy. The X.509v3 certificate needs to
 #	include a 2048-bit RSA public key and this is from the operator who
 #	authenticates the SIM/USIM.
+# imsi_privacy_attr: IMSI privacy attribute
+#	This field is used to help the EAP-SIM/AKA/AKA' server to identify
+#	the used certificate (and as such, the matching private key). This
+#	is set to an attribute in name=value format if the operator needs
+#	this information.
 #
 # domain_suffix_match: Constraint for server domain name
 #	If set, this FQDN is used as a suffix match requirement for the AAA
@@ -194,7 +199,26 @@
 #	be used to configure alternative FQDNs that will be considered home
 #	networks.
 #
+# home_ois: Home OI(s)
+#	This string field contains one or more comma delimited OIs (hexdump)
+#	identifying the access the access points that support authentication
+#	with this credential. There are an alternative to the use of the realm
+#	parameter. When using Home OIs to match the network, the EAP parameters
+#	need to be pre-configured with the credentials since the NAI Realm
+#	information may not be available or fetched.
+#	A successful authentication with the access point is possible as soon
+#	as at least one Home OI from the list matches an OI in the Roaming
+#	Consortium advertised by the access point.
+#	(Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+#	This string field contains the set of Home OI(s) (hexdump) that are
+#	required to be advertised by the AP for the credential to be considered
+#	matching.
+#	(Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
 # roaming_consortium: Roaming Consortium OI
+#	Deprecated: use home_ois instead.
 #	If roaming_consortium_len is non-zero, this field contains the
 #	Roaming Consortium OI that can be used to determine which access
 #	points support authentication with this credential. This is an
@@ -204,6 +228,7 @@
 #	may not be available or fetched.
 #
 # required_roaming_consortium: Required Roaming Consortium OI
+#	Deprecated: use required_home_ois instead.
 #	If required_roaming_consortium_len is non-zero, this field contains the
 #	Roaming Consortium OI that is required to be advertised by the AP for
 #	the credential to be considered matching.
@@ -320,7 +345,7 @@
 #	password="password"
 #	ca_cert="/etc/wpa_supplicant/ca.pem"
 #	domain="example.com"
-#	roaming_consortium=223344
+#	home_ois="223344"
 #	roaming_consortiums="112233,4455667788,aabbcc"
 #	eap=TTLS
 #	phase2="auth=MSCHAPV2"
diff --git a/wpa_supplicant/aidl/Android.bp b/wpa_supplicant/aidl/Android.bp
index 0785fe1..d7dcf97 100644
--- a/wpa_supplicant/aidl/Android.bp
+++ b/wpa_supplicant/aidl/Android.bp
@@ -33,9 +33,11 @@
     defaults: ["wpa_supplicant_cflags_defaults"],
     soc_specific: true,
     shared_libs: [
-        "android.hardware.wifi.supplicant-V1-ndk",
+        "android.hardware.wifi.supplicant-V2-ndk",
+        "android.system.keystore2-V1-ndk",
         "libbinder_ndk",
         "libbase",
+        "libcrypto",
         "libutils",
         "liblog",
         "libssl",
diff --git a/wpa_supplicant/aidl/aidl.cpp b/wpa_supplicant/aidl/aidl.cpp
index a7945cc..6a22a2a 100644
--- a/wpa_supplicant/aidl/aidl.cpp
+++ b/wpa_supplicant/aidl/aidl.cpp
@@ -186,6 +186,21 @@
 		wpa_s, ssid, rtype, default_txt);
 }
 
+void wpas_aidl_notify_permanent_id_req_denied(
+		struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->aidl)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying permanent_id_req denied to aidl control.");
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	return aidl_manager->notifyPermanentIdReqDenied(wpa_s);
+}
+
 void wpas_aidl_notify_anqp_query_done(
 	struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 	const struct wpa_bss_anqp *anqp)
@@ -502,7 +517,7 @@
 
 void wpas_aidl_notify_p2p_group_started(
 	struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int persistent,
-	int client)
+	int client, const u8 *ip)
 {
 	if (!wpa_s || !ssid)
 		return;
@@ -515,7 +530,7 @@
 	if (!aidl_manager)
 		return;
 
-	aidl_manager->notifyP2pGroupStarted(wpa_s, ssid, persistent, client);
+	aidl_manager->notifyP2pGroupStarted(wpa_s, ssid, persistent, client, ip);
 }
 
 void wpas_aidl_notify_p2p_group_removed(
@@ -671,7 +686,7 @@
 }
 
 void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid)
+		struct wpa_ssid *ssid, bool conn_status_requested)
 {
 	if (!wpa_s || !ssid)
 		return;
@@ -684,7 +699,7 @@
 	if (!aidl_manager)
 		return;
 
-	aidl_manager->notifyDppConfigReceived(wpa_s, ssid);
+	aidl_manager->notifyDppConfigReceived(wpa_s, ssid, conn_status_requested);
 }
 
 void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s)
@@ -692,6 +707,21 @@
 	wpas_aidl_notify_dpp_success(wpa_s, DppEventType::CONFIGURATION_SENT);
 }
 
+void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+                                                 enum dpp_status_error result)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "wpas_aidl_notify_dpp_connection_status_sent %d ", result);
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	aidl_manager->notifyDppConnectionStatusSent(wpa_s, result);
+}
+
 /* DPP Progress notifications */
 void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {
@@ -1033,3 +1063,38 @@
 	aidl_manager->notifyQosPolicyRequest(wpa_s, policies, num_policies);
 }
 
+ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value)
+{
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Requesting certificate from framework");
+
+	return aidl_manager->getCertificate(alias, value);
+}
+
+ssize_t wpas_aidl_list_aliases(const char *prefix, char ***aliases)
+{
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return -1;
+
+	wpa_printf(MSG_INFO, "Requesting aliases from framework");
+
+	return aidl_manager->listAliases(prefix, aliases);
+}
+
+void wpas_aidl_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
+		unsigned int count, int **scs_resp)
+{
+	if (!wpa_s || !count || !scs_resp)
+		return;
+
+	AidlManager *aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying Qos Policy SCS Response");
+	aidl_manager->notifyQosPolicyScsResponse(wpa_s, count, scs_resp);
+}
diff --git a/wpa_supplicant/aidl/aidl.h b/wpa_supplicant/aidl/aidl.h
index e8778c5..f9b374e 100644
--- a/wpa_supplicant/aidl/aidl.h
+++ b/wpa_supplicant/aidl/aidl.h
@@ -37,6 +37,7 @@
 	int wpas_aidl_notify_network_request(
 		struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 		enum wpa_ctrl_req_type rtype, const char *default_txt);
+	void wpas_aidl_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_anqp_query_done(
 		struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 		const struct wpa_bss_anqp *anqp);
@@ -78,7 +79,7 @@
 		struct wpa_supplicant *wpa_s, const char *reason);
 	void wpas_aidl_notify_p2p_group_started(
 		struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid,
-		int persistent, int client);
+		int persistent, int client, const u8 *ip);
 	void wpas_aidl_notify_p2p_group_removed(
 		struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid,
 		const char *role);
@@ -103,8 +104,10 @@
 	void wpas_aidl_notify_eap_error(
 		struct wpa_supplicant *wpa_s, int error_code);
 	void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-			struct wpa_ssid *ssid);
+		struct wpa_ssid *ssid, bool conn_status_requested);
 	void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
+	void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 	void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_dpp_not_compatible(struct wpa_supplicant *wpa_s);
@@ -142,6 +145,10 @@
 	void wpas_aidl_notify_qos_policy_reset(struct wpa_supplicant *wpa_s);
 	void wpas_aidl_notify_qos_policy_request(struct wpa_supplicant *wpa_s,
 		struct dscp_policy_data *policies, int num_policies);
+	ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value);
+	ssize_t wpas_aidl_list_aliases(const char *prefix, char ***aliases);
+	void wpas_aidl_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
+		unsigned int count, int **scs_resp);
 #else   // CONFIG_CTRL_IFACE_AIDL
 static inline int wpas_aidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -171,6 +178,8 @@
 {
 	return 0;
 }
+static void wpas_aidl_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s)
+{}
 static void wpas_aidl_notify_anqp_query_done(
 	struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 	const struct wpa_bss_anqp *anqp)
@@ -222,7 +231,7 @@
 {}
 static void wpas_aidl_notify_p2p_group_started(
 	struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int persistent,
-	int client)
+	int client, const u8 *ip)
 {}
 static void wpas_aidl_notify_p2p_group_removed(
 	struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, const char *role)
@@ -253,12 +262,13 @@
 	struct wpa_supplicant *wpa_s, int error_code)
 {}
 static void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid)
+	struct wpa_ssid *ssid, bool conn_status_requested)
 {}
-static void wpas_aidl_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *ssid);
 static void wpas_aidl_notify_dpp_config_sent(struct wpa_supplicant *wpa_s)
 {}
+static void wpas_aidl_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+	enum dpp_status_error result)
+{}
 static void wpas_aidl_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {}
 static void wpas_aidl_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s)
@@ -321,6 +331,16 @@
 						struct dscp_policy_data *policies,
 						int num_policies)
 {}
+static ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value)
+{
+	return -1;
+}
+static ssize_t wpas_aidl_list_aliases(const char *prefix, char ***aliases)
+{
+	return -1;
+}
+static void wpas_aidl_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
+	unsigned int count, int **scs_resp) {}
 #endif  // CONFIG_CTRL_IFACE_AIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index c115b78..db566ab 100644
--- a/wpa_supplicant/aidl/aidl_manager.cpp
+++ b/wpa_supplicant/aidl/aidl_manager.cpp
@@ -39,6 +39,7 @@
 const std::vector<uint8_t> kZeroBssid = {0, 0, 0, 0, 0, 0};
 
 using aidl::android::hardware::wifi::supplicant::GsmRand;
+using aidl::android::hardware::wifi::supplicant::KeyMgmtMask;
 
 /**
  * Check if the provided |wpa_supplicant| structure represents a P2P iface or
@@ -363,6 +364,12 @@
 	return byteArrToVec(mac_addr, ETH_ALEN);
 }
 
+inline std::array<uint8_t, ETH_ALEN> macAddrToArray(const uint8_t* mac_addr) {
+	std::array<uint8_t, ETH_ALEN> arr;
+	std::copy(mac_addr, mac_addr + ETH_ALEN, std::begin(arr));
+	return arr;
+}
+
 // Raw pointer to the global structure maintained by the core.
 // Declared here to be accessible to onDeath()
 struct wpa_global *wpa_global_;
@@ -400,6 +407,7 @@
 {
 	// Create the main aidl service object and register it.
 	wpa_printf(MSG_INFO, "Starting AIDL supplicant");
+	wpa_printf(MSG_INFO, "Interface version: %d", Supplicant::version);
 	supplicant_object_ = ndk::SharedRefBase::make<Supplicant>(global);
 	wpa_global_ = global;
 	std::string instance = std::string() + Supplicant::descriptor + "/default";
@@ -470,7 +478,7 @@
 		// Enable randomized source MAC address for GAS/ANQP
 		// Set the lifetime to 0, guarantees a unique address for each GAS
 		// session
-		wpa_s->conf->gas_rand_mac_addr = 1;
+		wpa_s->conf->gas_rand_mac_addr = WPAS_MAC_ADDR_STYLE_RANDOM;
 		wpa_s->conf->gas_rand_addr_lifetime = 0;
 	}
 
@@ -638,6 +646,59 @@
 	return 0;
 }
 
+// Some of the undefined AKMs in AIDL (Mostly extension AKMs like FT AKMs)
+// are mapped to the main AKM. This is for the framework to map the AKM to
+// correct security type.
+KeyMgmtMask convertSupplicantSelectedKeyMgmtForConnectionToAidl(int key_mgmt)
+{
+	switch (key_mgmt) {
+		case WPA_KEY_MGMT_IEEE8021X:
+			return KeyMgmtMask::WPA_EAP;
+		case WPA_KEY_MGMT_PSK:
+			return KeyMgmtMask::WPA_PSK;
+		case WPA_KEY_MGMT_NONE:
+			return KeyMgmtMask::NONE;
+		case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+			return KeyMgmtMask::IEEE8021X;
+		case WPA_KEY_MGMT_FT_IEEE8021X:
+			return KeyMgmtMask::FT_EAP;
+		case WPA_KEY_MGMT_FT_PSK:
+			return KeyMgmtMask::FT_PSK;
+		case WPA_KEY_MGMT_IEEE8021X_SHA256:
+			return KeyMgmtMask::WPA_EAP_SHA256;
+		case WPA_KEY_MGMT_PSK_SHA256:
+			return KeyMgmtMask::WPA_PSK_SHA256;
+		case WPA_KEY_MGMT_SAE:
+		case WPA_KEY_MGMT_FT_SAE:
+		case WPA_KEY_MGMT_SAE_EXT_KEY:
+		case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+			return KeyMgmtMask::SAE;
+		case WPA_KEY_MGMT_WAPI_PSK:
+			return KeyMgmtMask::WAPI_PSK;
+		case WPA_KEY_MGMT_WAPI_CERT:
+			return KeyMgmtMask::WAPI_CERT;
+		case WPA_KEY_MGMT_OSEN:
+			return KeyMgmtMask::OSEN;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+		case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+			return KeyMgmtMask::SUITE_B_192;
+		case WPA_KEY_MGMT_FILS_SHA256:
+		case WPA_KEY_MGMT_FT_FILS_SHA256:
+			return KeyMgmtMask::FILS_SHA256;
+		case WPA_KEY_MGMT_FILS_SHA384:
+		case WPA_KEY_MGMT_FT_FILS_SHA384:
+			return KeyMgmtMask::FILS_SHA384;
+		case WPA_KEY_MGMT_OWE:
+			return KeyMgmtMask::OWE;
+		case WPA_KEY_MGMT_DPP:
+			return KeyMgmtMask::DPP;
+		default:
+			wpa_printf(MSG_INFO, "Unable to convert supplicant key_mgmt 0x%x to AIDL",
+				    key_mgmt);
+			return (KeyMgmtMask) key_mgmt;
+	}
+}
+
 /**
  * Notify all listeners about any state changes on a particular interface.
  *
@@ -654,38 +715,53 @@
 		return 1;
 
 	// Invoke the |onStateChanged| method on all registered callbacks.
-	uint32_t aidl_network_id = UINT32_MAX;
-	std::vector<uint8_t> aidl_ssid;
+	SupplicantStateChangeData aidl_state_change_data = {};
+	aidl_state_change_data.id = UINT32_MAX;
+	aidl_state_change_data.newState = static_cast<StaIfaceCallbackState>(wpa_s->wpa_state);
+
 	if (wpa_s->current_ssid) {
-		aidl_network_id = wpa_s->current_ssid->id;
-		aidl_ssid.assign(
+		aidl_state_change_data.id = wpa_s->current_ssid->id;
+		std::vector<uint8_t> aidl_ssid(
 			wpa_s->current_ssid->ssid,
 			wpa_s->current_ssid->ssid + wpa_s->current_ssid->ssid_len);
+		aidl_state_change_data.ssid = aidl_ssid;
+		wpa_printf(MSG_INFO, "assoc key_mgmt 0x%x network key_mgmt 0x%x",
+			wpa_s->key_mgmt, wpa_s->current_ssid->key_mgmt);
 	}
-	std::vector<uint8_t> bssid;
+	std::array<uint8_t, ETH_ALEN> aidl_bssid;
 	// wpa_supplicant sets the |pending_bssid| field when it starts a
 	// connection. Only after association state does it update the |bssid|
 	// field. So, in the AIDL callback send the appropriate bssid.
 	if (wpa_s->wpa_state <= WPA_ASSOCIATED) {
-		bssid = macAddrToVec(wpa_s->pending_bssid);
+		aidl_bssid = macAddrToArray(wpa_s->pending_bssid);
 	} else {
-		bssid = macAddrToVec(wpa_s->bssid);
+		aidl_bssid = macAddrToArray(wpa_s->bssid);
 	}
-	bool fils_hlp_sent =
+	aidl_state_change_data.bssid = aidl_bssid;
+
+	aidl_state_change_data.filsHlpSent =
 		(wpa_auth_alg_fils(wpa_s->auth_alg) &&
 		 !dl_list_empty(&wpa_s->fils_hlp_req) &&
 		 (wpa_s->wpa_state == WPA_COMPLETED)) ? true : false;
+	if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+		// wpa_supplicant sets the frequency on receiving the EVENT_ASSOC.
+		aidl_state_change_data.frequencyMhz = wpa_s->assoc_freq;
+		// The key_mgmt is selected prior to sending the connect command
+		// to driver. But in case of CROSS-AKM Connection/Roaming, the
+		// key_mgmt is updated with the one from association IE. So the
+		// selected key_mgmt is accurate only after moving to
+		// associated state.
+		aidl_state_change_data.keyMgmtMask =
+			convertSupplicantSelectedKeyMgmtForConnectionToAidl(wpa_s->key_mgmt);
+	}
 
 	// Invoke the |onStateChanged| method on all registered callbacks.
 	std::function<
 		ndk::ScopedAStatus(std::shared_ptr<ISupplicantStaIfaceCallback>)>
 		func = std::bind(
-			&ISupplicantStaIfaceCallback::onStateChanged,
+			&ISupplicantStaIfaceCallback::onSupplicantStateChanged,
 			std::placeholders::_1,
-			static_cast<StaIfaceCallbackState>(
-				wpa_s->wpa_state),
-				bssid, aidl_network_id, aidl_ssid,
-				fils_hlp_sent);
+			aidl_state_change_data);
 	callWithEachStaIfaceCallback(
 		misc_utils::charBufToString(wpa_s->ifname), func);
 	return 0;
@@ -759,6 +835,30 @@
 
 #ifdef CONFIG_INTERWORKING
 /**
+ * Notify that the AT_PERMANENT_ID_REQ is denied from eap_peer when the strict
+ * conservative peer mode is enabled.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+*/
+void AidlManager::notifyPermanentIdReqDenied(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->current_ssid) {
+		wpa_printf(MSG_ERROR, "Current network NULL. Drop permanent_id_req_denied event!");
+		return;
+	}
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+	callWithEachStaNetworkCallback(
+			misc_utils::charBufToString(wpa_s->ifname),
+			current_ssid->id,
+			std::bind(
+			&ISupplicantStaNetworkCallback::
+				onPermanentIdReqDenied,
+			std::placeholders::_1));
+}
+
+/**
  * Notify all listeners about the end of an ANQP query.
  *
  * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
@@ -1332,7 +1432,7 @@
 
 void AidlManager::notifyP2pGroupStarted(
 	struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
-	int persistent, int client)
+	int persistent, int client, const u8 *ip)
 {
 	if (!wpa_group_s || !wpa_group_s->parent || !ssid)
 		return;
@@ -1362,14 +1462,32 @@
 		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 	}
 
+	P2pGroupStartedEventParams params;
+	params.groupInterfaceName = misc_utils::charBufToString(wpa_group_s->ifname);
+	params.isGroupOwner = aidl_is_go;
+	params.ssid = byteArrToVec(ssid->ssid, ssid->ssid_len);
+	params.frequencyMHz = aidl_freq;
+	params.psk = aidl_psk;
+	params.passphrase = misc_utils::charBufToString(ssid->passphrase);
+	params.isPersistent = aidl_is_persistent;
+	params.goDeviceAddress = macAddrToArray(wpa_group_s->go_dev_addr);
+	params.goInterfaceAddress = aidl_is_go ? macAddrToArray(wpa_group_s->own_addr) :
+			macAddrToArray(wpa_group_s->current_bss->bssid);
+	if (NULL != ip && !aidl_is_go) {
+		params.isP2pClientEapolIpAddressInfoPresent = true;
+		os_memcpy(&params.p2pClientIpInfo.ipAddressClient, &ip[0], 4);
+		os_memcpy(&params.p2pClientIpInfo.ipAddressMask, &ip[4], 4);
+		os_memcpy(&params.p2pClientIpInfo.ipAddressGo, &ip[8], 4);
+
+		wpa_printf(MSG_DEBUG, "P2P: IP Address allocated - CLI: 0x%x MASK: 0x%x GO: 0x%x",
+			   params.p2pClientIpInfo.ipAddressClient,
+			   params.p2pClientIpInfo.ipAddressMask,
+			   params.p2pClientIpInfo.ipAddressGo);
+        }
 	callWithEachP2pIfaceCallback(
 		misc_utils::charBufToString(wpa_s->ifname),
-		std::bind(
-		&ISupplicantP2pIfaceCallback::onGroupStarted,
-		std::placeholders::_1, misc_utils::charBufToString(wpa_group_s->ifname),
-		aidl_is_go, byteArrToVec(ssid->ssid, ssid->ssid_len),
-		aidl_freq, aidl_psk, misc_utils::charBufToString(ssid->passphrase),
-		macAddrToVec(wpa_group_s->go_dev_addr), aidl_is_persistent));
+		std::bind(&ISupplicantP2pIfaceCallback::onGroupStartedWithParams,
+		std::placeholders::_1, params));
 }
 
 void AidlManager::notifyP2pGroupRemoved(
@@ -1571,19 +1689,18 @@
  * @param config Configuration object
  */
 void AidlManager::notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
-		struct wpa_ssid *config)
+		struct wpa_ssid *config, bool conn_status_requested)
 {
-	DppAkm securityAkm;
-	DppConnectionKeys aidl_keys{};
 	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	DppConfigurationData aidl_dpp_config_data = {};
 
 	if ((config->key_mgmt & WPA_KEY_MGMT_SAE) &&
 			(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
-		securityAkm = DppAkm::SAE;
+		aidl_dpp_config_data.securityAkm = DppAkm::SAE;
 	} else if (config->key_mgmt & WPA_KEY_MGMT_PSK) {
-			securityAkm = DppAkm::PSK;
+		aidl_dpp_config_data.securityAkm = DppAkm::PSK;
 	} else if (config->key_mgmt & WPA_KEY_MGMT_DPP) {
-			securityAkm = DppAkm::DPP;
+		aidl_dpp_config_data.securityAkm = DppAkm::DPP;
 	} else {
 		/* Unsupported AKM */
 		wpa_printf(MSG_ERROR, "DPP: Error: Unsupported AKM 0x%X",
@@ -1592,29 +1709,31 @@
 		return;
 	}
 
-	std::string passphrase = misc_utils::charBufToString(config->passphrase);
+	aidl_dpp_config_data.password = misc_utils::charBufToString(config->passphrase);
+	aidl_dpp_config_data.psk = byteArrToVec(config->psk, 32);
 	std::vector<uint8_t> aidl_ssid(
 		config->ssid,
 		config->ssid + config->ssid_len);
+	aidl_dpp_config_data.ssid = aidl_ssid;
 
-	if (securityAkm == DppAkm::DPP) {
+	if (aidl_dpp_config_data.securityAkm == DppAkm::DPP) {
 		std::string connector_str = misc_utils::charBufToString(config->dpp_connector);
-		aidl_keys.connector = std::vector<uint8_t>(connector_str.begin(),
-			connector_str.end());
-		aidl_keys.cSign = byteArrToVec(config->dpp_csign, config->dpp_csign_len);
-		aidl_keys.netAccessKey = byteArrToVec(config->dpp_netaccesskey,
-			config->dpp_netaccesskey_len);
+		aidl_dpp_config_data.dppConnectionKeys.connector
+			= std::vector<uint8_t>(connector_str.begin(), connector_str.end());
+		aidl_dpp_config_data.dppConnectionKeys.cSign
+			= byteArrToVec(config->dpp_csign, config->dpp_csign_len);
+		aidl_dpp_config_data.dppConnectionKeys.netAccessKey
+			= byteArrToVec(config->dpp_netaccesskey, config->dpp_netaccesskey_len);
 	}
+	aidl_dpp_config_data.connStatusRequested = conn_status_requested;
 
 	/* At this point, the network is already registered, notify about new
 	 * received configuration
 	 */
 	callWithEachStaIfaceCallback(aidl_ifname,
 			std::bind(
-					&ISupplicantStaIfaceCallback::onDppSuccessConfigReceived,
-					std::placeholders::_1, aidl_ssid, passphrase,
-					byteArrToVec(config->psk, 32), securityAkm,
-					aidl_keys));
+					&ISupplicantStaIfaceCallback::onDppConfigReceived,
+					std::placeholders::_1, aidl_dpp_config_data));
 }
 
 /**
@@ -1631,6 +1750,55 @@
 					std::placeholders::_1));
 }
 
+DppStatusErrorCode convertSupplicantDppStatusErrorCodeToAidl(
+	enum dpp_status_error code)
+{
+	switch (code) {
+		case DPP_STATUS_OK:
+			return DppStatusErrorCode::SUCCESS;
+		case DPP_STATUS_NOT_COMPATIBLE:
+			return DppStatusErrorCode::NOT_COMPATIBLE;
+		case DPP_STATUS_AUTH_FAILURE:
+			return DppStatusErrorCode::AUTH_FAILURE;
+		case DPP_STATUS_UNWRAP_FAILURE:
+			return DppStatusErrorCode::UNWRAP_FAILURE;
+		case DPP_STATUS_BAD_GROUP:
+			return DppStatusErrorCode::BAD_GROUP;
+		case DPP_STATUS_CONFIGURE_FAILURE:
+			return DppStatusErrorCode::CONFIGURE_FAILURE;
+		case DPP_STATUS_RESPONSE_PENDING:
+			return DppStatusErrorCode::RESPONSE_PENDING;
+		case DPP_STATUS_INVALID_CONNECTOR:
+			return DppStatusErrorCode::INVALID_CONNECTOR;
+		case DPP_STATUS_CONFIG_REJECTED:
+			return DppStatusErrorCode::CONFIG_REJECTED;
+		case DPP_STATUS_NO_MATCH:
+			return DppStatusErrorCode::NO_MATCH;
+		case DPP_STATUS_NO_AP:
+			return DppStatusErrorCode::NO_AP;
+		case DPP_STATUS_CONFIGURE_PENDING:
+			return DppStatusErrorCode::CONFIGURE_PENDING;
+		case DPP_STATUS_CSR_NEEDED:
+			return DppStatusErrorCode::CSR_NEEDED;
+		case DPP_STATUS_CSR_BAD:
+			return DppStatusErrorCode::CSR_BAD;
+		case DPP_STATUS_NEW_KEY_NEEDED:
+			return DppStatusErrorCode::NEW_KEY_NEEDED;
+		default:
+			return DppStatusErrorCode::UNKNOWN;
+	}
+}
+
+void AidlManager::notifyDppConnectionStatusSent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result)
+{
+	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	callWithEachStaIfaceCallback(aidl_ifname,
+			std::bind(&ISupplicantStaIfaceCallback::onDppConnectionStatusResultSent,
+					std::placeholders::_1,
+					convertSupplicantDppStatusErrorCodeToAidl(result)));
+}
+
 /**
  * Notify listener about a DPP failure event
  *
@@ -1701,18 +1869,22 @@
 {
 	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
 
+	PmkSaCacheData aidl_pmksa_data = {};
+	aidl_pmksa_data.bssid = macAddrToArray(pmksa_entry->aa);
 	// Serialize PmkCacheEntry into blob.
 	std::stringstream ss(
 		std::stringstream::in | std::stringstream::out | std::stringstream::binary);
 	misc_utils::serializePmkCacheEntry(ss, pmksa_entry);
 	std::vector<uint8_t> serializedEntry(
 		std::istreambuf_iterator<char>(ss), {});
+	aidl_pmksa_data.serializedEntry = serializedEntry;
+	aidl_pmksa_data.expirationTimeInSec = pmksa_entry->expiration;
 
 	const std::function<
 		ndk::ScopedAStatus(std::shared_ptr<ISupplicantStaIfaceCallback>)>
 		func = std::bind(
-		&ISupplicantStaIfaceCallback::onPmkCacheAdded,
-		std::placeholders::_1, pmksa_entry->expiration, serializedEntry);
+		&ISupplicantStaIfaceCallback::onPmkSaCacheAdded,
+		std::placeholders::_1, aidl_pmksa_data);
 	callWithEachStaIfaceCallback(aidl_ifname, func);
 }
 
@@ -1918,24 +2090,33 @@
 	callWithEachStaIfaceCallback(misc_utils::charBufToString(wpa_s->ifname), func);
 }
 
-void AidlManager::notifyFrequencyChanged(struct wpa_supplicant *wpa_group_s, int frequency)
+void AidlManager::notifyFrequencyChanged(struct wpa_supplicant *wpa_s, int frequency)
 {
-	if (!wpa_group_s || !wpa_group_s->parent)
+	if (!wpa_s)
 		return;
 
-	// For group notifications, need to use the parent iface for callbacks.
-	struct wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
-	if (!wpa_s) {
+	std::string aidl_ifname = misc_utils::charBufToString(wpa_s->ifname);
+	struct wpa_supplicant *wpa_p2pdev_s = getTargetP2pIfaceForGroup(wpa_s);
+	if (wpa_p2pdev_s) {
+		// Notify frequency changed event on P2P interface
+		const std::function<
+			ndk::ScopedAStatus(std::shared_ptr<ISupplicantP2pIfaceCallback>)>
+			func = std::bind(&ISupplicantP2pIfaceCallback::onGroupFrequencyChanged,
+			std::placeholders::_1, aidl_ifname, frequency);
+		// For group notifications, need to use the parent iface for callbacks.
+		callWithEachP2pIfaceCallback(misc_utils::charBufToString(wpa_p2pdev_s->ifname), func);
+	} else if (wpa_s->current_ssid) {
+		// Notify frequency changed event on STA interface
+		const std::function<
+			ndk::ScopedAStatus(std::shared_ptr<ISupplicantStaIfaceCallback>)>
+			func = std::bind(
+			&ISupplicantStaIfaceCallback::onBssFrequencyChanged,
+			std::placeholders::_1, frequency);
+		callWithEachStaIfaceCallback(aidl_ifname, func);
+	} else {
 		wpa_printf(MSG_INFO, "Drop frequency changed event");
 		return;
-	}
-
-	const std::function<
-		ndk::ScopedAStatus(std::shared_ptr<ISupplicantP2pIfaceCallback>)>
-		func = std::bind(&ISupplicantP2pIfaceCallback::onGroupFrequencyChanged,
-		std::placeholders::_1, misc_utils::charBufToString(wpa_group_s->ifname),
-		frequency);
-	callWithEachP2pIfaceCallback(misc_utils::charBufToString(wpa_s->ifname), func);
+        }
 }
 
 void AidlManager::notifyCertification(struct wpa_supplicant *wpa_s,
@@ -1950,17 +2131,15 @@
 		return;
 	}
 	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+	if (!wpa_key_mgmt_wpa_ieee8021x(current_ssid->key_mgmt)) {
+		return;
+	}
 	if (NULL == subject || NULL == cert_hash || NULL == cert) {
 		wpa_printf(MSG_ERROR,
 				"Incomplete certificate information. Drop Certification event!");
 		return;
 	}
-	if (!wpa_key_mgmt_wpa_ieee8021x(current_ssid->key_mgmt)) {
-		wpa_printf(MSG_ERROR, "Not 802.1x configuration, Drop Certification event!");
-		return;
-	}
-	if (current_ssid->eap.cert.ca_path || current_ssid->eap.cert.ca_cert) {
-		wpa_printf(MSG_DEBUG, "Already has CA certificate. Drop Certification event!");
+	if (current_ssid->eap.cert.ca_cert) {
 		return;
 	}
 
@@ -2121,6 +2300,21 @@
 }
 
 /**
+ * Store the |INonStandardCertCallback| aidl object reference.
+ *
+ * @param callback Aidl reference of the |INonStandardCertCallback| object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int AidlManager::registerNonStandardCertCallbackAidlObject(
+	const std::shared_ptr<INonStandardCertCallback> &callback)
+{
+	if (callback == nullptr) return 1;
+	non_standard_cert_callback_ = callback;
+	return 0;
+}
+
+/**
  * Add a new iface callback aidl object reference to our
  * interface callback list.
  *
@@ -2472,6 +2666,113 @@
 			std::placeholders::_1, wpa_s->dscp_req_dialog_token, qosPolicyData));
 }
 
+ssize_t AidlManager::getCertificate(const char* alias, uint8_t** value) {
+	if (alias == nullptr || value == nullptr) {
+		wpa_printf(MSG_ERROR, "Null pointer argument was passed to getCertificate");
+		return -1;
+	}
+	if (auto cert = certificate_utils::getCertificate(alias, non_standard_cert_callback_)) {
+		*value = (uint8_t *) os_malloc(cert->size());
+		if (*value == nullptr) return -1;
+		os_memcpy(*value, cert->data(), cert->size());
+		return cert->size();
+	}
+	return -1;
+}
+
+ssize_t AidlManager::listAliases(const char *prefix, char ***aliases) {
+	if (prefix == nullptr || aliases == nullptr) {
+		wpa_printf(MSG_ERROR, "Null pointer argument was passed to listAliases");
+		return -1;
+	}
+
+	if (auto results =
+			certificate_utils::listAliases(prefix, non_standard_cert_callback_)) {
+		int count = results->size();
+		*aliases = (char **) os_malloc(sizeof(char *) * count);
+		if (*aliases == nullptr) {
+			wpa_printf(MSG_ERROR, "listAliases: os_malloc alias array error");
+			return -1;
+		}
+		os_memset(*aliases, 0, sizeof(char *) * count);
+
+		int index = 0;
+		for (auto it = results->begin(); it != results->end(); ++it) {
+			int alias_len = it->length();
+			char *alias = (char *) os_malloc(alias_len + 1);
+			if (alias == nullptr) {
+				wpa_printf(MSG_ERROR, "listAliases: os_malloc alias string error");
+				for (int i = 0; i < index; ++i) os_free((*aliases)[i]);
+				os_free(*aliases);
+				return -1;
+			}
+			os_memcpy(alias, it->data(), alias_len + 1);
+			(*aliases)[index] = alias;
+			index++;
+		}
+		return count;
+	}
+	return -1;
+}
+
+QosPolicyScsResponseStatusCode getQosPolicyScsResponseStatusCode(int scsResponseCode)
+{
+	QosPolicyScsResponseStatusCode status = QosPolicyScsResponseStatusCode::TIMEOUT;
+	/* Status code as per Ieee802.11-2020 Table 9-50—Status codes */
+	switch (scsResponseCode) {
+		case 0: /* SUCCESS */
+			status = QosPolicyScsResponseStatusCode::SUCCESS;
+			break;
+		case 37: /* REQUEST_DECLINED */
+			status = QosPolicyScsResponseStatusCode::TCLAS_REQUEST_DECLINED;
+			break;
+		case 56: /* REQUESTED_TCLAS_NOT_SUPPORTED */
+		case 80: /* REQUESTED_TCLAS_NOT_SUPPORTED */
+			status = QosPolicyScsResponseStatusCode::TCLAS_NOT_SUPPORTED_BY_AP;
+			break;
+		case 57: /* INSUFFICIENT_TCLAS_PROCESSING_RESOURCES */
+			status = QosPolicyScsResponseStatusCode::TCLAS_INSUFFICIENT_RESOURCES;
+			break;
+		case 81: /* TCLAS_RESOURCES_EXHAUSTED */
+			status = QosPolicyScsResponseStatusCode::TCLAS_RESOURCES_EXHAUSTED;
+			break;
+		case 128: /* TCLAS_PROCESSING_TERMINATED_INSUFFICIENT_QOS */
+			status = QosPolicyScsResponseStatusCode::TCLAS_PROCESSING_TERMINATED_INSUFFICIENT_QOS;
+			break;
+		case 129: /* TCLAS_PROCESSING_TERMINATED_POLICY_CONFLICT */
+			status = QosPolicyScsResponseStatusCode::TCLAS_PROCESSING_TERMINATED_POLICY_CONFLICT;
+			break;
+		case 97: /* TCLAS_PROCESSING_TERMINATED */
+			status = QosPolicyScsResponseStatusCode::TCLAS_PROCESSING_TERMINATED;
+			break;
+		default:
+			status = QosPolicyScsResponseStatusCode::TIMEOUT;
+			break;
+		return status;
+	}
+	return status;
+}
+
+void AidlManager::notifyQosPolicyScsResponse(struct wpa_supplicant *wpa_s,
+		unsigned int count, int **scs_resp)
+{
+	if (!wpa_s || !count || !scs_resp)
+		return;
+
+	std::vector<QosPolicyScsResponseStatus> scsResponses;
+
+	for (int i = 0; i < count; i++) {
+		QosPolicyScsResponseStatus resp;
+		resp.policyId = scs_resp[0][i] & 0xFF;
+		resp.qosPolicyScsResponseStatusCode = getQosPolicyScsResponseStatusCode(scs_resp[1][i]);
+		scsResponses.push_back(resp);
+	}
+	callWithEachStaIfaceCallback(
+		misc_utils::charBufToString(wpa_s->ifname), std::bind(
+			&ISupplicantStaIfaceCallback::onQosPolicyResponseForScs,
+			std::placeholders::_1, scsResponses));
+}
+
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index 1ed6899..fcea1f7 100644
--- a/wpa_supplicant/aidl/aidl_manager.h
+++ b/wpa_supplicant/aidl/aidl_manager.h
@@ -16,6 +16,7 @@
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantStaIfaceCallback.h>
 #include <aidl/android/hardware/wifi/supplicant/ISupplicantStaNetworkCallback.h>
 
+#include "certificate_utils.h"
 #include "p2p_iface.h"
 #include "p2p_network.h"
 #include "rsn_supp/pmksa_cache.h"
@@ -61,6 +62,8 @@
 	int notifyNetworkRequest(
 		struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int type,
 		const char *param);
+	void notifyPermanentIdReqDenied(
+		struct wpa_supplicant *wpa_s);
 	void notifyAnqpQueryDone(
 		struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
 		const struct wpa_bss_anqp *anqp);
@@ -101,7 +104,7 @@
 		struct wpa_supplicant *wpa_s, const char *reason);
 	void notifyP2pGroupStarted(
 		struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
-		int persistent, int client);
+		int persistent, int client, const u8 *ip);
 	void notifyP2pGroupRemoved(
 		struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
 		const char *role);
@@ -125,8 +128,11 @@
 		const u8 *p2p_dev_addr);
 	void notifyEapError(struct wpa_supplicant *wpa_s, int error_code);
 	void notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
-			struct wpa_ssid *config);
+		struct wpa_ssid *config,
+		bool conn_status_requested);
 	void notifyDppConfigSent(struct wpa_supplicant *wpa_s);
+	void notifyDppConnectionStatusSent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 	void notifyDppSuccess(struct wpa_supplicant *wpa_s, DppEventType code);
 	void notifyDppFailure(struct wpa_supplicant *wpa_s,
 			DppFailureCode code);
@@ -157,6 +163,10 @@
 	void notifyQosPolicyRequest(struct wpa_supplicant *wpa_s,
 			struct dscp_policy_data *policies,
 			int num_policies);
+	ssize_t getCertificate(const char* alias, uint8_t** value);
+	ssize_t listAliases(const char *prefix, char ***aliases);
+	void notifyQosPolicyScsResponse(struct wpa_supplicant *wpa_s,
+			unsigned int count, int **scs_resp);
 
 	// Methods called from aidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -186,6 +196,8 @@
 	int addStaNetworkCallbackAidlObject(
 		const std::string &ifname, int network_id,
 		const std::shared_ptr<ISupplicantStaNetworkCallback> &callback);
+	int registerNonStandardCertCallbackAidlObject(
+		const std::shared_ptr<INonStandardCertCallback> &callback);
 
 private:
 	AidlManager() = default;
@@ -273,6 +285,8 @@
 		const std::string,
 		std::vector<std::shared_ptr<ISupplicantStaNetworkCallback>>>
 		sta_network_callbacks_map_;
+	// NonStandardCertCallback registered by the client.
+	std::shared_ptr<INonStandardCertCallback> non_standard_cert_callback_;
 };
 
 // The aidl interface uses some values which are the same as internal ones to
diff --git a/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml b/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
index 3dc9b02..b80dadd 100644
--- a/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
+++ b/wpa_supplicant/aidl/android.hardware.wifi.supplicant.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="device">
 	<hal format="aidl">
 		<name>android.hardware.wifi.supplicant</name>
+		<version>2</version>
 		<fqname>ISupplicant/default</fqname>
 	</hal>
 </manifest>
diff --git a/wpa_supplicant/aidl/certificate_utils.cpp b/wpa_supplicant/aidl/certificate_utils.cpp
new file mode 100644
index 0000000..8750cea
--- /dev/null
+++ b/wpa_supplicant/aidl/certificate_utils.cpp
@@ -0,0 +1,211 @@
+/*
+ * WPA Supplicant - Certificate utils
+ * Copyright (c) 2022, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "certificate_utils.h"
+
+#define AT __func__ << ":" << __LINE__ << " "
+
+namespace ks2 = aidl::android::system::keystore2;
+namespace KMV1 = aidl::android::hardware::security::keymint;
+
+using aidl::android::hardware::wifi::supplicant::INonStandardCertCallback;
+
+namespace {
+
+constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
+
+constexpr const char kKeystore2ServiceName[] = "android.system.keystore2.IKeystoreService/default";
+
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
+ks2::KeyDescriptor mkKeyDescriptor(const std::string& alias) {
+	// If the key_id starts with the grant id prefix, we parse the following string as numeric
+	// grant id. We can then use the grant domain without alias to load the designated key.
+	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
+		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+		uint64_t tmp;
+		s >> std::hex >> tmp;
+		if (s.fail() || !s.eof()) {
+			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
+		}
+		return {
+			.domain = ks2::Domain::GRANT,
+			.nspace = static_cast<int64_t>(tmp),
+			.alias = std::nullopt,
+			.blob = std::nullopt,
+		};
+	} else {
+		return {
+			.domain = ks2::Domain::SELINUX,
+			.nspace = KS2_NAMESPACE_WIFI,
+			.alias = alias,
+			.blob = std::nullopt,
+		};
+	}
+}
+
+// Helper method to convert certs in DER format to PEM format required by
+// openssl library used by supplicant. If boringssl cannot parse the input as one or more
+// X509 certificates in DER encoding, this function returns the input as-is. The assumption in
+// that case is that either the `cert_bytes` is already PEM encoded, or `cert_bytes` is something
+// completely different that was intentionally installed by the Wi-Fi subsystem and it must not
+// be changed here.
+// If any error occurs during PEM encoding, this function returns std::nullopt and logs an error.
+std::optional<std::vector<uint8_t>> convertDerCertToPemOrPassthrough(
+	const std::vector<uint8_t>& cert_bytes) {
+	// If cert_bytes is a DER encoded X509 certificate, it must be reencoded as PEM, because
+	// wpa_supplicant only understand PEM. Otherwise the cert_bytes are returned as is.
+	const uint8_t* cert_current = cert_bytes.data();
+	const uint8_t* cert_end = cert_current + cert_bytes.size();
+	bssl::UniquePtr<BIO> pem_bio(BIO_new(BIO_s_mem()));
+	while (cert_current < cert_end) {
+		auto cert =
+			bssl::UniquePtr<X509>(d2i_X509(nullptr, &cert_current, cert_end - cert_current));
+		// If part of the bytes cannot be parsed as X509 DER certificate, the original blob
+		// shall be returned as-is.
+		if (!cert) {
+			wpa_printf(MSG_WARNING, "Could not parse DER X509 cert from buffer. Returning blob as is.");
+			return cert_bytes;
+		}
+
+		if (!PEM_write_bio_X509(pem_bio.get(), cert.get())) {
+			wpa_printf(MSG_ERROR, "Could not convert cert to PEM format.");
+			return std::nullopt;
+		}
+	}
+
+	const uint8_t* pem_bytes;
+	size_t pem_len;
+	if (!BIO_mem_contents(pem_bio.get(), &pem_bytes, &pem_len)) {
+		wpa_printf(MSG_ERROR, "Could not extract pem_bytes from BIO.");
+		return std::nullopt;
+	}
+	return {{pem_bytes, pem_bytes + pem_len}};
+}
+
+std::optional<std::vector<uint8_t>> getKeystore2Cert(const std::string& key) {
+	::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(kKeystore2ServiceName));
+	auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
+
+	if (!keystore2) {
+		wpa_printf(MSG_WARNING, "Unable to connect to Keystore 2.");
+		return {};
+	}
+
+	bool ca_cert = false;
+	std::string alias = key.c_str();
+	if (::android::base::StartsWith(alias, "CACERT_")) {
+		alias = alias.substr(7);
+		ca_cert = true;
+	} else if (::android::base::StartsWith(alias, "USRCERT_")) {
+		alias = alias.substr(8);
+	}
+
+	ks2::KeyDescriptor descriptor = mkKeyDescriptor(alias);
+
+	// If the key_id starts with the grant id prefix, we parse the following string as numeric
+	// grant id. We can then use the grant domain without alias to load the designated key.
+	if (::android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
+		std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+		uint64_t tmp;
+		s >> std::hex >> tmp;
+		if (s.fail() || !s.eof()) {
+			wpa_printf(MSG_ERROR, "Couldn't parse grant name: %s", alias.c_str());
+		}
+		descriptor.nspace = static_cast<int64_t>(tmp);
+		descriptor.domain = ks2::Domain::GRANT;
+		descriptor.alias = std::nullopt;
+	}
+
+	ks2::KeyEntryResponse response;
+	auto rc = keystore2->getKeyEntry(descriptor, &response);
+	if (!rc.isOk()) {
+		if (rc.getServiceSpecificError() != int32_t(ks2::ResponseCode::KEY_NOT_FOUND)) {
+			wpa_printf(MSG_WARNING, "Entry not found in Keystore 2.");
+		} else {
+			wpa_printf(MSG_WARNING, "Keystore 2 getKeyEntry failed error: %s", rc.getDescription().c_str());
+		}
+		return {};
+	}
+
+	if (ca_cert && response.metadata.certificateChain) {
+		return std::move(*response.metadata.certificateChain);
+	} else if (!ca_cert && response.metadata.certificate) {
+		return std::move(*response.metadata.certificate);
+	} else {
+		wpa_printf(MSG_WARNING, "No %s certificate found.", (ca_cert ? "CA" : "client"));
+		return {};
+	}
+}
+
+std::optional<std::vector<uint8_t>> getNonStandardCert(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
+	if (non_standard_callback == nullptr) {
+		wpa_printf(MSG_ERROR, "Non-standard cert callback is not available");
+		return std::nullopt;
+	}
+	std::vector<uint8_t> blob;
+	const auto& status = non_standard_callback->getBlob(alias, &blob);
+	if (!status.isOk()) {
+		wpa_printf(MSG_ERROR, "Cert callback error, code=%d",
+			status.getServiceSpecificError());
+		return std::nullopt;
+	}
+	return blob;
+}
+
+}  // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace certificate_utils {
+
+std::optional<std::vector<uint8_t>> getCertificate(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
+	std::vector<uint8_t> cert;
+	if (auto ks2_cert = getKeystore2Cert(alias)) {
+		cert = std::move(*ks2_cert);
+	} else if (auto blob = getNonStandardCert(alias, non_standard_callback)) {
+		cert = std::move(*blob);
+	} else {
+		wpa_printf(MSG_ERROR, "Failed to get certificate.");
+		return std::nullopt;
+	}
+
+	if (auto result_cert = convertDerCertToPemOrPassthrough(cert)) {
+		return result_cert;
+	} else {
+		wpa_printf(MSG_ERROR, "Conversion to PEM failed.");
+		return std::nullopt;
+	}
+}
+
+std::optional<std::vector<std::string>> listAliases(const std::string& prefix,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback) {
+	if (non_standard_callback == nullptr) {
+		wpa_printf(MSG_ERROR, "Non-standard cert callback is not available");
+		return std::nullopt;
+	}
+	std::vector<std::string> aliases;
+	const auto& status = non_standard_callback->listAliases(prefix, &aliases);
+	if (!status.isOk()) {
+		wpa_printf(MSG_ERROR, "Unable to retrieve aliases");
+		return std::nullopt;
+	}
+	return aliases;
+}
+
+}  // namespace certificate_utils
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/wpa_supplicant/aidl/certificate_utils.h b/wpa_supplicant/aidl/certificate_utils.h
new file mode 100644
index 0000000..adbafc8
--- /dev/null
+++ b/wpa_supplicant/aidl/certificate_utils.h
@@ -0,0 +1,42 @@
+/*
+ * WPA Supplicant - Certificate utils
+ * Copyright (c) 2022, Google Inc. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/wifi/supplicant/INonStandardCertCallback.h>
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <openssl/base.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <vector>
+
+extern "C"
+{
+#include "utils/common.h"
+}
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace certificate_utils {
+	std::optional<std::vector<uint8_t>> getCertificate(const std::string& alias,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback);
+	std::optional<std::vector<std::string>> listAliases(const std::string& prefix,
+		const std::shared_ptr<INonStandardCertCallback> &non_standard_callback);
+}  // namespace certificate_utils
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/wpa_supplicant/aidl/misc_utils.h b/wpa_supplicant/aidl/misc_utils.h
index 5c5b68c..d0330e0 100644
--- a/wpa_supplicant/aidl/misc_utils.h
+++ b/wpa_supplicant/aidl/misc_utils.h
@@ -88,6 +88,7 @@
 	ss.write((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
 	ss.write((char *) pmksa_entry->pmkid, PMKID_LEN);
 	ss.write((char *) pmksa_entry->aa, ETH_ALEN);
+	ss.write((char *) pmksa_entry->spa, ETH_ALEN);
 	// Omit wpa_ssid field because the network is created on connecting to a access point.
 	ss.write((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
 	ss.write((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
@@ -100,13 +101,25 @@
 	return ss;
 }
 
-inline std::stringstream& deserializePmkCacheEntry(
+inline std::int8_t deserializePmkCacheEntry(
 	std::stringstream &ss, struct rsn_pmksa_cache_entry *pmksa_entry) {
 	ss.seekg(0);
+	if (ss.str().size() < sizeof(pmksa_entry->pmk_len)) {
+		return -1;
+	}
+
 	ss.read((char *) &pmksa_entry->pmk_len, sizeof(pmksa_entry->pmk_len));
+	if ((pmksa_entry->pmk_len > PMK_LEN_MAX) ||
+	    (ss.str().size() < (sizeof(pmksa_entry->pmk_len) + pmksa_entry->pmk_len +
+	    PMKID_LEN + ETH_ALEN + ETH_ALEN + sizeof(pmksa_entry->akmp) +
+	    sizeof(pmksa_entry->reauth_time) + sizeof(pmksa_entry->expiration) +
+	    sizeof(pmksa_entry->opportunistic) + 1 /* fils_cache_id_set */)))
+		return -1;
+
 	ss.read((char *) pmksa_entry->pmk, pmksa_entry->pmk_len);
 	ss.read((char *) pmksa_entry->pmkid, PMKID_LEN);
 	ss.read((char *) pmksa_entry->aa, ETH_ALEN);
+	ss.read((char *) pmksa_entry->spa, ETH_ALEN);
 	// Omit wpa_ssid field because the network is created on connecting to a access point.
 	ss.read((char *) &pmksa_entry->akmp, sizeof(pmksa_entry->akmp));
 	ss.read((char *) &pmksa_entry->reauth_time, sizeof(pmksa_entry->reauth_time));
@@ -115,8 +128,13 @@
 	char byte = 0;
 	ss.read((char *) &byte, sizeof(byte));
 	pmksa_entry->fils_cache_id_set = (byte) ? 1 : 0;
+	if (pmksa_entry->fils_cache_id_set == 1) {
+		if((ss.str().size() - static_cast<uint32_t>(ss.tellg())) < FILS_CACHE_ID_LEN)
+			return -1;
+	}
+
 	ss.read((char *) pmksa_entry->fils_cache_id, FILS_CACHE_ID_LEN);
-	return ss;
+	return 0;
 }
 }  // namespace misc_utils
 }  // namespace supplicant
diff --git a/wpa_supplicant/aidl/p2p_iface.cpp b/wpa_supplicant/aidl/p2p_iface.cpp
index adfd0dd..33db802 100644
--- a/wpa_supplicant/aidl/p2p_iface.cpp
+++ b/wpa_supplicant/aidl/p2p_iface.cpp
@@ -23,13 +23,7 @@
 #include "driver_i.h"
 }
 
-#define P2P_MAX_JOIN_SCAN_ATTEMPTS 3
-// Wait time before triggering the single channel scan to discover Auto GO.
-// Use a shorter wait time when the given frequency is GO operating frequency.
-// The idea is to quickly finish scans and return the status to application.
-#define P2P_JOIN_SINGLE_CHANNEL_SCAN_INTERVAL_USECS 200000
-// Wait time before triggering the multiple channel scan to discover Auto GO.
-#define P2P_JOIN_MULTIPLE_CHANNEL_SCAN_INTERVAL_USECS 1000000
+#define P2P_JOIN_LIMIT 3
 
 namespace {
 const char kConfigMethodStrPbc[] = "pbc";
@@ -99,138 +93,6 @@
 	return 1;
 }
 
-static int setBandScanFreqsList(
-	struct wpa_supplicant *wpa_s,
-	enum hostapd_hw_mode hw_mode,
-	bool exclude_dfs,
-	struct wpa_driver_scan_params *params)
-{
-	struct hostapd_hw_modes *mode;
-	int count, i;
-
-	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, hw_mode, 0);
-	if (mode == NULL || !mode->num_channels) {
-		wpa_printf(MSG_ERROR,
-			"P2P: No channels supported in this hw_mode: %d", hw_mode);
-		return -1;
-	}
-
-	/*
-	 * Allocate memory for frequency array, allocate one extra
-	 * slot for the zero-terminator.
-	 */
-	params->freqs = (int *) os_calloc(mode->num_channels + 1, sizeof(int));
-	if (params->freqs == NULL) {
-		return -ENOMEM;
-	}
-	for (count = 0, i = 0; i < mode->num_channels; i++) {
-		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) {
-			continue;
-		}
-		if (exclude_dfs && (mode->channels[i].flag & HOSTAPD_CHAN_RADAR)) {
-			continue;
-		}
-		params->freqs[count++] = mode->channels[i].freq;
-	}
-	if (!count && params->freqs) {
-		wpa_printf(MSG_ERROR,
-			"P2P: All channels(exclude_dfs: %d) are disabled in this hw_mode: %d",
-			exclude_dfs, hw_mode);
-		os_free(params->freqs);
-		return -1;
-	}
-	return 0;
-}
-
-static int setScanFreq(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params,
-	int freq, int operating_freq)
-{
-	int frequency = operating_freq ? operating_freq : freq;
-	if (disabled_freq(wpa_s, frequency)) {
-		wpa_printf(MSG_ERROR,
-				"P2P: freq %d is not supported for a client.", frequency);
-		return -1;
-	}
-	/*
-	 * Allocate memory for frequency array, with one extra
-	 * slot for the zero-terminator.
-	 */
-	params->freqs = new int[2] {frequency, 0};
-	return 0;
-}
-
-/**
- * setP2pCliOptimizedScanFreqsList - Fill the frequencies to scan in Scan
- * parameters.
- * @wpa_s: Pointer to wpa_supplicant data
- * @params: Pointer to Scan parameters.
- * @freq: Frequency/Band requested to scan by the application, possible values are,
- *		0 - All the frequencies - full scan
- *		2 - Frequencies in 2.4GHz
- *		5 - Frequencies in 5GHz
- *		- Valid frequency
- * @operating_freq: Frequency of BSS if found in scan cache
- * Returns: Pointer to the BSS entry or %NULL if not found
- */
-static int setP2pCliOptimizedScanFreqsList(struct wpa_supplicant *wpa_s,
-	struct wpa_driver_scan_params *params, int freq, int operating_freq)
-{
-	int ret;
-	/* If BSS is found in scan cache, first scan its operating frequency */
-	if (!wpa_s->p2p_join_scan_count && operating_freq) {
-		ret = setScanFreq(wpa_s, params, freq, operating_freq);
-		if (!ret) {
-			return ret;
-		}
-	}
-
-	/* Empty freq params means scan all the frequencies */
-	if (freq == 0) {
-		return 0;
-	}
-	else if (freq == 2 || freq == 5) {
-		/* Scan the frequencies in the band */
-		enum hostapd_hw_mode mode;
-		int ret;
-		if (wpa_s->hw.modes == NULL) {
-			wpa_printf(MSG_DEBUG,
-				   "P2P: Unknown what %dG channels the driver supports.", freq);
-			return 0;
-		}
-		mode = freq == 5 ? HOSTAPD_MODE_IEEE80211A : HOSTAPD_MODE_IEEE80211G;
-		if (wpa_s->p2p_join_scan_count < 2) {
-			// scan all non DFS channels in the first two attempts
-			ret = setBandScanFreqsList(wpa_s, mode, true, params);
-			if (ret < 0 && (-ENOMEM != ret)) {
-				// try to scan all channels before returning error
-				ret = setBandScanFreqsList(wpa_s, mode, false, params);
-			}
-		} else {
-			// scan all channels
-			ret = setBandScanFreqsList(wpa_s, mode, false, params);
-		}
-		return ret;
-	} else {
-		/* Scan the frequency requested by the application */
-		ret = setScanFreq(wpa_s, params, freq, 0);
-		return ret;
-	}
-	return 0;
-}
-
-/**
- * getP2pJoinScanInterval - Get the delay in triggering the scan to discover
- * Auto GO.
- */
-static int getP2pJoinScanIntervalUsecs(int freq)
-{
-	if (freq == 5 || freq == 2 || freq == 0) {
-		return P2P_JOIN_MULTIPLE_CHANNEL_SCAN_INTERVAL_USECS;
-	} else {
-		return P2P_JOIN_SINGLE_CHANNEL_SCAN_INTERVAL_USECS;
-	}
-}
-
 /*
  * isAnyEtherAddr - match any ether address
  *
@@ -241,56 +103,9 @@
 	return (a[0] == 2) && !(a[1] | a[2] | a[3] | a[4] | a[5]);
 }
 
-/**
- * findBssBySsid - Fetch a BSS table entry based on SSID and optional BSSID.
- * @wpa_s: Pointer to wpa_supplicant data
- * @bssid: BSSID, 02:00:00:00:00:00 matches any bssid
- * @ssid: SSID
- * @ssid_len: Length of @ssid
- * Returns: Pointer to the BSS entry or %NULL if not found
- */
-struct wpa_bss* findBssBySsid(
-	struct wpa_supplicant *wpa_s, const u8 *bssid,
-	const u8 *ssid, size_t ssid_len)
-{
-	struct wpa_bss *bss;
-	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if ((isAnyEtherAddr(bssid) ||
-			os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) &&
-			bss->ssid_len == ssid_len &&
-			os_memcmp(bss->ssid, ssid, ssid_len) == 0)
-			return bss;
-	}
-	return NULL;
-}
-
-/**
- * findBssBySsidFromAnyInterface - Fetch a BSS table entry based on SSID and optional BSSID
- * by iterating through all the interfaces.
- * @head: Head of Pointer to wpa_supplicant data
- * @bssid: BSSID, 02:00:00:00:00:00 matches any bssid
- * @ssid: SSID
- * @ssid_len: Length of @ssid
- * Returns: Pointer to the BSS entry or %NULL if not found
- */
-struct wpa_bss* findBssBySsidFromAnyInterface(
-	struct wpa_supplicant *head, const u8 *bssid,
-	const u8 *ssid, size_t ssid_len)
-{
-	struct wpa_supplicant *wpa_s;
-	struct wpa_bss *bss = NULL;
-	for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) {
-		bss = findBssBySsid(wpa_s, bssid, ssid, ssid_len);
-		if (bss != NULL) {
-			return bss;
-		}
-	}
-	return bss;
-}
-
 struct wpa_ssid* addGroupClientNetwork(
 	struct wpa_supplicant* wpa_s,
-	uint8_t *group_owner_bssid,
+	const uint8_t *group_owner_bssid,
 	const std::vector<uint8_t>& ssid,
 	const std::string& passphrase)
 {
@@ -304,22 +119,13 @@
 	// set P2p network defaults
 	wpa_network->p2p_group = 1;
 	wpa_network->mode = wpas_mode::WPAS_MODE_INFRA;
-
-	wpa_network->auth_alg = WPA_AUTH_ALG_OPEN;
-	wpa_network->key_mgmt = WPA_KEY_MGMT_PSK;
-	wpa_network->proto = WPA_PROTO_RSN;
-	wpa_network->pairwise_cipher = WPA_CIPHER_CCMP;
-	wpa_network->group_cipher = WPA_CIPHER_CCMP;
 	wpa_network->disabled = 2;
 
 	// set necessary fields
-	os_memcpy(wpa_network->bssid, group_owner_bssid, ETH_ALEN);
-	wpa_network->bssid_set = 1;
-
 	wpa_network->ssid = (uint8_t *)os_malloc(ssid.size());
 	if (wpa_network->ssid == NULL) {
 		wpa_config_remove_network(wpa_s->conf, wpa_network->id);
-		return  NULL;
+		return NULL;
 	}
 	memcpy(wpa_network->ssid, ssid.data(), ssid.size());
 	wpa_network->ssid_len = ssid.size();
@@ -328,7 +134,7 @@
 	wpa_network->passphrase = dup_binstr(passphrase.c_str(), passphrase.length());
 	if (wpa_network->passphrase == NULL) {
 		wpa_config_remove_network(wpa_s->conf, wpa_network->id);
-		return  NULL;
+		return NULL;
 	}
 	wpa_config_update_psk(wpa_network);
 
@@ -336,15 +142,6 @@
 
 }
 
-void joinScanWrapper(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_supplicant *wpa_s = (struct wpa_supplicant *) eloop_ctx;
-
-	if (pending_join_scan_callback != NULL) {
-		pending_join_scan_callback();
-	}
-}
-
 void scanResJoinWrapper(
 	struct wpa_supplicant *wpa_s,
 	struct wpa_scan_results *scan_res)
@@ -360,85 +157,6 @@
 	}
 }
 
-int joinScanReq(
-	struct wpa_supplicant* wpa_s,
-	const std::vector<uint8_t>& ssid,
-	int freq, int operating_freq)
-{
-	int ret;
-	struct wpa_driver_scan_params params;
-	struct wpabuf *ies;
-	size_t ielen;
-	unsigned int bands;
-
-	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
-		wpa_printf(MSG_ERROR,
-			"P2P: P2P interface is gone, cancel join scan");
-		return -ENXIO;
-	}
-
-	os_memset(&params, 0, sizeof(params));
-	if (ssid.size() > 0) {
-		params.ssids[0].ssid = ssid.data();
-		params.ssids[0].ssid_len = ssid.size();
-	} else {
-		params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-		params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
-	}
-	wpa_printf(MSG_DEBUG, "Scan SSID %s for join with frequency %d"
-		"BSS operating_freq from scan cache %d",
-		wpa_ssid_txt(params.ssids[0].ssid, params.ssids[0].ssid_len), freq, operating_freq);
-
-	/* Construct an optimized p2p scan channel list */
-	ret = setP2pCliOptimizedScanFreqsList(wpa_s, &params, freq, operating_freq);
-	if (ret < 0) {
-		wpa_printf(MSG_ERROR,
-				   "Failed to set frequency in p2p scan params, error = %d", ret);
-		return -1;
-	}
-
-	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
-	ies = wpabuf_alloc(ielen);
-	if (ies == NULL) {
-		if (params.freqs) {
-			os_free(params.freqs);
-		}
-		return -1;
-	}
-
-	bands = wpas_get_bands(wpa_s, params.freqs);
-	p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
-
-	params.p2p_probe = 1;
-	params.extra_ies = (u8 *) wpabuf_head(ies);
-	params.extra_ies_len = wpabuf_len(ies);
-	if (wpa_s->clear_driver_scan_cache) {
-		wpa_printf(MSG_DEBUG,
-			"Request driver to clear scan cache due to local BSS flush");
-		params.only_new_results = 1;
-	}
-
-	ret = wpa_drv_scan(wpa_s, &params);
-	if (!ret) {
-		os_get_reltime(&wpa_s->scan_trigger_time);
-		if (wpa_s->scan_res_handler) {
-			wpa_printf(MSG_DEBUG, "Replace current running scan result handler");
-		}
-		wpa_s->p2p_join_scan_count++;
-		wpa_s->scan_res_handler = scanResJoinWrapper;
-		wpa_s->own_scan_requested = 1;
-		wpa_s->clear_driver_scan_cache = 0;
-	}
-
-	if (params.freqs) {
-		os_free(params.freqs);
-	}
-
-	wpabuf_free(ies);
-
-	return ret;
-}
-
 static bool is6GhzAllowed(struct wpa_supplicant *wpa_s) {
 	if (!wpa_s->global->p2p) return false;
 	return wpa_s->global->p2p->allow_6ghz;
@@ -446,9 +164,10 @@
 
 int joinGroup(
 	struct wpa_supplicant* wpa_s,
-	uint8_t *group_owner_bssid,
+	const uint8_t *group_owner_bssid,
 	const std::vector<uint8_t>& ssid,
-	const std::string& passphrase)
+	const std::string& passphrase,
+	uint32_t freq)
 {
 	int ret = 0;
 	int he = wpa_s->conf->p2p_go_he;
@@ -469,8 +188,9 @@
 	wpa_network->temporary = 1;
 
 	if (wpas_p2p_group_add_persistent(
-		wpa_s, wpa_network, 0, 0, 0, 0, ht40, vht,
-		CHANWIDTH_USE_HT, he, 0, NULL, 0, 0, is6GhzAllowed(wpa_s))) {
+		wpa_s, wpa_network, 0, 0, freq, 0, ht40, vht,
+		CONF_OPER_CHWIDTH_USE_HT, he, 0, NULL, 0, 0, is6GhzAllowed(wpa_s),
+		P2P_JOIN_LIMIT, isAnyEtherAddr(group_owner_bssid) ? NULL : group_owner_bssid)) {
 		ret = -1;
 	}
 
@@ -479,27 +199,6 @@
 	return ret;
 }
 
-void notifyGroupJoinFailure(
-	struct wpa_supplicant* wpa_s)
-{
-	u8 zero_addr[ETH_ALEN] = {0};
-	std::vector<uint8_t> ssid = {'D', 'I', 'R', 'E','C', 'T', '-'};
-	std::string passphrase = "";
-	struct wpa_ssid *wpa_network = addGroupClientNetwork(
-		wpa_s, zero_addr, ssid, passphrase);
-	if (wpa_network) {
-		wpa_network->temporary = 1;
-		wpas_notify_p2p_group_formation_failure(wpa_s, "Failed to find the group.");
-		wpas_notify_p2p_group_removed(
-			wpa_s, wpa_network, "client");
-		wpa_config_remove_network(
-			wpa_s->conf, wpa_network->id);
-	} else {
-		wpa_printf(MSG_ERROR,
-			"P2P: Cannot construct a network.");
-	}
-}
-
 void scanResJoinIgnore(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) {
 	wpa_printf(MSG_DEBUG, "P2P: Ignore group join scan results.");
 
@@ -1106,6 +805,16 @@
 		&P2pIface::setVendorElementsInternal, in_frameTypeMask, in_vendorElemBytes);
 }
 
+::ndk::ScopedAStatus P2pIface::configureEapolIpAddressAllocationParams(
+	int32_t in_ipAddressGo, int32_t in_ipAddressMask,
+	int32_t in_ipAddressStart, int32_t in_ipAddressEnd)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+		&P2pIface::configureEapolIpAddressAllocationParamsInternal,
+		in_ipAddressGo, in_ipAddressMask, in_ipAddressStart, in_ipAddressEnd);
+}
+
 std::pair<std::string, ndk::ScopedAStatus> P2pIface::getNameInternal()
 {
 	return {ifname_, ndk::ScopedAStatus::ok()};
@@ -1308,13 +1017,13 @@
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	int edmg = wpa_s->conf->p2p_go_edmg;
 	const char* pin =
 		pre_selected_pin.length() > 0 ? pre_selected_pin.data() : nullptr;
-	bool auto_join = !join_existing_group;
 	int new_pin = wpas_p2p_connect(
-		wpa_s, peer_address.data(), pin, wps_method, persistent, auto_join,
+		wpa_s, peer_address.data(), pin, wps_method, persistent, false,
 		join_existing_group, false, go_intent_signed, 0, 0, -1, false, ht40,
-		vht, CHANWIDTH_USE_HT, he, 0, nullptr, 0, is6GhzAllowed(wpa_s));
+		vht, CONF_OPER_CHWIDTH_USE_HT, he, edmg, nullptr, 0, is6GhzAllowed(wpa_s));
 	if (new_pin < 0) {
 		return {"", createStatus(SupplicantStatusCode::FAILURE_UNKNOWN)};
 	}
@@ -1376,7 +1085,6 @@
 	if (!wpa_group_s) {
 		return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
 	}
-	wpa_group_s->global->p2p_go_found_external_scan = 0;
 	if (wpas_p2p_group_remove(wpa_group_s, group_ifname.c_str())) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
@@ -1424,6 +1132,7 @@
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	int edmg = wpa_s->conf->p2p_go_edmg;
 	struct wpa_ssid* ssid =
 		wpa_config_get_network(wpa_s->conf, persistent_network_id);
 	if (ssid == NULL || ssid->disabled != 2) {
@@ -1434,7 +1143,7 @@
 	}
 	if (wpas_p2p_invite(
 		wpa_s, peer_address.data(), ssid, NULL, 0, 0, ht40, vht,
-		CHANWIDTH_USE_HT, 0, he, 0, is6GhzAllowed(wpa_s))) {
+		CONF_OPER_CHWIDTH_USE_HT, 0, he, edmg, is6GhzAllowed(wpa_s))) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
 	return ndk::ScopedAStatus::ok();
@@ -1891,12 +1600,13 @@
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	int edmg = wpa_s->conf->p2p_go_edmg;
 	struct wpa_ssid* ssid =
 		wpa_config_get_network(wpa_s->conf, persistent_network_id);
 	if (ssid == NULL) {
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, 0, 0, ht40, vht,
-			CHANWIDTH_USE_HT, he, 0, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1904,7 +1614,8 @@
 	} else if (ssid->disabled == 2) {
 		if (wpas_p2p_group_add_persistent(
 			wpa_s, ssid, 0, 0, 0, 0, ht40, vht,
-			CHANWIDTH_USE_HT, he, 0, NULL, 0, 0, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, NULL, 0, 0,
+			is6GhzAllowed(wpa_s), 0, NULL)) {
 			return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1922,6 +1633,7 @@
 	int he = wpa_s->conf->p2p_go_he;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	int edmg = wpa_s->conf->p2p_go_edmg;
 
 	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
 		return createStatus(SupplicantStatusCode::FAILURE_IFACE_DISABLED);
@@ -1937,6 +1649,10 @@
 			"Passphrase is invalid.");
 	}
 
+	wpa_printf(MSG_DEBUG,
+		    "P2P: Add group with config Role: %s network name: %s freq: %d",
+		    joinExistingGroup ? "CLIENT" : "GO",
+		    wpa_ssid_txt(ssid.data(), ssid.size()), freq);
 	if (!joinExistingGroup) {
 		struct p2p_data *p2p = wpa_s->global->p2p;
 		os_memcpy(p2p->ssid, ssid.data(), ssid.size());
@@ -1949,7 +1665,7 @@
 
 		if (wpas_p2p_group_add(
 			wpa_s, persistent, freq, 0, ht40, vht,
-			CHANWIDTH_USE_HT, he, 0, is6GhzAllowed(wpa_s))) {
+			CONF_OPER_CHWIDTH_USE_HT, he, edmg, is6GhzAllowed(wpa_s))) {
 			return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 		}
 		return ndk::ScopedAStatus::ok();
@@ -1958,84 +1674,11 @@
 	// The rest is for group join.
 	wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND before group join.");
 	wpas_p2p_stop_find(wpa_s);
-
 	if (peer_address.size() != ETH_ALEN) {
 		return createStatusWithMsg(SupplicantStatusCode::FAILURE_ARGS_INVALID,
 			"Peer address is invalid.");
 	}
-
-	if (pending_scan_res_join_callback != NULL) {
-		wpa_printf(MSG_WARNING, "P2P: Renew scan result callback with new request.");
-	}
-
-	pending_join_scan_callback =
-		[wpa_s, ssid, peer_address, freq]() {
-		if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
-			return;
-		}
-		int operating_freq = 0;
-		struct wpa_bss *bss = findBssBySsidFromAnyInterface(
-			wpa_s->global->ifaces, peer_address.data(), ssid.data(), ssid.size());
-		if (bss != NULL) {
-			wpa_printf(MSG_DEBUG, "P2P: Found Group owner " MACSTR "in scan cache",
-				MAC2STR(bss->bssid));
-			operating_freq = bss->freq;
-		}
-
-		int ret = joinScanReq(wpa_s, ssid, freq, operating_freq);
-		// for BUSY case, the scan might be occupied by WiFi.
-		// Do not give up immediately, but try again later.
-		if (-EBUSY == ret) {
-			// re-schedule this join scan
-			eloop_cancel_timeout(joinScanWrapper, wpa_s, NULL);
-			eloop_register_timeout(0, P2P_JOIN_SINGLE_CHANNEL_SCAN_INTERVAL_USECS,
-					joinScanWrapper, wpa_s, NULL);
-		} else if (0 != ret) {
-			notifyGroupJoinFailure(wpa_s);
-			pending_scan_res_join_callback = NULL;
-		}
-	};
-
-	pending_scan_res_join_callback = [wpa_s, ssid, passphrase, peer_address, freq, this]() {
-		if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
-			return;
-		}
-
-		wpa_printf(MSG_DEBUG, "P2P: Scan results received for join (reinvoke).");
-
-		struct wpa_bss *bss = findBssBySsid(
-			wpa_s, peer_address.data(), ssid.data(), ssid.size());
-		if (bss) {
-			wpa_s->global->p2p_go_found_external_scan = 1;
-			if (0 != joinGroup(wpa_s, bss->bssid, ssid, passphrase)) {
-				wpa_printf(MSG_ERROR, "P2P: Failed to join a group.");
-				wpa_s->global->p2p_go_found_external_scan = 0;
-			}
-			// no need to notify group join failure here,
-			// it will be handled by wpas_p2p_group_add_persistent
-			// called in joinGroup.
-			pending_scan_res_join_callback = NULL;
-			return;
-		}
-		wpa_printf(MSG_DEBUG, "P2P: Join scan count %d.", wpa_s->p2p_join_scan_count);
-		eloop_cancel_timeout(joinScanWrapper, wpa_s, NULL);
-		if (wpa_s->p2p_join_scan_count < P2P_MAX_JOIN_SCAN_ATTEMPTS) {
-			wpa_printf(MSG_DEBUG, "P2P: Try join again later.");
-			eloop_register_timeout(0, getP2pJoinScanIntervalUsecs(freq),
-				joinScanWrapper, wpa_s, this);
-			return;
-		}
-
-		wpa_printf(MSG_ERROR, "P2P: Failed to find the group with "
-			"network name %s - stop join attempt",
-			wpa_ssid_txt(ssid.data(), ssid.size()));
-		notifyGroupJoinFailure(wpa_s);
-		pending_scan_res_join_callback = NULL;
-	};
-
-	wpa_s->p2p_join_scan_count = 0;
-	pending_join_scan_callback();
-	if (pending_scan_res_join_callback == NULL) {
+	if (joinGroup(wpa_s, peer_address.data(), ssid, passphrase, freq)) {
 		return createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
 			"Failed to start scan.");
 	}
@@ -2058,11 +1701,16 @@
 	if (enable) {
 		wpa_s->conf->p2p_device_random_mac_addr = 1;
 		wpa_s->conf->p2p_interface_random_mac_addr = 1;
+		int status = wpas_p2p_mac_setup(wpa_s);
 
 		// restore config if it failed to set up MAC address.
-		if (wpas_p2p_mac_setup(wpa_s) < 0) {
+		if (status < 0) {
 			wpa_s->conf->p2p_device_random_mac_addr = 0;
 			wpa_s->conf->p2p_interface_random_mac_addr = 0;
+			if (status == -ENOTSUP) {
+				return createStatusWithMsg(SupplicantStatusCode::FAILURE_UNSUPPORTED,
+					"Failed to set up MAC address, feature not supported.");
+			}
 			return createStatusWithMsg(SupplicantStatusCode::FAILURE_UNKNOWN,
 				"Failed to set up MAC address.");
 		}
@@ -2187,6 +1835,23 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus P2pIface::configureEapolIpAddressAllocationParamsInternal(
+	uint32_t ipAddressGo, uint32_t ipAddressMask,
+	uint32_t ipAddressStart, uint32_t ipAddressEnd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Configure IP addresses for IP allocation in EAPOL"
+		   " ipAddressGo: 0x%x mask: 0x%x Range - Start: 0x%x End: 0x%x",
+			ipAddressGo, ipAddressMask, ipAddressStart, ipAddressEnd);
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+
+	os_memcpy(wpa_s->conf->ip_addr_go, &ipAddressGo, 4);
+	os_memcpy(wpa_s->conf->ip_addr_mask, &ipAddressMask, 4);
+	os_memcpy(wpa_s->conf->ip_addr_start, &ipAddressStart, 4);
+	os_memcpy(wpa_s->conf->ip_addr_end, &ipAddressEnd, 4);
+
+	return ndk::ScopedAStatus::ok();
+}
+
 /**
  * Retrieve the underlying |wpa_supplicant| struct
  * pointer for this iface.
diff --git a/wpa_supplicant/aidl/p2p_iface.h b/wpa_supplicant/aidl/p2p_iface.h
index 5f9903c..61972f2 100644
--- a/wpa_supplicant/aidl/p2p_iface.h
+++ b/wpa_supplicant/aidl/p2p_iface.h
@@ -172,6 +172,9 @@
 	::ndk::ScopedAStatus setVendorElements(
 		P2pFrameTypeMask in_frameTypeMask,
 		const std::vector<uint8_t>& in_vendorElemBytes) override;
+	::ndk::ScopedAStatus configureEapolIpAddressAllocationParams(
+		int32_t in_ipAddressGo, int32_t in_ipAddressMask,
+		int32_t in_ipAddressStart, int32_t in_ipAddressEnd) override;
 
 private:
 	// Corresponding worker functions for the AIDL methods.
@@ -292,6 +295,9 @@
 	ndk::ScopedAStatus setVendorElementsInternal(
 		P2pFrameTypeMask frameTypeMask,
 		const std::vector<uint8_t>& vendorElemBytes);
+	::ndk::ScopedAStatus configureEapolIpAddressAllocationParamsInternal(
+		uint32_t ipAddressGo, uint32_t ipAddressMask,
+		uint32_t ipAddressStart, uint32_t ipAddressEnd);
 
 	struct wpa_supplicant* retrieveIfacePtr();
 	struct wpa_supplicant* retrieveGroupIfacePtr(
diff --git a/wpa_supplicant/aidl/sta_iface.cpp b/wpa_supplicant/aidl/sta_iface.cpp
index f369142..afcd4b5 100644
--- a/wpa_supplicant/aidl/sta_iface.cpp
+++ b/wpa_supplicant/aidl/sta_iface.cpp
@@ -50,6 +50,7 @@
   WIDTH_80P80 = 4,
   WIDTH_5	 = 5,
   WIDTH_10	= 6,
+  WIDTH_320	= 7,
   WIDTH_INVALID = -1
 };
 
@@ -289,6 +290,12 @@
 	WPA_ASSERT(false);
 }
 
+inline std::array<uint8_t, ETH_ALEN> macAddrToArray(const uint8_t* mac_addr) {
+	std::array<uint8_t, ETH_ALEN> arr;
+	std::copy(mac_addr, mac_addr + ETH_ALEN, std::begin(arr));
+	return arr;
+}
+
 }  // namespace
 
 namespace aidl {
@@ -810,6 +817,32 @@
 		&StaIface::getConnectionMloLinksInfoInternal, _aidl_return);
 }
 
+::ndk::ScopedAStatus StaIface::getSignalPollResults(
+    std::vector<SignalPollResult> *results)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::getSignalPollResultsInternal, results);
+}
+
+::ndk::ScopedAStatus StaIface::addQosPolicyRequestForScs(
+		const std::vector<QosPolicyScsData>& in_qosPolicyData,
+		std::vector<QosPolicyScsRequestStatus>* _aidl_return)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::addQosPolicyRequestForScsInternal, _aidl_return, in_qosPolicyData);
+}
+
+::ndk::ScopedAStatus StaIface::removeQosPolicyForScs(
+		const std::vector<uint8_t>& in_scsPolicyIds,
+		std::vector<QosPolicyScsRequestStatus>* _aidl_return)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    &StaIface::removeQosPolicyForScsInternal, _aidl_return, in_scsPolicyIds);
+}
+
 std::pair<std::string, ndk::ScopedAStatus> StaIface::getNameInternal()
 {
 	return {ifname_, ndk::ScopedAStatus::ok()};
@@ -1793,7 +1826,9 @@
 
 	if (wpa_s->connection_set) {
 		capa.legacyMode = LegacyMode::UNKNOWN;
-		if (wpa_s->connection_he) {
+		if (wpa_s->connection_eht) {
+			capa.technology = WifiTechnology::EHT;
+		} else if (wpa_s->connection_he) {
 			capa.technology = WifiTechnology::HE;
 		} else if (wpa_s->connection_vht) {
 			capa.technology = WifiTechnology::VHT;
@@ -1824,6 +1859,9 @@
 		case CHAN_WIDTH_80P80:
 			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80P80;
 			break;
+		case CHAN_WIDTH_320:
+			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_320;
+			break;
 		default:
 			capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
 			break;
@@ -1863,6 +1901,12 @@
 
 	mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::TRUST_ON_FIRST_USE);
 
+	mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::SET_TLS_MINIMUM_VERSION);
+
+#ifdef EAP_TLSV1_3
+	mask |= static_cast<uint32_t>(WpaDriverCapabilitiesMask::TLS_V1_3);
+#endif
+
 	wpa_printf(MSG_DEBUG, "Driver capability mask: 0x%x", mask);
 
 	return {static_cast<WpaDriverCapabilitiesMask>(mask),
@@ -1976,10 +2020,406 @@
 
 std::pair<MloLinksInfo, ndk::ScopedAStatus> StaIface::getConnectionMloLinksInfoInternal()
 {
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
 	MloLinksInfo linksInfo;
+	MloLink link;
+
+	linksInfo.apMldMacAddress = macAddrToArray(wpa_s->ap_mld_addr);
+	if (!wpa_s->valid_links)
+		 return {linksInfo, ndk::ScopedAStatus::ok()};
+
+	for (int i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "Add MLO Link ID %d info", i);
+		// Associated link id.
+		if (os_memcmp(wpa_s->links[i].bssid, wpa_s->bssid, ETH_ALEN) == 0) {
+			linksInfo.apMloLinkId = i;
+		}
+		link.linkId = i;
+		link.staLinkMacAddress.assign(
+		    wpa_s->links[i].addr, wpa_s->links[i].addr + ETH_ALEN);
+		link.apLinkMacAddress = macAddrToArray(wpa_s->links[i].bssid);
+		link.frequencyMHz = wpa_s->links[i].freq;
+		// TODO (b/259710591): Once supplicant implements TID-to-link
+		// mapping, copy it here. Mapping can be changed in two
+		// scenarios
+		//    1. Mandatory mapping from AP
+		//    2. Negotiated mapping
+		// After association, framework call this API to get
+		// MloLinksInfo. If there is an update in mapping later, notify
+		// framework on the change using the callback,
+		// ISupplicantStaIfaceCallback.onMloLinksInfoChanged() with
+		// reason code as TID_TO_LINK_MAP. In absence of an advertised
+		// mapping by the AP, a default TID-to-link mapping is assumed
+		// unless an individual TID-to-link mapping is successfully
+		// negotiated.
+		link.tidsUplinkMap = 0xFF;
+		link.tidsDownlinkMap = 0xFF;
+		linksInfo.links.push_back(link);
+	}
+
 	return {linksInfo, ndk::ScopedAStatus::ok()};
 }
 
+std::pair<std::vector<SignalPollResult>, ndk::ScopedAStatus>
+StaIface::getSignalPollResultsInternal()
+{
+	std::vector<SignalPollResult> results;
+	struct wpa_signal_info si;
+	struct wpa_mlo_signal_info mlo_si;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+
+	if (wpa_s->valid_links && (wpa_drv_mlo_signal_poll(wpa_s, &mlo_si) == 0)) {
+		for (int i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo_si.valid_links & BIT(i)))
+				continue;
+
+			SignalPollResult result;
+			result.linkId = i;
+			result.currentRssiDbm = mlo_si.links[i].data.signal;
+			result.txBitrateMbps = mlo_si.links[i].data.current_tx_rate / 1000;
+			result.rxBitrateMbps = mlo_si.links[i].data.current_rx_rate / 1000;
+			result.frequencyMhz = mlo_si.links[i].frequency;
+			results.push_back(result);
+		}
+	} else if (wpa_drv_signal_poll(wpa_s, &si) == 0) {
+		SignalPollResult result;
+		result.linkId = 0;
+		result.currentRssiDbm = si.data.signal;
+		result.txBitrateMbps = si.data.current_tx_rate / 1000;
+		result.rxBitrateMbps = si.data.current_rx_rate / 1000;
+		result.frequencyMhz = si.frequency;
+		results.push_back(result);
+	}
+
+	return {results, ndk::ScopedAStatus::ok()};
+}
+
+static int set_type4_frame_classifier(QosPolicyScsData qos_policy_data,
+				      struct type4_params *param)
+{
+	u8 classifier_mask = 0;
+	uint32_t inMask = static_cast<uint32_t>(qos_policy_data.classifierParams.classifierParamMask);
+
+	if (qos_policy_data.classifierParams.ipVersion ==
+	    IpVersion::VERSION_4) {
+		param->ip_version = IPV4;
+	} else if (qos_policy_data.classifierParams.ipVersion ==
+	    IpVersion::VERSION_6) {
+		param->ip_version = IPV6;
+	} else {
+		wpa_printf(MSG_ERROR, "IP version missing/invalid");
+		return -1;
+	}
+
+	/* Classifier Mask - bit 0 = Ip Version */
+	classifier_mask |= BIT(0);
+
+	if (inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::SRC_IP)) {
+		if (param->ip_version == IPV4) {
+			if (qos_policy_data.classifierParams.srcIp.size() !=
+			    sizeof(param->ip_params.v4.src_ip)) {
+				wpa_printf(MSG_ERROR, "Invalid source IP");
+				return -1;
+			}
+			os_memcpy(&param->ip_params.v4.src_ip, qos_policy_data.classifierParams.srcIp.data(), 4);
+		} else {
+			if (qos_policy_data.classifierParams.srcIp.size() !=
+			    sizeof(param->ip_params.v6.src_ip)) {
+				wpa_printf(MSG_ERROR, "Invalid source IP");
+				return -1;
+			}
+			os_memcpy(&param->ip_params.v6.src_ip, qos_policy_data.classifierParams.srcIp.data(), 16);
+		}
+
+		/* Classifier Mask - bit 1 = Source IP Address */
+		classifier_mask |= BIT(1);
+	}
+
+	if (inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::DST_IP)) {
+		if (param->ip_version == IPV4) {
+			if (qos_policy_data.classifierParams.dstIp.size() !=
+			    sizeof(param->ip_params.v4.dst_ip)) {
+				wpa_printf(MSG_ERROR, "Invalid destination IP");
+				return -1;
+			}
+			os_memcpy(&param->ip_params.v4.dst_ip, qos_policy_data.classifierParams.dstIp.data(), 4);
+		} else {
+			if (qos_policy_data.classifierParams.dstIp.size() !=
+			    sizeof(param->ip_params.v6.dst_ip)) {
+				wpa_printf(MSG_ERROR, "Invalid destination IP");
+				return -1;
+			}
+			os_memcpy(&param->ip_params.v6.dst_ip, qos_policy_data.classifierParams.dstIp.data(), 16);
+		}
+
+		/* Classifier Mask - bit 2 = Destination IP Address */
+		classifier_mask |= BIT(2);
+	}
+
+	if ((inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::SRC_PORT))
+			&& (qos_policy_data.classifierParams.srcPort > 0)) {
+		if (param->ip_version == IPV4)
+			param->ip_params.v4.src_port = qos_policy_data.classifierParams.srcPort;
+		else
+			param->ip_params.v6.src_port = qos_policy_data.classifierParams.srcPort;
+
+		/* Classifier Mask - bit 3 = Source Port */
+		classifier_mask |= BIT(3);
+	}
+
+	if ((inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::DST_PORT_RANGE))
+			&& (qos_policy_data.classifierParams.dstPortRange.startPort > 0)) {
+		if (param->ip_version == IPV4)
+			param->ip_params.v4.dst_port = qos_policy_data.classifierParams.dstPortRange.startPort;
+		else
+			param->ip_params.v6.dst_port = qos_policy_data.classifierParams.dstPortRange.startPort;
+
+		/* Classifier Mask - bit 4 = Destination Port range */
+		classifier_mask |= BIT(4);
+	}
+
+	if ((inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::DSCP))
+			&& (qos_policy_data.classifierParams.dscp > 0)) {
+		if (param->ip_version == IPV4)
+			param->ip_params.v4.dscp = qos_policy_data.classifierParams.dscp;
+		else
+			param->ip_params.v6.dscp = qos_policy_data.classifierParams.dscp;
+
+		/* Classifier Mask - bit 5 = DSCP */
+		classifier_mask |= BIT(5);
+	}
+
+	if (inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::PROTOCOL_NEXT_HEADER)) {
+		if (!((qos_policy_data.classifierParams.protocolNextHdr ==
+		       ProtocolNextHeader::TCP) ||
+		      (qos_policy_data.classifierParams.protocolNextHdr ==
+		       ProtocolNextHeader::UDP) ||
+		      (qos_policy_data.classifierParams.protocolNextHdr ==
+		       ProtocolNextHeader::ESP))) {
+			wpa_printf(MSG_ERROR, "Invalid protocol");
+			return -1;
+		}
+		if (param->ip_version == IPV4) {
+			param->ip_params.v4.protocol =
+				(u8)qos_policy_data.classifierParams.protocolNextHdr;
+		} else {
+			param->ip_params.v6.next_header =
+				(u8)qos_policy_data.classifierParams.protocolNextHdr;
+		}
+
+		/* Classifier Mask - bit 6 = Protocol Number*/
+		classifier_mask |= BIT(6);
+	}
+
+	if (inMask & static_cast<uint32_t>(QosPolicyClassifierParamsMask::FLOW_LABEL)) {
+		if (qos_policy_data.classifierParams.flowLabelIpv6.size() !=
+		    sizeof(param->ip_params.v6.flow_label)) {
+			wpa_printf(MSG_ERROR, "Invalid flow label");
+			return -1;
+		}
+		os_memcpy(param->ip_params.v6.flow_label, qos_policy_data.classifierParams.flowLabelIpv6.data(),
+			  sizeof(qos_policy_data.classifierParams.flowLabelIpv6));
+
+		/* Classifier Mask - bit 7 = flow level */
+		classifier_mask |= BIT(7);
+	}
+
+	param->classifier_mask = classifier_mask;
+	return 0;
+}
+
+static int scs_parse_type4(struct tclas_element *elem, QosPolicyScsData qos_policy_data)
+{
+	struct type4_params type4_param;
+	memset(&type4_param, 0, sizeof(type4_param));
+
+	if (set_type4_frame_classifier(qos_policy_data, &type4_param) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to set frame_classifier 4");
+		return -1;
+	}
+
+	os_memcpy(&elem->frame_classifier.type4_param,
+		  &type4_param, sizeof(struct type4_params));
+	return 0;
+}
+
+/**
+ * This is a request to the AP (if it supports the feature) to apply the QoS policy
+ * on traffic in the Downlink.
+ */
+std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+StaIface::addQosPolicyRequestForScsInternal(const std::vector<QosPolicyScsData>& qosPolicyData)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct scs_robust_av_data *scs_data = &wpa_s->scs_robust_av_req;
+	struct scs_desc_elem desc_elem;
+	int user_priority, num_qos_policies;
+	unsigned int num_scs_ids = 0;
+	std::vector<QosPolicyScsRequestStatus> reports;
+
+	if (wpa_s->ongoing_scs_req) {
+		wpa_printf(MSG_ERROR, "AIDL: SCS Request already in queue");
+		return {std::vector<QosPolicyScsRequestStatus>(),
+			createStatus(SupplicantStatusCode::FAILURE_ONGOING_REQUEST)};
+	}
+	free_up_scs_desc(scs_data);
+
+	/**
+	 * format:
+	 * [scs_id=<decimal number>] [scs_up=<0-7>]
+	 * [classifier params based on classifier type]
+	 * [scs_id=<decimal number>] ...
+	 */
+	num_qos_policies = qosPolicyData.size();
+	for (int i = 0; i < num_qos_policies; i++) {
+		struct scs_desc_elem *new_desc_elems;
+		struct active_scs_elem *active_scs_desc;
+		struct tclas_element *elem;
+		bool scsid_active = false;
+		QosPolicyScsRequestStatus status;
+
+		memset(&desc_elem, 0, sizeof(desc_elem));
+		desc_elem.scs_id = qosPolicyData[i].policyId;
+		status.policyId = desc_elem.scs_id;
+		desc_elem.request_type = SCS_REQ_ADD;
+		dl_list_for_each(active_scs_desc, &wpa_s->active_scs_ids,
+				 struct active_scs_elem, list) {
+			if (desc_elem.scs_id == active_scs_desc->scs_id) {
+				scsid_active = true;
+				break;
+			}
+		}
+
+		if (scsid_active) {
+			wpa_printf(MSG_ERROR, "SCSID %d already active",
+				   desc_elem.scs_id);
+			status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::ALREADY_ACTIVE;
+			reports.push_back(status);
+			continue;
+		}
+
+		status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::INVALID;
+		user_priority = qosPolicyData[i].userPriority;
+		if (user_priority < 0 || user_priority > 7) {
+			wpa_printf(MSG_ERROR,
+				   "Intra-Access user priority invalid %d", user_priority);
+			reports.push_back(status);
+			continue;
+		}
+
+		desc_elem.intra_access_priority = user_priority;
+		desc_elem.scs_up_avail = true;
+
+		/**
+		 * Supported classifier type 4.
+		 */
+		desc_elem.tclas_elems = (struct tclas_element *) os_malloc(sizeof(struct tclas_element));
+		if (!desc_elem.tclas_elems) {
+			wpa_printf(MSG_ERROR,
+				   "Classifier type4 failed with Bad malloc");
+			reports.push_back(status);
+			continue;
+		}
+
+		elem = desc_elem.tclas_elems;
+		memset(elem, 0, sizeof(struct tclas_element));
+		elem->classifier_type = 4;
+		if (scs_parse_type4(elem, qosPolicyData[i]) < 0) {
+			os_free(elem);
+			reports.push_back(status);
+			continue;
+		}
+
+		desc_elem.num_tclas_elem = 1;
+
+		/* Reallocate memory to scs_desc_elems to accomodate further policies */
+		new_desc_elems = static_cast<struct scs_desc_elem *>(os_realloc(scs_data->scs_desc_elems,
+				(num_scs_ids + 1) * sizeof(struct scs_desc_elem)));
+		if (!new_desc_elems) {
+			os_free(elem);
+			reports.push_back(status);
+			continue;
+		}
+
+		scs_data->scs_desc_elems = new_desc_elems;
+		os_memcpy((u8 *) scs_data->scs_desc_elems + num_scs_ids *
+			  sizeof(desc_elem), &desc_elem, sizeof(desc_elem));
+		num_scs_ids++;
+		scs_data->num_scs_desc = num_scs_ids;
+		status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::SENT;
+		reports.push_back(status);
+	}
+	wpas_send_scs_req(wpa_s);
+	return {std::vector<QosPolicyScsRequestStatus>(reports),
+		ndk::ScopedAStatus::ok()};
+}
+
+std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+StaIface::removeQosPolicyForScsInternal(const std::vector<uint8_t>& scsPolicyIds)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct scs_robust_av_data *scs_data = &wpa_s->scs_robust_av_req;
+	struct scs_desc_elem desc_elem;
+	int count;
+	unsigned int num_scs_ids = 0;
+	std::vector<QosPolicyScsRequestStatus> reports;
+	struct active_scs_elem *scs_desc;
+
+	if (wpa_s->ongoing_scs_req) {
+		wpa_printf(MSG_ERROR, "AIDL: SCS Request already in queue");
+		return {std::vector<QosPolicyScsRequestStatus>(),
+			createStatus(SupplicantStatusCode::FAILURE_ONGOING_REQUEST)};
+	}
+	free_up_scs_desc(scs_data);
+
+	count = scsPolicyIds.size();
+	for (int i = 0; i < count; i++) {
+		struct scs_desc_elem *new_desc_elems;
+		QosPolicyScsRequestStatus status;
+		bool policy_id_exists = false;
+
+		memset(&desc_elem, 0, sizeof(desc_elem));
+		desc_elem.scs_id = scsPolicyIds[i];
+		status.policyId = scsPolicyIds[i];
+		desc_elem.request_type = SCS_REQ_REMOVE;
+		dl_list_for_each(scs_desc, &wpa_s->active_scs_ids,
+				struct active_scs_elem, list) {
+			if (desc_elem.scs_id == scs_desc->scs_id) {
+				policy_id_exists = true;
+				break;
+			}
+		}
+		if (policy_id_exists == false) {
+			status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::NOT_EXIST;
+			reports.push_back(status);
+			continue;
+		}
+
+		new_desc_elems = static_cast<struct scs_desc_elem *>(os_realloc(scs_data->scs_desc_elems, (num_scs_ids + 1) *
+				sizeof(struct scs_desc_elem)));
+		if (!new_desc_elems) {
+			status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::INVALID;
+			reports.push_back(status);
+			continue;
+		}
+
+		scs_data->scs_desc_elems = new_desc_elems;
+		os_memcpy((u8 *) scs_data->scs_desc_elems + num_scs_ids *
+			  sizeof(desc_elem), &desc_elem, sizeof(desc_elem));
+		num_scs_ids++;
+		scs_data->num_scs_desc = num_scs_ids;
+		status.qosPolicyScsRequestStatusCode = QosPolicyScsRequestStatusCode::SENT;
+		reports.push_back(status);
+	}
+	wpas_send_scs_req(wpa_s);
+
+	return {std::vector<QosPolicyScsRequestStatus>(reports),
+		ndk::ScopedAStatus::ok()};
+}
+
 /**
  * Retrieve the underlying |wpa_supplicant| struct
  * pointer for this iface.
diff --git a/wpa_supplicant/aidl/sta_iface.h b/wpa_supplicant/aidl/sta_iface.h
index 0ed29d8..db91445 100644
--- a/wpa_supplicant/aidl/sta_iface.h
+++ b/wpa_supplicant/aidl/sta_iface.h
@@ -157,6 +157,14 @@
 		const std::vector<QosPolicyStatus>& in_qosPolicyStatusList) override;
 	::ndk::ScopedAStatus removeAllQosPolicies() override;
 	::ndk::ScopedAStatus getConnectionMloLinksInfo(MloLinksInfo* _aidl_return) override;
+	::ndk::ScopedAStatus getSignalPollResults(
+		std::vector<SignalPollResult>* results) override;
+	::ndk::ScopedAStatus addQosPolicyRequestForScs(
+		const std::vector<QosPolicyScsData>& in_qosPolicyData,
+		std::vector<QosPolicyScsRequestStatus>* _aidl_return) override;
+	::ndk::ScopedAStatus removeQosPolicyForScs(
+		const std::vector<uint8_t>& in_scsPolicyIds,
+		std::vector<QosPolicyScsRequestStatus>* _aidl_return) override;
 
 private:
 	// Corresponding worker functions for the AIDL methods.
@@ -261,6 +269,15 @@
 		const std::vector<QosPolicyStatus>& qos_policy_status_list);
 	ndk::ScopedAStatus removeAllQosPoliciesInternal();
 	std::pair<MloLinksInfo, ndk::ScopedAStatus> getConnectionMloLinksInfoInternal();
+	std::pair<std::vector<SignalPollResult>, ndk::ScopedAStatus>
+		getSignalPollResultsInternal();
+	std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+		addQosPolicyRequestForScsInternal(
+		const std::vector<QosPolicyScsData>& qosPolicyData);
+	std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+		removeQosPolicyForScsInternal(
+		const std::vector<uint8_t>& scsPolicyIds);
+
 	struct wpa_supplicant* retrieveIfacePtr();
 
 	// Reference to the global wpa_struct. This is assumed to be valid for
diff --git a/wpa_supplicant/aidl/sta_network.cpp b/wpa_supplicant/aidl/sta_network.cpp
index 5b4a3e3..bb3045c 100644
--- a/wpa_supplicant/aidl/sta_network.cpp
+++ b/wpa_supplicant/aidl/sta_network.cpp
@@ -14,6 +14,7 @@
 extern "C"
 {
 #include "wps_supplicant.h"
+#include "crypto/tls.h"
 }
 
 namespace {
@@ -121,7 +122,9 @@
 	  ifname_(ifname),
 	  network_id_(network_id),
 	  is_valid_(true)
-{}
+{
+	tlsFlags = 0;
+}
 
 void StaNetwork::invalidate() { is_valid_ = false; }
 bool StaNetwork::isValid()
@@ -304,6 +307,15 @@
 		in_identity);
 }
 
+::ndk::ScopedAStatus StaNetwork::setStrictConservativePeerMode(
+	bool in_enable)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+		&StaNetwork::setStrictConservativePeerModeInternal,
+		in_enable);
+}
+
 ::ndk::ScopedAStatus StaNetwork::setEapAnonymousIdentity(
 	const std::vector<uint8_t>& in_identity)
 {
@@ -871,6 +883,14 @@
 		&StaNetwork::setRoamingConsortiumSelectionInternal, in_selectedRcoi);
 }
 
+::ndk::ScopedAStatus StaNetwork::setMinimumTlsVersionEapPhase1Param(
+	TlsVersion in_tlsVersion)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+		&StaNetwork::setMinimumTlsVersionEapPhase1ParamInternal, in_tlsVersion);
+}
+
 std::pair<uint32_t, ndk::ScopedAStatus> StaNetwork::getIdInternal()
 {
 	return {network_id_, ndk::ScopedAStatus::ok()};
@@ -1238,6 +1258,13 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus StaNetwork::setStrictConservativePeerModeInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->eap.strict_conservative_peer_mode = enable ? 1 : 0;
+	return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus StaNetwork::setEapAnonymousIdentityInternal(
 	const std::vector<uint8_t> &identity)
 {
@@ -2009,10 +2036,18 @@
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	int val = enable == true ? 1 : 0;
-	std::string suiteb_phase1("tls_suiteb=" + std::to_string(val));
+	if (enable) {
+		setTlsFlagsFor192BitMode(true /*rsaMode */);
+	} else {
+		tlsFlags &= ~TLS_CONN_SUITEB;
+	}
+	std::string phase1_params("tls_suiteb=" + std::to_string(val));
+	if (wpa_ssid->eap.phase1 != NULL) {
+		phase1_params.append(wpa_ssid->eap.phase1);
+	}
 
 	if (setStringKeyFieldAndResetState(
-		suiteb_phase1.c_str(), &(wpa_ssid->eap.phase1), "phase1")) {
+		phase1_params.c_str(), &(wpa_ssid->eap.phase1), "phase1")) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
 	return ndk::ScopedAStatus::ok();
@@ -2028,6 +2063,7 @@
 		"openssl_ciphers")) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
+	setTlsFlagsFor192BitMode(false /*rsaMode */);
 	return ndk::ScopedAStatus::ok();
 }
 
@@ -2135,7 +2171,11 @@
 	std::stringstream ss(
 		std::stringstream::in | std::stringstream::out | std::stringstream::binary);
 	ss.write((char *) serializedEntry.data(), std::streamsize(serializedEntry.size()));
-	misc_utils::deserializePmkCacheEntry(ss, new_entry);
+	if (misc_utils::deserializePmkCacheEntry(ss, new_entry) < 0) {
+		os_free(new_entry);
+		return createStatusWithMsg(SupplicantStatusCode::FAILURE_ARGS_INVALID,
+		 "Invalid pmk length");
+	}
 	new_entry->network_ctx = wpa_ssid;
 
 	// If there is an entry has a later expiration, ignore this one.
@@ -2156,10 +2196,19 @@
 	KeyMgmtMask mask)
 {
 	uint32_t key_mgmt_mask = static_cast<uint32_t>(mask);
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (key_mgmt_mask & ~kAllowedKeyMgmtMask) {
 		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
 	}
+#ifdef CONFIG_SAE
+	struct wpa_driver_capa capa;
+	int res = wpa_drv_get_capa(wpa_s, &capa);
+	if ((res == 0) && (key_mgmt_mask & WPA_KEY_MGMT_SAE) &&
+		(capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY)) {
+		key_mgmt_mask |= WPA_KEY_MGMT_SAE_EXT_KEY;
+	}
+#endif
 	setFastTransitionKeyMgmt(key_mgmt_mask);
 
 	if (key_mgmt_mask & WPA_KEY_MGMT_OWE) {
@@ -2497,6 +2546,11 @@
 			(capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE)) {
 			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE;
 		}
+		if ((key_mgmt_mask & WPA_KEY_MGMT_SAE_EXT_KEY) &&
+			(capa.key_mgmt_iftype[WPA_IF_STATION] &
+			    WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+		}
 #endif
 #ifdef CONFIG_FILS
 		if ((key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA256) &&
@@ -2580,13 +2634,13 @@
 	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
 	switch (mode) {
 	case SaeH2eMode::DISABLED:
-		wpa_s->conf->sae_pwe = 0;
+		wpa_s->conf->sae_pwe = SAE_PWE_HUNT_AND_PECK;
 		break;
 	case SaeH2eMode::H2E_MANDATORY:
-		wpa_s->conf->sae_pwe = 1;
+		wpa_s->conf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
 		break;
 	case SaeH2eMode::H2E_OPTIONAL:
-		wpa_s->conf->sae_pwe = 2;
+		wpa_s->conf->sae_pwe = SAE_PWE_BOTH;
 		break;
 	}
 	resetInternalStateAfterParamsUpdate();
@@ -2605,6 +2659,109 @@
 #endif
 }
 
+ndk::ScopedAStatus StaNetwork::setMinimumTlsVersionEapPhase1ParamInternal(TlsVersion tlsVersion)
+{
+	if (tlsVersion < TlsVersion::TLS_V1_0 || tlsVersion > TlsVersion::TLS_V1_3) {
+		return createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID);
+	}
+	if (tlsVersion == TlsVersion::TLS_V1_0) {
+		// no restriction
+		return ndk::ScopedAStatus::ok();
+	}
+
+	if (tlsVersion < TlsVersion::TLS_V1_3 && (tlsFlags & TLS_CONN_SUITEB)) {
+		// TLS configuration already set up for WPA3-Enterprise 192-bit mode
+		return ndk::ScopedAStatus::ok();
+	}
+
+	tlsFlags &= ~(TLS_CONN_DISABLE_TLSv1_3 | TLS_CONN_DISABLE_TLSv1_2 \
+			| TLS_CONN_DISABLE_TLSv1_1 | TLS_CONN_DISABLE_TLSv1_0);
+
+	// Fallback to disable lower version TLS cascadingly.
+	switch (tlsVersion) {
+#ifdef EAP_TLSV1_3
+		case TlsVersion::TLS_V1_3:
+			tlsFlags |= TLS_CONN_DISABLE_TLSv1_2;
+			FALLTHROUGH_INTENDED;
+#endif
+		case TlsVersion::TLS_V1_2:
+			tlsFlags |= TLS_CONN_DISABLE_TLSv1_1;
+			FALLTHROUGH_INTENDED;
+		case TlsVersion::TLS_V1_1:
+			tlsFlags |= TLS_CONN_DISABLE_TLSv1_0;
+			FALLTHROUGH_INTENDED;
+		default:
+			break;
+	}
+
+	generateTlsParams();
+	return ndk::ScopedAStatus::ok();
+}
+
+/**
+ * WPA3-Enterprise 192-bit mode workaround to force the connection to EAP-TLSv1.2 due to
+ * interoperability issues in TLSv1.3 which disables the SSL_SIGN_RSA_PKCS1_SHA384
+ * signature algorithm, and has its own set of incompatible cipher suites which the
+ * current WPA3 specification doesn't specify. The only specified cipher suites in the
+ * WPA3 specifications are:
+ * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, and
+ * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384.
+ * See boringssl/include/openssl/tls1.h for TLSv1.3 cipher suites.
+ */
+void StaNetwork::setTlsFlagsFor192BitMode(bool rsaMode) {
+	// Disable TLSv1.0 and TLSv1.1 by default for 192-bit mode
+	int flags = TLS_CONN_DISABLE_TLSv1_1 \
+			| TLS_CONN_DISABLE_TLSv1_0;
+	if (rsaMode) {
+		// Check if flags not set or already set to use EAP-TLSv1.3
+		if (tlsFlags == 0 || !(tlsFlags & TLS_CONN_DISABLE_TLSv1_2)) {
+			// Set up EAP-TLSv1.2 by default for maximum compatibility
+			tlsFlags |= TLS_CONN_DISABLE_TLSv1_3;
+			tlsFlags &= ~TLS_CONN_DISABLE_TLSv1_2;
+		}
+		tlsFlags |= TLS_CONN_SUITEB;
+	}
+
+	tlsFlags |= flags;
+	generateTlsParams();
+}
+
+void StaNetwork::generateTlsParams() {
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (wpa_ssid->eap.phase1 != NULL) {
+		os_free(wpa_ssid->eap.phase1);
+		wpa_ssid->eap.phase1 = NULL;
+	}
+	std::string tlsConfig;
+
+	if (tlsFlags & TLS_CONN_DISABLE_TLSv1_3) {
+		tlsConfig.append("tls_disable_tlsv1_3=1");
+	} else {
+		tlsConfig.append("tls_disable_tlsv1_3=0");
+	}
+	if (tlsFlags & TLS_CONN_DISABLE_TLSv1_2) {
+		tlsConfig.append("tls_disable_tlsv1_2=1");
+	} else {
+		tlsConfig.append("tls_disable_tlsv1_2=0");
+	}
+	if (tlsFlags & TLS_CONN_DISABLE_TLSv1_1) {
+		tlsConfig.append("tls_disable_tlsv1_1=1");
+	} else {
+		tlsConfig.append("tls_disable_tlsv1_1=0");
+	}
+	if (tlsFlags & TLS_CONN_DISABLE_TLSv1_0) {
+		tlsConfig.append("tls_disable_tlsv1_0=1");
+	} else {
+		tlsConfig.append("tls_disable_tlsv1_0=0");
+	}
+	if (tlsFlags & TLS_CONN_SUITEB) {
+		tlsConfig.append("tls_suiteb=1");
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS configuration: %s", tlsConfig.c_str());
+	setStringKeyFieldAndResetState(
+			tlsConfig.c_str(), &(wpa_ssid->eap.phase1), "phase1");
+}
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/aidl/sta_network.h b/wpa_supplicant/aidl/sta_network.h
index 524f44a..1c24702 100644
--- a/wpa_supplicant/aidl/sta_network.h
+++ b/wpa_supplicant/aidl/sta_network.h
@@ -22,6 +22,7 @@
 #include <aidl/android/hardware/wifi/supplicant/NetworkResponseEapSimUmtsAuthParams.h>
 #include <aidl/android/hardware/wifi/supplicant/SaeH2eMode.h>
 #include <aidl/android/hardware/wifi/supplicant/DppConnectionKeys.h>
+#include <aidl/android/hardware/wifi/supplicant/TlsVersion.h>
 
 extern "C"
 {
@@ -84,6 +85,8 @@
 		const std::vector<uint8_t>& in_identity) override;
 	::ndk::ScopedAStatus setEapEncryptedImsiIdentity(
 		const std::vector<uint8_t>& in_identity) override;
+	::ndk::ScopedAStatus setStrictConservativePeerMode(
+		bool in_enable) override;
 	::ndk::ScopedAStatus setEapAnonymousIdentity(
 		const std::vector<uint8_t>& in_identity) override;
 	::ndk::ScopedAStatus setEapPassword(
@@ -173,6 +176,8 @@
 	::ndk::ScopedAStatus enableSaePkOnlyMode(bool in_enable) override;
 	::ndk::ScopedAStatus setRoamingConsortiumSelection(
 		const std::vector<uint8_t>& in_selectedRcoi) override;
+	::ndk::ScopedAStatus setMinimumTlsVersionEapPhase1Param(
+		TlsVersion in_tlsVersion) override;
 
 private:
 	// Corresponding worker functions for the AIDL methods.
@@ -209,6 +214,8 @@
 		const std::vector<uint8_t>& identity);
 	ndk::ScopedAStatus setEapEncryptedImsiIdentityInternal(
 		const std::vector<uint8_t>& identity);
+	ndk::ScopedAStatus setStrictConservativePeerModeInternal(
+		bool enable);
 	ndk::ScopedAStatus setEapAnonymousIdentityInternal(
 		const std::vector<uint8_t>& identity);
 	ndk::ScopedAStatus setEapPasswordInternal(
@@ -302,6 +309,7 @@
 	ndk::ScopedAStatus enableSaePkOnlyModeInternal(bool enable);
 	ndk::ScopedAStatus setRoamingConsortiumSelectionInternal(
 		const std::vector<uint8_t>& selectedRcoi);
+	ndk::ScopedAStatus setMinimumTlsVersionEapPhase1ParamInternal(TlsVersion tlsVersion);
 
 	struct wpa_ssid* retrieveNetworkPtr();
 	struct wpa_supplicant* retrieveIfacePtr();
@@ -331,6 +339,8 @@
 		const uint8_t* value, const size_t value_len,
 		uint8_t** to_update_field, size_t* to_update_field_len,
 		const char* hexdump_prefix, bool resetState);
+	void setTlsFlagsFor192BitMode(bool);
+	void generateTlsParams();
 
 	// Reference to the global wpa_struct. This is assumed to be valid
 	// for the lifetime of the process.
@@ -340,6 +350,7 @@
 	// Id of the network this aidl object controls.
 	const int network_id_;
 	bool is_valid_;
+	int tlsFlags;
 
 	DISALLOW_COPY_AND_ASSIGN(StaNetwork);
 };
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 799790b..74602e4 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -239,6 +239,14 @@
 		&Supplicant::registerCallbackInternal, in_callback);
 }
 
+::ndk::ScopedAStatus Supplicant::registerNonStandardCertCallback(
+	const std::shared_ptr<INonStandardCertCallback>& in_callback)
+{
+	return validateAndCall(
+		this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+		&Supplicant::registerNonStandardCertCallbackInternal, in_callback);
+}
+
 ::ndk::ScopedAStatus Supplicant::setDebugParams(
 	DebugLevel in_level, bool in_showTimestamp,
 	bool in_showKeys)
@@ -547,6 +555,17 @@
 	return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Supplicant::registerNonStandardCertCallbackInternal(
+	const std::shared_ptr<INonStandardCertCallback>& callback)
+{
+	AidlManager* aidl_manager = AidlManager::getInstance();
+	if (!aidl_manager ||
+		aidl_manager->registerNonStandardCertCallbackAidlObject(callback)) {
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+	return ndk::ScopedAStatus::ok();
+}
+
 ndk::ScopedAStatus Supplicant::setDebugParamsInternal(
 	DebugLevel level, bool show_timestamp, bool show_keys)
 {
diff --git a/wpa_supplicant/aidl/supplicant.h b/wpa_supplicant/aidl/supplicant.h
index cbe9a67..12b9299 100644
--- a/wpa_supplicant/aidl/supplicant.h
+++ b/wpa_supplicant/aidl/supplicant.h
@@ -64,6 +64,8 @@
 		std::vector<IfaceInfo>* _aidl_return) override;
 	::ndk::ScopedAStatus registerCallback(
 		const std::shared_ptr<ISupplicantCallback>& in_callback) override;
+	::ndk::ScopedAStatus registerNonStandardCertCallback(
+		const std::shared_ptr<INonStandardCertCallback>& in_callback) override;
 	::ndk::ScopedAStatus setDebugParams(
 		DebugLevel in_level, bool in_showTimestamp, bool in_showKeys) override;
 	::ndk::ScopedAStatus getDebugLevel(DebugLevel* _aidl_return) override;
@@ -82,11 +84,13 @@
 		getP2pInterfaceInternal(const std::string& name);
 	std::pair<std::shared_ptr<ISupplicantStaIface>, ndk::ScopedAStatus>
 		getStaInterfaceInternal(const std::string& name);
-	
+
 	ndk::ScopedAStatus removeInterfaceInternal(const IfaceInfo& iface_info);
 	std::pair<std::vector<IfaceInfo>, ndk::ScopedAStatus> listInterfacesInternal();
 	ndk::ScopedAStatus registerCallbackInternal(
 		const std::shared_ptr<ISupplicantCallback>& callback);
+	ndk::ScopedAStatus registerNonStandardCertCallbackInternal(
+		const std::shared_ptr<INonStandardCertCallback>& callback);
 	ndk::ScopedAStatus setDebugParamsInternal(
 		DebugLevel level, bool show_timestamp, bool show_keys);
 	ndk::ScopedAStatus setConcurrencyPriorityInternal(IfaceType type);
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 52e4c04..bfdd53e 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -80,6 +80,7 @@
 
 # EAP-TLS
 CONFIG_EAP_TLS=y
+CONFIG_EAP_TLSV1_3=y
 
 # EAL-PEAP
 CONFIG_EAP_PEAP=y
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 94f0f83..9a2598b 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -90,7 +90,7 @@
 					      &conf->op_class,
 					      &conf->channel);
 
-	if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) {
+	if (hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_80P80MHZ) {
 		ieee80211_freq_to_chan(ssid->vht_center_freq2,
 				       &freq_seg_idx);
 		hostapd_set_oper_centr_freq_seg1_idx(conf, freq_seg_idx);
@@ -112,15 +112,15 @@
 
 #ifdef CONFIG_P2P
 	switch (hostapd_get_oper_chwidth(conf)) {
-	case CHANWIDTH_80MHZ:
-	case CHANWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
 		center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel,
 							conf->op_class);
 		wpa_printf(MSG_DEBUG,
 			   "VHT center channel %u for 80 or 80+80 MHz bandwidth",
 			   center_chan);
 		break;
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel,
 							 conf->op_class);
 		wpa_printf(MSG_DEBUG,
@@ -133,7 +133,7 @@
 		 * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
 		 * not supported.
 		 */
-		hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_160MHZ);
 		ieee80211_freq_to_channel_ext(ssid->frequency, 0,
 					      conf->vht_oper_chwidth,
 					      &conf->op_class,
@@ -145,7 +145,7 @@
 				   "VHT center channel %u for auto-selected 160 MHz bandwidth",
 				   center_chan);
 		} else {
-			hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
+			hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_80MHZ);
 			ieee80211_freq_to_channel_ext(ssid->frequency, 0,
 						      conf->vht_oper_chwidth,
 						      &conf->op_class,
@@ -174,11 +174,10 @@
 		   conf->channel);
 	hostapd_set_oper_centr_freq_seg0_idx(
 		conf, conf->channel + conf->secondary_channel * 2);
-	hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
+	hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_USE_HT);
 	ieee80211_freq_to_channel_ext(ssid->frequency, 0,
-		   conf->vht_oper_chwidth,
-		   &conf->op_class,
-		   &conf->channel);
+				      conf->vht_oper_chwidth,
+				      &conf->op_class, &conf->channel);
 }
 
 
@@ -205,14 +204,14 @@
 static int get_max_oper_chwidth_6ghz(int chwidth)
 {
 	switch (chwidth) {
-	case CHANWIDTH_USE_HT:
+	case CONF_OPER_CHWIDTH_USE_HT:
 		return 20;
-	case CHANWIDTH_40MHZ_6GHZ:
+	case CONF_OPER_CHWIDTH_40MHZ_6GHZ:
 		return 40;
-	case CHANWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_80MHZ:
 		return 80;
-	case CHANWIDTH_80P80MHZ:
-	case CHANWIDTH_160MHZ:
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
 		return 160;
 	default:
 		return 0;
@@ -253,8 +252,8 @@
 		wpa_printf(MSG_DEBUG,
 			   "Secondary channel offset %d for P2P group",
 			   conf->secondary_channel);
-		if (ssid->max_oper_chwidth == CHANWIDTH_40MHZ_6GHZ)
-			ssid->max_oper_chwidth = CHANWIDTH_USE_HT;
+		if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_40MHZ_6GHZ)
+			ssid->max_oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
 	}
 
 	if ((is_chanwidth_40_80 || is_chanwidth_160) && ssid->p2p_group &&
@@ -270,7 +269,7 @@
 			      struct hostapd_config *conf)
 {
 	conf->hw_mode = ieee80211_freq_to_channel_ext(ssid->frequency, 0,
-						      CHANWIDTH_USE_HT,
+						      CONF_OPER_CHWIDTH_USE_HT,
 						      &conf->op_class,
 						      &conf->channel);
 	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
@@ -327,6 +326,16 @@
 
 		if (mode && is_6ghz_freq(ssid->frequency) &&
 		    conf->hw_mode == HOSTAPD_MODE_IEEE80211A) {
+			if (mode->eht_capab[wpas_mode_to_ieee80211_mode(
+					    ssid->mode)].eht_supported &&
+			    ssid->eht)
+				conf->ieee80211be = 1;
+
+			if (mode->he_capab[wpas_mode_to_ieee80211_mode(
+					    ssid->mode)].he_supported &&
+			    ssid->he)
+				conf->ieee80211ax = 1;
+
 #ifdef CONFIG_P2P
 			wpas_conf_ap_he_6ghz(wpa_s, mode, ssid, conf);
 #endif /* CONFIG_P2P */
@@ -401,6 +410,11 @@
 			/* check this before VHT, because setting oper chan
 			 * width and friends is the same call for HE and VHT
 			 * and checks if conf->ieee8021ax == 1 */
+			if (mode->eht_capab[wpas_mode_to_ieee80211_mode(
+					    ssid->mode)].eht_supported &&
+			    ssid->eht)
+				conf->ieee80211be = 1;
+
 			if (mode->he_capab[wpas_mode_to_ieee80211_mode(
 					    ssid->mode)].he_supported &&
 			    ssid->he)
@@ -414,7 +428,9 @@
 		}
 	}
 
-	if (conf->secondary_channel) {
+	if (wpa_s->p2p_go_no_pri_sec_switch) {
+		conf->no_pri_sec_switch = 1;
+	} else if (conf->secondary_channel) {
 		struct wpa_supplicant *iface;
 
 		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
@@ -705,8 +721,12 @@
 		bss->wpa_group_rekey = 86400;
 	}
 
-	if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+	if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT) {
 		bss->ieee80211w = ssid->ieee80211w;
+	} else if (wpa_s->conf->pmf != MGMT_FRAME_PROTECTION_DEFAULT) {
+		if (ssid->mode == WPAS_MODE_AP)
+			bss->ieee80211w = wpa_s->conf->pmf;
+	}
 
 #ifdef CONFIG_OCV
 	bss->ocv = ssid->ocv;
@@ -1238,9 +1258,11 @@
 
 
 void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
-				const u8 *src_addr, const u8 *buf, size_t len)
+				const u8 *src_addr, const u8 *buf, size_t len,
+				enum frame_encryption encrypted)
 {
-	ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len);
+	ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len,
+			   encrypted);
 }
 
 
@@ -1819,7 +1841,8 @@
 
 
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-		       int offset, int width, int cf1, int cf2, int finished)
+		       int offset, int width, int cf1, int cf2,
+		       u16 punct_bitmap, int finished)
 {
 	struct hostapd_iface *iface = wpa_s->ap_iface;
 
@@ -1831,7 +1854,8 @@
 	if (wpa_s->current_ssid)
 		wpa_s->current_ssid->frequency = freq;
 	hostapd_event_ch_switch(iface->bss[0], freq, ht,
-				offset, width, cf1, cf2, finished);
+				offset, width, cf1, cf2, punct_bitmap,
+				finished);
 }
 
 
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index ccd3e7b..5835ecd 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -16,7 +16,8 @@
 			     struct wpa_ssid *ssid);
 void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
-				const u8 *src_addr, const u8 *buf, size_t len);
+				const u8 *src_addr, const u8 *buf, size_t len,
+				enum frame_encryption encrypted);
 int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			      const u8 *p2p_dev_addr);
 int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
@@ -72,7 +73,8 @@
 		      struct csa_settings *settings);
 int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-		       int offset, int width, int cf1, int cf2, int finished);
+		       int offset, int width, int cf1, int cf2,
+		       u16 punct_bitmap, int finished);
 struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					     int ndef);
 #ifdef CONFIG_AP
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index cb732f7..75bdec1 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -422,7 +422,7 @@
 		/* Poll for signal info to set initial scan interval */
 		struct wpa_signal_info siginfo;
 		if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
-		    siginfo.current_signal >= data->signal_threshold)
+		    siginfo.data.signal >= data->signal_threshold)
 			data->scan_interval = data->long_interval;
 	}
 
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index 41a26df..5a8f97c 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -137,7 +137,7 @@
 		/* Poll for signal info to set initial scan interval */
 		struct wpa_signal_info siginfo;
 		if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
-		    siginfo.current_signal >= data->signal_threshold)
+		    siginfo.data.signal >= data->signal_threshold)
 			data->scan_interval = data->long_interval;
 	}
 	wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 429c6e7..3204414 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -240,7 +240,7 @@
 /**
  * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
  * @wpa_s: Pointer to wpa_supplicant data
- * @bssid: BSSID
+ * @bssid: BSSID, or %NULL to match any BSSID
  * @ssid: SSID
  * @ssid_len: Length of @ssid
  * Returns: Pointer to the BSS entry or %NULL if not found
@@ -249,10 +249,11 @@
 			     const u8 *ssid, size_t ssid_len)
 {
 	struct wpa_bss *bss;
-	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+
+	if (bssid && !wpa_supplicant_filter_bssid_match(wpa_s, bssid))
 		return NULL;
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+		if ((!bssid || os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) &&
 		    bss->ssid_len == ssid_len &&
 		    os_memcmp(bss->ssid, ssid, ssid_len) == 0)
 			return bss;
@@ -379,6 +380,8 @@
 
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
+	int i;
+
 	if (bss == wpa_s->current_bss)
 		return 1;
 
@@ -388,9 +391,23 @@
 		       bss->ssid_len) != 0))
 		return 0; /* SSID has changed */
 
-	return !is_zero_ether_addr(bss->bssid) &&
-		(os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
+	if (!is_zero_ether_addr(bss->bssid) &&
+	    (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+	     os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0))
+		return 1;
+
+	if (!wpa_s->valid_links)
+		return 0;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
 }
 
 
@@ -441,7 +458,11 @@
 				    struct os_reltime *fetch_time)
 {
 	struct wpa_bss *bss;
-	char extra[50];
+	char extra[100];
+	const u8 *ml_ie;
+	char *pos, *end;
+	int ret = 0;
+	const u8 *mld_addr;
 
 	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
 	if (bss == NULL)
@@ -456,6 +477,14 @@
 	os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
 	wpa_bss_set_hessid(bss);
 
+	os_memset(bss->mld_addr, 0, ETH_ALEN);
+	ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (ml_ie) {
+		mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
+		if (mld_addr)
+			os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
+	}
+
 	if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
 	    wpa_bss_remove_oldest(wpa_s) != 0) {
 		wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
@@ -467,11 +496,21 @@
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
 	wpa_s->num_bss++;
+
+	extra[0] = '\0';
+	pos = extra;
+	end = pos + sizeof(extra);
 	if (!is_zero_ether_addr(bss->hessid))
-		os_snprintf(extra, sizeof(extra), " HESSID " MACSTR,
-			    MAC2STR(bss->hessid));
-	else
-		extra[0] = '\0';
+		ret = os_snprintf(pos, end - pos, " HESSID " MACSTR,
+				  MAC2STR(bss->hessid));
+
+	if (!is_zero_ether_addr(bss->mld_addr) &&
+	    !os_snprintf_error(end - pos, ret)) {
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, " MLD ADDR " MACSTR,
+				  MAC2STR(bss->mld_addr));
+	}
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
 		" SSID '%s' freq %d%s",
 		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
@@ -708,8 +747,19 @@
 		}
 		dl_list_add(prev, &bss->list_id);
 	}
-	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+	if (changes & WPA_BSS_IES_CHANGED_FLAG) {
+		const u8 *ml_ie, *mld_addr;
+
 		wpa_bss_set_hessid(bss);
+		os_memset(bss->mld_addr, 0, ETH_ALEN);
+		ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
+		if (ml_ie) {
+			mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
+							  ml_ie[1] - 1);
+			if (mld_addr)
+				os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
+		}
+	}
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 
 	notify_bss_changes(wpa_s, changes, bss);
@@ -937,12 +987,24 @@
 		return;
 
 	os_get_reltime(&t);
+
+	if (t.sec < age)
+		return; /* avoid underflow; there can be no older entries */
+
 	t.sec -= age;
 
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_in_use(wpa_s, bss))
 			continue;
 
+		if (wpa_s->reassoc_same_ess &&
+		    wpa_s->wpa_state != WPA_COMPLETED &&
+		    wpa_s->last_ssid &&
+		    bss->ssid_len == wpa_s->last_ssid->ssid_len &&
+		    os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
+			      bss->ssid_len) == 0)
+			continue;
+
 		if (os_reltime_before(&bss->last_update, &t)) {
 			wpa_bss_remove(wpa_s, bss, __func__);
 		} else
@@ -1281,12 +1343,16 @@
 	end = pos + bss->beacon_ie_len;
 
 	while (end - pos > 1) {
-		if (2 + pos[1] > end - pos)
+		u8 id, len;
+
+		id = *pos++;
+		len = *pos++;
+		if (len > end - pos)
 			break;
-		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
-		    vendor_type == WPA_GET_BE32(&pos[2]))
-			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
-		pos += 2 + pos[1];
+		if (id == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
+		    vendor_type == WPA_GET_BE32(pos))
+			wpabuf_put_data(buf, pos + 4, len - 4);
+		pos += len;
 	}
 
 	if (wpabuf_len(buf) == 0) {
@@ -1384,3 +1450,21 @@
 	return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
 				    capab);
 }
+
+
+/**
+ * wpa_bss_defrag_mle - Get a buffer holding a de-fragmented ML element
+ * @bss: BSS table entry
+ * @type: ML control type
+ */
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type)
+{
+	struct ieee802_11_elems elems;
+	const u8 *pos = wpa_bss_ie_ptr(bss);
+	size_t len = bss->ie_len;
+
+	if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+		return NULL;
+
+	return ieee802_11_defrag_mle(&elems, type);
+}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 146aaee..611da88 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -122,6 +122,8 @@
 	size_t ie_len;
 	/** Length of the following Beacon IE field in octets */
 	size_t beacon_ie_len;
+	/** MLD address of the AP */
+	u8 mld_addr[ETH_ALEN];
 	/* followed by ie_len octets of IEs */
 	/* followed by beacon_ie_len octets of IEs */
 	u8 ies[];
@@ -199,4 +201,6 @@
 			   unsigned int age_ms,
 			   struct os_reltime *update_time);
 
+struct wpabuf * wpa_bss_defrag_mle(const struct wpa_bss *bss, u8 type);
+
 #endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index c8844bb..a5d4d81 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -804,8 +804,12 @@
 #ifdef CONFIG_SAE
 		else if (os_strcmp(start, "SAE") == 0)
 			val |= WPA_KEY_MGMT_SAE;
+		else if (os_strcmp(start, "SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_SAE_EXT_KEY;
 		else if (os_strcmp(start, "FT-SAE") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE;
+		else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0)
+			val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_HS20
 		else if (os_strcmp(start, "OSEN") == 0)
@@ -1004,6 +1008,16 @@
 		pos += ret;
 	}
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sSAE-EXT-KEY",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
 	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
 		ret = os_snprintf(pos, end - pos, "%sFT-SAE",
 				  pos == buf ? "" : " ");
@@ -1013,6 +1027,16 @@
 		}
 		pos += ret;
 	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sFT-SAE-EXT-KEY",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_HS20
@@ -2321,6 +2345,50 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_mac_value(const struct parse_data *data,
+				      struct wpa_ssid *ssid, int line,
+				      const char *value)
+{
+	u8 mac_value[ETH_ALEN];
+
+	if (hwaddr_aton(value, mac_value) == 0) {
+		if (os_memcmp(mac_value, ssid->mac_value, ETH_ALEN) == 0)
+			return 1;
+		os_memcpy(ssid->mac_value, mac_value, ETH_ALEN);
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR, "Line %d: Invalid MAC address '%s'",
+		   line, value);
+	return -1;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_mac_value(const struct parse_data *data,
+					 struct wpa_ssid *ssid)
+{
+	const size_t size = 3 * ETH_ALEN;
+	char *value;
+	int res;
+
+	if (ssid->mac_addr != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS)
+		return NULL;
+
+	value = os_malloc(size);
+	if (!value)
+		return NULL;
+	res = os_snprintf(value, size, MACSTR, MAC2STR(ssid->mac_value));
+	if (os_snprintf_error(size, res)) {
+		os_free(value);
+		return NULL;
+	}
+	value[size - 1] = '\0';
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -2434,8 +2502,8 @@
 	{ INT_RANGE(ht, 0, 1) },
 	{ INT_RANGE(vht, 0, 1) },
 	{ INT_RANGE(ht40, -1, 1) },
-	{ INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT,
-		    CHANWIDTH_80P80MHZ) },
+	{ INT_RANGE(max_oper_chwidth, CONF_OPER_CHWIDTH_USE_HT,
+		    CONF_OPER_CHWIDTH_80P80MHZ) },
 	{ INT(vht_center_freq1) },
 	{ INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
@@ -2503,7 +2571,9 @@
 	{ INTe(machine_ocsp, machine_cert.ocsp) },
 	{ INT(eapol_flags) },
 	{ INTe(sim_num, sim_num) },
-	{ STRe(imsi_privacy_key, imsi_privacy_key) },
+	{ STRe(imsi_privacy_cert, imsi_privacy_cert) },
+	{ STRe(imsi_privacy_attr, imsi_privacy_attr) },
+	{ INTe(strict_conservative_peer_mode, strict_conservative_peer_mode) },
 	{ STRe(openssl_ciphers, openssl_ciphers) },
 	{ INTe(erp, erp) },
 #endif /* IEEE8021X_EAPOL */
@@ -2608,6 +2678,7 @@
 	{ INT_RANGE(macsec_integ_only, 0, 1) },
 	{ INT_RANGE(macsec_replay_protect, 0, 1) },
 	{ INT(macsec_replay_window) },
+	{ INT_RANGE(macsec_offload, 0, 2) },
 	{ INT_RANGE(macsec_port, 1, 65534) },
 	{ INT_RANGE(mka_priority, 0, 255) },
 	{ INT_RANGE(macsec_csindex, 0, 1) },
@@ -2618,7 +2689,8 @@
 	{ INT(update_identifier) },
 	{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
 #endif /* CONFIG_HS20 */
-	{ INT_RANGE(mac_addr, 0, 2) },
+	{ INT_RANGE(mac_addr, 0, 3) },
+	{ FUNC_KEY(mac_value) },
 	{ INT_RANGE(pbss, 0, 2) },
 	{ INT_RANGE(wps_disabled, 0, 1) },
 	{ INT_RANGE(fils_dh_group, 0, 65535) },
@@ -2629,6 +2701,7 @@
 	{ STR_LEN(dpp_csign) },
 	{ STR_LEN(dpp_pp_key) },
 	{ INT_RANGE(dpp_pfs, 0, 2) },
+	{ INT_RANGE(dpp_connector_privacy, 0, 1) },
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(owe_group, 0, 65535) },
 	{ INT_RANGE(owe_only, 0, 1) },
@@ -2638,6 +2711,8 @@
 	{ INT_RANGE(beacon_prot, 0, 1) },
 	{ INT_RANGE(transition_disable, 0, 255) },
 	{ INT_RANGE(sae_pk, 0, 2) },
+	{ INT_RANGE(disable_eht, 0, 1)},
+	{ INT_RANGE(enable_4addr_mode, 0, 1)},
 };
 
 #undef OFFSET
@@ -2771,7 +2846,8 @@
 	bin_clear_free(eap->identity, eap->identity_len);
 	os_free(eap->anonymous_identity);
 	os_free(eap->imsi_identity);
-	os_free(eap->imsi_privacy_key);
+	os_free(eap->imsi_privacy_cert);
+	os_free(eap->imsi_privacy_attr);
 	os_free(eap->machine_identity);
 	bin_clear_free(eap->password, eap->password_len);
 	bin_clear_free(eap->machine_password, eap->machine_password_len);
@@ -2875,7 +2951,8 @@
 		os_free(cred->req_conn_capab_port[i]);
 	os_free(cred->req_conn_capab_port);
 	os_free(cred->req_conn_capab_proto);
-	os_free(cred->imsi_privacy_key);
+	os_free(cred->imsi_privacy_cert);
+	os_free(cred->imsi_privacy_attr);
 	os_free(cred);
 }
 
@@ -2967,6 +3044,8 @@
 #endif /* CONFIG_MBO */
 	os_free(config->dpp_name);
 	os_free(config->dpp_mud_url);
+	os_free(config->dpp_extra_conf_req_name);
+	os_free(config->dpp_extra_conf_req_value);
 
 	os_free(config);
 }
@@ -3150,7 +3229,7 @@
 #ifdef CONFIG_MACSEC
 	ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
 #endif /* CONFIG_MACSEC */
-	ssid->mac_addr = -1;
+	ssid->mac_addr = WPAS_MAC_ADDR_STYLE_NOT_SET;
 	ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
 }
 
@@ -3501,53 +3580,62 @@
 }
 
 
-static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
-						   const char *value)
+static int
+wpa_config_set_cred_ois(u8 cred_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN],
+			size_t cred_ois_len[MAX_ROAMING_CONS],
+			unsigned int *cred_num_ois,
+			const char *value)
 {
-	u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
-	size_t roaming_consortiums_len[MAX_ROAMING_CONS];
-	unsigned int num_roaming_consortiums = 0;
+	u8 ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+	size_t ois_len[MAX_ROAMING_CONS];
+	unsigned int num_ois = 0;
 	const char *pos, *end;
 	size_t len;
 
-	os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
-	os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
+	len = os_strlen(value);
+	if (len / 2 < 3) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid organisation identifier (OI) list: %s",
+			   value);
+		return -1;
+	}
+
+	os_memset(ois, 0, sizeof(ois));
+	os_memset(ois_len, 0, sizeof(ois_len));
 
 	for (pos = value;;) {
 		end = os_strchr(pos, ',');
 		len = end ? (size_t) (end - pos) : os_strlen(pos);
 		if (!end && len == 0)
 			break;
-		if (len == 0 || (len & 1) != 0 ||
+		if (len / 2 < 3 || (len & 1) != 0 ||
 		    len / 2 > MAX_ROAMING_CONS_OI_LEN ||
 		    hexstr2bin(pos,
-			       roaming_consortiums[num_roaming_consortiums],
+			       ois[num_ois],
 			       len / 2) < 0) {
 			wpa_printf(MSG_INFO,
-				   "Invalid roaming_consortiums entry: %s",
+				   "Invalid organisation identifier (OI) entry: %s",
 				   pos);
 			return -1;
 		}
-		roaming_consortiums_len[num_roaming_consortiums] = len / 2;
-		num_roaming_consortiums++;
+		ois_len[num_ois] = len / 2;
+		num_ois++;
 
 		if (!end)
 			break;
 
-		if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
+		if (num_ois >= MAX_ROAMING_CONS) {
 			wpa_printf(MSG_INFO,
-				   "Too many roaming_consortiums OIs");
+				   "Too many OIs");
 			return -1;
 		}
 
 		pos = end + 1;
 	}
 
-	os_memcpy(cred->roaming_consortiums, roaming_consortiums,
-		  sizeof(roaming_consortiums));
-	os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
-		  sizeof(roaming_consortiums_len));
-	cred->num_roaming_consortiums = num_roaming_consortiums;
+	os_memcpy(cred_ois, ois, sizeof(ois));
+	os_memcpy(cred_ois_len, ois_len, sizeof(ois_len));
+	*cred_num_ois = num_ois;
 
 	return 0;
 }
@@ -3782,36 +3870,70 @@
 	}
 
 	if (os_strcmp(var, "roaming_consortium") == 0) {
-		if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+		if (len < 3 || len > sizeof(cred->home_ois[0])) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid "
 				   "roaming_consortium length %d (3..15 "
 				   "expected)", line, (int) len);
 			os_free(val);
 			return -1;
 		}
-		os_memcpy(cred->roaming_consortium, val, len);
-		cred->roaming_consortium_len = len;
+		wpa_printf(MSG_WARNING,
+			   "Line %d: option roaming_consortium is deprecated and will be removed in the future",
+			   line);
+		os_memcpy(cred->home_ois[0], val, len);
+		cred->home_ois_len[0] = len;
+		cred->num_home_ois = 1;
 		os_free(val);
 		return 0;
 	}
 
 	if (os_strcmp(var, "required_roaming_consortium") == 0) {
-		if (len < 3 || len > sizeof(cred->required_roaming_consortium))
-		{
+		if (len < 3 || len > sizeof(cred->required_home_ois[0])) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid "
 				   "required_roaming_consortium length %d "
 				   "(3..15 expected)", line, (int) len);
 			os_free(val);
 			return -1;
 		}
-		os_memcpy(cred->required_roaming_consortium, val, len);
-		cred->required_roaming_consortium_len = len;
+		wpa_printf(MSG_WARNING,
+			   "Line %d: option required_roaming_consortium is deprecated and will be removed in the future",
+			   line);
+		os_memcpy(cred->required_home_ois[0], val, len);
+		cred->required_home_ois_len[0] = len;
+		cred->num_required_home_ois = 1;
 		os_free(val);
 		return 0;
 	}
 
+	if (os_strcmp(var, "home_ois") == 0) {
+		res = wpa_config_set_cred_ois(cred->home_ois,
+					      cred->home_ois_len,
+					      &cred->num_home_ois,
+					      val);
+		if (res < 0)
+			wpa_printf(MSG_ERROR, "Line %d: invalid home_ois",
+				   line);
+		os_free(val);
+		return res;
+	}
+
+	if (os_strcmp(var, "required_home_ois") == 0) {
+		res = wpa_config_set_cred_ois(cred->required_home_ois,
+					      cred->required_home_ois_len,
+					      &cred->num_required_home_ois,
+					      val);
+		if (res < 0)
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid required_home_ois", line);
+		os_free(val);
+		return res;
+	}
+
 	if (os_strcmp(var, "roaming_consortiums") == 0) {
-		res = wpa_config_set_cred_roaming_consortiums(cred, val);
+		res = wpa_config_set_cred_ois(cred->roaming_consortiums,
+					      cred->roaming_consortiums_len,
+					      &cred->num_roaming_consortiums,
+					      val);
 		if (res < 0)
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid roaming_consortiums",
@@ -3911,9 +4033,20 @@
 		return 0;
 	}
 
-	if (os_strcmp(var, "imsi_privacy_key") == 0) {
-		os_free(cred->imsi_privacy_key);
-		cred->imsi_privacy_key = val;
+	if (os_strcmp(var, "imsi_privacy_cert") == 0) {
+		os_free(cred->imsi_privacy_cert);
+		cred->imsi_privacy_cert = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "imsi_privacy_attr") == 0) {
+		os_free(cred->imsi_privacy_attr);
+		cred->imsi_privacy_attr = val;
+		return 0;
+	}
+
+	if (os_strcmp(var, "strict_conservative_peer_mode") == 0) {
+		cred->strict_conservative_peer_mode = atoi(val);
 		return 0;
 	}
 
@@ -4067,8 +4200,14 @@
 	if (os_strcmp(var, "imsi") == 0)
 		return alloc_strdup(cred->imsi);
 
-	if (os_strcmp(var, "imsi_privacy_key") == 0)
-		return alloc_strdup(cred->imsi_privacy_key);
+	if (os_strcmp(var, "imsi_privacy_cert") == 0)
+		return alloc_strdup(cred->imsi_privacy_cert);
+
+	if (os_strcmp(var, "imsi_privacy_attr") == 0)
+		return alloc_strdup(cred->imsi_privacy_attr);
+
+	if (os_strcmp(var, "strict_conservative_peer_mode") == 0)
+		return alloc_int_str(cred->strict_conservative_peer_mode);
 
 	if (os_strcmp(var, "milenage") == 0) {
 		if (!(cred->milenage))
@@ -4114,14 +4253,14 @@
 		size_t buflen;
 		char *buf;
 
-		if (!cred->roaming_consortium_len)
+		if (!cred->num_home_ois || !cred->home_ois_len[0])
 			return NULL;
-		buflen = cred->roaming_consortium_len * 2 + 1;
+		buflen = cred->home_ois_len[0] * 2 + 1;
 		buf = os_malloc(buflen);
 		if (buf == NULL)
 			return NULL;
-		wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
-				 cred->roaming_consortium_len);
+		wpa_snprintf_hex(buf, buflen, cred->home_ois[0],
+				 cred->home_ois_len[0]);
 		return buf;
 	}
 
@@ -4129,14 +4268,64 @@
 		size_t buflen;
 		char *buf;
 
-		if (!cred->required_roaming_consortium_len)
+		if (!cred->num_required_home_ois ||
+		    !cred->required_home_ois_len[0])
 			return NULL;
-		buflen = cred->required_roaming_consortium_len * 2 + 1;
+		buflen = cred->required_home_ois_len[0] * 2 + 1;
 		buf = os_malloc(buflen);
 		if (buf == NULL)
 			return NULL;
-		wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
-				 cred->required_roaming_consortium_len);
+		wpa_snprintf_hex(buf, buflen, cred->required_home_ois[0],
+				 cred->required_home_ois_len[0]);
+		return buf;
+	}
+
+	if (os_strcmp(var, "home_ois") == 0) {
+		size_t buflen;
+		char *buf, *pos;
+		size_t i;
+
+		if (!cred->num_home_ois)
+			return NULL;
+		buflen = cred->num_home_ois * MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+		buf = os_malloc(buflen);
+		if (!buf)
+			return NULL;
+		pos = buf;
+		for (i = 0; i < cred->num_home_ois; i++) {
+			if (i > 0)
+				*pos++ = ',';
+			pos += wpa_snprintf_hex(
+				pos, buf + buflen - pos,
+				cred->home_ois[i],
+				cred->home_ois_len[i]);
+		}
+		*pos = '\0';
+		return buf;
+	}
+
+	if (os_strcmp(var, "required_home_ois") == 0) {
+		size_t buflen;
+		char *buf, *pos;
+		size_t i;
+
+		if (!cred->num_required_home_ois)
+			return NULL;
+		buflen = cred->num_required_home_ois *
+			MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+		buf = os_malloc(buflen);
+		if (!buf)
+			return NULL;
+		pos = buf;
+		for (i = 0; i < cred->num_required_home_ois; i++) {
+			if (i > 0)
+				*pos++ = ',';
+			pos += wpa_snprintf_hex(
+				pos, buf + buflen - pos,
+				cred->required_home_ois[i],
+				cred->required_home_ois_len[i]);
+		}
+		*pos = '\0';
 		return buf;
 	}
 
@@ -5207,6 +5396,7 @@
 	{ FUNC(p2p_device_persistent_mac_addr), 0 },
 	{ INT(p2p_interface_random_mac_addr), 0 },
 	{ INT(p2p_6ghz_disable), 0 },
+	{ INT(p2p_dfs_chan_enable), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
@@ -5240,6 +5430,7 @@
 	{ INT_RANGE(auto_interworking, 0, 1), 0 },
 	{ INT(okc), 0 },
 	{ INT(pmf), 0 },
+	{ INT_RANGE(sae_check_mfp, 0, 1), 0 },
 	{ FUNC(sae_groups), 0 },
 	{ INT_RANGE(sae_pwe, 0, 3), 0 },
 	{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
@@ -5258,9 +5449,9 @@
 	{ STR(osu_dir), 0 },
 	{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
 	{ INT(p2p_search_delay), 0},
-	{ INT(mac_addr), 0 },
+	{ INT_RANGE(mac_addr, 0, 2), 0 },
 	{ INT(rand_addr_lifetime), 0 },
-	{ INT(preassoc_mac_addr), 0 },
+	{ INT_RANGE(preassoc_mac_addr, 0, 2), 0 },
 	{ INT(key_mgmt_offload), 0},
 	{ INT(passive_scan), 0 },
 	{ INT(reassoc_same_bss_optim), 0 },
@@ -5290,6 +5481,9 @@
 	{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
 	{ STR(dpp_name), 0 },
 	{ STR(dpp_mud_url), 0 },
+	{ STR(dpp_extra_conf_req_name), 0 },
+	{ STR(dpp_extra_conf_req_value), 0 },
+	{ INT_RANGE(dpp_connector_privacy_default, 0, 1), 0 },
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
 	{ INT_RANGE(bss_no_flush_when_down, 0, 1), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 77d6ab5..e1f30c2 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -182,14 +182,36 @@
 	char *milenage;
 
 	/**
-	 * imsi_privacy_key - IMSI privacy key (PEM encoded X.509v3 certificate)
+	 * imsi_privacy_cert - IMSI privacy certificate
 	 *
 	 * This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent
-	 * identity (IMSI) to improve privacy. The X.509v3 certificate needs to
-	 * include a 2048-bit RSA public key and this is from the operator who
-	 * authenticates the SIM/USIM.
+	 * identity (IMSI) to improve privacy. The referenced PEM-encoded
+	 * X.509v3 certificate needs to include a 2048-bit RSA public key and
+	 * this is from the operator who authenticates the SIM/USIM.
 	 */
-	char *imsi_privacy_key;
+	char *imsi_privacy_cert;
+
+	/**
+	 * imsi_privacy_attr - IMSI privacy attribute
+	 *
+	 * This field is used to help the EAP-SIM/AKA/AKA' server to identify
+	 * the used certificate (and as such, the matching private key). This
+	 * is set to an attribute in name=value format if the operator needs
+	 * this information.
+	 */
+	char *imsi_privacy_attr;
+
+	/**
+	 * strict_conservative_peer_mode - Whether the strict conservative peer
+	 * mode is enabled or not
+	 *
+	 * This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+	 * for EAP-SIM/AKA/AKA', in convervative peer mode, a client error would
+	 * be sent to the server, but it allows to send the permanent identity
+	 * in some special cases according to 4.6.2 of RFC 4187; With the strict
+	 * mode, it never send the permanent identity to server for privacy concern.
+	 */
+	int strict_conservative_peer_mode;
 
 	/**
 	 * engine - Use an engine for private key operations
@@ -250,36 +272,50 @@
 	size_t num_domain;
 
 	/**
-	 * roaming_consortium - Roaming Consortium OI
+	 * home_ois - Home OIs
 	 *
-	 * If roaming_consortium_len is non-zero, this field contains the
-	 * Roaming Consortium OI that can be used to determine which access
-	 * points support authentication with this credential. This is an
-	 * alternative to the use of the realm parameter. When using Roaming
-	 * Consortium to match the network, the EAP parameters need to be
-	 * pre-configured with the credential since the NAI Realm information
-	 * may not be available or fetched.
+	 * If num_home_ois is non-zero, this field contains the set of Home OIs
+	 * that can be use to determine which access points support
+	 * authentication with this credential. These are an alternative to the
+	 * use of the realm parameter. When using Home OIs to match the network,
+	 * the EAP parameters need to be pre-configured with the credentials
+	 * since the NAI Realm information may not be available or fetched.
+	 * A successful authentication with the access point is possible as soon
+	 * as at least one Home OI from the list matches an OI in the Roaming
+	 * Consortium list advertised by the access point.
+	 * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
 	 */
-	u8 roaming_consortium[15];
+	u8 home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
 
 	/**
-	 * roaming_consortium_len - Length of roaming_consortium
+	 * home_ois_len - Length of home_ois[i]
 	 */
-	size_t roaming_consortium_len;
+	size_t home_ois_len[MAX_ROAMING_CONS];
 
 	/**
-	 * required_roaming_consortium - Required Roaming Consortium OI
+	 * num_home_ois - Number of entries in home_ois
+	 */
+	unsigned int num_home_ois;
+
+	/**
+	 * required_home_ois - Required Home OI(s)
 	 *
-	 * If required_roaming_consortium_len is non-zero, this field contains
-	 * the Roaming Consortium OI that is required to be advertised by the AP
-	 * for the credential to be considered matching.
+	 * If required_home_ois_len is non-zero, this field contains the set of
+	 * Home OI(s) that are required to be advertised by the AP for the
+	 * credential to be considered matching.
+	 * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
 	 */
-	u8 required_roaming_consortium[15];
+	u8 required_home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
 
 	/**
-	 * required_roaming_consortium_len - Length of required_roaming_consortium
+	 * required_home_ois_len - Length of required_home_ois
 	 */
-	size_t required_roaming_consortium_len;
+	size_t required_home_ois_len[MAX_ROAMING_CONS];
+
+	/**
+	 * num_required_home_ois - Number of entries in required_home_ois
+	 */
+	unsigned int num_required_home_ois;
 
 	/**
 	 * roaming_consortiums - Roaming Consortium OI(s) memberships
@@ -818,8 +854,8 @@
 	int p2p_add_cli_chan;
 	int p2p_ignore_shared_freq;
 	int p2p_optimize_listen_chan;
-
 	int p2p_6ghz_disable;
+	int p2p_dfs_chan_enable;
 
 	struct wpabuf *wps_vendor_ext_m1;
 
@@ -1231,6 +1267,25 @@
 	enum mfp_options pmf;
 
 	/**
+	 * sae_check_mfp - Whether to limit SAE based on PMF capabilities
+	 *
+	 * With this check SAE key_mgmt will not be selected if PMF is
+	 * not enabled.
+	 * Scenarios where enabling this check will limit SAE:
+	 *  1) ieee8011w=0 is set for the network.
+	 *  2) The AP does not have PMF enabled.
+	 *  3) ieee8011w for the network is the default(3), pmf=1 is enabled
+	 *     globally and the device does not support the BIP cipher.
+	 *
+	 * Useful to allow the BIP cipher check that occurs for ieee80211w=3
+	 * and pmf=1 to also avoid using SAE key_mgmt.
+	 * Useful when hardware does not support BIP to still to allow
+	 * connecting to sae_require_mfp=1 WPA2+WPA3-Personal transition mode
+	 *access points by automatically selecting PSK instead of SAE.
+	 */
+	int sae_check_mfp;
+
+	/**
 	 * sae_groups - Preference list of enabled groups for SAE
 	 *
 	 * By default (if this parameter is not set), the mandatory group 19
@@ -1246,7 +1301,7 @@
 	 * 1 = hash-to-element only
 	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
 	 */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 
 	/**
 	 * sae_pmkid_in_assoc - Whether to include PMKID in SAE Assoc Req
@@ -1366,7 +1421,7 @@
 	 * the per-network mac_addr parameter. Global mac_addr=1 can be used to
 	 * change this default behavior.
 	 */
-	int mac_addr;
+	enum wpas_mac_addr_style mac_addr;
 
 	/**
 	 * rand_addr_lifetime - Lifetime of random MAC address in seconds
@@ -1380,7 +1435,7 @@
 	 * 1 = use random MAC address
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
 	 */
-	int preassoc_mac_addr;
+	enum wpas_mac_addr_style preassoc_mac_addr;
 
 	/**
 	 * key_mgmt_offload - Use key management offload
@@ -1589,7 +1644,7 @@
 	 * 1 = use random MAC address
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
 	 */
-	int gas_rand_mac_addr;
+	enum wpas_mac_addr_style gas_rand_mac_addr;
 
 	/**
 	 * dpp_config_processing - How to process DPP configuration
@@ -1617,6 +1672,25 @@
 	char *dpp_mud_url;
 
 	/**
+	 * dpp_extra_conf_req_name - JSON node name of additional data for
+	 * Enrollee's DPP Configuration Request
+	 */
+	char *dpp_extra_conf_req_name;
+
+	/**
+	 * dpp_extra_conf_req_value - JSON node data of additional data for
+	 * Enrollee's DPP Configuration Request
+	 */
+	char *dpp_extra_conf_req_value;
+
+	/* dpp_connector_privacy_default - Default valur for Connector privacy
+	 *
+	 * This value is used as the default for the dpp_connector_privacy
+	 * network parameter for all new networks provisioned using DPP.
+	 */
+	int dpp_connector_privacy_default;
+
+	/**
 	 * coloc_intf_reporting - Colocated interference reporting
 	 *
 	 * dot11CoLocIntfReportingActivated
@@ -1795,6 +1869,7 @@
  * @name: Name of the configuration (e.g., path and file name for the
  * configuration file)
  * @cfgp: Pointer to previously allocated configuration data or %NULL if none
+ * @ro: Whether to mark networks from this configuration as read-only
  * Returns: Pointer to allocated configuration data or %NULL on failure
  *
  * This function reads configuration data, parses its contents, and allocates
@@ -1803,7 +1878,8 @@
  *
  * Each configuration backend needs to implement this function.
  */
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+				    bool ro);
 
 /**
  * wpa_config_write - Write or update configuration data
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 978cc6a..c7b7826 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -53,6 +53,13 @@
 		ssid->group_cipher &= ~WPA_CIPHER_CCMP;
 	}
 
+	if (is_6ghz_freq(ssid->frequency) && ssid->mode == WPAS_MODE_MESH &&
+	    ssid->key_mgmt == WPA_KEY_MGMT_NONE) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: key_mgmt for mesh network in 6 GHz should be SAE",
+			   line);
+		errors++;
+	}
 	if (ssid->mode == WPAS_MODE_MESH &&
 	    (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
 	    ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
@@ -289,7 +296,8 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
 
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+				    bool ro)
 {
 	FILE *f;
 	char buf[512], *pos;
@@ -337,6 +345,7 @@
 				errors++;
 				continue;
 			}
+			ssid->ro = ro;
 			if (head == NULL) {
 				head = tail = ssid;
 			} else {
@@ -805,6 +814,7 @@
 	INT(macsec_integ_only);
 	INT(macsec_replay_protect);
 	INT(macsec_replay_window);
+	INT(macsec_offload);
 	INT(macsec_port);
 	INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
 	INT(macsec_csindex);
@@ -833,6 +843,7 @@
 	STR(dpp_csign);
 	STR(dpp_pp_key);
 	INT(dpp_pfs);
+	INT(dpp_connector_privacy);
 #endif /* CONFIG_DPP */
 	INT(owe_group);
 	INT(owe_only);
@@ -879,6 +890,8 @@
 #ifdef CONFIG_HE_OVERRIDES
 	INT(disable_he);
 #endif /* CONFIG_HE_OVERRIDES */
+	INT(disable_eht);
+	INT(enable_4addr_mode);
 
 #undef STR
 #undef INT
@@ -920,12 +933,6 @@
 	if (cred->domain_suffix_match)
 		fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
 			cred->domain_suffix_match);
-	if (cred->roaming_consortium_len) {
-		fprintf(f, "\troaming_consortium=");
-		for (i = 0; i < cred->roaming_consortium_len; i++)
-			fprintf(f, "%02x", cred->roaming_consortium[i]);
-		fprintf(f, "\n");
-	}
 	if (cred->eap_method) {
 		const char *name;
 		name = eap_get_name(cred->eap_method[0].vendor,
@@ -1001,12 +1008,32 @@
 		}
 	}
 
-	if (cred->required_roaming_consortium_len) {
-		fprintf(f, "\trequired_roaming_consortium=");
-		for (i = 0; i < cred->required_roaming_consortium_len; i++)
-			fprintf(f, "%02x",
-				cred->required_roaming_consortium[i]);
-		fprintf(f, "\n");
+	if (cred->num_home_ois) {
+		size_t j;
+
+		fprintf(f, "\thome_ois=\"");
+		for (i = 0; i < cred->num_home_ois; i++) {
+			if (i > 0)
+				fprintf(f, ",");
+			for (j = 0; j < cred->home_ois_len[i]; j++)
+				fprintf(f, "%02x",
+					cred->home_ois[i][j]);
+		}
+		fprintf(f, "\"\n");
+	}
+
+	if (cred->num_required_home_ois) {
+		size_t j;
+
+		fprintf(f, "\trequired_home_ois=\"");
+		for (i = 0; i < cred->num_required_home_ois; i++) {
+			if (i > 0)
+				fprintf(f, ",");
+			for (j = 0; j < cred->required_home_ois_len[i]; j++)
+				fprintf(f, "%02x",
+					cred->required_home_ois[i][j]);
+		}
+		fprintf(f, "\"\n");
 	}
 
 	if (cred->num_roaming_consortiums) {
@@ -1036,6 +1063,16 @@
 		fprintf(f, "\tcert_id=\"%s\"\n", cred->cert_id);
 	if (cred->ca_cert_id)
 		fprintf(f, "\tca_cert_id=\"%s\"\n", cred->ca_cert_id);
+
+	if (cred->imsi_privacy_cert)
+		fprintf(f, "\timsi_privacy_cert=\"%s\"\n",
+			cred->imsi_privacy_cert);
+	if (cred->imsi_privacy_attr)
+		fprintf(f, "\timsi_privacy_attr=\"%s\"\n",
+			cred->imsi_privacy_attr);
+	if (cred->strict_conservative_peer_mode)
+		fprintf(f,"\tstrict_conservative_peer_mode=\"%d\"\n",
+			cred->strict_conservative_peer_mode);
 }
 
 
@@ -1347,6 +1384,9 @@
 	if (config->beacon_int)
 		fprintf(f, "beacon_int=%d\n", config->beacon_int);
 
+	if (config->sae_check_mfp)
+		fprintf(f, "sae_check_mfp=%d\n", config->sae_check_mfp);
+
 	if (config->sae_groups) {
 		int i;
 		fprintf(f, "sae_groups=");
@@ -1536,6 +1576,19 @@
 	if (config->dpp_config_processing)
 		fprintf(f, "dpp_config_processing=%d\n",
 			config->dpp_config_processing);
+	if (config->dpp_name)
+		fprintf(f, "dpp_name=%s\n", config->dpp_name);
+	if (config->dpp_mud_url)
+		fprintf(f, "dpp_mud_url=%s\n", config->dpp_mud_url);
+	if (config->dpp_extra_conf_req_name)
+		fprintf(f, "dpp_extra_conf_req_name=%s\n",
+			config->dpp_extra_conf_req_name);
+	if (config->dpp_extra_conf_req_value)
+		fprintf(f, "dpp_extra_conf_req_value=%s\n",
+			config->dpp_extra_conf_req_value);
+	if (config->dpp_connector_privacy_default)
+		fprintf(f, "dpp_connector_privacy_default=%d\n",
+			config->dpp_connector_privacy_default);
 	if (config->coloc_intf_reporting)
 		fprintf(f, "coloc_intf_reporting=%d\n",
 			config->coloc_intf_reporting);
@@ -1610,7 +1663,8 @@
 	}
 
 	for (ssid = config->ssid; ssid; ssid = ssid->next) {
-		if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+		if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary ||
+		    ssid->ro)
 			continue; /* do not save temporary networks */
 		if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt) &&
 		    !ssid->psk_set && !ssid->passphrase)
diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c
index 0bc977e..01e7aad 100644
--- a/wpa_supplicant/config_none.c
+++ b/wpa_supplicant/config_none.c
@@ -17,7 +17,8 @@
 #include "base64.h"
 
 
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+				    bool ro)
 {
 	struct wpa_config *config;
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 4d3d114..785acc3 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -46,7 +46,7 @@
 #define DEFAULT_MAX_OPER_CHWIDTH -1
 
 /* Consider global sae_pwe for SAE mechanism for PWE derivation */
-#define DEFAULT_SAE_PWE 4
+#define DEFAULT_SAE_PWE SAE_PWE_NOT_SET
 
 struct psk_list_entry {
 	struct dl_list list;
@@ -70,6 +70,14 @@
 	SAE_PK_MODE_DISABLED = 2,
 };
 
+enum wpas_mac_addr_style {
+	WPAS_MAC_ADDR_STYLE_NOT_SET = -1,
+	WPAS_MAC_ADDR_STYLE_PERMANENT = 0,
+	WPAS_MAC_ADDR_STYLE_RANDOM = 1,
+	WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI = 2,
+	WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS = 3,
+};
+
 /**
  * struct wpa_ssid - Network configuration data
  *
@@ -107,6 +115,21 @@
 	int id;
 
 	/**
+	 * ro - Whether a network is declared as read-only
+	 *
+	 * Every network which is defined in a config file that is passed to
+	 * wpa_supplicant using the -I option will be marked as read-only
+	 * using this flag. It has the effect that it won't be written to
+	 * /etc/wpa_supplicant.conf (from -c argument) if, e.g., wpa_gui tells
+	 * the daemon to save all changed configs.
+	 *
+	 * This is necessary because networks from /etc/wpa_supplicant.conf
+	 * have a higher priority and changes from an alternative file would be
+	 * silently overwritten without this.
+	 */
+	bool ro;
+
+	/**
 	 * priority - Priority group
 	 *
 	 * By default, all networks will get same priority group (0). If some
@@ -560,7 +583,9 @@
 
 	int he;
 
-	int max_oper_chwidth;
+	int eht;
+
+	enum oper_chan_width max_oper_chwidth;
 
 	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
@@ -837,6 +862,14 @@
 	struct os_reltime disabled_until;
 
 	/**
+	 * disabled_due_to - BSSID of the disabling failure
+	 *
+	 * This identifies the BSS that failed the connection attempt that
+	 * resulted in the network being temporarily disabled.
+	 */
+	u8 disabled_due_to[ETH_ALEN];
+
+	/**
 	 * parent_cred - Pointer to parent wpa_cred entry
 	 *
 	 * This pointer can be used to delete temporary networks when a wpa_cred
@@ -895,6 +928,18 @@
 	u32 macsec_replay_window;
 
 	/**
+	 * macsec_offload - Enable MACsec hardware offload
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0 = MACSEC_OFFLOAD_OFF (default)
+	 * 1 = MACSEC_OFFLOAD_PHY
+	 * 2 = MACSEC_OFFLOAD_MAC
+	 */
+	int macsec_offload;
+
+	/**
 	 * macsec_port - MACsec port (in SCI)
 	 *
 	 * Port component of the SCI.
@@ -965,12 +1010,21 @@
 	 * 0 = use permanent MAC address
 	 * 1 = use random MAC address for each ESS connection
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
+	 * 3 = use dedicated/pregenerated MAC address (see mac_value)
 	 *
 	 * Internally, special value -1 is used to indicate that the parameter
 	 * was not specified in the configuration (i.e., default behavior is
 	 * followed).
 	 */
-	int mac_addr;
+	enum wpas_mac_addr_style mac_addr;
+
+	/**
+	 * mac_value - Specific MAC address to be used
+	 *
+	 * When mac_addr policy is equal to 3 this is the value of the MAC
+	 * address that should be used.
+	 */
+	u8 mac_value[ETH_ALEN];
 
 	/**
 	 * no_auto_peer - Do not automatically peer with compatible mesh peers
@@ -1064,6 +1118,14 @@
 	int dpp_pfs_fallback;
 
 	/**
+	 * dpp_connector_privacy - Network introduction type
+	 * 0: unprotected variant from DPP R1
+	 * 1: privacy protecting (station Connector encrypted) variant from
+	 *    DPP R3
+	 */
+	int dpp_connector_privacy;
+
+	/**
 	 * owe_group - OWE DH Group
 	 *
 	 * 0 = use default (19) first and then try all supported groups one by
@@ -1182,7 +1244,26 @@
 	 * 1 = hash-to-element only
 	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
 	 */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
+
+	/**
+	 * disable_eht - Disable EHT (IEEE 802.11be) for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_eht;
+
+	/**
+	 * enable_4addr_mode - Set 4addr mode after association
+	 * 0 = Do not attempt to set 4addr mode
+	 * 1 = Try to set 4addr mode after association
+	 *
+	 * Linux requires that an interface is set to 4addr mode before it can
+	 * be added to a bridge. Set this to 1 for networks where you intent
+	 * to use the interface in a bridge.
+	 */
+	int enable_4addr_mode;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index b27c6cf..05e6e37 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -446,7 +446,8 @@
 }
 
 
-struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
+struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp,
+				    bool ro)
 {
 	TCHAR buf[256];
 	int errors = 0;
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e8a8118..9abfeb2 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -833,8 +833,6 @@
 			wpa_s->sae_commit_override = wpabuf_parse_bin(value);
 	} else if (os_strcasecmp(cmd, "driver_signal_override") == 0) {
 		ret = wpas_ctrl_iface_set_dso(wpa_s, value);
-	} else if (os_strcasecmp(cmd, "force_hunting_and_pecking_pwe") == 0) {
-		wpa_s->force_hunting_and_pecking_pwe = (atoi(value) != 0) ? 1 : 0;
 	} else if (os_strcasecmp(cmd, "disable_scs_support") == 0) {
 		wpa_s->disable_scs_support = !!atoi(value);
 	} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
@@ -866,6 +864,8 @@
 	} else if (os_strcasecmp(cmd,
 				 "dpp_ignore_netaccesskey_mismatch") == 0) {
 		wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value);
+	} else if (os_strcasecmp(cmd, "dpp_discard_public_action") == 0) {
+		wpa_s->dpp_discard_public_action = atoi(value);
 	} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
 		dpp_test = atoi(value);
 #endif /* CONFIG_DPP */
@@ -1317,6 +1317,7 @@
 	u8 target_ap[ETH_ALEN];
 	struct wpa_bss *bss;
 	const u8 *mdie;
+	bool force = os_strstr(addr, " force") != NULL;
 
 	if (hwaddr_aton(addr, target_ap)) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
@@ -1332,7 +1333,7 @@
 	else
 		mdie = NULL;
 
-	return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+	return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie, force);
 }
 #endif /* CONFIG_IEEE80211R */
 
@@ -2383,6 +2384,14 @@
 		return pos - buf;
 	pos += ret;
 
+	if (wpa_s->valid_links) {
+		ret = os_snprintf(pos, end - pos, "ap_mld_addr=" MACSTR "\n",
+				  MAC2STR(wpa_s->ap_mld_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 #ifdef CONFIG_HS20
 	if (wpa_s->current_bss &&
 	    (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
@@ -2537,12 +2546,21 @@
 					  wpa_s->current_ssid->ssid_len) : "");
 		if (wpa_s->wpa_state == WPA_COMPLETED) {
 			struct wpa_ssid *ssid = wpa_s->current_ssid;
+			char mld_addr[50];
+
+			mld_addr[0] = '\0';
+			if (wpa_s->valid_links)
+				os_snprintf(mld_addr, sizeof(mld_addr),
+					    " ap_mld_addr=" MACSTR,
+					    MAC2STR(wpa_s->ap_mld_addr));
+
 			wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
 				     "- connection to " MACSTR
-				     " completed %s [id=%d id_str=%s]",
+				     " completed %s [id=%d id_str=%s]%s",
 				     MAC2STR(wpa_s->bssid), "(auth)",
 				     ssid ? ssid->id : -1,
-				     ssid && ssid->id_str ? ssid->id_str : "");
+				     ssid && ssid->id_str ? ssid->id_str : "",
+				     mld_addr);
 		}
 	}
 #endif /* ANDROID */
@@ -2816,6 +2834,13 @@
 			return pos;
 		pos += ret;
 	}
+	if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sSAE-EXT-KEY",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
 #ifdef CONFIG_IEEE80211R
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
@@ -2838,6 +2863,13 @@
 			return pos;
 		pos += ret;
 	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, "%sFT/SAE-EXT-KEY",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
 #endif /* CONFIG_IEEE80211R */
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
@@ -3236,7 +3268,8 @@
 		return -1;
 	}
 	if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
-	    ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE_EXT_KEY) {
 		wpa_printf(MSG_ERROR,
 			   "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
 		return -1;
@@ -4318,6 +4351,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, " FT-SAE-EXT-KEY");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_SHA384
 	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) {
@@ -4335,6 +4374,12 @@
 			return pos - buf;
 		pos += ret;
 	}
+	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY) {
+		ret = os_snprintf(pos, end - pos, " SAE-EXT-KEY");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_SHA256
 	if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) {
@@ -4956,6 +5001,250 @@
 #endif /* CONFIG_FILS */
 
 
+static int print_rnr(struct wpa_bss *bss, char *pos, char *end)
+{
+	char *start = pos;
+	const u8 *ie, *ie_end;
+	unsigned int n = 0;
+	int ret;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+	if (!ie)
+		return 0;
+
+	ie_end = ie + 2 + ie[1];
+	ie += 2;
+
+	while (ie < ie_end) {
+		const struct ieee80211_neighbor_ap_info *info =
+			(const struct ieee80211_neighbor_ap_info *) ie;
+		const u8 *tbtt_start;
+		size_t left = ie_end - ie;
+
+		if (left < sizeof(struct ieee80211_neighbor_ap_info))
+			return 0;
+
+		left -= sizeof(struct ieee80211_neighbor_ap_info);
+		if (left < info->tbtt_info_len)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos,
+				  "ap_info[%u]: tbtt_info: hdr=0x%x, len=%u, op_c=%u, channel=%u, ",
+				  n, *ie, info->tbtt_info_len,
+				  info->op_class, info->channel);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		ie += sizeof(struct ieee80211_neighbor_ap_info);
+		tbtt_start = ie;
+		if (info->tbtt_info_len >= 1) {
+			ret = os_snprintf(pos, end - pos,
+					  "tbtt_offset=%u, ", *ie);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie++;
+			pos += ret;
+		}
+
+		if (info->tbtt_info_len >= 7) {
+			ret = os_snprintf(pos, end - pos,
+					  "bssid=" MACSTR ", ",
+					  MAC2STR(ie));
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie += ETH_ALEN;
+			pos += ret;
+		}
+
+		if (info->tbtt_info_len >= 11) {
+			ret = os_snprintf(pos, end - pos,
+					  "short SSID=0x%x, ",
+					  WPA_GET_LE32(ie));
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie += 4;
+			pos += ret;
+		}
+
+		if (info->tbtt_info_len >= 12) {
+			ret = os_snprintf(pos, end - pos,
+					  "bss_params=0x%x, ", *ie);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie++;
+			pos += ret;
+		}
+
+		if (info->tbtt_info_len >= 13) {
+			ret = os_snprintf(pos, end - pos,
+					  "PSD=0x%x, ", *ie);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie++;
+			pos += ret;
+		}
+
+		if (info->tbtt_info_len >= 16) {
+			ret = os_snprintf(pos, end - pos,
+					  "mld ID=%u, link ID=%u",
+					  *ie, *(ie + 1) & 0xF);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+
+			ie += 3;
+			pos += ret;
+		}
+
+		ie = tbtt_start + info->tbtt_info_len;
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		n++;
+	}
+
+	return pos - start;
+}
+
+
+static int print_ml(struct wpa_bss *bss, char *pos, char *end)
+{
+	const struct ieee80211_eht_ml *ml;
+	char *start = pos;
+	const u8 *ie, *ie_end;
+	u16 ml_control;
+	u8 common_info_length;
+	int ret;
+
+	ie = get_ml_ie(wpa_bss_ie_ptr(bss), bss->ie_len,
+		       MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!ie)
+		return 0;
+
+	ie_end = ie + 2 + ie[1];
+	ie += 3;
+	ml = (const struct ieee80211_eht_ml *) ie;
+
+	/* control + common info length + MLD MAC Address */
+	if (ie_end - ie < 2 + 1 + ETH_ALEN)
+		return 0;
+
+	ml_control = le_to_host16(ml->ml_control);
+
+	common_info_length = *(ie + 2);
+	ret = os_snprintf(pos, end - pos,
+			  "multi-link: control=0x%x, common info len=%u",
+			  ml_control, common_info_length);
+	if (os_snprintf_error(end - pos, ret))
+		return 0;
+	pos += ret;
+
+	ie += 2;
+	if (ie_end - ie < common_info_length)
+		return 0;
+
+	ie++;
+	common_info_length--;
+
+	if (common_info_length < ETH_ALEN)
+		return 0;
+
+	ret = os_snprintf(pos, end - pos, ", MLD addr=" MACSTR, MAC2STR(ie));
+	if (os_snprintf_error(end - pos, ret))
+		return 0;
+	pos += ret;
+
+	ie += ETH_ALEN;
+	common_info_length -= ETH_ALEN;
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+		if (common_info_length < 1)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos, ", link ID=%u", *ie & 0x0f);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie++;
+		common_info_length--;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+		if (common_info_length < 1)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos,
+				  ", BSS change parameters=0x%x", *ie);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie++;
+		common_info_length--;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+		if (common_info_length < 2)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos, ", MSD Info=0x%x",
+				  WPA_GET_LE16(ie));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += 2;
+		common_info_length -= 2;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+		if (common_info_length < 2)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos, ", EML capabilities=0x%x",
+				  WPA_GET_LE16(ie));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += 2;
+		common_info_length -= 2;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+		if (common_info_length < 2)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos, ", MLD capabilities=0x%x",
+				  WPA_GET_LE16(ie));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += 2;
+		common_info_length -= 2;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+		if (common_info_length < 1)
+			return 0;
+
+		ret = os_snprintf(pos, end - pos, ", MLD ID=0x%x\n", *ie);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += 1;
+		common_info_length--;
+	}
+
+	return pos - start;
+}
+
+
 static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			  unsigned long mask, char *buf, size_t buflen)
 {
@@ -5397,6 +5686,21 @@
 	}
 #endif /* CONFIG_FILS */
 
+	if (!is_zero_ether_addr(bss->mld_addr)) {
+		ret = os_snprintf(pos, end - pos,
+				  "ap_mld_addr=" MACSTR "\n",
+				  MAC2STR(bss->mld_addr));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_RNR)
+		pos += print_rnr(bss, pos, end);
+
+	if (mask & WPA_BSS_MASK_ML)
+		pos += print_ml(bss, pos, end);
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -5602,23 +5906,23 @@
 {
 	wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
 	/* MLME-DELETEKEYS.request */
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
 
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
-			0, KEY_FLAG_PAIRWISE);
+	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0,
+			NULL, 0, KEY_FLAG_PAIRWISE);
 	if (wpa_sm_ext_key_id(wpa_s->wpa))
-		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
+		wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 1, 0,
 				NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 	/* MLME-SETPROTECTION.request(None) */
 	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
@@ -5978,17 +6282,19 @@
 	if (freq2 < 0)
 		return -1;
 	if (freq2)
-		return CHANWIDTH_80P80MHZ;
+		return CONF_OPER_CHWIDTH_80P80MHZ;
 
 	switch (chwidth) {
 	case 0:
 	case 20:
 	case 40:
-		return CHANWIDTH_USE_HT;
+		return CONF_OPER_CHWIDTH_USE_HT;
 	case 80:
-		return CHANWIDTH_80MHZ;
+		return CONF_OPER_CHWIDTH_80MHZ;
 	case 160:
-		return CHANWIDTH_160MHZ;
+		return CONF_OPER_CHWIDTH_160MHZ;
+	case 320:
+		return CONF_OPER_CHWIDTH_320MHZ;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
 			   chwidth);
@@ -6095,7 +6401,7 @@
 		return -1;
 
 	if (allow_6ghz && chwidth == 40)
-		max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ;
+		max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
 
 	pos2 = os_strstr(pos, " ssid=");
 	if (pos2) {
@@ -6751,7 +7057,7 @@
 	allow_6ghz = os_strstr(cmd, " allow_6ghz") != NULL;
 
 	if (allow_6ghz && chwidth == 40)
-		max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ;
+		max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
 
 	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
 			       max_oper_chwidth, pref_freq, he, edmg,
@@ -6807,7 +7113,8 @@
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
 					 int id, int freq, int vht_center_freq2,
 					 int ht40, int vht, int vht_chwidth,
-					 int he, int edmg, bool allow_6ghz)
+					 int he, int edmg, bool allow_6ghz,
+					 const u8 *go_bssid)
 {
 	struct wpa_ssid *ssid;
 
@@ -6822,7 +7129,8 @@
 	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
 					     vht_center_freq2, 0, ht40, vht,
 					     vht_chwidth, he, edmg,
-					     NULL, 0, 0, allow_6ghz);
+					     NULL, 0, 0, allow_6ghz, 0,
+					     go_bssid);
 }
 
 
@@ -6836,6 +7144,7 @@
 	int edmg = wpa_s->conf->p2p_go_edmg;
 	int max_oper_chwidth, chwidth = 0, freq2 = 0;
 	char *token, *context = NULL;
+	u8 go_bssid_buf[ETH_ALEN], *go_bssid = NULL;
 #ifdef CONFIG_ACS
 	int acs = 0;
 #endif /* CONFIG_ACS */
@@ -6864,6 +7173,10 @@
 			persistent = 1;
 		} else if (os_strcmp(token, "allow_6ghz") == 0) {
 			allow_6ghz = true;
+		} else if (os_strncmp(token, "go_bssid=", 9) == 0) {
+			if (hwaddr_aton(token + 9, go_bssid_buf))
+				return -1;
+			go_bssid = go_bssid_buf;
 		} else {
 			wpa_printf(MSG_DEBUG,
 				   "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
@@ -6897,7 +7210,7 @@
 		return -1;
 
 	if (allow_6ghz && chwidth == 40)
-		max_oper_chwidth = CHANWIDTH_40MHZ_6GHZ;
+		max_oper_chwidth = CONF_OPER_CHWIDTH_40MHZ_6GHZ;
 
 	/* Allow DFS to be used for Autonomous GO */
 	wpa_s->p2p_go_allow_dfs = !!(wpa_s->drv_flags &
@@ -6907,7 +7220,8 @@
 		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
 						     freq, freq2, ht40, vht,
 						     max_oper_chwidth, he,
-						     edmg, allow_6ghz);
+						     edmg, allow_6ghz,
+						     go_bssid);
 
 	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
 				  max_oper_chwidth, he, edmg, allow_6ghz);
@@ -8045,9 +8359,9 @@
 	pos = buf;
 	end = buf + buflen;
 
-	ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+	ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%lu\n"
 			  "NOISE=%d\nFREQUENCY=%u\n",
-			  si.current_signal, si.current_txrate / 1000,
+			  si.data.signal, si.data.current_tx_rate / 1000,
 			  si.current_noise, si.frequency);
 	if (os_snprintf_error(end - pos, ret))
 		return -1;
@@ -8077,17 +8391,18 @@
 		pos += ret;
 	}
 
-	if (si.avg_signal) {
+	if (si.data.avg_signal) {
 		ret = os_snprintf(pos, end - pos,
-				  "AVG_RSSI=%d\n", si.avg_signal);
+				  "AVG_RSSI=%d\n", si.data.avg_signal);
 		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 
-	if (si.avg_beacon_signal) {
+	if (si.data.avg_beacon_signal) {
 		ret = os_snprintf(pos, end - pos,
-				  "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+				  "AVG_BEACON_RSSI=%d\n",
+				  si.data.avg_beacon_signal);
 		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
@@ -8123,7 +8438,7 @@
 int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 						enum wpa_driver_if_type if_type,
 						unsigned int *num,
-						unsigned int *freq_list)
+						struct weighted_pcl *freq_list)
 {
 	char *pos = wpa_s->get_pref_freq_list_override;
 	char *end;
@@ -8147,7 +8462,8 @@
 	pos++;
 	end = os_strchr(pos, ' ');
 	while (pos && (!end || pos < end) && count < *num) {
-		freq_list[count++] = atoi(pos);
+		freq_list[count].freq = atoi(pos);
+		freq_list[count++].flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
 		pos = os_strchr(pos, ',');
 		if (pos)
 			pos++;
@@ -8162,10 +8478,11 @@
 static int wpas_ctrl_iface_get_pref_freq_list(
 	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 {
-	unsigned int freq_list[100], num = 100, i;
+	unsigned int num = 100, i;
 	int ret;
 	enum wpa_driver_if_type iface_type;
 	char *pos, *end;
+	struct weighted_pcl freq_list[100];
 
 	pos = buf;
 	end = buf + buflen;
@@ -8196,7 +8513,7 @@
 
 	for (i = 0; i < num; i++) {
 		ret = os_snprintf(pos, end - pos, "%s%u",
-				  i > 0 ? "," : "", freq_list[i]);
+				  i > 0 ? "," : "", freq_list[i].freq);
 		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
@@ -8437,6 +8754,19 @@
 	wpas_dpp_chirp_stop(wpa_s);
 	wpa_s->dpp_pfs_fallback = 0;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	{
+		int i;
+
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			struct dpp_pb_info *info;
+
+			info = &wpa_s->dpp_pb[i];
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+		}
+	}
+#endif /* CONFIG_DPP3 */
 #ifdef CONFIG_TESTING_OPTIONS
 	os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN);
 	os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN);
@@ -8556,6 +8886,8 @@
 	wpa_s->dpp_discovery_override = NULL;
 	os_free(wpa_s->dpp_groups_override);
 	wpa_s->dpp_groups_override = NULL;
+	wpa_s->dpp_ignore_netaccesskey_mismatch = 0;
+	wpa_s->dpp_discard_public_action = 0;
 	dpp_test = DPP_TEST_DISABLED;
 #endif /* CONFIG_DPP */
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -8840,6 +9172,7 @@
 	unsigned int manual_scan_only_new = 0;
 	unsigned int scan_only = 0;
 	unsigned int scan_id_count = 0;
+	unsigned int manual_non_coloc_6ghz = 0;
 	int scan_id[MAX_SCAN_ID];
 	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res);
@@ -8917,6 +9250,10 @@
 				os_strstr(params, "wildcard_ssid=1") != NULL;
 		}
 
+		pos = os_strstr(params, "non_coloc_6ghz=");
+		if (pos)
+			manual_non_coloc_6ghz = !!atoi(pos + 15);
+
 		pos = params;
 		while (pos && *pos != '\0') {
 			if (os_strncmp(pos, "ssid ", 5) == 0) {
@@ -8986,6 +9323,7 @@
 		wpa_s->manual_scan_use_id = manual_scan_use_id;
 		wpa_s->manual_scan_only_new = manual_scan_only_new;
 		wpa_s->scan_id_count = scan_id_count;
+		wpa_s->manual_non_coloc_6ghz = manual_non_coloc_6ghz;
 		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
 		wpa_s->scan_res_handler = scan_res_handler;
 		os_free(wpa_s->manual_scan_freqs);
@@ -9009,6 +9347,7 @@
 		wpa_s->manual_scan_use_id = manual_scan_use_id;
 		wpa_s->manual_scan_only_new = manual_scan_only_new;
 		wpa_s->scan_id_count = scan_id_count;
+		wpa_s->manual_non_coloc_6ghz = manual_non_coloc_6ghz;
 		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
 		wpa_s->scan_res_handler = scan_res_handler;
 		os_free(wpa_s->manual_scan_freqs);
@@ -9523,7 +9862,7 @@
 		return -1;
 	}
 
-	wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
+	wpa_supplicant_rx_eapol(wpa_s, src, buf, len, FRAME_ENCRYPTION_UNKNOWN);
 	os_free(buf);
 
 	return 0;
@@ -9917,14 +10256,15 @@
 
 	/* First, use a zero key to avoid any possible duplicate key avoidance
 	 * in the driver. */
-	if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+	if (wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
 			    wpa_s->last_tk_key_idx, 1, zero, 6,
 			    zero, wpa_s->last_tk_len,
 			    KEY_FLAG_PAIRWISE_RX_TX) < 0)
 		return -1;
 
 	/* Set the previously configured key to reset its TSC/RSC */
-	return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
+	return wpa_drv_set_key(wpa_s, -1, wpa_s->last_tk_alg,
+			       wpa_s->last_tk_addr,
 			       wpa_s->last_tk_key_idx, 1, zero, 6,
 			       wpa_s->last_tk, wpa_s->last_tk_len,
 			       KEY_FLAG_PAIRWISE_RX_TX);
@@ -10022,8 +10362,9 @@
 		setup_cmd = atoi(tok_s + os_strlen(" setup_cmd="));
 
 	tok_s = os_strstr(cmd, " twt=");
-	if (tok_s)
-		sscanf(tok_s + os_strlen(" twt="), "%llu", &twt);
+	if (tok_s &&
+	    sscanf(tok_s + os_strlen(" twt="), "%llu", &twt) != 1)
+		return -1;
 
 	tok_s = os_strstr(cmd, " requestor=");
 	if (tok_s)
@@ -10618,6 +10959,7 @@
 	entry->reauth_time = now.sec + reauth_time;
 
 	entry->network_ctx = ssid;
+	os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
 
 	entry->external = true;
 
@@ -10816,6 +11158,8 @@
 #ifdef CONFIG_SAE
 		} else if (os_strcmp(token, "akmp=SAE") == 0) {
 			akmp = WPA_KEY_MGMT_SAE;
+		} else if (os_strcmp(token, "akmp=SAE-EXT-KEY") == 0) {
+			akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
 		} else if (os_strcmp(token, "akmp=FILS-SHA256") == 0) {
@@ -10858,8 +11202,8 @@
 		goto out;
 	}
 
-	ret = wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id,
-				   comeback, comeback_len);
+	ret = wpas_pasn_auth_start(wpa_s, wpa_s->own_addr, bssid, akmp, cipher,
+				   group, id, comeback, comeback_len);
 out:
 	os_free(comeback);
 	return ret;
@@ -10877,7 +11221,7 @@
 		return -1;
 	}
 
-	return wpas_pasn_deauthenticate(wpa_s, bssid);
+	return wpas_pasn_deauthenticate(wpa_s, wpa_s->own_addr, bssid);
 }
 
 #endif /* CONFIG_PASN */
@@ -11458,6 +11802,117 @@
 }
 
 
+static int wpas_ctrl_iface_mlo_signal_poll(struct wpa_supplicant *wpa_s,
+					   char *buf, size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+	struct wpa_mlo_signal_info mlo_si;
+
+	if (!wpa_s->valid_links)
+		return -1;
+
+	ret = wpa_drv_mlo_signal_poll(wpa_s, &mlo_si);
+	if (ret)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_si.valid_links & BIT(i)))
+			continue;
+
+		ret = os_snprintf(pos, end - pos,
+				  "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
+				  "NOISE=%d\nFREQUENCY=%u\n",
+				  i, mlo_si.links[i].data.signal,
+				  mlo_si.links[i].data.current_tx_rate / 1000,
+				  mlo_si.links[i].current_noise,
+				  mlo_si.links[i].frequency);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+
+		if (mlo_si.links[i].chanwidth != CHAN_WIDTH_UNKNOWN) {
+			ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
+					  channel_width_to_string(
+						  mlo_si.links[i].chanwidth));
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].center_frq1 > 0) {
+			ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n",
+					  mlo_si.links[i].center_frq1);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].center_frq2 > 0) {
+			ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n",
+					  mlo_si.links[i].center_frq2);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].data.avg_signal) {
+			ret = os_snprintf(pos, end - pos,
+					  "AVG_RSSI=%d\n",
+					  mlo_si.links[i].data.avg_signal);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (mlo_si.links[i].data.avg_beacon_signal) {
+			ret = os_snprintf(
+				pos, end - pos, "AVG_BEACON_RSSI=%d\n",
+				mlo_si.links[i].data.avg_beacon_signal);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_mlo_status(struct wpa_supplicant *wpa_s,
+				      char *buf, size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+
+	if (!wpa_s->valid_links)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
+				  "ap_link_addr=" MACSTR
+				  "\nsta_link_addr=" MACSTR "\n",
+				  i, wpa_s->links[i].freq,
+				  MAC2STR(wpa_s->links[i].bssid),
+				  MAC2STR(wpa_s->links[i].addr));
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -12439,6 +12894,14 @@
 		if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0)
 			reply_len = -1;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	} else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) {
+		if (wpas_dpp_push_button(wpa_s, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) {
+		if (wpas_dpp_push_button(wpa_s, buf + 15) < 0)
+			reply_len = -1;
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 	} else if (os_strncmp(buf, "MSCS ", 5) == 0) {
 		if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
@@ -12464,6 +12927,12 @@
 	} else if (os_strncmp(buf, "DSCP_QUERY ", 11) == 0) {
 		if (wpas_ctrl_iface_send_dscp_query(wpa_s, buf + 11))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "MLO_STATUS") == 0) {
+		reply_len = wpas_ctrl_iface_mlo_status(wpa_s, reply,
+						       reply_size);
+	} else if (os_strcmp(buf, "MLO_SIGNAL_POLL") == 0) {
+		reply_len = wpas_ctrl_iface_mlo_signal_poll(wpa_s, reply,
+							    reply_size);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -12609,8 +13078,11 @@
 	return 0;
 
 fail:
-	if (create_iface)
+	if (create_iface) {
+		/* wpa_supplicant does not create multi-BSS AP, so collapse to
+		 * WPA_IF_STATION to avoid unwanted clean up in the driver. */
 		wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+	}
 	return -1;
 }
 
@@ -12632,6 +13104,8 @@
 	if (!ret && delete_iface) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
 			   cmd);
+		/* wpa_supplicant does not create multi-BSS AP, so collapse to
+		 * WPA_IF_STATION to avoid unwanted clean up in the driver. */
 		ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
 	}
 	return ret;
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1178f40..1cbf7fa 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -337,9 +337,6 @@
 		else
 			reply_len = 2;
 	} else {
-		sockaddr_print(wpas_ctrl_cmd_debug_level(buf),
-			       "Control interface recv command from:",
-			       &from, fromlen);
 		reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
 							  &reply_len);
 	}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index e4e9b8d..0e47534 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -299,6 +299,25 @@
 
 
 /**
+ * Add a 64-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint64_t value)
+{
+	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
+					      &value);
+}
+
+
+/**
  * Add a DBus object path entry to the dict.
  *
  * @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 94a0efd..44685ea 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -46,6 +46,10 @@
 					const char *key,
 					const dbus_uint32_t value);
 
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+					const char *key,
+					const dbus_uint64_t value);
+
 dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
 					     const char *key,
 					     const char *value);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 9279ae4..8fc29b3 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1102,6 +1102,29 @@
 }
 
 
+void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "PskMismatch");
+	if (!msg)
+		return;
+
+	dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
 /**
  * wpas_dbus_signal_sta - Send a station related event signal
  * @wpa_s: %wpa_supplicant network interface data
@@ -2344,6 +2367,12 @@
 	case WPAS_DBUS_PROP_BSS_TM_STATUS:
 		prop = "BSSTMStatus";
 		break;
+	case WPAS_DBUS_PROP_MAC_ADDRESS:
+		prop = "MACAddress";
+		break;
+	case WPAS_DBUS_PROP_SIGNAL_CHANGE:
+		prop = "SignalChange";
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -3939,6 +3968,11 @@
 	  wpas_dbus_setter_mac_address_randomization_mask,
 	  NULL
 	},
+	{ "MACAddress", WPAS_DBUS_NEW_IFACE_INTERFACE, "ay",
+	  wpas_dbus_getter_mac_address,
+	  NULL,
+	  NULL,
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -4521,6 +4555,11 @@
 	  NULL,
 	  NULL
 	},
+	{ "SignalChange", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+	  wpas_dbus_getter_signal_change,
+	  NULL,
+	  NULL
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 26bdcb5..5c5d855 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -38,6 +38,8 @@
 	WPAS_DBUS_PROP_ROAM_COMPLETE,
 	WPAS_DBUS_PROP_SESSION_LENGTH,
 	WPAS_DBUS_PROP_BSS_TM_STATUS,
+	WPAS_DBUS_PROP_MAC_ADDRESS,
+	WPAS_DBUS_PROP_SIGNAL_CHANGE,
 };
 
 enum wpas_dbus_bss_prop {
@@ -252,6 +254,7 @@
 			   const u8 *ie, size_t ie_len, u32 ssi_signal);
 void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
 				 const char *status, const char *parameter);
+void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s);
 void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
 				     const u8 *sta);
 void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
@@ -583,6 +586,10 @@
 {
 }
 
+static inline void wpas_dbus_signal_psk_mismatch(struct wpa_supplicant *wpa_s)
+{
+}
+
 static inline
 void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
 				     const u8 *sta)
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 0b1002b..67ce970 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -152,7 +152,7 @@
 #ifdef CONFIG_INTERWORKING
 	"roaming_consortium", "required_roaming_consortium",
 #endif /* CONFIG_INTERWORKING */
-	NULL
+	"mac_value", NULL
 };
 
 static dbus_bool_t should_quote_opt(const char *key)
@@ -206,6 +206,8 @@
 	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 	DBusMessageIter	iter_dict;
 	char *value = NULL;
+	bool mac_addr3_set = false;
+	bool mac_value_set = false;
 
 	if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
 		return FALSE;
@@ -315,12 +317,30 @@
 		else if (os_strcmp(entry.key, "priority") == 0)
 			wpa_config_update_prio_list(wpa_s->conf);
 
+		/*
+		 * MAC address policy "3" needs to come with mac_value in
+		 * the message so make sure that it is present (checked after
+		 * the loop - here we just note what has been supplied).
+		 */
+		if (os_strcmp(entry.key, "mac_addr") == 0 &&
+		    atoi(value) == 3)
+			mac_addr3_set = true;
+		if (os_strcmp(entry.key, "mac_value") == 0)
+			mac_value_set = true;
+
 	skip_update:
 		os_free(value);
 		value = NULL;
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
+	if (mac_addr3_set && !mac_value_set) {
+		wpa_printf(MSG_INFO, "dbus: Invalid mac_addr policy config");
+		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+				     "Invalid mac_addr policy config");
+		return FALSE;
+	}
+
 	return TRUE;
 
 error:
@@ -332,6 +352,118 @@
 }
 
 
+static int set_cred_property(struct wpa_cred *cred,
+			     struct wpa_dbus_dict_entry *entry)
+{
+	size_t size;
+	int ret;
+	char *value;
+
+	if (entry->type == DBUS_TYPE_ARRAY &&
+	    entry->array_type == DBUS_TYPE_STRING) {
+		dbus_uint32_t i;
+
+		if (entry->array_len <= 0)
+			return -1;
+
+		for (i = 0; i < entry->array_len; i++) {
+			if (should_quote_opt(entry->key)) {
+				size = os_strlen(entry->strarray_value[i]);
+
+				size += 3;
+				value = os_zalloc(size);
+				if (!value)
+					return -1;
+
+				ret = os_snprintf(value, size, "\"%s\"",
+						  entry->strarray_value[i]);
+				if (os_snprintf_error(size, ret)) {
+					os_free(value);
+					return -1;
+				}
+			} else {
+				value = os_strdup(entry->strarray_value[i]);
+				if (!value)
+					return -1;
+			}
+
+			ret = wpa_config_set_cred(cred, entry->key, value, 0);
+			os_free(value);
+			if (ret < 0)
+				return -1;
+		}
+		return 0;
+	}
+
+	if (entry->type == DBUS_TYPE_ARRAY &&
+	    entry->array_type == DBUS_TYPE_BYTE) {
+		if (entry->array_len <= 0)
+			return -1;
+
+		size = entry->array_len * 2 + 1;
+		value = os_zalloc(size);
+		if (!value)
+			return -1;
+
+		ret = wpa_snprintf_hex(value, size,
+				       (u8 *) entry->bytearray_value,
+				       entry->array_len);
+		if (ret <= 0) {
+			os_free(value);
+			return -1;
+		}
+	} else if (entry->type == DBUS_TYPE_STRING) {
+		if (should_quote_opt(entry->key)) {
+			size = os_strlen(entry->str_value);
+
+			size += 3;
+			value = os_zalloc(size);
+			if (!value)
+				return -1;
+
+			ret = os_snprintf(value, size, "\"%s\"",
+					  entry->str_value);
+			if (os_snprintf_error(size, ret)) {
+				os_free(value);
+				return -1;
+			}
+		} else {
+			value = os_strdup(entry->str_value);
+			if (!value)
+				return -1;
+		}
+	} else if (entry->type == DBUS_TYPE_UINT32) {
+		size = 50;
+		value = os_zalloc(size);
+		if (!value)
+			return -1;
+
+		ret = os_snprintf(value, size, "%u", entry->uint32_value);
+		if (os_snprintf_error(size, ret)) {
+			os_free(value);
+			return -1;
+		}
+	} else if (entry->type == DBUS_TYPE_INT32) {
+		size = 50;
+		value = os_zalloc(size);
+		if (!value)
+			return -1;
+
+		ret = os_snprintf(value, size, "%d", entry->int32_value);
+		if (os_snprintf_error(size, ret)) {
+			os_free(value);
+			return -1;
+		}
+	} else {
+		return -1;
+	}
+
+	ret = wpa_config_set_cred(cred, entry->key, value, 0);
+	os_free(value);
+	return ret;
+}
+
+
 /**
  * set_cred_properties - Set the properties of a configured credential
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -348,91 +480,28 @@
 {
 	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 	DBusMessageIter	iter_dict;
-	char *value = NULL;
 
 	if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
 		return FALSE;
 
 	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
-		size_t size = 50;
-		int ret;
+		int res;
 
-		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
-			goto error;
-
-		value = NULL;
-		if (entry.type == DBUS_TYPE_ARRAY &&
-		    entry.array_type == DBUS_TYPE_BYTE) {
-			if (entry.array_len <= 0)
-				goto error;
-
-			size = entry.array_len * 2 + 1;
-			value = os_zalloc(size);
-			if (!value)
-				goto error;
-
-			ret = wpa_snprintf_hex(value, size,
-					       (u8 *) entry.bytearray_value,
-					       entry.array_len);
-			if (ret <= 0)
-				goto error;
-		} else if (entry.type == DBUS_TYPE_STRING) {
-			if (should_quote_opt(entry.key)) {
-				size = os_strlen(entry.str_value);
-
-				size += 3;
-				value = os_zalloc(size);
-				if (!value)
-					goto error;
-
-				ret = os_snprintf(value, size, "\"%s\"",
-						  entry.str_value);
-				if (os_snprintf_error(size, ret))
-					goto error;
-			} else {
-				value = os_strdup(entry.str_value);
-				if (!value)
-					goto error;
-			}
-		} else if (entry.type == DBUS_TYPE_UINT32) {
-			value = os_zalloc(size);
-			if (!value)
-				goto error;
-
-			ret = os_snprintf(value, size, "%u",
-					  entry.uint32_value);
-			if (os_snprintf_error(size, ret))
-				goto error;
-		} else if (entry.type == DBUS_TYPE_INT32) {
-			value = os_zalloc(size);
-			if (!value)
-				goto error;
-
-			ret = os_snprintf(value, size, "%d",
-					  entry.int32_value);
-			if (os_snprintf_error(size, ret))
-				goto error;
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+			res = -1;
 		} else {
-			goto error;
+			res = set_cred_property(cred, &entry);
+			wpa_dbus_dict_entry_clear(&entry);
 		}
 
-		ret = wpa_config_set_cred(cred, entry.key, value, 0);
-		if (ret < 0)
-			goto error;
-
-		os_free(value);
-		value = NULL;
-		wpa_dbus_dict_entry_clear(&entry);
+		if (res < 0) {
+			dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+					     "invalid message format");
+			return FALSE;
+		}
 	}
 
 	return TRUE;
-
-error:
-	os_free(value);
-	wpa_dbus_dict_entry_clear(&entry);
-	dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
-			     "invalid message format");
-	return FALSE;
 }
 
 
@@ -704,6 +773,9 @@
 	char *ifname = NULL;
 	char *confname = NULL;
 	char *bridge_ifname = NULL;
+	bool create_iface = false;
+	u8 *if_addr = NULL;
+	enum wpa_driver_if_type if_type = WPA_IF_STATION;
 
 	dbus_message_iter_init(message, &iter);
 
@@ -740,6 +812,33 @@
 			wpa_dbus_dict_entry_clear(&entry);
 			if (bridge_ifname == NULL)
 				goto oom;
+		} else if (os_strcmp(entry.key, "Create") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			create_iface = entry.bool_value;
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "Type") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "sta") == 0) {
+				if_type = WPA_IF_STATION;
+			} else if (os_strcmp(entry.str_value, "ap") == 0) {
+				if_type = WPA_IF_AP_BSS;
+			} else {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto error;
+			}
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "Address") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if_addr = os_malloc(ETH_ALEN);
+			if (if_addr == NULL) {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto oom;
+			}
+			if (hwaddr_aton(entry.str_value, if_addr)) {
+				wpa_dbus_dict_entry_clear(&entry);
+				goto error;
+			}
+			wpa_dbus_dict_entry_clear(&entry);
 		} else {
 			wpa_dbus_dict_entry_clear(&entry);
 			goto error;
@@ -761,6 +860,23 @@
 		struct wpa_supplicant *wpa_s;
 		struct wpa_interface iface;
 
+		if (create_iface) {
+			u8 mac_addr[ETH_ALEN];
+
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: creating an interface '%s'",
+				   __func__, ifname);
+			if (!global->ifaces ||
+			    wpa_drv_if_add(global->ifaces, if_type, ifname,
+					   if_addr, NULL, NULL, mac_addr,
+					   NULL) < 0) {
+				reply = wpas_dbus_error_unknown_error(
+					message,
+					"interface creation failed.");
+				goto out;
+			}
+		}
+
 		os_memset(&iface, 0, sizeof(iface));
 		iface.driver = driver;
 		iface.ifname = ifname;
@@ -771,6 +887,7 @@
 		if (wpa_s && wpa_s->dbus_new_path) {
 			const char *path = wpa_s->dbus_new_path;
 
+			wpa_s->added_vif = create_iface;
 			reply = dbus_message_new_method_return(message);
 			dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
 						 &path, DBUS_TYPE_INVALID);
@@ -778,6 +895,13 @@
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"wpa_supplicant couldn't grab this interface.");
+			if (create_iface) {
+				/* wpa_supplicant does not create multi-BSS AP,
+				 * so collapse to WPA_IF_STATION to avoid
+				 * unwanted clean up in the driver. */
+				wpa_drv_if_remove(global->ifaces,
+						  WPA_IF_STATION, ifname);
+			}
 		}
 	}
 
@@ -786,6 +910,7 @@
 	os_free(ifname);
 	os_free(confname);
 	os_free(bridge_ifname);
+	os_free(if_addr);
 	return reply;
 
 error:
@@ -814,19 +939,38 @@
 	struct wpa_supplicant *wpa_s;
 	char *path;
 	DBusMessage *reply = NULL;
+	bool delete_iface;
 
 	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
 			      DBUS_TYPE_INVALID);
 
 	wpa_s = get_iface_by_dbus_path(global, path);
-	if (wpa_s == NULL)
+	if (!wpa_s) {
 		reply = wpas_dbus_error_iface_unknown(message);
-	else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
+		goto out;
+	}
+	delete_iface = wpa_s->added_vif;
+	if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
 		reply = wpas_dbus_error_unknown_error(
 			message,
 			"wpa_supplicant couldn't remove this interface.");
+		goto out;
 	}
 
+	if (delete_iface) {
+		wpa_printf(MSG_DEBUG, "%s[dbus]: deleting the interface '%s'",
+			   __func__, wpa_s->ifname);
+		/* wpa_supplicant does not create multi-BSS AP, so collapse to
+		 * WPA_IF_STATION to avoid unwanted clean up in the driver. */
+		if (wpa_drv_if_remove(global->ifaces, WPA_IF_STATION,
+				      wpa_s->ifname)) {
+			reply = wpas_dbus_error_unknown_error(
+				message,
+				"wpa_supplicant couldn't delete this interface.");
+		}
+	}
+
+out:
 	return reply;
 }
 
@@ -908,8 +1052,10 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
+	dbus_bool_t b = wpa_debug_timestamp ? TRUE : FALSE;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
-						&wpa_debug_timestamp, error);
+						&b, error);
 
 }
 
@@ -927,8 +1073,10 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
+	dbus_bool_t b = wpa_debug_show_keys ? TRUE : FALSE;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
-						&wpa_debug_show_keys, error);
+						&b, error);
 
 }
 
@@ -1822,7 +1970,7 @@
 {
 	struct wpa_signal_info si;
 	DBusMessage *reply = NULL;
-	DBusMessageIter iter, iter_dict, variant_iter;
+	DBusMessageIter iter;
 	int ret;
 
 	ret = wpa_drv_signal_poll(wpa_s, &si);
@@ -1837,31 +1985,7 @@
 
 	dbus_message_iter_init_append(reply, &iter);
 
-	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-					      "a{sv}", &variant_iter) ||
-	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
-	    !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
-					si.current_signal) ||
-	    !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
-					si.current_txrate / 1000) ||
-	    !wpa_dbus_dict_append_int32(&iter_dict, "noise",
-					si.current_noise) ||
-	    !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
-					 si.frequency) ||
-	    (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
-	     !wpa_dbus_dict_append_string(
-		     &iter_dict, "width",
-		     channel_width_to_string(si.chanwidth))) ||
-	    (si.center_frq1 > 0 && si.center_frq2 > 0 &&
-	     (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
-					  si.center_frq1) ||
-	      !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
-					  si.center_frq2))) ||
-	    (si.avg_signal &&
-	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
-					 si.avg_signal)) ||
-	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
-	    !dbus_message_iter_close_container(&iter, &variant_iter))
+	if (wpas_dbus_new_from_signal_information(&iter, &si) != 0)
 		goto nomem;
 
 	return reply;
@@ -3131,12 +3255,16 @@
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-eap") ||
-			    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
-			     !wpa_dbus_dict_string_array_add_element(
-				     &iter_array, "wpa-ft-eap")))
+				    &iter_array, "wpa-eap"))
 				goto nomem;
 
+#ifdef CONFIG_IEEE80211R
+			if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-eap"))
+				goto nomem;
+#endif /* CONFIG_IEEE80211R */
+
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 			if (!wpa_dbus_dict_string_array_add_element(
 				    &iter_array, "wpa-eap-sha256"))
@@ -3146,12 +3274,16 @@
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-psk") ||
-			    ((capa.key_mgmt &
+				    &iter_array, "wpa-psk"))
+				goto nomem;
+
+#ifdef CONFIG_IEEE80211R
+			if ((capa.key_mgmt &
 			      WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
 			     !wpa_dbus_dict_string_array_add_element(
-				     &iter_array, "wpa-ft-psk")))
+				     &iter_array, "wpa-ft-psk"))
 				goto nomem;
+#endif /* CONFIG_IEEE80211R */
 
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 			if (!wpa_dbus_dict_string_array_add_element(
@@ -4312,6 +4444,7 @@
 	const char *new_value = NULL;
 	char buf[250];
 	size_t combined_len;
+	int wpa_sm_param;
 	int ret;
 
 	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
@@ -4330,6 +4463,35 @@
 	if (!new_value[0])
 		new_value = "NULL";
 
+	wpa_sm_param = -1;
+	if (os_strcmp(property_desc->data, "dot11RSNAConfigPMKLifetime") == 0)
+		wpa_sm_param = RSNA_PMK_LIFETIME;
+	else if (os_strcmp(property_desc->data,
+			   "dot11RSNAConfigPMKReauthThreshold") == 0)
+		wpa_sm_param = RSNA_PMK_REAUTH_THRESHOLD;
+	else if (os_strcmp(property_desc->data, "dot11RSNAConfigSATimeout") == 0)
+		wpa_sm_param = RSNA_SA_TIMEOUT;
+
+	if (wpa_sm_param != -1) {
+		char *end;
+		int val;
+
+		val = strtol(new_value, &end, 0);
+		if (*end) {
+			dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+				       "Invalid value for property %s",
+				       property_desc->dbus_property);
+			return FALSE;
+		}
+
+		if (wpa_sm_set_param(wpa_s->wpa, wpa_sm_param, val)) {
+			dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+				       "Failed to apply interface property %s",
+				       property_desc->dbus_property);
+			return FALSE;
+		}
+	}
+
 	ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
 			  new_value);
 	if (os_snprintf_error(combined_len, ret)) {
@@ -4588,6 +4750,27 @@
 
 
 /**
+ * wpas_dbus_getter_mac_address - Get MAC address of an interface
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: a list of stations
+ *
+ * Getter for "MACAddress" property.
+ */
+dbus_bool_t wpas_dbus_getter_mac_address(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						      wpa_s->own_addr, ETH_ALEN,
+						      error);
+}
+
+
+/**
  * wpas_dbus_getter_sta_address - Return the address of a connected station
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -5095,7 +5278,7 @@
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
 	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
-	const char *key_mgmt[16]; /* max 16 key managements may be supported */
+	const char *key_mgmt[18]; /* max 18 key managements may be supported */
 	int n;
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -5145,8 +5328,12 @@
 #ifdef CONFIG_SAE
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_SAE)
 		key_mgmt[n++] = "sae";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+		key_mgmt[n++] = "sae-ext-key";
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE)
 		key_mgmt[n++] = "ft-sae";
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		key_mgmt[n++] = "ft-sae-ext-key";
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_OWE
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_OWE)
@@ -5929,3 +6116,28 @@
 }
 
 #endif /* CONFIG_MESH */
+
+
+/**
+ * wpas_dbus_getter_signal_change - Get signal change
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "SignalChange" property.
+ */
+dbus_bool_t wpas_dbus_getter_signal_change(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	struct wpa_signal_info si = wpa_s->last_signal_info;
+
+	if (wpas_dbus_new_from_signal_information(iter, &si) != 0) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
+	return TRUE;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index a421083..97fa337 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -196,6 +196,7 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_stas);
 DECLARE_ACCESSOR(wpas_dbus_getter_mac_address_randomization_mask);
 DECLARE_ACCESSOR(wpas_dbus_setter_mac_address_randomization_mask);
+DECLARE_ACCESSOR(wpas_dbus_getter_mac_address);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_address);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_aid);
 DECLARE_ACCESSOR(wpas_dbus_getter_sta_caps);
@@ -246,6 +247,8 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers);
 DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group);
 
+DECLARE_ACCESSOR(wpas_dbus_getter_signal_change);
+
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index de79178..a178d87 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -355,9 +355,11 @@
 	char *pg_object_path = NULL;
 	int persistent_group = 0;
 	int freq = 0;
+	int retry_limit = 0;
 	char *iface = NULL;
 	unsigned int group_id = 0;
 	struct wpa_ssid *ssid;
+	u8 go_bssid_buf[ETH_ALEN], *go_bssid = NULL;
 
 	dbus_message_iter_init(message, &iter);
 
@@ -376,12 +378,23 @@
 			freq = entry.int32_value;
 			if (freq <= 0)
 				goto inv_args_clear;
+		} else if (os_strcmp(entry.key, "retry_limit") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
+			retry_limit = entry.int32_value;
+			if (retry_limit <= 0)
+				goto inv_args_clear;
 		} else if (os_strcmp(entry.key, "persistent_group_object") ==
 			   0 &&
-			   entry.type == DBUS_TYPE_OBJECT_PATH)
+			   entry.type == DBUS_TYPE_OBJECT_PATH) {
 			pg_object_path = os_strdup(entry.str_value);
-		else
+		} else if (os_strcmp(entry.key, "go_bssid") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, go_bssid_buf))
+				goto inv_args_clear;
+			go_bssid = go_bssid_buf;
+		} else {
 			goto inv_args_clear;
+		}
 
 		wpa_dbus_dict_entry_clear(&entry);
 	}
@@ -426,7 +439,8 @@
 
 		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
 						  0, 0, 0, 0, NULL, 0, 0,
-						  false)) {
+						  false, retry_limit,
+						  go_bssid)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index d9009ba..06f74ad 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -11,6 +11,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "drivers/driver.h"
 #include "dbus_common.h"
 #include "dbus_common_i.h"
 #include "dbus_new.h"
@@ -1023,3 +1024,134 @@
 	}
 	return NULL;
 }
+
+
+/**
+ * wpas_dbus_new_from_signal_information - Adds a wpa_signal_info
+ * to a DBusMessage.
+ * @msg: Pointer to message to append fields to
+ * @si: Pointer to wpa_signal_info to add to the message
+ * Returns: 0 on success, otherwise, an errorcode
+ *
+ * Adds all the pertinent fields from a wpa_signal_info to a DBusMessage.
+ * The same logic is useful in both responding to signal_poll calls, and
+ * sending signal_change signals.
+ */
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+					  struct wpa_signal_info *si)
+{
+	DBusMessageIter iter_dict, variant_iter;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+					si->data.signal) ||
+	    !wpa_dbus_dict_append_uint32(&iter_dict, "linkspeed",
+					si->data.current_tx_rate / 1000) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+					si->current_noise) ||
+	    !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+					 si->frequency) ||
+	    (si->chanwidth != CHAN_WIDTH_UNKNOWN &&
+	     !wpa_dbus_dict_append_string(
+		     &iter_dict, "width",
+		     channel_width_to_string(si->chanwidth))) ||
+	    (si->center_frq1 > 0 && si->center_frq2 > 0 &&
+	     (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+					  si->center_frq1) ||
+	      !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+					  si->center_frq2))) ||
+	    (si->data.avg_signal &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+					 si->data.avg_signal)) ||
+	    (si->data.rx_bytes &&
+	     !wpa_dbus_dict_append_uint64(&iter_dict, "rx-bytes",
+					  si->data.rx_bytes)) ||
+	    (si->data.tx_bytes &&
+	     !wpa_dbus_dict_append_uint64(&iter_dict, "tx-bytes",
+					  si->data.tx_bytes)) ||
+	    (si->data.rx_packets &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-packets",
+					  si->data.rx_packets)) ||
+	    (si->data.tx_packets &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-packets",
+					  si->data.tx_packets)) ||
+	    (si->data.beacons_count &&
+	     !wpa_dbus_dict_append_uint64(&iter_dict, "beacons",
+					  si->data.beacons_count)) ||
+	    (si->data.current_rx_rate &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "linkrxspeed",
+					  si->data.current_rx_rate)) ||
+	    (si->data.current_rx_rate &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "linktxspeed",
+					  si->data.current_tx_rate)) ||
+	    (si->data.inactive_msec &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "inactive-time",
+					 si->data.inactive_msec)) ||
+	    (si->data.tx_retry_failed &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "retries-failed",
+					  si->data.tx_retry_failed)) ||
+	    (si->data.tx_retry_count &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "retries",
+					  si->data.tx_retry_count)) ||
+	    (si->data.last_ack_rssi &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "last-ack-rssi",
+					 si->data.last_ack_rssi)) ||
+	    (si->data.fcs_error_count &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "fcs-errors",
+					  si->data.fcs_error_count)) ||
+	    (si->data.beacon_loss_count &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "beacon-losses",
+					  si->data.beacon_loss_count)) ||
+	    (si->data.expected_throughput &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "expected-throughput",
+					  si->data.expected_throughput)) ||
+	    (si->data.rx_drop_misc &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-drop-misc",
+					  si->data.rx_drop_misc)) ||
+	    (si->data.rx_mpdus &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mpdus",
+					  si->data.rx_mpdus)) ||
+	    (si->data.rx_hemcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-mcs",
+					  si->data.rx_hemcs)) ||
+	    (si->data.tx_hemcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-mcs",
+					  si->data.tx_hemcs)) ||
+	    (si->data.rx_vhtmcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-mcs",
+					  si->data.rx_vhtmcs)) ||
+	    (si->data.tx_vhtmcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-mcs",
+					  si->data.tx_vhtmcs)) ||
+	    (si->data.rx_mcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-mcs",
+					  si->data.rx_mcs)) ||
+	    (si->data.tx_mcs &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-mcs",
+					  si->data.tx_mcs)) ||
+	    (si->data.rx_he_nss &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-he-nss",
+					  si->data.rx_he_nss)) ||
+	    (si->data.tx_he_nss &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-he-nss",
+					  si->data.tx_he_nss)) ||
+	    (si->data.rx_vht_nss &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "rx-vht-nss",
+					  si->data.rx_vht_nss)) ||
+	    (si->data.tx_vht_nss &&
+	     !wpa_dbus_dict_append_uint32(&iter_dict, "tx-vht-nss",
+					  si->data.tx_vht_nss)) ||
+	    (si->data.avg_beacon_signal &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-beacon-rssi",
+					 si->data.avg_beacon_signal)) ||
+	    (si->data.avg_ack_signal &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-ack-rssi",
+					 si->data.avg_ack_signal)) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		return -ENOMEM;
+
+	return 0;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 7b63b28..c8d44a0 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -12,6 +12,8 @@
 
 #include <dbus/dbus.h>
 
+struct wpa_signal_info;
+
 typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
 					      void *user_data);
 typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
@@ -151,4 +153,7 @@
 					    const char *fallback_name,
 					    const char *fallback_string);
 
+int wpas_dbus_new_from_signal_information(DBusMessageIter *iter,
+					  struct wpa_signal_info *si);
+
 #endif /* WPA_DBUS_CTRL_H */
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 02012d1..898765c 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -2,7 +2,7 @@
 
 <refentry>
   <refentryinfo>
-    <date>07 August 2019</date>
+    <date>1 February 2023</date>
   </refentryinfo>
 
   <refmeta>
@@ -22,6 +22,7 @@
       <arg>-D<replaceable>driver</replaceable></arg>
       <arg>-P<replaceable>PID_file</replaceable></arg>
       <arg>-f<replaceable>output file</replaceable></arg>
+      <arg>-I<replaceable>additional config file</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
   <refsect1>
@@ -29,7 +30,7 @@
 
     <para>
     Wireless networks do not require physical access to the network equipment
-    in the same way as wired networks. This makes it easier for unauthorized
+    in the same way that wired networks do. This makes it easier for unauthorized
     users to passively monitor a network and capture all transmitted frames.
     In addition, unauthorized use of the network is much easier. In many cases,
     this can happen even without user's explicit knowledge since the wireless
@@ -41,14 +42,14 @@
     Link-layer encryption can be used to provide a layer of security for
     wireless networks. The original wireless LAN standard, IEEE 802.11,
     included a simple encryption mechanism, WEP. However, that proved to
-    be flawed in many areas and network protected with WEP cannot be consider
+    be flawed in many areas and networks protected with WEP cannot be considered
     secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
     can be used to improve the network security, but even that has inherited
     security issues due to the use of WEP for encryption. Wi-Fi Protected
-    Access and IEEE 802.11i amendment to the wireless LAN standard introduce
+    Access and the IEEE 802.11i amendment to the wireless LAN standard introduce
     a much improved mechanism for securing wireless networks. IEEE 802.11i
-    enabled networks that are using CCMP (encryption mechanism based on strong
-    cryptographic algorithm AES) can finally be called secure used for
+    enabled networks that are using CCMP (an encryption mechanism based on the strong
+    cryptographic algorithm AES) can finally be called secure and used for
     applications which require efficient protection against unauthorized
     access.
     </para>
@@ -69,13 +70,13 @@
     wpa_supplicant.</para>
 
     <para>Before wpa_supplicant can do its work, the network interface
-    must be available.  That means that the physical device must be
+    must be available. That means that the physical device must be
     present and enabled, and the driver for the device must be
     loaded. The daemon will exit immediately if the device is not already
     available.</para>
 
     <para>After <command>wpa_supplicant</command> has configured the
-    network device, higher level configuration such as DHCP may
+    network device, higher level configuration of the device, such as DHCP, may
     proceed.  There are a variety of ways to integrate wpa_supplicant
     into a machine's networking scripts, a few of which are described
     in sections below.</para>
@@ -86,7 +87,7 @@
     <itemizedlist>
       <listitem>
 	<para><command>wpa_supplicant</command> requests the kernel
-	driver to scan neighboring BSSes</para>
+	driver to scan neighboring BSSes (Basic Service Set)</para>
       </listitem>
 
       <listitem>
@@ -341,6 +342,13 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-I filename</term>
+	<listitem>
+          <para>Path to additional configuration file.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-d</term>
 	<listitem>
 	  <para>Increase debugging verbosity (<option>-dd</option> even
@@ -512,10 +520,10 @@
 wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
 </programlisting></blockquote>
 
-    <para>This makes the process fork into background.</para>
+    <para>This makes the process fork into the background.</para>
 
-    <para>The easiest way to debug problems, and to get debug log for
-    bug reports, is to start <command>wpa_supplicant</command> on
+    <para>The easiest way to debug problems, and to get a debug log for
+    bug reports, is to start <command>wpa_supplicant</command> in the
     foreground with debugging enabled:</para>
 
 <blockquote><programlisting>
@@ -699,7 +707,7 @@
   <refsect1>
     <title>Interface to pcmcia-cs/cardmrg</title>
 
-    <para>For example, following small changes to pcmcia-cs scripts
+    <para>For example, the following small changes to pcmcia-cs scripts
     can be used to enable WPA support:</para>
 
     <para>Add MODE="Managed" and WPA="y" to the network scheme in
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index bcf614b..895d5fa 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -17,6 +17,7 @@
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/gas_server.h"
+#include "crypto/random.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "wpa_supplicant_i.h"
@@ -418,12 +419,21 @@
 		DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
 		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
 		DPP_PA_CONNECTION_STATUS_RESULT);
-	offchannel_send_action(wpa_s, auth->curr_freq,
-			       auth->peer_mac_addr, wpa_s->own_addr, broadcast,
-			       wpabuf_head(msg), wpabuf_len(msg),
-			       500, wpas_dpp_tx_status, 0);
+	if (offchannel_send_action(wpa_s, auth->curr_freq,
+				   auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+				   wpabuf_head(msg), wpabuf_len(msg),
+				   500, wpas_dpp_tx_status, 0) < 0) {
+		wpas_notify_dpp_connection_status_sent(wpa_s, result);
+		wpabuf_free(msg);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
 	wpabuf_free(msg);
 
+	auth->conn_result_status = result;
+	auth->tx_conn_status_result_started = 1;
 	/* This exchange will be terminated in the TX status handler */
 	auth->remove_on_tx_status = 1;
 
@@ -490,6 +500,9 @@
 #endif /* CONFIG_DPP2 */
 
 	if (wpa_s->dpp_auth->remove_on_tx_status) {
+		if (auth->tx_conn_status_result_started) {
+			wpas_notify_dpp_connection_status_sent(wpa_s, auth->conn_result_status);
+		}
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Terminate authentication exchange due to a request to do so on TX status");
 		eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
@@ -802,7 +815,17 @@
 	}
 
 	addr = get_param(cmd, " tcp_addr=");
-	if (addr) {
+	if (addr && os_strcmp(addr, "from-uri") == 0) {
+		os_free(addr);
+		if (!peer_bi->host) {
+			wpa_printf(MSG_INFO,
+				   "DPP: TCP address not available in peer URI");
+			return -1;
+		}
+		tcp = 1;
+		os_memcpy(&ipaddr, peer_bi->host, sizeof(ipaddr));
+		tcp_port = peer_bi->port;
+	} else if (addr) {
 		int res;
 
 		res = hostapd_parse_ip_addr(addr, &ipaddr);
@@ -897,6 +920,9 @@
 	if (tcp)
 		return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port,
 				    wpa_s->conf->dpp_name, DPP_NETROLE_STA,
+				    wpa_s->conf->dpp_mud_url,
+				    wpa_s->conf->dpp_extra_conf_req_name,
+				    wpa_s->conf->dpp_extra_conf_req_value,
 				    wpa_s, wpa_s, wpas_dpp_process_conf_obj,
 				    wpas_dpp_tcp_msg_sent);
 #endif /* CONFIG_DPP2 */
@@ -1142,6 +1168,21 @@
 		return;
 	}
 
+	if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+				"No matching peer bootstrapping key found for PKEX - ignore message");
+			return;
+		}
+
+		if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+				"Mismatching peer PKEX bootstrapping key - ignore message");
+			return;
+		}
+	}
+
 	if (wpa_s->dpp_auth) {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
 			"Already in DPP authentication exchange - ignore new one");
@@ -1267,6 +1308,9 @@
 		ssid->dpp_connector = os_strdup(conf->connector);
 		if (!ssid->dpp_connector)
 			goto fail;
+
+		ssid->dpp_connector_privacy =
+			wpa_s->conf->dpp_connector_privacy_default;
 	}
 
 	if (conf->c_sign_key) {
@@ -1309,7 +1353,10 @@
 		if (dpp_akm_sae(conf->akm))
 			ssid->key_mgmt |= WPA_KEY_MGMT_SAE |
 				WPA_KEY_MGMT_FT_SAE;
-		ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+		if (dpp_akm_psk(conf->akm))
+			ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+		else
+			ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
 		if (conf->passphrase[0]) {
 			if (wpa_config_set_quoted(ssid, "psk",
 						  conf->passphrase) < 0)
@@ -1444,7 +1491,7 @@
 
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
 
-	wpas_notify_dpp_config_received(wpa_s, ssid);
+	wpas_notify_dpp_config_received(wpa_s, ssid, auth->conn_status_requested ? 1 : 0);
 
 	if (wpa_s->conf->dpp_config_processing == 2)
 		ssid->disabled = 0;
@@ -1606,6 +1653,14 @@
 			conf->server_name);
 #endif /* CONFIG_DPP2 */
 
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "success");
+		wpa_s->dpp_pb_result_indicated = true;
+	}
+
+#endif /* CONFIG_DPP3 */
+
 	return wpas_dpp_process_config(wpa_s, auth, conf);
 }
 
@@ -1789,7 +1844,7 @@
 		wpabuf_free(msg);
 
 		/* This exchange will be terminated in the TX status handler */
-		if (wpa_s->conf->dpp_config_processing < 2 ||
+		if (wpa_s->conf->dpp_config_processing < 1 ||
 		    wpa_s->dpp_conf_backup_received)
 			auth->remove_on_tx_status = 1;
 		return;
@@ -1832,7 +1887,9 @@
 	buf = dpp_build_conf_req_helper(auth, wpa_s->conf->dpp_name,
 					wpa_s->dpp_netrole,
 					wpa_s->conf->dpp_mud_url,
-					supp_op_classes);
+					supp_op_classes,
+					wpa_s->conf->dpp_extra_conf_req_name,
+					wpa_s->conf->dpp_extra_conf_req_value);
 	os_free(supp_op_classes);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
@@ -1869,7 +1926,7 @@
 static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+	dpp_notify_auth_success(wpa_s->dpp_auth, initiator);
 	wpas_notify_dpp_auth_success(wpa_s);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
@@ -2019,6 +2076,42 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static bool wpas_dpp_pb_active(struct wpa_supplicant *wpa_s)
+{
+	return (wpa_s->dpp_pb_time.sec || wpa_s->dpp_pb_time.usec) &&
+		wpa_s->dpp_pb_configurator;
+}
+
+
+static void wpas_dpp_remove_pb_hash(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	if (!wpa_s->dpp_pb_bi)
+		return;
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		struct dpp_pb_info *info = &wpa_s->dpp_pb[i];
+
+		if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+			continue;
+		if (os_memcmp(info->hash, wpa_s->dpp_pb_resp_hash,
+			      SHA256_MAC_LEN) == 0) {
+			/* Allow a new push button session to be established
+			 * immediately without the successfully completed
+			 * session triggering session overlap. */
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+		}
+	}
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src,
 				    const u8 *hdr, const u8 *buf, size_t len)
 {
@@ -2059,7 +2152,8 @@
 		int freq;
 
 		wpa_msg(wpa_s, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+			DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+			auth->conf_resp_status);
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
 		wpas_notify_dpp_config_accepted(wpa_s);
 		eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout,
@@ -2080,7 +2174,8 @@
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 	if (status == DPP_STATUS_OK) {
-		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+			auth->conf_resp_status);
 		wpas_notify_dpp_config_sent(wpa_s);
 	}
 	else {
@@ -2090,6 +2185,20 @@
 	dpp_auth_deinit(auth);
 	wpa_s->dpp_auth = NULL;
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
+		if (status == DPP_STATUS_OK)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"no-configuration-available");
+		wpa_s->dpp_pb_result_indicated = true;
+		if (status == DPP_STATUS_OK)
+			wpas_dpp_remove_pb_hash(wpa_s);
+		wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2195,6 +2304,7 @@
 	u16 r_bootstrap_len;
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_authentication *auth;
+	unsigned int wait_time, max_wait_time;
 
 	if (!wpa_s->dpp)
 		return;
@@ -2226,6 +2336,9 @@
 		return;
 	}
 
+	wpa_printf(MSG_DEBUG, "DPP: Start Authentication exchange with " MACSTR
+		   " based on the received Presence Announcement",
+		   MAC2STR(src));
 	auth = dpp_auth_init(wpa_s->dpp, wpa_s, peer_bi, NULL,
 			     DPP_CAPAB_CONFIGURATOR, freq, NULL, 0);
 	if (!auth)
@@ -2242,6 +2355,13 @@
 	 * MAC address information from the bootstrapping information. */
 	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
 
+	wait_time = wpa_s->max_remain_on_chan;
+	max_wait_time = wpa_s->dpp_resp_wait_time ?
+		wpa_s->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wpas_dpp_stop_listen_for_tx(wpa_s, freq, wait_time);
+
 	wpa_s->dpp_auth = auth;
 	if (wpas_dpp_auth_init_next(wpa_s) < 0) {
 		dpp_auth_deinit(wpa_s->dpp_auth);
@@ -2536,6 +2656,8 @@
 		return;
 	}
 
+	os_memset(&intro, 0, sizeof(intro));
+
 	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
 			       &trans_id_len);
 	if (!trans_id || trans_id_len != 1) {
@@ -2587,7 +2709,7 @@
 			     ssid->dpp_netaccesskey_len,
 			     ssid->dpp_csign,
 			     ssid->dpp_csign_len,
-			     connector, connector_len, &expiry);
+			     connector, connector_len, &expiry, NULL);
 	if (res != DPP_STATUS_OK) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in failure");
@@ -2603,6 +2725,7 @@
 	if (!entry)
 		goto fail;
 	os_memcpy(entry->aa, src, ETH_ALEN);
+	os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
 	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
 	entry->pmk_len = intro.pmk_len;
@@ -2646,7 +2769,7 @@
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
 fail:
-	os_memset(&intro, 0, sizeof(intro));
+	dpp_peer_intro_deinit(&intro);
 }
 
 
@@ -2706,19 +2829,38 @@
 }
 
 
+static void wpas_dpp_pkex_clear_code(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp_pkex_code && !wpa_s->dpp_pkex_identifier)
+		return;
+
+	/* Delete PKEX code and identifier on successful completion of
+	 * PKEX. We are not supposed to reuse these without being
+	 * explicitly requested to perform PKEX again. */
+	wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+	os_free(wpa_s->dpp_pkex_code);
+	wpa_s->dpp_pkex_code = NULL;
+	os_free(wpa_s->dpp_pkex_identifier);
+	wpa_s->dpp_pkex_identifier = NULL;
+
+}
+
+
 #ifdef CONFIG_DPP2
 static int wpas_dpp_pkex_done(void *ctx, void *conn,
 			      struct dpp_bootstrap_info *peer_bi)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	const char *cmd = wpa_s->dpp_pkex_auth_cmd;
+	char cmd[500];
 	const char *pos;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	struct dpp_bootstrap_info *own_bi = NULL;
 	struct dpp_authentication *auth;
 
-	if (!cmd)
-		cmd = "";
+	wpas_dpp_pkex_clear_code(wpa_s);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
 	wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
 		   cmd);
 
@@ -2766,7 +2908,11 @@
 	}
 
 	return dpp_tcp_auth(wpa_s->dpp, conn, auth, wpa_s->conf->dpp_name,
-			    DPP_NETROLE_STA, wpas_dpp_process_conf_obj,
+			    DPP_NETROLE_STA,
+			    wpa_s->conf->dpp_mud_url,
+			    wpa_s->conf->dpp_extra_conf_req_name,
+			    wpa_s->conf->dpp_extra_conf_req_value,
+			    wpas_dpp_process_conf_obj,
 			    wpas_dpp_tcp_msg_sent);
 }
 #endif /* CONFIG_DPP2 */
@@ -2787,7 +2933,8 @@
 	wpa_s->dpp_pkex = NULL;
 	pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr,
 			     wpa_s->dpp_pkex_identifier,
-			     wpa_s->dpp_pkex_code, v2);
+			     wpa_s->dpp_pkex_code, wpa_s->dpp_pkex_code_len,
+			     v2);
 	if (!pkex)
 		return -1;
 	pkex->forced_ver = ver != PKEX_VER_AUTO;
@@ -2946,6 +3093,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP2
+	if (dpp_controller_is_own_pkex_req(wpa_s->dpp, buf, len)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX Exchange Request is from local Controller - ignore request");
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (wpa_s->dpp_pkex) {
 		/* TODO: Support parallel operations */
 		wpa_printf(MSG_DEBUG,
@@ -2957,6 +3112,7 @@
 						   wpa_s->own_addr, src,
 						   wpa_s->dpp_pkex_identifier,
 						   wpa_s->dpp_pkex_code,
+						   wpa_s->dpp_pkex_code_len,
 						   buf, len, v2);
 	if (!wpa_s->dpp_pkex) {
 		wpa_printf(MSG_DEBUG,
@@ -2964,6 +3120,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_announcement) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Started PB PKEX (no more PB announcements)");
+		wpabuf_free(wpa_s->dpp_pb_announcement);
+		wpa_s->dpp_pb_announcement = NULL;
+	}
+#endif /* CONFIG_DPP3 */
 	wpa_s->dpp_pkex_wait_auth_req = false;
 	msg = wpa_s->dpp_pkex->exchange_resp;
 	wait_time = wpa_s->max_remain_on_chan;
@@ -3028,10 +3192,35 @@
 {
 	struct dpp_bootstrap_info *bi;
 
+	wpas_dpp_pkex_clear_code(wpa_s);
 	bi = dpp_pkex_finish(wpa_s->dpp, wpa_s->dpp_pkex, peer, freq);
 	if (!bi)
 		return NULL;
+
 	wpa_s->dpp_pkex = NULL;
+
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && !wpa_s->dpp_pb_configurator &&
+	    os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_init_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement response hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement response",
+			    wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpas_dpp_push_button_stop(wpa_s);
+		return NULL;
+	}
+#endif /* CONFIG_DPP3 */
+
 	return bi;
 }
 
@@ -3113,6 +3302,28 @@
 	if (!bi)
 		return;
 
+#ifdef CONFIG_DPP3
+	if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_configurator &&
+	    os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_resp_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+			    wpa_s->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
 		    wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
@@ -3128,6 +3339,589 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static void wpas_dpp_pb_pkex_init(struct wpa_supplicant *wpa_s,
+				  unsigned int freq, const u8 *src,
+				  const u8 *r_hash)
+{
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	size_t len;
+
+	if (wpa_s->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending previously generated PKEX Exchange Request to "
+			   MACSTR, MAC2STR(src));
+		msg = wpa_s->dpp_pkex->exchange_req;
+		wait_time = wpa_s->max_remain_on_chan;
+		if (wait_time > 2000)
+			wait_time = 2000;
+		offchannel_send_action(wpa_s, freq, src,
+				       wpa_s->own_addr, broadcast,
+				       wpabuf_head(msg), wpabuf_len(msg),
+				       wait_time, wpas_dpp_tx_pkex_status, 0);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+		   MACSTR, MAC2STR(src));
+
+	if (!wpa_s->dpp_pb_cmd) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No configuration to provision as push button Configurator");
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi;
+	os_memcpy(wpa_s->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+	pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr,
+			     "PBPKEX", (const char *) wpa_s->dpp_pb_c_nonce,
+			     wpa_s->dpp_pb_bi->curve->nonce_len,
+			     true);
+	if (!pkex) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+	pkex->freq = freq;
+
+	wpa_s->dpp_pkex = pkex;
+	msg = wpa_s->dpp_pkex->exchange_req;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_REQ);
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	offchannel_send_action(wpa_s, pkex->freq, src,
+			       wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_pkex_status, 0);
+	pkex->exch_req_wait_time = 2000;
+	pkex->exch_req_tries = 1;
+
+	/* Use the externally provided configuration */
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	len = 30 + os_strlen(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pkex_auth_cmd = os_malloc(len);
+	if (wpa_s->dpp_pkex_auth_cmd)
+		os_snprintf(wpa_s->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    wpa_s->dpp_pkex_bi->id, wpa_s->dpp_pb_cmd);
+	else
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static void
+wpas_dpp_rx_pb_presence_announcement(struct wpa_supplicant *wpa_s,
+				     const u8 *src, const u8 *hdr,
+				     const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *r_hash;
+	u16 r_hash_len;
+	unsigned int i;
+	bool found = false;
+	struct dpp_pb_info *info, *tmp;
+	struct os_reltime now, age;
+	struct wpabuf *msg;
+
+	os_get_reltime(&now);
+	wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+		   MACSTR, MAC2STR(src));
+
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		info = &wpa_s->dpp_pb[i];
+		if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+		    os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Active push button Enrollee already known");
+		found = true;
+		info->rx_time = now;
+	}
+
+	if (!found) {
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &wpa_s->dpp_pb[i];
+			if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+				continue;
+
+			if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+				wpa_hexdump(MSG_DEBUG,
+					    "DPP: Push button Enrollee hash expired",
+					    tmp->hash, SHA256_MAC_LEN);
+				tmp->rx_time.sec = 0;
+				tmp->rx_time.usec = 0;
+				continue;
+			}
+
+			wpa_hexdump(MSG_DEBUG,
+				    "DPP: Push button session overlap with hash",
+				    tmp->hash, SHA256_MAC_LEN);
+			if (!wpa_s->dpp_pb_result_indicated &&
+			    wpas_dpp_pb_active(wpa_s)) {
+				wpa_msg(wpa_s, MSG_INFO,
+					DPP_EVENT_PB_RESULT "session-overlap");
+				wpa_s->dpp_pb_result_indicated = true;
+			}
+			wpas_dpp_push_button_stop(wpa_s);
+			return;
+		}
+
+		/* Replace the oldest entry */
+		info = &wpa_s->dpp_pb[0];
+		for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &wpa_s->dpp_pb[i];
+			if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+				info = tmp;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+		os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+		info->rx_time = now;
+	}
+
+	if (!wpas_dpp_pb_active(wpa_s)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Discard message since own push button has not been pressed");
+		return;
+	}
+
+	if (wpa_s->dpp_pb_announce_time.sec == 0 &&
+	    wpa_s->dpp_pb_announce_time.usec == 0) {
+		/* Start a wait before allowing PKEX to be initiated */
+		wpa_s->dpp_pb_announce_time = now;
+	}
+
+	if (!wpa_s->dpp_pb_bi) {
+		int res;
+
+		res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex");
+		if (res < 0)
+			return;
+		wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res);
+		if (!wpa_s->dpp_pb_bi)
+			return;
+
+		if (random_get_bytes(wpa_s->dpp_pb_c_nonce,
+				     wpa_s->dpp_pb_bi->curve->nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate C-nonce");
+			wpas_dpp_push_button_stop(wpa_s);
+			return;
+		}
+	}
+
+	/* Skip the response if one was sent within last 50 ms since the
+	 * Enrollee is going to send out at least three announcement messages.
+	 */
+	os_reltime_sub(&now, &wpa_s->dpp_pb_last_resp, &age);
+	if (age.sec == 0 && age.usec < 50000) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+		return;
+	}
+
+	msg = dpp_build_pb_announcement_resp(
+		wpa_s->dpp_pb_bi, r_hash, wpa_s->dpp_pb_c_nonce,
+		wpa_s->dpp_pb_bi->curve->nonce_len);
+	if (!msg) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send Push Button Presence Announcement Response to "
+		   MACSTR, MAC2STR(src));
+	wpa_s->dpp_pb_last_resp = now;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+	offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       0, NULL, 0);
+	wpabuf_free(msg);
+
+	if (os_reltime_expired(&now, &wpa_s->dpp_pb_announce_time, 15))
+		wpas_dpp_pb_pkex_init(wpa_s, freq, src, r_hash);
+}
+
+
+static void
+wpas_dpp_rx_pb_presence_announcement_resp(struct wpa_supplicant *wpa_s,
+					  const u8 *src, const u8 *hdr,
+					  const u8 *buf, size_t len,
+					  unsigned int freq)
+{
+	const u8 *i_hash, *r_hash, *c_nonce;
+	u16 i_hash_len, r_hash_len, c_nonce_len;
+	bool overlap = false;
+
+	if (!wpa_s->dpp_pb_announcement || !wpa_s->dpp_pb_bi ||
+	    wpa_s->dpp_pb_configurator) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Not in active push button Enrollee mode - discard Push Button Presence Announcement Response from "
+			   MACSTR, MAC2STR(src));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Push Button Presence Announcement Response from "
+		   MACSTR, MAC2STR(src));
+
+	i_hash = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+			      &i_hash_len);
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	c_nonce = dpp_get_attr(buf, len, DPP_ATTR_CONFIGURATOR_NONCE,
+			       &c_nonce_len);
+	if (!i_hash || i_hash_len != SHA256_MAC_LEN ||
+	    !r_hash || r_hash_len != SHA256_MAC_LEN ||
+	    !c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_hash, i_hash_len);
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator Nonce",
+		    c_nonce, c_nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ &&
+	    os_memcmp(r_hash, wpa_s->dpp_pb_bi->pubkey_hash_chirp,
+		      SHA256_MAC_LEN - 1) == 0)
+		goto skip_hash_check;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (os_memcmp(r_hash, wpa_s->dpp_pb_bi->pubkey_hash_chirp,
+		      SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unexpected push button Responder hash - abort");
+		overlap = true;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+skip_hash_check:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_s->dpp_pb_resp_freq &&
+	    os_memcmp(i_hash, wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Push button session overlap detected - abort");
+		overlap = true;
+	}
+
+	if (overlap) {
+		if (!wpa_s->dpp_pb_result_indicated) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"session-overlap");
+			wpa_s->dpp_pb_result_indicated = true;
+		}
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (!wpa_s->dpp_pb_resp_freq) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+			"discovered push button AP/Configurator " MACSTR,
+			MAC2STR(src));
+		wpa_s->dpp_pb_resp_freq = freq;
+		os_memcpy(wpa_s->dpp_pb_init_hash, i_hash, SHA256_MAC_LEN);
+		os_memcpy(wpa_s->dpp_pb_c_nonce, c_nonce, c_nonce_len);
+		wpa_s->dpp_pb_c_nonce_len = c_nonce_len;
+		/* Stop announcement iterations after at least one more full
+		 * round and one extra round for postponed session overlap
+		 * detection. */
+		wpa_s->dpp_pb_stop_iter = 3;
+	}
+}
+
+
+static void
+wpas_dpp_tx_priv_intro_status(struct wpa_supplicant *wpa_s,
+			      unsigned int freq, const u8 *dst,
+			      const u8 *src, const u8 *bssid,
+			      const u8 *data, size_t data_len,
+			      enum offchannel_send_action_result result)
+{
+	const char *res_txt;
+
+	res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
+		(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
+		 "FAILED");
+	wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
+		   " result=%s (DPP Private Peer Introduction Update)",
+		   freq, MAC2STR(dst), res_txt);
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+		" freq=%u result=%s", MAC2STR(dst), freq, res_txt);
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " version=%u",
+		MAC2STR(src), wpa_s->dpp_intro_peer_version);
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Try connection again after successful network introduction");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+}
+
+
+static int
+wpas_dpp_send_private_peer_intro_update(struct wpa_supplicant *wpa_s,
+					struct dpp_introduction *intro,
+					struct wpa_ssid *ssid,
+					const u8 *dst, unsigned int freq)
+{
+	struct wpabuf *pt, *msg, *enc_ct;
+	size_t len;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+	const u8 *aad;
+	size_t aad_len;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "HPKE(kem_id=%u kdf_id=%u aead_id=%u)",
+		   intro->kem_id, intro->kdf_id, intro->aead_id);
+
+	/* Plaintext for HPKE */
+	len = 5 + 4 + os_strlen(ssid->dpp_connector);
+	pt = wpabuf_alloc(len);
+	if (!pt)
+		return -1;
+
+	/* Protocol Version */
+	conn_ver = dpp_get_connector_version(ssid->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+	wpabuf_put_le16(pt, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(pt, 1);
+	wpabuf_put_u8(pt, ver);
+
+	/* Connector */
+	wpabuf_put_le16(pt, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(pt, os_strlen(ssid->dpp_connector));
+	wpabuf_put_str(pt, ssid->dpp_connector);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Plaintext for HPKE", pt);
+
+	/* HPKE(pt) using AP's public key (from its Connector) */
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, 0);
+	if (!msg) {
+		wpabuf_free(pt);
+		return -1;
+	}
+	aad = wpabuf_head_u8(msg) + 2; /* from the OUI field (inclusive) */
+	aad_len = DPP_HDR_LEN; /* to the DPP Frame Type field (inclusive) */
+	wpa_hexdump(MSG_MSGDUMP, "DPP: AAD for HPKE", aad, aad_len);
+
+	enc_ct = hpke_base_seal(intro->kem_id, intro->kdf_id, intro->aead_id,
+				intro->peer_key, NULL, 0, aad, aad_len,
+				wpabuf_head(pt), wpabuf_len(pt));
+	wpabuf_free(pt);
+	wpabuf_free(msg);
+	if (!enc_ct) {
+		wpa_printf(MSG_INFO, "DPP: HPKE Seal(Connector) failed");
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE enc|ct", enc_ct);
+
+	/* HPKE(pt) to generate payload for Wrapped Data */
+	len = 5 + 4 + wpabuf_len(enc_ct);
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, len);
+	if (!msg) {
+		wpabuf_free(enc_ct);
+		return -1;
+	}
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	/* Wrapped Data */
+	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+	wpabuf_put_le16(msg, wpabuf_len(enc_ct));
+	wpabuf_put_buf(msg, enc_ct);
+	wpabuf_free(enc_ct);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Update", msg);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(dst), freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
+	offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr, broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_priv_intro_status, 0);
+	wpabuf_free(msg);
+
+	return 0;
+}
+
+
+static void
+wpas_dpp_rx_priv_peer_intro_notify(struct wpa_supplicant *wpa_s,
+				   const u8 *src, const u8 *hdr,
+				   const u8 *buf, size_t len,
+				   unsigned int freq)
+{
+	struct wpa_ssid *ssid;
+	const u8 *connector, *trans_id, *version;
+	u16 connector_len, trans_id_len, version_len;
+	u8 peer_version = 1;
+	struct dpp_introduction intro;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_time now;
+	struct os_reltime rnow;
+	os_time_t expiry;
+	unsigned int seconds;
+	enum dpp_status_error res;
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Notify from "
+		   MACSTR, MAC2STR(src));
+	if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
+	    os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
+			   MACSTR " - drop", MAC2STR(src));
+		return;
+	}
+	offchannel_send_action_done(wpa_s);
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid == wpa_s->dpp_intro_network)
+			break;
+	}
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Profile not found for network introduction");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_transaction_id", MAC2STR(src));
+		goto fail;
+	}
+	if (trans_id[0] != TRANSACTION_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore frame with unexpected Transaction ID %u",
+			   trans_id[0]);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=transaction_id_mismatch", MAC2STR(src));
+		goto fail;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_connector", MAC2STR(src));
+		goto fail;
+	}
+
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include valid Version");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=missing_version", MAC2STR(src));
+		goto fail;
+	}
+
+	res = dpp_peer_intro(&intro, ssid->dpp_connector,
+			     ssid->dpp_netaccesskey,
+			     ssid->dpp_netaccesskey_len,
+			     ssid->dpp_csign,
+			     ssid->dpp_csign_len,
+			     connector, connector_len, &expiry, NULL);
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure");
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
+			" fail=peer_connector_validation_failed", MAC2STR(src));
+		wpas_dpp_send_conn_status_result(wpa_s, res);
+		goto fail;
+	}
+
+	peer_version = version[0];
+	if (intro.peer_version && intro.peer_version >= 2 &&
+	    peer_version != intro.peer_version) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+			   intro.peer_version, peer_version);
+		wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_NO_MATCH);
+		goto fail;
+	}
+	wpa_s->dpp_intro_peer_version = peer_version;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry)
+		goto fail;
+	entry->dpp_pfs = peer_version >= 2;
+	os_memcpy(entry->aa, src, ETH_ALEN);
+	os_memcpy(entry->spa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
+	entry->pmk_len = intro.pmk_len;
+	entry->akmp = WPA_KEY_MGMT_DPP;
+	if (expiry) {
+		os_get_time(&now);
+		seconds = expiry - now.sec;
+	} else {
+		seconds = 86400 * 7;
+	}
+
+	if (wpas_dpp_send_private_peer_intro_update(wpa_s, &intro, ssid, src,
+						    freq) < 0) {
+		os_free(entry);
+		goto fail;
+	}
+
+	os_get_reltime(&rnow);
+	entry->expiration = rnow.sec + seconds;
+	entry->reauth_time = rnow.sec + seconds;
+	entry->network_ctx = ssid;
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+
+	/* Association will be initiated from TX status handler for the Private
+	 * Peer Intro Update: wpas_dpp_tx_priv_intro_status() */
+
+fail:
+	dpp_peer_intro_deinit(&intro);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
 			const u8 *buf, size_t len, unsigned int freq)
 {
@@ -3151,6 +3945,15 @@
 		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
 		   MACSTR " freq=%u",
 		   crypto_suite, type, MAC2STR(src), freq);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->dpp_discard_public_action &&
+	    type != DPP_PA_PEER_DISCOVERY_RESP &&
+	    type != DPP_PA_PRIV_PEER_INTRO_NOTIFY) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Discard received DPP Public Action frame");
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (crypto_suite != 1) {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
 			   crypto_suite);
@@ -3230,6 +4033,20 @@
 		wpas_dpp_rx_reconfig_auth_conf(wpa_s, src, hdr, buf, len, freq);
 		break;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+		wpas_dpp_rx_pb_presence_announcement(wpa_s, src, hdr,
+						     buf, len, freq);
+		break;
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP:
+		wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr,
+							  buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_NOTIFY:
+		wpas_dpp_rx_priv_peer_intro_notify(wpa_s, src, hdr,
+						   buf, len, freq);
+		break;
+#endif /* CONFIG_DPP3 */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignored unsupported frame subtype %d", type);
@@ -3288,7 +4105,7 @@
 		 * TX status handler, but since there was no such handler call
 		 * yet, simply send out the event message and proceed with
 		 * exchange. */
-		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1");
+		dpp_notify_auth_success(auth, 1);
 		wpa_s->dpp_auth_ok_on_ack = 0;
 	}
 
@@ -3433,7 +4250,8 @@
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 	if (ok) {
-		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+			auth->conf_resp_status);
 		wpas_notify_dpp_config_sent(wpa_s);
 	}
 	else {
@@ -3443,6 +4261,20 @@
 	dpp_auth_deinit(wpa_s->dpp_auth);
 	wpa_s->dpp_auth = NULL;
 	wpabuf_free(resp);
+#ifdef CONFIG_DPP3
+	if (!wpa_s->dpp_pb_result_indicated && wpas_dpp_pb_active(wpa_s)) {
+		if (ok)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT
+				"could-not-connect");
+		wpa_s->dpp_pb_result_indicated = true;
+		if (ok)
+			wpas_dpp_remove_pb_hash(wpa_s);
+		wpas_dpp_push_button_stop(wpa_s);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -3493,6 +4325,63 @@
 }
 
 
+#ifdef CONFIG_DPP3
+static int wpas_dpp_start_private_peer_intro(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid,
+					     struct wpa_bss *bss)
+{
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	size_t len;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+
+	len = 5 + 5;
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_QUERY, len);
+	if (!msg)
+		return -1;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, TRANSACTION_ID);
+
+	conn_ver = dpp_get_connector_version(ssid->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+
+	/* Protocol Version */
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, ver);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Query", msg);
+
+	/* TODO: Timeout on AP response */
+	wait_time = wpa_s->max_remain_on_chan;
+	if (wait_time > 2000)
+		wait_time = 2000;
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(bss->bssid), bss->freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
+	offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
+			       broadcast,
+			       wpabuf_head(msg), wpabuf_len(msg),
+			       wait_time, wpas_dpp_tx_introduction_status, 0);
+	wpabuf_free(msg);
+
+	/* Request this connection attempt to terminate - new one will be
+	 * started when network introduction protocol completes */
+	os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
+	wpa_s->dpp_intro_network = ssid;
+	return 1;
+}
+#endif /* CONFIG_DPP3 */
+
+
 int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			   struct wpa_bss *bss)
 {
@@ -3509,7 +4398,7 @@
 	if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
 	    !(ied.key_mgmt & WPA_KEY_MGMT_DPP))
 		return 0; /* AP does not support DPP AKM - continue */
-	if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid))
+	if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr, ssid))
 		return 0; /* PMKSA exists for DPP AKM - continue */
 
 	if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
@@ -3532,11 +4421,18 @@
 	}
 
 	wpa_printf(MSG_DEBUG,
-		   "DPP: Starting network introduction protocol to derive PMKSA for "
-		   MACSTR, MAC2STR(bss->bssid));
+		   "DPP: Starting %snetwork introduction protocol to derive PMKSA for "
+		   MACSTR,
+		   ssid->dpp_connector_privacy ? "private " : "",
+		   MAC2STR(bss->bssid));
 	if (wpa_s->wpa_state == WPA_SCANNING)
 		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 
+#ifdef CONFIG_DPP3
+	if (ssid->dpp_connector_privacy)
+		return wpas_dpp_start_private_peer_intro(wpa_s, ssid, bss);
+#endif /* CONFIG_DPP3 */
+
 	len = 5 + 4 + os_strlen(ssid->dpp_connector);
 #ifdef CONFIG_DPP2
 	len += 5;
@@ -3726,6 +4622,7 @@
 	wpa_s->dpp_pkex_code = os_strdup(pos + 6);
 	if (!wpa_s->dpp_pkex_code)
 		return -1;
+	wpa_s->dpp_pkex_code_len = os_strlen(wpa_s->dpp_pkex_code);
 
 	pos = os_strstr(cmd, " ver=");
 	if (pos) {
@@ -3774,7 +4671,7 @@
 			return -1;
 	}
 
-	if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
+	if ((id_val != 0 && id_val != 1))
 		return -1;
 
 	/* TODO: Support multiple PKEX entries */
@@ -3803,6 +4700,9 @@
 	wpa_s->dpp_pkex_wait_auth_req = false;
 	if (wpa_s->dpp_gas_client && wpa_s->dpp_gas_dialog_token >= 0)
 		gas_query_stop(wpa_s->gas, wpa_s->dpp_gas_dialog_token);
+#ifdef CONFIG_DPP3
+	wpas_dpp_push_button_stop(wpa_s);
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -4077,29 +4977,20 @@
 }
 
 
-static void wpas_dpp_chirp_scan_res_handler(struct wpa_supplicant *wpa_s,
-					    struct wpa_scan_results *scan_res)
+static int * wpas_dpp_presence_ann_channels(struct wpa_supplicant *wpa_s,
+					    struct dpp_bootstrap_info *bi)
 {
-	struct dpp_bootstrap_info *bi = wpa_s->dpp_chirp_bi;
 	unsigned int i;
 	struct hostapd_hw_modes *mode;
 	int c;
 	struct wpa_bss *bss;
 	bool chan6 = wpa_s->hw.modes == NULL;
-
-	if (!bi && !wpa_s->dpp_reconfig_ssid)
-		return;
-
-	wpa_s->dpp_chirp_scan_done = 1;
-
-	os_free(wpa_s->dpp_chirp_freqs);
-	wpa_s->dpp_chirp_freqs = NULL;
+	int *freqs = NULL;
 
 	/* Channels from own bootstrapping info */
 	if (bi) {
 		for (i = 0; i < bi->num_freq; i++)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs,
-					     bi->freq[i]);
+			int_array_add_unique(&freqs, bi->freq[i]);
 	}
 
 	/* Preferred chirping channels */
@@ -4117,7 +5008,7 @@
 		}
 	}
 	if (chan6)
-		int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
+		int_array_add_unique(&freqs, 2437);
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
 			HOSTAPD_MODE_IEEE80211A, false);
@@ -4136,9 +5027,9 @@
 				chan149 = 1;
 		}
 		if (chan149)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 5745);
+			int_array_add_unique(&freqs, 5745);
 		else if (chan44)
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 5220);
+			int_array_add_unique(&freqs, 5220);
 	}
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
@@ -4151,7 +5042,7 @@
 					   HOSTAPD_CHAN_RADAR)) ||
 			    chan->freq != 60480)
 				continue;
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs, 60480);
+			int_array_add_unique(&freqs, 60480);
 			break;
 		}
 	}
@@ -4160,10 +5051,26 @@
 	 * Connectivity element */
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (wpa_bss_get_vendor_ie(bss, DPP_CC_IE_VENDOR_TYPE))
-			int_array_add_unique(&wpa_s->dpp_chirp_freqs,
-					     bss->freq);
+			int_array_add_unique(&freqs, bss->freq);
 	}
 
+	return freqs;
+}
+
+
+static void wpas_dpp_chirp_scan_res_handler(struct wpa_supplicant *wpa_s,
+					    struct wpa_scan_results *scan_res)
+{
+	struct dpp_bootstrap_info *bi = wpa_s->dpp_chirp_bi;
+
+	if (!bi && !wpa_s->dpp_reconfig_ssid)
+		return;
+
+	wpa_s->dpp_chirp_scan_done = 1;
+
+	os_free(wpa_s->dpp_chirp_freqs);
+	wpa_s->dpp_chirp_freqs = wpas_dpp_presence_ann_channels(wpa_s, bi);
+
 	if (!wpa_s->dpp_chirp_freqs ||
 	    eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL) < 0)
 		wpas_dpp_chirp_stop(wpa_s);
@@ -4461,3 +5368,314 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+#define DPP_PB_ANNOUNCE_PER_CHAN 3
+
+static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq);
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpas_dpp_pb_tx_status(struct wpa_supplicant *wpa_s,
+				  unsigned int freq, const u8 *dst,
+				  const u8 *src, const u8 *bssid,
+				  const u8 *data, size_t data_len,
+				  enum offchannel_send_action_result result)
+{
+	if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to send push button announcement on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 0, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Push button announcement on %d MHz sent",
+		   freq);
+	if (wpa_s->dpp_pb_discovery_done) {
+		wpa_s->dpp_pb_announce_count = 0;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Wait for push button announcement response and PKEX on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 500000, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	} else if (wpa_s->dpp_pb_announce_count >= DPP_PB_ANNOUNCE_PER_CHAN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Wait for push button announcement response on %d MHz",
+			   freq);
+		if (eloop_register_timeout(0, 50000, wpas_dpp_pb_next,
+					   wpa_s, NULL) < 0)
+			wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (wpas_dpp_pb_announce(wpa_s, freq) < 0)
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq)
+{
+	struct wpabuf *msg;
+	int type;
+
+	msg = wpa_s->dpp_pb_announcement;
+	if (!msg)
+		return -1;
+
+	wpa_s->dpp_pb_announce_count++;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send push button announcement %d/%d (%d MHz)",
+		   wpa_s->dpp_pb_announce_count, DPP_PB_ANNOUNCE_PER_CHAN,
+		   freq);
+
+	type = DPP_PA_PB_PRESENCE_ANNOUNCEMENT;
+	if (wpa_s->dpp_pb_announce_count == 1)
+		wpa_msg(wpa_s, MSG_INFO,
+			DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+			MAC2STR(broadcast), freq, type);
+	if (offchannel_send_action(
+		    wpa_s, freq, broadcast, wpa_s->own_addr, broadcast,
+		    wpabuf_head(msg), wpabuf_len(msg),
+		    1000, wpas_dpp_pb_tx_status, 0) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct os_reltime now;
+	int freq;
+
+	if (!wpa_s->dpp_pb_freqs)
+		return;
+
+	os_get_reltime(&now);
+	offchannel_send_action_done(wpa_s);
+
+	if (os_reltime_expired(&now, &wpa_s->dpp_pb_time, 100)) {
+		wpa_printf(MSG_DEBUG, "DPP: Push button wait time expired");
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+
+	if (wpa_s->dpp_pb_freq_idx >= int_array_len(wpa_s->dpp_pb_freqs)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Completed push button announcement round");
+		wpa_s->dpp_pb_freq_idx = 0;
+		if (wpa_s->dpp_pb_stop_iter > 0) {
+			wpa_s->dpp_pb_stop_iter--;
+
+			if (wpa_s->dpp_pb_stop_iter == 1) {
+				wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+					"wait for AP/Configurator to allow PKEX to be initiated");
+				if (eloop_register_timeout(10, 0,
+							   wpas_dpp_pb_next,
+							   wpa_s, NULL) < 0) {
+					wpas_dpp_push_button_stop(wpa_s);
+					return;
+				}
+				return;
+			}
+
+			if (wpa_s->dpp_pb_stop_iter == 0) {
+				wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS
+					"start push button PKEX responder on the discovered channel (%d MHz)",
+					wpa_s->dpp_pb_resp_freq);
+				wpa_s->dpp_pb_discovery_done = true;
+
+				wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi;
+
+				os_free(wpa_s->dpp_pkex_code);
+				wpa_s->dpp_pkex_code = os_memdup(
+					wpa_s->dpp_pb_c_nonce,
+					wpa_s->dpp_pb_c_nonce_len);
+				wpa_s->dpp_pkex_code_len =
+					wpa_s->dpp_pb_c_nonce_len;
+
+				os_free(wpa_s->dpp_pkex_identifier);
+				wpa_s->dpp_pkex_identifier =
+					os_strdup("PBPKEX");
+
+				if (!wpa_s->dpp_pkex_code ||
+				    !wpa_s->dpp_pkex_identifier) {
+					wpas_dpp_push_button_stop(wpa_s);
+					return;
+				}
+
+				wpa_s->dpp_pkex_ver = PKEX_VER_ONLY_2;
+
+				os_free(wpa_s->dpp_pkex_auth_cmd);
+				wpa_s->dpp_pkex_auth_cmd = NULL;
+			}
+		}
+	}
+
+	if (wpa_s->dpp_pb_discovery_done)
+		freq = wpa_s->dpp_pb_resp_freq;
+	else
+		freq = wpa_s->dpp_pb_freqs[wpa_s->dpp_pb_freq_idx++];
+	wpa_s->dpp_pb_announce_count = 0;
+	if (!wpa_s->dpp_pb_announcement) {
+		wpa_printf(MSG_DEBUG, "DPP: Push button announcements stopped");
+		return;
+	}
+	if (wpas_dpp_pb_announce(wpa_s, freq) < 0) {
+		wpas_dpp_push_button_stop(wpa_s);
+		return;
+	}
+}
+
+
+static void wpas_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Active push button Configurator mode expired");
+	wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+static int wpas_dpp_push_button_configurator(struct wpa_supplicant *wpa_s,
+					     const char *cmd)
+{
+	wpa_s->dpp_pb_configurator = true;
+	wpa_s->dpp_pb_announce_time.sec = 0;
+	wpa_s->dpp_pb_announce_time.usec = 0;
+	str_clear_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+	if (cmd) {
+		wpa_s->dpp_pb_cmd = os_strdup(cmd);
+		if (!wpa_s->dpp_pb_cmd)
+			return -1;
+	}
+	eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
+			       wpa_s, NULL);
+
+	return 0;
+}
+
+
+static void wpas_dpp_pb_scan_res_handler(struct wpa_supplicant *wpa_s,
+					 struct wpa_scan_results *scan_res)
+{
+	if (!wpa_s->dpp_pb_time.sec && !wpa_s->dpp_pb_time.usec)
+		return;
+
+	os_free(wpa_s->dpp_pb_freqs);
+	wpa_s->dpp_pb_freqs = wpas_dpp_presence_ann_channels(wpa_s, NULL);
+
+	wpa_printf(MSG_DEBUG, "DPP: Scan completed for PB discovery");
+	if (!wpa_s->dpp_pb_freqs ||
+	    eloop_register_timeout(0, 0, wpas_dpp_pb_next, wpa_s, NULL) < 0)
+		wpas_dpp_push_button_stop(wpa_s);
+}
+
+
+int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int res;
+
+	if (!wpa_s->dpp)
+		return -1;
+	wpas_dpp_push_button_stop(wpa_s);
+	wpas_dpp_stop(wpa_s);
+	wpas_dpp_chirp_stop(wpa_s);
+
+	os_get_reltime(&wpa_s->dpp_pb_time);
+
+	if (cmd &&
+	    (os_strstr(cmd, " role=configurator") ||
+	     os_strstr(cmd, " conf=")))
+		return wpas_dpp_push_button_configurator(wpa_s, cmd);
+
+	wpa_s->dpp_pb_configurator = false;
+
+	wpa_s->dpp_pb_freq_idx = 0;
+
+	res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex");
+	if (res < 0)
+		return -1;
+	wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res);
+	if (!wpa_s->dpp_pb_bi)
+		return -1;
+
+	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_netrole = DPP_NETROLE_STA;
+	wpa_s->dpp_qr_mutual = 0;
+	wpa_s->dpp_pb_announcement =
+		dpp_build_pb_announcement(wpa_s->dpp_pb_bi);
+	if (!wpa_s->dpp_pb_announcement)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Scan to create channel list for PB discovery");
+	wpa_s->scan_req = MANUAL_SCAN_REQ;
+	wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+	return 0;
+}
+
+
+void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->dpp)
+		return;
+	os_free(wpa_s->dpp_pb_freqs);
+	wpa_s->dpp_pb_freqs = NULL;
+	wpabuf_free(wpa_s->dpp_pb_announcement);
+	wpa_s->dpp_pb_announcement = NULL;
+	if (wpa_s->dpp_pb_bi) {
+		char id[20];
+
+		os_snprintf(id, sizeof(id), "%u", wpa_s->dpp_pb_bi->id);
+		dpp_bootstrap_remove(wpa_s->dpp, id);
+		wpa_s->dpp_pb_bi = NULL;
+		if (!wpa_s->dpp_pb_result_indicated) {
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
+			wpa_s->dpp_pb_result_indicated = true;
+		}
+	}
+
+	wpa_s->dpp_pb_resp_freq = 0;
+	wpa_s->dpp_pb_stop_iter = 0;
+	wpa_s->dpp_pb_discovery_done = false;
+	os_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+
+	eloop_cancel_timeout(wpas_dpp_pb_next, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_push_button_expire, wpa_s, NULL);
+	if (wpas_dpp_pb_active(wpa_s)) {
+		wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+		if (!wpa_s->dpp_pb_result_indicated)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed");
+	}
+	wpa_s->dpp_pb_time.sec = 0;
+	wpa_s->dpp_pb_time.usec = 0;
+	dpp_pkex_free(wpa_s->dpp_pkex);
+	wpa_s->dpp_pkex = NULL;
+	os_free(wpa_s->dpp_pkex_auth_cmd);
+	wpa_s->dpp_pkex_auth_cmd = NULL;
+
+	wpa_s->dpp_pb_result_indicated = false;
+
+	str_clear_free(wpa_s->dpp_pb_cmd);
+	wpa_s->dpp_pb_cmd = NULL;
+
+	if (wpa_s->scan_res_handler == wpas_dpp_pb_scan_res_handler) {
+		wpas_abort_ongoing_scan(wpa_s);
+		wpa_s->scan_res_handler = NULL;
+	}
+}
+
+#endif /* CONFIG_DPP3 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index 2b03a54..e2bdd9d 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -44,5 +44,7 @@
 int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd);
 int wpas_dpp_conf_set(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_push_button(struct wpa_supplicant *wpa_s, const char *cmd);
+void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s);
 
 #endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 237f4e0..d707cf5 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -143,7 +143,7 @@
 	return -1;
 }
 
-static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, int link_id,
 				  enum wpa_alg alg, const u8 *addr,
 				  int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
@@ -163,6 +163,7 @@
 	params.key = key;
 	params.key_len = key_len;
 	params.key_flag = key_flag;
+	params.link_id = link_id;
 
 	if (alg != WPA_ALG_NONE) {
 		/* keyidx = 1 can be either a broadcast or--with
@@ -349,8 +350,11 @@
 static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
 				  struct hostapd_sta_add_params *params)
 {
-	if (wpa_s->driver->sta_add)
+	if (wpa_s->driver->sta_add) {
+		/* Set link_id to -1 as it's needed for AP only */
+		params->mld_link_id = -1;
 		return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
+	}
 	return -1;
 }
 
@@ -406,20 +410,10 @@
 	return 0;
 }
 
-static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
-				      unsigned int freq,
-				      unsigned int wait,
-				      const u8 *dst, const u8 *src,
-				      const u8 *bssid,
-				      const u8 *data, size_t data_len,
-				      int no_cck)
-{
-	if (wpa_s->driver->send_action)
-		return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
-						  wait, dst, src, bssid,
-						  data, data_len, no_cck);
-	return -1;
-}
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+			unsigned int wait, const u8 *dst, const u8 *src,
+			const u8 *bssid, const u8 *data, size_t data_len,
+			int no_cck);
 
 static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
 {
@@ -523,6 +517,14 @@
 int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
 			struct wpa_signal_info *si);
 
+static inline int wpa_drv_mlo_signal_poll(struct wpa_supplicant *wpa_s,
+					  struct wpa_mlo_signal_info *mlo_si)
+{
+	if (wpa_s->driver->mlo_signal_poll)
+		return wpa_s->driver->mlo_signal_poll(wpa_s->drv_priv, mlo_si);
+	return -1;
+}
+
 static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s,
 				       struct wpa_channel_info *ci)
 {
@@ -804,6 +806,14 @@
 						 window);
 }
 
+static inline int wpa_drv_set_offload(struct wpa_supplicant *wpa_s, u8 offload)
+{
+	if (!wpa_s->driver->set_offload)
+		return -1;
+	return wpa_s->driver->set_offload(wpa_s->drv_priv, offload);
+
+}
+
 static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
 						   u64 cs)
 {
@@ -964,7 +974,7 @@
 static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
 					     enum wpa_driver_if_type if_type,
 					     unsigned int *num,
-					     unsigned int *freq_list)
+					     struct weighted_pcl *freq_list)
 {
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->get_pref_freq_list_override)
@@ -1117,4 +1127,49 @@
 	return wpa_s->driver->dpp_listen(wpa_s->drv_priv, enable);
 }
 
+static inline int wpa_drv_send_pasn_resp(struct wpa_supplicant *wpa_s,
+					 struct pasn_auth *params)
+{
+	if (!wpa_s->driver->send_pasn_resp)
+		return -1;
+	return wpa_s->driver->send_pasn_resp(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_set_secure_ranging_ctx(struct wpa_supplicant *wpa_s,
+						 const u8 *own_addr,
+						 const u8 *peer_addr,
+						 u32 cipher, u8 tk_len,
+						 const u8 *tk,
+						 u8 ltf_keyseed_len,
+						 const u8 *ltf_keyseed,
+						 u32 action)
+{
+	struct secure_ranging_params params;
+
+	if (!wpa_s->driver->set_secure_ranging_ctx)
+		return -1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.action = action;
+	params.own_addr = own_addr;
+	params.peer_addr = peer_addr;
+	params.cipher = cipher;
+	params.tk_len = tk_len;
+	params.tk = tk;
+	params.ltf_keyseed_len = ltf_keyseed_len;
+	params.ltf_keyseed = ltf_keyseed;
+
+	return wpa_s->driver->set_secure_ranging_ctx(wpa_s->drv_priv, &params);
+}
+
+static inline int
+wpas_drv_get_sta_mlo_info(struct wpa_supplicant *wpa_s,
+			  struct driver_sta_mlo_info *mlo_info)
+{
+	if (!wpa_s->driver->get_sta_mlo_info)
+		return 0;
+
+	return wpa_s->driver->get_sta_mlo_info(wpa_s->drv_priv, mlo_info);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index f806895..9641062 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -714,7 +714,7 @@
 
 	printf("Sending fake EAP-Request-Identity\n");
 	eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
-			  sizeof(*hdr) + 5);
+			  sizeof(*hdr) + 5, FRAME_ENCRYPTION_UNKNOWN);
 }
 
 
@@ -842,7 +842,8 @@
 			  wpabuf_len(eap));
 		eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
 				  (u8 *) dot1x,
-				  sizeof(*dot1x) + wpabuf_len(eap));
+				  sizeof(*dot1x) + wpabuf_len(eap),
+				  FRAME_ENCRYPTION_UNKNOWN);
 		os_free(dot1x);
 	}
 }
@@ -1471,7 +1472,7 @@
 	dl_list_init(&wpa_s.bss);
 	dl_list_init(&wpa_s.bss_id);
 	if (conf)
-		wpa_s.conf = wpa_config_read(conf, NULL);
+		wpa_s.conf = wpa_config_read(conf, NULL, false);
 	else
 		wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
 	if (wpa_s.conf == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 266de4c..253f87d 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -140,7 +140,7 @@
 	struct wpa_bss *bss = NULL;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-	if (ssid->ssid_len > 0)
+	if (ssid && ssid->ssid_len > 0)
 		bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
 	if (!bss)
 		bss = wpa_bss_get_bssid(wpa_s, bssid);
@@ -168,7 +168,23 @@
 }
 
 
-static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+static void wpa_supplicant_update_link_bss(struct wpa_supplicant *wpa_s,
+					   u8 link_id, const u8 *bssid)
+{
+	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+	}
+
+	if (bss)
+		wpa_s->links[link_id].bss = bss;
+}
+
+
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s,
+					union wpa_event_data *data)
 {
 	struct wpa_ssid *ssid, *old_ssid;
 	u8 drv_ssid[SSID_MAX_LEN];
@@ -241,8 +257,15 @@
 	if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 		u8 wpa_ie[80];
 		size_t wpa_ie_len = sizeof(wpa_ie);
+		bool skip_default_rsne;
+
+		/* Do not override RSNE/RSNXE with the default values if the
+		 * driver indicated the actual values used in the
+		 * (Re)Association Request frame. */
+		skip_default_rsne = data && data->assoc_info.req_ies;
 		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
-					      wpa_ie, &wpa_ie_len) < 0)
+					      wpa_ie, &wpa_ie_len,
+					      skip_default_rsne) < 0)
 			wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
 	} else {
 		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
@@ -286,6 +309,18 @@
 }
 
 
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->valid_links)
+		return;
+
+	wpa_s->valid_links = 0;
+	wpa_s->mlo_assoc_link_id = 0;
+	os_memset(wpa_s->ap_mld_addr, 0, ETH_ALEN);
+	os_memset(wpa_s->links, 0, sizeof(wpa_s->links));
+}
+
+
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 {
 	int bssid_changed;
@@ -338,6 +373,7 @@
 	wpa_s->current_ssid = NULL;
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	wpa_s->key_mgmt = 0;
+	wpa_s->allowed_key_mgmts = 0;
 
 	wpas_rrm_reset(wpa_s);
 	wpa_s->wnmsleep_used = 0;
@@ -352,10 +388,13 @@
 
 	if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
 		wpa_s->enabled_4addr_mode = 0;
+
+	wpa_s->wps_scan_done = false;
+	wpas_reset_mlo_info(wpa_s);
 }
 
 
-static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s, bool authorized)
 {
 	struct wpa_ie_data ie;
 	int pmksa_set = -1;
@@ -379,6 +418,8 @@
 						    NULL, NULL, 0, NULL, 0);
 		if (pmksa_set == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
+			if (authorized)
+				wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 			break;
 		}
 	}
@@ -570,7 +611,8 @@
 #ifdef CONFIG_WEP
 	int wep_ok;
 #endif /* CONFIG_WEP */
-	bool is_6ghz_bss = is_6ghz_freq(bss->freq);
+	bool is_6ghz_bss_or_mld = is_6ghz_freq(bss->freq) ||
+		!is_zero_ether_addr(bss->mld_addr);
 
 	ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
 	if (ret >= 0)
@@ -585,10 +627,10 @@
 #endif /* CONFIG_WEP */
 
 	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-	if (is_6ghz_bss && !rsn_ie) {
+	if (is_6ghz_bss_or_mld && !rsn_ie) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - 6 GHz BSS without RSNE");
+				"   skip - 6 GHz/MLD BSS without RSNE");
 		return 0;
 	}
 
@@ -606,7 +648,7 @@
 		if (!ie.has_group)
 			ie.group_cipher = wpa_default_rsn_cipher(bss->freq);
 
-		if (is_6ghz_bss) {
+		if (is_6ghz_bss_or_mld) {
 			/* WEP and TKIP are not allowed on 6 GHz */
 			ie.pairwise_cipher &= ~(WPA_CIPHER_WEP40 |
 						WPA_CIPHER_WEP104 |
@@ -657,12 +699,12 @@
 			break;
 		}
 
-		if (is_6ghz_bss) {
+		if (is_6ghz_bss_or_mld) {
 			/* MFPC must be supported on 6 GHz */
 			if (!(ie.capabilities & WPA_CAPABILITY_MFPC)) {
 				if (debug_print)
 					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   skip RSNE - 6 GHz without MFPC");
+						"   skip RSNE - 6 GHz/MLD without MFPC");
 				break;
 			}
 
@@ -702,10 +744,10 @@
 		return 1;
 	}
 
-	if (is_6ghz_bss) {
+	if (is_6ghz_bss_or_mld) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - 6 GHz BSS without matching RSNE");
+				"   skip - 6 GHz/MLD BSS without matching RSNE");
 		return 0;
 	}
 
@@ -925,11 +967,24 @@
 				continue;
 			}
 
+			if (flagged && ((rate_ie[j] & 0x7f) ==
+					BSS_MEMBERSHIP_SELECTOR_HE_PHY)) {
+				if (!he_supported(mode, IEEE80211_MODE_INFRA)) {
+					if (debug_print)
+						wpa_dbg(wpa_s, MSG_DEBUG,
+							"   hardware does not support HE PHY");
+					return 0;
+				}
+				continue;
+			}
+
 #ifdef CONFIG_SAE
 			if (flagged && ((rate_ie[j] & 0x7f) ==
 					BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
-				if (wpa_s->conf->sae_pwe == 0 &&
+				if (wpa_s->conf->sae_pwe ==
+				    SAE_PWE_HUNT_AND_PECK &&
 				    !ssid->sae_password_id &&
+				    !is_6ghz_freq(bss->freq) &&
 				    wpa_key_mgmt_sae(ssid->key_mgmt)) {
 					if (debug_print)
 						wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1362,9 +1417,10 @@
 #ifdef CONFIG_SAE
 	/* When using SAE Password Identifier and when operationg on the 6 GHz
 	 * band, only H2E is allowed. */
-	if ((wpa_s->conf->sae_pwe == 1 || is_6ghz_freq(bss->freq) ||
-	     ssid->sae_password_id) &&
-	    wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
+	if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     is_6ghz_freq(bss->freq) || ssid->sae_password_id) &&
+	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	    !(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
@@ -1480,7 +1536,8 @@
 
 #ifdef CONFIG_DPP
 	if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
-	    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+	    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, wpa_s->own_addr,
+				 ssid) &&
 	    (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
 	     !ssid->dpp_csign)) {
 		if (debug_print)
@@ -1771,10 +1828,13 @@
 			   struct wpa_bss *selected,
 			   struct wpa_ssid *ssid)
 {
-	if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+	if ((eap_is_wps_pbc_enrollee(&ssid->eap) &&
+	     wpas_wps_partner_link_overlap_detect(wpa_s)) ||
+	    wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
 			"PBC session overlap");
 		wpas_notify_wps_event_pbc_overlap(wpa_s);
+		wpa_s->wps_overlap = true;
 #ifdef CONFIG_P2P
 		if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
 		    wpa_s->p2p_in_provisioning) {
@@ -1917,7 +1977,7 @@
 					   struct wpa_bss *selected)
 {
 	int min_diff, diff;
-	int to_5ghz;
+	int to_5ghz, to_6ghz;
 	int cur_level;
 	unsigned int cur_est, sel_est;
 	struct wpa_signal_info si;
@@ -1963,9 +2023,9 @@
 	 * information about our currently associated AP.
 	 */
 	if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
-	    (si.avg_beacon_signal || si.avg_signal)) {
-		cur_level = si.avg_beacon_signal ? si.avg_beacon_signal :
-			si.avg_signal;
+	    (si.data.avg_beacon_signal || si.data.avg_signal)) {
+		cur_level = si.data.avg_beacon_signal ?
+			si.data.avg_beacon_signal : si.data.avg_signal;
 		cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level,
 						   si.current_noise);
 
@@ -1984,8 +2044,11 @@
 	}
 
 	to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
+	to_6ghz = is_6ghz_freq(selected->freq) &&
+		!is_6ghz_freq(current_bss->freq);
 
-	if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2 &&
+	if (cur_level < 0 &&
+	    cur_level > selected->level + to_5ghz * 2 + to_6ghz * 2 &&
 	    sel_est < cur_est * 1.2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
 			"signal level");
@@ -2037,6 +2100,8 @@
 
 	if (to_5ghz)
 		min_diff -= 2;
+	if (to_6ghz)
+		min_diff -= 2;
 	diff = selected->level - cur_level;
 	if (diff < min_diff) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
@@ -2278,6 +2343,9 @@
 		}
 	}
 
+	if (wpa_s->supp_pbc_active && !wpas_wps_partner_link_scan_done(wpa_s))
+		return ret;
+
 	return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
 
 scan_work_done:
@@ -2344,6 +2412,8 @@
 			wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
 			return -1;
 		}
+		wpa_s->supp_pbc_active = false;
+
 		if (new_scan)
 			wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 		/*
@@ -2378,6 +2448,9 @@
 			if (res == 1)
 				return 0;
 
+			if (wpas_p2p_retry_limit_exceeded(wpa_s))
+				return 0;
+
 			if (wpa_s->p2p_in_provisioning ||
 			    wpa_s->show_group_started ||
 			    wpa_s->p2p_in_invitation) {
@@ -2511,6 +2584,17 @@
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 }
 
+
+int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+	return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
+	return wpas_select_network_from_last_scan(wpa_s, 1, 1);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
 #ifdef CONFIG_WNM
 
 static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
@@ -2628,6 +2712,26 @@
 #endif /* CONFIG_INTERWORKING */
 
 
+static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->enabled_4addr_mode) {
+		wpa_printf(MSG_DEBUG, "4addr mode already set");
+		return;
+	}
+
+	if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to set 4addr mode");
+		goto fail;
+	}
+	wpa_s->enabled_4addr_mode = 1;
+	wpa_msg(wpa_s, MSG_INFO, "Successfully set 4addr mode");
+	return;
+
+fail:
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+}
+
+
 static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s,
 					const u8 *ies, size_t ies_len)
 {
@@ -2680,11 +2784,7 @@
 		goto fail;
 	}
 
-	if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
-		wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
-		goto fail;
-	}
-	wpa_s->enabled_4addr_mode = 1;
+	wpa_supplicant_set_4addr_mode(wpa_s);
 	return;
 
 fail:
@@ -2773,8 +2873,10 @@
 		p += len;
 	}
 
-	if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0)
+	if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0) {
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, 0);
 		return 0;
+	}
 
 	wpa_hexdump(MSG_DEBUG,
 		    "WPA: Update cipher suite selection based on IEs in driver-generated WPA/RSNE in AssocReq",
@@ -2801,6 +2903,35 @@
 		return -1;
 	}
 
+#ifdef CONFIG_OCV
+	if (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
+	     (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) && ssid->ocv)
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV,
+				 !!(ie.capabilities & WPA_CAPABILITY_OCVC));
+#endif /* CONFIG_OCV */
+
+	/*
+	 * Update PMK in wpa_sm and the driver if roamed to WPA/WPA2 PSK from a
+	 * different AKM.
+	 */
+	if (wpa_s->key_mgmt != ie.key_mgmt &&
+	    wpa_key_mgmt_wpa_psk_no_sae(ie.key_mgmt)) {
+		if (!ssid->psk_set) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"No PSK available for association");
+			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
+			return -1;
+		}
+
+		wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL);
+		if (wpa_s->conf->key_mgmt_offload &&
+		    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD) &&
+		    wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0,
+				    ssid->psk, PMK_LEN, KEY_FLAG_PMK))
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"WPA: Cannot set PMK for key management offload");
+	}
+
 	wpa_s->key_mgmt = ie.key_mgmt;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT %s and proto %d",
@@ -3023,7 +3154,8 @@
 				get_supported_channel_width(&req_elems);
 			enum chan_width ap_operation_chan_width =
 				get_operation_channel_width(&resp_elems);
-			if (wpa_s->connection_vht || wpa_s->connection_he) {
+			if (wpa_s->connection_vht || wpa_s->connection_he ||
+			    wpa_s->connection_eht) {
 				wpa_s->connection_channel_bandwidth =
 					get_sta_operation_chan_width(ap_operation_chan_width,
 					sta_supported_chan_width);
@@ -3056,7 +3188,8 @@
 			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 				break;
 			found = 1;
-			wpa_find_assoc_pmkid(wpa_s);
+			wpa_find_assoc_pmkid(wpa_s,
+					     data->assoc_info.authorized);
 		}
 		if (!found_x && p[0] == WLAN_EID_RSNX) {
 			if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len))
@@ -3139,7 +3272,9 @@
 #ifdef CONFIG_OWE
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
 	    (!bssid_known ||
-	     owe_process_assoc_resp(wpa_s->wpa, bssid,
+	     owe_process_assoc_resp(wpa_s->wpa,
+				    wpa_s->valid_links ?
+				    wpa_s->ap_mld_addr : bssid,
 				    data->assoc_info.resp_ies,
 				    data->assoc_info.resp_ies_len) < 0)) {
 		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
@@ -3391,6 +3526,121 @@
 }
 
 
+static int wpa_drv_get_mlo_info(struct wpa_supplicant *wpa_s)
+{
+	struct driver_sta_mlo_info mlo;
+	int i;
+
+	os_memset(&mlo, 0, sizeof(mlo));
+	if (wpas_drv_get_sta_mlo_info(wpa_s, &mlo)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO link info");
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return -1;
+	}
+
+	if (wpa_s->valid_links == mlo.valid_links) {
+		bool match = true;
+
+		if (!mlo.valid_links)
+			return 0;
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo.valid_links & BIT(i)))
+				continue;
+
+			if (os_memcmp(wpa_s->links[i].addr, mlo.links[i].addr,
+				      ETH_ALEN) != 0 ||
+			    os_memcmp(wpa_s->links[i].bssid, mlo.links[i].bssid,
+				      ETH_ALEN) != 0) {
+				match = false;
+				break;
+			}
+		}
+
+		if (match && wpa_s->mlo_assoc_link_id == mlo.assoc_link_id &&
+		    os_memcmp(wpa_s->ap_mld_addr, mlo.ap_mld_addr,
+			      ETH_ALEN) == 0)
+			return 0;
+	}
+
+	wpa_s->valid_links = mlo.valid_links;
+	wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
+	os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
+		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
+		wpa_s->links[i].freq = mlo.links[i].freq;
+		wpa_supplicant_update_link_bss(wpa_s, i, mlo.links[i].bssid);
+	}
+
+	return 0;
+}
+
+
+static int wpa_sm_set_ml_info(struct wpa_supplicant *wpa_s)
+{
+	struct driver_sta_mlo_info drv_mlo;
+	struct wpa_sm_mlo wpa_mlo;
+	const u8 *bss_rsn = NULL, *bss_rsnx = NULL;
+	int i;
+
+	os_memset(&drv_mlo, 0, sizeof(drv_mlo));
+	if (wpas_drv_get_sta_mlo_info(wpa_s, &drv_mlo)) {
+		wpa_dbg(wpa_s, MSG_INFO, "Failed to get MLO link info");
+		return -1;
+	}
+
+	os_memset(&wpa_mlo, 0, sizeof(wpa_mlo));
+	if (!drv_mlo.valid_links)
+		goto out;
+
+	os_memcpy(wpa_mlo.ap_mld_addr, drv_mlo.ap_mld_addr, ETH_ALEN);
+	wpa_mlo.assoc_link_id = drv_mlo.assoc_link_id;
+	wpa_mlo.valid_links = drv_mlo.valid_links;
+	wpa_mlo.req_links = drv_mlo.req_links;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		struct wpa_bss *bss;
+
+		if (!(drv_mlo.req_links & BIT(i)))
+			continue;
+
+		bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
+		if (!bss) {
+			wpa_supplicant_update_scan_results(wpa_s);
+			bss = wpa_supplicant_get_new_bss(
+				wpa_s, drv_mlo.links[i].bssid);
+		}
+
+		if (!bss) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"Failed to get MLO link %d BSS", i);
+			return -1;
+		}
+
+		bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+		bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+		wpa_mlo.links[i].ap_rsne = bss_rsn ? (u8 *) bss_rsn : NULL;
+		wpa_mlo.links[i].ap_rsne_len = bss_rsn ? 2 + bss_rsn[1] : 0;
+		wpa_mlo.links[i].ap_rsnxe = bss_rsnx ? (u8 *) bss_rsnx : NULL;
+		wpa_mlo.links[i].ap_rsnxe_len = bss_rsnx ? 2 + bss_rsnx[1] : 0;
+
+		os_memcpy(wpa_mlo.links[i].bssid, drv_mlo.links[i].bssid,
+			  ETH_ALEN);
+		os_memcpy(wpa_mlo.links[i].addr, drv_mlo.links[i].addr,
+			  ETH_ALEN);
+	}
+
+out:
+	return wpa_sm_set_mlo_params(wpa_s->wpa, &wpa_mlo);
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				       union wpa_event_data *data)
 {
@@ -3450,6 +3700,13 @@
 		return;
 	}
 
+	if (wpa_drv_get_mlo_info(wpa_s) < 0) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get MLO connection info");
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return;
+	}
+
 	if (ft_completed &&
 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION)) {
 		wpa_msg(wpa_s, MSG_INFO, "Attempt to roam to " MACSTR,
@@ -3473,14 +3730,22 @@
 		ft_completed = wpa_fils_is_completed(wpa_s->wpa);
 
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-	/* For driver based roaming, insert PSK during the initial association */
-	if (is_zero_ether_addr(wpa_s->bssid) &&
-		wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
-		/* In case the driver wants to handle re-assocs, pass it down the PMK. */
-		wpa_dbg(wpa_s, MSG_DEBUG, "Pass the PMK to the driver");
-		wpa_sm_install_pmk(wpa_s->wpa);
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
+		/*
+		 * For driver based roaming, insert PSK during
+		 * the initial association
+		 */
+		if (is_zero_ether_addr(wpa_s->bssid) &&
+			wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+			/*
+			 * In case the driver wants to handle re-assocs,
+			 * pass it down the PMK.
+			 */
+			wpa_dbg(wpa_s, MSG_DEBUG, "Pass the PMK to the driver");
+			wpa_sm_install_pmk(wpa_s->wpa);
+		}
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+#endif
 
 	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
 	if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
@@ -3505,7 +3770,7 @@
 		if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
 			wpa_clear_keys(wpa_s, bssid);
 		}
-		if (wpa_supplicant_select_config(wpa_s) < 0) {
+		if (wpa_supplicant_select_config(wpa_s, data) < 0) {
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 			return;
@@ -3542,6 +3807,15 @@
 		wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
 	}
 	wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+
+	if (wpa_sm_set_ml_info(wpa_s)) {
+		wpa_dbg(wpa_s, MSG_INFO,
+			"Failed to set MLO connection info to wpa_sm");
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+		return;
+	}
+
 	if (wpa_s->l2)
 		l2_packet_notify_auth_start(wpa_s->l2);
 
@@ -3601,14 +3875,23 @@
 		eapol_sm_notify_eap_success(wpa_s->eapol, true);
 	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
 		   wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
-		/*
-		 * We are done; the driver will take care of RSN 4-way
-		 * handshake.
-		 */
-		wpa_supplicant_cancel_auth_timeout(wpa_s);
-		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
-		eapol_sm_notify_portValid(wpa_s->eapol, true);
-		eapol_sm_notify_eap_success(wpa_s->eapol, true);
+		if (already_authorized) {
+			/*
+			 * We are done; the driver will take care of RSN 4-way
+			 * handshake.
+			 */
+			wpa_supplicant_cancel_auth_timeout(wpa_s);
+			wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+			eapol_sm_notify_portValid(wpa_s->eapol, true);
+			eapol_sm_notify_eap_success(wpa_s->eapol, true);
+		} else {
+			/* Update port, WPA_COMPLETED state from the
+			 * EVENT_PORT_AUTHORIZED handler when the driver is done
+			 * with the 4-way handshake.
+			 */
+			wpa_msg(wpa_s, MSG_INFO,
+				"ASSOC INFO: wait for driver port authorized indication");
+		}
 	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
 		   wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 		/*
@@ -3657,15 +3940,17 @@
 		os_get_reltime(&now);
 		os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
 		if (age.sec == 0 && age.usec < 200000 &&
-		    os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
-		    0) {
+		    os_memcmp(wpa_s->pending_eapol_rx_src,
+			      wpa_s->valid_links ? wpa_s->ap_mld_addr : bssid,
+			      ETH_ALEN) == 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
 				"frame that was received just before "
 				"association notification");
 			wpa_supplicant_rx_eapol(
 				wpa_s, wpa_s->pending_eapol_rx_src,
 				wpabuf_head(wpa_s->pending_eapol_rx),
-				wpabuf_len(wpa_s->pending_eapol_rx));
+				wpabuf_len(wpa_s->pending_eapol_rx),
+				wpa_s->pending_eapol_encrypted);
 		}
 		wpabuf_free(wpa_s->pending_eapol_rx);
 		wpa_s->pending_eapol_rx = NULL;
@@ -3729,6 +4014,9 @@
 #ifdef CONFIG_DPP2
 	wpa_s->dpp_pfs_fallback = 0;
 #endif /* CONFIG_DPP2 */
+
+	if (wpa_s->current_ssid && wpa_s->current_ssid->enable_4addr_mode)
+		wpa_supplicant_set_4addr_mode(wpa_s);
 }
 
 
@@ -3837,7 +4125,8 @@
 			"pre-shared key may be incorrect");
 		if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
 			return; /* P2P group removed */
-		wpas_auth_failed(wpa_s, "WRONG_KEY");
+		wpas_auth_failed(wpa_s, "WRONG_KEY", prev_pending_bssid);
+		wpas_notify_psk_mismatch(wpa_s);
 #ifdef CONFIG_DPP2
 		wpas_dpp_send_conn_status_result(wpa_s,
 						 DPP_STATUS_AUTH_FAILURE);
@@ -4341,7 +4630,7 @@
 		(wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
 	       eapol_sm_failed(wpa_s->eapol))) &&
 	     !wpa_s->eap_expected_failure))
-		wpas_auth_failed(wpa_s, "AUTH_FAILED");
+		wpas_auth_failed(wpa_s, "AUTH_FAILED", addr);
 
 #ifdef CONFIG_P2P
 	if (deauth && reason_code > 0) {
@@ -4847,7 +5136,10 @@
 					       data->assoc_info.fils_pmk,
 					       data->assoc_info.fils_pmk_len,
 					       data->assoc_info.fils_pmkid,
-					       wpa_s->bssid, fils_cache_id);
+					       wpa_s->valid_links ?
+					       wpa_s->ap_mld_addr :
+					       wpa_s->bssid,
+					       fils_cache_id);
 		} else if (data->assoc_info.fils_pmkid) {
 			/* Update the current PMKSA used for this connection */
 			pmksa_cache_set_current(wpa_s->wpa,
@@ -5236,6 +5528,7 @@
 			os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
 			wpa_s->scan_start_time.sec = 0;
 			wpa_s->scan_start_time.usec = 0;
+			wpa_s->wps_scan_done = true;
 			wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
 				diff.sec, diff.usec);
 		}
@@ -5390,6 +5683,39 @@
 		break;
 #endif /* CONFIG_AP */
 
+	case EVENT_LINK_CH_SWITCH_STARTED:
+	case EVENT_LINK_CH_SWITCH:
+		if (!data || !wpa_s->current_ssid ||
+		    !(wpa_s->valid_links & BIT(data->ch_switch.link_id)))
+			break;
+
+		wpa_msg(wpa_s, MSG_INFO,
+			"%sfreq=%d link_id=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+			event == EVENT_LINK_CH_SWITCH ?
+			WPA_EVENT_LINK_CHANNEL_SWITCH :
+			WPA_EVENT_LINK_CHANNEL_SWITCH_STARTED,
+			data->ch_switch.freq,
+			data->ch_switch.link_id,
+			data->ch_switch.ht_enabled,
+			data->ch_switch.ch_offset,
+			channel_width_to_string(data->ch_switch.ch_width),
+			data->ch_switch.cf1,
+			data->ch_switch.cf2);
+		if (event == EVENT_LINK_CH_SWITCH_STARTED)
+			break;
+
+		wpa_s->links[data->ch_switch.link_id].freq =
+			data->ch_switch.freq;
+		if (wpa_s->links[data->ch_switch.link_id].bss &&
+		    wpa_s->links[data->ch_switch.link_id].bss->freq !=
+		    data->ch_switch.freq) {
+			wpa_s->links[data->ch_switch.link_id].bss->freq =
+				data->ch_switch.freq;
+			notify_bss_changes(
+				wpa_s, WPA_BSS_FREQ_CHANGED_FLAG,
+				wpa_s->links[data->ch_switch.link_id].bss);
+		}
+		break;
 	case EVENT_CH_SWITCH_STARTED:
 	case EVENT_CH_SWITCH:
 		if (!data || !wpa_s->current_ssid)
@@ -5447,6 +5773,7 @@
 					  data->ch_switch.ch_width,
 					  data->ch_switch.cf1,
 					  data->ch_switch.cf2,
+					  data->ch_switch.punct_bitmap,
 					  1);
 		}
 #endif /* CONFIG_AP */
@@ -5647,22 +5974,26 @@
 	case EVENT_EAPOL_RX:
 		wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
 					data->eapol_rx.data,
-					data->eapol_rx.data_len);
+					data->eapol_rx.data_len,
+					data->eapol_rx.encrypted);
 		break;
 	case EVENT_SIGNAL_CHANGE:
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
-			"above=%d signal=%d noise=%d txrate=%d",
+			"above=%d signal=%d noise=%d txrate=%lu",
 			data->signal_change.above_threshold,
-			data->signal_change.current_signal,
+			data->signal_change.data.signal,
 			data->signal_change.current_noise,
-			data->signal_change.current_txrate);
+			data->signal_change.data.current_tx_rate);
 		wpa_bss_update_level(wpa_s->current_bss,
-				     data->signal_change.current_signal);
+				     data->signal_change.data.signal);
 		bgscan_notify_signal_change(
 			wpa_s, data->signal_change.above_threshold,
-			data->signal_change.current_signal,
+			data->signal_change.data.signal,
 			data->signal_change.current_noise,
-			data->signal_change.current_txrate);
+			data->signal_change.data.current_tx_rate);
+		os_memcpy(&wpa_s->last_signal_info, data,
+			  sizeof(struct wpa_signal_info));
+		wpas_notify_signal_change(wpa_s);
 		break;
 	case EVENT_INTERFACE_MAC_CHANGED:
 		wpa_supplicant_update_mac_addr(wpa_s);
@@ -5923,7 +6254,21 @@
 		sme_external_auth_trigger(wpa_s, data);
 #endif /* CONFIG_SAE */
 		break;
+#ifdef CONFIG_PASN
+	case EVENT_PASN_AUTH:
+		wpas_pasn_auth_trigger(wpa_s, &data->pasn_auth);
+		break;
+#endif /* CONFIG_PASN */
 	case EVENT_PORT_AUTHORIZED:
+#ifndef CONFIG_NO_WPA
+		if (data->port_authorized.td_bitmap_len) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA3: Transition Disable bitmap from the driver event: 0x%x",
+				   data->port_authorized.td_bitmap[0]);
+			wpas_transition_disable(
+				wpa_s, data->port_authorized.td_bitmap[0]);
+		}
+#endif /* CONFIG_NO_WPA */
 		wpa_supplicant_event_port_authorized(wpa_s);
 		break;
 	case EVENT_STATION_OPMODE_CHANGED:
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index 8e865f3..6cffe71 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -359,7 +359,7 @@
     summary("NFC Handover Request message for DPP: " + str(message))
 
     if handover.peer_crn is not None and not alt:
-        summary("NFC handover request from peer was already received - do not send own")
+        summary("NFC handover request from peer was already received - do not send own[1]")
         return
     if handover.client:
         summary("Use already started handover client")
@@ -382,7 +382,8 @@
         handover.client = client
 
     if handover.peer_crn is not None and not alt:
-        summary("NFC handover request from peer was already received - do not send own")
+        summary("NFC handover request from peer was already received - do not send own[2] (except alt)")
+        run_client_alt(handover, alt)
         return
 
     summary("Sending handover request")
@@ -876,6 +877,11 @@
     if init_on_touch:
         summary("Starting handover client (init_on_touch)")
         dpp_handover_client(handover)
+        summary("llcp_worker init_on_touch processing completed: try_own={} hs_sent={} no_alt_proposal={} start_client_alt={}".format(handover.try_own, handover.hs_sent, handover.no_alt_proposal, handover.start_client_alt))
+        if handover.start_client_alt and not handover.hs_sent:
+            summary("Try alternative handover request before exiting llcp_worker")
+            handover.start_client_alt = False
+            dpp_handover_client(handover, alt=True)
         summary("Exiting llcp_worker thread (init_on_touch)")
         return
 
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index a6172d6..c301f74 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -30,6 +30,8 @@
 #define GAS_QUERY_WAIT_TIME_INITIAL 1000
 #define GAS_QUERY_WAIT_TIME_COMEBACK 150
 
+#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
+
 /**
  * struct gas_query_pending - Pending GAS query
  */
@@ -402,14 +404,14 @@
 
 static void gas_query_rx_initial(struct gas_query *gas,
 				 struct gas_query_pending *query,
-				 const u8 *adv_proto, const u8 *resp,
-				 size_t len, u16 comeback_delay)
+				 const u8 *adv_proto, size_t adv_proto_len,
+				 const u8 *resp, size_t len, u16 comeback_delay)
 {
 	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
 		   MACSTR " (dialog_token=%u comeback_delay=%u)",
 		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
 
-	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+	query->adv_proto = wpabuf_alloc_copy(adv_proto, adv_proto_len);
 	if (query->adv_proto == NULL) {
 		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
 		return;
@@ -434,9 +436,9 @@
 
 static void gas_query_rx_comeback(struct gas_query *gas,
 				  struct gas_query_pending *query,
-				  const u8 *adv_proto, const u8 *resp,
-				  size_t len, u8 frag_id, u8 more_frags,
-				  u16 comeback_delay)
+				  const u8 *adv_proto, size_t adv_proto_len,
+				  const u8 *resp, size_t len, u8 frag_id,
+				  u8 more_frags, u16 comeback_delay)
 {
 	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
 		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
@@ -445,7 +447,7 @@
 		   more_frags, comeback_delay);
 	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
 
-	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+	if (adv_proto_len != wpabuf_len(query->adv_proto) ||
 	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
 		      wpabuf_len(query->adv_proto)) != 0) {
 		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
@@ -514,6 +516,7 @@
 	u8 action, dialog_token, frag_id = 0, more_frags = 0;
 	u16 comeback_delay, resp_len;
 	const u8 *pos, *adv_proto;
+	size_t adv_proto_len;
 	int prot, pmf;
 	unsigned int left;
 
@@ -589,25 +592,31 @@
 	if (pos + 2 > data + len)
 		return 0;
 	comeback_delay = WPA_GET_LE16(pos);
+	if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
+		comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
 	pos += 2;
 
 	/* Advertisement Protocol element */
-	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+	adv_proto = pos;
+	left = data + len - adv_proto;
+	if (left < 2 || adv_proto[1] > left - 2) {
 		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
 			   "Protocol element in the response from " MACSTR,
 			   MAC2STR(sa));
 		return 0;
 	}
 
-	if (*pos != WLAN_EID_ADV_PROTO) {
+	if (*adv_proto != WLAN_EID_ADV_PROTO) {
 		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
 			   "Protocol element ID %u in response from " MACSTR,
-			   *pos, MAC2STR(sa));
+			   *adv_proto, MAC2STR(sa));
 		return 0;
 	}
+	adv_proto_len = 2 + adv_proto[1];
+	if (adv_proto_len > (size_t) (data + len - pos))
+		return 0; /* unreachable due to an earlier check */
 
-	adv_proto = pos;
-	pos += 2 + pos[1];
+	pos += adv_proto_len;
 
 	/* Query Response Length */
 	if (pos + 2 > data + len) {
@@ -631,11 +640,12 @@
 	}
 
 	if (action == WLAN_PA_GAS_COMEBACK_RESP)
-		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
-				      frag_id, more_frags, comeback_delay);
+		gas_query_rx_comeback(gas, query, adv_proto, adv_proto_len,
+				      pos, resp_len, frag_id, more_frags,
+				      comeback_delay);
 	else
-		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
-				     comeback_delay);
+		gas_query_rx_initial(gas, query, adv_proto, adv_proto_len,
+				     pos, resp_len, comeback_delay);
 
 	return 0;
 }
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 02e6390..5b31f7b 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -143,7 +143,7 @@
 }
 
 
-static int supp_set_key(void *ctx, enum wpa_alg alg,
+static int supp_set_key(void *ctx, int link_id, enum wpa_alg alg,
 			const u8 *addr, int key_idx, int set_tx,
 			const u8 *seq, size_t seq_len,
 			const u8 *key, size_t key_len, enum key_flag key_flag)
@@ -172,8 +172,9 @@
 
 	if (is_broadcast_ether_addr(addr))
 		addr = peer->addr;
-	return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
-			       set_tx, seq, seq_len, key, key_len, key_flag);
+	return wpa_drv_set_key(peer->ibss_rsn->wpa_s, link_id, alg, addr,
+			       key_idx, set_tx, seq, seq_len, key, key_len,
+			       key_flag);
 }
 
 
@@ -352,7 +353,7 @@
 		}
 	}
 
-	return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
+	return wpa_drv_set_key(ibss_rsn->wpa_s, -1, alg, addr, idx,
 			       1, seq, 6, key, key_len, key_flag);
 }
 
@@ -772,7 +773,8 @@
 
 static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
 				     struct ibss_rsn_peer *peer,
-				     const u8 *buf, size_t len)
+				     const u8 *buf, size_t len,
+				     enum frame_encryption encrypted)
 {
 	int supp;
 	u8 *tmp;
@@ -788,7 +790,7 @@
 		peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
 		wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
 			   MACSTR, MAC2STR(peer->addr));
-		wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
+		wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len, encrypted);
 	} else {
 		if (ibss_rsn_is_auth_started(peer) == 0) {
 			wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for "
@@ -809,7 +811,8 @@
 
 
 int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
-		      const u8 *buf, size_t len)
+		      const u8 *buf, size_t len,
+		      enum frame_encryption encrypted)
 {
 	struct ibss_rsn_peer *peer;
 
@@ -818,7 +821,8 @@
 
 	peer = ibss_rsn_get_peer(ibss_rsn, src_addr);
 	if (peer)
-		return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len);
+		return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len,
+						 encrypted);
 
 	if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
 		/*
@@ -836,7 +840,7 @@
 					    IBSS_RSN_AUTH_EAPOL_BY_US);
 
 		return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
-						 buf, len);
+						 buf, len, encrypted);
 	}
 
 	return 0;
@@ -865,7 +869,7 @@
 		 * still have a pairwise key configured. */
 		wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer "
 			   MACSTR, MAC2STR(addr));
-		wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0,
+		wpa_drv_set_key(ibss_rsn->wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
 				NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 	}
 
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
index 626c543..cff45a7 100644
--- a/wpa_supplicant/ibss_rsn.h
+++ b/wpa_supplicant/ibss_rsn.h
@@ -57,7 +57,8 @@
 int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
 void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
 int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
-		      const u8 *buf, size_t len);
+		      const u8 *buf, size_t len,
+		      enum frame_encryption encrypted);
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
 void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
 			  size_t len);
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 1b7c7dc..cb1165e 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -144,9 +144,9 @@
 	struct wpa_cred *cred;
 
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-		if (cred->roaming_consortium_len)
+		if (cred->num_home_ois)
 			return 1;
-		if (cred->required_roaming_consortium_len)
+		if (cred->num_required_home_ois)
 			return 1;
 		if (cred->num_roaming_consortiums)
 			return 1;
@@ -421,6 +421,11 @@
 			case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
 				wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
 				break;
+			default:
+				wpa_printf(MSG_DEBUG,
+					   "Unsupported EAP-TTLS inner method %u",
+					   *pos);
+				break;
 			}
 			break;
 		case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
@@ -1065,9 +1070,21 @@
 			goto fail;
 	}
 
-	if (cred->imsi_privacy_key && cred->imsi_privacy_key[0]) {
-		if (wpa_config_set_quoted(ssid, "imsi_privacy_key",
-					  cred->imsi_privacy_key) < 0)
+	if (cred->imsi_privacy_cert && cred->imsi_privacy_cert[0]) {
+		if (wpa_config_set_quoted(ssid, "imsi_privacy_cert",
+					  cred->imsi_privacy_cert) < 0)
+			goto fail;
+	}
+
+	if (cred->imsi_privacy_attr && cred->imsi_privacy_attr[0]) {
+		if (wpa_config_set_quoted(ssid, "imsi_privacy_attr",
+					  cred->imsi_privacy_attr) < 0)
+			goto fail;
+	}
+
+	if (cred->strict_conservative_peer_mode) {
+		if (wpa_config_set_quoted(ssid, "strict_conservative_peer_mode",
+					  "1") < 0)
 			goto fail;
 	}
 
@@ -1086,8 +1103,7 @@
 }
 
 
-static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
-					    size_t rc_len)
+static int oi_element_match(const u8 *ie, const u8 *oi, size_t oi_len)
 {
 	const u8 *pos, *end;
 	u8 lens;
@@ -1112,24 +1128,24 @@
 	if ((lens & 0x0f) + (lens >> 4) > end - pos)
 		return 0;
 
-	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+	if ((lens & 0x0f) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
 		return 1;
 	pos += lens & 0x0f;
 
-	if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+	if ((lens >> 4) == oi_len && os_memcmp(pos, oi, oi_len) == 0)
 		return 1;
 	pos += lens >> 4;
 
-	if (pos < end && (size_t) (end - pos) == rc_len &&
-	    os_memcmp(pos, rc_id, rc_len) == 0)
+	if (pos < end && (size_t) (end - pos) == oi_len &&
+	    os_memcmp(pos, oi, oi_len) == 0)
 		return 1;
 
 	return 0;
 }
 
 
-static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
-					 const u8 *rc_id, size_t rc_len)
+static int oi_anqp_match(const struct wpabuf *anqp, const u8 *oi,
+			 size_t oi_len)
 {
 	const u8 *pos, *end;
 	u8 len;
@@ -1145,7 +1161,7 @@
 		len = *pos++;
 		if (len > end - pos)
 			break;
-		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+		if (len == oi_len && os_memcmp(pos, oi, oi_len) == 0)
 			return 1;
 		pos += len;
 	}
@@ -1154,11 +1170,26 @@
 }
 
 
-static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
-				    const u8 *rc_id, size_t rc_len)
+static int oi_match(const u8 *ie, const struct wpabuf *anqp,
+		    const u8 *oi, size_t oi_len)
 {
-	return roaming_consortium_element_match(ie, rc_id, rc_len) ||
-		roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+	return oi_element_match(ie, oi, oi_len) ||
+		oi_anqp_match(anqp, oi, oi_len);
+}
+
+
+static int cred_home_ois_match(const u8 *ie, const struct wpabuf *anqp,
+			       const struct wpa_cred *cred) {
+	unsigned int i;
+
+	/* There's a match if at least one of the home OI matches. */
+	for (i = 0; i < cred->num_home_ois; i++) {
+		if (oi_match(ie, anqp, cred->home_ois[i],
+			     cred->home_ois_len[i]))
+			return 1;
+	}
+
+	return 0;
 }
 
 
@@ -1169,9 +1200,8 @@
 	unsigned int i;
 
 	for (i = 0; i < cred->num_roaming_consortiums; i++) {
-		if (roaming_consortium_match(ie, anqp,
-					     cred->roaming_consortiums[i],
-					     cred->roaming_consortiums_len[i]))
+		if (oi_match(ie, anqp, cred->roaming_consortiums[i],
+			     cred->roaming_consortiums_len[i]))
 			return 1;
 	}
 
@@ -1182,8 +1212,9 @@
 static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
 {
 	const u8 *ie;
+	unsigned int i;
 
-	if (cred->required_roaming_consortium_len == 0)
+	if (cred->num_required_home_ois == 0)
 		return 0;
 
 	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@@ -1192,11 +1223,16 @@
 	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
 		return 1;
 
-	return !roaming_consortium_match(ie,
-					 bss->anqp ?
-					 bss->anqp->roaming_consortium : NULL,
-					 cred->required_roaming_consortium,
-					 cred->required_roaming_consortium_len);
+	/* According to Passpoint specification, there must be a match for
+	 * each required home OI provided. */
+	for (i = 0; i < cred->num_required_home_ois; i++) {
+		if (!oi_match(ie, bss->anqp ?
+			      bss->anqp->roaming_consortium : NULL,
+			      cred->required_home_ois[i],
+			      cred->required_home_ois_len[i]))
+			return 1;
+	}
+	return 0;
 }
 
 
@@ -1396,26 +1432,24 @@
 		return NULL;
 
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-		if (cred->roaming_consortium_len == 0 &&
+		if (cred->num_home_ois == 0 &&
+		    cred->num_required_home_ois == 0 &&
 		    cred->num_roaming_consortiums == 0)
 			continue;
 
 		if (!cred->eap_method)
 			continue;
 
-		if ((cred->roaming_consortium_len == 0 ||
-		     !roaming_consortium_match(ie, anqp,
-					       cred->roaming_consortium,
-					       cred->roaming_consortium_len)) &&
-		    !cred_roaming_consortiums_match(ie, anqp, cred) &&
-		    (cred->required_roaming_consortium_len == 0 ||
-		     !roaming_consortium_match(
-			     ie, anqp, cred->required_roaming_consortium,
-			     cred->required_roaming_consortium_len)))
+		/* If there's required home OIs, there must be a match for each
+		 * required OI (see Passpoint v3.2 - 9.1.2 - RequiredHomeOI). */
+		if (cred->num_required_home_ois > 0 &&
+		    cred_no_required_oi_match(cred, bss))
 			continue;
 
-		if (cred_no_required_oi_match(cred, bss))
+		if (!cred_home_ois_match(ie, anqp, cred) &&
+		    !cred_roaming_consortiums_match(ie, anqp, cred))
 			continue;
+
 		if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
 			continue;
 		if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
@@ -1631,9 +1665,8 @@
 	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
 	anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL;
 	for (i = 0; (ie || anqp) && i < cred->num_roaming_consortiums; i++) {
-		if (!roaming_consortium_match(
-			    ie, anqp, cred->roaming_consortiums[i],
-			    cred->roaming_consortiums_len[i]))
+		if (!oi_match(ie, anqp, cred->roaming_consortiums[i],
+			      cred->roaming_consortiums_len[i]))
 			continue;
 
 		ssid->roaming_consortium_selection =
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index b67396d..486fc6a 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -225,7 +225,7 @@
 		    ifmsh->conf->ieee80211n,
 		    ifmsh->conf->ieee80211ac,
 		    ifmsh->conf->ieee80211ax,
-		    false,
+		    ifmsh->conf->ieee80211be,
 		    ifmsh->conf->secondary_channel,
 		    hostapd_get_oper_chwidth(ifmsh->conf),
 		    hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
@@ -634,6 +634,7 @@
 	wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
 	wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
 	wpa_s->mesh_he_enabled = !!params->freq.he_enabled;
+	wpa_s->mesh_eht_enabled = !!params->freq.eht_enabled;
 	if (params->freq.ht_enabled && params->freq.sec_channel_offset)
 		ssid->ht40 = params->freq.sec_channel_offset;
 
@@ -643,23 +644,27 @@
 		switch (params->freq.bandwidth) {
 		case 80:
 			if (params->freq.center_freq2) {
-				ssid->max_oper_chwidth = CHANWIDTH_80P80MHZ;
+				ssid->max_oper_chwidth =
+					CONF_OPER_CHWIDTH_80P80MHZ;
 				ssid->vht_center_freq2 =
 					params->freq.center_freq2;
 			} else {
-				ssid->max_oper_chwidth = CHANWIDTH_80MHZ;
+				ssid->max_oper_chwidth =
+					CONF_OPER_CHWIDTH_80MHZ;
 			}
 			break;
 		case 160:
-			ssid->max_oper_chwidth = CHANWIDTH_160MHZ;
+			ssid->max_oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
 			break;
 		default:
-			ssid->max_oper_chwidth = CHANWIDTH_USE_HT;
+			ssid->max_oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
 			break;
 		}
 	}
 	if (wpa_s->mesh_he_enabled)
 		ssid->he = 1;
+	if (wpa_s->mesh_eht_enabled)
+		ssid->eht = 1;
 	if (ssid->beacon_int > 0)
 		params->beacon_int = ssid->beacon_int;
 	else if (wpa_s->conf->beacon_int > 0)
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 2eb9a7e..da6d9db 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -263,6 +263,13 @@
 	if (type != PLINK_CLOSE && conf->ocv)
 		buf_len += OCV_OCI_EXTENDED_LEN;
 #endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211BE
+	if (type != PLINK_CLOSE && wpa_s->mesh_eht_enabled) {
+		buf_len += 3 + 2 + EHT_PHY_CAPAB_LEN + EHT_MCS_NSS_CAPAB_LEN +
+			EHT_PPE_THRESH_CAPAB_LEN;
+		buf_len += 3 + sizeof(struct ieee80211_eht_operation);
+}
+#endif /* CONFIG_IEEE80211BE */
 
 	buf = wpabuf_alloc(buf_len);
 	if (!buf)
@@ -390,7 +397,6 @@
 		wpabuf_put_data(buf, he_capa_oper, pos - he_capa_oper);
 	}
 #endif /* CONFIG_IEEE80211AX */
-
 #ifdef CONFIG_OCV
 	if (type != PLINK_CLOSE && conf->ocv) {
 		struct wpa_channel_info ci;
@@ -407,6 +413,21 @@
 	}
 #endif /* CONFIG_OCV */
 
+#ifdef CONFIG_IEEE80211BE
+	if (type != PLINK_CLOSE && wpa_s->mesh_eht_enabled) {
+		u8 eht_capa_oper[3 +
+				 2 +
+				 EHT_PHY_CAPAB_LEN +
+				 EHT_MCS_NSS_CAPAB_LEN +
+				 EHT_PPE_THRESH_CAPAB_LEN +
+				 3 + sizeof(struct ieee80211_eht_operation)];
+		pos = hostapd_eid_eht_capab(bss, eht_capa_oper,
+					    IEEE80211_MODE_MESH);
+		pos = hostapd_eid_eht_operation(bss, pos);
+		wpabuf_put_data(buf, eht_capa_oper, pos - eht_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"Mesh MPM: failed to add AMPE and MIC IE");
@@ -758,6 +779,13 @@
 			  elems->he_capabilities, elems->he_capabilities_len);
 	copy_sta_he_6ghz_capab(data, sta, elems->he_6ghz_band_cap);
 #endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+	copy_sta_eht_capab(data, sta, IEEE80211_MODE_MESH,
+			   elems->he_capabilities,
+			   elems->he_capabilities_len,
+			   elems->eht_capabilities,
+			   elems->eht_capabilities_len);
+#endif /*CONFIG_IEEE80211BE */
 
 	if (hostapd_get_aid(data, sta) < 0) {
 		wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
@@ -779,6 +807,8 @@
 	params.he_capab = sta->he_capab;
 	params.he_capab_len = sta->he_capab_len;
 	params.he_6ghz_capab = sta->he_6ghz_capab;
+	params.eht_capab = sta->eht_capab;
+	params.eht_capab_len = sta->eht_capab_len;
 	params.flags |= WPA_STA_WMM;
 	params.flags_mask |= WPA_STA_AUTHENTICATED;
 	if (conf->security == MESH_CONF_SEC_NONE) {
@@ -879,7 +909,8 @@
 
 	if (conf->security & MESH_CONF_SEC_AMPE) {
 		wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
-		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
+		wpa_drv_set_key(wpa_s, -1,
+				wpa_cipher_to_alg(conf->pairwise_cipher),
 				sta->addr, 0, 0, seq, sizeof(seq),
 				sta->mtk, sta->mtk_len,
 				KEY_FLAG_PAIRWISE_RX_TX);
@@ -888,7 +919,8 @@
 				sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
 		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
 				sta->mgtk, sta->mgtk_len);
-		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
+		wpa_drv_set_key(wpa_s, -1,
+				wpa_cipher_to_alg(conf->group_cipher),
 				sta->addr, sta->mgtk_key_id, 0,
 				sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
 				sta->mgtk, sta->mgtk_len,
@@ -900,7 +932,7 @@
 			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
 					sta->igtk, sta->igtk_len);
 			wpa_drv_set_key(
-				wpa_s,
+				wpa_s, -1,
 				wpa_cipher_to_alg(conf->mgmt_group_cipher),
 				sta->addr, sta->igtk_key_id, 0,
 				sta->igtk_rsc, sizeof(sta->igtk_rsc),
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 65daa77..12dcc30 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -118,7 +118,7 @@
 	}
 	wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
 
-	return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+	return wpa_drv_set_key(mesh_rsn->wpa_s, -1, alg, addr, idx,
 			       1, seq, 6, key, key_len, key_flag);
 }
 
@@ -194,7 +194,7 @@
 		/* group mgmt */
 		wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
 				rsn->igtk, rsn->igtk_len);
-		wpa_drv_set_key(rsn->wpa_s,
+		wpa_drv_set_key(rsn->wpa_s, -1,
 				wpa_cipher_to_alg(rsn->mgmt_group_cipher),
 				broadcast_ether_addr,
 				rsn->igtk_key_id, 1,
@@ -205,7 +205,7 @@
 	/* group privacy / data frames */
 	wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
 			rsn->mgtk, rsn->mgtk_len);
-	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
+	wpa_drv_set_key(rsn->wpa_s, -1, wpa_cipher_to_alg(rsn->group_cipher),
 			broadcast_ether_addr,
 			rsn->mgtk_key_id, 1, seq, sizeof(seq),
 			rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index ec223d8..89a0389 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -16,6 +16,7 @@
 #include "dbus/dbus_common.h"
 #include "dbus/dbus_new.h"
 #include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
 #include "fst/fst.h"
 #include "crypto/tls.h"
 #include "bss.h"
@@ -37,9 +38,13 @@
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #ifdef CONFIG_AIDL
-	global->aidl = wpas_aidl_init(global);
-	if (!global->aidl)
-		return -1;
+	// Initialize AIDL here if daemonize is disabled.
+	// Otherwise initialize it later.
+	if (!global->params.daemonize) {
+		global->aidl = wpas_aidl_init(global);
+		if (!global->aidl)
+			return -1;
+	}
 #endif /* CONFIG_AIDL */
 
 	return 0;
@@ -67,9 +72,18 @@
 			return -1;
 	}
 
-	/* HIDL interface wants to keep track of the P2P mgmt iface. */
+#ifdef CONFIG_AIDL
+	/*
+	 * AIDL initialization may not be complete here if daemonize is enabled.
+	 * Initialization is done after daemonizing in order to avoid
+	 * issues with the file descriptor.
+	 */
+	if (!wpa_s->global->aidl)
+		return 0;
+	/* AIDL interface wants to keep track of the P2P mgmt iface. */
 	if (wpas_aidl_register_interface(wpa_s))
 		return -1;
+#endif
 
 	return 0;
 }
@@ -82,7 +96,7 @@
 		wpas_dbus_unregister_interface(wpa_s);
 	}
 
-	/* HIDL interface wants to keep track of the P2P mgmt iface. */
+	/* AIDL interface wants to keep track of the P2P mgmt iface. */
 	wpas_aidl_unregister_interface(wpa_s);
 }
 
@@ -237,6 +251,15 @@
 }
 
 
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_MAC_ADDRESS);
+}
+
+
 void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->p2p_mgmt)
@@ -280,6 +303,12 @@
 }
 
 
+void wpas_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s)
+{
+	wpas_aidl_notify_permanent_id_req_denied(wpa_s);
+}
+
+
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->p2p_mgmt)
@@ -424,6 +453,10 @@
 		wpa_s->last_ssid = NULL;
 	if (wpa_s->current_ssid == ssid)
 		wpa_s->current_ssid = NULL;
+#if defined(CONFIG_SME) && defined(CONFIG_SAE)
+	if (wpa_s->sme.ext_auth_wpa_ssid == ssid)
+		wpa_s->sme.ext_auth_wpa_ssid = NULL;
+#endif /* CONFIG_SME && CONFIG_SAE */
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
@@ -437,11 +470,6 @@
 		wpas_notify_persistent_group_removed(wpa_s, ssid);
 
 	wpas_p2p_network_removed(wpa_s, ssid);
-
-#ifdef CONFIG_PASN
-	if (wpa_s->pasn.ssid == ssid)
-		wpa_s->pasn.ssid = NULL;
-#endif /* CONFIG_PASN */
 }
 
 
@@ -787,7 +815,7 @@
 
 	wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent, ip);
 
-	wpas_aidl_notify_p2p_group_started(wpa_s, ssid, persistent, client);
+	wpas_aidl_notify_p2p_group_started(wpa_s, ssid, persistent, client, ip);
 }
 
 
@@ -952,6 +980,12 @@
 }
 
 
+void wpas_notify_psk_mismatch(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_psk_mismatch(wpa_s);
+}
+
+
 void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid)
 {
@@ -1099,13 +1133,13 @@
 /* DPP Success notifications */
 
 void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-	    struct wpa_ssid *ssid)
+	    struct wpa_ssid *ssid, bool conn_status_requested)
 {
 #ifdef CONFIG_DPP
 	if (!wpa_s)
 		return;
 
-	wpas_aidl_notify_dpp_config_received(wpa_s, ssid);
+	wpas_aidl_notify_dpp_config_received(wpa_s, ssid, conn_status_requested);
 #endif /* CONFIG_DPP */
 }
 
@@ -1119,6 +1153,17 @@
 #endif /* CONFIG_DPP */
 }
 
+void wpas_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+	    enum dpp_status_error result)
+{
+#ifdef CONFIG_DPP2
+	if (!wpa_s)
+		return;
+
+	wpas_aidl_notify_dpp_connection_status_sent(wpa_s, result);
+#endif /* CONFIG_DPP2 */
+}
+
 /* DPP Progress notifications */
 void wpas_notify_dpp_auth_success(struct wpa_supplicant *wpa_s)
 {
@@ -1331,3 +1376,28 @@
 
 	wpas_aidl_notify_frequency_changed(wpa_s, frequency);
 }
+
+ssize_t wpas_get_certificate(const char *alias, uint8_t** value)
+{
+	wpa_printf(MSG_INFO, "wpas_get_certificate");
+	return wpas_aidl_get_certificate(alias, value);
+}
+
+ssize_t wpas_list_aliases(const char *prefix, char ***aliases)
+{
+	return wpas_aidl_list_aliases(prefix, aliases);
+}
+
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SIGNAL_CHANGE);
+}
+
+void wpas_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
+		unsigned int num_scs_resp, int **scs_resp)
+{
+	if (!wpa_s || !num_scs_resp || !scs_resp)
+		return;
+
+	wpas_aidl_notify_qos_policy_scs_response(wpa_s, num_scs_resp, scs_resp);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 996be84..bc2bd64 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -19,6 +19,7 @@
 struct wps_event_fail;
 struct tls_cert_data;
 struct wpa_cred;
+struct rsn_pmksa_cache_entry;
 
 int wpas_notify_supplicant_initialized(struct wpa_global *global);
 void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
@@ -39,6 +40,7 @@
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_mac_address_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid);
@@ -48,6 +50,7 @@
 				 struct wpa_ssid *ssid,
 				 enum wpa_ctrl_req_type rtype,
 				 const char *default_txt);
+void wpas_notify_permanent_id_req_denied(struct wpa_supplicant *wpa_s);
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
 void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
 void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
@@ -149,6 +152,7 @@
 void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
 			    const char *parameter);
 void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code);
+void wpas_notify_psk_mismatch(struct wpa_supplicant *wpa_s);
 void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid);
 void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
@@ -180,8 +184,10 @@
 void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
 		struct wpa_supplicant *wpa_s, const char *url);
 void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
-	    struct wpa_ssid *ssid);
+		struct wpa_ssid *ssid, bool conn_status_requested);
 void wpas_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
+void wpas_notify_dpp_connection_status_sent(struct wpa_supplicant *wpa_s,
+		enum dpp_status_error result);
 void wpas_notify_dpp_auth_success(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_resp_pending(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_not_compatible(struct wpa_supplicant *wpa_s);
@@ -196,8 +202,6 @@
 		const char *channel_list, unsigned short band_list[], int size);
 void wpas_notify_dpp_config_accepted(struct wpa_supplicant *wpa_s);
 void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
-void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
-				 struct rsn_pmksa_cache_entry *entry);
 void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
 				    struct wpa_ssid *ssid,
 				    u8 bitmap);
@@ -218,5 +222,12 @@
 void wpas_notify_qos_policy_request(struct wpa_supplicant *wpa_s,
 		struct dscp_policy_data *policies, int num_policies);
 void wpas_notify_frequency_changed(struct wpa_supplicant *wpa_s, int frequency);
+ssize_t wpas_get_certificate(const char *alias, uint8_t** value);
+ssize_t wpas_list_aliases(const char *prefix, char ***aliases);
+void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
+				 struct rsn_pmksa_cache_entry *entry);
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s);
+void wpas_notify_qos_policy_scs_response(struct wpa_supplicant *wpa_s,
+		unsigned int num_scs_resp, int **scs_resp);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index bd53c5c..b4ad3ca 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -103,10 +103,7 @@
 		    NOT_ALLOWED)
 			return NOT_ALLOWED;
 
-		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
-		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
-		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
-		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
+		if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL))
 			return NOT_ALLOWED;
 
 		if (flags & HOSTAPD_CHAN_NO_IR)
@@ -175,14 +172,8 @@
 		    NOT_ALLOWED)
 			return NOT_ALLOWED;
 
-		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
-		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
-		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
-		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
-		    (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
-		    (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
-		    (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
-		    (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
+		if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL) ||
+		    !(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL))
 			return NOT_ALLOWED;
 
 		if (flags & HOSTAPD_CHAN_NO_IR)
@@ -470,8 +461,9 @@
 	 * or used.
 	 */
 	if (wpas_sta_secondary_channel_offset(bss, &current, &chan) < 0 &&
-	    ieee80211_freq_to_channel_ext(bss->freq, 0, CHANWIDTH_USE_HT,
-					  &current, &chan) == NUM_HOSTAPD_MODES)
+	    ieee80211_freq_to_channel_ext(bss->freq, 0,
+					  CONF_OPER_CHWIDTH_USE_HT, &current,
+					  &chan) == NUM_HOSTAPD_MODES)
 		return 0;
 
 	/*
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 3c395e9..09c11e0 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -129,7 +129,7 @@
 			       const u8 *ssid, size_t ssid_len);
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
 				int *force_freq, int *pref_freq, int go,
-				unsigned int *pref_freq_list,
+				struct weighted_pcl *pref_freq_list,
 				unsigned int *num_pref_freq);
 static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
 				   const u8 *ssid, size_t ssid_len);
@@ -165,6 +165,8 @@
 			     struct wpa_used_freq_data *freqs, unsigned int num,
 			     enum wpas_p2p_channel_update_trig trig);
 static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_disallowed_freq(struct wpa_global *global,
+				    unsigned int freq);
 
 
 static int wpas_get_6ghz_he_chwidth_capab(struct hostapd_hw_modes *mode)
@@ -192,7 +194,7 @@
 		return -1;
 
 	num = get_shared_radio_freqs(wpa_s, freqs,
-				     wpa_s->num_multichan_concurrent);
+				     wpa_s->num_multichan_concurrent, false);
 	os_free(freqs);
 
 	unused = wpa_s->num_multichan_concurrent - num;
@@ -219,7 +221,8 @@
 		return 0;
 
 	num = get_shared_radio_freqs_data(wpa_s, freqs,
-					  wpa_s->num_multichan_concurrent);
+					  wpa_s->num_multichan_concurrent,
+					  false);
 
 	os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
 
@@ -339,6 +342,23 @@
 }
 
 
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+			 struct wpa_driver_scan_params *params,
+			 bool include_6ghz)
+{
+	wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+				params, false, false, false);
+	wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+				params, false, false, false);
+	wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD,
+				params, false, false, false);
+	if (!wpa_s->conf->p2p_6ghz_disable &&
+	    is_p2p_allow_6ghz(wpa_s->global->p2p) && include_6ghz)
+		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+					params, true, true, false);
+}
+
+
 static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
 {
 	struct wpa_supplicant *wpa_s = work->wpa_s;
@@ -361,14 +381,9 @@
 		params->only_new_results = 1;
 	}
 
-	if (!params->p2p_include_6ghz && !params->freqs) {
-		wpa_printf(MSG_DEBUG,
-			   "P2P: Exclude 6 GHz channels - update the scan frequency list");
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
-					0);
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
-					0);
-	}
+	if (!params->freqs)
+		wpas_p2p_scan_freqs(wpa_s, params, params->p2p_include_6ghz);
+
 	ret = wpa_drv_scan(wpa_s, params);
 	if (ret == 0)
 		wpa_s->curr_scan_cookie = params->scan_cookie;
@@ -447,6 +462,13 @@
 					num_req_dev_types, req_dev_types);
 	if (wps_ie == NULL)
 		goto fail;
+
+	/*
+	 * In case 6 GHz channels are requested as part of the P2P scan, only
+	 * the PSCs would be included as P2P GOs are not expected to be
+	 * collocated, i.e., they would not be announced in the RNR element of
+	 * other APs.
+	 */
 	if (!wpa_s->conf->p2p_6ghz_disable)
 		params->p2p_include_6ghz = include_6ghz;
 	switch (type) {
@@ -533,9 +555,9 @@
 		return WPA_IF_P2P_GO;
 	case P2P_GROUP_INTERFACE_CLIENT:
 		return WPA_IF_P2P_CLIENT;
+	default:
+		return WPA_IF_P2P_GROUP;
 	}
-
-	return WPA_IF_P2P_GROUP;
 }
 
 
@@ -705,7 +727,8 @@
 	struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
 	struct wpa_ssid *persistent_go;
 	int p2p_no_group_iface;
-	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int size;
 
 	wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
 
@@ -995,6 +1018,7 @@
 	}
 
 	wpa_s->p2p_in_invitation = 0;
+	wpa_s->p2p_retry_limit = 0;
 	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
 
@@ -1389,6 +1413,7 @@
 		wpa_s->p2p_in_provisioning = 0;
 	}
 	wpa_s->p2p_in_invitation = 0;
+	wpa_s->p2p_retry_limit = 0;
 	wpa_s->group_formation_reported = 1;
 
 	if (!success) {
@@ -2087,7 +2112,7 @@
 		ssid->auth_alg |= WPA_AUTH_ALG_SAE;
 		ssid->key_mgmt = WPA_KEY_MGMT_SAE;
 		ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
-		ssid->sae_pwe = 1;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt");
 	} else {
 		p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
@@ -2178,6 +2203,7 @@
 	d->passive_scan = s->passive_scan;
 	d->pmf = s->pmf;
 	d->p2p_6ghz_disable = s->p2p_6ghz_disable;
+	d->sae_pwe = s->sae_pwe;
 
 	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
 	    !d->wps_nfc_pw_from_config) {
@@ -2406,6 +2432,21 @@
 }
 
 
+bool wpas_p2p_retry_limit_exceeded(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->p2p_in_invitation || !wpa_s->p2p_retry_limit ||
+	    wpa_s->p2p_in_invitation <= wpa_s->p2p_retry_limit)
+		return false;
+
+	wpa_printf(MSG_DEBUG, "P2P: Group join retry limit exceeded");
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->p2pdev, NULL);
+	eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+			       wpa_s->p2pdev, NULL);
+	return true;
+}
+
+
 static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -3097,6 +3138,68 @@
 }
 
 
+/**
+ * Pick the best frequency the driver suggests.
+ *
+ * num_pref_freq is used as both input and output
+ * - input: the max size of pref_freq_list,
+ * - output: the valid size of pref_freq_list filled with data.
+ */
+static int wpas_p2p_pick_best_pref_freq(struct wpa_supplicant *wpa_s, bool go,
+					struct weighted_pcl *pref_freq_list,
+					unsigned int *num_pref_freq)
+{
+	int best_freq = 0;
+	unsigned int max_pref_freq, i;
+	int res;
+	enum wpa_driver_if_type iface_type;
+
+	max_pref_freq = *num_pref_freq;
+	*num_pref_freq = 0;
+
+	if (go)
+		iface_type = WPA_IF_P2P_GO;
+	else
+		iface_type = WPA_IF_P2P_CLIENT;
+
+	res = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &max_pref_freq,
+					 pref_freq_list);
+	if (!res && !is_p2p_allow_6ghz(wpa_s->global->p2p))
+		max_pref_freq = p2p_remove_6ghz_channels(pref_freq_list,
+							 max_pref_freq);
+	if (res || !max_pref_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: No preferred frequency list available");
+		return 0;
+	}
+
+	*num_pref_freq = max_pref_freq;
+	i = 0;
+	while (i < *num_pref_freq &&
+	       (!p2p_supported_freq(wpa_s->global->p2p,
+				    pref_freq_list[i].freq) ||
+		wpas_p2p_disallowed_freq(wpa_s->global,
+					 pref_freq_list[i].freq) ||
+		!p2p_pref_freq_allowed(&pref_freq_list[i], go))) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: preferred_freq_list[%d]=%d is disallowed",
+			   i, pref_freq_list[i].freq);
+		i++;
+	}
+	if (i != *num_pref_freq) {
+		best_freq = pref_freq_list[i].freq;
+		wpa_printf(MSG_DEBUG, "P2P: Using preferred_freq_list[%d]=%d",
+			   i, best_freq);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: All driver preferred frequencies are disallowed for P2P use");
+		*num_pref_freq = 0;
+	}
+
+	return best_freq;
+}
+
+
 static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
 				  const u8 *go_dev_addr, const u8 *ssid,
 				  size_t ssid_len, int *go, u8 *group_bssid,
@@ -3109,6 +3212,10 @@
 	struct wpa_used_freq_data *freqs;
 	struct wpa_supplicant *grp;
 	int best_freq;
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int num_pref_freq;
+	int res;
+
 
 	if (!persistent_group) {
 		wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
@@ -3214,6 +3321,12 @@
 		os_free(freqs);
 	}
 
+	num_pref_freq = P2P_MAX_PREF_CHANNELS;
+	res = wpas_p2p_pick_best_pref_freq(wpa_s, *go, pref_freq_list,
+					   &num_pref_freq);
+	if (res > 0)
+		best_freq = res;
+
 	/* Get one of the frequencies currently in use */
 	if (best_freq > 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
@@ -3291,7 +3404,8 @@
 				wpa_s->conf->p2p_go_he,
 				wpa_s->conf->p2p_go_edmg, NULL,
 				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
-				1, is_p2p_allow_6ghz(wpa_s->global->p2p));
+				1, is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
+				NULL);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpa_msg_global(wpa_s, MSG_INFO,
@@ -3521,7 +3635,8 @@
 				      ssid->mode == WPAS_MODE_P2P_GO ?
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
 				      0, 1,
-				      is_p2p_allow_6ghz(wpa_s->global->p2p));
+				      is_p2p_allow_6ghz(wpa_s->global->p2p), 0,
+				      NULL);
 }
 
 
@@ -3700,13 +3815,7 @@
 		if (res == NO_IR)
 			ret = NO_IR;
 		if (!is_6ghz) {
-			if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
-				return NOT_ALLOWED;
-			if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
-				return NOT_ALLOWED;
-			if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
-				return NOT_ALLOWED;
-			if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+			if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL))
 				return NOT_ALLOWED;
 		} else if (is_6ghz &&
 			   (!(wpas_get_6ghz_he_chwidth_capab(mode) &
@@ -3784,21 +3893,8 @@
 			ret = NO_IR;
 
 		if (!is_6ghz_op_class(op_class)) {
-			if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
-				return NOT_ALLOWED;
-			if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
-				return NOT_ALLOWED;
-			if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
-				return NOT_ALLOWED;
-			if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
-				return NOT_ALLOWED;
-			if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
-				return NOT_ALLOWED;
-			if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
-				return NOT_ALLOWED;
-			if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
-				return NOT_ALLOWED;
-			if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+			if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL) ||
+			    !(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL))
 				return NOT_ALLOWED;
 		} else if (is_6ghz_op_class(op_class) &&
 			   (!(wpas_get_6ghz_he_chwidth_capab(mode) &
@@ -3832,6 +3928,10 @@
 	int flag = 0;
 	enum chan_allowed res, res2;
 
+	if (is_6ghz_op_class(op_class) && !is_6ghz_psc_frequency(
+			p2p_channel_to_freq(op_class, channel)))
+		return NOT_ALLOWED;
+
 	res2 = res = has_channel(wpa_s->global, mode, op_class, channel, &flag);
 	if (bw == BW40MINUS) {
 		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
@@ -3893,8 +3993,10 @@
 		const struct oper_class_map *o = &global_op_class[op];
 		unsigned int ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
+		bool check_dfs_supported = (is_p2p_dfs_chan_enabled(wpa_s->global->p2p)
+							&& is_dfs_global_op_class(o->op_class));
 
-		if (o->p2p == NO_P2P_SUPP ||
+		if ((!check_dfs_supported && o->p2p == NO_P2P_SUPP) ||
 		    (is_6ghz_op_class(o->op_class) && p2p_disable_6ghz))
 			continue;
 
@@ -3973,8 +4075,7 @@
 
 	for (op = 0; global_op_class[op].op_class; op++) {
 		const struct oper_class_map *o = &global_op_class[op];
-		u16 ch;
-		int chan = channel;
+		u16 ch = 0;
 
 		/* Allow DFS channels marked as NO_P2P_SUPP to be used with
 		 * driver offloaded DFS. */
@@ -3985,15 +4086,22 @@
 		     wpa_s->conf->p2p_6ghz_disable))
 			continue;
 
+		/* IEEE Std 802.11ax-2021 26.17.2.3.2: "A 6 GHz-only AP should
+		 * set up the BSS with a primary 20 MHz channel that coincides
+		 * with a preferred scanning channel (PSC)."
+		 * 6 GHz BW40 operation class 132 in wpa_supplicant uses the
+		 * lowest 20 MHz channel for simplicity, so increase ch by 4 to
+		 * match the PSC.
+		 */
 		if (is_6ghz_op_class(o->op_class) && o->bw == BW40 &&
 		    get_6ghz_sec_channel(channel) < 0)
-			chan = channel - 4;
+			ch = 4;
 
-		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+		for (ch += o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
 			    (o->bw != BW40PLUS && o->bw != BW40MINUS &&
 			     o->bw != BW40) ||
-			    ch != chan)
+			    ch != channel)
 				continue;
 			ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
 						      ch, o->bw);
@@ -4597,7 +4705,7 @@
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0, 0, false);
+					0, 0, false, 0, NULL);
 			} else if (response_done) {
 				wpas_p2p_group_add(wpa_s, 1, freq,
 						   0, 0, 0, 0, 0, 0, false);
@@ -4720,7 +4828,7 @@
 			NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
 			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0,
-			is_p2p_allow_6ghz(wpa_s->global->p2p));
+			is_p2p_allow_6ghz(wpa_s->global->p2p), 0, NULL);
 	} else {
 		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0,
 				   is_p2p_allow_6ghz(wpa_s->global->p2p));
@@ -4732,7 +4840,7 @@
 
 static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
 				       unsigned int *len,
-				       unsigned int *freq_list)
+				       struct weighted_pcl *freq_list)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
@@ -4742,6 +4850,7 @@
 
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
 {
+	int ret = 0;
 	u8 addr[ETH_ALEN] = {0};
 
 	if (wpa_s->conf->p2p_device_random_mac_addr == 0)
@@ -4779,16 +4888,20 @@
 		wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address.");
 	}
 
-	if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+	ret = wpa_drv_set_mac_addr(wpa_s, addr);
+
+	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"Failed to set random MAC address");
-		return -EINVAL;
+		return ret;
 	}
 
-	if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+	ret = wpa_supplicant_update_mac_addr(wpa_s);
+
+	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"Could not update MAC address information");
-		return -EINVAL;
+		return ret;
 	}
 
 	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
@@ -4858,6 +4971,7 @@
 	p2p.p2ps_group_capability = p2ps_group_capability;
 	p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
 	p2p.p2p_6ghz_disable = wpa_s->conf->p2p_6ghz_disable;
+	p2p.p2p_dfs_chan_enable = wpa_s->conf->p2p_dfs_chan_enable;
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -5528,14 +5642,8 @@
 	if (freq > 0) {
 		freqs[0] = freq;
 		params.freqs = freqs;
-	} else if (wpa_s->conf->p2p_6ghz_disable ||
-		   !is_p2p_allow_6ghz(wpa_s->global->p2p)) {
-		wpa_printf(MSG_DEBUG,
-			   "P2P: 6 GHz disabled - update the scan frequency list");
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params,
-					0);
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, &params,
-					0);
+	} else {
+		wpas_p2p_scan_freqs(wpa_s, &params, true);
 	}
 
 	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
@@ -5708,7 +5816,7 @@
 
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
 				int *force_freq, int *pref_freq, int go,
-				unsigned int *pref_freq_list,
+				struct weighted_pcl *pref_freq_list,
 				unsigned int *num_pref_freq)
 {
 	struct wpa_used_freq_data *freqs;
@@ -5804,47 +5912,14 @@
 				i++;
 			}
 		} else if (!wpa_s->conf->num_p2p_pref_chan) {
-			enum wpa_driver_if_type iface_type;
-
-			if (go)
-				iface_type = WPA_IF_P2P_GO;
-			else
-				iface_type = WPA_IF_P2P_CLIENT;
-
 			wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
-				best_freq, go);
+				   best_freq, go);
 
-			res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
-						 &max_pref_freq,
-						 pref_freq_list);
-			if (!res && max_pref_freq > 0) {
-				*num_pref_freq = max_pref_freq;
-				i = 0;
-				while (i < *num_pref_freq &&
-					(!p2p_supported_freq(wpa_s->global->p2p,
-						pref_freq_list[i]) ||
-					wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i]))) {
-					wpa_printf(MSG_DEBUG,
-						"P2P: preferred_freq_list[%d]=%d is disallowed",
-						i, pref_freq_list[i]);
-					i++;
-				}
-				if (i != *num_pref_freq) {
-					best_freq = pref_freq_list[i];
-					wpa_printf(MSG_DEBUG,
-						"P2P: Using preferred_freq_list[%d]=%d",
-						i, best_freq);
-				} else {
-					wpa_printf(MSG_DEBUG,
-						"P2P: All driver preferred frequencies are "
-						"disallowed for P2P use");
-					*num_pref_freq = 0;
-				}
-			} else {
-				wpa_printf(MSG_DEBUG,
-					"P2P: No preferred frequency list available");
-			}
+			*num_pref_freq = max_pref_freq;
+			res = wpas_p2p_pick_best_pref_freq(wpa_s, go, pref_freq_list,
+							   num_pref_freq);
+			if (res > 0)
+				best_freq = res;
 		}
 	}
 
@@ -5955,7 +6030,8 @@
 	enum wpa_driver_if_type iftype;
 	const u8 *if_addr;
 	struct wpa_ssid *ssid = NULL;
-	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int size;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
@@ -6240,7 +6316,7 @@
 
 	if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
 		unsigned int i, size = P2P_MAX_PREF_CHANNELS;
-		unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+		struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
 		int res;
 
 		res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
@@ -6252,16 +6328,19 @@
 			i = 0;
 			while (i < size &&
 			       (!p2p_supported_freq(wpa_s->global->p2p,
-						    pref_freq_list[i]) ||
-				wpas_p2p_disallowed_freq(wpa_s->global,
-							 pref_freq_list[i]))) {
+						    pref_freq_list[i].freq) ||
+				wpas_p2p_disallowed_freq(
+					wpa_s->global,
+					pref_freq_list[i].freq) ||
+				!p2p_pref_freq_allowed(&pref_freq_list[i],
+						       true))) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
-					   i, pref_freq_list[i]);
+					   i, pref_freq_list[i].freq);
 				i++;
 			}
 			if (i != size) {
-				freq = pref_freq_list[i];
+				freq = pref_freq_list[i].freq;
 				wpa_printf(MSG_DEBUG,
 					   "P2P: Using preferred_freq_list[%d]=%d",
 					   i, freq);
@@ -6505,7 +6584,8 @@
 		return -1;
 
 	num = get_shared_radio_freqs_data(wpa_s, freqs,
-					  wpa_s->num_multichan_concurrent);
+					  wpa_s->num_multichan_concurrent,
+					  false);
 
 	if (wpa_s->current_ssid &&
 	    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
@@ -6740,8 +6820,8 @@
 
 			if (!is_6ghz &&
 			    ieee80211_freq_to_channel_ext(
-				    cand, -1, CHANWIDTH_USE_HT, &op_class,
-				    &chan) != NUM_HOSTAPD_MODES &&
+				    cand, -1, CONF_OPER_CHWIDTH_USE_HT,
+				    &op_class, &chan) != NUM_HOSTAPD_MODES &&
 			    wpas_p2p_verify_channel(
 				    wpa_s, hwmode, op_class, chan,
 				    BW40MINUS) == ALLOWED)
@@ -6749,8 +6829,8 @@
 
 			if (!supported && !is_6ghz &&
 			    ieee80211_freq_to_channel_ext(
-				    cand, 1, CHANWIDTH_USE_HT, &op_class,
-				    &chan) != NUM_HOSTAPD_MODES &&
+				    cand, 1, CONF_OPER_CHWIDTH_USE_HT,
+				    &op_class, &chan) != NUM_HOSTAPD_MODES &&
 			    wpas_p2p_verify_channel(
 				    wpa_s, hwmode, op_class, chan,
 				    BW40PLUS) == ALLOWED)
@@ -6896,6 +6976,7 @@
 		       bool allow_6ghz)
 {
 	struct p2p_go_neg_results params;
+	int selected_freq = 0;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
@@ -6910,12 +6991,12 @@
 	wpas_p2p_stop_find_oper(wpa_s);
 
 	if (!wpa_s->p2p_go_do_acs) {
-		freq = wpas_p2p_select_go_freq(wpa_s, freq);
-		if (freq < 0)
+		selected_freq = wpas_p2p_select_go_freq(wpa_s, freq);
+		if (selected_freq < 0)
 			return -1;
 	}
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+	if (wpas_p2p_init_go_params(wpa_s, &params, selected_freq, vht_center_freq2,
 				    ht40, vht, max_oper_chwidth, he, edmg,
 				    NULL))
 		return -1;
@@ -6926,6 +7007,8 @@
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
 	if (wpa_s == NULL)
 		return -1;
+	if (freq > 0)
+		wpa_s->p2p_go_no_pri_sec_switch = 1;
 	wpas_start_wps_go(wpa_s, &params, 0);
 
 	return 0;
@@ -6934,9 +7017,12 @@
 
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *params, int addr_allocated,
-				 int freq, int force_scan)
+				 int freq, int force_scan, int retry_limit,
+				 const u8 *go_bssid)
 {
 	struct wpa_ssid *ssid;
+	int other_iface_found = 0;
+	struct wpa_supplicant *ifs;
 
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
 	if (wpa_s == NULL)
@@ -6959,6 +7045,16 @@
 		WPA_CIPHER_CCMP;
 	ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+	if (is_6ghz_freq(freq) &&
+	    is_p2p_6ghz_capable(wpa_s->global->p2p)) {
+		ssid->auth_alg |= WPA_AUTH_ALG_SAE;
+		ssid->key_mgmt |= WPA_KEY_MGMT_SAE;
+		ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+		ssid->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Enable SAE auth_alg and key_mgmt");
+	}
+
 	ssid->ssid = os_malloc(params->ssid_len);
 	if (ssid->ssid == NULL) {
 		wpa_config_remove_network(wpa_s->conf, ssid->id);
@@ -6975,12 +7071,38 @@
 	if (params->passphrase)
 		ssid->passphrase = os_strdup(params->passphrase);
 
+	if (go_bssid) {
+		ssid->bssid_set = 1;
+		os_memcpy(ssid->bssid, go_bssid, ETH_ALEN);
+	}
+
 	wpa_s->show_group_started = 1;
 	wpa_s->p2p_in_invitation = 1;
+	wpa_s->p2p_retry_limit = retry_limit;
 	wpa_s->p2p_invite_go_freq = freq;
 	wpa_s->p2p_go_group_formation_completed = 0;
 	wpa_s->global->p2p_group_formation = wpa_s;
 
+	/*
+	 * Get latest scan results from driver in case cached scan results from
+	 * interfaces on the same wiphy allow us to skip the next scan by fast
+	 * associating. Also update the scan time to the most recent scan result
+	 * fetch time on the same radio so it reflects the actual time the last
+	 * scan result event occurred.
+	 */
+	wpa_supplicant_update_scan_results(wpa_s);
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs == wpa_s)
+			continue;
+		if (!other_iface_found || os_reltime_before(&wpa_s->last_scan,
+							    &ifs->last_scan)) {
+			other_iface_found = 1;
+			wpa_s->last_scan.sec = ifs->last_scan.sec;
+			wpa_s->last_scan.usec = ifs->last_scan.usec;
+		}
+	}
+
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
 			     NULL);
 	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
@@ -7000,7 +7122,8 @@
 				  int edmg,
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
-				  bool allow_6ghz)
+				  bool allow_6ghz, int retry_limit,
+				  const u8 *go_bssid)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -7045,6 +7168,7 @@
 			freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
 			if (freq < 0)
 				return -1;
+			wpa_s->p2p_go_no_pri_sec_switch = 1;
 		} else {
 			freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
 			if (freq < 0 ||
@@ -7068,7 +7192,7 @@
 		}
 
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
-					     force_scan);
+					     force_scan, retry_limit, go_bssid);
 	} else {
 		return -1;
 	}
@@ -7636,7 +7760,8 @@
 	int force_freq = 0;
 	int res;
 	int no_pref_freq_given = pref_freq == 0;
-	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int size;
 
 	if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
 		return -1;
@@ -7725,7 +7850,8 @@
 	int persistent;
 	int freq = 0, force_freq = 0, pref_freq = 0;
 	int res;
-	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
+	struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int size;
 
 	wpa_s->p2p_persistent_go_freq = 0;
 	wpa_s->p2p_go_ht40 = 0;
@@ -7822,6 +7948,7 @@
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
 		wpa_s->p2p_in_invitation = 0;
+		wpa_s->p2p_retry_limit = 0;
 	}
 
 	os_memset(go_dev_addr, 0, ETH_ALEN);
@@ -8316,7 +8443,7 @@
 }
 
 
-int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
 	    !wpa_s->p2p_in_provisioning)
@@ -8352,7 +8479,7 @@
 	if (!freqs)
 		return;
 
-	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
 
 	os_memset(&chan, 0, sizeof(chan));
 	os_memset(&cli_chan, 0, sizeof(cli_chan));
@@ -8538,6 +8665,10 @@
 				"in group formation",
 				wpa_s->global->p2p_group_formation->ifname);
 			ret = 1;
+		} else if (wpa_s->global->p2p_group_formation == wpa_s) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Skip Extended Listen timeout and allow scans on current interface for group formation");
+			ret = 2;
 		}
 	}
 
@@ -8651,6 +8782,7 @@
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
 		wpa_s->p2p_in_invitation = 0;
+		wpa_s->p2p_retry_limit = 0;
 	}
 	wpa_s->global->p2p_go_wait_client.sec = 0;
 	if (addr == NULL)
@@ -9660,9 +9792,6 @@
 	if (!wpa_s->conf->p2p_optimize_listen_chan)
 		return;
 
-	if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
-		return;
-
 	curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
 	for (i = 0, cand = 0; i < num; i++) {
 		ieee80211_freq_to_chan(freqs[i].freq, &chan);
@@ -9732,10 +9861,13 @@
 
 	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode &&
 	    (wpa_s->ap_iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A ||
+	     is_6ghz_freq(wpa_s->ap_iface->freq) ||
 	     conf->hw_mode != HOSTAPD_MODE_IEEE80211G)) {
 		wpa_dbg(wpa_s, MSG_INFO,
-			"P2P CSA: CSA from Hardware mode %d to %d is not supported",
-			wpa_s->ap_iface->current_mode->mode, conf->hw_mode);
+			"P2P CSA: CSA from hardware mode %d%s to %d is not supported",
+			wpa_s->ap_iface->current_mode->mode,
+			is_6ghz_freq(wpa_s->ap_iface->freq) ? " (6 GHz)" : "",
+			conf->hw_mode);
 		ret = -1;
 		goto out;
 	}
@@ -9784,13 +9916,15 @@
 		csa_settings.freq_params.center_freq2 = freq2;
 
 		switch (conf->vht_oper_chwidth) {
-		case CHANWIDTH_80MHZ:
-		case CHANWIDTH_80P80MHZ:
+		case CONF_OPER_CHWIDTH_80MHZ:
+		case CONF_OPER_CHWIDTH_80P80MHZ:
 			csa_settings.freq_params.bandwidth = 80;
 			break;
-		case CHANWIDTH_160MHZ:
+		case CONF_OPER_CHWIDTH_160MHZ:
 			csa_settings.freq_params.bandwidth = 160;
 			break;
+		default:
+			break;
 		}
 	}
 
@@ -9896,7 +10030,7 @@
 	if (!freqs)
 		return;
 
-	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num, false);
 
 	/* Previous attempt to move a GO was not possible -- try again. */
 	wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 5a869e7..d71f770 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -52,7 +52,8 @@
 				  int max_oper_chwidth, int he, int edmg,
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
-				  bool allow_6ghz);
+				  bool allow_6ghz, int retry_limit,
+				  const u8 *go_bssid);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid);
 enum wpas_p2p_prov_disc_use {
@@ -146,6 +147,9 @@
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 				       const u8 *addr);
 int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+void wpas_p2p_scan_freqs(struct wpa_supplicant *wpa_s,
+			 struct wpa_driver_scan_params *params,
+			 bool include_6ghz);
 int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
 					  struct hostapd_hw_modes *mode,
 					  u8 channel);
@@ -203,9 +207,9 @@
 void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
 void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
 void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
-int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
 int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
 void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
+bool wpas_p2p_retry_limit_exceeded(struct wpa_supplicant *wpa_s);
 void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
 void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
 void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
@@ -300,11 +304,6 @@
 {
 }
 
-static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
-{
-	return 0;
-}
-
 static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
 {
 	return 0;
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index dc21b6a..edecfde 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -23,12 +23,14 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "bss.h"
+#include "scan.h"
 #include "config.h"
 
 static const int dot11RSNAConfigPMKLifetime = 43200;
 
 struct wpa_pasn_auth_work {
-	u8 bssid[ETH_ALEN];
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
 	int akmp;
 	int cipher;
 	u16 group;
@@ -37,6 +39,15 @@
 };
 
 
+static int wpas_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+			       int noack, unsigned int freq, unsigned int wait)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
 static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
 {
 	wpabuf_free(awork->comeback);
@@ -52,6 +63,8 @@
 	wpa_printf(MSG_DEBUG, "PASN: Auth work timeout - stopping auth");
 
 	wpas_pasn_auth_stop(wpa_s);
+
+	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
 }
 
 
@@ -64,7 +77,8 @@
 }
 
 
-static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
+static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s,
+				  const u8 *peer_addr,
 				  int akmp, int cipher, u8 status,
 				  struct wpabuf *comeback,
 				  u16 comeback_after)
@@ -80,7 +94,7 @@
 
 			wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
 				" akmp=%s, status=%u comeback_after=%u comeback=%s",
-				MAC2STR(bssid),
+				MAC2STR(peer_addr),
 				wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
 				status, comeback_after, comeback_txt);
 
@@ -91,176 +105,15 @@
 
 	wpa_msg(wpa_s, MSG_INFO,
 		PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
-		MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+		MAC2STR(peer_addr), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
 		status);
 }
 
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpabuf *buf = NULL;
-	int ret;
-
-	ret = sae_set_group(&pasn->sae, pasn->group);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
-		return NULL;
-	}
-
-	ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
-				    wpa_s->own_addr, pasn->bssid,
-				    NULL, NULL);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
-		return NULL;
-	}
-
-	/* Need to add the entire Authentication frame body */
-	buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
-		return NULL;
-	}
-
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 1);
-	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
-	sae_write_commit(&pasn->sae, buf, NULL, 0);
-	pasn->sae.state = SAE_COMMITTED;
-
-	return buf;
-}
-
-
-static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	const u8 *data;
-	size_t buf_len;
-	u16 len, res, alg, seq, status;
-	int groups[] = { pasn->group, 0 };
-	int ret;
-
-	if (!wd)
-		return -1;
-
-	data = wpabuf_head_u8(wd);
-	buf_len = wpabuf_len(wd);
-
-	/* first handle the commit message */
-	if (buf_len < 2) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
-		return -1;
-	}
-
-	len = WPA_GET_LE16(data);
-	if (len < 6 || buf_len - 2 < len) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
-		return -1;
-	}
-
-	buf_len -= 2;
-	data += 2;
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 1 ||
-	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
-		return -1;
-	}
-
-	res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
-			       1);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
-		return -1;
-	}
-
-	/* Process the commit message and derive the PMK */
-	ret = sae_process_commit(&pasn->sae);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-		return -1;
-	}
-
-	buf_len -= len;
-	data += len;
-
-	/* Handle the confirm message */
-	if (buf_len < 2) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
-		return -1;
-	}
-
-	len = WPA_GET_LE16(data);
-	if (len < 6 || buf_len - 2 < len) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
-		return -1;
-	}
-
-	buf_len -= 2;
-	data += 2;
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
-		return -1;
-	}
-
-	res = sae_check_confirm(&pasn->sae, data + 6, len - 6);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
-	pasn->sae.state = SAE_ACCEPTED;
-
-	return 0;
-}
-
-
-static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpabuf *buf = NULL;
-
-	/* Need to add the entire authentication frame body */
-	buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
-		return NULL;
-	}
-
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 2);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	sae_write_confirm(&pasn->sae, buf);
-	pasn->sae.state = SAE_CONFIRMED;
-
-	return buf;
-}
-
-
-static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
-				  struct wpa_ssid *ssid, int group)
+static struct sae_pt *
+wpas_pasn_sae_derive_pt(struct wpa_ssid *ssid, int group)
 {
 	const char *password = ssid->sae_password;
 	int groups[2] = { group, 0 };
@@ -270,15 +123,26 @@
 
 	if (!password) {
 		wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+		return NULL;
+	}
+
+	return sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+			    (const u8 *) password, os_strlen(password),
+			    ssid->sae_password_id);
+}
+
+
+static int wpas_pasn_sae_setup_pt(struct wpa_ssid *ssid, int group)
+{
+	if (!ssid->sae_password && !ssid->passphrase) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
 		return -1;
 	}
 
 	if (ssid->pt)
 		return 0; /* PT already derived */
 
-	ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
-				 (const u8 *) password, os_strlen(password),
-				 ssid->sae_password_id);
+	ssid->pt = wpas_pasn_sae_derive_pt(ssid, group);
 
 	return ssid->pt ? 0 : -1;
 }
@@ -286,858 +150,329 @@
 #endif /* CONFIG_SAE */
 
 
-#ifdef CONFIG_FILS
-
-static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
+static int wpas_pasn_get_params_from_bss(struct wpa_supplicant *wpa_s,
+					 struct pasn_peer *peer)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpabuf *buf = NULL;
-	struct wpabuf *erp_msg;
 	int ret;
-
-	erp_msg = eapol_sm_build_erp_reauth_start(wpa_s->eapol);
-	if (!erp_msg) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
-		return NULL;
-	}
-
-	if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
-	    random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
-		goto fail;
-
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
-		    FILS_NONCE_LEN);
-
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
-		    FILS_SESSION_LEN);
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		goto fail;
-
-	/* Add the authentication algorithm */
-	wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
-	/* Authentication Transaction seq# */
-	wpabuf_put_le16(buf, 1);
-
-	/* Status Code */
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	/* Own RSNE */
-	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
-
-	/* FILS Nonce */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
-	wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN);
-
-	/* FILS Session */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
-	wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN);
-
-	/* Wrapped Data (ERP) */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
-	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
-	wpabuf_put_buf(buf, erp_msg);
-
-	/*
-	 * Calculate pending PMKID here so that we do not need to maintain a
-	 * copy of the EAP-Initiate/Reauth message.
-	 */
-	ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
-			     wpabuf_len(erp_msg),
-			     pasn->fils.erp_pmkid);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
-		goto fail;
-	}
-
-	wpabuf_free(erp_msg);
-	erp_msg = NULL;
-
-	wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
-	return buf;
-fail:
-	wpabuf_free(erp_msg);
-	wpabuf_free(buf);
-	return NULL;
-}
-
-
-static void wpas_pasn_initiate_eapol(struct wpa_supplicant *wpa_s)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct eapol_config eapol_conf;
-	struct wpa_ssid *ssid = pasn->ssid;
-
-	wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
-
-	eapol_sm_notify_eap_success(wpa_s->eapol, false);
-	eapol_sm_notify_eap_fail(wpa_s->eapol, false);
-	eapol_sm_notify_portControl(wpa_s->eapol, Auto);
-
-	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
-	eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
-	eapol_conf.workaround = ssid->eap_workaround;
-
-	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
-}
-
-
-static struct wpabuf * wpas_pasn_wd_fils_auth(struct wpa_supplicant *wpa_s)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
+	const u8 *rsne, *rsnxe;
 	struct wpa_bss *bss;
-	const u8 *indic;
-	u16 fils_info;
-
-	wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
-		   pasn->fils.completed);
-
-	/* Nothing to add as we are done */
-	if (pasn->fils.completed)
-		return NULL;
-
-	if (!pasn->ssid) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
-		return NULL;
-	}
-
-	bss = wpa_bss_get_bssid(wpa_s, pasn->bssid);
-	if (!bss) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: BSS not found");
-		return NULL;
-	}
-
-	indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
-	if (!indic || indic[1] < 2) {
-		wpa_printf(MSG_DEBUG, "PASN: Missing FILS Indication IE");
-		return NULL;
-	}
-
-	fils_info = WPA_GET_LE16(indic + 2);
-	if (!(fils_info & BIT(9))) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: FILS auth without PFS not supported");
-		return NULL;
-	}
-
-	wpas_pasn_initiate_eapol(wpa_s);
-
-	return wpas_pasn_fils_build_auth(wpa_s);
-}
-
-
-static int wpas_pasn_wd_fils_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct ieee802_11_elems elems;
 	struct wpa_ie_data rsne_data;
-	u8 rmsk[ERP_MAX_KEY_LEN];
-	size_t rmsk_len;
-	u8 anonce[FILS_NONCE_LEN];
-	const u8 *data;
-	size_t buf_len;
-	struct wpabuf *fils_wd = NULL;
-	u16 alg, seq, status;
-	int ret;
+	int sel, key_mgmt, pairwise_cipher;
+	int network_id = 0, group = 19;
+	struct wpa_ssid *ssid = NULL;
+	size_t ssid_str_len = 0;
+	const u8 *ssid_str = NULL;
+	const u8 *peer_addr = peer->peer_addr;
 
-	if (!wd)
-		return -1;
+	bss = wpa_bss_get_bssid(wpa_s, peer_addr);
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+		bss = wpa_bss_get_bssid(wpa_s, peer_addr);
+		if (!bss) {
+			wpa_printf(MSG_DEBUG, "PASN: BSS not found");
+			return -1;
+		}
+	}
 
-	data = wpabuf_head(wd);
-	buf_len = wpabuf_len(wd);
-
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
-		    data, buf_len);
-
-	/* first handle the header */
-	if (buf_len < 6) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne) {
+		wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
 		return -1;
 	}
 
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
-	    status != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: FILS: Dropping peer authentication");
-		return -1;
-	}
-
-	data += 6;
-	buf_len -= 6;
-
-	if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
-		return -1;
-	}
-
-	if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
-	    !elems.wrapped_data) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
-		return -1;
-	}
-
-	ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-			       &rsne_data);
+	ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data);
 	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data");
 		return -1;
 	}
 
-	ret = wpa_pasn_validate_rsne(&rsne_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+	rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+	ssid_str_len = bss->ssid_len;
+	ssid_str = bss->ssid;
+
+	/* Get the network configuration based on the obtained SSID */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!wpas_network_disabled(wpa_s, ssid) &&
+		    ssid_str_len == ssid->ssid_len &&
+		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0)
+			break;
+	}
+
+	if (ssid)
+		network_id = ssid->id;
+
+	sel = rsne_data.pairwise_cipher;
+	if (ssid && ssid->pairwise_cipher)
+		sel &= ssid->pairwise_cipher;
+
+	wpa_printf(MSG_DEBUG, "PASN: peer pairwise 0x%x, select 0x%x",
+		   rsne_data.pairwise_cipher, sel);
+
+	pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
+	if (pairwise_cipher < 0) {
+		wpa_msg(wpa_s, MSG_WARNING,
+			"PASN: Failed to select pairwise cipher");
 		return -1;
 	}
 
-	if (rsne_data.num_pmkid) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: FILS: Not expecting PMKID in RSNE");
+	sel = rsne_data.key_mgmt;
+	if (ssid && ssid->key_mgmt)
+		sel &= ssid->key_mgmt;
+
+	wpa_printf(MSG_DEBUG, "PASN: peer AKMP 0x%x, select 0x%x",
+		   rsne_data.key_mgmt, sel);
+#ifdef CONFIG_SAE
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) || !ssid)
+		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211R
+	if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME |
+				  WPA_DRIVER_FLAGS_UPDATE_FT_IES)))
+		sel &= ~WPA_KEY_MGMT_FT;
+#endif /* CONFIG_IEEE80211R */
+	if (0) {
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SHA384
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
+		key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/802.1X-SHA384");
+		if (ssid && !ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Disable PMKSA caching for FT/802.1X connection");
+			pmksa_cache_clear_current(wpa_s->wpa);
+		}
+#endif /* CONFIG_SHA384 */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+	} else if ((sel & WPA_KEY_MGMT_SAE_EXT_KEY) &&
+		   (ieee802_11_rsnx_capab(rsnxe,
+					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
+		   (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
+		key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE (ext key)");
+	} else if ((sel & WPA_KEY_MGMT_SAE) &&
+		   (ieee802_11_rsnx_capab(rsnxe,
+					   WLAN_RSNX_CAPAB_SAE_H2E)) &&
+		   (wpas_pasn_sae_setup_pt(ssid, group) == 0)) {
+		key_mgmt = WPA_KEY_MGMT_SAE;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT SAE");
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+		key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+		key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) &&
+		   os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
+		key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/802.1X");
+		if (ssid && !ssid->ft_eap_pmksa_caching &&
+		    pmksa_cache_get_current(wpa_s->wpa)) {
+			/* PMKSA caching with FT may have interoperability
+			 * issues, so disable that case by default for now.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Disable PMKSA caching for FT/802.1X connection");
+			pmksa_cache_clear_current(wpa_s->wpa);
+		}
+	} else if (sel & WPA_KEY_MGMT_FT_PSK) {
+		key_mgmt = WPA_KEY_MGMT_FT_PSK;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT FT/PSK");
+#endif /* CONFIG_IEEE80211R */
+	} else if (sel & WPA_KEY_MGMT_PASN) {
+		key_mgmt = WPA_KEY_MGMT_PASN;
+		wpa_printf(MSG_DEBUG, "PASN: using KEY_MGMT PASN");
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: invalid AKMP");
 		return -1;
 	}
 
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce,
-		    FILS_NONCE_LEN);
-	os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
-
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
-		    FILS_SESSION_LEN);
-
-	if (os_memcmp(pasn->fils.session, elems.fils_session,
-		      FILS_SESSION_LEN)) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
-		return -1;
-	}
-
-	fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
-				    WLAN_EID_EXT_WRAPPED_DATA);
-
-	if (!fils_wd) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: FILS: Failed getting wrapped data");
-		return -1;
-	}
-
-	eapol_sm_process_erp_finish(wpa_s->eapol, wpabuf_head(fils_wd),
-				    wpabuf_len(fils_wd));
-
-	wpabuf_free(fils_wd);
-	fils_wd = NULL;
-
-	if (eapol_sm_failed(wpa_s->eapol)) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
-		return -1;
-	}
-
-	rmsk_len = ERP_MAX_KEY_LEN;
-	ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
-
-	if (ret == PMK_LEN) {
-		rmsk_len = PMK_LEN;
-		ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
-	}
-
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
-		return -1;
-	}
-
-	ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
-			       pasn->fils.nonce, anonce, NULL, 0,
-			       pasn->pmk, &pasn->pmk_len);
-
-	forced_memzero(rmsk, sizeof(rmsk));
-
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
-		return -1;
-	}
-
-	wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
-		    PMKID_LEN);
-
-	wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
-
-	wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
-				 pasn->pmk_len, pasn->fils.erp_pmkid,
-				 pasn->bssid, pasn->akmp);
-
-	pasn->fils.completed = true;
+	peer->akmp = key_mgmt;
+	peer->cipher = pairwise_cipher;
+	peer->network_id = network_id;
+	peer->group = group;
 	return 0;
 }
 
-#endif /* CONFIG_FILS */
 
-
-static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
+static int wpas_pasn_set_keys_from_cache(struct wpa_supplicant *wpa_s,
+					 const u8 *own_addr,
+					 const u8 *peer_addr,
+					 int cipher, int akmp)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct ptksa_cache_entry *entry;
 
-	if (pasn->using_pmksa)
-		return NULL;
-
-	switch (pasn->akmp) {
-	case WPA_KEY_MGMT_PASN:
-		/* no wrapped data */
-		return NULL;
-	case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
-		if (pasn->trans_seq == 0)
-			return wpas_pasn_wd_sae_commit(wpa_s);
-		if (pasn->trans_seq == 2)
-			return wpas_pasn_wd_sae_confirm(wpa_s);
-#endif /* CONFIG_SAE */
-		wpa_printf(MSG_ERROR,
-			   "PASN: SAE: Cannot derive wrapped data");
-		return NULL;
-	case WPA_KEY_MGMT_FILS_SHA256:
-	case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
-		return wpas_pasn_wd_fils_auth(wpa_s);
-#endif /* CONFIG_FILS */
-	case WPA_KEY_MGMT_FT_PSK:
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-		/*
-		 * Wrapped data with these AKMs is optional and is only needed
-		 * for further validation of FT security parameters. For now do
-		 * not use them.
-		 */
-		return NULL;
-	default:
-		wpa_printf(MSG_ERROR,
-			   "PASN: TODO: Wrapped data for akmp=0x%x",
-			   pasn->akmp);
-		return NULL;
+	entry = ptksa_cache_get(wpa_s->ptksa, peer_addr, cipher);
+	if (!entry) {
+		wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+			   " not present in PTKSA cache", MAC2STR(peer_addr));
+		return -1;
 	}
+
+	if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: own addr " MACSTR " and PTKSA entry own addr "
+			   MACSTR " differ",
+			   MAC2STR(own_addr), MAC2STR(entry->own_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+		   MAC2STR(peer_addr));
+	wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, cipher,
+				       entry->ptk.tk_len,
+				       entry->ptk.tk,
+				       entry->ptk.ltf_keyseed_len,
+				       entry->ptk.ltf_keyseed, 0);
+	return 0;
 }
 
 
-static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
+static void wpas_pasn_configure_next_peer(struct wpa_supplicant *wpa_s,
+					  struct pasn_auth *pasn_params)
 {
-	if (pasn->using_pmksa)
-		return WPA_PASN_WRAPPED_DATA_NO;
+	struct pasn_peer *peer;
+	u8 comeback_len = 0;
+	const u8 *comeback = NULL;
 
-	/* Note: Valid AKMP is expected to already be validated */
-	switch (pasn->akmp) {
-	case WPA_KEY_MGMT_SAE:
-		return WPA_PASN_WRAPPED_DATA_SAE;
-	case WPA_KEY_MGMT_FILS_SHA256:
-	case WPA_KEY_MGMT_FILS_SHA384:
-		return WPA_PASN_WRAPPED_DATA_FILS_SK;
-	case WPA_KEY_MGMT_FT_PSK:
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-		/*
-		 * Wrapped data with these AKMs is optional and is only needed
-		 * for further validation of FT security parameters. For now do
-		 * not use them.
-		 */
-		return WPA_PASN_WRAPPED_DATA_NO;
-	case WPA_KEY_MGMT_PASN:
-	default:
-		return WPA_PASN_WRAPPED_DATA_NO;
-	}
-}
+	if (!pasn_params)
+		return;
 
+	while (wpa_s->pasn_count < pasn_params->num_peers) {
+		peer = &pasn_params->peer[wpa_s->pasn_count];
 
-static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
-					      const struct wpabuf *comeback)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
-	const u8 *pmkid;
-	u8 wrapped_data;
-	int ret;
-	u16 capab;
-
-	wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
-
-	if (pasn->trans_seq)
-		return NULL;
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		goto fail;
-
-	/* Get public key */
-	pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
-	pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
-	if (!pubkey) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
-		goto fail;
-	}
-
-	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
-
-	wpa_pasn_build_auth_header(buf, pasn->bssid,
-				   wpa_s->own_addr, pasn->bssid,
-				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
-	pmkid = NULL;
-	if (wpa_key_mgmt_ft(pasn->akmp)) {
-		ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
-						pasn->bssid,
-						pasn->pmk_r1,
-						&pasn->pmk_r1_len,
-						pasn->pmk_r1_name);
-		if (ret) {
+		if (os_memcmp(wpa_s->bssid, peer->peer_addr, ETH_ALEN) == 0) {
 			wpa_printf(MSG_DEBUG,
-				   "PASN: FT: Failed to derive keys");
-			goto fail;
+				   "PASN: Associated peer is not expected");
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
 		}
 
-		pmkid = pasn->pmk_r1_name;
-	} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
-		struct rsn_pmksa_cache_entry *pmksa;
+		if (wpas_pasn_set_keys_from_cache(wpa_s, peer->own_addr,
+						  peer->peer_addr,
+						  peer->cipher,
+						  peer->akmp) == 0) {
+			peer->status = PASN_STATUS_SUCCESS;
+			wpa_s->pasn_count++;
+			continue;
+		}
 
-		pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
-					       NULL, NULL, pasn->akmp);
-		if (pmksa)
-			pmkid = pmksa->pmkid;
+		if (wpas_pasn_get_params_from_bss(wpa_s, peer)) {
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
+		}
 
-		/*
-		 * Note: Even when PMKSA is available, also add wrapped data as
-		 * it is possible that the PMKID is no longer valid at the AP.
-		 */
-		wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
+		if (wpas_pasn_auth_start(wpa_s, peer->own_addr,
+					 peer->peer_addr, peer->akmp,
+					 peer->cipher, peer->group,
+					 peer->network_id,
+					 comeback, comeback_len)) {
+			peer->status = PASN_STATUS_FAILURE;
+			wpa_s->pasn_count++;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "PASN: Sent PASN auth start for " MACSTR,
+			   MAC2STR(peer->peer_addr));
+		return;
 	}
 
-	if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
-		goto fail;
-
-	if (!wrapped_data_buf)
-		wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
-	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
-				  pubkey, true, comeback, -1);
-
-	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
-		goto fail;
-
-	/* Add own RNSXE */
-	capab = 0;
-	capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
-		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
-		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
-	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
-	wpa_pasn_add_rsnxe(buf, capab);
-
-	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
-				   wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
-				   wpabuf_len(buf) - IEEE80211_HDRLEN,
-				   pasn->hash);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
-		goto fail;
+	if (wpa_s->pasn_count == pasn_params->num_peers) {
+		wpa_drv_send_pasn_resp(wpa_s, pasn_params);
+		wpa_printf(MSG_DEBUG, "PASN: Response sent");
+		os_free(wpa_s->pasn_params);
+		wpa_s->pasn_params = NULL;
 	}
-
-	pasn->trans_seq++;
-
-	wpabuf_free(wrapped_data_buf);
-	wpabuf_free(pubkey);
-
-	wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
-	return buf;
-fail:
-	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	wpabuf_free(wrapped_data_buf);
-	wpabuf_free(pubkey);
-	wpabuf_free(buf);
-	return NULL;
 }
 
 
-static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
+void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpabuf *buf, *wrapped_data_buf = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len, data_len;
-	const u8 *data;
-	u8 *ptr;
-	u8 wrapped_data;
-	int ret;
+	if (!wpa_s->pasn_params)
+		return;
 
-	wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+	wpa_s->pasn_params->peer[wpa_s->pasn_count].status = status;
+	wpa_s->pasn_count++;
+	wpas_pasn_configure_next_peer(wpa_s, wpa_s->pasn_params);
+}
 
-	if (pasn->trans_seq != 2)
-		return NULL;
 
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		goto fail;
+static void wpas_pasn_delete_peers(struct wpa_supplicant *wpa_s,
+				   struct pasn_auth *pasn_params)
+{
+	struct pasn_peer *peer;
+	unsigned int i;
 
-	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+	if (!pasn_params)
+		return;
 
-	wpa_pasn_build_auth_header(buf, pasn->bssid,
-				   wpa_s->own_addr, pasn->bssid,
-				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
-
-	wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
-
-	if (!wrapped_data_buf)
-		wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
-
-	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
-				  NULL, false, NULL, -1);
-
-	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
-		goto fail;
-	wpabuf_free(wrapped_data_buf);
-	wrapped_data_buf = NULL;
-
-	/* Add the MIC */
-	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
-	wpabuf_put_u8(buf, WLAN_EID_MIC);
-	wpabuf_put_u8(buf, mic_len);
-	ptr = wpabuf_put(buf, mic_len);
-
-	os_memset(ptr, 0, mic_len);
-
-	data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
-	data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
-	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
-		       wpa_s->own_addr, pasn->bssid,
-		       pasn->hash, mic_len * 2, data, data_len, mic);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
-		goto fail;
+	for (i = 0; i < pasn_params->num_peers; i++) {
+		peer = &pasn_params->peer[i];
+		ptksa_cache_flush(wpa_s->ptksa, peer->peer_addr,
+				  WPA_CIPHER_NONE);
 	}
+}
 
-#ifdef CONFIG_TESTING_OPTIONS
-	if (wpa_s->conf->pasn_corrupt_mic) {
-		wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
-		mic[0] = ~mic[0];
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
 
-	os_memcpy(ptr, mic, mic_len);
+static void wpas_pasn_initiate_eapol(struct pasn_data *pasn,
+				     struct wpa_ssid *ssid)
+{
+	struct eapol_config eapol_conf;
 
-	pasn->trans_seq++;
+	wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
 
-	wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
-	return buf;
-fail:
-	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	wpabuf_free(wrapped_data_buf);
-	wpabuf_free(buf);
-	return NULL;
+	eapol_sm_notify_eap_success(pasn->eapol, false);
+	eapol_sm_notify_eap_fail(pasn->eapol, false);
+	eapol_sm_notify_portControl(pasn->eapol, Auto);
+
+	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+	eapol_conf.fast_reauth = pasn->fast_reauth;
+	eapol_conf.workaround = ssid->eap_workaround;
+
+	eapol_sm_notify_config(pasn->eapol, &ssid->eap, &eapol_conf);
 }
 
 
 static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-
-	wpa_printf(MSG_DEBUG, "PASN: Reset");
-
-	crypto_ecdh_deinit(pasn->ecdh);
-	pasn->ecdh = NULL;
+	struct pasn_data *pasn = &wpa_s->pasn;
 
 	wpas_pasn_cancel_auth_work(wpa_s);
 	wpa_s->pasn_auth_work = NULL;
-
 	eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL);
 
-	pasn->akmp = 0;
-	pasn->cipher = 0;
-	pasn->group = 0;
-	pasn->trans_seq = 0;
-	pasn->pmk_len = 0;
-	pasn->using_pmksa = false;
-
-	forced_memzero(pasn->pmk, sizeof(pasn->pmk));
-	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
-	forced_memzero(&pasn->hash, sizeof(pasn->hash));
-
-	wpabuf_free(pasn->beacon_rsne_rsnxe);
-	pasn->beacon_rsne_rsnxe = NULL;
-
-	wpabuf_free(pasn->comeback);
-	pasn->comeback = NULL;
-	pasn->comeback_after = 0;
-
-#ifdef CONFIG_SAE
-	sae_clear_data(&pasn->sae);
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
-	os_memset(&pasn->fils, 0, sizeof(pasn->fils));
-#endif /* CONFIG_FILS*/
-
-#ifdef CONFIG_IEEE80211R
-	forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
-	pasn->pmk_r1_len = 0;
-	os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
-#endif /* CONFIG_IEEE80211R */
-	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-}
-
-
-static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
-			     struct wpa_ie_data *rsn_data,
-			     struct wpa_pasn_params_data *pasn_data,
-			     struct wpabuf *wrapped_data)
-{
-	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-
-	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
-	pasn->pmk_len = 0;
-
-	if (pasn->akmp == WPA_KEY_MGMT_PASN) {
-		wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
-
-		pasn->pmk_len = WPA_PASN_PMK_LEN;
-		os_memcpy(pasn->pmk, pasn_default_pmk,
-			  sizeof(pasn_default_pmk));
-		return 0;
-	}
-
-	if (wpa_key_mgmt_ft(pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R
-		wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
-		pasn->pmk_len = pasn->pmk_r1_len;
-		os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
-		pasn->using_pmksa = true;
-		return 0;
-#else /* CONFIG_IEEE80211R */
-		wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
-		return -1;
-#endif /* CONFIG_IEEE80211R */
-	}
-
-	if (rsn_data->num_pmkid) {
-		struct rsn_pmksa_cache_entry *pmksa;
-
-		pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
-					       rsn_data->pmkid, NULL,
-					       pasn->akmp);
-		if (pmksa) {
-			wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
-
-			pasn->pmk_len = pmksa->pmk_len;
-			os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
-			pasn->using_pmksa = true;
-
-			return 0;
-		}
-	}
-
-#ifdef CONFIG_SAE
-	if (pasn->akmp == WPA_KEY_MGMT_SAE) {
-		int ret;
-
-		ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
-		if (ret) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Failed processing SAE wrapped data");
-			pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			return -1;
-		}
-
-		wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
-		pasn->pmk_len = PMK_LEN;
-		os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
-
-		wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
-					 pasn->pmk_len, pasn->sae.pmkid,
-					 pasn->bssid, pasn->akmp);
-		return 0;
-	}
-#endif /* CONFIG_SAE */
-
-#ifdef CONFIG_FILS
-	if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-	    pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-		int ret;
-
-		ret = wpas_pasn_wd_fils_rx(wpa_s, wrapped_data);
-		if (ret) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Failed processing FILS wrapped data");
-			pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			return -1;
-		}
-
-		return 0;
-	}
-#endif	/* CONFIG_FILS */
-
-	/* TODO: Derive PMK based on wrapped data */
-	wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
-	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	return -1;
-}
-
-
-static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
-			   int akmp, int cipher, u16 group, int freq,
-			   const u8 *beacon_rsne, u8 beacon_rsne_len,
-			   const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
-			   int network_id, struct wpabuf *comeback)
-{
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct wpa_ssid *ssid = NULL;
-	struct wpabuf *frame;
-	int ret;
-	bool derive_kdk;
-
-	/* TODO: Currently support only ECC groups */
-	if (!dragonfly_suitable_group(group, 1)) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Reject unsuitable group %u", group);
-		return -1;
-	}
-
-	ssid = wpa_config_get_network(wpa_s->conf, network_id);
-
-	switch (akmp) {
-	case WPA_KEY_MGMT_PASN:
-		break;
-#ifdef CONFIG_SAE
-	case WPA_KEY_MGMT_SAE:
-		if (!ssid) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: No network profile found for SAE");
-			return -1;
-		}
-
-		if (!ieee802_11_rsnx_capab(beacon_rsnxe,
-					   WLAN_RSNX_CAPAB_SAE_H2E)) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: AP does not support SAE H2E");
-			return -1;
-		}
-
-		if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Failed to derive PT");
-			return -1;
-		}
-
-		pasn->sae.state = SAE_NOTHING;
-		pasn->sae.send_confirm = 0;
-		pasn->ssid = ssid;
-		break;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-	case WPA_KEY_MGMT_FILS_SHA256:
-	case WPA_KEY_MGMT_FILS_SHA384:
-		pasn->ssid = ssid;
-		break;
-#endif /* CONFIG_FILS */
-#ifdef CONFIG_IEEE80211R
-	case WPA_KEY_MGMT_FT_PSK:
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-		break;
-#endif /* CONFIG_IEEE80211R */
-	default:
-		wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
-		return -1;
-	}
-
-	pasn->ecdh = crypto_ecdh_init(group);
-	if (!pasn->ecdh) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
-		goto fail;
-	}
-
-	pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
-					       beacon_rsnxe_len);
-	if (!pasn->beacon_rsne_rsnxe) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE/RSNXE");
-		goto fail;
-	}
-
-	wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, beacon_rsne_len);
-	if (beacon_rsnxe && beacon_rsnxe_len)
-		wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
-				beacon_rsnxe_len);
-
-	pasn->akmp = akmp;
-	pasn->cipher = cipher;
-	pasn->group = group;
-	pasn->freq = freq;
-
-	derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
-		ieee802_11_rsnx_capab(beacon_rsnxe,
-				      WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
-	if (!derive_kdk)
-		derive_kdk = wpa_s->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
-	if (derive_kdk)
-		pasn->kdk_len = WPA_KDK_MAX_LEN;
-	else
-		pasn->kdk_len = 0;
-	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
-
-	os_memcpy(pasn->bssid, bssid, ETH_ALEN);
-
-	wpa_printf(MSG_DEBUG,
-		   "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
-		   MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
-		   pasn->group);
-
-	frame = wpas_pasn_build_auth_1(wpa_s, comeback);
-	if (!frame) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
-		goto fail;
-	}
-
-	ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
-				pasn->freq, 1000);
-
-	wpabuf_free(frame);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
-		goto fail;
-	}
-
-	eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
-	return 0;
-
-fail:
-	return -1;
+	wpa_pasn_reset(pasn);
 }
 
 
 static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
-					  const u8 *bssid, int akmp, int cipher)
+					  const u8 *peer_addr, int akmp,
+					  int cipher)
 {
 	struct wpa_bss *bss;
 	const u8 *rsne;
 	struct wpa_ie_data rsne_data;
 	int ret;
 
-	if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+	if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Not doing authentication with current BSS");
 		return NULL;
 	}
 
-	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "PASN: BSS not found");
 		return NULL;
@@ -1170,8 +505,14 @@
 {
 	struct wpa_supplicant *wpa_s = work->wpa_s;
 	struct wpa_pasn_auth_work *awork = work->ctx;
+	struct pasn_data *pasn = &wpa_s->pasn;
+	struct wpa_ssid *ssid;
 	struct wpa_bss *bss;
 	const u8 *rsne, *rsnxe;
+	const u8 *indic;
+	u16 fils_info;
+	u16 capab = 0;
+	bool derive_kdk;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
@@ -1192,7 +533,7 @@
 	 * authentication is not allowed, e.g., a connection with the AP was
 	 * established.
 	 */
-	bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
+	bss = wpas_pasn_allowed(wpa_s, awork->peer_addr, awork->akmp,
 				awork->cipher);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed");
@@ -1207,15 +548,115 @@
 
 	rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
 
-	ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
+	derive_kdk = (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+		ieee802_11_rsnx_capab(rsnxe,
+				      WLAN_RSNX_CAPAB_SECURE_LTF);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (!derive_kdk)
+		derive_kdk = wpa_s->conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (derive_kdk)
+		pasn->kdk_len = WPA_KDK_MAX_LEN;
+	else
+		pasn->kdk_len = 0;
+	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
+	    ieee802_11_rsnx_capab(rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+		pasn->secure_ltf = true;
+	else
+		pasn->secure_ltf = false;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	pasn->corrupt_mic = wpa_s->conf->pasn_corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+	pasn->rsnxe_capab = capab;
+	pasn->send_mgmt = wpas_pasn_send_mlme;
+
+	ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
+
+#ifdef CONFIG_SAE
+	if (awork->akmp == WPA_KEY_MGMT_SAE) {
+		if (!ssid) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: No network profile found for SAE");
+			goto fail;
+		}
+		pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+		if (!pasn->pt) {
+			wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
+			goto fail;
+		}
+		pasn->network_id = ssid->id;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	/* Prepare needed information for wpas_pasn_wd_fils_auth(). */
+	if (awork->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+	    awork->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+		indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+		if (!ssid) {
+			wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
+		} else if (!indic || indic[1] < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Missing FILS Indication IE");
+		} else {
+			fils_info = WPA_GET_LE16(indic + 2);
+			if ((fils_info & BIT(9)) && ssid) {
+				pasn->eapol = wpa_s->eapol;
+				pasn->network_id = ssid->id;
+				wpas_pasn_initiate_eapol(pasn, ssid);
+				pasn->fils_eapol = true;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "PASN: FILS auth without PFS not supported");
+			}
+		}
+		pasn->fast_reauth = wpa_s->conf->fast_reauth;
+	}
+#endif /* CONFIG_FILS */
+
+	pasn->cb_ctx = wpa_s;
+	pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+
+	if (wpa_key_mgmt_ft(awork->akmp)) {
+#ifdef CONFIG_IEEE80211R
+		ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, awork->akmp,
+						awork->peer_addr,
+						pasn->pmk_r1,
+						&pasn->pmk_r1_len,
+						pasn->pmk_r1_name);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FT: Failed to derive keys");
+			goto fail;
+		}
+#else /* CONFIG_IEEE80211R */
+		goto fail;
+#endif /* CONFIG_IEEE80211R */
+	}
+
+
+	ret = wpas_pasn_start(pasn, awork->own_addr, awork->peer_addr,
+			      awork->peer_addr, awork->akmp, awork->cipher,
 			      awork->group, bss->freq, rsne, *(rsne + 1) + 2,
 			      rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
-			      awork->network_id, awork->comeback);
+			      awork->comeback);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Failed to start PASN authentication");
 		goto fail;
 	}
+	eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
 
 	/* comeback token is no longer needed at this stage */
 	wpabuf_free(awork->comeback);
@@ -1230,7 +671,8 @@
 }
 
 
-int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
+			 const u8 *own_addr, const u8 *peer_addr,
 			 int akmp, int cipher, u16 group, int network_id,
 			 const u8 *comeback, size_t comeback_len)
 {
@@ -1238,7 +680,7 @@
 	struct wpa_bss *bss;
 
 	wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x",
-		   MAC2STR(bssid), akmp, cipher);
+		   MAC2STR(peer_addr), akmp, cipher);
 
 	/*
 	 * TODO: Consider modifying the offchannel logic to handle additional
@@ -1262,7 +704,7 @@
 		return -1;
 	}
 
-	bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
+	bss = wpas_pasn_allowed(wpa_s, peer_addr, akmp, cipher);
 	if (!bss)
 		return -1;
 
@@ -1272,7 +714,8 @@
 	if (!awork)
 		return -1;
 
-	os_memcpy(awork->bssid, bssid, ETH_ALEN);
+	os_memcpy(awork->own_addr, own_addr, ETH_ALEN);
+	os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
 	awork->akmp = akmp;
 	awork->cipher = cipher;
 	awork->group = group;
@@ -1299,14 +742,14 @@
 
 void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct pasn_data *pasn = &wpa_s->pasn;
 
 	if (!wpa_s->pasn.ecdh)
 		return;
 
 	wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
 
-	wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
+	wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
 			      pasn->status, pasn->comeback,
 			      pasn->comeback_after);
 
@@ -1315,282 +758,127 @@
 
 
 static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
-				     struct wpas_pasn *pasn,
+				     struct pasn_data *pasn,
 				     struct wpa_pasn_params_data *params)
 {
 	int akmp = pasn->akmp;
 	int cipher = pasn->cipher;
 	u16 group = pasn->group;
-	u8 bssid[ETH_ALEN];
-	int network_id = pasn->ssid ? pasn->ssid->id : 0;
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
 
 	wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
-	os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+	os_memcpy(own_addr, pasn->own_addr, ETH_ALEN);
+	os_memcpy(peer_addr, pasn->peer_addr, ETH_ALEN);
 	wpas_pasn_reset(wpa_s);
 
-	return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
-				    network_id,
+	return wpas_pasn_auth_start(wpa_s, own_addr, peer_addr, akmp, cipher,
+				    group, pasn->network_id,
 				    params->comeback, params->comeback_len);
 }
 
 
+static void wpas_pasn_deauth_cb(struct ptksa_cache_entry *entry)
+{
+	struct wpa_supplicant *wpa_s = entry->ctx;
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+
+	/* Use a copy of the addresses from the entry to avoid issues with the
+	 * entry getting freed during deauthentication processing. */
+	os_memcpy(own_addr, entry->own_addr, ETH_ALEN);
+	os_memcpy(peer_addr, entry->addr, ETH_ALEN);
+	wpas_pasn_deauthenticate(wpa_s, own_addr, peer_addr);
+}
+
+
 int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 		      const struct ieee80211_mgmt *mgmt, size_t len)
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	struct ieee802_11_elems elems;
-	struct wpa_ie_data rsn_data;
-	struct wpa_pasn_params_data pasn_params;
-	struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len;
-	u16 status;
-	int ret, inc_y;
-	u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
-			      (WLAN_FC_STYPE_AUTH << 4));
+	struct pasn_data *pasn = &wpa_s->pasn;
+	struct wpa_pasn_params_data pasn_data;
+	int ret;
 
-	if (!wpa_s->pasn_auth_work || !mgmt ||
-	    len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+	if (!wpa_s->pasn_auth_work)
 		return -2;
 
-	/* Not an Authentication frame; do nothing */
-	if ((mgmt->frame_control & fc) != fc)
-		return -2;
+	pasn->cb_ctx = wpa_s;
+	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
+	if (ret == 0) {
+		ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
+				pasn->cipher, dot11RSNAConfigPMKLifetime,
+				&pasn->ptk,
+				wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
+				wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
 
-	/* Not our frame; do nothing */
-	if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) != 0 ||
-	    os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
-	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
-		return -2;
-
-	/* Not PASN; do nothing */
-	if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
-		return -2;
-
-	if (mgmt->u.auth.auth_transaction !=
-	    host_to_le16(pasn->trans_seq + 1)) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: RX: Invalid transaction sequence: (%u != %u)",
-			   le_to_host16(mgmt->u.auth.auth_transaction),
-			   pasn->trans_seq + 1);
-		return -1;
+		if (pasn->pmksa_entry)
+			wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
 	}
 
-	status = le_to_host16(mgmt->u.auth.status_code);
-
-	if (status != WLAN_STATUS_SUCCESS &&
-	    status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Authentication rejected - status=%u", status);
-		pasn->status = status;
-		wpas_pasn_auth_stop(wpa_s);
-		return -1;
-	}
-
-	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
-				   len - offsetof(struct ieee80211_mgmt,
-						  u.auth.variable),
-				   &elems, 0) == ParseFailed) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed parsing Authentication frame");
-		goto fail;
-	}
-
-	/* Check that the MIC IE exists. Save it and zero out the memory */
-	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
-	if (status == WLAN_STATUS_SUCCESS) {
-		if (!elems.mic || elems.mic_len != mic_len) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Invalid MIC. Expecting len=%u",
-				   mic_len);
-			goto fail;
-		} else {
-			os_memcpy(mic, elems.mic, mic_len);
-			/* TODO: Clean this up.. Should not be modifying the
-			 * received message buffer. */
-			os_memset((u8 *) elems.mic, 0, mic_len);
-		}
-	}
-
-	if (!elems.pasn_params || !elems.pasn_params_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Missing PASN Parameters IE");
-		goto fail;
-	}
-
-	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
-					  elems.pasn_params_len + 3,
-					  true, &pasn_params);
-	if (ret) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed validation PASN of Parameters IE");
-		goto fail;
-	}
-
-	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Authentication temporarily rejected");
-
-		if (pasn_params.comeback && pasn_params.comeback_len) {
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Comeback token available. After=%u",
-				   pasn_params.after);
-
-			if (!pasn_params.after)
-				return wpas_pasn_immediate_retry(wpa_s, pasn,
-								 &pasn_params);
-
-			pasn->comeback = wpabuf_alloc_copy(
-				pasn_params.comeback, pasn_params.comeback_len);
-			if (pasn->comeback)
-				pasn->comeback_after = pasn_params.after;
-		}
-
-		pasn->status = status;
-		goto fail;
-	}
-
-	ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-			       &rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
-		goto fail;
-	}
-
-	ret = wpa_pasn_validate_rsne(&rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
-		goto fail;
-	}
-
-	if (pasn->akmp != rsn_data.key_mgmt ||
-	    pasn->cipher != rsn_data.pairwise_cipher) {
-		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
-		goto fail;
-	}
-
-	if (pasn->group != pasn_params.group) {
-		wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
-		goto fail;
-	}
-
-	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
-		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
-		goto fail;
-	}
-
-	if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
-		inc_y = 1;
-	} else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
-		   pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
-		inc_y = 0;
-	} else {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Invalid first octet in pubkey=0x%x",
-			   pasn_params.pubkey[0]);
-		goto fail;
-	}
-
-	secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
-					 pasn_params.pubkey + 1,
-					 pasn_params.pubkey_len - 1);
-
-	if (!secret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
-		goto fail;
-	}
-
-	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
-		wrapped_data = ieee802_11_defrag(&elems,
-						 WLAN_EID_EXTENSION,
-						 WLAN_EID_EXT_WRAPPED_DATA);
-
-		if (!wrapped_data) {
-			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
-			goto fail;
-		}
-	}
-
-	ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
-		goto fail;
-	}
-
-	ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
-			      wpa_s->own_addr, pasn->bssid,
-			      wpabuf_head(secret), wpabuf_len(secret),
-			      &pasn->ptk, pasn->akmp, pasn->cipher,
-			      pasn->kdk_len);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
-		goto fail;
-	}
-
-	wpabuf_free(wrapped_data);
-	wrapped_data = NULL;
-	wpabuf_free(secret);
-	secret = NULL;
-
-	/* Verify the MIC */
-	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
-		       pasn->bssid, wpa_s->own_addr,
-		       wpabuf_head(pasn->beacon_rsne_rsnxe),
-		       wpabuf_len(pasn->beacon_rsne_rsnxe),
-		       (u8 *) &mgmt->u.auth,
-		       len - offsetof(struct ieee80211_mgmt, u.auth),
-		       out_mic);
-
-	wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
-	if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
-		goto fail;
-	}
-
-	pasn->trans_seq++;
-
-	wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
-
-	frame = wpas_pasn_build_auth_3(wpa_s);
-	if (!frame) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
-		goto fail;
-	}
-
-	ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
-				pasn->freq, 100);
-	wpabuf_free(frame);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
-		goto fail;
-	}
-
-	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
-
-	ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher,
-			dot11RSNAConfigPMKLifetime, &pasn->ptk);
-
 	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
 
-	pasn->status = WLAN_STATUS_SUCCESS;
-	return 0;
-fail:
-	wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
-	wpabuf_free(wrapped_data);
-	wpabuf_free(secret);
+	if (ret == -1) {
+		wpas_pasn_auth_stop(wpa_s);
+		wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_FAILURE);
+	}
 
-	/*
-	 * TODO: In case of an error the standard allows to silently drop
-	 * the frame and terminate the authentication exchange. However, better
-	 * reply to the AP with an error status.
-	 */
-	if (status == WLAN_STATUS_SUCCESS)
-		pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	else
-		pasn->status = status;
+	if (ret == 1)
+		ret = wpas_pasn_immediate_retry(wpa_s, pasn, &pasn_data);
 
-	wpas_pasn_auth_stop(wpa_s);
-	return -1;
+	return ret;
+}
+
+
+void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s,
+			    struct pasn_auth *pasn_auth)
+{
+	struct pasn_peer *src, *dst;
+	unsigned int i, num_peers = pasn_auth->num_peers;
+
+	if (wpa_s->pasn_params) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth_trigger: Already in progress");
+		return;
+	}
+
+	if (!num_peers || num_peers > WPAS_MAX_PASN_PEERS) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth trigger: Invalid number of peers");
+		return;
+	}
+
+	wpa_s->pasn_params = os_zalloc(sizeof(struct pasn_auth));
+	if (!wpa_s->pasn_params) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth trigger: Failed to allocate a buffer");
+		return;
+	}
+
+	wpa_s->pasn_count = 0;
+	wpa_s->pasn_params->num_peers = num_peers;
+
+	for (i = 0; i < num_peers; i++) {
+		dst = &wpa_s->pasn_params->peer[i];
+		src = &pasn_auth->peer[i];
+		os_memcpy(dst->own_addr, wpa_s->own_addr, ETH_ALEN);
+		os_memcpy(dst->peer_addr, src->peer_addr, ETH_ALEN);
+		dst->ltf_keyseed_required = src->ltf_keyseed_required;
+		dst->status = PASN_STATUS_SUCCESS;
+
+		if (!is_zero_ether_addr(src->own_addr)) {
+			os_memcpy(dst->own_addr, src->own_addr, ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "PASN: Own (source) MAC addr: "
+				   MACSTR, MAC2STR(dst->own_addr));
+		}
+	}
+
+	if (pasn_auth->action == PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT) {
+		wpas_pasn_delete_peers(wpa_s, wpa_s->pasn_params);
+		os_free(wpa_s->pasn_params);
+		wpa_s->pasn_params = NULL;
+	} else if (pasn_auth->action == PASN_ACTION_AUTH) {
+		wpas_pasn_configure_next_peer(wpa_s, wpa_s->pasn_params);
+	}
 }
 
 
@@ -1598,12 +886,8 @@
 			     const u8 *data, size_t data_len, u8 acked)
 
 {
-	struct wpas_pasn *pasn = &wpa_s->pasn;
-	const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data;
-	u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
-			      (WLAN_FC_STYPE_AUTH << 4));
-
-	wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+	struct pasn_data *pasn = &wpa_s->pasn;
+	int ret;
 
 	if (!wpa_s->pasn_auth_work) {
 		wpa_printf(MSG_DEBUG,
@@ -1611,73 +895,46 @@
 		return -1;
 	}
 
-	if (!mgmt ||
-	    data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
-		return -1;
+	ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+	if (ret != 1)
+		return ret;
 
-	/* Not an authentication frame; do nothing */
-	if ((mgmt->frame_control & fc) != fc)
-		return -1;
-
-	/* Not our frame; do nothing */
-	if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
-	    os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) ||
-	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
-		return -1;
-
-	/* Not PASN; do nothing */
-	if (mgmt->u.auth.auth_alg !=  host_to_le16(WLAN_AUTH_PASN))
-		return -1;
-
-	if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
-		wpa_printf(MSG_ERROR,
-			   "PASN: Invalid transaction sequence: (%u != %u)",
-			   pasn->trans_seq,
-			   le_to_host16(mgmt->u.auth.auth_transaction));
+	if (!wpa_s->pasn_params) {
+		wpas_pasn_auth_stop(wpa_s);
 		return 0;
 	}
 
-	wpa_printf(MSG_ERROR,
-		   "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
-		   acked);
-
-	/*
-	 * Even if the frame was not acked, do not treat this is an error, and
-	 * try to complete the flow, relying on the PASN timeout callback to
-	 * clean up.
-	 */
-	if (pasn->trans_seq == 3) {
-		wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
-			   MAC2STR(pasn->bssid));
-		/*
-		 * Either frame was not ACKed or it was ACKed but the trans_seq
-		 * != 1, i.e., not expecting an RX frame, so we are done.
-		 */
-		wpas_pasn_auth_stop(wpa_s);
-	}
+	wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
+				      pasn->cipher, pasn->akmp);
+	wpas_pasn_auth_stop(wpa_s);
+	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
 
 	return 0;
 }
 
 
-int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
+			     const u8 *peer_addr)
 {
 	struct wpa_bss *bss;
 	struct wpabuf *buf;
 	struct ieee80211_mgmt *deauth;
 	int ret;
 
-	if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+	if (os_memcmp(wpa_s->bssid, peer_addr, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Cannot deauthenticate from current BSS");
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
-		   MACSTR, MAC2STR(bssid));
-	ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
+	wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, 0, 0, NULL,
+				       0, NULL, 1);
 
-	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
+		   MACSTR, MAC2STR(peer_addr));
+	ptksa_cache_flush(wpa_s->ptksa, peer_addr, WPA_CIPHER_NONE);
+
+	bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "PASN: deauth: BSS not found");
 		return -1;
@@ -1695,9 +952,9 @@
 	deauth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
 					     (WLAN_FC_STYPE_DEAUTH << 4));
 
-	os_memcpy(deauth->da, bssid, ETH_ALEN);
-	os_memcpy(deauth->sa, wpa_s->own_addr, ETH_ALEN);
-	os_memcpy(deauth->bssid, bssid, ETH_ALEN);
+	os_memcpy(deauth->da, peer_addr, ETH_ALEN);
+	os_memcpy(deauth->sa, own_addr, ETH_ALEN);
+	os_memcpy(deauth->bssid, peer_addr, ETH_ALEN);
 	deauth->u.deauth.reason_code =
 		host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
 
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 3ae99da..3aa6675 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -131,7 +131,7 @@
 }
 
 
-static int wpa_supplicant_set_key(void *wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *wpa_s, int link_id, enum wpa_alg alg,
 				  const u8 *addr, int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
 				  const u8 *key, size_t key_len,
@@ -318,7 +318,7 @@
 	}
 
 	os_memset(&wpa_s, 0, sizeof(wpa_s));
-	wpa_s.conf = wpa_config_read(argv[1], NULL);
+	wpa_s.conf = wpa_config_read(argv[1], NULL, false);
 	if (wpa_s.conf == NULL) {
 		printf("Failed to parse configuration file '%s'.\n", argv[1]);
 		return -1;
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
index 6110797..ff648b2 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -599,8 +599,9 @@
 					   size_t len)
 {
 	u8 dialog_token;
-	unsigned int i, count;
+	unsigned int i, count, num_active_scs, j = 0;
 	struct active_scs_elem *scs_desc, *prev;
+	int *scs_resp[2];
 
 	if (len < 2)
 		return;
@@ -632,6 +633,26 @@
 		return;
 	}
 
+	num_active_scs = dl_list_len(&wpa_s->active_scs_ids);
+	if (num_active_scs < count) {
+		wpa_printf(MSG_ERROR, "Unexpected number of SCS responses."
+			   " Expected < %d, received %d", num_active_scs, count);
+		return;
+	}
+
+	scs_resp[0] = (int *) os_zalloc(num_active_scs);
+	if (!scs_resp[0]) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for scs_resp");
+		return;
+	}
+
+	scs_resp[1] = (int *) os_zalloc(num_active_scs);
+	if (!scs_resp[1]) {
+		os_free(scs_resp[0]);
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for scs_resp");
+		return;
+	}
+
 	for (i = 0; i < count; i++) {
 		u8 id;
 		u16 status;
@@ -664,6 +685,8 @@
 
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
 			" SCSID=%u status_code=%u", MAC2STR(src), id, status);
+		scs_resp[0][j] = id;
+		scs_resp[1][j++] = status;
 	}
 
 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
@@ -676,10 +699,17 @@
 				WPA_EVENT_SCS_RESULT "bssid=" MACSTR
 				" SCSID=%u status_code=response_not_received",
 				MAC2STR(src), scs_desc->scs_id);
+			if (j < num_active_scs) {
+				scs_resp[0][j] = scs_desc->scs_id;
+				scs_resp[1][j++] = -1; /* TIMEOUT indicator for AIDL */
+			}
 			dl_list_del(&scs_desc->list);
 			os_free(scs_desc);
 		}
 	}
+	wpas_notify_qos_policy_scs_response(wpa_s, j, scs_resp);
+	os_free(scs_resp[0]);
+	os_free(scs_resp[1]);
 }
 
 
@@ -706,14 +736,15 @@
 
 
 static int write_ipv4_info(char *pos, int total_len,
-			   const struct ipv4_params *v4)
+			   const struct ipv4_params *v4,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v4->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 source address");
@@ -728,7 +759,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 destination address");
@@ -743,7 +774,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -752,7 +783,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -761,7 +792,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -775,14 +806,15 @@
 
 
 static int write_ipv6_info(char *pos, int total_len,
-			   const struct ipv6_params *v6)
+			   const struct ipv6_params *v6,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET6_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v6->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 source addr");
@@ -797,7 +829,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 destination addr");
@@ -812,7 +844,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -821,7 +853,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -830,7 +862,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d",
 				  v6->next_header);
 		if (os_snprintf_error(rem_len, res))
@@ -861,7 +893,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v4.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v4.src_ip,
 			  &frame_classifier[3], 4);
 	}
@@ -874,14 +906,14 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v4.dst_ip,
 			  &frame_classifier[7], 4);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v4.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v4.src_port =
 			WPA_GET_BE16(&frame_classifier[11]);
 	}
@@ -894,7 +926,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v4.dst_port =
 			WPA_GET_BE16(&frame_classifier[13]);
 	}
@@ -903,7 +935,7 @@
 
 	/* Classifier Mask - bit 6 = Protocol */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v4.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v4.protocol = frame_classifier[16];
 	}
 
@@ -928,7 +960,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v6.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v6.src_ip,
 			  &frame_classifier[3], 16);
 	}
@@ -940,14 +972,14 @@
 				   "QM: IPv6: Both domain name and destination IP address not expected");
 			return -1;
 		}
-		type4_param->ip_params.v6.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v6.dst_ip,
 			  &frame_classifier[19], 16);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v6.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v6.src_port =
 				WPA_GET_BE16(&frame_classifier[35]);
 	}
@@ -960,7 +992,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v6.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v6.dst_port =
 				WPA_GET_BE16(&frame_classifier[37]);
 	}
@@ -969,7 +1001,7 @@
 
 	/* Classifier Mask - bit 6 = Next Header */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v6.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v6.next_header = frame_classifier[40];
 	}
 
@@ -1075,9 +1107,11 @@
 		}
 
 		if (type4->ip_version == IPV4)
-			res = write_ipv4_info(pos, len, &type4->ip_params.v4);
+			res = write_ipv4_info(pos, len, &type4->ip_params.v4,
+					      type4->classifier_mask);
 		else
-			res = write_ipv6_info(pos, len, &type4->ip_params.v6);
+			res = write_ipv6_info(pos, len, &type4->ip_params.v6,
+					      type4->classifier_mask);
 
 		if (res <= 0) {
 			wpa_printf(MSG_ERROR,
@@ -1294,11 +1328,17 @@
 		attr = qos_ie + 6;
 		rem_attrs_len = qos_ie[1] - 4;
 
-		while (rem_attrs_len > 2 && rem_attrs_len >= 2 + attr[1]) {
-			wpas_fill_dscp_policy(&policy, attr[0], attr[1],
-					      &attr[2]);
-			rem_attrs_len -= 2 + attr[1];
-			attr += 2 + attr[1];
+		while (rem_attrs_len > 2) {
+			u8 attr_id, attr_len;
+
+			attr_id = *attr++;
+			attr_len = *attr++;
+			rem_attrs_len -= 2;
+			if (attr_len > rem_attrs_len)
+				break;
+			wpas_fill_dscp_policy(&policy, attr_id, attr_len, attr);
+			rem_attrs_len -= attr_len;
+			attr += attr_len;
 		}
 
 		rem_len -= ie_len;
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 4457b6c..238fe68 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -731,24 +731,24 @@
 		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
 
 		switch (vht_oper->vht_op_info_chwidth) {
-		case 1:
+		case CHANWIDTH_80MHZ:
 			seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx;
 			seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx;
 			if (seg1 && abs(seg1 - seg0) == 8)
-				vht = CHANWIDTH_160MHZ;
+				vht = CONF_OPER_CHWIDTH_160MHZ;
 			else if (seg1)
-				vht = CHANWIDTH_80P80MHZ;
+				vht = CONF_OPER_CHWIDTH_80P80MHZ;
 			else
-				vht = CHANWIDTH_80MHZ;
+				vht = CONF_OPER_CHWIDTH_80MHZ;
 			break;
-		case 2:
-			vht = CHANWIDTH_160MHZ;
+		case CHANWIDTH_160MHZ:
+			vht = CONF_OPER_CHWIDTH_160MHZ;
 			break;
-		case 3:
-			vht = CHANWIDTH_80P80MHZ;
+		case CHANWIDTH_80P80MHZ:
+			vht = CONF_OPER_CHWIDTH_80P80MHZ;
 			break;
 		default:
-			vht = CHANWIDTH_USE_HT;
+			vht = CONF_OPER_CHWIDTH_USE_HT;
 			break;
 		}
 	}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index a683eac..c3984a4 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -299,6 +299,8 @@
 		return -1;
 	}
 
+	wpa_s->wps_scan_done = false;
+
 	return 0;
 }
 
@@ -443,17 +445,74 @@
 	}
 
 	if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
+		struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+		/*
+		 * Perform a single-channel scan if the GO has already been
+		 * discovered on another non-P2P interface. Note that a scan
+		 * initiated by a P2P interface (e.g., the device interface)
+		 * should already have sufficient IEs and scan results will be
+		 * fetched on interface creation in that case.
+		 */
+		if (wpa_s->p2p_in_invitation == 1 && ssid) {
+			struct wpa_supplicant *ifs;
+			struct wpa_bss *bss = NULL;
+			const u8 *bssid = ssid->bssid_set ? ssid->bssid : NULL;
+
+			dl_list_for_each(ifs, &wpa_s->radio->ifaces,
+					 struct wpa_supplicant, radio_list) {
+				bss = wpa_bss_get(ifs, bssid, ssid->ssid,
+						  ssid->ssid_len);
+				if (bss)
+					break;
+			}
+			if (bss && !disabled_freq(wpa_s, bss->freq)) {
+				params->freqs = os_calloc(2, sizeof(int));
+				if (params->freqs) {
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"P2P: Scan only the known GO frequency %d MHz during invitation",
+						bss->freq);
+					params->freqs[0] = bss->freq;
+				}
+			}
+		}
+
 		/*
 		 * Optimize scan based on GO information during persistent
 		 * group reinvocation
 		 */
-		if (wpa_s->p2p_in_invitation < 5 &&
+		if (!params->freqs && wpa_s->p2p_in_invitation < 5 &&
 		    wpa_s->p2p_invite_go_freq > 0) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
-				wpa_s->p2p_invite_go_freq);
-			params->freqs = os_calloc(2, sizeof(int));
-			if (params->freqs)
-				params->freqs[0] = wpa_s->p2p_invite_go_freq;
+			if (wpa_s->p2p_invite_go_freq == 2 ||
+			    wpa_s->p2p_invite_go_freq == 5) {
+				enum hostapd_hw_mode mode;
+
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2P: Scan only GO preferred band %d GHz during invitation",
+					wpa_s->p2p_invite_go_freq);
+
+				if (!wpa_s->hw.modes)
+					return;
+				mode = wpa_s->p2p_invite_go_freq == 5 ?
+					HOSTAPD_MODE_IEEE80211A :
+					HOSTAPD_MODE_IEEE80211G;
+				if (wpa_s->p2p_in_invitation <= 2)
+					wpa_add_scan_freqs_list(wpa_s, mode,
+								params, false,
+								false, true);
+				if (!params->freqs || params->freqs[0] == 0)
+					wpa_add_scan_freqs_list(wpa_s, mode,
+								params, false,
+								false, false);
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2P: Scan only GO preferred frequency %d MHz during invitation",
+					wpa_s->p2p_invite_go_freq);
+				params->freqs = os_calloc(2, sizeof(int));
+				if (params->freqs)
+					params->freqs[0] =
+					    wpa_s->p2p_invite_go_freq;
+			}
 		}
 		wpa_s->p2p_in_invitation++;
 		if (wpa_s->p2p_in_invitation > 20) {
@@ -465,6 +524,7 @@
 			 */
 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
 			wpa_s->p2p_in_invitation = 0;
+			wpa_s->p2p_retry_limit = 0;
 		}
 	}
 #endif /* CONFIG_P2P */
@@ -709,7 +769,9 @@
 
 int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
 			    enum hostapd_hw_mode band,
-			    struct wpa_driver_scan_params *params, bool is_6ghz)
+			    struct wpa_driver_scan_params *params,
+			    bool is_6ghz, bool only_6ghz_psc,
+			    bool exclude_radar)
 {
 	/* Include only supported channels for the specified band */
 	struct hostapd_hw_modes *mode;
@@ -717,7 +779,7 @@
 	int *freqs, i;
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
-	if (!mode)
+	if (!mode || !mode->num_channels)
 		return -1;
 
 	if (params->freqs) {
@@ -734,6 +796,14 @@
 	for (i = 0; i < mode->num_channels; i++) {
 		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
 			continue;
+		if (exclude_radar &&
+		    (mode->channels[i].flag & HOSTAPD_CHAN_RADAR))
+			continue;
+
+		if (is_6ghz && only_6ghz_psc &&
+		    !is_6ghz_psc_frequency(mode->channels[i].freq))
+			continue;
+
 		params->freqs[num_chans++] = mode->channels[i].freq;
 	}
 	params->freqs[num_chans] = 0;
@@ -752,13 +822,13 @@
 
 	if (wpa_s->setband_mask & WPA_SETBAND_5G)
 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
-					false);
+					false, false, false);
 	if (wpa_s->setband_mask & WPA_SETBAND_2G)
 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
-					false);
+					false, false, false);
 	if (wpa_s->setband_mask & WPA_SETBAND_6G)
 		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
-					true);
+					true, false, false);
 }
 
 
@@ -1036,19 +1106,6 @@
 	}
 
 #ifdef CONFIG_P2P
-#ifdef ANDROID
-	if (wpa_s->global->p2p_go_found_external_scan &&
-	    (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) &&
-	    (wpa_s->global->p2p_group_formation == wpa_s)) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Try to fast associate since GO is found in external scan");
-		wpa_s->global->p2p_go_found_external_scan = 0;
-		if (wpa_supplicant_fast_associate(wpa_s) >= 0) {
-			return;
-		}
-	}
-#endif
-
 	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
 	    wpa_s->go_params && !wpa_s->conf->passive_scan) {
 		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
@@ -1280,7 +1337,8 @@
 
 		params.freqs = os_calloc(num + 1, sizeof(int));
 		if (params.freqs) {
-			num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+			num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+						     false);
 			if (num > 0) {
 				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
 					"current operating channels since "
@@ -1341,6 +1399,12 @@
 		}
 	}
 
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_s->manual_non_coloc_6ghz) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Collocated 6 GHz logic is disabled");
+		params.non_coloc_6ghz = 1;
+	}
+
 	scan_params = &params;
 
 scan:
@@ -1364,7 +1428,13 @@
 
 		params.freqs = os_calloc(num + 1, sizeof(int));
 		if (params.freqs) {
-			num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+			/*
+			 * Exclude the operating frequency of the current
+			 * interface since we're looking to transition off of
+			 * it.
+			 */
+			num = get_shared_radio_freqs(wpa_s, params.freqs, num,
+						     true);
 			if (num > 0 && num == wpa_s->num_multichan_concurrent) {
 				wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
 			} else {
@@ -1374,20 +1444,9 @@
 		}
 	}
 
-	if (!params.freqs &&
-	    (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) &&
-	    !is_p2p_allow_6ghz(wpa_s->global->p2p) &&
-	    is_6ghz_supported(wpa_s)) {
-		int i;
-
-		/* Exclude 6 GHz channels from the full scan for P2P connection
-		 * since the 6 GHz band is disabled for P2P uses. */
-		wpa_printf(MSG_DEBUG,
-			   "P2P: 6 GHz disabled - update the scan frequency list");
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params, false);
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, &params, false);
-		wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211AD, &params, false);
-	}
+	if (!params.freqs && is_6ghz_supported(wpa_s) &&
+	    (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning))
+		wpas_p2p_scan_freqs(wpa_s, &params, true);
 #endif /* CONFIG_P2P */
 
 	ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
@@ -1939,6 +1998,18 @@
 }
 
 
+const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type)
+{
+	size_t ie_len = res->ie_len;
+
+	/* Use the Beacon frame IEs if res->ie_len is not available */
+	if (!ie_len)
+		ie_len = res->beacon_ie_len;
+
+	return get_ml_ie((const u8 *) (res + 1), ie_len, type);
+}
+
+
 /**
  * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
  * @res: Scan result entry
@@ -2364,6 +2435,8 @@
 	{ -1, 780000 }  /* SNR > 37 */
 };
 
+/* EHT needs to be enabled in order to achieve MCS12 and MCS13 rates. */
+#define EHT_MCS 12
 
 static const struct minsnr_bitrate_entry he20_table[] = {
 	{ 0, 0 },
@@ -2379,7 +2452,9 @@
 	{ 31, 114700 }, /* HE20 MCS9 */
 	{ 34, 129000 }, /* HE20 MCS10 */
 	{ 36, 143400 }, /* HE20 MCS11 */
-	{ -1, 143400 }  /* SNR > 29 */
+	{ 39, 154900 }, /* EHT20 MCS12 */
+	{ 42, 172100 }, /* EHT20 MCS13 */
+	{ -1, 172100 }  /* SNR > 42 */
 };
 
 static const struct minsnr_bitrate_entry he40_table[] = {
@@ -2396,7 +2471,9 @@
 	{ 34, 229400 }, /* HE40 MCS9 */
 	{ 37, 258100 }, /* HE40 MCS10 */
 	{ 39, 286800 }, /* HE40 MCS11 */
-	{ -1, 286800 }  /* SNR > 34 */
+	{ 42, 309500 }, /* EHT40 MCS12 */
+	{ 45, 344100 }, /* EHT40 MCS13 */
+	{ -1, 344100 }  /* SNR > 45 */
 };
 
 static const struct minsnr_bitrate_entry he80_table[] = {
@@ -2413,7 +2490,9 @@
 	{ 37, 480400 }, /* HE80 MCS9 */
 	{ 40, 540400 }, /* HE80 MCS10 */
 	{ 42, 600500 }, /* HE80 MCS11 */
-	{ -1, 600500 }  /* SNR > 37 */
+	{ 45, 648500 }, /* EHT80 MCS12 */
+	{ 48, 720600 }, /* EHT80 MCS13 */
+	{ -1, 720600 }  /* SNR > 48 */
 };
 
 
@@ -2431,9 +2510,31 @@
 	{ 40, 960800 },  /* HE160 MCS9 */
 	{ 43, 1080900 }, /* HE160 MCS10 */
 	{ 45, 1201000 }, /* HE160 MCS11 */
-	{ -1, 1201000 }  /* SNR > 37 */
+	{ 48, 1297100 }, /* EHT160 MCS12 */
+	{ 51, 1441200 }, /* EHT160 MCS13 */
+	{ -1, 1441200 }  /* SNR > 51 */
 };
 
+/* See IEEE P802.11be/D2.0, Table 36-86: EHT-MCSs for 4x996-tone RU, NSS,u = 1
+ */
+static const struct minsnr_bitrate_entry eht320_table[] = {
+	{ 0, 0 },
+	{ 14, 144100 },   /* EHT320 MCS0 */
+	{ 17, 288200 },   /* EHT320 MCS1 */
+	{ 21, 432400 },   /* EHT320 MCS2 */
+	{ 23, 576500 },   /* EHT320 MCS3 */
+	{ 27, 864700 },   /* EHT320 MCS4 */
+	{ 30, 1152900 },  /* EHT320 MCS5 */
+	{ 32, 1297100 },  /* EHT320 MCS6 */
+	{ 37, 1441200 },  /* EHT320 MCS7 */
+	{ 41, 1729400 },  /* EHT320 MCS8 */
+	{ 43, 1921500 },  /* EHT320 MCS9 */
+	{ 46, 2161800 },  /* EHT320 MCS10 */
+	{ 48, 2401900 },  /* EHT320 MCS11 */
+	{ 51, 2594100 },  /* EHT320 MCS12 */
+	{ 54, 2882400 },  /* EHT320 MCS13 */
+	{ -1, 2882400 }   /* SNR > 54 */
+};
 
 static unsigned int interpolate_rate(int snr, int snr0, int snr1,
 				     int rate0, int rate1)
@@ -2485,17 +2586,18 @@
 }
 
 
-static unsigned int max_he_rate(const struct minsnr_bitrate_entry table[],
-				int snr)
+static unsigned int max_he_eht_rate(const struct minsnr_bitrate_entry table[],
+				    int snr, bool eht)
 {
 	const struct minsnr_bitrate_entry *prev, *entry = table;
 
-	while (entry->minsnr != -1 && snr >= entry->minsnr)
+	while (entry->minsnr != -1 && snr >= entry->minsnr &&
+	       (eht || entry - table <= EHT_MCS))
 		entry++;
 	if (entry == table)
 		return 0;
 	prev = entry - 1;
-	if (entry->minsnr == -1)
+	if (entry->minsnr == -1 || (!eht && entry - table > EHT_MCS))
 		return prev->bitrate;
 	return interpolate_rate(snr, prev->minsnr, entry->minsnr,
 				prev->bitrate, entry->bitrate);
@@ -2633,8 +2735,11 @@
 	if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
 		/* Use +2 to assume HE is always faster than HT/VHT */
 		struct ieee80211_he_capabilities *he;
+		struct ieee80211_eht_capabilities *eht;
 		struct he_capabilities *own_he;
-		u8 cw;
+		u8 cw, boost = 2;
+		const u8 *eht_ie;
+		bool is_eht = false;
 
 		ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
 		if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
@@ -2642,7 +2747,18 @@
 		he = (struct ieee80211_he_capabilities *) &ie[3];
 		own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
 
-		tmp = max_he_rate(he20_table, snr) + 2;
+		/* Use +3 to assume EHT is always faster than HE */
+		if (hw_mode->eht_capab[IEEE80211_MODE_INFRA].eht_supported) {
+			eht_ie = get_ie_ext(ies, ies_len,
+					    WLAN_EID_EXT_EHT_CAPABILITIES);
+			if (eht_ie &&
+			    (eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN)) {
+				is_eht = true;
+				boost = 3;
+			}
+		}
+
+		tmp = max_he_eht_rate(he20_table, snr, is_eht) + boost;
 		if (tmp > est)
 			est = tmp;
 
@@ -2651,14 +2767,14 @@
 		if (cw &
 		    (IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
 		     HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
-			tmp = max_he_rate(he40_table, snr) + 2;
+			tmp = max_he_eht_rate(he40_table, snr, is_eht) + boost;
 			if (tmp > est)
 				est = tmp;
 		}
 
 		if (!IS_2P4GHZ(freq) &&
 		    (cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
-			tmp = max_he_rate(he80_table, snr) + 2;
+			tmp = max_he_eht_rate(he80_table, snr, is_eht) + boost;
 			if (tmp > est)
 				est = tmp;
 		}
@@ -2666,7 +2782,20 @@
 		if (!IS_2P4GHZ(freq) &&
 		    (cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 			   HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
-			tmp = max_he_rate(he160_table, snr) + 2;
+			tmp = max_he_eht_rate(he160_table, snr, is_eht) + boost;
+			if (tmp > est)
+				est = tmp;
+		}
+
+		if (!is_eht)
+			return est;
+
+		eht = (struct ieee80211_eht_capabilities *) &eht_ie[3];
+
+		if (is_6ghz_freq(freq) &&
+		    (eht->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+		     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
+			tmp = max_he_eht_rate(eht320_table, snr, true);
 			if (tmp > est)
 				est = tmp;
 		}
@@ -2915,6 +3044,7 @@
 	params->relative_adjust_band = src->relative_adjust_band;
 	params->relative_adjust_rssi = src->relative_adjust_rssi;
 	params->p2p_include_6ghz = src->p2p_include_6ghz;
+	params->non_coloc_6ghz = src->non_coloc_6ghz;
 	return params;
 
 failed:
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index d1780eb..30f4395 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -51,6 +51,7 @@
 				struct scan_info *info, int new_scan);
 int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
+const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type);
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
 				  u32 vendor_type);
 const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
@@ -91,6 +92,7 @@
 int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
 			    enum hostapd_hw_mode band,
 			    struct wpa_driver_scan_params *params,
-			    bool is_6ghz);
+			    bool is_6ghz, bool only_6ghz_psc,
+			    bool exclude_radar);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index cc55fa6..ea4023c 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "utils/eloop.h"
+#include "utils/ext_password.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
@@ -53,7 +54,7 @@
 }
 
 
-static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
+static int sme_set_sae_group(struct wpa_supplicant *wpa_s, bool external)
 {
 	int *groups = wpa_s->conf->sae_groups;
 	int default_groups[] = { 19, 20, 21, 0 };
@@ -72,6 +73,8 @@
 		if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
 				wpa_s->sme.sae.group);
+			wpa_s->sme.sae.akmp = external ?
+				wpa_s->sme.ext_auth_key_mgmt : wpa_s->key_mgmt;
 			return 0;
 		}
 		wpa_s->sme.sae_group_index++;
@@ -83,17 +86,22 @@
 
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 						 struct wpa_ssid *ssid,
-						 const u8 *bssid, int external,
+						 const u8 *bssid,
+						 const u8 *mld_addr,
+						 int external,
 						 int reuse, int *ret_use_pt,
 						 bool *ret_use_pk)
 {
 	struct wpabuf *buf;
 	size_t len;
-	const char *password;
+	char *password = NULL;
 	struct wpa_bss *bss;
 	int use_pt = 0;
 	bool use_pk = false;
 	u8 rsnxe_capa = 0;
+	int key_mgmt = external ? wpa_s->sme.ext_auth_key_mgmt :
+		wpa_s->key_mgmt;
+	const u8 *addr = mld_addr ? mld_addr : bssid;
 
 	if (ret_use_pt)
 		*ret_use_pt = 0;
@@ -105,7 +113,7 @@
 		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
 		buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override));
 		if (!buf)
-			return NULL;
+			goto fail;
 		if (!external) {
 			wpabuf_put_le16(buf, 1); /* Transaction seq# */
 			wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
@@ -115,25 +123,58 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	password = ssid->sae_password;
-	if (!password)
-		password = ssid->passphrase;
+	if (ssid->sae_password) {
+		password = os_strdup(ssid->sae_password);
+		if (!password) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"SAE: Failed to allocate password");
+			goto fail;
+		}
+	}
+	if (!password && ssid->passphrase) {
+		password = os_strdup(ssid->passphrase);
+		if (!password) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"SAE: Failed to allocate password");
+			goto fail;
+		}
+	}
+	if (!password && ssid->ext_psk) {
+		struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+						     ssid->ext_psk);
+
+		if (!pw) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"SAE: No password found from external storage");
+			goto fail;
+		}
+
+		password = os_malloc(wpabuf_len(pw) + 1);
+		if (!password) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"SAE: Failed to allocate password");
+			goto fail;
+		}
+		os_memcpy(password, wpabuf_head(pw), wpabuf_len(pw));
+		password[wpabuf_len(pw)] = '\0';
+		ext_password_free(pw);
+	}
 	if (!password) {
 		wpa_printf(MSG_DEBUG, "SAE: No password available");
-		return NULL;
+		goto fail;
 	}
 
 	if (reuse && wpa_s->sme.sae.tmp &&
-	    os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
+	    os_memcmp(addr, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: Reuse previously generated PWE on a retry with the same AP");
 		use_pt = wpa_s->sme.sae.h2e;
 		use_pk = wpa_s->sme.sae.pk;
 		goto reuse_data;
 	}
-	if (sme_set_sae_group(wpa_s) < 0) {
+	if (sme_set_sae_group(wpa_s, external) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
-		return NULL;
+		goto fail;
 	}
 
 	bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
@@ -151,7 +192,14 @@
 			rsnxe_capa = rsnxe[2];
 	}
 
-	if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
+	if (ssid->sae_password_id &&
+	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		use_pt = 1;
+	if (wpa_key_mgmt_sae_ext_key(key_mgmt) &&
+	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		use_pt = 1;
+	if (bss && is_6ghz_freq(bss->freq) &&
+	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 #ifdef CONFIG_SAE_PK
 	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -167,42 +215,47 @@
 	if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: Cannot use PK with the selected AP");
-		return NULL;
+		goto fail;
 	}
 #endif /* CONFIG_SAE_PK */
 
-	if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
+	if (use_pt || wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	    wpa_s->conf->sae_pwe == SAE_PWE_BOTH) {
 		use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
 
-		if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
-		    wpa_s->conf->sae_pwe != 3 &&
+		if ((wpa_s->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		     ssid->sae_password_id ||
+		     wpa_key_mgmt_sae_ext_key(key_mgmt)) &&
+		    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		    !use_pt) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Cannot use H2E with the selected AP");
-			return NULL;
+			goto fail;
 		}
 	}
 
+	if (use_pt && !ssid->pt)
+		wpa_s_setup_sae_pt(wpa_s->conf, ssid, true);
 	if (use_pt &&
 	    sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
-				  wpa_s->own_addr, bssid,
+				  wpa_s->own_addr, addr,
 				  wpa_s->sme.sae_rejected_groups, NULL) < 0)
-		return NULL;
+		goto fail;
 	if (!use_pt &&
 	    sae_prepare_commit(wpa_s->own_addr, bssid,
 			       (u8 *) password, os_strlen(password),
 			       &wpa_s->sme.sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
-		return NULL;
+		goto fail;
 	}
 	if (wpa_s->sme.sae.tmp) {
-		os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
+		os_memcpy(wpa_s->sme.sae.tmp->bssid, addr, ETH_ALEN);
 		if (use_pt && use_pk)
 			wpa_s->sme.sae.pk = 1;
 #ifdef CONFIG_SAE_PK
 		os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
 			  ETH_ALEN);
-		os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
+		os_memcpy(wpa_s->sme.sae.tmp->peer_addr, addr, ETH_ALEN);
 		sae_pk_set_password(&wpa_s->sme.sae, password);
 #endif /* CONFIG_SAE_PK */
 	}
@@ -213,7 +266,7 @@
 		len += 4 + os_strlen(ssid->sae_password_id);
 	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
 	if (buf == NULL)
-		return NULL;
+		goto fail;
 	if (!external) {
 		wpabuf_put_le16(buf, 1); /* Transaction seq# */
 		if (use_pk)
@@ -226,14 +279,19 @@
 	if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
 			     ssid->sae_password_id) < 0) {
 		wpabuf_free(buf);
-		return NULL;
+		goto fail;
 	}
 	if (ret_use_pt)
 		*ret_use_pt = use_pt;
 	if (ret_use_pk)
 		*ret_use_pk = use_pk;
 
+	str_clear_free(password);
 	return buf;
+
+fail:
+	str_clear_free(password);
+	return NULL;
 }
 
 
@@ -320,6 +378,188 @@
 }
 
 
+static bool wpas_ml_element(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpabuf *mlbuf;
+	const u8 *rnr_ie, *pos;
+	u8 ml_ie_len, rnr_ie_len;
+	const struct ieee80211_eht_ml *eht_ml;
+	const struct eht_ml_basic_common_info *ml_basic_common_info;
+	u8 i;
+	const u16 control =
+		host_to_le16(MULTI_LINK_CONTROL_TYPE_BASIC |
+			     BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+			     BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+			     BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA);
+	bool ret = false;
+
+	if (!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO))
+		return false;
+
+	mlbuf = wpa_bss_defrag_mle(bss, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mlbuf) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No ML element");
+		return false;
+	}
+
+	ml_ie_len = wpabuf_len(mlbuf);
+
+	/* control + common info len + MLD address + MLD link information */
+	if (ml_ie_len < 2 + 1 + ETH_ALEN + 1)
+		goto out;
+
+	eht_ml = wpabuf_head(mlbuf);
+	if ((eht_ml->ml_control & control) != control) {
+		wpa_printf(MSG_DEBUG, "MLD: Unexpected ML element control=0x%x",
+			   eht_ml->ml_control);
+		goto out;
+	}
+
+	ml_basic_common_info =
+		(const struct eht_ml_basic_common_info *) eht_ml->variable;
+
+	/* common info length should be valid (self, mld_addr, link_id) */
+	if (ml_basic_common_info->len < 1 + ETH_ALEN + 1)
+		goto out;
+
+	/* get the MLD address and MLD link ID */
+	os_memcpy(wpa_s->ap_mld_addr, ml_basic_common_info->mld_addr,
+		  ETH_ALEN);
+	wpa_s->mlo_assoc_link_id = ml_basic_common_info->variable[0] &
+		EHT_ML_LINK_ID_MSK;
+
+	os_memcpy(wpa_s->links[wpa_s->mlo_assoc_link_id].bssid, bss->bssid,
+		  ETH_ALEN);
+	wpa_s->links[wpa_s->mlo_assoc_link_id].freq = bss->freq;
+
+	wpa_printf(MSG_DEBUG, "MLD: address=" MACSTR ", link ID=%u",
+		   MAC2STR(wpa_s->ap_mld_addr), wpa_s->mlo_assoc_link_id);
+
+	wpa_s->valid_links = BIT(wpa_s->mlo_assoc_link_id);
+
+	rnr_ie = wpa_bss_get_ie(bss, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
+	if (!rnr_ie) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RNR element");
+		ret = true;
+		goto out;
+	}
+
+	rnr_ie_len = rnr_ie[1];
+	pos = rnr_ie + 2;
+
+	while (rnr_ie_len > sizeof(struct ieee80211_neighbor_ap_info)) {
+		const struct ieee80211_neighbor_ap_info *ap_info =
+			(const struct ieee80211_neighbor_ap_info *) pos;
+		const u8 *data = ap_info->data;
+		size_t len = sizeof(struct ieee80211_neighbor_ap_info) +
+			ap_info->tbtt_info_len;
+
+		wpa_printf(MSG_DEBUG, "MLD: op_class=%u, channel=%u",
+			   ap_info->op_class, ap_info->channel);
+
+		if (len > rnr_ie_len)
+			break;
+
+		if (ap_info->tbtt_info_len < 16) {
+			rnr_ie_len -= len;
+			pos += len;
+			continue;
+		}
+
+		data += 13;
+
+		wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
+			   *data, *(data + 1) & 0xF);
+
+		if (*data) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Reported link not part of MLD");
+		} else {
+			struct wpa_bss *neigh_bss =
+				wpa_bss_get_bssid(wpa_s, ap_info->data + 1);
+			u8 link_id = *(data + 1) & 0xF;
+
+			if (neigh_bss) {
+				if (wpa_scan_res_match(wpa_s, 0, neigh_bss,
+						       wpa_s->current_ssid,
+						       1, 0)) {
+					wpa_s->valid_links |= BIT(link_id);
+					os_memcpy(wpa_s->links[link_id].bssid,
+						  ap_info->data + 1, ETH_ALEN);
+					wpa_s->links[link_id].freq =
+						neigh_bss->freq;
+				} else {
+					wpa_printf(MSG_DEBUG,
+						   "MLD: Neighbor doesn't match current SSID - skip link");
+				}
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "MLD: Neighbor not found in scan");
+			}
+		}
+
+		rnr_ie_len -= len;
+		pos += len;
+	}
+
+	wpa_printf(MSG_DEBUG, "MLD: valid_links=0x%x", wpa_s->valid_links);
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
+			   i, MAC2STR(wpa_s->links[i].bssid));
+	}
+
+	ret = true;
+out:
+	wpabuf_free(mlbuf);
+	return ret;
+}
+
+
+static void wpas_sme_ml_auth(struct wpa_supplicant *wpa_s,
+			     union wpa_event_data *data,
+			     int ie_offset)
+{
+	struct ieee802_11_elems elems;
+	const u8 *mld_addr;
+
+	if (!wpa_s->valid_links)
+		return;
+
+	if (ieee802_11_parse_elems(data->auth.ies + ie_offset,
+				   data->auth.ies_len - ie_offset,
+				   &elems, 0) != ParseOK) {
+		wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
+		goto out;
+	}
+
+	if (!elems.basic_mle || !elems.basic_mle_len) {
+		wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication");
+		goto out;
+	}
+
+	mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+	if (!mld_addr)
+		goto out;
+
+	wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
+
+	if (os_memcmp(wpa_s->ap_mld_addr, mld_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected "
+			   MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
+		goto out;
+	}
+
+	return;
+out:
+	wpa_printf(MSG_DEBUG, "MLD: Authentication - clearing MLD state");
+	wpas_reset_mlo_info(wpa_s);
+}
+
+
 static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 				    struct wpa_bss *bss, struct wpa_ssid *ssid,
 				    int start)
@@ -364,6 +604,13 @@
 	params.ssid_len = bss->ssid_len;
 	params.p2p = ssid->p2p_group;
 
+	if (wpas_ml_element(wpa_s, bss)) {
+		wpa_printf(MSG_DEBUG, "MLD: In authentication");
+		params.mld = true;
+		params.mld_link_id = wpa_s->mlo_assoc_link_id;
+		params.ap_mld_addr = wpa_s->ap_mld_addr;
+	}
+
 	if (wpa_s->sme.ssid_len != params.ssid_len ||
 	    os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
 		wpa_s->sme.prev_bssid_set = 0;
@@ -408,8 +655,13 @@
 #endif /* CONFIG_DPP */
 		} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
 			   wpa_key_mgmt_sae(ied.key_mgmt)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
-			params.auth_alg = WPA_AUTH_ALG_SAE;
+			if (wpas_is_sae_avoided(wpa_s, ssid, &ied)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"SAE enabled, but disallowing SAE auth_alg without PMF");
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+				params.auth_alg = WPA_AUTH_ALG_SAE;
+			}
 		} else {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
@@ -444,7 +696,9 @@
 		if (wpa_key_mgmt_fils(ssid->key_mgmt))
 			cache_id = wpa_bss_get_fils_cache_id(bss);
 #endif /* CONFIG_FILS */
-		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+					    params.mld ? params.ap_mld_addr :
+					    bss->bssid,
 					    wpa_s->current_ssid,
 					    try_opportunistic, cache_id,
 					    0) == 0)
@@ -452,7 +706,8 @@
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
 					      wpa_s->sme.assoc_req_ie,
-					      &wpa_s->sme.assoc_req_ie_len)) {
+					      &wpa_s->sme.assoc_req_ie_len,
+					      false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
 				"key management and encryption suites");
 			wpas_connect_work_done(wpa_s);
@@ -465,7 +720,8 @@
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
 					      wpa_s->sme.assoc_req_ie,
-					      &wpa_s->sme.assoc_req_ie_len)) {
+					      &wpa_s->sme.assoc_req_ie_len,
+					      false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
 				"key management and encryption suites");
 			wpas_connect_work_done(wpa_s);
@@ -485,7 +741,8 @@
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
 					      wpa_s->sme.assoc_req_ie,
-					      &wpa_s->sme.assoc_req_ie_len)) {
+					      &wpa_s->sme.assoc_req_ie_len,
+					      false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
 				"key management and encryption suites (no "
 				"scan results)");
@@ -567,7 +824,7 @@
 
 		if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used &&
 		    os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
-		    wpa_sm_has_ptk(wpa_s->wpa)) {
+		    wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
 				"over-the-air");
 			params.auth_alg = WPA_AUTH_ALG_FT;
@@ -747,11 +1004,14 @@
 
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
-	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0,
+	    pmksa_cache_set_current(wpa_s->wpa, NULL,
+				    params.mld ? params.ap_mld_addr :
+				    bss->bssid,
+				    ssid, 0,
 				    NULL,
-				    wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ?
-				    WPA_KEY_MGMT_FT_SAE :
-				    WPA_KEY_MGMT_SAE) == 0) {
+				    wpa_key_mgmt_sae(wpa_s->key_mgmt) ?
+				    wpa_s->key_mgmt :
+				    (int) WPA_KEY_MGMT_SAE) == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
@@ -762,7 +1022,10 @@
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
 		if (start)
 			resp = sme_auth_build_sae_commit(wpa_s, ssid,
-							 bss->bssid, 0,
+							 bss->bssid,
+							 params.mld ?
+							 params.ap_mld_addr :
+							 NULL, 0,
 							 start == 2, NULL,
 							 NULL);
 		else
@@ -841,7 +1104,9 @@
 			goto no_fils;
 		}
 
-		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL,
+					    params.mld ? params.ap_mld_addr :
+					    bss->bssid,
 					    ssid, 0,
 					    wpa_bss_get_fils_cache_id(bss),
 					    0) == 0)
@@ -894,7 +1159,7 @@
 	 */
 	if (wpa_s->num_multichan_concurrent < 2) {
 		int freq, num;
-		num = get_shared_radio_freqs(wpa_s, &freq, 1);
+		num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
 		if (num > 0 && freq > 0 && freq != params.freq) {
 			wpa_printf(MSG_DEBUG,
 				   "Conflicting frequency found (%d != %d)",
@@ -979,6 +1244,7 @@
 	wpa_s->rsnxe_len = 0;
 
 	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+	wpas_notify_auth_changed(wpa_s);
 }
 
 
@@ -1040,11 +1306,30 @@
 
 #ifdef CONFIG_SAE
 
+#define WPA_AUTH_FRAME_ML_IE_LEN	(6 + ETH_ALEN)
+
+static void wpa_auth_ml_ie(struct wpabuf *buf, const u8 *mld_addr)
+{
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 4 + ETH_ALEN);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
+
+	/* Basic Multi-Link element Control field */
+	wpabuf_put_u8(buf, 0x0);
+	wpabuf_put_u8(buf, 0x0);
+
+	/* Common Info */
+	wpabuf_put_u8(buf, 0x7); /* length = Length field + MLD MAC address */
+	wpabuf_put_data(buf, mld_addr, ETH_ALEN);
+}
+
+
 static int sme_external_auth_build_buf(struct wpabuf *buf,
 				       struct wpabuf *params,
 				       const u8 *sa, const u8 *da,
 				       u16 auth_transaction, u16 seq_num,
-				       u16 status_code)
+				       u16 status_code, const u8 *mld_addr)
 {
 	struct ieee80211_mgmt *resp;
 
@@ -1063,6 +1348,9 @@
 	if (params)
 		wpabuf_put_buf(buf, params);
 
+	if (mld_addr)
+		wpa_auth_ml_ie(buf, mld_addr);
+
 	return 0;
 }
 
@@ -1076,15 +1364,19 @@
 	bool use_pk;
 	u16 status;
 
-	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
-					 &use_pk);
+	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid,
+					 wpa_s->sme.ext_ml_auth ?
+					 wpa_s->sme.ext_auth_ap_mld_addr : NULL,
+					 1, 0, &use_pt, &use_pk);
 	if (!resp) {
 		wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
 		return -1;
 	}
 
 	wpa_s->sme.sae.state = SAE_COMMITTED;
-	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp));
+	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp) +
+			   (wpa_s->sme.ext_ml_auth ? WPA_AUTH_FRAME_ML_IE_LEN :
+			    0));
 	if (!buf) {
 		wpabuf_free(resp);
 		return -1;
@@ -1098,7 +1390,11 @@
 	else
 		status = WLAN_STATUS_SUCCESS;
 	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
-				    bssid, 1, wpa_s->sme.seq_num, status);
+				    wpa_s->sme.ext_ml_auth ?
+				    wpa_s->sme.ext_auth_ap_mld_addr : bssid, 1,
+				    wpa_s->sme.seq_num, status,
+				    wpa_s->sme.ext_ml_auth ?
+				    wpa_s->own_addr : NULL);
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
@@ -1112,6 +1408,7 @@
 {
 	struct external_auth params;
 
+	wpa_s->sme.ext_auth_wpa_ssid = NULL;
 	os_memset(&params, 0, sizeof(params));
 	params.status = status;
 	params.ssid = wpa_s->sme.ext_auth_ssid;
@@ -1130,13 +1427,18 @@
 	size_t ssid_str_len = data->external_auth.ssid_len;
 	const u8 *ssid_str = data->external_auth.ssid;
 
+	wpa_s->sme.ext_auth_wpa_ssid = NULL;
 	/* Get the SSID conf from the ssid string obtained */
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 		if (!wpas_network_disabled(wpa_s, ssid) &&
 		    ssid_str_len == ssid->ssid_len &&
 		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
-		    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
+		    wpa_key_mgmt_sae(ssid->key_mgmt)) {
+			/* Make sure PT is derived */
+			wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
+			wpa_s->sme.ext_auth_wpa_ssid = ssid;
 			break;
+		}
 	}
 	if (!ssid ||
 	    sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
@@ -1159,7 +1461,9 @@
 	}
 
 	wpa_s->sme.sae.state = SAE_CONFIRMED;
-	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp));
+	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp) +
+			   (wpa_s->sme.ext_ml_auth ? WPA_AUTH_FRAME_ML_IE_LEN :
+			    0));
 	if (!buf) {
 		wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure");
 		wpabuf_free(resp);
@@ -1168,18 +1472,51 @@
 	wpa_s->sme.seq_num++;
 	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
 				    da, 2, wpa_s->sme.seq_num,
-				    WLAN_STATUS_SUCCESS);
+				    WLAN_STATUS_SUCCESS,
+				    wpa_s->sme.ext_ml_auth ?
+				    wpa_s->own_addr : NULL);
+
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
 }
 
 
+static bool is_sae_key_mgmt_suite(struct wpa_supplicant *wpa_s, u32 suite)
+{
+	/* suite is supposed to be the selector value in host byte order with
+	 * the OUI in three most significant octets. However, the initial
+	 * implementation swapped that byte order and did not work with drivers
+	 * that followed the expected byte order. Keep a workaround here to
+	 * match that initial implementation so that already deployed use cases
+	 * remain functional. */
+	if (RSN_SELECTOR_GET(&suite) == RSN_AUTH_KEY_MGMT_SAE) {
+		/* Old drivers which follow initial implementation send SAE AKM
+		 * for both SAE and FT-SAE connections. In that case, determine
+		 * the actual AKM from wpa_s->key_mgmt. */
+		wpa_s->sme.ext_auth_key_mgmt = wpa_s->key_mgmt;
+		return true;
+	}
+
+	if (suite == RSN_AUTH_KEY_MGMT_SAE)
+		wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE;
+	else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE)
+		wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+	else if (suite == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+		wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+	else if (suite == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+		wpa_s->sme.ext_auth_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+	else
+		return false;
+
+	return true;
+}
+
+
 void sme_external_auth_trigger(struct wpa_supplicant *wpa_s,
 			       union wpa_event_data *data)
 {
-	if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) !=
-	    RSN_AUTH_KEY_MGMT_SAE)
+	if (!is_sae_key_mgmt_suite(wpa_s, data->external_auth.key_mgmt_suite))
 		return;
 
 	if (data->external_auth.action == EXT_AUTH_START) {
@@ -1190,6 +1527,13 @@
 		os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid,
 			  data->external_auth.ssid_len);
 		wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len;
+		if (data->external_auth.mld_addr) {
+			wpa_s->sme.ext_ml_auth = true;
+			os_memcpy(wpa_s->sme.ext_auth_ap_mld_addr,
+				  data->external_auth.mld_addr, ETH_ALEN);
+		} else {
+			wpa_s->sme.ext_ml_auth = false;
+		}
 		wpa_s->sme.seq_num = 0;
 		wpa_s->sme.sae.state = SAE_NOTHING;
 		wpa_s->sme.sae.send_confirm = 0;
@@ -1251,9 +1595,45 @@
 }
 
 
+static int sme_external_ml_auth(struct wpa_supplicant *wpa_s,
+				const u8 *data, size_t len, int ie_offset)
+{
+	struct ieee802_11_elems elems;
+	const u8 *mld_addr;
+
+	if (ieee802_11_parse_elems(data + ie_offset, len - ie_offset,
+				   &elems, 0) != ParseOK) {
+		wpa_printf(MSG_DEBUG, "MLD: Failed parsing elements");
+		return -1;
+	}
+
+	if (!elems.basic_mle || !elems.basic_mle_len) {
+		wpa_printf(MSG_DEBUG, "MLD: No ML element in authentication");
+		return -1;
+	}
+
+	mld_addr = get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+	if (!mld_addr) {
+		wpa_printf(MSG_DEBUG, "MLD: No MLD address in ML element");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "MLD: mld_address=" MACSTR, MAC2STR(mld_addr));
+
+	if (os_memcmp(wpa_s->sme.ext_auth_ap_mld_addr, mld_addr, ETH_ALEN) !=
+	    0) {
+		wpa_printf(MSG_DEBUG, "MLD: Unexpected MLD address (expected "
+			   MACSTR ")", MAC2STR(wpa_s->ap_mld_addr));
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			u16 status_code, const u8 *data, size_t len,
-			int external, const u8 *sa)
+			int external, const u8 *sa, int *ie_offset)
 {
 	int *groups;
 
@@ -1263,7 +1643,8 @@
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+	    ((external && wpa_s->sme.ext_auth_wpa_ssid) ||
+	     (!external && wpa_s->current_bss && wpa_s->current_ssid))) {
 		int default_groups[] = { 19, 20, 21, 0 };
 		u16 group;
 		const u8 *token_pos;
@@ -1297,53 +1678,76 @@
 		token_len = len - sizeof(le16);
 		h2e = wpa_s->sme.sae.h2e;
 		if (h2e) {
+			u8 id, elen, extid;
+
 			if (token_len < 3) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"SME: Too short SAE anti-clogging token container");
 				return -1;
 			}
-			if (token_pos[0] != WLAN_EID_EXTENSION ||
-			    token_pos[1] == 0 ||
-			    token_pos[1] > token_len - 2 ||
-			    token_pos[2] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) {
+			id = *token_pos++;
+			elen = *token_pos++;
+			extid = *token_pos++;
+			if (id != WLAN_EID_EXTENSION ||
+			    elen == 0 || elen > token_len - 2 ||
+			    extid != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"SME: Invalid SAE anti-clogging token container header");
 				return -1;
 			}
-			token_len = token_pos[1] - 1;
-			token_pos += 3;
+			token_len = elen - 1;
 		}
+
+		*ie_offset = token_pos + token_len - data;
+
 		wpa_s->sme.sae_token = wpabuf_alloc_copy(token_pos, token_len);
+		if (!wpa_s->sme.sae_token) {
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"SME: Failed to allocate SAE token");
+			return -1;
+		}
+
 		wpa_hexdump_buf(MSG_DEBUG, "SME: Requested anti-clogging token",
 				wpa_s->sme.sae_token);
-		if (!external)
+		if (!external) {
 			sme_send_authentication(wpa_s, wpa_s->current_bss,
 						wpa_s->current_ssid, 2);
-		else
+		} else {
+			if (wpa_s->sme.ext_ml_auth &&
+			    sme_external_ml_auth(wpa_s, data, len, *ie_offset))
+				return -1;
+
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
+		}
 		return 0;
 	}
 
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+	    ((external && wpa_s->sme.ext_auth_wpa_ssid) ||
+	     (!external && wpa_s->current_bss && wpa_s->current_ssid))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
 		int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
 				     wpa_s->sme.sae.group);
 		wpa_s->sme.sae_group_index++;
-		if (sme_set_sae_group(wpa_s) < 0)
+		if (sme_set_sae_group(wpa_s, external) < 0)
 			return -1; /* no other groups enabled */
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
-		if (!external)
+		if (!external) {
 			sme_send_authentication(wpa_s, wpa_s->current_bss,
 						wpa_s->current_ssid, 1);
-		else
+		} else {
+			if (wpa_s->sme.ext_ml_auth &&
+			    sme_external_ml_auth(wpa_s, data, len, *ie_offset))
+				return -1;
+
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
+		}
 		return 0;
 	}
 
@@ -1375,8 +1779,9 @@
 		groups = wpa_s->conf->sae_groups;
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
-		if ((!external && wpa_s->current_bss == NULL) ||
-		    wpa_s->current_ssid == NULL)
+		if ((external && !wpa_s->sme.ext_auth_wpa_ssid) ||
+		    (!external &&
+		     (!wpa_s->current_bss || !wpa_s->current_ssid)))
 			return -1;
 		if (wpa_s->sme.sae.state != SAE_COMMITTED) {
 			wpa_printf(MSG_DEBUG,
@@ -1406,7 +1811,8 @@
 		res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
 				       groups, status_code ==
 				       WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
-				       status_code == WLAN_STATUS_SAE_PK);
+				       status_code == WLAN_STATUS_SAE_PK,
+				       ie_offset);
 		if (res == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message due to reflection attack");
@@ -1429,11 +1835,16 @@
 
 		wpabuf_free(wpa_s->sme.sae_token);
 		wpa_s->sme.sae_token = NULL;
-		if (!external)
+		if (!external) {
 			sme_send_authentication(wpa_s, wpa_s->current_bss,
 						wpa_s->current_ssid, 0);
-		else
+		} else {
+			if (wpa_s->sme.ext_ml_auth &&
+			    sme_external_ml_auth(wpa_s, data, len, *ie_offset))
+				return -1;
+
 			sme_external_auth_send_sae_confirm(wpa_s, sa);
+		}
 		return 0;
 	} else if (auth_transaction == 2) {
 		if (status_code != WLAN_STATUS_SUCCESS)
@@ -1441,8 +1852,13 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
 		if (wpa_s->sme.sae.state != SAE_CONFIRMED)
 			return -1;
-		if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
+		if (sae_check_confirm(&wpa_s->sme.sae, data, len,
+				      ie_offset) < 0)
 			return -1;
+		if (external && wpa_s->sme.ext_ml_auth &&
+		    sme_external_ml_auth(wpa_s, data, len, *ie_offset))
+			return -1;
+
 		wpa_s->sme.sae.state = SAE_ACCEPTED;
 		sae_clear_temp_data(&wpa_s->sme.sae);
 
@@ -1463,7 +1879,7 @@
 {
 	wpa_printf(MSG_DEBUG,
 		   "SME: SAE completed - setting PMK for 4-way handshake");
-	wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+	wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, wpa_s->sme.sae.pmk_len,
 		       wpa_s->sme.sae.pmkid, bssid);
 	if (wpa_s->conf->sae_pmkid_in_assoc) {
 		/* Update the own RSNE contents now that we have set the PMK
@@ -1508,12 +1924,13 @@
 
 	if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) {
 		int res;
+		int ie_offset = 0;
 
 		res = sme_sae_auth(
 			wpa_s, le_to_host16(header->u.auth.auth_transaction),
 			le_to_host16(header->u.auth.status_code),
 			header->u.auth.variable,
-			len - auth_length, 1, header->sa);
+			len - auth_length, 1, header->sa, &ie_offset);
 		if (res < 0) {
 			/* Notify failure to the driver */
 			sme_send_external_auth_status(
@@ -1526,7 +1943,10 @@
 		if (res != 1)
 			return;
 
-		if (sme_sae_set_pmk(wpa_s, wpa_s->sme.ext_auth_bssid) < 0)
+		if (sme_sae_set_pmk(wpa_s,
+				    wpa_s->sme.ext_ml_auth ?
+				    wpa_s->sme.ext_auth_ap_mld_addr :
+				    wpa_s->sme.ext_auth_bssid) < 0)
 			return;
 	}
 }
@@ -1537,6 +1957,7 @@
 void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	int ie_offset = 0;
 
 	if (ssid == NULL) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
@@ -1550,7 +1971,9 @@
 		return;
 	}
 
-	if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
+	if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0 &&
+	    !(wpa_s->valid_links &&
+	      os_memcmp(wpa_s->ap_mld_addr, data->auth.peer, ETH_ALEN) == 0)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
 			"unexpected peer " MACSTR,
 			MAC2STR(data->auth.peer));
@@ -1568,10 +1991,13 @@
 
 #ifdef CONFIG_SAE
 	if (data->auth.auth_type == WLAN_AUTH_SAE) {
+		const u8 *addr = wpa_s->pending_bssid;
 		int res;
+
 		res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
 				   data->auth.status_code, data->auth.ies,
-				   data->auth.ies_len, 0, data->auth.peer);
+				   data->auth.ies_len, 0, data->auth.peer,
+				   &ie_offset);
 		if (res < 0) {
 			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -1580,7 +2006,10 @@
 		if (res != 1)
 			return;
 
-		if (sme_sae_set_pmk(wpa_s, wpa_s->pending_bssid) < 0)
+		if (wpa_s->valid_links)
+			addr = wpa_s->ap_mld_addr;
+
+		if (sme_sae_set_pmk(wpa_s, addr) < 0)
 			return;
 	}
 #endif /* CONFIG_SAE */
@@ -1708,6 +2137,11 @@
 	}
 #endif /* CONFIG_FILS */
 
+	/* TODO: Support additional auth_type values as well */
+	if (data->auth.auth_type == WLAN_AUTH_OPEN ||
+	    data->auth.auth_type == WLAN_AUTH_SAE)
+		wpas_sme_ml_auth(wpa_s, data, ie_offset);
+
 	sme_associate(wpa_s, ssid->mode, data->auth.peer,
 		      data->auth.auth_type);
 }
@@ -1997,6 +2431,7 @@
 #ifdef CONFIG_HE_OVERRIDES
 	wpa_supplicant_apply_he_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HE_OVERRIDES */
+	wpa_supplicant_apply_eht_overrides(wpa_s, ssid, &params);
 #ifdef CONFIG_IEEE80211R
 	if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
 	    get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len,
@@ -2125,6 +2560,31 @@
 	else
 		params.uapsd = -1;
 
+	if (wpa_s->valid_links) {
+		unsigned int i;
+
+		wpa_printf(MSG_DEBUG,
+			   "MLD: In association. assoc_link_id=%u, valid_links=0x%x",
+			   wpa_s->mlo_assoc_link_id, wpa_s->valid_links);
+
+		params.mld_params.mld_addr = wpa_s->ap_mld_addr;
+		params.mld_params.valid_links = wpa_s->valid_links;
+		params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(wpa_s->valid_links & BIT(i)))
+				continue;
+
+			params.mld_params.mld_links[i].bssid =
+				wpa_s->links[i].bssid;
+			params.mld_params.mld_links[i].freq =
+				wpa_s->links[i].freq;
+
+			wpa_printf(MSG_DEBUG, "MLD: id=%u, freq=%d, " MACSTR,
+				   i, wpa_s->links[i].freq,
+				   MAC2STR(wpa_s->links[i].bssid));
+		}
+	}
+
 	if (wpa_drv_associate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
 			"driver failed");
@@ -2222,6 +2682,34 @@
 	}
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_DPP
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+	    !data->assoc_reject.timed_out &&
+	    data->assoc_reject.status_code == WLAN_STATUS_INVALID_PMKID) {
+		struct rsn_pmksa_cache_entry *pmksa;
+
+		pmksa = pmksa_cache_get_current(wpa_s->wpa);
+		if (pmksa) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"DPP: Drop PMKSA cache entry for the BSS due to invalid PMKID report");
+			wpa_sm_pmksa_cache_remove(wpa_s->wpa, pmksa);
+		}
+		wpa_sm_aborted_cached(wpa_s->wpa);
+		if (wpa_s->current_bss) {
+			struct wpa_bss *bss = wpa_s->current_bss;
+			struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"DPP: Try network introduction again");
+			wpas_connect_work_done(wpa_s);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			wpa_supplicant_connect(wpa_s, bss, ssid);
+			return;
+		}
+	}
+#endif /* CONFIG_DPP */
+
 	/*
 	 * For now, unconditionally terminate the previous authentication. In
 	 * theory, this should not be needed, but mac80211 gets quite confused
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index da69a87..4eab335 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -1,5 +1,5 @@
 [Unit]
-Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Description=WPA supplicant daemon (for interface %I using nl80211)
 Requires=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device
 Before=network.target
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 55d2b9c..b0d610f 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -1,5 +1,5 @@
 [Unit]
-Description=WPA supplicant daemon (interface-specific version)
+Description=WPA supplicant daemon (for interface %I)
 Requires=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device
 Before=network.target
diff --git a/wpa_supplicant/utils/log2pcap.py b/wpa_supplicant/utils/log2pcap.py
index 141aecb..9a3f08d 100755
--- a/wpa_supplicant/utils/log2pcap.py
+++ b/wpa_supplicant/utils/log2pcap.py
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
-# Copyright (c) 2012, Intel Corporation
+# Copyright (c) 2012-2022, Intel Corporation
 #
 # Author: Johannes Berg <johannes@sipsolutions.net>
 #
@@ -8,6 +8,7 @@
 # See README for more details.
 
 import sys, struct, re
+from binascii import unhexlify
 
 def write_pcap_header(pcap_file):
     pcap_file.write(
@@ -32,7 +33,7 @@
         sys.exit(2)
 
     input_file = open(input, 'r')
-    pcap_file = open(pcap, 'w')
+    pcap_file = open(pcap, 'wb')
     frame_re = re.compile(r'(([0-9]+.[0-9]{6}):\s*)?nl80211: MLME event frame - hexdump\(len=[0-9]*\):((\s*[0-9a-fA-F]{2})*)')
 
     write_pcap_header(pcap_file)
@@ -47,7 +48,7 @@
             ts = 0
         hexdata = m.group(3)
         hexdata = hexdata.split()
-        data = ''.join([chr(int(x, 16)) for x in hexdata])
+        data = unhexlify("".join(hexdata))
         pcap_addpacket(pcap_file, ts, data)
 
     input_file.close()
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 4c7e6dc..72a119d 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -524,6 +524,11 @@
 		rep->mul_bssid->subelem_len = elen - 1;
 		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
 		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Unsupported neighbor report subelement id %u",
+			   id);
+		break;
 	}
 }
 
@@ -921,7 +926,8 @@
 {
 	const u8 *ie;
 	u8 op_class, chan;
-	int sec_chan = 0, vht = 0;
+	int sec_chan = 0;
+	enum oper_chan_width vht = CONF_OPER_CHWIDTH_USE_HT;
 	enum phy_type phy_type;
 	u32 info;
 	struct ieee80211_ht_operation *ht_oper = NULL;
@@ -1457,15 +1463,22 @@
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
 		char url[256];
+		u8 url_len;
 
-		if (end - pos < 1 || 1 + pos[0] > end - pos) {
+		if (end - pos < 1) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
 				   "Management Request (URL)");
 			return;
 		}
-		os_memcpy(url, pos + 1, pos[0]);
-		url[pos[0]] = '\0';
-		pos += 1 + pos[0];
+		url_len = *pos++;
+		if (url_len > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Invalid BSS Transition Management Request (URL truncated)");
+			return;
+		}
+		os_memcpy(url, pos, url_len);
+		url[url_len] = '\0';
+		pos += url_len;
 
 		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
 			wpa_sm_pmf_enabled(wpa_s->wpa),
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 0e2315d..f808ac4 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -413,6 +413,18 @@
 }
 
 
+static int wpa_cli_cmd_mlo_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "MLO_STATUS");
+}
+
+
+static int wpa_cli_cmd_mlo_signal_poll(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "MLO_SIGNAL_POLL");
+}
+
+
 static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -491,7 +503,7 @@
 		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
 		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
-		"sae_groups", "dtim_period", "beacon_int",
+		"sae_check_mfp", "sae_groups", "dtim_period", "beacon_int",
 		"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
 		"scan_cur_freq", "scan_res_valid_for_connect",
 		"sched_scan_interval",
@@ -590,6 +602,7 @@
 		"go_venue_group", "go_venue_type",
 		"wps_nfc_dev_pw_id", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+		"sae_check_mfp",
 		"dtim_period", "beacon_int", "ignore_old_scan_res",
 		"scan_cur_freq", "scan_res_valid_for_connect",
 		"sched_scan_interval",
@@ -1467,12 +1480,14 @@
 #ifdef CONFIG_HE_OVERRIDES
 	"disable_he",
 #endif /* CONFIG_HE_OVERRIDES */
+	"disable_eht",
 	"ap_max_inactivity", "dtim_period", "beacon_int",
 #ifdef CONFIG_MACSEC
 	"macsec_policy",
 	"macsec_integ_only",
 	"macsec_replay_protect",
 	"macsec_replay_window",
+	"macsec_offload",
 	"macsec_port",
 	"mka_priority",
 #endif /* CONFIG_MACSEC */
@@ -3134,7 +3149,7 @@
 static int wpa_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
 					    char *argv[])
 {
-	return wpa_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
+	return wpa_cli_cmd(ctrl, "DPP_CONTROLLER_START", 0, argc, argv);
 }
 
 
@@ -3159,6 +3174,15 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+static int wpa_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_PUSH_BUTTON", 0, argc, argv);
+}
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 
 
@@ -3239,19 +3263,18 @@
 
 #ifdef CONFIG_PASN
 
-static int wpa_cli_cmd_pasn_auth_start(struct wpa_ctrl *ctrl, int argc,
-				       char *argv[])
+static int wpa_cli_cmd_pasn_start(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	return wpa_cli_cmd(ctrl, "PASN_AUTH_START", 4, argc, argv);
+	return wpa_cli_cmd(ctrl, "PASN_START", 4, argc, argv);
 }
 
 
-static int wpa_cli_cmd_pasn_auth_stop(struct wpa_ctrl *ctrl, int argc,
-				      char *argv[])
+static int wpa_cli_cmd_pasn_stop(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	return wpa_cli_cmd(ctrl, "PASN_AUTH_STOP", 0, argc, argv);
+	return wpa_cli_cmd(ctrl, "PASN_STOP", 0, argc, argv);
 }
 
+
 static int wpa_cli_cmd_ptksa_cache_list(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
@@ -3994,14 +4017,19 @@
 	  cli_cmd_flag_none,
 	  "= stop DPP chirp" },
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	{ "dpp_push_button", wpa_cli_cmd_dpp_push_button, NULL,
+	  cli_cmd_flag_none,
+	  "= press DPP push button" },
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 	{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
 	  "= list all BSS entries (scan results)" },
 #ifdef CONFIG_PASN
-	{ "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
+	{ "pasn_start", wpa_cli_cmd_pasn_start, NULL,
 	  cli_cmd_flag_none,
 	  "bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> nid=<network id> = Start PASN authentication" },
-	{ "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
+	{ "pasn_stop", wpa_cli_cmd_pasn_stop, NULL,
 	  cli_cmd_flag_none,
 	  "= Stop PASN authentication" },
 	{ "ptksa_cache_list", wpa_cli_cmd_ptksa_cache_list, NULL,
@@ -4023,6 +4051,12 @@
 	{ "dscp_query", wpa_cli_cmd_dscp_query, NULL,
 	  cli_cmd_flag_none,
 	  "wildcard/domain_name=<string> = Send DSCP Query" },
+	{ "mlo_status", wpa_cli_cmd_mlo_status, NULL,
+	  cli_cmd_flag_none,
+	  "= get MLO status" },
+	{ "mlo_signal_poll", wpa_cli_cmd_mlo_signal_poll, NULL,
+	  cli_cmd_flag_none,
+	  "= get mlo signal parameters" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index d9c07e6..cfab4f1 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -7,6 +7,7 @@
  */
 
 #include "includes.h"
+#include <termios.h>
 
 #include "common.h"
 #include "crypto/sha1.h"
@@ -14,6 +15,7 @@
 
 int main(int argc, char *argv[])
 {
+	struct termios term;
 	unsigned char psk[32];
 	int i;
 	char *ssid, *passphrase, buf[64], *pos;
@@ -31,11 +33,28 @@
 	if (argc > 2) {
 		passphrase = argv[2];
 	} else {
+		bool ctrl_echo;
+
 		fprintf(stderr, "# reading passphrase from stdin\n");
+		if (tcgetattr(STDIN_FILENO, &term) < 0) {
+			perror("tcgetattr");
+			return 1;
+		}
+		ctrl_echo = term.c_lflag & ECHO;
+		term.c_lflag &= ~ECHO;
+		if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+			perror("tcsetattr:error disabling echo");
+			return 1;
+		}
 		if (fgets(buf, sizeof(buf), stdin) == NULL) {
 			fprintf(stderr, "Failed to read passphrase\n");
 			return 1;
 		}
+		term.c_lflag |= ECHO;
+		if (ctrl_echo && tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0) {
+			perror("tcsetattr:error enabling echo");
+			return 1;
+		}
 		buf[sizeof(buf) - 1] = '\0';
 		pos = buf;
 		while (*pos != '\0') {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index c5d7168..31a9af6 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -414,6 +414,7 @@
 	p.key = params->key_len ? params->key : NULL;
 	p.key_len = params->key_len;
 	p.key_flag = params->key_flag;
+	p.link_id = -1;
 
 	res = iface->driver->set_key(iface->drv_priv, &p);
 	wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
@@ -1134,7 +1135,8 @@
 
 
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
-			     const u8 *buf, size_t len)
+			     const u8 *buf, size_t len,
+			     enum frame_encryption encrypted)
 {
 	struct wpa_priv_interface *iface = ctx;
 	struct msghdr msg;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index ccf945d..1344ef6 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -17,6 +17,7 @@
 #endif /* CONFIG_MATCH_IFACE */
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/sha1.h"
 #include "eapol_supp/eapol_supp_sm.h"
@@ -68,6 +69,7 @@
 #include "ap/ap_config.h"
 #include "ap/hostapd.h"
 #endif /* CONFIG_MESH */
+#include "aidl/aidl.h"
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -142,7 +144,7 @@
 			continue;
 
 		set = 1;
-		wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+		wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP, NULL,
 				i, i == ssid->wep_tx_keyidx, NULL, 0,
 				ssid->wep_key[i], ssid->wep_key_len[i],
 				i == ssid->wep_tx_keyidx ?
@@ -206,7 +208,7 @@
 	/* TODO: should actually remember the previously used seq#, both for TX
 	 * and RX from each STA.. */
 
-	ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen,
+	ret = wpa_drv_set_key(wpa_s, -1, alg, NULL, 0, 1, seq, 6, key, keylen,
 			      KEY_FLAG_GROUP_RX_TX_DEFAULT);
 	os_memset(key, 0, sizeof(key));
 	return ret;
@@ -403,6 +405,7 @@
 #ifdef CONFIG_WEP
 	int i;
 #endif /* CONFIG_WEP */
+	struct wpa_sm_mlo mlo;
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
 		wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
@@ -443,6 +446,8 @@
 			 wpa_s->mgmt_group_cipher);
 
 	pmksa_cache_clear_current(wpa_s->wpa);
+	os_memset(&mlo, 0, sizeof(mlo));
+	wpa_sm_set_mlo_params(wpa_s->wpa, &mlo);
 }
 
 
@@ -765,18 +770,18 @@
 	for (i = 0; i < max; i++) {
 		if (wpa_s->keys_cleared & BIT(i))
 			continue;
-		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+		wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
 				NULL, 0, KEY_FLAG_GROUP);
 	}
 	/* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */
 	if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr &&
 	    !is_zero_ether_addr(addr)) {
 		if (!(wpa_s->keys_cleared & BIT(0)))
-			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
-					0, NULL, 0, KEY_FLAG_PAIRWISE);
+			wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
+					NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 		if (!(wpa_s->keys_cleared & BIT(15)))
-			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
-					0, NULL, 0, KEY_FLAG_PAIRWISE);
+			wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 1, 0,
+					NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 		/* MLME-SETPROTECTION.request(None) */
 		wpa_drv_mlme_setprotection(
 			wpa_s, addr,
@@ -985,6 +990,13 @@
 	if (state == WPA_COMPLETED && wpa_s->new_connection) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		int fils_hlp_sent = 0;
+		char mld_addr[50];
+
+		mld_addr[0] = '\0';
+		if (wpa_s->valid_links)
+			os_snprintf(mld_addr, sizeof(mld_addr),
+				    " ap_mld_addr=" MACSTR,
+				    MAC2STR(wpa_s->ap_mld_addr));
 
 #ifdef CONFIG_SME
 		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
@@ -997,11 +1009,11 @@
 
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-			MACSTR " completed [id=%d id_str=%s%s]",
+			MACSTR " completed [id=%d id_str=%s%s]%s",
 			MAC2STR(wpa_s->bssid),
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "",
-			fils_hlp_sent ? " FILS_HLP_SENT" : "");
+			fils_hlp_sent ? " FILS_HLP_SENT" : "", mld_addr);
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_s->consecutive_conn_failures = 0;
@@ -1122,6 +1134,7 @@
 	wpa_s->group_cipher = 0;
 	wpa_s->mgmt_group_cipher = 0;
 	wpa_s->key_mgmt = 0;
+	wpa_s->allowed_key_mgmts = 0;
 	if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
 		wpa_supplicant_set_state(wpa_s, new_state);
 
@@ -1149,14 +1162,14 @@
 
 	if (wpa_s->confname == NULL)
 		return -1;
-	conf = wpa_config_read(wpa_s->confname, NULL);
+	conf = wpa_config_read(wpa_s->confname, NULL, false);
 	if (conf == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
 			"file '%s' - exiting", wpa_s->confname);
 		return -1;
 	}
 	if (wpa_s->confanother &&
-	    !wpa_config_read(wpa_s->confanother, conf)) {
+	    !wpa_config_read(wpa_s->confanother, conf, true)) {
 		wpa_msg(wpa_s, MSG_ERROR,
 			"Failed to parse the configuration file '%s' - exiting",
 			wpa_s->confanother);
@@ -1341,6 +1354,214 @@
 			 wpas_get_ssid_pmf(wpa_s, ssid));
 }
 
+/**
+ * wpa_supplicant_get_psk - Get PSK from config or external database
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @psk: Buffer for the PSK
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function obtains the PSK for a network, either included inline in the
+ * config or retrieved from an external database.
+ */
+static int wpa_supplicant_get_psk(struct wpa_supplicant *wpa_s,
+				  struct wpa_bss *bss, struct wpa_ssid *ssid,
+				  u8 *psk)
+{
+	if (ssid->psk_set) {
+		wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
+				ssid->psk, PMK_LEN);
+		os_memcpy(psk, ssid->psk, PMK_LEN);
+		return 0;
+	}
+
+#ifndef CONFIG_NO_PBKDF2
+	if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) {
+		if (pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+				4096, psk, PMK_LEN) != 0) {
+			wpa_msg(wpa_s, MSG_WARNING, "Error in pbkdf2_sha1()");
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+				psk, PMK_LEN);
+		return 0;
+	}
+#endif /* CONFIG_NO_PBKDF2 */
+
+#ifdef CONFIG_EXT_PASSWORD
+	if (ssid->ext_psk) {
+		struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+						     ssid->ext_psk);
+		char pw_str[64 + 1];
+
+		if (!pw) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"EXT PW: No PSK found from external storage");
+			return -1;
+		}
+
+		if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"EXT PW: Unexpected PSK length %d in external storage",
+				(int) wpabuf_len(pw));
+			ext_password_free(pw);
+			return -1;
+		}
+
+		os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+		pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+		if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+		{
+			if (pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+					4096, psk, PMK_LEN) != 0) {
+				wpa_msg(wpa_s, MSG_WARNING,
+					"Error in pbkdf2_sha1()");
+				forced_memzero(pw_str, sizeof(pw_str));
+				ext_password_free(pw);
+				return -1;
+			}
+			wpa_hexdump_key(MSG_MSGDUMP,
+					"PSK (from external passphrase)",
+					psk, PMK_LEN);
+		} else
+#endif /* CONFIG_NO_PBKDF2 */
+		if (wpabuf_len(pw) == 2 * PMK_LEN) {
+			if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+				wpa_msg(wpa_s, MSG_INFO,
+					"EXT PW: Invalid PSK hex string");
+				forced_memzero(pw_str, sizeof(pw_str));
+				ext_password_free(pw);
+				return -1;
+			}
+			wpa_hexdump_key(MSG_MSGDUMP, "PSK (from external PSK)",
+					psk, PMK_LEN);
+		} else {
+			wpa_msg(wpa_s, MSG_INFO,
+				"EXT PW: No suitable PSK available");
+			forced_memzero(pw_str, sizeof(pw_str));
+			ext_password_free(pw);
+			return -1;
+		}
+
+		forced_memzero(pw_str, sizeof(pw_str));
+		ext_password_free(pw);
+
+		return 0;
+	}
+#endif /* CONFIG_EXT_PASSWORD */
+
+	return -1;
+}
+
+
+static void wpas_update_allowed_key_mgmt(struct wpa_supplicant *wpa_s,
+					 struct wpa_ssid *ssid)
+{
+	int akm_count = wpa_s->max_num_akms;
+	u8 capab = 0;
+
+	if (akm_count < 2)
+		return;
+
+	akm_count--;
+	wpa_s->allowed_key_mgmts = 0;
+	switch (wpa_s->key_mgmt) {
+	case WPA_KEY_MGMT_PSK:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	case WPA_KEY_MGMT_PSK_SHA256:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		break;
+	case WPA_KEY_MGMT_SAE:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE_EXT_KEY;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	case WPA_KEY_MGMT_SAE_EXT_KEY:
+		if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_SAE;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+			akm_count--;
+			wpa_s->allowed_key_mgmts |= WPA_KEY_MGMT_PSK;
+		}
+		if (!akm_count)
+			break;
+		if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			wpa_s->allowed_key_mgmts |=
+				WPA_KEY_MGMT_PSK_SHA256;
+		break;
+	default:
+		return;
+	}
+
+	if (wpa_s->conf->sae_pwe != SAE_PWE_HUNT_AND_PECK &&
+	    wpa_s->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+	if (ssid->sae_pk)
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+
+	if (!((wpa_s->allowed_key_mgmts &
+	       (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY)) && capab))
+		return;
+
+	if (!wpa_s->rsnxe_len) {
+		wpa_s->rsnxe_len = 3;
+		wpa_s->rsnxe[0] = WLAN_EID_RSNX;
+		wpa_s->rsnxe[1] = 1;
+		wpa_s->rsnxe[2] = 0;
+	}
+
+	wpa_s->rsnxe[2] |= capab;
+}
+
 
 /**
  * wpa_supplicant_set_suites - Set authentication and encryption parameters
@@ -1350,6 +1571,7 @@
  * @wpa_ie: Buffer for the WPA/RSN IE
  * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
  * used buffer length in case the functions returns success.
+ * @skip_default_rsne: Whether to skip setting of the default RSNE/RSNXE
  * Returns: 0 on success or -1 on failure
  *
  * This function is used to configure authentication and encryption parameters
@@ -1358,10 +1580,12 @@
  */
 int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *bss, struct wpa_ssid *ssid,
-			      u8 *wpa_ie, size_t *wpa_ie_len)
+			      u8 *wpa_ie, size_t *wpa_ie_len,
+			      bool skip_default_rsne)
 {
 	struct wpa_ie_data ie;
-	int sel, proto, sae_pwe;
+	int sel, proto;
+	enum sae_pwe sae_pwe;
 	const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
 
 	if (bss) {
@@ -1541,8 +1765,10 @@
 
 	sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
-		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) ||
+	    wpas_is_sae_avoided(wpa_s, ssid, &ie))
+		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_SAE_EXT_KEY |
+			 WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_FT_SAE_EXT_KEY);
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_IEEE80211R
 	if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME |
@@ -1587,13 +1813,15 @@
 	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
-	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
-		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
 #endif /* CONFIG_IEEE80211R */
 	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+#ifdef CONFIG_IEEE80211R
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
 	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
@@ -1618,6 +1846,13 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
 #endif /* CONFIG_DPP */
 #ifdef CONFIG_SAE
+	} else if (sel & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RSN: using KEY_MGMT FT/SAE (ext key)");
+	} else if (sel & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE (ext key)");
 	} else if (sel & WPA_KEY_MGMT_FT_SAE) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
@@ -1669,7 +1904,8 @@
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
 
 	if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
-	    wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+	    (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED ||
+	     (bss && is_6ghz_freq(bss->freq)))) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"RSN: Management frame protection required but the selected AP does not enable it");
 		return -1;
@@ -1682,18 +1918,16 @@
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
 #endif /* CONFIG_OCV */
 	sae_pwe = wpa_s->conf->sae_pwe;
-	if (ssid->sae_password_id && sae_pwe != 3)
-		sae_pwe = 1;
-	if (bss && is_6ghz_freq(bss->freq)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hash-to-element mode for 6GHz BSS.");
-		sae_pwe = 1;
+	if ((ssid->sae_password_id ||
+	     wpa_key_mgmt_sae_ext_key(wpa_s->key_mgmt)) &&
+	    sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	if (bss && is_6ghz_freq(bss->freq) &&
+	    sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"RSN: Enable SAE hash-to-element mode for 6 GHz BSS");
+		sae_pwe = SAE_PWE_BOTH;
 	}
-#ifdef CONFIG_TESTING_OPTIONS
-	if (wpa_s->force_hunting_and_pecking_pwe) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: force hunting and pecking mode.");
-		sae_pwe = 0;
-	}
-#endif
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
 #ifdef CONFIG_SAE_PK
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
@@ -1704,6 +1938,12 @@
 			  (!ssid->sae_password && ssid->passphrase &&
 			   sae_pk_valid_password(ssid->passphrase))));
 #endif /* CONFIG_SAE_PK */
+	if (bss && is_6ghz_freq(bss->freq) &&
+	    wpas_get_ssid_pmf(wpa_s, ssid) != MGMT_FRAME_PROTECTION_REQUIRED) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Force MFPR=1 on 6 GHz");
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+				 MGMT_FRAME_PROTECTION_REQUIRED);
+	}
 #ifdef CONFIG_TESTING_OPTIONS
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
 			 wpa_s->ft_rsnxe_used);
@@ -1743,16 +1983,21 @@
 		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0);
 	}
 
-	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
-		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
-		return -1;
-	}
+	if (!skip_default_rsne) {
+		if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
+						    wpa_ie_len)) {
+			wpa_msg(wpa_s, MSG_WARNING,
+				"RSN: Failed to generate RSNE/WPA IE");
+			return -1;
+		}
 
-	wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
-	if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
-					   &wpa_s->rsnxe_len)) {
-		wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
-		return -1;
+		wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
+		if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
+						   &wpa_s->rsnxe_len)) {
+			wpa_msg(wpa_s, MSG_WARNING,
+				"RSN: Failed to generate RSNXE");
+			return -1;
+		}
 	}
 
 	if (0) {
@@ -1766,120 +2011,27 @@
 #endif /* CONFIG_DPP */
 	} else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
 		int psk_set = 0;
-		int sae_only;
 
-		sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK |
-					      WPA_KEY_MGMT_FT_PSK |
-					      WPA_KEY_MGMT_PSK_SHA256)) == 0;
+		if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) {
+			u8 psk[PMK_LEN];
 
-		if (ssid->psk_set && !sae_only) {
-			wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
-					ssid->psk, PMK_LEN);
-			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
-				       NULL);
-			psk_set = 1;
+			if (wpa_supplicant_get_psk(wpa_s, bss, ssid,
+						   psk) == 0) {
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+					       NULL);
+				psk_set = 1;
+			}
+			forced_memzero(psk, sizeof(psk));
 		}
 
 		if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
-		    (ssid->sae_password || ssid->passphrase))
+		    (ssid->sae_password || ssid->passphrase || ssid->ext_psk))
 			psk_set = 1;
 
-#ifndef CONFIG_NO_PBKDF2
-		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
-		    ssid->passphrase && !sae_only) {
-			u8 psk[PMK_LEN];
-
-			if (pbkdf2_sha1(ssid->passphrase, bss->ssid,
-					bss->ssid_len,
-					4096, psk, PMK_LEN) != 0) {
-				wpa_msg(wpa_s, MSG_WARNING,
-					"Error in pbkdf2_sha1()");
-				return -1;
-			}
-			wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
-					psk, PMK_LEN);
-			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
-			psk_set = 1;
-			os_memset(psk, 0, sizeof(psk));
-		}
-#endif /* CONFIG_NO_PBKDF2 */
-#ifdef CONFIG_EXT_PASSWORD
-		if (ssid->ext_psk && !sae_only) {
-			struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
-							     ssid->ext_psk);
-			char pw_str[64 + 1];
-			u8 psk[PMK_LEN];
-
-			if (pw == NULL) {
-				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
-					"found from external storage");
-				return -1;
-			}
-
-			if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
-				wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
-					"PSK length %d in external storage",
-					(int) wpabuf_len(pw));
-				ext_password_free(pw);
-				return -1;
-			}
-
-			os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
-			pw_str[wpabuf_len(pw)] = '\0';
-
-#ifndef CONFIG_NO_PBKDF2
-			if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
-			{
-				if (pbkdf2_sha1(pw_str, bss->ssid,
-						bss->ssid_len,
-						4096, psk, PMK_LEN) != 0) {
-					wpa_msg(wpa_s, MSG_WARNING,
-						"Error in pbkdf2_sha1()");
-					ext_password_free(pw);
-					return -1;
-				}
-				os_memset(pw_str, 0, sizeof(pw_str));
-				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
-						"external passphrase)",
-						psk, PMK_LEN);
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
-					       NULL);
-				psk_set = 1;
-				os_memset(psk, 0, sizeof(psk));
-			} else
-#endif /* CONFIG_NO_PBKDF2 */
-			if (wpabuf_len(pw) == 2 * PMK_LEN) {
-				if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
-					wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
-						"Invalid PSK hex string");
-					os_memset(pw_str, 0, sizeof(pw_str));
-					ext_password_free(pw);
-					return -1;
-				}
-				wpa_hexdump_key(MSG_MSGDUMP,
-						"PSK (from external PSK)",
-						psk, PMK_LEN);
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
-					       NULL);
-				psk_set = 1;
-				os_memset(psk, 0, sizeof(psk));
-			} else {
-				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
-					"PSK available");
-				os_memset(pw_str, 0, sizeof(pw_str));
-				ext_password_free(pw);
-				return -1;
-			}
-
-			os_memset(pw_str, 0, sizeof(pw_str));
-			ext_password_free(pw);
-		}
-#endif /* CONFIG_EXT_PASSWORD */
-
 		if (!psk_set) {
 			wpa_msg(wpa_s, MSG_INFO,
 				"No PSK available for association");
-			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
+			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", NULL);
 			return -1;
 		}
 #ifdef CONFIG_OWE
@@ -1915,6 +2067,10 @@
 		wpa_dbg(wpa_s, MSG_INFO,
 			"WPA: Updating to KEY_MGMT SAE+PSK for seamless roaming");
 	}
+#else
+	if (wpa_key_mgmt_cross_akm(wpa_s->key_mgmt) &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		wpas_update_allowed_key_mgmt(wpa_s, ssid);
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 	return 0;
@@ -2098,31 +2254,53 @@
 }
 
 
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s,
+			    enum wpas_mac_addr_style style,
+			    struct wpa_ssid *ssid)
 {
 	struct os_reltime now;
 	u8 addr[ETH_ALEN];
 
 	os_get_reltime(&now);
-	if (wpa_s->last_mac_addr_style == style &&
-	    wpa_s->last_mac_addr_change.sec != 0 &&
-	    !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
-				wpa_s->conf->rand_addr_lifetime)) {
-		wpa_msg(wpa_s, MSG_DEBUG,
-			"Previously selected random MAC address has not yet expired");
-		return 0;
+	/* Random addresses are valid within a given ESS so check
+	 * expiration/value only when continuing to use the same ESS. */
+	if (wpa_s->last_mac_addr_style == style && wpa_s->reassoc_same_ess) {
+		if (style == WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS) {
+			/* Pregenerated addresses do not expire but their value
+			 * might have changed, so let's check that. */
+			if (os_memcmp(wpa_s->own_addr, ssid->mac_value,
+				      ETH_ALEN) == 0)
+				return 0;
+		} else if ((wpa_s->last_mac_addr_change.sec != 0 ||
+			    wpa_s->last_mac_addr_change.usec != 0) &&
+			   !os_reltime_expired(
+				   &now,
+				   &wpa_s->last_mac_addr_change,
+				   wpa_s->conf->rand_addr_lifetime)) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Previously selected random MAC address has not yet expired");
+			return 0;
+		}
 	}
 
 	switch (style) {
-	case 1:
+	case WPAS_MAC_ADDR_STYLE_RANDOM:
 		if (random_mac_addr(addr) < 0)
 			return -1;
 		break;
-	case 2:
+	case WPAS_MAC_ADDR_STYLE_RANDOM_SAME_OUI:
 		os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
 		if (random_mac_addr_keep_oui(addr) < 0)
 			return -1;
 		break;
+	case WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS:
+		if (!ssid) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Invalid 'ssid' for address policy 3");
+			return -1;
+		}
+		os_memcpy(addr, ssid->mac_value, ETH_ALEN);
+		break;
 	default:
 		return -1;
 	}
@@ -2146,7 +2324,7 @@
 	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
 		MAC2STR(addr));
 
-	return 0;
+	return 1;
 }
 
 
@@ -2156,11 +2334,13 @@
 	    !wpa_s->conf->preassoc_mac_addr)
 		return 0;
 
-	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+	return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr,
+				       NULL);
 }
 
 
-static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid)
+void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+			bool force)
 {
 #ifdef CONFIG_SAE
 	int *groups = conf->sae_groups;
@@ -2175,9 +2355,11 @@
 		password = ssid->passphrase;
 
 	if (!password ||
-	    (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+	    (conf->sae_pwe == SAE_PWE_HUNT_AND_PECK && !ssid->sae_password_id &&
+	     !wpa_key_mgmt_sae_ext_key(ssid->key_mgmt) &&
+	     !force &&
 	     !sae_pk_valid_password(password)) ||
-	    conf->sae_pwe == 3) {
+	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) {
 		/* PT derivation not needed */
 		sae_deinit_pt(ssid->pt);
 		ssid->pt = NULL;
@@ -2247,7 +2429,7 @@
 			      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
 	struct wpa_connect_work *cwork;
-	int rand_style;
+	enum wpas_mac_addr_style rand_style;
 
 	wpa_s->own_disconnect_req = 0;
 	wpa_s->own_reconnect_req = 0;
@@ -2259,7 +2441,7 @@
 	wpabuf_free(wpa_s->pending_eapol_rx);
 	wpa_s->pending_eapol_rx = NULL;
 
-	if (ssid->mac_addr == -1)
+	if (ssid->mac_addr == WPAS_MAC_ADDR_STYLE_NOT_SET)
 		rand_style = wpa_s->conf->mac_addr;
 	else
 		rand_style = ssid->mac_addr;
@@ -2288,14 +2470,19 @@
 #endif /* CONFIG_SAE */
 	}
 #ifdef CONFIG_SAE
-	wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+	wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
 #endif /* CONFIG_SAE */
 
-	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
-		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+	if (rand_style > WPAS_MAC_ADDR_STYLE_PERMANENT) {
+		int status = wpas_update_random_addr(wpa_s, rand_style, ssid);
+
+		if (status < 0)
 			return;
-		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-	} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
+		if (rand_style != WPAS_MAC_ADDR_STYLE_DEDICATED_PER_ESS &&
+		    status > 0) /* MAC changed */
+			wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+	} else if (rand_style == WPAS_MAC_ADDR_STYLE_PERMANENT &&
+		   wpa_s->mac_addr_changed) {
 		if (wpas_restore_permanent_mac_addr(wpa_s) < 0)
 			return;
 	}
@@ -2345,6 +2532,7 @@
 		if (bss)
 			ssid->frequency = bss->freq;
 		if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+			wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
 			wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
 			return;
 		}
@@ -2467,116 +2655,142 @@
 }
 
 
-void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
-			  const struct wpa_ssid *ssid,
-			  struct hostapd_freq_params *freq)
+static struct wpa_bss * ibss_find_existing_bss(struct wpa_supplicant *wpa_s,
+					       const struct wpa_ssid *ssid)
 {
-	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
-	enum hostapd_hw_mode hw_mode;
-	struct hostapd_hw_modes *mode = NULL;
-	int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
-			   184, 192 };
-	int bw80[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955,
-		       6035, 6115, 6195, 6275, 6355, 6435, 6515,
-		       6595, 6675, 6755, 6835, 6915, 6995 };
-	int bw160[] = { 5955, 6115, 6275, 6435, 6595, 6755, 6915 };
-	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
-	u8 channel;
-	int i, chan_idx, ht40 = -1, res, obss_scan = 1;
-	unsigned int j, k;
-	struct hostapd_freq_params vht_freq;
-	int chwidth, seg0, seg1;
-	u32 vht_caps = 0;
-	bool is_24ghz, is_6ghz;
-
-	freq->freq = ssid->frequency;
+	unsigned int j;
 
 	for (j = 0; j < wpa_s->last_scan_res_used; j++) {
 		struct wpa_bss *bss = wpa_s->last_scan_res[j];
 
-		if (ssid->mode != WPAS_MODE_IBSS)
-			break;
-
-		/* Don't adjust control freq in case of fixed_freq */
-		if (ssid->fixed_freq)
-			break;
-
 		if (!bss_is_ibss(bss))
 			continue;
 
 		if (ssid->ssid_len == bss->ssid_len &&
-		    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
-			wpa_printf(MSG_DEBUG,
-				   "IBSS already found in scan results, adjust control freq: %d",
-				   bss->freq);
-			freq->freq = bss->freq;
-			obss_scan = 0;
-			break;
-		}
+		    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0)
+			return bss;
 	}
+	return NULL;
+}
 
+
+static bool ibss_mesh_can_use_ht(struct wpa_supplicant *wpa_s,
+				 const struct wpa_ssid *ssid,
+				 struct hostapd_hw_modes *mode)
+{
 	/* For IBSS check HT_IBSS flag */
 	if (ssid->mode == WPAS_MODE_IBSS &&
 	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
-		return;
+		return false;
 
 	if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
 	    wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
 	    wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
 		wpa_printf(MSG_DEBUG,
 			   "IBSS: WEP/TKIP detected, do not try to enable HT");
-		return;
+		return false;
 	}
 
-	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
-	for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
-		if (wpa_s->hw.modes[i].mode == hw_mode) {
-			mode = &wpa_s->hw.modes[i];
-			break;
-		}
-	}
-
-	if (!mode)
-		return;
-
-	freq->channel = channel;
-
-	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
-		hw_mode == HOSTAPD_MODE_IEEE80211B;
-
-	/* HT/VHT and corresponding overrides are not applicable to 6 GHz.
-	 * However, HE is mandatory for 6 GHz.
-	 */
-	is_6ghz = is_6ghz_freq(freq->freq);
-	if (is_6ghz)
-		goto skip_to_6ghz;
+	if (!ht_supported(mode))
+		return false;
 
 #ifdef CONFIG_HT_OVERRIDES
-	if (ssid->disable_ht) {
-		freq->ht_enabled = 0;
-		return;
-	}
+	if (ssid->disable_ht)
+		return false;
 #endif /* CONFIG_HT_OVERRIDES */
 
-	freq->ht_enabled = ht_supported(mode);
-	if (!freq->ht_enabled)
-		return;
+	return true;
+}
 
-	/* Allow HE on 2.4 GHz without VHT: see nl80211_put_freq_params() */
-	if (is_24ghz)
-		freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported;
+
+static bool ibss_mesh_can_use_vht(struct wpa_supplicant *wpa_s,
+				  const struct wpa_ssid *ssid,
+				  struct hostapd_hw_modes *mode)
+{
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return false;
+
+	if (!drv_supports_vht(wpa_s, ssid))
+		return false;
+
+	/* For IBSS check VHT_IBSS flag */
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+		return false;
+
+	if (!vht_supported(mode))
+		return false;
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (ssid->disable_vht)
+		return false;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	return true;
+}
+
+
+static bool ibss_mesh_can_use_he(struct wpa_supplicant *wpa_s,
+				 const struct wpa_ssid *ssid,
+				 const struct hostapd_hw_modes *mode,
+				 int ieee80211_mode)
+{
 #ifdef CONFIG_HE_OVERRIDES
-	if (is_24ghz && ssid->disable_he)
-		freq->he_enabled = 0;
+	if (ssid->disable_he)
+		return false;
 #endif /* CONFIG_HE_OVERRIDES */
 
-	/* Setup higher BW only for 5 GHz */
-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+	switch (mode->mode) {
+	case HOSTAPD_MODE_IEEE80211G:
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211A:
+		return mode->he_capab[ieee80211_mode].he_supported;
+	default:
+		return false;
+	}
+}
+
+
+static bool ibss_mesh_can_use_eht(struct wpa_supplicant *wpa_s,
+				  const struct wpa_ssid *ssid,
+				  const struct hostapd_hw_modes *mode,
+				  int ieee80211_mode)
+{
+	if (ssid->disable_eht)
+		return false;
+
+	switch(mode->mode) {
+	case HOSTAPD_MODE_IEEE80211G:
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211A:
+		return mode->eht_capab[ieee80211_mode].eht_supported;
+	default:
+		return false;
+	}
+}
+
+
+static void ibss_mesh_select_40mhz(struct wpa_supplicant *wpa_s,
+				   const struct wpa_ssid *ssid,
+				   struct hostapd_hw_modes *mode,
+				   struct hostapd_freq_params *freq,
+				   int obss_scan) {
+	int chan_idx;
+	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+	int i, res;
+	unsigned int j;
+	static const int ht40plus[] = {
+		36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 165, 173,
+		184, 192
+	};
+	int ht40 = -1;
+
+	if (!freq->ht_enabled)
 		return;
 
 	for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
 		pri_chan = &mode->channels[chan_idx];
-		if (pri_chan->chan == channel)
+		if (pri_chan->chan == freq->channel)
 			break;
 		pri_chan = NULL;
 	}
@@ -2587,21 +2801,14 @@
 	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
 		return;
 
-	freq->channel = pri_chan->chan;
-
 #ifdef CONFIG_HT_OVERRIDES
-	if (ssid->disable_ht40) {
-#ifdef CONFIG_VHT_OVERRIDES
-		if (ssid->disable_vht)
-			return;
-#endif /* CONFIG_VHT_OVERRIDES */
-		goto skip_ht40;
-	}
-#endif /* CONFIG_HT_OVERRIDES */
+	if (ssid->disable_ht40)
+		return;
+#endif
 
 	/* Check/setup HT40+/HT40- */
 	for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
-		if (ht40plus[j] == channel) {
+		if (ht40plus[j] == freq->channel) {
 			ht40 = 1;
 			break;
 		}
@@ -2610,7 +2817,7 @@
 	/* Find secondary channel */
 	for (i = 0; i < mode->num_channels; i++) {
 		sec_chan = &mode->channels[i];
-		if (sec_chan->chan == channel + ht40 * 4)
+		if (sec_chan->chan == freq->channel + ht40 * 4)
 			break;
 		sec_chan = NULL;
 	}
@@ -2663,38 +2870,48 @@
 		wpa_scan_results_free(scan_res);
 	}
 
-#ifdef CONFIG_HT_OVERRIDES
-skip_ht40:
-#endif /* CONFIG_HT_OVERRIDES */
 	wpa_printf(MSG_DEBUG,
 		   "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
 		   freq->channel, freq->sec_channel_offset);
+}
 
-	if (!drv_supports_vht(wpa_s, ssid))
-		return;
 
-	/* For IBSS check VHT_IBSS flag */
-	if (ssid->mode == WPAS_MODE_IBSS &&
-	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
-		return;
+static bool ibss_mesh_select_80_160mhz(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid *ssid,
+				       struct hostapd_hw_modes *mode,
+				       struct hostapd_freq_params *freq,
+				       int ieee80211_mode, bool is_6ghz) {
+	static const int bw80[] = {
+		5180, 5260, 5500, 5580, 5660, 5745, 5825,
+		5955, 6035, 6115, 6195, 6275, 6355, 6435,
+		6515, 6595, 6675, 6755, 6835, 6915, 6995
+	};
+	static const int bw160[] = {
+		5955, 6115, 6275, 6435, 6595, 6755, 6915
+	};
+	struct hostapd_freq_params vht_freq;
+	int i;
+	unsigned int j, k;
+	int chwidth, seg0, seg1;
+	u32 vht_caps = 0;
+	u8 channel = freq->channel;
 
-#ifdef CONFIG_VHT_OVERRIDES
-	if (ssid->disable_vht) {
-		freq->vht_enabled = 0;
-		return;
-	}
-#endif /* CONFIG_VHT_OVERRIDES */
+	if (!freq->vht_enabled && !freq->he_enabled)
+		return true;
 
-skip_to_6ghz:
 	vht_freq = *freq;
 
-	/* 6 GHz does not have VHT enabled, so allow that exception here. */
-	vht_freq.vht_enabled = vht_supported(mode);
-	if (!vht_freq.vht_enabled && !is_6ghz)
-		return;
-
-	/* Enable HE with VHT for 5 GHz */
-	freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported;
+	chwidth = CONF_OPER_CHWIDTH_USE_HT;
+	seg0 = freq->channel + 2 * freq->sec_channel_offset;
+	seg1 = 0;
+	if (freq->sec_channel_offset == 0) {
+		seg0 = 0;
+		/* Don't try 80 MHz if 40 MHz failed, except in 6 GHz */
+		if (freq->ht_enabled && !is_6ghz)
+			goto skip_80mhz;
+	}
+	if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+		goto skip_80mhz;
 
 	/* setup center_freq1, bandwidth */
 	for (j = 0; j < ARRAY_SIZE(bw80); j++) {
@@ -2705,36 +2922,34 @@
 
 	if (j == ARRAY_SIZE(bw80) ||
 	    ieee80211_freq_to_chan(bw80[j], &channel) == NUM_HOSTAPD_MODES)
-		return;
+		goto skip_80mhz;
 
-	/* Back to HT configuration if channel not usable */
+	/* Use 40 MHz if channel not usable */
 	if (!ibss_mesh_is_80mhz_avail(channel, mode))
-		return;
+		goto skip_80mhz;
 
-	chwidth = CHANWIDTH_80MHZ;
+	chwidth = CONF_OPER_CHWIDTH_80MHZ;
 	seg0 = channel + 6;
 	seg1 = 0;
 
+	/* In 160 MHz, the initial four 20 MHz channels were validated
+	 * above. If 160 MHz is supported, check the remaining four 20 MHz
+	 * channels for the total of 160 MHz bandwidth for 6 GHz.
+	 */
 	if ((mode->he_capab[ieee80211_mode].phy_cap[
 		     HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz) {
-		/* In 160 MHz, the initial four 20 MHz channels were validated
-		 * above; check the remaining four 20 MHz channels for the total
-		 * of 160 MHz bandwidth.
-		 */
-		if (!ibss_mesh_is_80mhz_avail(channel + 16, mode))
-			return;
-
+	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz &&
+	    ibss_mesh_is_80mhz_avail(channel + 16, mode)) {
 		for (j = 0; j < ARRAY_SIZE(bw160); j++) {
 			if (freq->freq == bw160[j]) {
-				chwidth = CHANWIDTH_160MHZ;
+				chwidth = CONF_OPER_CHWIDTH_160MHZ;
 				seg0 = channel + 14;
 				break;
 			}
 		}
 	}
 
-	if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) {
+	if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
 		/* setup center_freq2, bandwidth */
 		for (k = 0; k < ARRAY_SIZE(bw80); k++) {
 			/* Only accept 80 MHz segments separated by a gap */
@@ -2743,7 +2958,7 @@
 
 			if (ieee80211_freq_to_chan(bw80[k], &channel) ==
 			    NUM_HOSTAPD_MODES)
-				return;
+				break;
 
 			for (i = channel; i < channel + 16; i += 4) {
 				struct hostapd_channel_data *chan;
@@ -2758,56 +2973,112 @@
 					continue;
 
 				/* Found a suitable second segment for 80+80 */
-				chwidth = CHANWIDTH_80P80MHZ;
+				chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
 				if (!is_6ghz)
 					vht_caps |=
 						VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
 				seg1 = channel + 6;
 			}
 
-			if (chwidth == CHANWIDTH_80P80MHZ)
+			if (chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
 				break;
 		}
-	} else if (ssid->max_oper_chwidth == CHANWIDTH_160MHZ) {
+	} else if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
 		if (freq->freq == 5180) {
-			chwidth = CHANWIDTH_160MHZ;
+			chwidth = CONF_OPER_CHWIDTH_160MHZ;
 			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 			seg0 = 50;
 		} else if (freq->freq == 5520) {
-			chwidth = CHANWIDTH_160MHZ;
+			chwidth = CONF_OPER_CHWIDTH_160MHZ;
 			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 			seg0 = 114;
 		}
-	} else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) {
-		chwidth = CHANWIDTH_USE_HT;
-		seg0 = channel + 2;
-#ifdef CONFIG_HT_OVERRIDES
-		if (ssid->disable_ht40)
-			seg0 = 0;
-#endif /* CONFIG_HT_OVERRIDES */
 	}
 
-#ifdef CONFIG_HE_OVERRIDES
-	if (ssid->disable_he) {
-		vht_freq.he_enabled = 0;
-		freq->he_enabled = 0;
-	}
-#endif /* CONFIG_HE_OVERRIDES */
+skip_80mhz:
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
 				    freq->channel, ssid->enable_edmg,
 				    ssid->edmg_channel, freq->ht_enabled,
-				    vht_freq.vht_enabled, freq->he_enabled,
-				    false,
+				    freq->vht_enabled, freq->he_enabled,
+				    freq->eht_enabled,
 				    freq->sec_channel_offset,
 				    chwidth, seg0, seg1, vht_caps,
 				    &mode->he_capab[ieee80211_mode],
-				    NULL) != 0)
-		return;
+				    &mode->eht_capab[ieee80211_mode]) != 0)
+		return false;
 
 	*freq = vht_freq;
 
 	wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
 		   freq->center_freq1, freq->center_freq2, freq->bandwidth);
+	return true;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+			  const struct wpa_ssid *ssid,
+			  struct hostapd_freq_params *freq)
+{
+	int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	int i, obss_scan = 1;
+	u8 channel;
+	bool is_6ghz;
+
+	freq->freq = ssid->frequency;
+
+	if (ssid->mode == WPAS_MODE_IBSS && !ssid->fixed_freq) {
+		struct wpa_bss *bss = ibss_find_existing_bss(wpa_s, ssid);
+
+		if (bss) {
+			wpa_printf(MSG_DEBUG,
+				   "IBSS already found in scan results, adjust control freq: %d",
+				   bss->freq);
+			freq->freq = bss->freq;
+			obss_scan = 0;
+		}
+	}
+
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+	for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+		if (wpa_s->hw.modes[i].mode == hw_mode &&
+		    hw_mode_get_channel(&wpa_s->hw.modes[i], freq->freq,
+					NULL) != NULL) {
+			mode = &wpa_s->hw.modes[i];
+			break;
+		}
+	}
+
+	if (!mode)
+		return;
+
+	is_6ghz = is_6ghz_freq(freq->freq);
+
+	freq->ht_enabled = 0;
+	freq->vht_enabled = 0;
+	freq->he_enabled = 0;
+	freq->eht_enabled = 0;
+
+	if (!is_6ghz)
+		freq->ht_enabled = ibss_mesh_can_use_ht(wpa_s, ssid, mode);
+	if (freq->ht_enabled)
+		freq->vht_enabled = ibss_mesh_can_use_vht(wpa_s, ssid, mode);
+	if (freq->vht_enabled || is_6ghz)
+		freq->he_enabled = ibss_mesh_can_use_he(wpa_s, ssid, mode,
+							ieee80211_mode);
+	freq->channel = channel;
+	/* Setup higher BW only for 5 GHz */
+	if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+		ibss_mesh_select_40mhz(wpa_s, ssid, mode, freq, obss_scan);
+		if (!ibss_mesh_select_80_160mhz(wpa_s, ssid, mode, freq,
+						ieee80211_mode, is_6ghz))
+			freq->he_enabled = freq->vht_enabled = false;
+	}
+
+	if (freq->he_enabled)
+		freq->eht_enabled = ibss_mesh_can_use_eht(wpa_s, ssid, mode,
+							  ieee80211_mode);
 }
 
 
@@ -2992,6 +3263,16 @@
 	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		int try_opportunistic;
 		const u8 *cache_id = NULL;
+		const u8 *addr = bss->bssid;
+
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
+		    !is_zero_ether_addr(bss->mld_addr))
+			addr = bss->mld_addr;
+
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+		    wpa_s->valid_links)
+			addr = wpa_s->ap_mld_addr;
 
 		try_opportunistic = (ssid->proactive_key_caching < 0 ?
 				     wpa_s->conf->okc :
@@ -3001,7 +3282,7 @@
 		if (wpa_key_mgmt_fils(ssid->key_mgmt))
 			cache_id = wpa_bss_get_fils_cache_id(bss);
 #endif /* CONFIG_FILS */
-		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, addr,
 					    ssid, try_opportunistic,
 					    cache_id, 0) == 0) {
 			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
@@ -3011,7 +3292,7 @@
 		}
 		wpa_ie_len = max_wpa_ie_len;
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_ie, &wpa_ie_len)) {
+					      wpa_ie, &wpa_ie_len, false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
 				"key management and encryption suites");
 			os_free(wpa_ie);
@@ -3023,7 +3304,7 @@
 		/* No PMKSA caching, but otherwise similar to RSN/WPA */
 		wpa_ie_len = max_wpa_ie_len;
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
-					      wpa_ie, &wpa_ie_len)) {
+					      wpa_ie, &wpa_ie_len, false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
 				"key management and encryption suites");
 			os_free(wpa_ie);
@@ -3043,7 +3324,7 @@
 	} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 		wpa_ie_len = max_wpa_ie_len;
 		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
-					      wpa_ie, &wpa_ie_len)) {
+					      wpa_ie, &wpa_ie_len, false)) {
 			wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
 				"key management and encryption suites (no "
 				"scan results)");
@@ -3114,7 +3395,7 @@
 #endif /* CONFIG_FILS */
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_SAE
-	if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
+	if (wpa_key_mgmt_sae(wpa_s->key_mgmt))
 		algs = WPA_AUTH_ALG_SAE;
 #endif /* CONFIG_SAE */
 
@@ -3368,7 +3649,7 @@
 			}
 #ifdef CONFIG_SME
 			if (len > 0 && wpa_s->sme.ft_used &&
-			    wpa_sm_has_ptk(wpa_s->wpa)) {
+			    wpa_sm_has_ft_keys(wpa_s->wpa, md)) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"SME: Trying to use FT over-the-air");
 				algs |= WPA_AUTH_ALG_FT;
@@ -3652,6 +3933,7 @@
 	int use_crypt, ret, bssid_changed;
 	unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
 	struct wpa_driver_associate_params params;
+	u8 psk[PMK_LEN];
 #if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL)
 	int wep_keys_set = 0;
 #endif /* CONFIG_WEP || IEEE8021X_EAPOL */
@@ -3800,6 +4082,11 @@
 		wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
 	}
 
+	/* Set current_ssid before changing state to ASSOCIATING, so that the
+	 * selected SSID is available to wpas_notify_state_changed(). */
+	old_ssid = wpa_s->current_ssid;
+	wpa_s->current_ssid = ssid;
+
 	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
 	if (bss) {
 		params.ssid = bss->ssid;
@@ -3915,6 +4202,7 @@
 	params.group_suite = cipher_group;
 	params.mgmt_group_suite = cipher_group_mgmt;
 	params.key_mgmt_suite = wpa_s->key_mgmt;
+	params.allowed_key_mgmts = wpa_s->allowed_key_mgmts;
 	params.wpa_proto = wpa_s->wpa_proto;
 	wpa_s->auth_alg = params.auth_alg;
 	params.mode = ssid->mode;
@@ -3933,16 +4221,18 @@
 #endif /* CONFIG_WEP */
 
 	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
-	    (
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-	     (params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
+	     ((params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
+	      (params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK))) {
 #else
-	     params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+	     (params.allowed_key_mgmts &
+	      (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK)))) {
 #endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
-	     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
 		params.passphrase = ssid->passphrase;
-		if (ssid->psk_set)
-			params.psk = ssid->psk;
+		if (wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+			params.psk = psk;
 	}
 
 	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
@@ -3963,16 +4253,16 @@
 		else
 			params.req_key_mgmt_offload = 1;
 
-		if ((
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
-		     (params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
-#else
-		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
-#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+		if (((params.key_mgmt_suite & WPA_KEY_MGMT_PSK) ||
 		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 		     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
-		    ssid->psk_set)
-			params.psk = ssid->psk;
+#else
+		if ((wpa_key_mgmt_wpa_psk_no_sae(params.key_mgmt_suite) ||
+		     wpa_key_mgmt_wpa_psk_no_sae(params.allowed_key_mgmts)) &&
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+		    wpa_supplicant_get_psk(wpa_s, bss, ssid, psk) == 0)
+			params.psk = psk;
 	}
 
 	params.drop_unencrypted = use_crypt;
@@ -4020,6 +4310,7 @@
 #ifdef CONFIG_HE_OVERRIDES
 	wpa_supplicant_apply_he_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HE_OVERRIDES */
+	wpa_supplicant_apply_eht_overrides(wpa_s, ssid, &params);
 
 #ifdef CONFIG_P2P
 	/*
@@ -4029,7 +4320,7 @@
 	 */
 	if (wpa_s->num_multichan_concurrent < 2) {
 		int freq, num;
-		num = get_shared_radio_freqs(wpa_s, &freq, 1);
+		num = get_shared_radio_freqs(wpa_s, &freq, 1, false);
 		if (num > 0 && freq > 0 && freq != params.freq.freq) {
 			wpa_printf(MSG_DEBUG,
 				   "Assoc conflicting freq found (%d != %d)",
@@ -4045,7 +4336,7 @@
 #endif /* CONFIG_P2P */
 
 	if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
-	    wpa_s->current_ssid)
+	    old_ssid)
 		params.prev_bssid = prev_bssid;
 
 #ifdef CONFIG_SAE
@@ -4053,6 +4344,7 @@
 #endif /* CONFIG_SAE */
 
 	ret = wpa_drv_associate(wpa_s, &params);
+	forced_memzero(psk, sizeof(psk));
 	os_free(wpa_ie);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -4115,15 +4407,13 @@
 	}
 #endif /* CONFIG_WEP */
 
-	if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
+	if (old_ssid && old_ssid != ssid) {
 		/*
 		 * Do not allow EAP session resumption between different
 		 * network configurations.
 		 */
 		eapol_sm_invalidate_cached_session(wpa_s->eapol);
 	}
-	old_ssid = wpa_s->current_ssid;
-	wpa_s->current_ssid = ssid;
 
 	if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
 		wpa_s->current_bss = bss;
@@ -4136,6 +4426,8 @@
 	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
 		wpas_notify_network_changed(wpa_s);
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+		wpas_notify_auth_changed(wpa_s);
 }
 
 
@@ -4555,7 +4847,7 @@
 	wpa_s->last_owe_group = 0;
 	if (ssid) {
 		ssid->owe_transition_bss_select_count = 0;
-		wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+		wpa_s_setup_sae_pt(wpa_s->conf, ssid, false);
 	}
 
 	if (wpa_s->connect_without_scan ||
@@ -5034,6 +5326,7 @@
  * @src_addr: Source address of the EAPOL frame
  * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
  * @len: Length of the EAPOL data
+ * @encrypted: Whether the frame was encrypted
  *
  * This function is called for each received EAPOL frame. Most driver
  * interfaces rely on more generic OS mechanism for receiving frames through
@@ -5042,11 +5335,15 @@
  * code by calling this function.
  */
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
-			     const u8 *buf, size_t len)
+			     const u8 *buf, size_t len,
+			     enum frame_encryption encrypted)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *connected_addr = wpa_s->valid_links ?
+		wpa_s->ap_mld_addr : wpa_s->bssid;
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " (encrypted=%d)",
+		MAC2STR(src_addr), encrypted);
 	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
 	if (wpa_s->own_disconnect_req) {
@@ -5069,7 +5366,7 @@
 #ifdef CONFIG_AP
 	     !wpa_s->ap_iface &&
 #endif /* CONFIG_AP */
-	     os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
+	     os_memcmp(src_addr, connected_addr, ETH_ALEN) != 0)) {
 		/*
 		 * There is possible race condition between receiving the
 		 * association event and the EAPOL frame since they are coming
@@ -5082,26 +5379,29 @@
 		 * Authenticator uses BSSID as the src_addr (which is not the
 		 * case with wired IEEE 802.1X).
 		 */
-		wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
-			"of received EAPOL frame (state=%s bssid=" MACSTR ")",
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Not associated - Delay processing of received EAPOL frame (state=%s connected_addr="
+			MACSTR ")",
 			wpa_supplicant_state_txt(wpa_s->wpa_state),
-			MAC2STR(wpa_s->bssid));
+			MAC2STR(connected_addr));
 		wpabuf_free(wpa_s->pending_eapol_rx);
 		wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
 		if (wpa_s->pending_eapol_rx) {
 			os_get_reltime(&wpa_s->pending_eapol_rx_time);
 			os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
 				  ETH_ALEN);
+			wpa_s->pending_eapol_encrypted = encrypted;
 		}
 		return;
 	}
 
 	wpa_s->last_eapol_matches_bssid =
-		os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+		os_memcmp(src_addr, connected_addr, ETH_ALEN) == 0;
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface) {
-		wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
+		wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len,
+					   encrypted);
 		return;
 	}
 #endif /* CONFIG_AP */
@@ -5161,7 +5461,8 @@
 #ifdef CONFIG_IBSS_RSN
 	if (wpa_s->current_ssid &&
 	    wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
-		ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
+		ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len,
+				  encrypted);
 		return;
 	}
 #endif /* CONFIG_IBSS_RSN */
@@ -5176,11 +5477,12 @@
 	if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
 	    wpa_s->key_mgmt != WPA_KEY_MGMT_OWE &&
 	    wpa_s->key_mgmt != WPA_KEY_MGMT_DPP &&
-	    eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
+	    eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len,
+			      encrypted) > 0)
 		return;
 	wpa_drv_poll(wpa_s);
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK))
-		wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
+		wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len, encrypted);
 	else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 		/*
 		 * Set portValid = true here since we are going to skip 4-way
@@ -5193,6 +5495,14 @@
 }
 
 
+static void wpa_supplicant_rx_eapol_cb(void *ctx, const u8 *src_addr,
+				       const u8 *buf, size_t len)
+{
+	wpa_supplicant_rx_eapol(ctx, src_addr, buf, len,
+				FRAME_ENCRYPTION_UNKNOWN);
+}
+
+
 static int wpas_eapol_needs_l2_packet(struct wpa_supplicant *wpa_s)
 {
 	return !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_CONTROL_PORT) ||
@@ -5202,6 +5512,10 @@
 
 int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 {
+	u8 prev_mac_addr[ETH_ALEN];
+
+	os_memcpy(prev_mac_addr, wpa_s->own_addr, ETH_ALEN);
+
 	if ((!wpa_s->p2p_mgmt ||
 	     !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
 	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
@@ -5210,7 +5524,7 @@
 					   wpa_drv_get_mac_addr(wpa_s),
 					   ETH_P_EAPOL,
 					   wpas_eapol_needs_l2_packet(wpa_s) ?
-					   wpa_supplicant_rx_eapol : NULL,
+					   wpa_supplicant_rx_eapol_cb : NULL,
 					   wpa_s, 0);
 		if (wpa_s->l2 == NULL)
 			return -1;
@@ -5239,6 +5553,9 @@
 		fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
 #endif /* CONFIG_FST */
 
+	if (os_memcmp(prev_mac_addr, wpa_s->own_addr, ETH_ALEN) != 0)
+		wpas_notify_mac_address_changed(wpa_s);
+
 	return 0;
 }
 
@@ -5264,7 +5581,7 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
 		" (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
 	wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
-				len - sizeof(*eth));
+				len - sizeof(*eth), FRAME_ENCRYPTION_UNKNOWN);
 }
 
 
@@ -5805,6 +6122,17 @@
 #endif /* CONFIG_HE_OVERRIDES */
 
 
+void wpa_supplicant_apply_eht_overrides(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params)
+{
+	if (!ssid)
+		return;
+
+	params->disable_eht = ssid->disable_eht;
+}
+
+
 static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
 {
 #ifdef PCSC_FUNCS
@@ -5990,6 +6318,7 @@
 void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
 				       struct fst_wpa_obj *iface_obj)
 {
+	os_memset(iface_obj, 0, sizeof(*iface_obj));
 	iface_obj->ctx              = wpa_s;
 	iface_obj->get_bssid        = wpas_fst_get_bssid_cb;
 	iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
@@ -6621,7 +6950,7 @@
 #else /* CONFIG_BACKEND_FILE */
 		wpa_s->confname = os_strdup(iface->confname);
 #endif /* CONFIG_BACKEND_FILE */
-		wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
+		wpa_s->conf = wpa_config_read(wpa_s->confname, NULL, false);
 		if (wpa_s->conf == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to read or parse "
 				   "configuration '%s'.", wpa_s->confname);
@@ -6629,7 +6958,7 @@
 		}
 		wpa_s->confanother = os_rel2abs_path(iface->confanother);
 		if (wpa_s->confanother &&
-		    !wpa_config_read(wpa_s->confanother, wpa_s->conf)) {
+		    !wpa_config_read(wpa_s->confanother, wpa_s->conf, true)) {
 			wpa_printf(MSG_ERROR,
 				   "Failed to read or parse configuration '%s'.",
 				   wpa_s->confanother);
@@ -6644,12 +6973,24 @@
 			os_free(wpa_s->conf->ctrl_interface);
 			wpa_s->conf->ctrl_interface =
 				os_strdup(iface->ctrl_interface);
+			if (!wpa_s->conf->ctrl_interface) {
+				wpa_printf(MSG_ERROR,
+					   "Failed to duplicate control interface '%s'.",
+					   iface->ctrl_interface);
+				return -1;
+			}
 		}
 
 		if (iface->driver_param) {
 			os_free(wpa_s->conf->driver_param);
 			wpa_s->conf->driver_param =
 				os_strdup(iface->driver_param);
+			if (!wpa_s->conf->driver_param) {
+				wpa_printf(MSG_ERROR,
+					   "Failed to duplicate driver param '%s'.",
+					   iface->driver_param);
+				return -1;
+			}
 		}
 
 		if (iface->p2p_mgmt && !iface->ctrl_interface) {
@@ -6781,6 +7122,7 @@
 		wpa_s->num_multichan_concurrent =
 			capa.num_multichan_concurrent;
 		wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+		wpa_s->max_num_akms = capa.max_num_akms;
 
 		if (capa.mac_addr_rand_scan_supported)
 			wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
@@ -6795,6 +7137,9 @@
 		    wpa_s->extended_capa[2] & 0x40)
 			wpa_s->multi_bss_support = 1;
 	}
+#ifdef CONFIG_PASN
+	wpa_pasn_sm_set_caps(wpa_s->wpa, wpa_s->drv_flags2);
+#endif /* CONFIG_PASN */
 	if (wpa_s->max_remain_on_chan == 0)
 		wpa_s->max_remain_on_chan = 1000;
 
@@ -7414,26 +7759,63 @@
 	global->params.daemonize = params->daemonize;
 	global->params.wait_for_monitor = params->wait_for_monitor;
 	global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
-	if (params->pid_file)
+
+	if (params->pid_file) {
 		global->params.pid_file = os_strdup(params->pid_file);
-	if (params->ctrl_interface)
+		if (!global->params.pid_file) {
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
+
+	if (params->ctrl_interface) {
 		global->params.ctrl_interface =
 			os_strdup(params->ctrl_interface);
-	if (params->ctrl_interface_group)
+		if (!global->params.ctrl_interface) {
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
+
+	if (params->ctrl_interface_group) {
 		global->params.ctrl_interface_group =
 			os_strdup(params->ctrl_interface_group);
-	if (params->override_driver)
+		if (!global->params.ctrl_interface_group) {
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
+
+	if (params->override_driver) {
 		global->params.override_driver =
 			os_strdup(params->override_driver);
-	if (params->override_ctrl_interface)
+		if (!global->params.override_driver) {
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
+
+	if (params->override_ctrl_interface) {
 		global->params.override_ctrl_interface =
 			os_strdup(params->override_ctrl_interface);
+		if (!global->params.override_ctrl_interface) {
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
+
 #ifdef CONFIG_MATCH_IFACE
 	global->params.match_iface_count = params->match_iface_count;
 	if (params->match_iface_count) {
 		global->params.match_ifaces =
 			os_calloc(params->match_iface_count,
 				  sizeof(struct wpa_interface));
+		if (!global->params.match_ifaces) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to allocate match interfaces");
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
 		os_memcpy(global->params.match_ifaces,
 			  params->match_ifaces,
 			  params->match_iface_count *
@@ -7441,9 +7823,15 @@
 	}
 #endif /* CONFIG_MATCH_IFACE */
 #ifdef CONFIG_P2P
-	if (params->conf_p2p_dev)
+	if (params->conf_p2p_dev) {
 		global->params.conf_p2p_dev =
 			os_strdup(params->conf_p2p_dev);
+		if (!global->params.conf_p2p_dev) {
+			wpa_printf(MSG_ERROR, "Failed to allocate conf p2p");
+			wpa_supplicant_deinit(global);
+			return NULL;
+		}
+	}
 #endif /* CONFIG_P2P */
 	wpa_debug_level = global->params.wpa_debug_level =
 		params->wpa_debug_level;
@@ -7531,6 +7919,15 @@
 					wpa_s->ctrl_iface);
 	}
 
+#ifdef CONFIG_AIDL
+	// If daemonize is enabled, initialize AIDL here.
+	if (global->params.daemonize) {
+		global->aidl = wpas_aidl_init(global);
+		if (!global->aidl)
+			return -1;
+	}
+#endif /* CONFIG_AIDL */
+
 	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
 	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
 
@@ -7778,7 +8175,7 @@
 	if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
 		wpa_printf(MSG_DEBUG, "Continuous association failures - "
 			   "consider temporary network disabling");
-		wpas_auth_failed(wpa_s, "CONN_FAILED");
+		wpas_auth_failed(wpa_s, "CONN_FAILED", bssid);
 	}
 	/*
 	 * Multiple consecutive connection failures mean that other APs are
@@ -7955,6 +8352,8 @@
 	case WPA_CTRL_REQ_EAP_PASSWORD:
 		bin_clear_free(eap->password, eap->password_len);
 		eap->password = (u8 *) os_strdup(value);
+		if (!eap->password)
+			return -1;
 		eap->password_len = value_len;
 		eap->pending_req_password = 0;
 		if (ssid == wpa_s->current_ssid)
@@ -7963,6 +8362,8 @@
 	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
 		bin_clear_free(eap->new_password, eap->new_password_len);
 		eap->new_password = (u8 *) os_strdup(value);
+		if (!eap->new_password)
+			return -1;
 		eap->new_password_len = value_len;
 		eap->pending_req_new_password = 0;
 		if (ssid == wpa_s->current_ssid)
@@ -7971,6 +8372,8 @@
 	case WPA_CTRL_REQ_EAP_PIN:
 		str_clear_free(eap->cert.pin);
 		eap->cert.pin = os_strdup(value);
+		if (!eap->cert.pin)
+			return -1;
 		eap->pending_req_pin = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
@@ -7978,6 +8381,8 @@
 	case WPA_CTRL_REQ_EAP_OTP:
 		bin_clear_free(eap->otp, eap->otp_len);
 		eap->otp = (u8 *) os_strdup(value);
+		if (!eap->otp)
+			return -1;
 		eap->otp_len = value_len;
 		os_free(eap->pending_req_otp);
 		eap->pending_req_otp = NULL;
@@ -7986,6 +8391,8 @@
 	case WPA_CTRL_REQ_EAP_PASSPHRASE:
 		str_clear_free(eap->cert.private_key_passwd);
 		eap->cert.private_key_passwd = os_strdup(value);
+		if (!eap->cert.private_key_passwd)
+			return -1;
 		eap->pending_req_passphrase = 0;
 		if (ssid == wpa_s->current_ssid)
 			wpa_s->reassociate = 1;
@@ -7993,6 +8400,8 @@
 	case WPA_CTRL_REQ_SIM:
 		str_clear_free(eap->external_sim_resp);
 		eap->external_sim_resp = os_strdup(value);
+		if (!eap->external_sim_resp)
+			return -1;
 		eap->pending_req_sim = 0;
 		break;
 	case WPA_CTRL_REQ_PSK_PASSPHRASE:
@@ -8070,6 +8479,26 @@
 	    !ssid->mem_only_psk)
 		return 1;
 
+#ifdef IEEE8021X_EAPOL
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	if (ssid->eap.imsi_privacy_cert) {
+		struct crypto_rsa_key *key;
+		bool failed = false;
+
+		key = crypto_rsa_key_read(ssid->eap.imsi_privacy_cert, false);
+		if (!key)
+			failed = true;
+		crypto_rsa_key_free(key);
+		if (failed) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid imsi_privacy_cert (%s) - disable network",
+				   ssid->eap.imsi_privacy_cert);
+			return 1;
+		}
+	}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+#endif /* IEEE8021X_EAPOL */
+
 	return 0;
 }
 
@@ -8100,6 +8529,13 @@
 			return NO_MGMT_FRAME_PROTECTION;
 		}
 
+#ifdef CONFIG_OCV
+		/* Enable PMF if OCV is being enabled */
+		if (wpa_s->conf->pmf == NO_MGMT_FRAME_PROTECTION &&
+		    ssid && ssid->ocv)
+			return MGMT_FRAME_PROTECTION_OPTIONAL;
+#endif /* CONFIG_OCV */
+
 		return wpa_s->conf->pmf;
 	}
 
@@ -8107,6 +8543,19 @@
 }
 
 
+#ifdef CONFIG_SAE
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+			 struct wpa_ssid *ssid,
+			 const struct wpa_ie_data *ie)
+{
+	return wpa_s->conf->sae_check_mfp &&
+		(!(ie->capabilities &
+		   (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) ||
+		 wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION);
+}
+#endif /* CONFIG_SAE */
+
+
 int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
 	if (wpa_s->current_ssid == NULL ||
@@ -8127,7 +8576,8 @@
 }
 
 
-void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+		      const u8 *bssid)
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	int dur;
@@ -8190,11 +8640,16 @@
 		ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
 		ssid->auth_failures, dur, reason) + 1;
 	char *msg = os_malloc(msg_len);
+	if (!msg)
+		return;
 	snprintf(msg, msg_len, format_str,
 		ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
 		ssid->auth_failures, dur, reason);
 	wpas_notify_ssid_temp_disabled(wpa_s, msg);
 	os_free(msg);
+
+	if (bssid)
+		os_memcpy(ssid->disabled_due_to, bssid, ETH_ALEN);
 }
 
 
@@ -8211,8 +8666,15 @@
 	}
 	ssid->disabled_until.sec = 0;
 	ssid->disabled_until.usec = 0;
-	if (clear_failures)
+	if (clear_failures) {
 		ssid->auth_failures = 0;
+	} else if (!is_zero_ether_addr(ssid->disabled_due_to)) {
+		wpa_printf(MSG_DEBUG, "Mark BSSID " MACSTR
+			   " ignored to allow a lower priority BSS, if any, to be tried next",
+			   MAC2STR(ssid->disabled_due_to));
+		wpa_bssid_ignore_add(wpa_s, ssid->disabled_due_to);
+		os_memset(ssid->disabled_due_to, 0, ETH_ALEN);
+	}
 }
 
 
@@ -8326,7 +8788,7 @@
  */
 int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
 				struct wpa_used_freq_data *freqs_data,
-				unsigned int len)
+				unsigned int len, bool exclude_current)
 {
 	struct wpa_supplicant *ifs;
 	u8 bssid[ETH_ALEN];
@@ -8342,6 +8804,9 @@
 		if (idx == len)
 			break;
 
+		if (exclude_current && ifs == wpa_s)
+			continue;
+
 		if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
 			continue;
 
@@ -8379,7 +8844,8 @@
  * are using the same radio as the current interface.
  */
 int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
-			   int *freq_array, unsigned int len)
+			   int *freq_array, unsigned int len,
+			   bool exclude_current)
 {
 	struct wpa_used_freq_data *freqs_data;
 	int num, i;
@@ -8390,7 +8856,8 @@
 	if (!freqs_data)
 		return -1;
 
-	num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+	num = get_shared_radio_freqs_data(wpa_s, freqs_data, len,
+					  exclude_current);
 	for (i = 0; i < num; i++)
 		freq_array[i] = freqs_data[i].freq;
 
@@ -8715,17 +9182,17 @@
 				continue;
 			wpa_printf(MSG_DEBUG,
 				   "Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d",
-				   si->current_signal,
+				   si->data.signal,
 				   dso->si_current_signal,
-				   si->avg_signal,
+				   si->data.avg_signal,
 				   dso->si_avg_signal,
-				   si->avg_beacon_signal,
+				   si->data.avg_beacon_signal,
 				   dso->si_avg_beacon_signal,
 				   si->current_noise,
 				   dso->si_current_noise);
-			si->current_signal = dso->si_current_signal;
-			si->avg_signal = dso->si_avg_signal;
-			si->avg_beacon_signal = dso->si_avg_beacon_signal;
+			si->data.signal = dso->si_current_signal;
+			si->data.avg_signal = dso->si_avg_signal;
+			si->data.avg_beacon_signal = dso->si_avg_beacon_signal;
 			si->current_noise = dso->si_current_noise;
 			break;
 		}
@@ -8776,3 +9243,43 @@
 
 	return scan_res;
 }
+
+
+static bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	int i;
+
+	if (!wpa_s->valid_links)
+		return false;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(wpa_s->valid_links & BIT(i)))
+			continue;
+
+		if (os_memcmp(wpa_s->links[i].bssid, addr, ETH_ALEN) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
+int wpa_drv_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+			unsigned int wait, const u8 *dst, const u8 *src,
+			const u8 *bssid, const u8 *data, size_t data_len,
+			int no_cck)
+{
+	if (!wpa_s->driver->send_action)
+		return -1;
+
+	if (data_len > 0 && data[0] != WLAN_ACTION_PUBLIC) {
+		if (wpas_ap_link_address(wpa_s, dst))
+			dst = wpa_s->ap_mld_addr;
+
+		if (wpas_ap_link_address(wpa_s, bssid))
+			bssid = wpa_s->ap_mld_addr;
+	}
+
+	return wpa_s->driver->send_action(wpa_s->drv_priv, freq, wait, dst, src,
+					  bssid, data, data_len, no_cck);
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index fd54fef..8753fba 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -419,6 +419,34 @@
 # RSN.
 #pmf=0
 
+# sae_check_mfp: Require PMF support to select SAE key_mgmt
+# 0 = Do not check PMF for SAE (default)
+# 1 = Limit SAE when PMF is not enabled
+#
+# When enabled SAE will not be selected if PMF will not be used
+# for the connection.
+# Scenarios where this check will limit SAE:
+#  1) ieee80211w=0 is set for the network
+#  2) The AP does not have PMF enabled.
+#  3) ieee80211w is unset, pmf=1 is enabled globally, and
+#     the device does not support the BIP cipher.
+# Consider the configuration of global parameterss sae_check_mfp=1, pmf=1 and a
+# network configured with ieee80211w unset and key_mgmt=SAE WPA-PSK.
+# In the example WPA-PSK will be used if the device does not support
+# the BIP cipher or the AP has PMF disabled.
+# Limiting SAE with this check can avoid failing to associate to an AP
+# that is configured with sae_requires_mfp=1 if the device does
+# not support PMF due to lack of the BIP cipher.
+#
+# Enabling this check helps with compliance of the WPA3
+# specification for WPA3-Personal transition mode.
+# The WPA3 specification section 2.3 "WPA3-Personal transition mode" item 8
+# states "A STA shall negotiate PMF when associating to an AP using SAE".
+# With this check WPA3 capable devices when connecting
+# to transition mode APs that do not advertise PMF support
+# will not use SAE and instead fallback to PSK.
+#sae_check_mfp=0
+
 # Enabled SAE finite cyclic groups in preference order
 # By default (if this parameter is not set), the mandatory group 19 (ECC group
 # defined over a 256-bit prime order field, NIST P-256) is preferred and groups
@@ -655,7 +683,26 @@
 #	be used to configure alternative FQDNs that will be considered home
 #	networks.
 #
+# home_ois: Home OI(s)
+#	This string field contains one or more comma delimited OIs (hexdump)
+#	identifying the access the access points that support authentication
+#	with this credential. There are an alternative to the use of the realm
+#	parameter. When using Home OIs to match the network, the EAP parameters
+#	need to be pre-configured with the credentials since the NAI Realm
+#	information may not be available or fetched.
+#	A successful authentication with the access point is possible as soon
+#	as at least one Home OI from the list matches an OI in the Roaming
+#	Consortium advertised by the access point.
+#	(Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOI)
+#
+# required_home_ois: Required Home OI(s)
+#	This string field contains the set of Home OI(s) (hexdump) that are
+#	required to be advertised by the AP for the credential to be considered
+#	matching.
+#	(Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/HomeOIList/<X+>/HomeOIRequired)
+#
 # roaming_consortium: Roaming Consortium OI
+#	Deprecated: use home_ois instead.
 #	If roaming_consortium_len is non-zero, this field contains the
 #	Roaming Consortium OI that can be used to determine which access
 #	points support authentication with this credential. This is an
@@ -665,6 +712,7 @@
 #	may not be available or fetched.
 #
 # required_roaming_consortium: Required Roaming Consortium OI
+#	Deprecated: use required_home_ois instead.
 #	If required_roaming_consortium_len is non-zero, this field contains the
 #	Roaming Consortium OI that is required to be advertised by the AP for
 #	the credential to be considered matching.
@@ -770,7 +818,7 @@
 #	password="password"
 #	ca_cert="/etc/wpa_supplicant/ca.pem"
 #	domain="example.com"
-#	roaming_consortium=223344
+#	home_ois="223344"
 #	eap=TTLS
 #	phase2="auth=MSCHAPV2"
 #}
@@ -1094,6 +1142,15 @@
 # 0: No replay window, strict check (default)
 # 1..2^32-1: number of packets that could be misordered
 #
+# macsec_offload - Enable MACsec hardware offload
+#
+# This setting applies only when MACsec is in use, i.e.,
+#  - the key server has decided to enable MACsec
+#
+# 0 = MACSEC_OFFLOAD_OFF (default)
+# 1 = MACSEC_OFFLOAD_PHY
+# 2 = MACSEC_OFFLOAD_MAC
+#
 # macsec_port: IEEE 802.1X/MACsec port
 # Port component of the SCI
 # Range: 1-65534 (default: 1)
@@ -1175,6 +1232,14 @@
 #	unencrypted identity with EAP types that support different tunnelled
 #	identity, e.g., EAP-TTLS). This field can also be used with
 #	EAP-SIM/AKA/AKA' to store the pseudonym identity.
+# strict_conservative_peer_mode: Whether the strict conservative peer mode
+#	is enabled. This field is used to handle the reponse of AT_PERMANENT_ID_REQ
+#	for EAP-SIM/AKA/AKA'. In non-strict convervative peer mode, a client
+#	error would be sent to the server, but the mode will send the permanent
+#	identity in some special cases according to 4.6.2 of RFC 4187; With the
+#	strict mode, the permanent identity is never sent to the server.
+#	0 = disabled (default)
+#	1 = enabled
 # password: Password string for EAP. This field can include either the
 #	plaintext password (using ASCII or hex string) or a NtPasswordHash
 #	(16-byte MD4 hash of password) in hash:<32 hex digits> format.
@@ -1493,6 +1558,12 @@
 # 2: do not allow PFS to be used
 #dpp_pfs=0
 
+# DPP Network introduction type
+# 0: unprotected variant from DPP R1 (default)
+# 1: privacy protecting (station Connector encrypted) variant from
+#    DPP R3
+#dpp_connector_privacy=0
+
 # Whether beacon protection is enabled
 # This depends on management frame protection (ieee80211w) being enabled and
 # beacon protection support indication from the driver.
@@ -1621,6 +1692,10 @@
 #  2: MCS 0-9
 #  3: not supported
 
+# disable_eht: Whether EHT should be disabled.
+# 0 = EHT enabled (if supported) (default)
+# 1 = EHT disabled
+
 # multi_ap_backhaul_sta: Multi-AP backhaul STA functionality
 # 0 = normal STA (default)
 # 1 = backhaul STA
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 3adb819..01b3148 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -22,6 +22,7 @@
 #include "wmm_ac.h"
 #include <netinet/in.h>
 #include <netinet/in6.h>
+#include "pasn/pasn_common.h"
 
 extern const char *const wpa_supplicant_version;
 extern const char *const wpa_supplicant_license;
@@ -311,7 +312,6 @@
 	unsigned int p2p_24ghz_social_channels:1;
 	unsigned int pending_p2ps_group:1;
 	unsigned int pending_group_iface_for_p2ps:1;
-	unsigned int p2p_go_found_external_scan:1;
 	unsigned int pending_p2ps_group_freq;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -542,59 +542,6 @@
 	int num_policies;
 };
 
-#ifdef CONFIG_PASN
-
-struct pasn_fils {
-	u8 nonce[FILS_NONCE_LEN];
-	u8 anonce[FILS_NONCE_LEN];
-	u8 session[FILS_SESSION_LEN];
-	u8 erp_pmkid[PMKID_LEN];
-	bool completed;
-};
-
-struct wpas_pasn {
-	int akmp;
-	int cipher;
-	u16 group;
-	int freq;
-	size_t kdk_len;
-
-	u8 trans_seq;
-	u8 status;
-
-	u8 bssid[ETH_ALEN];
-	size_t pmk_len;
-	u8 pmk[PMK_LEN_MAX];
-	bool using_pmksa;
-
-	u8 hash[SHA384_MAC_LEN];
-
-	struct wpabuf *beacon_rsne_rsnxe;
-	struct wpa_ptk ptk;
-	struct crypto_ecdh *ecdh;
-
-	struct wpabuf *comeback;
-	u16 comeback_after;
-
-#ifdef CONFIG_SAE
-	struct sae_data sae;
-#endif /* CONFIG_SAE */
-
-	struct wpa_ssid *ssid;
-
-#ifdef CONFIG_FILS
-	struct pasn_fils fils;
-#endif /* CONFIG_FILS */
-
-#ifdef CONFIG_IEEE80211R
-	u8 pmk_r1[PMK_LEN_MAX];
-	size_t pmk_r1_len;
-	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
-#endif /* CONFIG_IEEE80211R */
-};
-#endif /* CONFIG_PASN */
-
-
 enum ip_version {
 	IPV4 = 4,
 	IPV6 = 6,
@@ -608,7 +555,6 @@
 	u16 dst_port;
 	u8 dscp;
 	u8 protocol;
-	u8 param_mask;
 };
 
 
@@ -620,7 +566,6 @@
 	u8 dscp;
 	u8 next_header;
 	u8 flow_label[3];
-	u8 param_mask;
 };
 
 
@@ -757,6 +702,15 @@
 	struct wpa_bss *current_bss;
 	int ap_ies_from_associnfo;
 	unsigned int assoc_freq;
+	u8 ap_mld_addr[ETH_ALEN];
+	u8 mlo_assoc_link_id;
+	u16 valid_links; /* bitmap of valid MLO link IDs */
+	struct ml_sta_link_info {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		unsigned int freq;
+		struct wpa_bss *bss;
+	} links[MAX_NUM_MLD_LINKS];
 	u8 *last_con_fail_realm;
 	size_t last_con_fail_realm_len;
 
@@ -767,6 +721,11 @@
 	int key_mgmt;
 	int wpa_proto;
 	int mgmt_group_cipher;
+	/*
+	 * Allowed key management suites for roaming/initial connection
+	 * when the driver's SME is in use.
+	 */
+	int allowed_key_mgmts;
 
 	void *drv_priv; /* private data used by driver_ops */
 	void *global_drv_priv;
@@ -902,6 +861,7 @@
 	unsigned int own_scan_requested:1;
 	unsigned int own_scan_running:1;
 	unsigned int clear_driver_scan_cache:1;
+	unsigned int manual_non_coloc_6ghz:1;
 	unsigned int manual_scan_id;
 	int scan_interval; /* time in sec between scans to find suitable AP */
 	int normal_scans; /* normal scans run before sched_scan */
@@ -951,6 +911,7 @@
 	unsigned int max_match_sets;
 	unsigned int max_remain_on_chan;
 	unsigned int max_stations;
+	unsigned int max_num_akms;
 
 	int pending_mic_error_report;
 	int pending_mic_error_pairwise;
@@ -966,6 +927,7 @@
 	struct wpabuf *pending_eapol_rx;
 	struct os_reltime pending_eapol_rx_time;
 	u8 pending_eapol_rx_src[ETH_ALEN];
+	enum frame_encryption pending_eapol_encrypted;
 	unsigned int last_eapol_matches_bssid:1;
 	unsigned int eapol_failed:1;
 	unsigned int eap_expected_failure:1;
@@ -987,7 +949,7 @@
 	unsigned int connection_11b_only:1;
 
 	struct os_reltime last_mac_addr_change;
-	int last_mac_addr_style;
+	enum wpas_mac_addr_style last_mac_addr_style;
 
 	struct ibss_rsn *ibss_rsn;
 
@@ -1036,8 +998,12 @@
 		unsigned int sae_pmksa_caching:1;
 		u16 seq_num;
 		u8 ext_auth_bssid[ETH_ALEN];
+		struct wpa_ssid *ext_auth_wpa_ssid;
 		u8 ext_auth_ssid[SSID_MAX_LEN];
 		size_t ext_auth_ssid_len;
+		int ext_auth_key_mgmt;
+		u8 ext_auth_ap_mld_addr[ETH_ALEN];
+		bool ext_ml_auth;
 		int *sae_rejected_groups;
 #endif /* CONFIG_SAE */
 	} sme;
@@ -1058,6 +1024,7 @@
 	unsigned int mesh_ht_enabled:1;
 	unsigned int mesh_vht_enabled:1;
 	unsigned int mesh_he_enabled:1;
+	unsigned int mesh_eht_enabled:1;
 	struct wpa_driver_mesh_join_params *mesh_params;
 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
 	/* struct external_pmksa_cache::list */
@@ -1108,6 +1075,7 @@
 	int p2p_sd_over_ctrl_iface;
 	int p2p_in_provisioning;
 	int p2p_in_invitation;
+	int p2p_retry_limit;
 	int p2p_invite_go_freq;
 	int pending_invite_ssid_id;
 	int show_group_started;
@@ -1160,6 +1128,7 @@
 	unsigned int user_initiated_pd:1;
 	unsigned int p2p_go_group_formation_completed:1;
 	unsigned int group_formation_reported:1;
+	unsigned int p2p_go_no_pri_sec_switch:1;
 	unsigned int waiting_presence_resp;
 	int p2p_first_connection_timeout;
 	unsigned int p2p_nfc_tag_enabled:1;
@@ -1371,7 +1340,6 @@
 	unsigned int oci_freq_override_ft_assoc;
 	unsigned int oci_freq_override_fils_assoc;
 	unsigned int oci_freq_override_wnm_sleep;
-	int force_hunting_and_pecking_pwe;
 	unsigned int disable_eapol_g2_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -1484,9 +1452,11 @@
 	int dpp_gas_dialog_token;
 	u8 dpp_intro_bssid[ETH_ALEN];
 	void *dpp_intro_network;
+	u8 dpp_intro_peer_version;
 	struct dpp_pkex *dpp_pkex;
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
+	size_t dpp_pkex_code_len;
 	char *dpp_pkex_identifier;
 	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
@@ -1517,11 +1487,33 @@
 	int dpp_reconfig_ssid_id;
 	struct dpp_reconfig_id *dpp_reconfig_id;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	struct os_reltime dpp_pb_time;
+	bool dpp_pb_configurator;
+	int *dpp_pb_freqs;
+	unsigned int dpp_pb_freq_idx;
+	unsigned int dpp_pb_announce_count;
+	struct wpabuf *dpp_pb_announcement;
+	struct dpp_bootstrap_info *dpp_pb_bi;
+	unsigned int dpp_pb_resp_freq;
+	u8 dpp_pb_init_hash[SHA256_MAC_LEN];
+	int dpp_pb_stop_iter;
+	bool dpp_pb_discovery_done;
+	u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+	size_t dpp_pb_c_nonce_len;
+	bool dpp_pb_result_indicated;
+	struct os_reltime dpp_pb_announce_time;
+	struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+	u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+	struct os_reltime dpp_pb_last_resp;
+	char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
 	char *dpp_discovery_override;
 	char *dpp_groups_override;
 	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+	unsigned int dpp_discard_public_action:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_DPP */
 
@@ -1538,9 +1530,15 @@
 	struct robust_av_data robust_av;
 	bool mscs_setup_done;
 
+	bool wps_scan_done; /* Set upon receiving scan results event */
+	bool supp_pbc_active; /* Set for interface when PBC is triggered */
+	bool wps_overlap;
+
 #ifdef CONFIG_PASN
-	struct wpas_pasn pasn;
+	struct pasn_data pasn;
 	struct wpa_radio_work *pasn_auth_work;
+	unsigned int pasn_count;
+	struct pasn_auth *pasn_params;
 #endif /* CONFIG_PASN */
 	struct scs_robust_av_data scs_robust_av_req;
 	u8 scs_dialog_token;
@@ -1555,6 +1553,8 @@
 	unsigned int enable_dscp_policy_capa:1;
 	unsigned int connection_dscp:1;
 	unsigned int wait_for_dscp_req:1;
+
+	struct wpa_signal_info last_signal_info;
 };
 
 
@@ -1568,6 +1568,9 @@
 void wpa_supplicant_apply_he_overrides(
 	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 	struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_eht_overrides(
+	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+	struct wpa_driver_associate_params *params);
 
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
@@ -1584,7 +1587,8 @@
 				struct wpa_ssid *ssid, struct wpa_ie_data *ie);
 int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *bss, struct wpa_ssid *ssid,
-			      u8 *wpa_ie, size_t *wpa_ie_len);
+			      u8 *wpa_ie, size_t *wpa_ie_len,
+			      bool skip_default_rsne);
 int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *bss,
@@ -1652,8 +1656,9 @@
 int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid);
 void wpa_supplicant_terminate_proc(struct wpa_global *global);
-void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
-			     const u8 *buf, size_t len);
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *own_addr,
+			     const u8 *buf, size_t len,
+			     enum frame_encryption encrypted);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
@@ -1661,7 +1666,8 @@
 void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, const char *reason,
+		      const u8 *bssid);
 void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
 			      struct wpa_ssid *ssid, int clear_failures);
 int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
@@ -1670,7 +1676,8 @@
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
 void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
-int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style,
+			    struct wpa_ssid *ssid);
 int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
 void add_freq(int *freqs, int *num_freqs, int freq);
 
@@ -1795,6 +1802,7 @@
 void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
 void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+int wpa_wps_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
 struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid **selected_ssid);
 int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
@@ -1803,6 +1811,7 @@
 int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
 					   struct wpa_bss *current_bss,
 					   struct wpa_bss *seleceted);
+void wpas_reset_mlo_info(struct wpa_supplicant *wpa_s);
 
 /* eap_register.c */
 int eap_register_methods(void);
@@ -1838,6 +1847,12 @@
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
+void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid,
+			bool force);
+
+bool wpas_is_sae_avoided(struct wpa_supplicant *wpa_s,
+			struct wpa_ssid *ssid,
+			const struct wpa_ie_data *ie);
 
 int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
 
@@ -1847,9 +1862,11 @@
 
 int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
 				struct wpa_used_freq_data *freqs_data,
-				unsigned int len);
+				unsigned int len, bool exclude_current);
 int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
-			   int *freq_array, unsigned int len);
+			   int *freq_array, unsigned int len,
+			   bool exclude_current);
+int disabled_freq(struct wpa_supplicant *wpa_s, int freq);
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
 
@@ -1890,7 +1907,7 @@
 int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
 						enum wpa_driver_if_type if_type,
 						unsigned int *num,
-						unsigned int *freq_list);
+						struct weighted_pcl *freq_list);
 
 int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
 int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
@@ -1923,7 +1940,7 @@
 int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
 			 size_t domain_name_length);
 
-int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *own_addr,
 			 const u8 *bssid, int akmp, int cipher,
 			 u16 group, int network_id,
 			 const u8 *comeback, size_t comeback_len);
@@ -1934,6 +1951,10 @@
 		      const struct ieee80211_mgmt *mgmt, size_t len);
 int disabled_freq(struct wpa_supplicant *wpa_s, int freq);
 
-int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *own_addr,
+			     const u8 *bssid);
+void wpas_pasn_auth_trigger(struct wpa_supplicant *wpa_s,
+			    struct pasn_auth *pasn_auth);
+void wpas_pasn_auth_work_done(struct wpa_supplicant *wpa_s, int status);
 
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index 7a558f3..cec26c4 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -7,3 +7,5 @@
 p2p_add_cli_chan=1
 oce=1
 sae_pwe=2
+p2p_optimize_listen_chan=1
+wowlan_disconnect_on_deinit=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 68236da..bba7777 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -250,7 +250,7 @@
 		else
 			wpa_s->group_cipher = cipher;
 	}
-	return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+	return wpa_drv_set_key(wpa_s, -1, WPA_ALG_WEP,
 			       unicast ? wpa_s->bssid : NULL,
 			       keyidx, unicast, NULL, 0, key, keylen,
 			       unicast ? KEY_FLAG_PAIRWISE_RX_TX :
@@ -286,7 +286,7 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	int res, pmk_len;
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 
 	wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
 		   result_str(result));
@@ -336,7 +336,11 @@
 	wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
 		   "handshake");
 
-	pmk_len = PMK_LEN;
+	if (wpa_key_mgmt_sha384(wpa_s->key_mgmt))
+		pmk_len = PMK_LEN_SUITE_B_192;
+	else
+		pmk_len = PMK_LEN;
+
 	if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
 #ifdef CONFIG_IEEE80211R
 		u8 buf[2 * PMK_LEN];
@@ -351,7 +355,7 @@
 		res = -1;
 #endif /* CONFIG_IEEE80211R */
 	} else {
-		res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+		res = eapol_sm_get_key(eapol, pmk, pmk_len);
 		if (res) {
 			/*
 			 * EAP-LEAP is an exception from other EAP methods: it
@@ -371,7 +375,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
 			"handshake", pmk, pmk_len);
 
-	if (wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, pmk,
+	if (wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0, NULL, 0, pmk,
 			    pmk_len, KEY_FLAG_PMK)) {
 		wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
 	}
@@ -533,7 +537,7 @@
 }
 
 
-static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
+static int wpa_supplicant_set_key(void *_wpa_s, int link_id, enum wpa_alg alg,
 				  const u8 *addr, int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
 				  const u8 *key, size_t key_len,
@@ -562,8 +566,8 @@
 		wpa_s->last_tk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
-			       key, key_len, key_flag);
+	return wpa_drv_set_key(wpa_s, link_id, alg, addr, key_idx, set_tx, seq,
+			       seq_len, key, key_len, key_flag);
 }
 
 
@@ -948,28 +952,9 @@
 void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			const char *field_name, const char *txt)
 {
-	char *buf;
-	size_t buflen;
-	int len;
-
-	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return;
-	len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
-			  field_name, ssid->id, txt);
-	if (os_snprintf_error(buflen, len)) {
-		os_free(buf);
-		return;
-	}
-	if (ssid->ssid && buflen > len + ssid->ssid_len) {
-		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
-		len += ssid->ssid_len;
-		buf[len] = '\0';
-	}
-	buf[buflen - 1] = '\0';
-	wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
-	os_free(buf);
+	wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s-%d:%s needed for SSID %s",
+		field_name, ssid->id, txt,
+		wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 }
 
 
@@ -1089,6 +1074,12 @@
 	wpa_drv_set_supp_port(wpa_s, authorized);
 }
 
+static void wpa_supplicant_permanent_id_req_denied_cb(void *ctx)
+{
+	struct wpas_supplicant *wpa_s = ctx;
+
+	wpas_notify_permanent_id_req_denied(wpa_s);
+}
 
 static void wpa_supplicant_cert_cb(void *ctx, struct tls_cert_data *cert,
 				   const char *cert_hash)
@@ -1183,6 +1174,23 @@
 
 	wpas_notify_open_ssl_failure(wpa_s, reason_string);
 }
+
+static bool wpas_encryption_required(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_s->wpa &&
+		wpa_sm_has_ptk_installed(wpa_s->wpa) &&
+		wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+static ssize_t wpa_supplicant_get_certificate_cb(
+						const char* alias, uint8_t** value)
+{
+	wpa_printf(MSG_INFO, "wpa_supplicant_get_certificate_cb");
+	return wpas_get_certificate(alias, value);
+}
+
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -1224,6 +1232,7 @@
 	ctx->port_cb = wpa_supplicant_port_cb;
 	ctx->cb = wpa_supplicant_eapol_cb;
 	ctx->cert_cb = wpa_supplicant_cert_cb;
+	ctx->permanent_id_req_denied_cb = wpa_supplicant_permanent_id_req_denied_cb;
 	ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
 	ctx->status_cb = wpa_supplicant_status_cb;
 	ctx->eap_error_cb = wpa_supplicant_eap_error_cb;
@@ -1231,6 +1240,8 @@
 	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->eap_method_selected_cb = wpa_supplicant_eap_method_selected_cb;
 	ctx->open_ssl_failure_cb = wpa_supplicant_open_ssl_failure_cb;
+	ctx->get_certificate_cb = wpa_supplicant_get_certificate_cb;
+	ctx->encryption_required = wpas_encryption_required;
 	ctx->cb_ctx = wpa_s;
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {
@@ -1265,7 +1276,7 @@
 
 	if (wpa_s->conf->key_mgmt_offload &&
 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
-		return wpa_drv_set_key(wpa_s, 0, NULL, 0, 0,
+		return wpa_drv_set_key(wpa_s, -1, 0, NULL, 0, 0,
 				       NULL, 0, pmk, pmk_len, KEY_FLAG_PMK);
 	else
 		return 0;
@@ -1313,9 +1324,8 @@
 }
 
 
-static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap)
 {
-	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
 	int changed = 0;
 
@@ -1328,7 +1338,7 @@
 #ifdef CONFIG_SAE
 	if ((bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) &&
 	    wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
-	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 	    (ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
 	     (ssid->group_cipher & WPA_CIPHER_TKIP))) {
 		wpa_printf(MSG_DEBUG,
@@ -1343,7 +1353,7 @@
 	    wpa_s->sme.sae.state == SAE_ACCEPTED &&
 	    wpa_s->sme.sae.pk &&
 #endif /* CONFIG_SME */
-	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
 	    (ssid->sae_pk != SAE_PK_MODE_ONLY ||
 	     ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
 	     (ssid->group_cipher & WPA_CIPHER_TKIP))) {
@@ -1398,17 +1408,50 @@
 }
 
 
-static void wpa_supplicant_store_ptk(void *ctx, u8 *addr, int cipher,
+static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
+{
+	struct wpa_supplicant *wpa_s = _wpa_s;
+	wpas_transition_disable(wpa_s, bitmap);
+}
+
+
+static void wpa_supplicant_store_ptk(void *ctx, const u8 *addr, int cipher,
 				     u32 life_time, const struct wpa_ptk *ptk)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	ptksa_cache_add(wpa_s->ptksa, addr, cipher, life_time, ptk);
+	ptksa_cache_add(wpa_s->ptksa, wpa_s->own_addr, addr, cipher, life_time,
+			ptk, NULL, NULL, 0);
 }
 
 #endif /* CONFIG_NO_WPA */
 
 
+#ifdef CONFIG_PASN
+static int wpa_supplicant_set_ltf_keyseed(void *_wpa_s, const u8 *own_addr,
+					  const u8 *peer_addr,
+					  size_t ltf_keyseed_len,
+					  const u8 *ltf_keyseed)
+{
+	struct wpa_supplicant *wpa_s = _wpa_s;
+
+	return wpa_drv_set_secure_ranging_ctx(wpa_s, own_addr, peer_addr, 0, 0,
+					      NULL, ltf_keyseed_len,
+					      ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
+static void
+wpa_supplicant_notify_pmksa_cache_entry(void *_wpa_s,
+					struct rsn_pmksa_cache_entry *entry)
+{
+	struct wpa_supplicant *wpa_s = _wpa_s;
+
+	wpas_notify_pmk_cache_added(wpa_s, entry);
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -1471,6 +1514,10 @@
 	ctx->channel_info = wpa_supplicant_channel_info;
 	ctx->transition_disable = wpa_supplicant_transition_disable;
 	ctx->store_ptk = wpa_supplicant_store_ptk;
+#ifdef CONFIG_PASN
+	ctx->set_ltf_keyseed = wpa_supplicant_set_ltf_keyseed;
+#endif /* CONFIG_PASN */
+	ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry;
 
 	wpa_s->wpa = wpa_sm_init(ctx);
 	if (wpa_s->wpa == NULL) {
diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h
index 338af4e..dd692b9 100644
--- a/wpa_supplicant/wpas_glue.h
+++ b/wpa_supplicant/wpas_glue.h
@@ -27,4 +27,6 @@
 void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 			const char *field_name, const char *txt);
 
+void wpas_transition_disable(struct wpa_supplicant *wpa_s, u8 bitmap);
+
 #endif /* WPAS_GLUE_H */
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index c3ef93b..600b3bc 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -98,6 +98,12 @@
 }
 
 
+static int wpas_set_offload(void *wpa_s, u8 offload)
+{
+	return wpa_drv_set_offload(wpa_s, offload);
+}
+
+
 static unsigned int conf_offset_val(enum confidentiality_offset co)
 {
 	switch (co) {
@@ -219,6 +225,7 @@
 	kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
 	kay_ctx->enable_encrypt = wpas_enable_encrypt;
 	kay_ctx->set_replay_protect = wpas_set_replay_protect;
+	kay_ctx->set_offload = wpas_set_offload;
 	kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
 	kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
 	kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
@@ -239,7 +246,8 @@
 	kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
 
 	res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_replay_protect,
-				  ssid->macsec_replay_window, ssid->macsec_port,
+				  ssid->macsec_replay_window,
+				  ssid->macsec_offload, ssid->macsec_port,
 				  ssid->mka_priority, ssid->macsec_csindex,
 				  wpa_s->ifname, wpa_s->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
@@ -262,32 +270,6 @@
 }
 
 
-static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s,
-					  const u8 *addr, u8 *sid, size_t *len)
-{
-	const u8 *session_id;
-	size_t id_len, need_len;
-
-	session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len);
-	if (session_id == NULL) {
-		wpa_printf(MSG_DEBUG,
-			   "Failed to get SessionID from EAPOL state machines");
-		return -1;
-	}
-
-	need_len = 1 + 2 * 32 /* random size */;
-	if (need_len > id_len) {
-		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
-		return -1;
-	}
-
-	os_memcpy(sid, session_id, need_len);
-	*len = need_len;
-
-	return 0;
-}
-
-
 static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr,
 				   u8 *msk, size_t *len)
 {
@@ -320,8 +302,8 @@
 void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
 				      const u8 *peer_addr)
 {
-	u8 *sid;
-	size_t sid_len = 128;
+	const u8 *sid;
+	size_t sid_len;
 	struct mka_key_name *ckn;
 	struct mka_key *cak;
 	struct mka_key *msk;
@@ -335,10 +317,9 @@
 		   MACSTR, MAC2STR(peer_addr));
 
 	msk = os_zalloc(sizeof(*msk));
-	sid = os_zalloc(sid_len);
 	ckn = os_zalloc(sizeof(*ckn));
 	cak = os_zalloc(sizeof(*cak));
-	if (!msk || !sid || !ckn || !cak)
+	if (!msk || !ckn || !cak)
 		goto fail;
 
 	msk->len = DEFAULT_KEY_LEN;
@@ -347,8 +328,8 @@
 		goto fail;
 	}
 
-	if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len))
-	{
+	sid = eapol_sm_get_session_id(wpa_s->eapol, &sid_len);
+	if (!sid) {
 		wpa_printf(MSG_ERROR,
 			   "IEEE 802.1X: Could not get EAP Session Id");
 		goto fail;
@@ -382,7 +363,6 @@
 		os_memset(msk, 0, sizeof(*msk));
 		os_free(msk);
 	}
-	os_free(sid);
 	os_free(ckn);
 	if (cak) {
 		os_memset(cak, 0, sizeof(*cak));
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 7428f02..cd94b64 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -79,6 +79,18 @@
 }
 
 
+static struct wpabuf * wpas_wps_get_wps_ie(struct wpa_bss *bss)
+{
+	/* Return the latest receive WPS IE from the AP regardless of whether
+	 * it was from a Beacon frame or Probe Response frame to avoid using
+	 * stale information. */
+	if (bss->beacon_newer)
+		return wpa_bss_get_vendor_ie_multi_beacon(bss,
+							  WPS_IE_VENDOR_TYPE);
+	return wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+}
+
+
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
 	if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
@@ -141,8 +153,7 @@
 			struct wpabuf *wps;
 			struct wps_parse_attr attr;
 
-			wps = wpa_bss_get_vendor_ie_multi(bss,
-							  WPS_IE_VENDOR_TYPE);
+			wps = wpas_wps_get_wps_ie(bss);
 			if (wps && wps_parse_msg(wps, &attr) == 0 &&
 			    attr.wps_state &&
 			    *attr.wps_state == WPS_STATE_CONFIGURED)
@@ -1002,6 +1013,8 @@
 	 * during an EAP-WSC exchange).
 	 */
 	wpas_notify_wps_event_fail(wpa_s, &data.fail);
+	wpa_s->supp_pbc_active = false;
+	wpa_s->wps_overlap = false;
 	wpas_clear_wps(wpa_s);
 }
 
@@ -1204,6 +1217,8 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	if (multi_ap_backhaul_sta)
 		ssid->multi_ap_backhaul_sta = 1;
+	wpa_s->supp_pbc_active = true;
+	wpa_s->wps_overlap = false;
 	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
@@ -1371,6 +1386,8 @@
 			wpas_clear_wps(wpa_s);
 	}
 
+	wpa_s->supp_pbc_active = false;
+	wpa_s->wps_overlap = false;
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL);
 	wpa_s->after_wps = 0;
 
@@ -1705,7 +1722,7 @@
 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
 		return -1;
 
-	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	wps_ie = wpas_wps_get_wps_ie(bss);
 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
 		if (!wps_ie) {
 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
@@ -1779,13 +1796,13 @@
 	int ret = 0;
 
 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
-		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		wps_ie = wpas_wps_get_wps_ie(bss);
 		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
 			/* allow wildcard SSID for WPS PBC */
 			ret = 1;
 		}
 	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
-		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+		wps_ie = wpas_wps_get_wps_ie(bss);
 		if (wps_ie &&
 		    (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
 		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
@@ -1828,9 +1845,40 @@
 }
 
 
+static bool wpas_wps_is_pbc_overlap(struct wps_ap_info *ap,
+				    struct wpa_bss *selected,
+				    struct wpa_ssid *ssid,
+				    const u8 *sel_uuid)
+{
+	if (!ap->pbc_active ||
+	    os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
+		return false;
+
+	if (!is_zero_ether_addr(ssid->bssid) &&
+	    os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
+			   " in active PBC mode due to local BSSID limitation",
+			   MAC2STR(ap->bssid));
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " MACSTR,
+		   MAC2STR(ap->bssid));
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+		    ap->uuid, UUID_LEN);
+	if (!sel_uuid || os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0)
+		return true;
+
+	/* TODO: verify that this is reasonable dual-band situation */
+
+	return false;
+}
+
+
 int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *selected, struct wpa_ssid *ssid)
 {
+	struct wpa_supplicant *iface;
 	const u8 *sel_uuid;
 	struct wpabuf *wps_ie;
 	int ret = 0;
@@ -1859,36 +1907,21 @@
 		sel_uuid = NULL;
 	}
 
-	for (i = 0; i < wpa_s->num_wps_ap; i++) {
-		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		for (i = 0; i < iface->num_wps_ap; i++) {
+			struct wps_ap_info *ap = &iface->wps_ap[i];
 
-		if (!ap->pbc_active ||
-		    os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
-			continue;
-
-		if (!is_zero_ether_addr(ssid->bssid) &&
-		    os_memcmp(ap->bssid, ssid->bssid, ETH_ALEN) != 0) {
-			wpa_printf(MSG_DEBUG, "WPS: Ignore another BSS " MACSTR
-				   " in active PBC mode due to local BSSID limitation",
-				   MAC2STR(ap->bssid));
-			continue;
+			if (wpas_wps_is_pbc_overlap(ap, selected, ssid,
+						    sel_uuid)) {
+				ret = 1; /* PBC overlap */
+				wpa_msg(iface, MSG_INFO,
+					"WPS: PBC overlap detected: "
+					MACSTR " and " MACSTR,
+					MAC2STR(selected->bssid),
+					MAC2STR(ap->bssid));
+				break;
+			}
 		}
-
-		wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
-			   MACSTR, MAC2STR(ap->bssid));
-		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
-			    ap->uuid, UUID_LEN);
-		if (sel_uuid == NULL ||
-		    os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
-			ret = 1; /* PBC overlap */
-			wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
-				MACSTR " and " MACSTR,
-				MAC2STR(selected->bssid),
-				MAC2STR(ap->bssid));
-			break;
-		}
-
-		/* TODO: verify that this is reasonable dual-band situation */
 	}
 
 	wpabuf_free(wps_ie);
@@ -1907,7 +1940,8 @@
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		struct wpabuf *ie;
-		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+
+		ie = wpas_wps_get_wps_ie(bss);
 		if (!ie)
 			continue;
 		if (wps_is_selected_pbc_registrar(ie))
@@ -2995,6 +3029,48 @@
 }
 
 
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *iface;
+
+	for (iface = global->ifaces; iface; iface = iface->next) {
+		if (iface == wpa_s)
+			continue;
+
+		if (!iface->supp_pbc_active)
+			continue;
+
+		/* Scan results are available for both links. While the current
+		 * link will proceed for network selection, ensure the partner
+		 * link also gets an attempt at network selection and connect
+		 * with the selected BSS. */
+		if (iface->wps_scan_done)
+			wpa_wps_supplicant_fast_associate(iface);
+		else
+			return false;
+	}
+
+	return true;
+}
+
+
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *iface;
+
+	for (iface = global->ifaces; iface; iface = iface->next) {
+		if (iface == wpa_s)
+			continue;
+		if (iface->wps_overlap)
+			return true;
+	}
+
+	return false;
+}
+
+
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wps_ap_info *ap;
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 41b9298..5c8b833 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -87,6 +87,8 @@
 				    const struct wpabuf *sel);
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_results *scan_res);
+bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s);
+bool wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
 
@@ -146,6 +148,17 @@
 {
 }
 
+static inline bool wpas_wps_partner_link_scan_done(struct wpa_supplicant *wpa_s)
+{
+	return true;
+}
+
+static inline bool
+wpas_wps_partner_link_overlap_detect(struct wpa_supplicant *wpa_s)
+{
+	return false;
+}
+
 static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
 					 const u8 *bssid)
 {