[automerger skipped] Security fix for out of bound read in p2p_invite am: 1d9ae9a554 am: 34e4630ad2 -s ours

am skip reason: Merged-In I00f8ba9bea7bd36b52ae66250233230cac22ae83 with SHA-1 947b5e2ed3 is already in history

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

Change-Id: I13a9415484a2e05e4d6a248bcdaf6ce354821681
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..d41a71a 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)
@@ -2361,7 +2365,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 +2379,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 +2418,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 +2585,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 +3481,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 +3640,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 +4286,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) {
@@ -4460,6 +4482,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 +4503,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) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index ad994d4..94f31ee 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -861,6 +861,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 +902,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 +983,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 +1388,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 +1716,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;
@@ -2531,6 +2561,9 @@
 	case 160:
 		bandwidth = CHAN_WIDTH_160;
 		break;
+	case 320:
+		bandwidth = CHAN_WIDTH_320;
+		break;
 	default:
 		bandwidth = CHAN_WIDTH_20;
 		break;
@@ -3184,6 +3217,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 +3390,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;
@@ -3674,7 +3712,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 +4234,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..c5e74a6 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,7 +1024,7 @@
 # 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
 
 ##### IEEE 802.1X-2004 related configuration ##################################
@@ -1412,6 +1415,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
@@ -2514,12 +2523,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 +2808,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 +3089,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 +3137,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..ec40dda 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)
@@ -1208,6 +1215,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 +1492,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 +1517,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 +1586,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 +1686,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 +1756,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" },
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..5c6e2f6 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -218,7 +218,9 @@
 	} d;
 } AttrOrOID;
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
 DEFINE_STACK_OF(AttrOrOID)
+#endif
 
 typedef struct {
 	int type;
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..8cb5813 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);
@@ -370,48 +421,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 +454,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 +592,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 +641,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)
 {
@@ -656,7 +719,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,8 +727,9 @@
 	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
@@ -687,6 +751,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 +768,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 +778,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 +787,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 +816,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 +833,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)) {
@@ -873,12 +961,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;
 		}
 	}
 
@@ -908,21 +998,50 @@
 }
 
 
+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 +1051,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);
 }
 
 
@@ -989,8 +1107,19 @@
 	iface->conf->channel = ideal_chan->chan;
 	iface->freq = ideal_chan->freq;
 
-	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 +1195,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..5dc8a8f 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;
 }
 
@@ -1449,6 +1462,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 +1551,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..6dbb223 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -443,6 +443,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 +671,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;
 
@@ -755,12 +756,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 */
@@ -913,6 +917,8 @@
 	u8 ext_capa[EXT_CAPA_MAX_LEN];
 
 	u8 rnr;
+	char *config_id;
+	bool xrates_supported;
 };
 
 /**
@@ -1048,7 +1054,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 +1079,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 +1101,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 +1109,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,7 +1140,7 @@
 
 	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;
 #endif /* CONFIG_IEEE80211BE */
@@ -1141,10 +1149,17 @@
 #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 +1173,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 +1210,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..1ffc37f 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);
@@ -716,6 +724,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 +906,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 +989,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 +1027,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..dbc6b06 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -462,14 +462,86 @@
 }
 
 
+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,
+				   bool bcast_probe_resp, 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
@@ -516,6 +588,8 @@
 	}
 #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);
@@ -531,6 +605,9 @@
 					   WLAN_FC_STYPE_PROBE_RESP);
 	if (req)
 		os_memcpy(resp->da, req->sa, ETH_ALEN);
+	else if (bcast_probe_resp)
+		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 +649,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 +664,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 +1220,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, false, elems.mbssid_known_bss,
+				      elems.mbssid_known_bss_len);
 	if (resp == NULL)
 		return;
 
@@ -1163,7 +1253,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 +1301,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, false, NULL, 0);
 }
 
 #endif /* NEED_AP_MLME */
@@ -1228,7 +1319,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,
+				      true, NULL, 0);
 }
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1275,22 +1367,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
@@ -1507,7 +1601,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);
@@ -1559,6 +1658,9 @@
 	}
 #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 +1747,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 +1800,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 +1884,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 +1986,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;
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..168e5f5 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,9 @@
 		return "REMOVE";
 	case STA_DISASSOC_FROM_CLI:
 		return "DISASSOC_FROM_CLI";
+	default:
+		return "?";
 	}
-
-	return "?";
 }
 
 
@@ -218,6 +218,7 @@
 {
 	int len, res, ret, i;
 	const char *keyid;
+	const u8 *dpp_pkhash;
 
 	if (!sta)
 		return 0;
@@ -386,6 +387,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;
 }
 
@@ -1060,7 +1073,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..4c33e86 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;
@@ -859,14 +862,16 @@
 			     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",
 		       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,
@@ -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;
 	}
 
@@ -979,10 +989,10 @@
 	hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
 	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 +1001,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",
 		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);
 	if (!finished)
 		return;
 
@@ -1007,6 +1017,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 +1163,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 +1173,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 +1572,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 +1587,7 @@
 		}
 	}
 
-	ieee802_1x_receive(hapd, src, data, data_len);
+	ieee802_1x_receive(hapd, src, data, data_len, encrypted);
 }
 
 #endif /* HOSTAPD */
@@ -1952,7 +1982,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)
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..58492e5 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
@@ -1387,29 +1460,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;
 }
@@ -1718,7 +1773,7 @@
 			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 +1839,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 +1941,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 +2189,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 +2203,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 +2874,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 +3117,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 +3591,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;
@@ -3538,15 +3643,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;
 	}
 
@@ -3676,7 +3781,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 +3795,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 +3901,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..a88f9b6 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);
@@ -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..ed5ff41 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");
 	}
@@ -1087,13 +1087,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..e53f0dc 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;
 
@@ -4539,6 +3729,15 @@
 					 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,
@@ -4629,6 +3828,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)
@@ -4652,9 +3854,6 @@
 		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 +3885,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,7 +3903,7 @@
 			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,
@@ -5185,7 +4384,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);
@@ -6589,7 +5788,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 +5892,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 +6245,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 +6254,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 +6662,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 +6670,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 +6766,253 @@
 	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;
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index fa1f47b..5f443fc 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,7 +45,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 +215,12 @@
 		       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);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index dbbf9a6..caaadce 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,26 @@
 {
 	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;
 
 	*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 +223,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 +231,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 +239,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 +253,7 @@
 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
 	oper->oper_info.ccfs1 = seg1;
 
-	return pos + 4;
+	return pos + elen;
 }
 
 
@@ -257,7 +300,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 +310,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 +326,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 +344,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 +362,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..12273c3 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,7 +219,7 @@
 	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);
+		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
 		u8 control;
 
@@ -430,10 +432,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 +533,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..681b6d7 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -96,12 +96,12 @@
 		hapd->iconf->vht_oper_centr_freq_seg1_idx;
 
 	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
-	if (hapd->iconf->vht_oper_chwidth == 2) {
+	if (hapd->iconf->vht_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 +109,13 @@
 			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 (hapd->iconf->vht_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/wpa_auth.c b/src/ap/wpa_auth.c
index ad91883..7aff64f 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"
@@ -149,6 +150,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)
 {
@@ -735,16 +750,13 @@
 {
 #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(sm->addr), sm->ip_addr_bit);
+		bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
 	}
 #endif /* CONFIG_P2P */
 	if (sm->GUpdateStationKeys) {
@@ -1641,22 +1653,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 +1685,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);
@@ -1705,6 +1720,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 +1740,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;
@@ -2181,13 +2212,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 +2242,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;
@@ -2307,8 +2346,10 @@
 	}
 	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);
 }
 
@@ -2320,6 +2361,7 @@
 	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 +2375,33 @@
 		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,
+						sm->addr, sm->wpa_auth->addr,
+						sm->pmk_r1_name, ptk,
+						ptk_name, sm->wpa_key_mgmt,
+						sm->pairwise, kdk_len);
+		} else {
+			ret = wpa_auth_derive_ptk_ft(sm, ptk);
 		}
-		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 +2415,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",
+			     sm->wpa_auth->addr, sm->addr, 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;
 }
 
 
@@ -2389,6 +2466,19 @@
 			      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,7 +2487,6 @@
 		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,
@@ -2405,7 +2494,7 @@
 				      conf->r0_key_holder,
 				      conf->r0_key_holder_len,
 				      sm->addr, pmk_r0, pmk_r0_name,
-				      use_sha384) < 0)
+				      sm->wpa_key_mgmt) < 0)
 			return -1;
 
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
@@ -2413,7 +2502,7 @@
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
 					     sm->addr, sm->pmk_r1_name,
-					     use_sha384);
+					     fils_ft_len);
 		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
 			return -1;
@@ -2892,6 +2981,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,
@@ -3178,12 +3281,14 @@
 		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(sm->addr), sm->ip_addr_bit);
 		}
 	}
 #endif /* CONFIG_P2P */
@@ -3487,6 +3592,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];
@@ -3600,9 +3720,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 +3817,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;
 
@@ -4704,11 +4839,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 +4882,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)
@@ -4866,16 +5011,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 +5052,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 +5122,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 +5160,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)
@@ -5273,7 +5452,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 +5529,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 */
@@ -5569,9 +5750,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,
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..88d63bb 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"
@@ -805,15 +806,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 +837,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 +867,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)
@@ -2081,9 +2112,7 @@
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
 {
 	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;
+	size_t pmk_r0_len, pmk_r1_len;
 	u8 pmk_r1[PMK_LEN_MAX];
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
@@ -2101,6 +2130,17 @@
 	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;
+	pmk_r1_len = pmk_r0_len;
+
 	if (sm->xxkey_len > 0) {
 		mpmk = sm->xxkey;
 		mpmk_len = sm->xxkey_len;
@@ -2127,7 +2167,7 @@
 	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)
+			      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,
@@ -2521,12 +2561,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 +2736,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 +2745,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 +2768,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 +2793,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 +2834,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 +2868,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,6 +2926,18 @@
 			     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;
@@ -2887,7 +2976,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 +3077,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 +3137,8 @@
 
 	*out_session_timeout = session_timeout;
 
+	*pmk_r1_len = r0->pmk_r0_len;
+
 	return 0;
 }
 
@@ -3066,8 +3159,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 +3170,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 +3184,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 +3209,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 +3289,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 +3337,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 +3387,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 +3455,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 +3498,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 +3580,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 +3594,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 +3607,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 +3626,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 +4196,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..7ed3f2b 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
@@ -297,7 +299,7 @@
 
 #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,
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/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..d2fc00f 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;
 
@@ -1499,6 +1615,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 +1662,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 +1735,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 +1804,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 +1867,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 +1909,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 +1927,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 +4146,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 +4224,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 +4240,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 +4254,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 +4434,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 +4462,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 +4477,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 +4491,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 +4547,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 +4558,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 +4584,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 +4602,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 +4611,8 @@
 			   bi->curve->name,
 			   pkhash,
 			   bi->version,
-			   supp_curves);
+			   supp_curves,
+			   host);
 }
 
 
@@ -4796,6 +5013,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 +5042,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 +5092,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..6646301 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;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index d87a2ca..d8ca168 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);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 44335de..3c3667f 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,39 +408,14 @@
 		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
@@ -385,6 +440,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 +649,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 +667,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)) {
@@ -886,7 +943,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 +953,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 +1009,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 +1114,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 +1150,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 +1163,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 +1171,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 +1200,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 +1326,9 @@
 		if (chan < 25 || chan > 29)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1313,8 +1377,9 @@
 		if (chan != 25)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1369,8 +1434,9 @@
 		if (chan != 25)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1395,8 +1461,9 @@
 		if (chan < 149 || chan > 165)
 			return -1;
 		return 5000 + 5 * chan;
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -1458,6 +1525,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 +1549,9 @@
 		if (chan < 25 || chan > 29)
 			return -1;
 		return 56160 + 2160 * (chan - 24);
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 /**
@@ -1822,6 +1891,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 +1983,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 +2347,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 +2372,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 +2678,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 +2688,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 +2803,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 +2833,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 +2925,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 +2933,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 +2952,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 +3071,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 +3117,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 +3148,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 +3160,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..9a6915d 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;
@@ -215,9 +216,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 +281,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 +339,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..e5e4e83 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
 
@@ -2440,13 +2457,19 @@
 #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))
 
 /* 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 +2477,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 +2489,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 +2567,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
@@ -2685,4 +2848,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..f972061 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-2022, 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,111 @@
  *
  *	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.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -974,7 +1080,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 +1089,29 @@
 	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,
 };
 
+/* 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 */
@@ -1270,6 +1397,9 @@
 
 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 +1444,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 +1630,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 +1658,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 +1742,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 +1788,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 +1796,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 */
 };
 
@@ -2532,6 +2704,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 +2823,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 +2920,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
@@ -4070,6 +4345,22 @@
 	 * 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,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -4683,7 +4974,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 +5176,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 +5243,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,
@@ -5727,7 +6064,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 +6150,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 +6183,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 +6219,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 +6237,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 +6516,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 +6556,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,
@@ -6936,6 +7339,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 +8491,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 +9109,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 =
@@ -9359,6 +9889,14 @@
  * @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.
+ *
  */
 enum qca_wlan_vendor_attr_twt_nudge {
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0,
@@ -9367,6 +9905,7 @@
 	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,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST,
@@ -10657,14 +11196,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 +11547,18 @@
  * 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.
  */
 enum qca_wlan_vendor_attr_get_sta_info {
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -11054,6 +11612,8 @@
 	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,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -11573,24 +12133,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
@@ -11821,6 +12419,470 @@
 };
 
 /**
+ * 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_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. 0-passive, 1-active.
+	 */
+	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,
+
+	/* 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 unsigned 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 unsigned 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 unsigned 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 unsigned 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 unsigned 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 unsigned 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 unsigned 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 unsigned value to indicate data RSSI in dBm when aborting the
+	 * roam scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ROAM_STATS_DATA_RSSI = 35,
+	/* 8-bit unsigned 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.
+	 * 0 - Partial roam scan, 1 - Full roam scan
+	 */
+	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,
+
+	/* 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 +13260,1584 @@
 	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.
+ */
+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,
+
+	/* 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.
+ */
+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,
+
+	/* 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,
+};
+
 #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..edb3f0c 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -126,9 +126,28 @@
 }
 #endif
 
+static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
+
+struct tls_session_data {
+	struct dl_list list;
+	struct wpabuf *buf;
+};
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+	struct dl_list sessions; /* struct tls_session_data */
+};
+
+static struct tls_context *tls_global = NULL;
+static tls_get_certificate_cb certificate_callback_global = NULL;
+
 #ifdef ANDROID
 #include <openssl/pem.h>
-#include <keystore/keystore_get.h>
 
 #include <log/log.h>
 #include <log/log_event_list.h>
@@ -152,9 +171,12 @@
 {
 	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);
+	if (tls_global != NULL && certificate_callback_global != NULL) {
+		wpa_printf(MSG_INFO, "Retrieving certificate using callback");
+		int length = (*certificate_callback_global)(tls_global->cb_ctx, alias, &value);
+		if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+			BIO_write(bio, value, length);
+	}
 	free(value);
 	return bio;
 }
@@ -229,26 +251,6 @@
 
 #endif /* ANDROID */
 
-static int tls_openssl_ref_count = 0;
-static int tls_ex_idx_session = -1;
-
-struct tls_session_data {
-	struct dl_list list;
-	struct wpabuf *buf;
-};
-
-struct tls_context {
-	void (*event_cb)(void *ctx, enum tls_event ev,
-			 union tls_event_data *data);
-	void *cb_ctx;
-	int cert_in_cb;
-	char *ocsp_stapling_response;
-	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;
@@ -1584,6 +1586,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",
@@ -3261,7 +3272,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 };
 
@@ -4180,8 +4195,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 +5421,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 +5531,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 +5603,9 @@
 		return "DH";
 	case EVP_PKEY_EC:
 		return "EC";
+	default:
+		return "?";
 	}
-	return "?";
 }
 
 
@@ -6014,3 +6032,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..4ddfe07 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -66,6 +66,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 */
@@ -211,6 +212,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 +669,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 +721,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 +899,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 +1048,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 +1298,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 +1659,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 +1695,42 @@
 	 * 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;
 };
 
 struct wpa_driver_mesh_bss_params {
@@ -1767,6 +1884,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 +1987,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 +2167,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 +2188,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,6 +2311,14 @@
 
 	/* 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;
 };
 
 
@@ -2190,14 +2334,19 @@
 #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)
 
 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 +2356,25 @@
 	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 */
 };
 
 struct hostapd_sta_add_params {
@@ -2340,11 +2501,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 +2510,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
@@ -2594,6 +2754,90 @@
 	const u8 *pmkid;
 };
 
+#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 */
 enum nested_attr {
 	NESTED_ATTR_NOT_USED = 0,
@@ -2601,6 +2845,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 +3095,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 +4122,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
@@ -4445,14 +4727,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 +4926,37 @@
 	 */
 	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);
+
 #ifdef CONFIG_TESTING_OPTIONS
 	int (*register_frame)(void *priv, u16 type,
 			      const u8 *match, size_t match_len,
@@ -5234,6 +5550,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,
 };
 
 
@@ -5856,6 +6196,7 @@
 		const u8 *src;
 		const u8 *data;
 		size_t data_len;
+		enum frame_encryption encrypted;
 	} eapol_rx;
 
 	/**
@@ -5948,6 +6289,7 @@
 	 * @ch_width: Channel width
 	 * @cf1: Center frequency 1
 	 * @cf2: Center frequency 2
+	 * @link_id: Link ID of the MLO link
 	 */
 	struct ch_switch {
 		int freq;
@@ -5956,6 +6298,7 @@
 		enum chan_width ch_width;
 		int cf1;
 		int cf2;
+		int link_id;
 	} ch_switch;
 
 	/**
@@ -6133,6 +6476,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 +6550,20 @@
 	event.eapol_rx.src = src;
 	event.eapol_rx.data = data;
 	event.eapol_rx.data_len = data_len;
+	event.eapol_rx.encrypted = FRAME_ENCRYPTION_UNKNOWN;
+	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)
+{
+	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;
 	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
 }
 
@@ -6210,6 +6580,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_nl80211.c b/src/drivers/driver_nl80211.c
index 6bd88be..c7e9bfe 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"
@@ -219,8 +221,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 +275,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;
+#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);
 }
 
 
@@ -1015,6 +1029,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 +1251,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 +1523,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 +1537,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 +1554,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 +1582,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 +1687,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 +1702,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];
@@ -2385,8 +2431,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 */
@@ -3130,9 +3175,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 +3221,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 +3430,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 +3438,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 +3575,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 +3643,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 +3873,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 +3986,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 +4045,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 +4159,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]) {
@@ -4563,18 +4650,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 +4704,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 +4734,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 */
 
 
@@ -4775,7 +4917,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. */
@@ -4943,11 +5086,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 */
@@ -5055,6 +5200,9 @@
 		case 160:
 			cw = NL80211_CHAN_WIDTH_160;
 			break;
+		case 320:
+			cw = NL80211_CHAN_WIDTH_320;
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -5107,8 +5255,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;
 }
@@ -6100,6 +6249,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 +6394,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 +6502,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 +6599,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 +6611,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,13 +6796,13 @@
 	    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;
@@ -6540,9 +6823,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 +6851,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 +6965,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 +7285,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 +7303,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 +7496,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 +7525,8 @@
 		[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 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -7272,34 +7569,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 +7637,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;
@@ -7328,6 +7657,16 @@
 				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 (stats[NL80211_STA_INFO_RX_BITRATE] &&
@@ -7341,9 +7680,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]) {
@@ -7358,6 +7700,16 @@
 				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 (stats[NL80211_STA_INFO_TID_STATS])
@@ -7366,6 +7718,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)
@@ -8106,7 +8478,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];
 
@@ -8782,12 +9154,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 +9170,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 +9476,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 +9502,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 +9516,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 +9531,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 +9565,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)",
@@ -9886,6 +10430,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 +10481,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 +10505,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;
@@ -10727,6 +11303,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)
@@ -11441,9 +12018,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 +12053,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 +12079,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 +12149,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 +12206,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 +12219,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 +12655,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)
@@ -12496,6 +13308,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 +13379,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 +13393,7 @@
 #ifdef CONFIG_DPP
 	.dpp_listen = nl80211_dpp_listen,
 #endif /* CONFIG_DPP */
+	.get_sta_mlo_info = nl80211_get_sta_mlo_info,
 #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..694fb1b 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -68,6 +68,7 @@
 	unsigned int use_nl_connect:1;
 
 	u8 addr[ETH_ALEN];
+	u8 prev_addr[ETH_ALEN];
 
 	int freq;
 	int bandwidth;
@@ -128,6 +129,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 +182,8 @@
 	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;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -221,6 +225,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 +242,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 +275,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..2f3b85a 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;
 		}
 	}
 
@@ -668,6 +674,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 +874,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 +1078,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 +1138,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 +1226,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 +1395,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 +2033,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
@@ -2474,7 +2560,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 +2579,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 +2653,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 5c103a4..46e4efb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -178,6 +178,11 @@
 	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_AFTER_LAST)
 	}
 #undef C2S
@@ -414,11 +419,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 +726,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 +819,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);
 	}
@@ -673,6 +973,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,10 +990,10 @@
 
 
 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,
-				 int finished)
+				 struct nlattr *ifindex, struct nlattr *link,
+				 struct nlattr *freq, struct nlattr *type,
+				 struct nlattr *bw, struct nlattr *cf1,
+				 struct nlattr *cf2, int finished)
 {
 	struct i802_bss *bss;
 	union wpa_event_data data;
@@ -754,6 +1057,25 @@
 
 	if (finished)
 		bss->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 ?
@@ -923,8 +1245,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;
@@ -1080,6 +1405,7 @@
 		       struct nlattr *wmm, struct nlattr *req_ie)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 stype = 0, auth_type = 0;
 	const u8 *data;
 	size_t len;
 
@@ -1109,11 +1435,34 @@
 		   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) {
+
+	/* 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;
@@ -1335,7 +1684,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 +1716,7 @@
 		}
 	}
 	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-		char msg[500], *pos, *end;
+		char msg[MAX_REPORT_FREQS * 5], *pos, *end;
 		int res;
 
 		pos = msg;
@@ -1490,11 +1839,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 +2441,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 +2453,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 +2473,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 +2728,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 +2840,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 +2972,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 */
 
@@ -2712,6 +3156,9 @@
 				    struct nlattr **tb)
 {
 	const u8 *addr;
+	union wpa_event_data event;
+
+	os_memset(&event, 0, sizeof(event));
 
 	if (!tb[NL80211_ATTR_MAC] ||
 	    nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
@@ -2729,7 +3176,15 @@
 		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 +3244,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 +3266,7 @@
 {
 	u8 *src_addr;
 	u16 ethertype;
+	enum frame_encryption encrypted;
 
 	if (!tb[NL80211_ATTR_MAC] ||
 	    !tb[NL80211_ATTR_FRAME] ||
@@ -2816,6 +3275,8 @@
 
 	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;
 
 	switch (ethertype) {
 	case ETH_P_RSN_PREAUTH:
@@ -2824,9 +3285,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);
 		break;
 	default:
 		wpa_printf(MSG_INFO,
@@ -2923,8 +3385,8 @@
 	data.bss_color_collision.bitmap =
 		nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
 
-	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08lx",
-		   data.bss_color_collision.bitmap);
+	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
+		   (long long unsigned int) data.bss_color_collision.bitmap);
 	wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
 }
 
@@ -2975,17 +3437,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;
@@ -3087,7 +3542,7 @@
 		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,11 +3554,13 @@
 				   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],
@@ -3114,6 +3571,7 @@
 	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],
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..c14a91b 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
@@ -753,6 +772,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 +789,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 +1136,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
@@ -1237,6 +1271,16 @@
  *      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_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1481,6 +1525,13 @@
 
 	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,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2340,8 +2391,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 +2716,41 @@
  *	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.
  * @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 +3263,23 @@
 
 	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,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3229,6 +3334,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 +4961,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 +4988,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,
@@ -5872,7 +5986,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 +6286,14 @@
  * @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.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -6239,6 +6361,7 @@
 	NL80211_EXT_FEATURE_BSS_COLOR,
 	NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
 	NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+	NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
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/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..e1f72ac 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 */
@@ -5540,7 +5555,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 +5563,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 +5619,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..27bdac3 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)
@@ -1132,7 +1134,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);
 };
 
 
@@ -2338,6 +2341,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 +2402,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 +2427,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..afd8969 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -142,6 +142,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 +180,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 +294,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 +346,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 +598,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 +614,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 +647,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 +664,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,7 +693,7 @@
 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;
@@ -669,25 +710,30 @@
 
 	/* 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 */
 	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)))) {
 			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 +745,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 +760,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 +785,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 +904,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 +1140,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 +1184,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 +1233,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 +1529,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..bca5b90 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
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/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..e3c7892 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);
@@ -92,6 +94,13 @@
 	void (*transition_disable)(void *ctx, u8 bitmap);
 	void (*store_ptk)(void *ctx, 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..4d52542 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,12 +502,14 @@
 	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,
 			 sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
@@ -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..ed43cc1 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)
@@ -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/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..b4518bf 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,13 @@
 	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);
+}
diff --git a/wpa_supplicant/aidl/aidl.h b/wpa_supplicant/aidl/aidl.h
index e8778c5..f963e61 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,7 @@
 	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);
 #else   // CONFIG_CTRL_IFACE_AIDL
 static inline int wpas_aidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -171,6 +175,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 +228,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 +259,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 +328,10 @@
 						struct dscp_policy_data *policies,
 						int num_policies)
 {}
+static ssize_t wpas_aidl_get_certificate(const char* alias, uint8_t** value)
+{
+	return -1;
+}
 #endif  // CONFIG_CTRL_IFACE_AIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/aidl/aidl_manager.cpp b/wpa_supplicant/aidl/aidl_manager.cpp
index da90c38..5c36301 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;
 	}
 
@@ -654,38 +662,46 @@
 		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;
+	aidl_state_change_data.keyMgmtMask = (KeyMgmtMask) wpa_s->key_mgmt;
+	// wpa_supplicant sets the frequency on receiving the EVENT_ASSOC.
+	aidl_state_change_data.frequencyMhz =
+		wpa_s->wpa_state >= WPA_ASSOCIATED ? wpa_s->assoc_freq : 0;
 
 	// 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;
@@ -758,6 +774,30 @@
 }
 
 /**
+ * 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.
@@ -1319,7 +1359,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;
@@ -1349,14 +1389,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(
@@ -1558,19 +1616,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",
@@ -1579,29 +1636,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));
 }
 
 /**
@@ -1618,6 +1677,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
  *
@@ -1688,18 +1796,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);
 }
 
@@ -1905,24 +2017,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,
@@ -2108,6 +2229,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.
  *
@@ -2459,6 +2595,20 @@
 			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;
+}
+
 }  // namespace supplicant
 }  // namespace wifi
 }  // namespace hardware
diff --git a/wpa_supplicant/aidl/aidl_manager.h b/wpa_supplicant/aidl/aidl_manager.h
index 1ed6899..223f4b2 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,7 @@
 	void notifyQosPolicyRequest(struct wpa_supplicant *wpa_s,
 			struct dscp_policy_data *policies,
 			int num_policies);
+	ssize_t getCertificate(const char* alias, uint8_t** value);
 
 	// Methods called from aidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -186,6 +193,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 +282,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..a48fcb3 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,18 @@
 	// 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;
+	if (!isAnyEtherAddr(group_owner_bssid)) {
+		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 +139,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 +147,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 +162,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 +169,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 +193,8 @@
 	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, true)) {
 		ret = -1;
 	}
 
@@ -479,27 +203,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 +809,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 +1021,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 +1089,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 +1136,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 +1147,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 +1604,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 +1618,7 @@
 	} 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, false)) {
 			return createStatus(SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN);
 		} else {
 			return ndk::ScopedAStatus::ok();
@@ -1922,6 +1636,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 +1652,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 +1668,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 +1677,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 +1704,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 +1838,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 776d689..674b30c 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()};
@@ -1781,7 +1814,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;
@@ -1812,6 +1847,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;
@@ -1851,6 +1889,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),
@@ -1964,10 +2008,98 @@
 
 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)) {
+		for (int i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo_si.valid_links & BIT(i)))
+				continue;
+
+			SignalPollResult result;
+			result.linkId = 0;
+			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()};
+}
+
+
+std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+StaIface::addQosPolicyRequestForScsInternal(const std::vector<QosPolicyScsData>& qosPolicyData)
+{
+	return {std::vector<QosPolicyScsRequestStatus>(),
+		createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED)};
+}
+
+std::pair<std::vector<QosPolicyScsRequestStatus>, ndk::ScopedAStatus>
+StaIface::removeQosPolicyForScsInternal(const std::vector<uint8_t>& scsPolicyIds)
+{
+	return {std::vector<QosPolicyScsRequestStatus>(),
+		createStatus(SupplicantStatusCode::FAILURE_UNSUPPORTED)};
+}
+
 /**
  * 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 61c71a1..e245247 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)
 {
@@ -2005,10 +2032,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();
@@ -2024,6 +2059,7 @@
 		"openssl_ciphers")) {
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
+	setTlsFlagsFor192BitMode(false /*rsaMode */);
 	return ndk::ScopedAStatus::ok();
 }
 
@@ -2131,7 +2167,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.
@@ -2152,10 +2192,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) {
@@ -2489,6 +2538,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) &&
@@ -2572,13 +2626,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();
@@ -2597,6 +2651,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..f4c3811 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) {
@@ -414,7 +413,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 +706,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 +1243,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);
 }
 
 
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index ccd3e7b..865429e 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,
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..0f986bb 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
@@ -252,7 +252,8 @@
 	if (!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);
@@ -943,6 +993,14 @@
 		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 +1339,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 +1446,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..f3e2edb 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 */
@@ -2618,7 +2688,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 +2700,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 +2710,7 @@
 	{ INT_RANGE(beacon_prot, 0, 1) },
 	{ INT_RANGE(transition_disable, 0, 255) },
 	{ INT_RANGE(sae_pk, 0, 2) },
+	{ INT_RANGE(disable_eht, 0, 1)},
 };
 
 #undef OFFSET
@@ -2771,7 +2844,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 +2949,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 +3042,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 +3227,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 +3578,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 +3868,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 +4031,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 +4198,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 +4251,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 +4266,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;
 	}
 
@@ -5240,6 +5427,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 +5446,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 +5478,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..dbb79dd 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
@@ -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..1b8526b 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;
@@ -331,6 +339,7 @@
 	while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
 		if (os_strcmp(pos, "network={") == 0) {
 			ssid = wpa_config_read_network(f, &line, id++);
+			ssid->ro = ro;
 			if (ssid == NULL) {
 				wpa_printf(MSG_ERROR, "Line %d: failed to "
 					   "parse network block.", line);
@@ -833,6 +842,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 +889,7 @@
 #ifdef CONFIG_HE_OVERRIDES
 	INT(disable_he);
 #endif /* CONFIG_HE_OVERRIDES */
+	INT(disable_eht);
 
 #undef STR
 #undef INT
@@ -920,12 +931,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 +1006,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 +1061,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 +1382,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 +1574,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 +1661,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..33b46e5 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,7 @@
 
 	int he;
 
-	int max_oper_chwidth;
+	enum oper_chan_width max_oper_chwidth;
 
 	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
@@ -837,6 +860,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
@@ -965,12 +996,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 +1104,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 +1230,15 @@
 	 * 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;
 };
 
 #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..27397e9 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,
@@ -6822,7 +7128,7 @@
 	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, false);
 }
 
 
@@ -6897,7 +7203,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 &
@@ -8045,9 +8351,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 +8383,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 +8430,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 +8454,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 +8470,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 +8505,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 +8746,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 +8878,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 +9164,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 +9242,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 +9315,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 +9339,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 +9854,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 +10248,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 +10354,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 +10951,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 +11150,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 +11194,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 +11213,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 +11794,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 +12886,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 +12919,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 +13070,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 +13096,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..9c23588 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2344,6 +2344,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 +3945,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 +4532,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..ca8506f 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 {
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..9d1728c 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -355,6 +355,8 @@
 	char *pg_object_path = NULL;
 	int persistent_group = 0;
 	int freq = 0;
+	int retry_limit = 0;
+	int force_go_bssid = 0;
 	char *iface = NULL;
 	unsigned int group_id = 0;
 	struct wpa_ssid *ssid;
@@ -376,6 +378,14 @@
 			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, "force_go_bssid") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			force_go_bssid = entry.bool_value;
 		} else if (os_strcmp(entry.key, "persistent_group_object") ==
 			   0 &&
 			   entry.type == DBUS_TYPE_OBJECT_PATH)
@@ -426,7 +436,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,
+						  force_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..e21f912 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,131 @@
 	}
 	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.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..9a7d601 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -2,7 +2,7 @@
 
 <refentry>
   <refentryinfo>
-    <date>07 August 2019</date>
+    <date>13 September 2022</date>
   </refentryinfo>
 
   <refmeta>
@@ -22,6 +22,7 @@
       <arg>-D<replaceable>driver</replaceable></arg>
       <arg>-P<replaceable>PID_file</replaceable></arg>
       <arg>-f<replaceable>output file</replaceable></arg>
+      <arg>-I<replaceable>additional config file</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
   <refsect1>
@@ -341,6 +342,13 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-I filename</term>
+	<listitem>
+          <para>Path to additional configuration file.</para>
+	</listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-d</term>
 	<listitem>
 	  <para>Increase debugging verbosity (<option>-dd</option> even
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 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..5dd2a51 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
@@ -406,20 +407,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 +514,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)
 {
@@ -964,7 +963,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 +1116,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..3275b64 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,6 +388,9 @@
 
 	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);
 }
 
 
@@ -570,7 +609,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 +625,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 +646,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 +697,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 +742,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,10 +965,22 @@
 				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 &&
 				    wpa_key_mgmt_sae(ssid->key_mgmt)) {
 					if (debug_print)
@@ -1362,9 +1414,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 +1533,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 +1825,12 @@
 			   struct wpa_bss *selected,
 			   struct wpa_ssid *ssid)
 {
-	if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+	if (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) {
@@ -1963,9 +2019,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);
 
@@ -2278,6 +2334,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 +2403,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 +2439,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 +2575,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)
@@ -2801,6 +2876,28 @@
 		return -1;
 	}
 
+	/*
+	 * Update PMK in wpa_sm and the driver if roamed to WPA/WPA2 PSK from a
+	 * different AKM.
+	 */
+	if (wpa_s->key_mgmt != ie.key_mgmt &&
+	    wpa_key_mgmt_wpa_psk_no_sae(ie.key_mgmt)) {
+		if (!ssid->psk_set) {
+			wpa_dbg(wpa_s, MSG_INFO,
+				"No PSK available for association");
+			wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE", 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 +3120,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);
@@ -3391,6 +3489,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 +3663,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 +3693,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 +3733,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 +3770,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 +3838,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 +3903,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;
@@ -3837,7 +4085,7 @@
 			"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);
 #ifdef CONFIG_DPP2
 		wpas_dpp_send_conn_status_result(wpa_s,
 						 DPP_STATUS_AUTH_FAILURE);
@@ -4341,7 +4589,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) {
@@ -5236,6 +5484,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 +5639,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)
@@ -5647,22 +5929,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 +6209,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 9a459c2..4d0fc63 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))
@@ -1630,9 +1664,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..16530fb 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -643,18 +643,20 @@
 		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;
 		}
 	}
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 2eb9a7e..c1ed8c4 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -879,7 +879,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 +889,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 +902,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..3122c58 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"
@@ -237,6 +238,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 +290,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 +440,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 +457,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 +802,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);
 }
 
 
@@ -1099,13 +1114,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 +1134,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 +1357,15 @@
 
 	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);
+}
+
+
+void wpas_notify_signal_change(struct wpa_supplicant *wpa_s)
+{
+	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SIGNAL_CHANGE);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 996be84..b1824ec 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);
@@ -180,8 +183,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 +201,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 +221,9 @@
 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);
+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);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index bd53c5c..5dca8f7 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -470,8 +470,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..926ba7a 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);
@@ -192,7 +192,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 +219,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 +340,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 +379,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 +460,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 +553,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 +725,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 +1016,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 +1411,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 +2110,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 +2201,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 +2430,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;
@@ -3291,7 +3330,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,
+				false);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpa_msg_global(wpa_s, MSG_INFO,
@@ -3521,7 +3561,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,
+				      false);
 }
 
 
@@ -3832,6 +3873,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))
@@ -3973,8 +4018,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 +4029,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 +4648,7 @@
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0, 0, false);
+					0, 0, false, 0, false);
 			} else if (response_done) {
 				wpas_p2p_group_add(wpa_s, 1, freq,
 						   0, 0, 0, 0, 0, 0, false);
@@ -4720,7 +4771,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, false);
 	} 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 +4783,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 +4793,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 +4831,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,
@@ -5528,14 +5584,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 +5758,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;
@@ -5822,16 +5872,19 @@
 				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]))) {
+						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]);
+						i, pref_freq_list[i].freq);
 					i++;
 				}
 				if (i != *num_pref_freq) {
-					best_freq = pref_freq_list[i];
+					best_freq = pref_freq_list[i].freq;
 					wpa_printf(MSG_DEBUG,
 						"P2P: Using preferred_freq_list[%d]=%d",
 						i, best_freq);
@@ -5955,7 +6008,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 +6294,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 +6306,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 +6562,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 +6798,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 +6807,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 +6954,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 +6969,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 +6985,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 +6995,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,
+				 bool force_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)
@@ -6975,12 +7039,38 @@
 	if (params->passphrase)
 		ssid->passphrase = os_strdup(params->passphrase);
 
+	if (force_go_bssid && params->bssid_set) {
+		ssid->bssid_set = 1;
+		os_memcpy(ssid->bssid, params->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 +7090,8 @@
 				  int edmg,
 				  const struct p2p_channels *channels,
 				  int connection_timeout, int force_scan,
-				  bool allow_6ghz)
+				  bool allow_6ghz, int retry_limit,
+				  bool force_go_bssid)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -7045,6 +7136,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 +7160,8 @@
 		}
 
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
-					     force_scan);
+					     force_scan, retry_limit,
+					     force_go_bssid);
 	} else {
 		return -1;
 	}
@@ -7636,7 +7729,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 +7819,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 +7917,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);
@@ -8352,7 +8448,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 +8634,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 +8751,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 +9761,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 +9830,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 +9885,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 +9999,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..e113c62 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,
+				  bool force_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);
@@ -206,6 +210,7 @@
 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);
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..a561891 100644
--- a/wpa_supplicant/robust_av.c
+++ b/wpa_supplicant/robust_av.c
@@ -706,14 +706,15 @@
 
 
 static int write_ipv4_info(char *pos, int total_len,
-			   const struct ipv4_params *v4)
+			   const struct ipv4_params *v4,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v4->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 source address");
@@ -728,7 +729,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv4 destination address");
@@ -743,7 +744,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -752,7 +753,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -761,7 +762,7 @@
 		rem_len -= res;
 	}
 
-	if (v4->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -775,14 +776,15 @@
 
 
 static int write_ipv6_info(char *pos, int total_len,
-			   const struct ipv6_params *v6)
+			   const struct ipv6_params *v6,
+			   u8 classifier_mask)
 {
 	int res, rem_len;
 	char addr[INET6_ADDRSTRLEN];
 
 	rem_len = total_len;
 
-	if (v6->param_mask & BIT(1)) {
+	if (classifier_mask & BIT(1)) {
 		if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 source addr");
@@ -797,7 +799,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(2)) {
+	if (classifier_mask & BIT(2)) {
 		if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
 			wpa_printf(MSG_ERROR,
 				   "QM: Failed to set IPv6 destination addr");
@@ -812,7 +814,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(3)) {
+	if (classifier_mask & BIT(3)) {
 		res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -821,7 +823,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(4)) {
+	if (classifier_mask & BIT(4)) {
 		res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
 		if (os_snprintf_error(rem_len, res))
 			return -1;
@@ -830,7 +832,7 @@
 		rem_len -= res;
 	}
 
-	if (v6->param_mask & BIT(6)) {
+	if (classifier_mask & BIT(6)) {
 		res = os_snprintf(pos, rem_len, " protocol=%d",
 				  v6->next_header);
 		if (os_snprintf_error(rem_len, res))
@@ -861,7 +863,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v4.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v4.src_ip,
 			  &frame_classifier[3], 4);
 	}
@@ -874,14 +876,14 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v4.dst_ip,
 			  &frame_classifier[7], 4);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v4.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v4.src_port =
 			WPA_GET_BE16(&frame_classifier[11]);
 	}
@@ -894,7 +896,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v4.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v4.dst_port =
 			WPA_GET_BE16(&frame_classifier[13]);
 	}
@@ -903,7 +905,7 @@
 
 	/* Classifier Mask - bit 6 = Protocol */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v4.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v4.protocol = frame_classifier[16];
 	}
 
@@ -928,7 +930,7 @@
 
 	/* Classifier Mask - bit 1 = Source IP Address */
 	if (classifier_mask & BIT(1)) {
-		type4_param->ip_params.v6.param_mask |= BIT(1);
+		type4_param->classifier_mask |= BIT(1);
 		os_memcpy(&type4_param->ip_params.v6.src_ip,
 			  &frame_classifier[3], 16);
 	}
@@ -940,14 +942,14 @@
 				   "QM: IPv6: Both domain name and destination IP address not expected");
 			return -1;
 		}
-		type4_param->ip_params.v6.param_mask |= BIT(2);
+		type4_param->classifier_mask |= BIT(2);
 		os_memcpy(&type4_param->ip_params.v6.dst_ip,
 			  &frame_classifier[19], 16);
 	}
 
 	/* Classifier Mask - bit 3 = Source Port */
 	if (classifier_mask & BIT(3)) {
-		type4_param->ip_params.v6.param_mask |= BIT(3);
+		type4_param->classifier_mask |= BIT(3);
 		type4_param->ip_params.v6.src_port =
 				WPA_GET_BE16(&frame_classifier[35]);
 	}
@@ -960,7 +962,7 @@
 			return -1;
 		}
 
-		type4_param->ip_params.v6.param_mask |= BIT(4);
+		type4_param->classifier_mask |= BIT(4);
 		type4_param->ip_params.v6.dst_port =
 				WPA_GET_BE16(&frame_classifier[37]);
 	}
@@ -969,7 +971,7 @@
 
 	/* Classifier Mask - bit 6 = Next Header */
 	if (classifier_mask & BIT(6)) {
-		type4_param->ip_params.v6.param_mask |= BIT(6);
+		type4_param->classifier_mask |= BIT(6);
 		type4_param->ip_params.v6.next_header = frame_classifier[40];
 	}
 
@@ -1075,9 +1077,11 @@
 		}
 
 		if (type4->ip_version == IPV4)
-			res = write_ipv4_info(pos, len, &type4->ip_params.v4);
+			res = write_ipv4_info(pos, len, &type4->ip_params.v4,
+					      type4->classifier_mask);
 		else
-			res = write_ipv6_info(pos, len, &type4->ip_params.v6);
+			res = write_ipv6_info(pos, len, &type4->ip_params.v6,
+					      type4->classifier_mask);
 
 		if (res <= 0) {
 			wpa_printf(MSG_ERROR,
@@ -1294,11 +1298,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..88d7e6e 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;
 }
 
@@ -444,16 +446,66 @@
 
 	if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
 		/*
+		 * 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 && wpa_s->current_ssid) {
+			struct wpa_supplicant *ifs;
+			struct wpa_bss *bss = NULL;
+			struct wpa_ssid *ssid = wpa_s->current_ssid;
+			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)
+					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 == NULL && 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 +517,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 +762,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 +772,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 +789,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 +815,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 +1099,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 +1330,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 +1392,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 +1421,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 +1437,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 +1991,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 +2428,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 +2445,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 +2464,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 +2483,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 +2503,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 +2579,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 +2728,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 +2740,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 +2760,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 +2775,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 +3037,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..406e357 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,11 @@
 			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;
 #ifdef CONFIG_SAE_PK
 	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
@@ -167,42 +212,45 @@
 	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 &&
 	    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 +261,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 +274,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 +373,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 +599,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 +650,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 +691,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 +701,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 +715,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 +736,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 +819,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 +999,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 +1017,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 +1099,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 +1154,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 +1239,7 @@
 	wpa_s->rsnxe_len = 0;
 
 	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+	wpas_notify_auth_changed(wpa_s);
 }
 
 
@@ -1076,8 +1337,8 @@
 	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, NULL,
+					 1, 0, &use_pt, &use_pk);
 	if (!resp) {
 		wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
 		return -1;
@@ -1112,6 +1373,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 +1392,18 @@
 	size_t ssid_str_len = data->external_auth.ssid_len;
 	const u8 *ssid_str = data->external_auth.ssid;
 
+	wpa_s->sme.ext_auth_wpa_ssid = NULL;
 	/* Get the SSID conf from the ssid string obtained */
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 		if (!wpas_network_disabled(wpa_s, ssid) &&
 		    ssid_str_len == ssid->ssid_len &&
 		    os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 &&
-		    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)))
+		    wpa_key_mgmt_sae(ssid->key_mgmt)) {
+			/* Make sure PT is derived */
+			wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+			wpa_s->sme.ext_auth_wpa_ssid = ssid;
 			break;
+		}
 	}
 	if (!ssid ||
 	    sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid,
@@ -1175,11 +1442,41 @@
 }
 
 
+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) {
@@ -1253,7 +1550,7 @@
 
 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 +1560,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,23 +1595,36 @@
 		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;
 		}
+
+		if (ie_offset)
+			*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)
@@ -1322,19 +1633,20 @@
 		else
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
 		return 0;
 	}
 
 	if (auth_transaction == 1 &&
 	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
-	    (external || wpa_s->current_bss) && wpa_s->current_ssid) {
+	    ((external && wpa_s->sme.ext_auth_wpa_ssid) ||
+	     (!external && wpa_s->current_bss && wpa_s->current_ssid))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
 		int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
 				     wpa_s->sme.sae.group);
 		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)
@@ -1343,7 +1655,7 @@
 		else
 			sme_external_auth_send_sae_commit(
 				wpa_s, wpa_s->sme.ext_auth_bssid,
-				wpa_s->current_ssid);
+				wpa_s->sme.ext_auth_wpa_ssid);
 		return 0;
 	}
 
@@ -1375,8 +1687,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 +1719,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");
@@ -1441,7 +1755,8 @@
 		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;
 		wpa_s->sme.sae.state = SAE_ACCEPTED;
 		sae_clear_temp_data(&wpa_s->sme.sae);
@@ -1463,7 +1778,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
@@ -1513,7 +1828,7 @@
 			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, NULL);
 		if (res < 0) {
 			/* Notify failure to the driver */
 			sme_send_external_auth_status(
@@ -1537,6 +1852,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 +1866,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 +1886,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 +1901,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 +2032,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 +2326,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 +2455,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 +2577,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/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..bfdc42d 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,6 +1480,7 @@
 #ifdef CONFIG_HE_OVERRIDES
 	"disable_he",
 #endif /* CONFIG_HE_OVERRIDES */
+	"disable_eht",
 	"ap_max_inactivity", "dtim_period", "beacon_int",
 #ifdef CONFIG_MACSEC
 	"macsec_policy",
@@ -3134,7 +3148,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 +3173,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 +3262,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 +4016,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 +4050,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 f871b9e..521ff90 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"
@@ -142,7 +143,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 +207,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 +404,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 +445,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 +769,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 +989,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 +1008,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 +1133,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 +1161,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 +1353,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 +1570,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 +1579,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 +1764,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 +1812,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 +1845,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 +1903,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 +1917,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 +1937,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 +1982,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 +2010,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 +2066,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 +2253,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 +2323,7 @@
 	wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
 		MAC2STR(addr));
 
-	return 0;
+	return 1;
 }
 
 
@@ -2156,11 +2333,12 @@
 	    !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)
 {
 #ifdef CONFIG_SAE
 	int *groups = conf->sae_groups;
@@ -2175,9 +2353,10 @@
 		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) &&
 	     !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 +2426,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 +2438,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;
@@ -2291,11 +2470,16 @@
 	wpa_s_setup_sae_pt(wpa_s->conf, ssid);
 #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;
 	}
@@ -2711,7 +2895,7 @@
 	if (!ibss_mesh_is_80mhz_avail(channel, mode))
 		return;
 
-	chwidth = CHANWIDTH_80MHZ;
+	chwidth = CONF_OPER_CHWIDTH_80MHZ;
 	seg0 = channel + 6;
 	seg1 = 0;
 
@@ -2727,14 +2911,14 @@
 
 		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 */
@@ -2758,28 +2942,28 @@
 					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;
+	} else if (ssid->max_oper_chwidth == CONF_OPER_CHWIDTH_USE_HT) {
+		chwidth = CONF_OPER_CHWIDTH_USE_HT;
 		seg0 = channel + 2;
 #ifdef CONFIG_HT_OVERRIDES
 		if (ssid->disable_ht40)
@@ -2992,6 +3176,10 @@
 	    wpa_key_mgmt_wpa(ssid->key_mgmt)) {
 		int try_opportunistic;
 		const u8 *cache_id = NULL;
+		const u8 *addr = bss->bssid;
+
+		if (wpa_s->valid_links)
+			addr = wpa_s->ap_mld_addr;
 
 		try_opportunistic = (ssid->proactive_key_caching < 0 ?
 				     wpa_s->conf->okc :
@@ -3001,7 +3189,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 +3199,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 +3211,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 +3231,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 +3302,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 +3556,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 +3840,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 +3989,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 +4109,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 +4128,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 +4160,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 +4217,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 +4227,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 +4243,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 +4251,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 +4314,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 +4333,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);
 }
 
 
@@ -5034,6 +5233,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 +5242,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 +5273,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 +5286,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 +5368,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 +5384,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 +5402,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 +5419,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 +5431,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 +5460,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 +5488,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 +6029,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 +6225,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 +6857,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 +6865,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 +6880,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 +7029,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 +7044,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 +7666,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 +7730,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;
@@ -7778,7 +8073,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 +8250,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 +8260,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 +8270,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 +8279,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 +8289,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 +8298,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 +8377,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 +8427,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 +8441,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 +8474,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 +8538,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 +8564,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 +8686,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 +8702,9 @@
 		if (idx == len)
 			break;
 
+		if (exclude_current && ifs == wpa_s)
+			continue;
+
 		if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
 			continue;
 
@@ -8379,7 +8742,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 +8754,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 +9080,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 +9141,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..7a5f9cb 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"
 #}
@@ -1175,6 +1223,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 +1549,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 +1683,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..d364212 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,10 @@
 		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;
 		int *sae_rejected_groups;
 #endif /* CONFIG_SAE */
 	} sme;
@@ -1108,6 +1072,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 +1125,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 +1337,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 +1449,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 +1484,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 +1527,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 +1550,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 +1565,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 +1584,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 +1653,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 +1663,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 +1673,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 +1799,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 +1808,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 +1844,11 @@
 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 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 +1858,10 @@
 
 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);
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
 
@@ -1890,7 +1902,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 +1935,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 +1946,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..a1846b1 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -7,3 +7,4 @@
 p2p_add_cli_chan=1
 oce=1
 sae_pwe=2
+p2p_optimize_listen_chan=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 68236da..0921f52 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_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, 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/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 7428f02..de7dc98 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,7 @@
 	 * during an EAP-WSC exchange).
 	 */
 	wpas_notify_wps_event_fail(wpa_s, &data.fail);
+	wpa_s->supp_pbc_active = false;
 	wpas_clear_wps(wpa_s);
 }
 
@@ -1204,6 +1216,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 +1385,7 @@
 			wpas_clear_wps(wpa_s);
 	}
 
+	wpa_s->supp_pbc_active = false;
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CANCEL);
 	wpa_s->after_wps = 0;
 
@@ -1705,7 +1720,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 +1794,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 +1843,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 +1905,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 +1938,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 +3027,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)
 {