Snap for 9239618 from 9c83f39a13c8ffd7d1075bd00cd624bfb4b7ab33 to tm-platform-release

Change-Id: I40c76d25eac3f6ea8f90feb9e04768e968d80afd
diff --git a/Android.mk b/Android.mk
index bd7a409..bb8326c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,10 +1,15 @@
-LOCAL_PATH:= $(call my-dir)
+S_LOCAL_PATH := $(call my-dir)
 
 ifneq ($(filter VER_0_8_X VER_2_1_DEVEL,$(WPA_SUPPLICANT_VERSION)),)
 # The order of the 2 Android.mks does matter!
 # TODO: Clean up the Android.mks, reset all the temporary variables at the
 # end of each Android.mk, so that one Android.mk doesn't depend on variables
 # set up in the other Android.mk.
-include $(LOCAL_PATH)/hostapd/Android.mk \
-        $(LOCAL_PATH)/wpa_supplicant/Android.mk
+include $(S_LOCAL_PATH)/hostapd/Android.mk \
+        $(S_LOCAL_PATH)/wpa_supplicant/Android.mk
+ifneq ($(TARGET_BUILD_VARIANT), user)
+ifeq ($(shell test $(PLATFORM_VERSION_LAST_STABLE) -ge 8 ; echo $$?), 0)
+include $(S_LOCAL_PATH)/hs20/client/Android.mk
+endif #End of Check for platform version
+endif #End of Check for target build variant
 endif
diff --git a/hostapd/Android.bp b/hostapd/Android.bp
index d7cc39b..2a25208 100644
--- a/hostapd/Android.bp
+++ b/hostapd/Android.bp
@@ -287,7 +287,6 @@
         "src/crypto/fips_prf_openssl.c",
         "src/crypto/aes-siv.c",
         "src/crypto/aes-ctr.c",
-        "src/crypto/aes-omac1.c",
         "src/crypto/sha1-prf.c",
         "src/crypto/sha1-tlsprf.c",
         "src/crypto/sha256-prf.c",
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 4c37b77..adb4c08 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -314,6 +314,12 @@
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_IEEE80211BE
+CONFIG_IEEE80211AX=y
+L_CFLAGS += -DCONFIG_IEEE80211BE
+OBJS += src/ap/ieee802_11_eht.c
+endif
+
 ifdef CONFIG_IEEE80211AX
 L_CFLAGS += -DCONFIG_IEEE80211AX
 endif
@@ -673,6 +679,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
+L_CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_openssl.c
 OBJS += src/crypto/tls_openssl_ocsp.c
@@ -845,7 +852,9 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += src/crypto/aes-encblock.c
 endif
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-omac1.c
+endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 98a0102..5f06378 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -343,6 +343,12 @@
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_IEEE80211BE
+CONFIG_IEEE80211AX=y
+CFLAGS += -DCONFIG_IEEE80211BE
+OBJS += ../src/ap/ieee802_11_eht.o
+endif
+
 ifdef CONFIG_IEEE80211AX
 CFLAGS += -DCONFIG_IEEE80211AX
 OBJS += ../src/ap/ieee802_11_he.o
@@ -712,6 +718,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 CONFIG_CRYPTO=openssl
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_openssl.o
@@ -936,11 +943,13 @@
 ifdef NEED_AES_ENCBLOCK
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
+ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
 ifneq ($(CONFIG_TLS), wolfssl)
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
+endif
 ifdef NEED_AES_UNWRAP
 ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
diff --git a/hostapd/android.config b/hostapd/android.config
index 6ed6f6a..eaeda89 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -28,6 +28,9 @@
 # Broadcom vendor extensions to nl80211
 ifeq ($(BOARD_WLAN_DEVICE),bcmdhd)
 CONFIG_DRIVER_NL80211_BRCM=y
+# Synaptics vendor extensions to nl80211
+else ifeq ($(BOARD_WLAN_DEVICE),synadhd)
+CONFIG_DRIVER_NL80211_SYNA=y
 else
 # QCA vendor extensions to nl80211
 CONFIG_DRIVER_NL80211_QCA=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 7e605ff..2d5a510 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -118,52 +118,6 @@
 #endif /* CONFIG_NO_VLAN */
 
 
-int hostapd_acl_comp(const void *a, const void *b)
-{
-	const struct mac_acl_entry *aa = a;
-	const struct mac_acl_entry *bb = b;
-	return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
-}
-
-
-int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
-			    int vlan_id, const u8 *addr)
-{
-	struct mac_acl_entry *newacl;
-
-	newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
-	if (!newacl) {
-		wpa_printf(MSG_ERROR, "MAC list reallocation failed");
-		return -1;
-	}
-
-	*acl = newacl;
-	os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
-	os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
-	(*acl)[*num].vlan_id.untagged = vlan_id;
-	(*acl)[*num].vlan_id.notempty = !!vlan_id;
-	(*num)++;
-
-	return 0;
-}
-
-
-void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
-			    const u8 *addr)
-{
-	int i = 0;
-
-	while (i < *num) {
-		if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
-			os_remove_in_array(*acl, *num, sizeof(**acl), i);
-			(*num)--;
-		} else {
-			i++;
-		}
-	}
-}
-
-
 static int hostapd_config_read_maclist(const char *fname,
 				       struct mac_acl_entry **acl, int *num)
 {
@@ -2635,6 +2589,9 @@
 		bss->eap_sim_aka_result_ind = atoi(pos);
 	} else if (os_strcmp(buf, "eap_sim_id") == 0) {
 		bss->eap_sim_id = atoi(pos);
+	} else if (os_strcmp(buf, "imsi_privacy_key") == 0) {
+		os_free(bss->imsi_privacy_key);
+		bss->imsi_privacy_key = os_strdup(pos);
 #endif /* EAP_SERVER_SIM */
 #ifdef EAP_SERVER_TNC
 	} else if (os_strcmp(buf, "tnc") == 0) {
@@ -2975,7 +2932,8 @@
 		bss->wpa_psk_radius = atoi(pos);
 		if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
 		    bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
-		    bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+		    bss->wpa_psk_radius != PSK_RADIUS_REQUIRED &&
+		    bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: unknown wpa_psk_radius %d",
 				   line, bss->wpa_psk_radius);
@@ -3139,6 +3097,7 @@
 				   line, pos);
 			return 1;
 		}
+		conf->hw_mode_set = true;
 	} else if (os_strcmp(buf, "wps_rf_bands") == 0) {
 		if (os_strcmp(pos, "ad") == 0)
 			bss->wps_rf_bands = WPS_RF_60GHZ;
@@ -3193,6 +3152,8 @@
 		conf->acs_freq_list_present = 1;
 	} else if (os_strcmp(buf, "acs_exclude_6ghz_non_psc") == 0) {
 		conf->acs_exclude_6ghz_non_psc = atoi(pos);
+	} else if (os_strcmp(buf, "enable_background_radar") == 0) {
+		conf->enable_background_radar = atoi(pos);
 	} else if (os_strcmp(buf, "min_tx_power") == 0) {
 		int val = atoi(pos);
 
@@ -3642,6 +3603,8 @@
 				   line, pos);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "he_6ghz_reg_pwr_type") == 0) {
+		conf->he_6ghz_reg_pwr_type = atoi(pos);
 	} else if (os_strcmp(buf, "he_oper_chwidth") == 0) {
 		conf->he_oper_chwidth = atoi(pos);
 	} else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) {
@@ -4301,6 +4264,8 @@
 		conf->skip_send_eapol = atoi(pos);
 	} else if (os_strcmp(buf, "enable_eapol_large_timeout") == 0) {
 		conf->enable_eapol_large_timeout = atoi(pos);
+	} else if (os_strcmp(buf, "eap_skip_prot_success") == 0) {
+		bss->eap_skip_prot_success = atoi(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_SAE
 	} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -4665,6 +4630,16 @@
 			return 1;
 		}
 		bss->mka_priority = mka_priority;
+	} else if (os_strcmp(buf, "macsec_csindex") == 0) {
+		int macsec_csindex = atoi(pos);
+
+		if (macsec_csindex < 0 || macsec_csindex > 1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid macsec_csindex (%d): '%s'.",
+				   line, macsec_csindex, pos);
+			return 1;
+		}
+		bss->macsec_csindex = macsec_csindex;
 	} else if (os_strcmp(buf, "mka_cak") == 0) {
 		size_t len = os_strlen(pos);
 
@@ -4701,6 +4676,8 @@
 		bss->disable_11ac = !!atoi(pos);
 	} else if (os_strcmp(buf, "disable_11ax") == 0) {
 		bss->disable_11ax = !!atoi(pos);
+	} else if (os_strcmp(buf, "disable_11be") == 0) {
+		bss->disable_11be = !!atoi(pos);
 #ifdef CONFIG_PASN
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcmp(buf, "force_kdk_derivation") == 0) {
@@ -4728,6 +4705,20 @@
 			return 1;
 	} else if (os_strcmp(buf, "rnr") == 0) {
 		bss->rnr = atoi(pos);
+#ifdef CONFIG_IEEE80211BE
+	} else if (os_strcmp(buf, "ieee80211be") == 0) {
+		conf->ieee80211be = atoi(pos);
+	} else if (os_strcmp(buf, "eht_oper_chwidth") == 0) {
+		conf->eht_oper_chwidth = atoi(pos);
+	} else if (os_strcmp(buf, "eht_oper_centr_freq_seg0_idx") == 0) {
+		conf->eht_oper_centr_freq_seg0_idx = atoi(pos);
+	} else if (os_strcmp(buf, "eht_su_beamformer") == 0) {
+		conf->eht_phy_capab.su_beamformer = atoi(pos);
+	} else if (os_strcmp(buf, "eht_su_beamformee") == 0) {
+		conf->eht_phy_capab.su_beamformee = atoi(pos);
+	} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
+		conf->eht_phy_capab.mu_beamformer = atoi(pos);
+#endif /* CONFIG_IEEE80211BE */
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index 9830f5a..c98bdb6 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -13,10 +13,5 @@
 int hostapd_set_iface(struct hostapd_config *conf,
 		      struct hostapd_bss_config *bss, const char *field,
 		      char *value);
-int hostapd_acl_comp(const void *a, const void *b);
-int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
-			    int vlan_id, const u8 *addr);
-void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
-			    const u8 *addr);
 
 #endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index a62f3c7..ad994d4 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -772,235 +772,6 @@
 
 #ifdef CONFIG_WNM_AP
 
-static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
-						const char *cmd)
-{
-	u8 addr[ETH_ALEN];
-	int disassoc_timer;
-	struct sta_info *sta;
-
-	if (hwaddr_aton(cmd, addr))
-		return -1;
-	if (cmd[17] != ' ')
-		return -1;
-	disassoc_timer = atoi(cmd + 17);
-
-	sta = ap_get_sta(hapd, addr);
-	if (sta == NULL) {
-		wpa_printf(MSG_DEBUG, "Station " MACSTR
-			   " not found for disassociation imminent message",
-			   MAC2STR(addr));
-		return -1;
-	}
-
-	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
-}
-
-
-static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
-					   const char *cmd)
-{
-	u8 addr[ETH_ALEN];
-	const char *url, *timerstr;
-	int disassoc_timer;
-	struct sta_info *sta;
-
-	if (hwaddr_aton(cmd, addr))
-		return -1;
-
-	sta = ap_get_sta(hapd, addr);
-	if (sta == NULL) {
-		wpa_printf(MSG_DEBUG, "Station " MACSTR
-			   " not found for ESS disassociation imminent message",
-			   MAC2STR(addr));
-		return -1;
-	}
-
-	timerstr = cmd + 17;
-	if (*timerstr != ' ')
-		return -1;
-	timerstr++;
-	disassoc_timer = atoi(timerstr);
-	if (disassoc_timer < 0 || disassoc_timer > 65535)
-		return -1;
-
-	url = os_strchr(timerstr, ' ');
-	if (url == NULL)
-		return -1;
-	url++;
-
-	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
-}
-
-
-static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
-					 const char *cmd)
-{
-	u8 addr[ETH_ALEN];
-	const char *pos, *end;
-	int disassoc_timer = 0;
-	struct sta_info *sta;
-	u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
-	u8 bss_term_dur[12];
-	char *url = NULL;
-	int ret;
-	u8 nei_rep[1000];
-	int nei_len;
-	u8 mbo[10];
-	size_t mbo_len = 0;
-
-	if (hwaddr_aton(cmd, addr)) {
-		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
-		return -1;
-	}
-
-	sta = ap_get_sta(hapd, addr);
-	if (sta == NULL) {
-		wpa_printf(MSG_DEBUG, "Station " MACSTR
-			   " not found for BSS TM Request message",
-			   MAC2STR(addr));
-		return -1;
-	}
-
-	pos = os_strstr(cmd, " disassoc_timer=");
-	if (pos) {
-		pos += 16;
-		disassoc_timer = atoi(pos);
-		if (disassoc_timer < 0 || disassoc_timer > 65535) {
-			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
-			return -1;
-		}
-	}
-
-	pos = os_strstr(cmd, " valid_int=");
-	if (pos) {
-		pos += 11;
-		valid_int = atoi(pos);
-	}
-
-	pos = os_strstr(cmd, " dialog_token=");
-	if (pos) {
-		pos += 14;
-		dialog_token = atoi(pos);
-	}
-
-	pos = os_strstr(cmd, " bss_term=");
-	if (pos) {
-		pos += 10;
-		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
-		/* TODO: TSF configurable/learnable */
-		bss_term_dur[0] = 4; /* Subelement ID */
-		bss_term_dur[1] = 10; /* Length */
-		os_memset(&bss_term_dur[2], 0, 8);
-		end = os_strchr(pos, ',');
-		if (end == NULL) {
-			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
-			return -1;
-		}
-		end++;
-		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
-	}
-
-	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
-						  sizeof(nei_rep));
-	if (nei_len < 0)
-		return -1;
-
-	pos = os_strstr(cmd, " url=");
-	if (pos) {
-		size_t len;
-		pos += 5;
-		end = os_strchr(pos, ' ');
-		if (end)
-			len = end - pos;
-		else
-			len = os_strlen(pos);
-		url = os_malloc(len + 1);
-		if (url == NULL)
-			return -1;
-		os_memcpy(url, pos, len);
-		url[len] = '\0';
-		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
-	}
-
-	if (os_strstr(cmd, " pref=1"))
-		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
-	if (os_strstr(cmd, " abridged=1"))
-		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
-	if (os_strstr(cmd, " disassoc_imminent=1"))
-		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-
-#ifdef CONFIG_MBO
-	pos = os_strstr(cmd, "mbo=");
-	if (pos) {
-		unsigned int mbo_reason, cell_pref, reassoc_delay;
-		u8 *mbo_pos = mbo;
-
-		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
-			     &reassoc_delay, &cell_pref);
-		if (ret != 3) {
-			wpa_printf(MSG_DEBUG,
-				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
-			ret = -1;
-			goto fail;
-		}
-
-		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
-			wpa_printf(MSG_DEBUG,
-				   "Invalid MBO transition reason code %u",
-				   mbo_reason);
-			ret = -1;
-			goto fail;
-		}
-
-		/* Valid values for Cellular preference are: 0, 1, 255 */
-		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
-			wpa_printf(MSG_DEBUG,
-				   "Invalid MBO cellular capability %u",
-				   cell_pref);
-			ret = -1;
-			goto fail;
-		}
-
-		if (reassoc_delay > 65535 ||
-		    (reassoc_delay &&
-		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
-			wpa_printf(MSG_DEBUG,
-				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
-			ret = -1;
-			goto fail;
-		}
-
-		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
-		*mbo_pos++ = 1;
-		*mbo_pos++ = mbo_reason;
-		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
-		*mbo_pos++ = 1;
-		*mbo_pos++ = cell_pref;
-
-		if (reassoc_delay) {
-			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
-			*mbo_pos++ = 2;
-			WPA_PUT_LE16(mbo_pos, reassoc_delay);
-			mbo_pos += 2;
-		}
-
-		mbo_len = mbo_pos - mbo;
-	}
-#endif /* CONFIG_MBO */
-
-	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
-				  valid_int, bss_term_dur, dialog_token, url,
-				  nei_len ? nei_rep : NULL, nei_len,
-				  mbo_len ? mbo : NULL, mbo_len);
-#ifdef CONFIG_MBO
-fail:
-#endif /* CONFIG_MBO */
-	os_free(url);
-	return ret;
-}
-
-
 static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
 					     const char *cmd)
 {
@@ -1362,43 +1133,6 @@
 }
 
 
-static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
-{
-	struct sta_info *sta;
-	struct vlan_description vlan_id;
-
-	if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
-		return;
-
-	for (sta = hapd->sta_list; sta; sta = sta->next) {
-		if (!hostapd_maclist_found(hapd->conf->accept_mac,
-					   hapd->conf->num_accept_mac,
-					   sta->addr, &vlan_id) ||
-		    (vlan_id.notempty &&
-		     vlan_compare(&vlan_id, sta->vlan_desc)))
-			ap_sta_disconnect(hapd, sta, sta->addr,
-					  WLAN_REASON_UNSPECIFIED);
-	}
-}
-
-
-static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
-{
-	struct sta_info *sta;
-	struct vlan_description vlan_id;
-
-	for (sta = hapd->sta_list; sta; sta = sta->next) {
-		if (hostapd_maclist_found(hapd->conf->deny_mac,
-					  hapd->conf->num_deny_mac, sta->addr,
-					  &vlan_id) &&
-		    (!vlan_id.notempty ||
-		     !vlan_compare(&vlan_id, sta->vlan_desc)))
-			ap_sta_disconnect(hapd, sta, sta->addr,
-					  WLAN_REASON_UNSPECIFIED);
-	}
-}
-
-
 static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd,
 				       const char *bands)
 {
@@ -1519,6 +1253,9 @@
 	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
 		os_free(hapd->dpp_configurator_params);
 		hapd->dpp_configurator_params = os_strdup(value);
+#ifdef CONFIG_DPP2
+		dpp_controller_set_params(hapd->iface->interfaces->dpp, value);
+#endif /* CONFIG_DPP2 */
 	} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
 		hapd->dpp_init_max_tries = atoi(value);
 	} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
@@ -2838,7 +2575,7 @@
 
 	for (i = 0; i < iface->num_bss; i++) {
 
-		/* Save CHAN_SWITCH VHT and HE config */
+		/* Save CHAN_SWITCH VHT, HE, and EHT config */
 		hostapd_chan_switch_config(iface->bss[i],
 					   &settings.freq_params);
 
@@ -3383,80 +3120,6 @@
 }
 
 
-static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
-					  const char *txtaddr)
-{
-	u8 addr[ETH_ALEN];
-	struct vlan_description vlan_id;
-
-	if (!(*num))
-		return 0;
-
-	if (hwaddr_aton(txtaddr, addr))
-		return -1;
-
-	if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
-		hostapd_remove_acl_mac(acl, num, addr);
-
-	return 0;
-}
-
-
-static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
-					      int *num)
-{
-	while (*num)
-		hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
-}
-
-
-static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
-					   char *buf, size_t buflen)
-{
-	int i = 0, len = 0, ret = 0;
-
-	if (!acl)
-		return 0;
-
-	while (i < num) {
-		ret = os_snprintf(buf + len, buflen - len,
-				  MACSTR " VLAN_ID=%d\n",
-				  MAC2STR(acl[i].addr),
-				  acl[i].vlan_id.untagged);
-		if (ret < 0 || (size_t) ret >= buflen - len)
-			return len;
-		i++;
-		len += ret;
-	}
-	return len;
-}
-
-
-static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
-					  const char *cmd)
-{
-	u8 addr[ETH_ALEN];
-	struct vlan_description vlan_id;
-	int ret = 0, vlanid = 0;
-	const char *pos;
-
-	if (hwaddr_aton(cmd, addr))
-		return -1;
-
-	pos = os_strstr(cmd, "VLAN_ID=");
-	if (pos)
-		vlanid = atoi(pos + 8);
-
-	if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
-		ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
-		if (ret != -1 && *acl)
-			qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
-	}
-
-	return ret < 0 ? -1 : 0;
-}
-
-
 static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
 					     const char *field, char *buf,
 					     size_t buflen)
@@ -3832,14 +3495,15 @@
 		if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
 			if (hostapd_ctrl_iface_acl_add_mac(
 				    &hapd->conf->accept_mac,
-				    &hapd->conf->num_accept_mac, buf + 19))
+				    &hapd->conf->num_accept_mac, buf + 19) ||
+			    hostapd_set_acl(hapd))
 				reply_len = -1;
 		} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
-			if (!hostapd_ctrl_iface_acl_del_mac(
+			if (hostapd_ctrl_iface_acl_del_mac(
 				    &hapd->conf->accept_mac,
-				    &hapd->conf->num_accept_mac, buf + 19))
-				hostapd_disassoc_accept_mac(hapd);
-			else
+				    &hapd->conf->num_accept_mac, buf + 19) ||
+			    hostapd_set_acl(hapd) ||
+			    hostapd_disassoc_accept_mac(hapd))
 				reply_len = -1;
 		} else if (os_strcmp(buf + 11, "SHOW") == 0) {
 			reply_len = hostapd_ctrl_iface_acl_show_mac(
@@ -3849,20 +3513,23 @@
 			hostapd_ctrl_iface_acl_clear_list(
 				&hapd->conf->accept_mac,
 				&hapd->conf->num_accept_mac);
-			hostapd_disassoc_accept_mac(hapd);
+			if (hostapd_set_acl(hapd) ||
+			    hostapd_disassoc_accept_mac(hapd))
+				reply_len = -1;
 		}
 	} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
 		if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
-			if (!hostapd_ctrl_iface_acl_add_mac(
+			if (hostapd_ctrl_iface_acl_add_mac(
 				    &hapd->conf->deny_mac,
-				    &hapd->conf->num_deny_mac, buf + 17))
-				hostapd_disassoc_deny_mac(hapd);
-			else
+				    &hapd->conf->num_deny_mac, buf + 17) ||
+			    hostapd_set_acl(hapd) ||
+			    hostapd_disassoc_deny_mac(hapd))
 				reply_len = -1;
 		} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
 			if (hostapd_ctrl_iface_acl_del_mac(
 				    &hapd->conf->deny_mac,
-				    &hapd->conf->num_deny_mac, buf + 17))
+				    &hapd->conf->num_deny_mac, buf + 17) ||
+			    hostapd_set_acl(hapd))
 				reply_len = -1;
 		} else if (os_strcmp(buf + 9, "SHOW") == 0) {
 			reply_len = hostapd_ctrl_iface_acl_show_mac(
@@ -3872,6 +3539,8 @@
 			hostapd_ctrl_iface_acl_clear_list(
 				&hapd->conf->deny_mac,
 				&hapd->conf->num_deny_mac);
+			if (hostapd_set_acl(hapd))
+				reply_len = -1;
 		}
 #ifdef CONFIG_DPP
 	} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
@@ -3963,6 +3632,10 @@
 			if (os_snprintf_error(reply_size, reply_len))
 				reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SET ", 21) == 0) {
+		if (dpp_configurator_set(hapd->iface->interfaces->dpp,
+					 buf + 20) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
 		if (dpp_configurator_remove(hapd->iface->interfaces->dpp,
 					    buf + 24) < 0)
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 2855acd..6a41dcf 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -156,10 +156,20 @@
 #CONFIG_IEEE80211AC=y
 
 # IEEE 802.11ax HE support
+#CONFIG_IEEE80211AX=y
+
+# IEEE 802.11be EHT support
+# CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE.
 # Note: This is experimental and work in progress. The definitions are still
 # subject to change and this should not be expected to interoperate with the
-# final IEEE 802.11ax version.
-#CONFIG_IEEE80211AX=y
+# final IEEE 802.11be version.
+#CONFIG_IEEE80211BE=y
+
+# Simultaneous Authentication of Equals (SAE), WPA3-Personal
+#CONFIG_SAE=y
+
+# SAE Public Key, WPA3-Personal
+#CONFIG_SAE_PK=y
 
 # Remove debugging code that is printing out debug messages to stdout.
 # This can be used to reduce the size of the hostapd considerably if debugging
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 3c2019f..f37d563 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -225,6 +225,16 @@
 # Default behavior is to include all PSC and non-PSC channels.
 #acs_exclude_6ghz_non_psc=1
 
+# Enable background radar feature
+# This feature allows CAC to be run on dedicated radio RF chains while the
+# radio(s) are otherwise running normal AP activities on other channels.
+# This requires that the driver and the radio support it before feature will
+# actually be enabled, i.e., this parameter value is ignored with drivers that
+# do not advertise support for the capability.
+# 0: Leave disabled (default)
+# 1: Enable it.
+#enable_background_radar=1
+
 # Set minimum permitted max TX power (in dBm) for ACS and DFS channel selection.
 # (default 0, i.e., not constraint)
 #min_tx_power=20
@@ -965,6 +975,13 @@
 #     (default)
 #he_6ghz_tx_ant_pat=1
 
+# 6 GHz Access Point type
+# This config is to set the 6 GHz Access Point type. Possible options are:
+# 0 = Indoor AP (default)
+# 1 = Standard Power AP
+# This has no impact for operation on other bands.
+#he_6ghz_reg_pwr_type=0
+
 # Unsolicited broadcast Probe Response transmission settings
 # This is for the 6 GHz band only. If the interval is set to a non-zero value,
 # the AP schedules unsolicited broadcast Probe Response frames to be
@@ -973,6 +990,40 @@
 # Valid range: 0..20 TUs; default is 0 (disabled)
 #unsol_bcast_probe_resp_interval=0
 
+##### IEEE 802.11be related configuration #####################################
+
+#ieee80211be: Whether IEEE 802.11be (EHT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+#ieee80211be=1
+
+#disable_11be: Boolean (0/1) to disable EHT for a specific BSS
+#disable_11be=0
+
+#eht_su_beamformer: EHT single user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#eht_su_beamformer=1
+
+#eht_su_beamformee: EHT single user beamformee support
+# 0 = not supported (default)
+# 1 = supported
+#eht_su_beamformee=1
+
+#eht_mu_beamformer: EHT multiple user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#eht_mu_beamformer=1
+
+# EHT operating channel information; see matching he_* parameters for details.
+# The field eht_oper_centr_freq_seg0_idx field is used to indicate center
+# frequency of 40, 80, and 160 MHz bandwidth operation.
+# 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_centr_freq_seg0_idx
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
@@ -1070,6 +1121,10 @@
 # mka_priority (Priority of MKA Actor)
 # Range: 0..255 (default: 255)
 #
+# macsec_csindex: IEEE 802.1X/MACsec cipher suite
+# 0 = GCM-AES-128 (default)
+# 1 = GCM-AES-256 (default)
+#
 # mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
 # This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
 # In this mode, instances of hostapd can act as MACsec peers. The peer
@@ -1243,12 +1298,11 @@
 
 # dh_file: File path to DH/DSA parameters file (in PEM format)
 # This is an optional configuration file for setting parameters for an
-# ephemeral DH key exchange. In most cases, the default RSA authentication does
-# not use this configuration. However, it is possible setup RSA to use
-# ephemeral DH key exchange. In addition, ciphers with DSA keys always use
-# ephemeral DH keys. This can be used to achieve forward secrecy. If the file
-# is in DSA parameters format, it will be automatically converted into DH
-# params. This parameter is required if anonymous EAP-FAST is used.
+# ephemeral DH key exchange. If the file is in DSA parameters format, it will
+# be automatically converted into DH params. If the used TLS library supports
+# automatic DH parameter selection, that functionality will be used if this
+# parameter is not set. DH parameters are required if anonymous EAP-FAST is
+# used.
 # You can generate DH parameters file with OpenSSL, e.g.,
 # "openssl dhparam -out /etc/hostapd.dh.pem 2048"
 #dh_file=/etc/hostapd.dh.pem
@@ -1369,6 +1423,10 @@
 # 3 = use pseudonyms and use fast reauthentication (default)
 #eap_sim_id=3
 
+# IMSI privacy key (PEM encoded RSA 2048-bit private key) for decrypting
+# permanent identity when using EAP-SIM/AKA/AKA'.
+#imsi_privacy_key=imsi-privacy-key.pem
+
 # Trusted Network Connect (TNC)
 # If enabled, TNC validation will be required before the peer is allowed to
 # connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
@@ -1651,12 +1709,15 @@
 #wpa_psk_file=/etc/hostapd.wpa_psk
 
 # Optionally, WPA passphrase can be received from RADIUS authentication server
-# This requires macaddr_acl to be set to 2 (RADIUS)
+# This requires macaddr_acl to be set to 2 (RADIUS) for wpa_psk_radius values
+# 1 and 2.
 # 0 = disabled (default)
 # 1 = optional; use default passphrase/psk if RADIUS server does not include
 #	Tunnel-Password
 # 2 = required; reject authentication if RADIUS server does not include
 #	Tunnel-Password
+# 3 = ask RADIUS server during 4-way handshake if there is no locally
+#	configured PSK/passphrase for the STA
 #wpa_psk_radius=0
 
 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 2609121..60396f3 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1169,7 +1169,7 @@
 		       "arguments (count and freq)\n"
 		       "usage: <cs_count> <freq> [sec_channel_offset=] "
 		       "[center_freq1=] [center_freq2=] [bandwidth=] "
-		       "[blocktx] [ht|vht]\n");
+		       "[blocktx] [ht|vht|he|eht]\n");
 		return -1;
 	}
 
diff --git a/hostapd/main.c b/hostapd/main.c
index d028fb5..eab57b6 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -15,6 +15,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/uuid.h"
+#include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/tls.h"
 #include "common/version.h"
@@ -725,7 +726,6 @@
 		case 'v':
 			show_version();
 			exit(1);
-			break;
 		case 'g':
 			if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
 				return -1;
@@ -947,6 +947,7 @@
 
 	fst_global_deinit();
 
+	crypto_unload();
 	os_program_deinit();
 
 	return ret;
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index c42d53c..bb77341 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -60,6 +60,10 @@
 
 L_CFLAGS += -Wno-unused-parameter
 
+ifeq ($(shell test $(PLATFORM_VERSION_LAST_STABLE) -ge 8 ; echo $$?), 0)
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+L_CFLAGS += -DANDROID_LOG_NAME='"hs20-osu-client"'
+endif
 
 ########################
 include $(CLEAR_VARS)
@@ -71,9 +75,15 @@
 
 LOCAL_SHARED_LIBRARIES := libc libcutils
 LOCAL_SHARED_LIBRARIES += libcrypto libssl
+ifeq ($(shell test $(PLATFORM_VERSION_LAST_STABLE) -ge 8 ; echo $$?), 0)
+LOCAL_VENDOR_MODULE := true
+LOCAL_SHARED_LIBRARIES += libxml2
+LOCAL_SHARED_LIBRARIES += liblog
+else
 #LOCAL_SHARED_LIBRARIES += libxml2
 LOCAL_STATIC_LIBRARIES += libxml2
 LOCAL_SHARED_LIBRARIES += libicuuc
+endif # End of check for platform version
 LOCAL_SHARED_LIBRARIES += libcurl
 
 LOCAL_CFLAGS := $(L_CFLAGS)
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 11bf0db..7b274da 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -2911,20 +2911,27 @@
 	int found;
 	char *host = NULL;
 
-	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)",
-		   !ctx->no_osu_cert_validation, ctx->server_url);
+	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s server_url=%s)",
+		   !ctx->no_osu_cert_validation, cert->url ? cert->url : "N/A",
+		   ctx->server_url);
 
-	host = get_hostname(ctx->server_url);
+	if (ctx->no_osu_cert_validation && cert->url)
+		host = get_hostname(cert->url);
+	else
+		host = get_hostname(ctx->server_url);
 
-	for (i = 0; i < ctx->server_dnsname_count; i++)
-		os_free(ctx->server_dnsname[i]);
-	os_free(ctx->server_dnsname);
-	ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
-	ctx->server_dnsname_count = 0;
+	if (!ctx->no_osu_cert_validation) {
+		for (i = 0; i < ctx->server_dnsname_count; i++)
+			os_free(ctx->server_dnsname[i]);
+		os_free(ctx->server_dnsname);
+		ctx->server_dnsname = os_calloc(cert->num_dnsname,
+						sizeof(char *));
+		ctx->server_dnsname_count = 0;
+	}
 
 	found = 0;
 	for (i = 0; i < cert->num_dnsname; i++) {
-		if (ctx->server_dnsname) {
+		if (!ctx->no_osu_cert_validation && ctx->server_dnsname) {
 			ctx->server_dnsname[ctx->server_dnsname_count] =
 				os_strdup(cert->dnsname[i]);
 			if (ctx->server_dnsname[ctx->server_dnsname_count])
@@ -3249,7 +3256,6 @@
 		default:
 			usage();
 			exit(0);
-			break;
 		}
 	}
 
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
index 39d10e0..194518e 100644
--- a/hs20/client/spp_client.c
+++ b/hs20/client/spp_client.c
@@ -564,7 +564,6 @@
 			free(id);
 			return -1;
 		}
-		return 0;
 	}
 
 	if (strcasecmp(name, "uploadMO") == 0) {
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index a50e907..72694be 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -1385,8 +1385,11 @@
 			   SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
 		return NULL;
 	val = db_get_osu_config_val(ctx, realm, "signup_url");
-	if (val == NULL)
+	if (!val) {
+		hs20_eventlog(ctx, NULL, realm, session_id,
+			      "signup_url not configured in osu_config", NULL);
 		return NULL;
+	}
 
 	spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
 						NULL);
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 0030edc..faaedbf 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -540,6 +540,10 @@
 		if (!acs_usable_chan(chan))
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    iface->conf->acs_exclude_dfs)
+			continue;
+
 		if (!is_in_chanlist(iface, chan))
 			continue;
 
@@ -670,6 +674,10 @@
 		if (!chan_pri_allowed(chan))
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    iface->conf->acs_exclude_dfs)
+			continue;
+
 		if (!is_in_chanlist(iface, chan))
 			continue;
 
@@ -1044,7 +1052,9 @@
 
 	for (i = 0; i < mode->num_channels; i++) {
 		chan = &mode->channels[i];
-		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+		    ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		     iface->conf->acs_exclude_dfs))
 			continue;
 
 		if (!is_in_chanlist(iface, chan))
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 33c68d4..db86a76 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration helper functions
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -463,9 +463,12 @@
 	wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
 			      (u8 *) ssid->wpa_passphrase,
 			      os_strlen(ssid->wpa_passphrase));
-	pbkdf2_sha1(ssid->wpa_passphrase,
-		    ssid->ssid, ssid->ssid_len,
-		    4096, ssid->wpa_psk->psk, PMK_LEN);
+	if (pbkdf2_sha1(ssid->wpa_passphrase,
+			ssid->ssid, ssid->ssid_len,
+			4096, ssid->wpa_psk->psk, PMK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "Error in pbkdf2_sha1()");
+		return -1;
+	}
 	wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
 			ssid->wpa_psk->psk, PMK_LEN);
 	return 0;
@@ -811,6 +814,7 @@
 	os_free(conf->eap_fast_a_id);
 	os_free(conf->eap_fast_a_id_info);
 	os_free(conf->eap_sim_db);
+	os_free(conf->imsi_privacy_key);
 	os_free(conf->radius_server_clients);
 	os_free(conf->radius);
 	os_free(conf->radius_das_shared_secret);
@@ -1245,15 +1249,18 @@
 
 	if (full_config && bss->wpa &&
 	    bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+	    bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
 	    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
 		wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
 			   "RADIUS checking (macaddr_acl=2) enabled.");
 		return -1;
 	}
 
-	if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+	if (full_config && bss->wpa &&
+	    wpa_key_mgmt_wpa_psk_no_sae(bss->wpa_key_mgmt) &&
 	    bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
 	    bss->ssid.wpa_psk_file == NULL &&
+	    bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
 	    (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
 	     bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
 		wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
@@ -1426,7 +1433,7 @@
 #endif /* CONFIG_SAE_PK */
 
 #ifdef CONFIG_FILS
-	if (full_config && bss->fils_discovery_min_int &&
+	if (full_config && bss->fils_discovery_max_int &&
 	    bss->unsol_bcast_probe_resp_interval) {
 		wpa_printf(MSG_ERROR,
 			   "Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
@@ -1434,6 +1441,14 @@
 	}
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_IEEE80211BE
+	if (full_config && !bss->disable_11be && bss->disable_11ax) {
+		bss->disable_11be = true;
+		wpa_printf(MSG_INFO,
+			   "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	return 0;
 }
 
@@ -1465,6 +1480,13 @@
 {
 	size_t i;
 
+	if (full_config && is_6ghz_op_class(conf->op_class) &&
+	    !conf->hw_mode_set) {
+		/* Use the appropriate hw_mode value automatically when the
+		 * op_class parameter has been set, but hw_mode was not. */
+		conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+	}
+
 	if (full_config && conf->ieee80211d &&
 	    (!conf->country[0] || !conf->country[1])) {
 		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
@@ -1502,6 +1524,14 @@
 			return -1;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (full_config && conf->ieee80211be && !conf->ieee80211ax) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot set ieee80211be without ieee80211ax");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	for (i = 0; i < conf->num_bss; i++) {
 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
 			return -1;
@@ -1648,3 +1678,49 @@
 	return with_pk;
 }
 #endif /* CONFIG_SAE_PK */
+
+
+int hostapd_acl_comp(const void *a, const void *b)
+{
+	const struct mac_acl_entry *aa = a;
+	const struct mac_acl_entry *bb = b;
+	return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
+}
+
+
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+			    int vlan_id, const u8 *addr)
+{
+	struct mac_acl_entry *newacl;
+
+	newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+	if (!newacl) {
+		wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+		return -1;
+	}
+
+	*acl = newacl;
+	os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+	os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+	(*acl)[*num].vlan_id.untagged = vlan_id;
+	(*acl)[*num].vlan_id.notempty = !!vlan_id;
+	(*num)++;
+
+	return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+			    const u8 *addr)
+{
+	int i = 0;
+
+	while (i < *num) {
+		if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+			os_remove_in_array(*acl, *num, sizeof(**acl), i);
+			(*num)--;
+		} else {
+			i++;
+		}
+	}
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c1a0f71..b97d49c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,12 @@
 #include "fst/fst.h"
 #include "vlan.h"
 
+enum macaddr_acl {
+	ACCEPT_UNLESS_DENIED = 0,
+	DENY_UNLESS_ACCEPTED = 1,
+	USE_EXTERNAL_RADIUS_AUTH = 2
+};
+
 /**
  * mesh_conf - local MBSS state and settings
  */
@@ -331,12 +337,11 @@
 	int eap_reauth_period;
 	int erp_send_reauth_start;
 	char *erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+	bool eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
 
-	enum macaddr_acl {
-		ACCEPT_UNLESS_DENIED = 0,
-		DENY_UNLESS_ACCEPTED = 1,
-		USE_EXTERNAL_RADIUS_AUTH = 2
-	} macaddr_acl;
+	enum macaddr_acl macaddr_acl;
 	struct mac_acl_entry *accept_mac;
 	int num_accept_mac;
 	struct mac_acl_entry *deny_mac;
@@ -364,7 +369,8 @@
 	enum {
 		PSK_RADIUS_IGNORED = 0,
 		PSK_RADIUS_ACCEPTED = 1,
-		PSK_RADIUS_REQUIRED = 2
+		PSK_RADIUS_REQUIRED = 2,
+		PSK_RADIUS_DURING_4WAY_HS = 3,
 	} wpa_psk_radius;
 	int wpa_pairwise;
 	int group_cipher; /* wpa_group value override from configuation */
@@ -439,6 +445,7 @@
 	int eap_teap_id;
 	int eap_sim_aka_result_ind;
 	int eap_sim_id;
+	char *imsi_privacy_key;
 	int tnc;
 	int fragment_size;
 	u16 pwd_group;
@@ -537,6 +544,7 @@
 	bool disable_11n;
 	bool disable_11ac;
 	bool disable_11ax;
+	bool disable_11be;
 
 	/* IEEE 802.11v */
 	int time_advertisement;
@@ -849,6 +857,13 @@
 	int mka_priority;
 
 	/**
+	 * macsec_csindex - Cipher suite index for MACsec
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_csindex;
+
+	/**
 	 * mka_ckn - MKA pre-shared CKN
 	 */
 #define MACSEC_CKN_MAX_LEN 32
@@ -937,6 +952,15 @@
 };
 
 /**
+ * struct eht_phy_capabilities_info - EHT PHY capabilities
+ */
+struct eht_phy_capabilities_info {
+	bool su_beamformer;
+	bool su_beamformee;
+	bool mu_beamformer;
+};
+
+/**
  * struct hostapd_config - Per-radio interface configuration
  */
 struct hostapd_config {
@@ -957,7 +981,9 @@
 	int acs_exclude_dfs;
 	u8 min_tx_power;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	bool hw_mode_set;
 	int acs_exclude_6ghz_non_psc;
+	int enable_background_radar;
 	enum {
 		LONG_PREAMBLE = 0,
 		SHORT_PREAMBLE = 1
@@ -1075,6 +1101,7 @@
 	u8 he_6ghz_max_ampdu_len_exp;
 	u8 he_6ghz_rx_ant_pat;
 	u8 he_6ghz_tx_ant_pat;
+	u8 he_6ghz_reg_pwr_type;
 #endif /* CONFIG_IEEE80211AX */
 
 	/* VHT enable/disable config from CHAN_SWITCH */
@@ -1102,11 +1129,27 @@
 	unsigned int airtime_update_interval;
 #define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
 #endif /* CONFIG_AIRTIME_POLICY */
+
+	int ieee80211be;
+#ifdef CONFIG_IEEE80211BE
+	u8 eht_oper_chwidth;
+	u8 eht_oper_centr_freq_seg0_idx;
+	struct eht_phy_capabilities_info eht_phy_capab;
+#endif /* CONFIG_IEEE80211BE */
+
+	/* EHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_EHT_ENABLED BIT(0)
+#define CH_SWITCH_EHT_DISABLED BIT(1)
+	unsigned int ch_switch_eht_config;
 };
 
 
 static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
 {
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		return conf->eht_oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
 		return conf->he_oper_chwidth;
@@ -1117,6 +1160,10 @@
 static inline void
 hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
 {
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		conf->eht_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
 		conf->he_oper_chwidth = oper_chwidth;
@@ -1127,6 +1174,10 @@
 static inline u8
 hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
 {
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		return conf->eht_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
 		return conf->he_oper_centr_freq_seg0_idx;
@@ -1138,6 +1189,10 @@
 hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
 				     u8 oper_centr_freq_seg0_idx)
 {
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_IEEE80211AX
 	if (conf->ieee80211ax)
 		conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
@@ -1197,5 +1252,10 @@
 bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
 bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
 int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
+int hostapd_acl_comp(const void *a, const void *b);
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+			    int vlan_id, const u8 *addr);
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+			    const u8 *addr);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index e917736..8af7a0e 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -418,6 +418,8 @@
 		    const struct ieee80211_vht_capabilities *vht_capab,
 		    const struct ieee80211_he_capabilities *he_capab,
 		    size_t he_capab_len,
+		    const struct ieee80211_eht_capabilities *eht_capab,
+		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set)
@@ -440,6 +442,8 @@
 	params.vht_capabilities = vht_capab;
 	params.he_capab = he_capab;
 	params.he_capab_len = he_capab_len;
+	params.eht_capab = eht_capab;
+	params.eht_capab_len = eht_capab_len;
 	params.he_6ghz_capab = he_6ghz_capab;
 	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
 	params.vht_opmode = vht_opmode;
@@ -547,7 +551,7 @@
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     int freq, int channel, int edmg, u8 edmg_channel,
 		     int ht_enabled, int vht_enabled,
-		     int he_enabled,
+		     int he_enabled, bool eht_enabled,
 		     int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1)
 {
@@ -556,12 +560,15 @@
 
 	if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
 				    edmg_channel, ht_enabled,
-				    vht_enabled, he_enabled, sec_channel_offset,
-				    oper_chwidth,
+				    vht_enabled, he_enabled, eht_enabled,
+				    sec_channel_offset, oper_chwidth,
 				    center_segment0, center_segment1,
 				    cmode ? cmode->vht_capab : 0,
 				    cmode ?
-				    &cmode->he_capab[IEEE80211_MODE_AP] : NULL))
+				    &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
+				    cmode ?
+				    &cmode->eht_capab[IEEE80211_MODE_AP] :
+				    NULL))
 		return -1;
 
 	if (hapd->driver == NULL)
@@ -810,9 +817,10 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int he_enabled,
+			  int he_enabled, bool eht_enabled,
 			  int sec_channel_offset, int oper_chwidth,
-			  int center_segment0, int center_segment1)
+			  int center_segment0, int center_segment1,
+			  bool radar_background)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	struct hostapd_freq_params data;
@@ -830,18 +838,24 @@
 
 	if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
 				    ht_enabled,
-				    vht_enabled, he_enabled, sec_channel_offset,
+				    vht_enabled, he_enabled, eht_enabled,
+				    sec_channel_offset,
 				    oper_chwidth, center_segment0,
 				    center_segment1,
 				    cmode->vht_capab,
-				    &cmode->he_capab[IEEE80211_MODE_AP])) {
+				    &cmode->he_capab[IEEE80211_MODE_AP],
+				    &cmode->eht_capab[IEEE80211_MODE_AP])) {
 		wpa_printf(MSG_ERROR, "Can't set freq params");
 		return -1;
 	}
+	data.radar_background = radar_background;
 
 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
 	if (!res) {
-		iface->cac_started = 1;
+		if (radar_background)
+			iface->radar_background.cac_started = 1;
+		else
+			iface->cac_started = 1;
 		os_get_reltime(&iface->dfs_cac_start);
 	}
 
@@ -953,13 +967,15 @@
 	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
 				 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
 	params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+	params.eht_enabled = !!(hapd->iface->conf->ieee80211be);
 	params.ch_width = 20;
 	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
 		params.ch_width = 40;
 
 	/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
 	 */
-	if ((hapd->iface->conf->ieee80211ax ||
+	if ((hapd->iface->conf->ieee80211be ||
+	     hapd->iface->conf->ieee80211ax ||
 	     hapd->iface->conf->ieee80211ac) &&
 	    params.ht40_enabled) {
 		u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 61c8f64..b4fb766 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -43,6 +43,8 @@
 		    const struct ieee80211_vht_capabilities *vht_capab,
 		    const struct ieee80211_he_capabilities *he_capab,
 		    size_t he_capab_len,
+		    const struct ieee80211_eht_capabilities *eht_capab,
+		    size_t eht_capab_len,
 		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
 		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
 		    int set);
@@ -64,8 +66,8 @@
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
 		     int freq, int channel, int edmg, u8 edmg_channel,
-		     int ht_enabled, int vht_enabled,
-		     int he_enabled, int sec_channel_offset, int oper_chwidth,
+		     int ht_enabled, int vht_enabled, int he_enabled,
+		     bool eht_enabled, int sec_channel_offset, int oper_chwidth,
 		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
@@ -128,9 +130,10 @@
 int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
-			  int he_enabled,
+			  int he_enabled, bool eht_enabled,
 			  int sec_channel_offset, int oper_chwidth,
-			  int center_segment0, int center_segment1);
+			  int center_segment0, int center_segment1,
+			  bool radar_background);
 int hostapd_drv_do_acs(struct hostapd_data *hapd);
 int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
 			     u16 reason_code, const u8 *ie, size_t ielen);
@@ -299,6 +302,17 @@
 	return hapd->driver->switch_channel(hapd->drv_priv, settings);
 }
 
+#ifdef CONFIG_IEEE80211AX
+static inline int hostapd_drv_switch_color(struct hostapd_data *hapd,
+					   struct cca_settings *settings)
+{
+	if (!hapd->driver || !hapd->driver->switch_color || !hapd->drv_priv)
+		return -1;
+
+	return hapd->driver->switch_color(hapd->drv_priv, settings);
+}
+#endif /* CONFIG_IEEE80211AX */
+
 static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
 				     size_t buflen)
 {
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 8e12daf..fd9c96f 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "crypto/crypto.h"
 #include "crypto/tls.h"
 #include "eap_server/eap.h"
 #include "eap_server/eap_sim_db.h"
@@ -168,6 +169,9 @@
 			wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
 				   data->alert.description);
 		break;
+	case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+		/* Not applicable to TLS server */
+		break;
 	}
 }
 #endif /* EAP_TLS_FUNCS */
@@ -209,6 +213,7 @@
 	cfg->eap_teap_id = hapd->conf->eap_teap_id;
 	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;
 	cfg->tnc = hapd->conf->tnc;
 	cfg->wps = hapd->wps;
 	cfg->fragment_size = hapd->conf->fragment_size;
@@ -222,6 +227,9 @@
 		cfg->server_id_len = 7;
 	}
 	cfg->erp = hapd->conf->eap_server_erp;
+#ifdef CONFIG_TESTING_OPTIONS
+	cfg->skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	return cfg;
 }
@@ -292,6 +300,22 @@
 	}
 #endif /* EAP_TLS_FUNCS */
 
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	crypto_rsa_key_free(hapd->imsi_privacy_key);
+	hapd->imsi_privacy_key = NULL;
+	if (hapd->conf->imsi_privacy_key) {
+		hapd->imsi_privacy_key = crypto_rsa_key_read(
+			hapd->conf->imsi_privacy_key, true);
+		if (!hapd->imsi_privacy_key) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to read/parse IMSI privacy key %s",
+				   hapd->conf->imsi_privacy_key);
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
 #ifdef EAP_SIM_DB
 	if (hapd->conf->eap_sim_db) {
 		hapd->eap_sim_db_priv =
@@ -332,6 +356,11 @@
 	hapd->radius_srv = NULL;
 #endif /* RADIUS_SERVER */
 
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	crypto_rsa_key_free(hapd->imsi_privacy_key);
+	hapd->imsi_privacy_key = NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
 #ifdef EAP_TLS_FUNCS
 	if (hapd->ssl_ctx) {
 		tls_deinit(hapd->ssl_ctx);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8cd1c41..eaa4033 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -186,7 +186,8 @@
 }
 
 
-static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
+static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos,
+				    u8 *end, int chan_spacing,
 				    struct hostapd_channel_data *start,
 				    struct hostapd_channel_data *prev)
 {
@@ -198,31 +199,23 @@
 	/* number of channels */
 	*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
 	/* maximum transmit power level */
-	*pos++ = start->max_tx_power;
+	if (!is_6ghz_op_class(hapd->iconf->op_class))
+		*pos++ = start->max_tx_power;
+	else
+		*pos++ = 0; /* Reserved when operating on the 6 GHz band */
 
 	return pos;
 }
 
 
-static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
-				int max_len)
+static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos,
+					    u8 *end)
 {
-	u8 *pos = eid;
-	u8 *end = eid + max_len;
 	int i;
 	struct hostapd_hw_modes *mode;
 	struct hostapd_channel_data *start, *prev;
 	int chan_spacing = 1;
 
-	if (!hapd->iconf->ieee80211d || max_len < 6 ||
-	    hapd->iface->current_mode == NULL)
-		return eid;
-
-	*pos++ = WLAN_EID_COUNTRY;
-	pos++; /* length will be set later */
-	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
-	pos += 3;
-
 	mode = hapd->iface->current_mode;
 	if (mode->mode == HOSTAPD_MODE_IEEE80211A)
 		chan_spacing = 4;
@@ -240,7 +233,8 @@
 		}
 
 		if (start && prev) {
-			pos = hostapd_eid_country_add(pos, end, chan_spacing,
+			pos = hostapd_eid_country_add(hapd, pos, end,
+						      chan_spacing,
 						      start, prev);
 			start = NULL;
 		}
@@ -250,10 +244,50 @@
 	}
 
 	if (start) {
-		pos = hostapd_eid_country_add(pos, end, chan_spacing,
+		pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing,
 					      start, prev);
 	}
 
+	return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+				int max_len)
+{
+	u8 *pos = eid;
+	u8 *end = eid + max_len;
+
+	if (!hapd->iconf->ieee80211d || max_len < 6 ||
+	    hapd->iface->current_mode == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_COUNTRY;
+	pos++; /* length will be set later */
+	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+	pos += 3;
+
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		/* Force the third octet of the country string to indicate
+		 * Global Operating Class (Table E-4) */
+		eid[4] = 0x04;
+
+		/* Operating Triplet field */
+		/* Operating Extension Identifier (>= 201 to indicate this is
+		 * not a Subband Triplet field) */
+		*pos++ = 201;
+		/* Operating Class */
+		*pos++ = hapd->iconf->op_class;
+		/* Coverage Class */
+		*pos++ = 0;
+		/* Subband Triplets are required only for the 20 MHz case */
+		if (hapd->iconf->op_class == 131 ||
+		    hapd->iconf->op_class == 136)
+			pos = hostapd_fill_subband_triplets(hapd, pos, end);
+	} else {
+		pos = hostapd_fill_subband_triplets(hapd, pos, end);
+	}
+
 	if ((pos - eid) & 1) {
 		if (end - pos < 1)
 			return eid;
@@ -463,12 +497,25 @@
 			3 + sizeof(struct ieee80211_he_operation) +
 			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
 			3 + sizeof(struct ieee80211_spatial_reuse);
-		if (is_6ghz_op_class(hapd->iconf->op_class))
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
 			buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
 				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+			 /* An additional Transmit Power Envelope element for
+			  * subordinate client */
+			if (hapd->iconf->he_6ghz_reg_pwr_type ==
+			    HE_6GHZ_INDOOR_AP)
+				buflen += 4;
+		}
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
 	buflen += hostapd_mbo_ie_len(hapd);
 	buflen += hostapd_eid_owe_trans_len(hapd);
@@ -578,14 +625,30 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		u8 *cca_pos;
+
 		pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
 		pos = hostapd_eid_he_operation(hapd, pos);
+
+		/* BSS Color Change Announcement element */
+		cca_pos = hostapd_eid_cca(hapd, pos);
+		if (cca_pos != pos)
+			hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+		pos = cca_pos;
+
 		pos = hostapd_eid_spatial_reuse(hapd, pos);
 		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
 		pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+		pos = hostapd_eid_eht_operation(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -1319,6 +1382,15 @@
 	buf_len = pos - buf;
 	total_len += buf_len;
 
+#ifdef CONFIG_IEEE80211AX
+	/* Transmit Power Envelope element(s) */
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		total_len += 4;
+		if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
+			total_len += 4;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	head = os_zalloc(total_len);
 	if (!head)
 		return NULL;
@@ -1391,6 +1463,9 @@
 		pos += buf_len;
 	}
 
+	if (is_6ghz_op_class(hapd->iconf->op_class))
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+
 	*len = pos - (u8 *) head;
 	wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
 		    head, pos - (u8 *) head);
@@ -1465,12 +1540,25 @@
 			3 + sizeof(struct ieee80211_he_operation) +
 			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
 			3 + sizeof(struct ieee80211_spatial_reuse);
-		if (is_6ghz_op_class(hapd->iconf->op_class))
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
 			tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
 				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+			 /* An additional Transmit Power Envelope element for
+			  * subordinate client */
+			if (hapd->iconf->he_6ghz_reg_pwr_type ==
+			    HE_6GHZ_INDOOR_AP)
+				tail_len += 4;
+		}
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+		tail_len += 3 + sizeof(struct ieee80211_eht_operation);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	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);
@@ -1600,15 +1688,32 @@
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		u8 *cca_pos;
+
 		tailpos = hostapd_eid_he_capab(hapd, tailpos,
 					       IEEE80211_MODE_AP);
 		tailpos = hostapd_eid_he_operation(hapd, tailpos);
+
+		/* BSS Color Change Announcement element */
+		cca_pos = hostapd_eid_cca(hapd, tailpos);
+		if (cca_pos != tailpos)
+			hapd->cca_c_off_beacon = cca_pos - tail - 2;
+		tailpos = cca_pos;
+
 		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
 		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
 		tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
 	}
 #endif /* CONFIG_IEEE80211AX */
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+						IEEE80211_MODE_AP);
+		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
@@ -1845,12 +1950,14 @@
 				    iconf->channel, iconf->enable_edmg,
 				    iconf->edmg_channel, iconf->ieee80211n,
 				    iconf->ieee80211ac, iconf->ieee80211ax,
+				    iconf->ieee80211be,
 				    iconf->secondary_channel,
 				    hostapd_get_oper_chwidth(iconf),
 				    hostapd_get_oper_centr_freq_seg0_idx(iconf),
 				    hostapd_get_oper_centr_freq_seg1_idx(iconf),
 				    cmode->vht_capab,
-				    &cmode->he_capab[IEEE80211_MODE_AP]) == 0)
+				    &cmode->he_capab[IEEE80211_MODE_AP],
+				    &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
 		params.freq = &freq;
 
 	res = hostapd_drv_set_ap(hapd, &params);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1d8fb82..29b41f5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -24,6 +24,7 @@
 #include "ap_drv_ops.h"
 #include "mbo_ap.h"
 #include "taxonomy.h"
+#include "wnm_ap.h"
 
 
 static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
@@ -724,15 +725,15 @@
 	} else {
 		/* CAC started and CAC time set - calculate remaining time */
 		struct os_reltime now;
-		unsigned int left_time;
+		long left_time;
 
 		os_reltime_age(&iface->dfs_cac_start, &now);
-		left_time = iface->dfs_cac_ms / 1000 - now.sec;
+		left_time = (long) iface->dfs_cac_ms / 1000 - now.sec;
 		ret = os_snprintf(buf + len, buflen - len,
 				  "cac_time_seconds=%u\n"
-				  "cac_time_left_seconds=%u\n",
+				  "cac_time_left_seconds=%lu\n",
 				  iface->dfs_cac_ms / 1000,
-				  left_time);
+				  left_time > 0 ? left_time : 0);
 	}
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
@@ -746,6 +747,7 @@
 			  "ieee80211n=%d\n"
 			  "ieee80211ac=%d\n"
 			  "ieee80211ax=%d\n"
+			  "ieee80211be=%d\n"
 			  "beacon_int=%u\n"
 			  "dtim_period=%d\n",
 			  iface->conf->channel,
@@ -758,12 +760,27 @@
 			  !hapd->conf->disable_11ac,
 			  iface->conf->ieee80211ax &&
 			  !hapd->conf->disable_11ax,
+			  iface->conf->ieee80211be &&
+			  !hapd->conf->disable_11be,
 			  iface->conf->beacon_int,
 			  hapd->conf->dtim_period);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
+#ifdef CONFIG_IEEE80211BE
+	if (iface->conf->ieee80211be && !hapd->conf->disable_11be) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "eht_oper_chwidth=%d\n"
+				  "eht_oper_centr_freq_seg0_idx=%d\n",
+				  iface->conf->eht_oper_chwidth,
+				  iface->conf->eht_oper_centr_freq_seg0_idx);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_IEEE80211AX
 	if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
 		ret = os_snprintf(buf + len, buflen - len,
@@ -918,6 +935,7 @@
 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
 	settings->block_tx = !!os_strstr(pos, " blocktx");
 #undef SET_CSA_SETTING
 
@@ -1047,3 +1065,351 @@
 
 #endif /* CONFIG_MESH */
 #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_WNM_AP
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+					 const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	if (cmd[17] != ' ')
+		return -1;
+	disassoc_timer = atoi(cmd + 17);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+				    const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *url, *timerstr;
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for ESS disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	timerstr = cmd + 17;
+	if (*timerstr != ' ')
+		return -1;
+	timerstr++;
+	disassoc_timer = atoi(timerstr);
+	if (disassoc_timer < 0 || disassoc_timer > 65535)
+		return -1;
+
+	url = os_strchr(timerstr, ' ');
+	if (url == NULL)
+		return -1;
+	url++;
+
+	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+				  const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos, *end;
+	int disassoc_timer = 0;
+	struct sta_info *sta;
+	u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
+	u8 bss_term_dur[12];
+	char *url = NULL;
+	int ret;
+	u8 nei_rep[1000];
+	int nei_len;
+	u8 mbo[10];
+	size_t mbo_len = 0;
+
+	if (hwaddr_aton(cmd, addr)) {
+		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for BSS TM Request message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " disassoc_timer=");
+	if (pos) {
+		pos += 16;
+		disassoc_timer = atoi(pos);
+		if (disassoc_timer < 0 || disassoc_timer > 65535) {
+			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " valid_int=");
+	if (pos) {
+		pos += 11;
+		valid_int = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " dialog_token=");
+	if (pos) {
+		pos += 14;
+		dialog_token = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " bss_term=");
+	if (pos) {
+		pos += 10;
+		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+		/* TODO: TSF configurable/learnable */
+		bss_term_dur[0] = 4; /* Subelement ID */
+		bss_term_dur[1] = 10; /* Length */
+		os_memset(&bss_term_dur[2], 0, 8);
+		end = os_strchr(pos, ',');
+		if (end == NULL) {
+			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+			return -1;
+		}
+		end++;
+		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+	}
+
+	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+						  sizeof(nei_rep));
+	if (nei_len < 0)
+		return -1;
+
+	pos = os_strstr(cmd, " url=");
+	if (pos) {
+		size_t len;
+		pos += 5;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		url = os_malloc(len + 1);
+		if (url == NULL)
+			return -1;
+		os_memcpy(url, pos, len);
+		url[len] = '\0';
+		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	}
+
+	if (os_strstr(cmd, " pref=1"))
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+	if (os_strstr(cmd, " abridged=1"))
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+	if (os_strstr(cmd, " disassoc_imminent=1"))
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+	pos = os_strstr(cmd, "mbo=");
+	if (pos) {
+		unsigned int mbo_reason, cell_pref, reassoc_delay;
+		u8 *mbo_pos = mbo;
+
+		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
+			     &reassoc_delay, &cell_pref);
+		if (ret != 3) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
+			ret = -1;
+			goto fail;
+		}
+
+		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO transition reason code %u",
+				   mbo_reason);
+			ret = -1;
+			goto fail;
+		}
+
+		/* Valid values for Cellular preference are: 0, 1, 255 */
+		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO cellular capability %u",
+				   cell_pref);
+			ret = -1;
+			goto fail;
+		}
+
+		if (reassoc_delay > 65535 ||
+		    (reassoc_delay &&
+		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
+			ret = -1;
+			goto fail;
+		}
+
+		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = mbo_reason;
+		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = cell_pref;
+
+		if (reassoc_delay) {
+			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+			*mbo_pos++ = 2;
+			WPA_PUT_LE16(mbo_pos, reassoc_delay);
+			mbo_pos += 2;
+		}
+
+		mbo_len = mbo_pos - mbo;
+	}
+#endif /* CONFIG_MBO */
+
+	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+				  valid_int, bss_term_dur, dialog_token, url,
+				  nei_len ? nei_rep : NULL, nei_len,
+				  mbo_len ? mbo : NULL, mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
+	os_free(url);
+	return ret;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+				   const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct vlan_description vlan_id;
+
+	if (!(*num))
+		return 0;
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
+		hostapd_remove_acl_mac(acl, num, addr);
+
+	return 0;
+}
+
+
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+				       int *num)
+{
+	while (*num)
+		hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
+}
+
+
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+				    char *buf, size_t buflen)
+{
+	int i = 0, len = 0, ret = 0;
+
+	if (!acl)
+		return 0;
+
+	while (i < num) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  MACSTR " VLAN_ID=%d\n",
+				  MAC2STR(acl[i].addr),
+				  acl[i].vlan_id.untagged);
+		if (ret < 0 || (size_t) ret >= buflen - len)
+			return len;
+		i++;
+		len += ret;
+	}
+	return len;
+}
+
+
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+				   const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct vlan_description vlan_id;
+	int ret = 0, vlanid = 0;
+	const char *pos;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = os_strstr(cmd, "VLAN_ID=");
+	if (pos)
+		vlanid = atoi(pos + 8);
+
+	if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
+		ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
+		if (ret != -1 && *acl)
+			qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+	}
+
+	return ret < 0 ? -1 : 0;
+}
+
+
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct vlan_description vlan_id;
+
+	if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
+		return 0;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!hostapd_maclist_found(hapd->conf->accept_mac,
+					   hapd->conf->num_accept_mac,
+					   sta->addr, &vlan_id) ||
+		    (vlan_id.notempty &&
+		     vlan_compare(&vlan_id, sta->vlan_desc)))
+			ap_sta_disconnect(hapd, sta, sta->addr,
+					  WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
+
+
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct vlan_description vlan_id;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (hostapd_maclist_found(hapd->conf->deny_mac,
+					  hapd->conf->num_deny_mac, sta->addr,
+					  &vlan_id) &&
+		    (!vlan_id.notempty ||
+		     !vlan_compare(&vlan_id, sta->vlan_desc)))
+			ap_sta_disconnect(hapd, sta, sta->addr,
+					  WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index d1dcebf..614f042 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -37,4 +37,21 @@
 				       const u8 *addr, char *buf, size_t len);
 void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
 
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+					 const char *cmd);
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+				    const char *cmd);
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+				  const char *cmd);
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+				   const char *cmd);
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+				   const char *txtaddr);
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+				       int *num);
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+				    char *buf, size_t buflen);
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd);
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd);
+
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 5c99ecf..e46dd7e 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -19,6 +19,26 @@
 #include "dfs.h"
 
 
+enum dfs_channel_type {
+	DFS_ANY_CHANNEL,
+	DFS_AVAILABLE, /* non-radar or radar-available */
+	DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+			u8 *oper_centr_freq_seg0_idx,
+			u8 *oper_centr_freq_seg1_idx,
+			enum dfs_channel_type *channel_type);
+
+
+static bool dfs_use_radar_background(struct hostapd_iface *iface)
+{
+	return (iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) &&
+		iface->conf->enable_background_radar;
+}
+
+
 static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
 {
 	int n_chans = 1;
@@ -51,15 +71,27 @@
 }
 
 
+/* dfs_channel_available: select new channel according to type parameter */
 static int dfs_channel_available(struct hostapd_channel_data *chan,
-				 int skip_radar)
+				 enum dfs_channel_type type)
 {
+	if (type == DFS_NO_CAC_YET) {
+		/* Select only radar channel where CAC has not been
+		 * performed yet
+		 */
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		     HOSTAPD_CHAN_DFS_USABLE)
+			return 1;
+		return 0;
+	}
+
 	/*
 	 * When radar detection happens, CSA is performed. However, there's no
 	 * time for CAC, so radar channels must be skipped when finding a new
 	 * channel for CSA, unless they are available for immediate use.
 	 */
-	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
 	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
 	     HOSTAPD_CHAN_DFS_AVAILABLE))
 		return 0;
@@ -138,7 +170,7 @@
 
 static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
 				    int first_chan_idx, int num_chans,
-				    int skip_radar)
+				    enum dfs_channel_type type)
 {
 	struct hostapd_channel_data *first_chan, *chan;
 	int i;
@@ -177,7 +209,7 @@
 			return 0;
 		}
 
-		if (!dfs_channel_available(chan, skip_radar)) {
+		if (!dfs_channel_available(chan, type)) {
 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
 				   first_chan->freq + i * 20);
 			return 0;
@@ -207,7 +239,7 @@
  */
 static int dfs_find_channel(struct hostapd_iface *iface,
 			    struct hostapd_channel_data **ret_chan,
-			    int idx, int skip_radar)
+			    int idx, enum dfs_channel_type type)
 {
 	struct hostapd_hw_modes *mode;
 	struct hostapd_channel_data *chan;
@@ -232,7 +264,7 @@
 		}
 
 		/* Skip incompatible chandefs */
-		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
+		if (!dfs_chan_range_available(mode, i, n_chans, type)) {
 			wpa_printf(MSG_DEBUG,
 				   "DFS: range not available for %d (%d)",
 				   chan->freq, chan->chan);
@@ -475,7 +507,7 @@
 		      int *secondary_channel,
 		      u8 *oper_centr_freq_seg0_idx,
 		      u8 *oper_centr_freq_seg1_idx,
-		      int skip_radar)
+		      enum dfs_channel_type type)
 {
 	struct hostapd_hw_modes *mode;
 	struct hostapd_channel_data *chan = NULL;
@@ -499,7 +531,7 @@
 		return NULL;
 
 	/* Get the count first */
-	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
 	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
 		   num_available_chandefs);
 	if (num_available_chandefs == 0)
@@ -508,7 +540,7 @@
 	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
 		return NULL;
 	chan_idx = _rand % num_available_chandefs;
-	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+	dfs_find_channel(iface, &chan, chan_idx, type);
 	if (!chan) {
 		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
 		return NULL;
@@ -537,7 +569,7 @@
 		for (i = 0; i < num_available_chandefs - 1; i++) {
 			/* start from chan_idx + 1, end when chan_idx - 1 */
 			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
-			dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
+			dfs_find_channel(iface, &chan2, chan_idx2, type);
 			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
 				/* two channels are not adjacent */
 				sec_chan_idx_80p80 = chan2->chan;
@@ -568,6 +600,30 @@
 }
 
 
+static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+{
+	struct hostapd_channel_data *channel;
+	u8 cf1 = 0, cf2 = 0;
+	int sec = 0;
+
+	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+					skip_radar ? DFS_AVAILABLE :
+					DFS_ANY_CHANNEL);
+	if (!channel) {
+		wpa_printf(MSG_ERROR, "could not get valid channel");
+		return -1;
+	}
+
+	iface->freq = channel->freq;
+	iface->conf->channel = channel->chan;
+	iface->conf->secondary_channel = sec;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+
+	return 0;
+}
+
+
 static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
 {
 	struct hostapd_hw_modes *mode;
@@ -755,7 +811,6 @@
  */
 int hostapd_handle_dfs(struct hostapd_iface *iface)
 {
-	struct hostapd_channel_data *channel;
 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
 	int skip_radar = 0;
 
@@ -810,28 +865,17 @@
 		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
 			   res, res ? "yes": "no");
 		if (res) {
-			int sec = 0;
-			u8 cf1 = 0, cf2 = 0;
-
-			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
-							skip_radar);
-			if (!channel) {
-				wpa_printf(MSG_ERROR, "could not get valid channel");
+			if (dfs_set_valid_channel(iface, skip_radar) < 0) {
 				hostapd_set_state(iface, HAPD_IFACE_DFS);
 				return 0;
 			}
-
-			iface->freq = channel->freq;
-			iface->conf->channel = channel->chan;
-			iface->conf->secondary_channel = sec;
-			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
-			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
 		}
 	} while (res);
 
 	/* Finally start CAC */
 	hostapd_set_state(iface, HAPD_IFACE_DFS);
-	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+		   dfs_use_radar_background(iface) ? " (background)" : "");
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
 		iface->freq,
@@ -844,17 +888,41 @@
 	res = hostapd_start_dfs_cac(
 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
-		iface->conf->ieee80211ax,
+		iface->conf->ieee80211ax, iface->conf->ieee80211be,
 		iface->conf->secondary_channel,
 		hostapd_get_oper_chwidth(iface->conf),
 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
-		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+		dfs_use_radar_background(iface));
 
 	if (res) {
 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
 		return -1;
 	}
 
+	if (dfs_use_radar_background(iface)) {
+		/* Cache background radar parameters. */
+		iface->radar_background.channel = iface->conf->channel;
+		iface->radar_background.secondary_channel =
+			iface->conf->secondary_channel;
+		iface->radar_background.freq = iface->freq;
+		iface->radar_background.centr_freq_seg0_idx =
+			hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+		iface->radar_background.centr_freq_seg1_idx =
+			hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+
+		/*
+		 * Let's select a random channel according to the
+		 * regulations and perform CAC on dedicated radar chain.
+		 */
+		res = dfs_set_valid_channel(iface, 1);
+		if (res < 0)
+			return res;
+
+		iface->radar_background.temp_ch = 1;
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -876,6 +944,177 @@
 }
 
 
+static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+					      int channel, int freq,
+					      int secondary_channel,
+					      u8 current_vht_oper_chwidth,
+					      u8 oper_centr_freq_seg0_idx,
+					      u8 oper_centr_freq_seg1_idx)
+{
+	struct hostapd_hw_modes *cmode = iface->current_mode;
+	int ieee80211_mode = IEEE80211_MODE_AP, err;
+	struct csa_settings csa_settings;
+	u8 new_vht_oper_chwidth;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+		"freq=%d chan=%d sec_chan=%d", freq, channel,
+		secondary_channel);
+
+	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
+
+	/* Setup CSA request */
+	os_memset(&csa_settings, 0, sizeof(csa_settings));
+	csa_settings.cs_count = 5;
+	csa_settings.block_tx = 1;
+#ifdef CONFIG_MESH
+	if (iface->mconf)
+		ieee80211_mode = IEEE80211_MODE_MESH;
+#endif /* CONFIG_MESH */
+	err = hostapd_set_freq_params(&csa_settings.freq_params,
+				      iface->conf->hw_mode,
+				      freq, channel,
+				      iface->conf->enable_edmg,
+				      iface->conf->edmg_channel,
+				      iface->conf->ieee80211n,
+				      iface->conf->ieee80211ac,
+				      iface->conf->ieee80211ax,
+				      iface->conf->ieee80211be,
+				      secondary_channel,
+				      new_vht_oper_chwidth,
+				      oper_centr_freq_seg0_idx,
+				      oper_centr_freq_seg1_idx,
+				      cmode->vht_capab,
+				      &cmode->he_capab[ieee80211_mode],
+				      &cmode->eht_capab[ieee80211_mode]);
+
+	if (err) {
+		wpa_printf(MSG_ERROR,
+			   "DFS failed to calculate CSA freq params");
+		hostapd_disable_iface(iface);
+		return err;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+		if (err)
+			break;
+	}
+
+	if (err) {
+		wpa_printf(MSG_WARNING,
+			   "DFS failed to schedule CSA (%d) - trying fallback",
+			   err);
+		iface->freq = freq;
+		iface->conf->channel = channel;
+		iface->conf->secondary_channel = secondary_channel;
+		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
+
+		hostapd_disable_iface(iface);
+		hostapd_enable_iface(iface);
+
+		return 0;
+	}
+
+	/* Channel configuration will be updated once CSA completes and
+	 * ch_switch_notify event is received */
+	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
+	return 0;
+}
+
+
+static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+{
+	int sec = 0;
+	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+	struct hostapd_channel_data *channel;
+	u8 oper_centr_freq_seg0_idx = 0;
+	u8 oper_centr_freq_seg1_idx = 0;
+
+	/*
+	 * Allow selection of DFS channel in ETSI to comply with
+	 * uniform spreading.
+	 */
+	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+		channel_type = DFS_ANY_CHANNEL;
+
+	channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
+					channel_type);
+	if (!channel ||
+	    channel->chan == iface->conf->channel ||
+	    channel->chan == iface->radar_background.channel)
+		channel = dfs_downgrade_bandwidth(iface, &sec,
+						  &oper_centr_freq_seg0_idx,
+						  &oper_centr_freq_seg1_idx,
+						  &channel_type);
+	if (!channel ||
+	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+				  channel->freq, channel->chan,
+				  iface->conf->ieee80211n,
+				  iface->conf->ieee80211ac,
+				  iface->conf->ieee80211ax,
+				  iface->conf->ieee80211be,
+				  sec, hostapd_get_oper_chwidth(iface->conf),
+				  oper_centr_freq_seg0_idx,
+				  oper_centr_freq_seg1_idx, true)) {
+		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+		iface->radar_background.channel = -1;
+		return;
+	}
+
+	iface->radar_background.channel = channel->chan;
+	iface->radar_background.freq = channel->freq;
+	iface->radar_background.secondary_channel = sec;
+	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: setting background chain to chan %d (%d MHz)",
+		   __func__, channel->chan, channel->freq);
+}
+
+
+static bool
+hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
+{
+	return dfs_use_radar_background(iface) &&
+		iface->radar_background.channel != -1 &&
+		iface->radar_background.freq == freq;
+}
+
+
+static int
+hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+{
+	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+	iface->conf->channel = iface->radar_background.channel;
+	iface->freq = iface->radar_background.freq;
+	iface->conf->secondary_channel =
+		iface->radar_background.secondary_channel;
+	hostapd_set_oper_centr_freq_seg0_idx(
+		iface->conf, iface->radar_background.centr_freq_seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(
+		iface->conf, iface->radar_background.centr_freq_seg1_idx);
+
+	hostpad_dfs_update_background_chain(iface);
+
+	return hostapd_dfs_request_channel_switch(
+		iface, iface->conf->channel, iface->freq,
+		iface->conf->secondary_channel, current_vht_oper_chwidth,
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+}
+
+
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
 			     int ht_enabled, int chan_offset, int chan_width,
 			     int cf1, int cf2)
@@ -896,6 +1135,22 @@
 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
 				      chan_width, cf1, cf2,
 				      HOSTAPD_CHAN_DFS_AVAILABLE);
+
+			/*
+			 * Radar event from background chain for the selected
+			 * channel. Perform CSA, move the main chain to the
+			 * selected channel and configure the background chain
+			 * to a new DFS channel.
+			 */
+			if (hostapd_dfs_is_background_event(iface, freq)) {
+				iface->radar_background.cac_started = 0;
+				if (!iface->radar_background.temp_ch)
+					return 0;
+
+				iface->radar_background.temp_ch = 0;
+				return hostapd_dfs_start_channel_switch_background(iface);
+			}
+
 			/*
 			 * Just mark the channel available when CAC completion
 			 * event is received in enabled state. CAC result could
@@ -912,6 +1167,9 @@
 				iface->cac_started = 0;
 			}
 		}
+	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+		iface->radar_background.cac_started = 0;
+		hostpad_dfs_update_background_chain(iface);
 	}
 
 	return 0;
@@ -940,7 +1198,8 @@
 static struct hostapd_channel_data *
 dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
 			u8 *oper_centr_freq_seg0_idx,
-			u8 *oper_centr_freq_seg1_idx, int *skip_radar)
+			u8 *oper_centr_freq_seg1_idx,
+			enum dfs_channel_type *channel_type)
 {
 	struct hostapd_channel_data *channel;
 
@@ -948,22 +1207,22 @@
 		channel = dfs_get_valid_channel(iface, secondary_channel,
 						oper_centr_freq_seg0_idx,
 						oper_centr_freq_seg1_idx,
-						*skip_radar);
+						*channel_type);
 		if (channel) {
 			wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d",
 				   channel->chan);
 			return channel;
 		}
 
-		if (*skip_radar) {
-			*skip_radar = 0;
+		if (*channel_type != DFS_ANY_CHANNEL) {
+			*channel_type = DFS_ANY_CHANNEL;
 		} else {
 			int oper_chwidth;
 
 			oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
 			if (oper_chwidth == CHANWIDTH_USE_HT)
 				break;
-			*skip_radar = 1;
+			*channel_type = DFS_AVAILABLE;
 			hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
 		}
 	}
@@ -981,7 +1240,7 @@
 	int secondary_channel;
 	u8 oper_centr_freq_seg0_idx = 0;
 	u8 oper_centr_freq_seg1_idx = 0;
-	int skip_radar = 0;
+	enum dfs_channel_type channel_type = DFS_ANY_CHANNEL;
 	int err = 1;
 
 	/* Radar detected during active CAC */
@@ -989,13 +1248,13 @@
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
 					&oper_centr_freq_seg0_idx,
 					&oper_centr_freq_seg1_idx,
-					skip_radar);
+					channel_type);
 
 	if (!channel) {
 		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
 						  &oper_centr_freq_seg0_idx,
 						  &oper_centr_freq_seg1_idx,
-						  &skip_radar);
+						  &channel_type);
 		if (!channel) {
 			wpa_printf(MSG_ERROR, "No valid channel available");
 			return err;
@@ -1022,20 +1281,61 @@
 }
 
 
+static int
+hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+					    int freq)
+{
+	if (!dfs_use_radar_background(iface))
+		return -1; /* Background radar chain not supported. */
+
+	wpa_printf(MSG_DEBUG,
+		   "%s called (background CAC active: %s, CSA active: %s)",
+		   __func__, iface->radar_background.cac_started ? "yes" : "no",
+		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+	/* Check if CSA in progress */
+	if (hostapd_csa_in_progress(iface))
+		return 0;
+
+	if (hostapd_dfs_is_background_event(iface, freq)) {
+		/*
+		 * Radar pattern is reported on the background chain.
+		 * Just select a new random channel according to the
+		 * regulations for monitoring.
+		 */
+		hostpad_dfs_update_background_chain(iface);
+		return 0;
+	}
+
+	/*
+	 * If background radar detection is supported and the radar channel
+	 * monitored by the background chain is available switch to it without
+	 * waiting for the CAC.
+	 */
+	if (iface->radar_background.channel == -1)
+		return -1; /* Background radar chain not available. */
+
+	if (iface->radar_background.cac_started) {
+		/*
+		 * Background channel not available yet. Perform CAC on the
+		 * main chain.
+		 */
+		iface->radar_background.temp_ch = 1;
+		return -1;
+	}
+
+	return hostapd_dfs_start_channel_switch_background(iface);
+}
+
+
 static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
 {
 	struct hostapd_channel_data *channel;
 	int secondary_channel;
 	u8 oper_centr_freq_seg0_idx;
 	u8 oper_centr_freq_seg1_idx;
-	u8 new_vht_oper_chwidth;
-	int skip_radar = 1;
-	struct csa_settings csa_settings;
-	unsigned int i;
-	int err = 1;
-	struct hostapd_hw_modes *cmode = iface->current_mode;
+	enum dfs_channel_type channel_type = DFS_AVAILABLE;
 	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-	int ieee80211_mode = IEEE80211_MODE_AP;
 
 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
 		   __func__, iface->cac_started ? "yes" : "no",
@@ -1054,13 +1354,13 @@
 	 * uniform spreading.
 	 */
 	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
-		skip_radar = 0;
+		channel_type = DFS_ANY_CHANNEL;
 
 	/* Perform channel switch/CSA */
 	channel = dfs_get_valid_channel(iface, &secondary_channel,
 					&oper_centr_freq_seg0_idx,
 					&oper_centr_freq_seg1_idx,
-					skip_radar);
+					channel_type);
 
 	if (!channel) {
 		/*
@@ -1068,11 +1368,11 @@
 		 * there is another channel where we can switch even if it
 		 * requires to perform a CAC first.
 		 */
-		skip_radar = 0;
+		channel_type = DFS_ANY_CHANNEL;
 		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
 						  &oper_centr_freq_seg0_idx,
 						  &oper_centr_freq_seg1_idx,
-						  &skip_radar);
+						  &channel_type);
 		if (!channel) {
 			/*
 			 * Toggle interface state to enter DFS state
@@ -1083,7 +1383,7 @@
 			return 0;
 		}
 
-		if (!skip_radar) {
+		if (channel_type == DFS_ANY_CHANNEL) {
 			iface->freq = channel->freq;
 			iface->conf->channel = channel->chan;
 			iface->conf->secondary_channel = secondary_channel;
@@ -1098,73 +1398,12 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
-		   channel->chan);
-	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
-		"freq=%d chan=%d sec_chan=%d", channel->freq,
-		channel->chan, secondary_channel);
-
-	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
-	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
-
-	/* Setup CSA request */
-	os_memset(&csa_settings, 0, sizeof(csa_settings));
-	csa_settings.cs_count = 5;
-	csa_settings.block_tx = 1;
-#ifdef CONFIG_MESH
-	if (iface->mconf)
-		ieee80211_mode = IEEE80211_MODE_MESH;
-#endif /* CONFIG_MESH */
-	err = hostapd_set_freq_params(&csa_settings.freq_params,
-				      iface->conf->hw_mode,
-				      channel->freq,
-				      channel->chan,
-				      iface->conf->enable_edmg,
-				      iface->conf->edmg_channel,
-				      iface->conf->ieee80211n,
-				      iface->conf->ieee80211ac,
-				      iface->conf->ieee80211ax,
-				      secondary_channel,
-				      new_vht_oper_chwidth,
-				      oper_centr_freq_seg0_idx,
-				      oper_centr_freq_seg1_idx,
-				      cmode->vht_capab,
-				      &cmode->he_capab[ieee80211_mode]);
-
-	if (err) {
-		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
-		hostapd_disable_iface(iface);
-		return err;
-	}
-
-	for (i = 0; i < iface->num_bss; i++) {
-		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
-		if (err)
-			break;
-	}
-
-	if (err) {
-		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
-			   err);
-		iface->freq = channel->freq;
-		iface->conf->channel = channel->chan;
-		iface->conf->secondary_channel = secondary_channel;
-		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
-		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
-						     oper_centr_freq_seg0_idx);
-		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
-						     oper_centr_freq_seg1_idx);
-
-		hostapd_disable_iface(iface);
-		hostapd_enable_iface(iface);
-		return 0;
-	}
-
-	/* Channel configuration will be updated once CSA completes and
-	 * ch_switch_notify event is received */
-
-	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
-	return 0;
+	return hostapd_dfs_request_channel_switch(iface, channel->chan,
+						  channel->freq,
+						  secondary_channel,
+						  current_vht_oper_chwidth,
+						  oper_centr_freq_seg0_idx,
+						  oper_centr_freq_seg1_idx);
 }
 
 
@@ -1172,8 +1411,6 @@
 			       int ht_enabled, int chan_offset, int chan_width,
 			       int cf1, int cf2)
 {
-	int res;
-
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
@@ -1186,20 +1423,23 @@
 		return 0;
 
 	/* mark radar frequency as invalid */
-	res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
-			    cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
-	if (!res)
+	if (!set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+			   cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE))
 		return 0;
 
-	/* Skip if reported radar event not overlapped our channels */
-	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
-	if (!res)
-		return 0;
+	if (!hostapd_dfs_is_background_event(iface, freq)) {
+		/* Skip if reported radar event not overlapped our channels */
+		if (!dfs_are_channels_overlapped(iface, freq, chan_width,
+						 cf1, cf2))
+			return 0;
+	}
 
-	/* radar detected while operating, switch the channel. */
-	res = hostapd_dfs_start_channel_switch(iface);
+	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+		/* Radar detected while operating, switch the channel. */
+		return hostapd_dfs_start_channel_switch(iface);
+	}
 
-	return res;
+	return 0;
 }
 
 
@@ -1219,9 +1459,14 @@
 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
 
-	/* Handle cases where all channels were initially unavailable */
-	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
+	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) {
+		/* Handle cases where all channels were initially unavailable */
 		hostapd_handle_dfs(iface);
+	} else if (dfs_use_radar_background(iface) &&
+		   iface->radar_background.channel == -1) {
+		/* Reset radar background chain if disabled */
+		hostpad_dfs_update_background_chain(iface);
+	}
 
 	return 0;
 }
@@ -1259,17 +1504,24 @@
 			  int ht_enabled, int chan_offset, int chan_width,
 			  int cf1, int cf2)
 {
-	/* This is called when the driver indicates that an offloaded DFS has
-	 * started CAC. */
-	hostapd_set_state(iface, HAPD_IFACE_DFS);
+	if (hostapd_dfs_is_background_event(iface, freq)) {
+		iface->radar_background.cac_started = 1;
+	} else {
+		/* This is called when the driver indicates that an offloaded
+		 * DFS has started CAC. */
+		hostapd_set_state(iface, HAPD_IFACE_DFS);
+		iface->cac_started = 1;
+	}
 	/* TODO: How to check CAC time for ETSI weather channels? */
 	iface->dfs_cac_ms = 60000;
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
 		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-		"seg1=%d cac_time=%ds",
+		"seg1=%d cac_time=%ds%s",
 		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-		iface->dfs_cac_ms / 1000);
-	iface->cac_started = 1;
+		iface->dfs_cac_ms / 1000,
+		hostapd_dfs_is_background_event(iface, freq) ?
+		" (background)" : "");
+
 	os_get_reltime(&iface->dfs_cac_start);
 	return 0;
 }
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 96a13fb..d4cbed8 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -31,6 +31,7 @@
 static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
 static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
 					    struct dpp_authentication *auth);
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd);
 #ifdef CONFIG_DPP2
 static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
 						    void *timeout_ctx);
@@ -346,14 +347,8 @@
 #endif /* CONFIG_DPP2 */
 
 
-enum hostapd_dpp_pkex_ver {
-	PKEX_VER_AUTO,
-	PKEX_VER_ONLY_1,
-	PKEX_VER_ONLY_2,
-};
-
 static int hostapd_dpp_pkex_init(struct hostapd_data *hapd,
-				 enum hostapd_dpp_pkex_ver ver,
+				 enum dpp_pkex_ver ver,
 				 const struct hostapd_ip_addr *ipaddr,
 				 int tcp_port)
 {
@@ -1160,6 +1155,21 @@
 }
 
 
+#ifdef CONFIG_DPP3
+static void hostapd_dpp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_new_key)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+	hostapd_dpp_start_gas_client(hapd);
+}
+#endif /* CONFIG_DPP3 */
+
+
 static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
 				    enum gas_query_ap_result result,
 				    const struct wpabuf *adv_proto,
@@ -1169,6 +1179,7 @@
 	const u8 *pos;
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+	int res;
 
 	if (!auth || !auth->auth_success) {
 		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
@@ -1199,7 +1210,16 @@
 		goto fail;
 	}
 
-	if (dpp_conf_resp_rx(auth, resp) < 0) {
+	res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP3
+	if (res == -3) {
+		wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+		eloop_register_timeout(0, 0, hostapd_dpp_build_new_key, hapd,
+				       NULL);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		goto fail;
 	}
@@ -1986,6 +2006,17 @@
 	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
 		   MAC2STR(src));
 
+	if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_1 && v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv2 Exchange Request when configured to be PKEX v1 only");
+		return;
+	}
+	if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_2 && !v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv1 Exchange Request when configured to be PKEX v2 only");
+		return;
+	}
+
 	/* TODO: Support multiple PKEX codes by iterating over all the enabled
 	 * values here */
 
@@ -2349,6 +2380,13 @@
 	if (!auth)
 		return;
 
+#ifdef CONFIG_DPP3
+	if (auth->waiting_new_key && ok) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
 		   ok);
 	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
@@ -2409,6 +2447,11 @@
 {
 	struct dpp_bootstrap_info *own_bi;
 	const char *pos, *end;
+#ifdef CONFIG_DPP3
+		enum dpp_pkex_ver ver = PKEX_VER_AUTO;
+#else /* CONFIG_DPP3 */
+		enum dpp_pkex_ver ver = PKEX_VER_ONLY_1;
+#endif /* CONFIG_DPP3 */
 	int tcp_port = DPP_TCP_PORT;
 	struct hostapd_ip_addr *ipaddr = NULL;
 #ifdef CONFIG_DPP2
@@ -2474,27 +2517,22 @@
 	if (!hapd->dpp_pkex_code)
 		return -1;
 
+	pos = os_strstr(cmd, " ver=");
+	if (pos) {
+		int v;
+
+		pos += 5;
+		v = atoi(pos);
+		if (v == 1)
+			ver = PKEX_VER_ONLY_1;
+		else if (v == 2)
+			ver = PKEX_VER_ONLY_2;
+		else
+			return -1;
+	}
+	hapd->dpp_pkex_ver = ver;
+
 	if (os_strstr(cmd, " init=1")) {
-#ifdef CONFIG_DPP3
-		enum hostapd_dpp_pkex_ver ver = PKEX_VER_AUTO;
-#else /* CONFIG_DPP3 */
-		enum hostapd_dpp_pkex_ver ver = PKEX_VER_ONLY_1;
-#endif /* CONFIG_DPP3 */
-
-		pos = os_strstr(cmd, " ver=");
-		if (pos) {
-			int v;
-
-			pos += 5;
-			v = atoi(pos);
-			if (v == 1)
-				ver = PKEX_VER_ONLY_1;
-			else if (v == 2)
-				ver = PKEX_VER_ONLY_2;
-			else
-				return -1;
-		}
-
 		if (hostapd_dpp_pkex_init(hapd, ver, ipaddr, tcp_port) < 0)
 			return -1;
 	} else {
@@ -2646,6 +2684,9 @@
 	if (hapd->iface->interfaces)
 		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);
+#endif /* CONFIG_DPP3 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
 	hostapd_dpp_pkex_remove(hapd, "*");
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f353a0e..643a273 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -261,12 +261,7 @@
 	}
 #endif /* NEED_AP_MLME */
 
-#ifdef CONFIG_INTERWORKING
-	if (elems.ext_capab && elems.ext_capab_len > 4) {
-		if (elems.ext_capab[4] & 0x01)
-			sta->qos_map_enabled = 1;
-	}
-#endif /* CONFIG_INTERWORKING */
+	check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
 
 #ifdef CONFIG_HS20
 	wpabuf_free(sta->hs20_ie);
@@ -870,10 +865,11 @@
 
 	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, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "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",
 		       finished ? "had" : "starting",
 		       freq, ht, hapd->iconf->ch_switch_vht_config,
-		       hapd->iconf->ch_switch_he_config, offset,
+		       hapd->iconf->ch_switch_he_config,
+		       hapd->iconf->ch_switch_eht_config, offset,
 		       width, channel_width_to_string(width), cf1, cf2);
 
 	if (!hapd->iface->current_mode) {
@@ -953,9 +949,23 @@
 		else if (hapd->iconf->ch_switch_he_config &
 			 CH_SWITCH_HE_DISABLED)
 			hapd->iconf->ieee80211ax = 0;
+#ifdef CONFIG_IEEE80211BE
+	} else if (hapd->iconf->ch_switch_eht_config) {
+		/* CHAN_SWITCH EHT config */
+		if (hapd->iconf->ch_switch_eht_config &
+		    CH_SWITCH_EHT_ENABLED) {
+			hapd->iconf->ieee80211be = 1;
+			hapd->iconf->ieee80211ax = 1;
+			if (!is_6ghz_freq(hapd->iface->freq))
+				hapd->iconf->ieee80211ac = 1;
+		} else if (hapd->iconf->ch_switch_eht_config &
+			   CH_SWITCH_EHT_DISABLED)
+			hapd->iconf->ieee80211be = 0;
+#endif /* CONFIG_IEEE80211BE */
 	}
 	hapd->iconf->ch_switch_vht_config = 0;
 	hapd->iconf->ch_switch_he_config = 0;
+	hapd->iconf->ch_switch_eht_config = 0;
 
 	if (width == CHAN_WIDTH_40 || width == CHAN_WIDTH_80 ||
 	    width == CHAN_WIDTH_80P80 || width == CHAN_WIDTH_160)
@@ -1011,7 +1021,9 @@
 		hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
 
 #ifdef CONFIG_OCV
-	if (hapd->conf->ocv) {
+	if (hapd->conf->ocv &&
+	    !(hapd->iface->drv_flags2 &
+	      WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP)) {
 		struct sta_info *sta;
 		bool check_sa_query = false;
 
@@ -2084,6 +2096,32 @@
 			data->wds_sta_interface.ifname,
 			data->wds_sta_interface.sta_addr);
 		break;
+#ifdef CONFIG_IEEE80211AX
+	case EVENT_BSS_COLOR_COLLISION:
+		/* The BSS color is shared amongst all BBSs on a specific phy.
+		 * Therefore we always start the color change on the primary
+		 * BSS. */
+		wpa_printf(MSG_DEBUG, "BSS color collision on %s",
+			   hapd->conf->iface);
+		hostapd_switch_color(hapd->iface->bss[0],
+				     data->bss_color_collision.bitmap);
+		break;
+	case EVENT_CCA_STARTED_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA started on on %s",
+			   hapd->conf->iface);
+		break;
+	case EVENT_CCA_ABORTED_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+			   hapd->conf->iface);
+		hostapd_cleanup_cca_params(hapd);
+		break;
+	case EVENT_CCA_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+			   hapd->conf->iface);
+		hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+		hostapd_cleanup_cca_params(hapd);
+		break;
+#endif /* CONFIG_IEEE80211AX */
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 4b88641..ef53c41 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -66,6 +66,10 @@
 static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
 static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
 						    void *timeout_ctx);
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+						 void *user_ctx);
+#endif /* CONFIG_IEEE80211AX */
 
 
 int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -462,6 +466,10 @@
 	}
 	eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
 #endif /* CONFIG_SAE */
+
+#ifdef CONFIG_IEEE80211AX
+	eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211AX */
 }
 
 
@@ -1458,14 +1466,14 @@
 }
 
 
-static void hostapd_set_acl(struct hostapd_data *hapd)
+int hostapd_set_acl(struct hostapd_data *hapd)
 {
 	struct hostapd_config *conf = hapd->iconf;
-	int err;
+	int err = 0;
 	u8 accept_acl;
 
 	if (hapd->iface->drv_max_acl_mac_addrs == 0)
-		return;
+		return 0;
 
 	if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
 		accept_acl = 1;
@@ -1474,7 +1482,7 @@
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set accept acl");
-			return;
+			return -1;
 		}
 	} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
 		accept_acl = 0;
@@ -1483,9 +1491,10 @@
 					   accept_acl);
 		if (err) {
 			wpa_printf(MSG_DEBUG, "Failed to set deny acl");
-			return;
+			return -1;
 		}
 	}
+	return err;
 }
 
 
@@ -2067,6 +2076,7 @@
 				     hapd->iconf->ieee80211n,
 				     hapd->iconf->ieee80211ac,
 				     hapd->iconf->ieee80211ax,
+				     hapd->iconf->ieee80211be,
 				     hapd->iconf->secondary_channel,
 				     hostapd_get_oper_chwidth(hapd->iconf),
 				     hostapd_get_oper_centr_freq_seg0_idx(
@@ -3452,12 +3462,14 @@
 				    conf->channel, conf->enable_edmg,
 				    conf->edmg_channel, conf->ieee80211n,
 				    conf->ieee80211ac, conf->ieee80211ax,
-				    conf->secondary_channel,
+				    conf->ieee80211be, conf->secondary_channel,
 				    hostapd_get_oper_chwidth(conf),
 				    hostapd_get_oper_centr_freq_seg0_idx(conf),
 				    hostapd_get_oper_centr_freq_seg1_idx(conf),
 				    conf->vht_capab,
 				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				    NULL,
+				    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
 				    NULL))
 		return -1;
 
@@ -3545,11 +3557,12 @@
 		    &hapd->iface->cs_oper_class,
 		    &chan) == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_DEBUG,
-			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d)",
 			   settings->freq_params.freq,
 			   settings->freq_params.sec_channel_offset,
 			   settings->freq_params.vht_enabled,
-			   settings->freq_params.he_enabled);
+			   settings->freq_params.he_enabled,
+			   settings->freq_params.eht_enabled);
 		return -1;
 	}
 
@@ -3606,6 +3619,11 @@
 void hostapd_chan_switch_config(struct hostapd_data *hapd,
 				struct hostapd_freq_params *freq_params)
 {
+	if (freq_params->eht_enabled)
+		hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_ENABLED;
+	else
+		hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_DISABLED;
+
 	if (freq_params->he_enabled)
 		hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
 	else
@@ -3618,7 +3636,8 @@
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "CHAN_SWITCH HE config 0x%x VHT config 0x%x",
+		       "CHAN_SWITCH EHT config 0x%x HE config 0x%x VHT config 0x%x",
+		       hapd->iconf->ch_switch_eht_config,
 		       hapd->iconf->ch_switch_he_config,
 		       hapd->iconf->ch_switch_vht_config);
 }
@@ -3696,6 +3715,7 @@
 	iface->conf->ieee80211n = freq_params->ht_enabled;
 	iface->conf->ieee80211ac = freq_params->vht_enabled;
 	iface->conf->ieee80211ax = freq_params->he_enabled;
+	iface->conf->ieee80211be = freq_params->eht_enabled;
 
 	/*
 	 * cs_params must not be cleared earlier because the freq_params
@@ -3706,6 +3726,139 @@
 	hostapd_enable_iface(iface);
 }
 
+
+#ifdef CONFIG_IEEE80211AX
+
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd)
+{
+	hapd->cca_count = 0;
+	hapd->cca_color = 0;
+	hapd->cca_c_off_beacon = 0;
+	hapd->cca_c_off_proberesp = 0;
+	hapd->cca_in_progress = false;
+}
+
+
+static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+				     struct cca_settings *settings)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	u8 old_color;
+	int ret;
+
+	if (!iface || iface->conf->he_op.he_bss_color_disabled)
+		return -1;
+
+	old_color = iface->conf->he_op.he_bss_color;
+	iface->conf->he_op.he_bss_color = hapd->cca_color;
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+	if (ret)
+		return ret;
+
+	iface->conf->he_op.he_bss_color = old_color;
+
+	settings->cca_count = hapd->cca_count;
+	settings->cca_color = hapd->cca_color,
+	hapd->cca_in_progress = true;
+
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_cca);
+	if (ret) {
+		free_beacon_data(&settings->beacon_after);
+		return ret;
+	}
+
+	settings->counter_offset_beacon = hapd->cca_c_off_beacon;
+	settings->counter_offset_presp = hapd->cca_c_off_proberesp;
+
+	return 0;
+}
+
+
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+						 void *user_ctx)
+{
+	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+	os_time_t delta_t;
+	unsigned int b;
+	int i, r;
+
+	 /* CCA can be triggered once the handler constantly receives
+	  * color collision events to for at least
+	  * DOT11BSS_COLOR_COLLISION_AP_PERIOD (50 s by default). */
+	delta_t = hapd->last_color_collision.sec -
+		hapd->first_color_collision.sec;
+	if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD)
+		return;
+
+	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)))
+			break;
+
+		r = (r + 1) % HE_OPERATION_BSS_COLOR_MAX;
+	}
+
+	if (i == HE_OPERATION_BSS_COLOR_MAX) {
+		/* There are no free colors so turn BSS coloring off */
+		wpa_printf(MSG_INFO,
+			   "No free colors left, turning off BSS coloring");
+		hapd->iface->conf->he_op.he_bss_color_disabled = 1;
+		hapd->iface->conf->he_op.he_bss_color = os_random() % 63 + 1;
+		for (b = 0; b < hapd->iface->num_bss; b++)
+			ieee802_11_set_beacon(hapd->iface->bss[b]);
+		return;
+	}
+
+	for (b = 0; b < hapd->iface->num_bss; b++) {
+		struct hostapd_data *bss = hapd->iface->bss[b];
+		struct cca_settings settings;
+		int ret;
+
+		hostapd_cleanup_cca_params(bss);
+		bss->cca_color = r;
+		bss->cca_count = 10;
+
+		if (hostapd_fill_cca_settings(bss, &settings)) {
+			hostapd_cleanup_cca_params(bss);
+			continue;
+		}
+
+		ret = hostapd_drv_switch_color(bss, &settings);
+		if (ret)
+			hostapd_cleanup_cca_params(bss);
+
+		free_beacon_data(&settings.beacon_cca);
+		free_beacon_data(&settings.beacon_after);
+	}
+}
+
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+{
+	struct os_reltime now;
+
+	if (hapd->cca_in_progress)
+		return;
+
+	if (os_get_reltime(&now))
+		return;
+
+	hapd->color_collision_bitmap = bitmap;
+	hapd->last_color_collision = now;
+
+	if (eloop_is_timeout_registered(hostapd_switch_color_timeout_handler,
+					hapd, NULL))
+		return;
+
+	hapd->first_color_collision = now;
+	/* 10 s window as margin for persistent color collision reporting */
+	eloop_register_timeout(DOT11BSS_COLOR_COLLISION_AP_PERIOD + 10, 0,
+			       hostapd_switch_color_timeout_handler,
+			       hapd, NULL);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
 #endif /* NEED_AP_MLME */
 
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 7f7877b..6b9b65f 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -14,6 +14,7 @@
 #endif /* CONFIG_SQLITE */
 
 #include "common/defs.h"
+#include "common/dpp.h"
 #include "utils/list.h"
 #include "ap_config.h"
 #include "drivers/driver.h"
@@ -207,6 +208,7 @@
 
 	void *ssl_ctx;
 	void *eap_sim_db_priv;
+	struct crypto_rsa_key *imsi_privacy_key;
 	struct radius_server_data *radius_srv;
 	struct dl_list erp_keys; /* struct eap_server_erp_key */
 
@@ -294,6 +296,17 @@
 	unsigned int cs_c_off_ecsa_beacon;
 	unsigned int cs_c_off_ecsa_proberesp;
 
+#ifdef CONFIG_IEEE80211AX
+	bool cca_in_progress;
+	u8 cca_count;
+	u8 cca_color;
+	unsigned int cca_c_off_beacon;
+	unsigned int cca_c_off_proberesp;
+	struct os_reltime first_color_collision;
+	struct os_reltime last_color_collision;
+	u64 color_collision_bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
 #ifdef CONFIG_P2P
 	struct p2p_data *p2p;
 	struct p2p_group *p2p_group;
@@ -388,6 +401,7 @@
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
 	char *dpp_pkex_identifier;
+	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
 	char *dpp_configurator_params;
 	struct os_reltime dpp_last_init;
@@ -521,6 +535,21 @@
 	int *basic_rates;
 	int freq;
 
+	/* Background radar configuration */
+	struct {
+		int channel;
+		int secondary_channel;
+		int freq;
+		int centr_freq_seg0_idx;
+		int centr_freq_seg1_idx;
+		/* Main chain is on temporary channel during
+		 * CAC detection on radar offchain.
+		 */
+		unsigned int temp_ch:1;
+		/* CAC started on radar offchain */
+		unsigned int cac_started:1;
+	} radar_background;
+
 	u16 hw_flags;
 
 	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
@@ -648,6 +677,9 @@
 int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
 void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
 
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 				 int (*cb)(void *ctx, const u8 *sa,
@@ -693,4 +725,6 @@
 				struct fst_wpa_obj *iface_obj);
 #endif /* CONFIG_FST */
 
+int hostapd_set_acl(struct hostapd_data *hapd);
+
 #endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 6140a49..394e292 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -498,6 +498,7 @@
 	struct sae_password_entry *pw;
 	struct sae_pt *pt = NULL;
 	const struct sae_pk *pk = NULL;
+	struct hostapd_sta_wpa_psk_short *psk = NULL;
 
 	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
 		if (!is_broadcast_ether_addr(pw->peer_addr) &&
@@ -519,6 +520,15 @@
 		pt = hapd->conf->ssid.pt;
 	}
 
+	if (!password) {
+		for (psk = sta->psk; psk; psk = psk->next) {
+			if (psk->is_passphrase) {
+				password = psk->passphrase;
+				break;
+			}
+		}
+	}
+
 	if (pw_entry)
 		*pw_entry = pw;
 	if (s_pt)
@@ -2315,9 +2325,8 @@
 }
 
 
-static int
-ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
-			   int res, struct radius_sta *info)
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+			       int res, struct radius_sta *info)
 {
 	u32 session_timeout = info->session_timeout;
 	u32 acct_interim_interval = info->acct_interim_interval;
@@ -3112,6 +3121,7 @@
 	int ret, inc_y;
 	bool derive_keys;
 	u32 i;
+	bool derive_kdk;
 
 	if (!groups)
 		groups = default_groups;
@@ -3151,10 +3161,14 @@
 	sta->pasn->akmp = rsn_data.key_mgmt;
 	sta->pasn->cipher = rsn_data.pairwise_cipher;
 
-	if (hapd->conf->force_kdk_derivation ||
-	    ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
-	     ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
-				       WLAN_RSNX_CAPAB_SECURE_LTF)))
+	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;
@@ -4123,32 +4137,6 @@
 }
 
 
-static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
-			   const u8 *ext_capab_ie, size_t ext_capab_ie_len)
-{
-#ifdef CONFIG_INTERWORKING
-	/* check for QoS Map support */
-	if (ext_capab_ie_len >= 5) {
-		if (ext_capab_ie[4] & 0x01)
-			sta->qos_map_enabled = 1;
-	}
-#endif /* CONFIG_INTERWORKING */
-
-	if (ext_capab_ie_len > 0) {
-		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
-		os_free(sta->ext_capability);
-		sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
-		if (sta->ext_capability) {
-			sta->ext_capability[0] = ext_capab_ie_len;
-			os_memcpy(sta->ext_capability + 1, ext_capab_ie,
-				  ext_capab_ie_len);
-		}
-	}
-
-	return WLAN_STATUS_SUCCESS;
-}
-
-
 #ifdef CONFIG_OWE
 
 static int owe_group_supported(struct hostapd_data *hapd, u16 group)
@@ -4203,8 +4191,21 @@
 	else
 		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 
-	crypto_ecdh_deinit(sta->owe_ecdh);
-	sta->owe_ecdh = crypto_ecdh_init(group);
+	if (sta->owe_group == group && sta->owe_ecdh) {
+		/* This is a workaround for mac80211 behavior of retransmitting
+		 * the Association Request frames multiple times if the link
+		 * layer retries (i.e., seq# remains same) fail. The mac80211
+		 * initiated retransmission will use a different seq# and as
+		 * such, will go through duplicate detection. If we were to
+		 * change our DH key for that attempt, there would be two
+		 * different DH shared secrets and the STA would likely select
+		 * the wrong one. */
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
+	} else {
+		crypto_ecdh_deinit(sta->owe_ecdh);
+		sta->owe_ecdh = crypto_ecdh_init(group);
+	}
 	if (!sta->owe_ecdh)
 		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 	sta->owe_group = group;
@@ -4553,6 +4554,17 @@
 		}
 	}
 #endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
+					  elems.he_capabilities,
+					  elems.he_capabilities_len,
+					  elems.eht_capabilities,
+					  elems.eht_capabilities_len);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_P2P
 	if (elems.p2p) {
@@ -4924,6 +4936,7 @@
 	struct ieee80211_ht_capabilities ht_cap;
 	struct ieee80211_vht_capabilities vht_cap;
 	struct ieee80211_he_capabilities he_cap;
+	struct ieee80211_eht_capabilities eht_cap;
 	int set = 1;
 
 	/*
@@ -4980,6 +4993,11 @@
 				     sta->he_capab_len);
 	}
 #endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+	if (sta->flags & WLAN_STA_EHT)
+		hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
+				      sta->eht_capab_len);
+#endif /* CONFIG_IEEE80211BE */
 
 	/*
 	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -4993,6 +5011,8 @@
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags & WLAN_STA_HE ? &he_cap : NULL,
 			    sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+			    sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
+			    sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
 			    sta->he_6ghz_capab,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
 			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
@@ -5043,6 +5063,13 @@
 	if (sta && sta->dpp_pfs)
 		buflen += 5 + sta->dpp_pfs->curve->prime_len;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	buf = os_zalloc(buflen);
 	if (!buf) {
 		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -5151,6 +5178,7 @@
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
 		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
 		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_cca(hapd, p);
 		p = hostapd_eid_spatial_reuse(hapd, p);
 		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
 		p = hostapd_eid_he_6ghz_band_cap(hapd, p);
@@ -5188,6 +5216,13 @@
 rsnxe_done:
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_eht_operation(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 #ifdef CONFIG_OWE
 	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
 	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
@@ -6903,6 +6938,38 @@
 }
 
 
+static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
+				 enum max_tx_pwr_interpretation tx_pwr_intrpn,
+				 u8 tx_pwr_cat, u8 tx_pwr)
+{
+	int i;
+
+	*eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
+	*eid++ = 2 + tx_pwr_count; /* Length */
+
+	/*
+	 * Transmit Power Information field
+	 *	bits 0-2 : Maximum Transmit Power Count
+	 *	bits 3-5 : Maximum Transmit Power Interpretation
+	 *	bits 6-7 : Maximum Transmit Power Category
+	 */
+	*eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
+
+	/* Maximum Transmit Power field */
+	for (i = 0; i <= tx_pwr_count; i++)
+		*eid++ = tx_pwr;
+
+	return eid;
+}
+
+
+/*
+ * TODO: Extract power limits from channel data after 6G regulatory
+ *	support.
+ */
+#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT      (-1) /* dBm/MHz */
+#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT  5    /* dBm/MHz */
+
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
 {
 	struct hostapd_iface *iface = hapd->iface;
@@ -6927,6 +6994,43 @@
 	if (i == mode->num_channels)
 		return eid;
 
+#ifdef CONFIG_IEEE80211AX
+	/* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
+	 * States): An AP that is an Indoor Access Point per regulatory rules
+	 * shall send at least two Transmit Power Envelope elements in Beacon
+	 * and Probe Response frames as follows:
+	 *  - Maximum Transmit Power Category subfield = Default;
+	 *	Unit interpretation = Regulatory client EIRP PSD
+	 *  - Maximum Transmit Power Category subfield = Subordinate Device;
+	 *	Unit interpretation = Regulatory client EIRP PSD
+	 */
+	if (is_6ghz_op_class(iconf->op_class)) {
+		enum max_tx_pwr_interpretation tx_pwr_intrpn;
+
+		/* Same Maximum Transmit Power for all 20 MHz bands */
+		tx_pwr_count = 0;
+		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+
+		/* Default Transmit Power Envelope for Global Operating Class */
+		tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+					   REG_DEFAULT_CLIENT, tx_pwr);
+
+		/* Indoor Access Point must include an additional TPE for
+		 * subordinate devices */
+		if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+			/* TODO: Extract PSD limits from channel data */
+			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+						   tx_pwr_intrpn,
+						   REG_SUBORDINATE_CLIENT,
+						   tx_pwr);
+		}
+
+		return eid;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	switch (hostapd_get_oper_chwidth(iconf)) {
 	case CHANWIDTH_USE_HT:
 		if (iconf->secondary_channel == 0) {
@@ -6999,19 +7103,9 @@
 	else
 		tx_pwr = max_tx_power;
 
-	*eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE;
-	*eid++ = 2 + tx_pwr_count;
-
-	/*
-	 * Max Transmit Power count and
-	 * Max Transmit Power units = 0 (EIRP)
-	 */
-	*eid++ = tx_pwr_count;
-
-	for (i = 0; i <= tx_pwr_count; i++)
-		*eid++ = tx_pwr;
-
-	return eid;
+	return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
+				    0 /* Reserved for bands other than 6 GHz */,
+				    tx_pwr);
 }
 
 
@@ -7022,7 +7116,8 @@
 
 	if (!hapd->cs_freq_params.channel ||
 	    (!hapd->cs_freq_params.vht_enabled &&
-	     !hapd->cs_freq_params.he_enabled))
+	     !hapd->cs_freq_params.he_enabled &&
+	     !hapd->cs_freq_params.eht_enabled))
 		return eid;
 
 	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c59ad5e..fa1f47b 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -78,6 +78,10 @@
 			  const struct ieee80211_he_capabilities *he_cap,
 			  struct ieee80211_he_capabilities *neg_he_cap,
 			  size_t he_capab_len);
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+			   const struct ieee80211_eht_capabilities *src,
+			   struct ieee80211_eht_capabilities *dest,
+			   size_t len);
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab);
@@ -100,6 +104,7 @@
 			   const u8 *he_6ghz_capab);
 int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
 				 enum ieee80211_op_mode mode);
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 		       const u8 *buf, size_t len, int ack);
 void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -194,7 +199,20 @@
 
 void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
 u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
 size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
 u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+			       int res, struct radius_sta *info);
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+			   enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode,
+		       const u8 *he_capab, size_t he_capab_len,
+		       const u8 *eht_capab, size_t eht_capab_len);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 783ee6d..4277d82 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,8 @@
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
 #include "ieee802_11.h"
 #include "ieee802_1x.h"
 #include "ieee802_11_auth.h"
@@ -43,6 +45,11 @@
 	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
 	size_t auth_msg_len;
 	struct hostapd_acl_query_data *next;
+	bool radius_psk;
+	int akm;
+	u8 *anonce;
+	u8 *eapol;
+	size_t eapol_len;
 };
 
 
@@ -95,9 +102,11 @@
 
 static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
 {
-	if (query == NULL)
+	if (!query)
 		return;
 	os_free(query->auth_msg);
+	os_free(query->anonce);
+	os_free(query->eapol);
 	os_free(query);
 }
 
@@ -111,7 +120,7 @@
 
 	query->radius_id = radius_client_get_id(hapd->radius);
 	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
-	if (msg == NULL)
+	if (!msg)
 		return -1;
 
 	if (radius_msg_make_authenticator(msg) < 0) {
@@ -153,6 +162,31 @@
 		goto fail;
 	}
 
+	if (query->akm &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+				       wpa_akm_to_suite(query->akm))) {
+		wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
+		goto fail;
+	}
+
+	if (query->anonce &&
+	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+				   RADIUS_VENDOR_ID_FREERADIUS,
+				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
+				   query->anonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
+		goto fail;
+	}
+
+	if (query->eapol &&
+	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+				   RADIUS_VENDOR_ID_FREERADIUS,
+				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
+				   query->eapol, query->eapol_len)) {
+		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
+		goto fail;
+	}
+
 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
 		goto fail;
 	return 0;
@@ -260,23 +294,23 @@
 
 		/* No entry in the cache - query external RADIUS server */
 		query = os_zalloc(sizeof(*query));
-		if (query == NULL) {
+		if (!query) {
 			wpa_printf(MSG_ERROR, "malloc for query data failed");
 			return HOSTAPD_ACL_REJECT;
 		}
 		os_get_reltime(&query->timestamp);
 		os_memcpy(query->addr, addr, ETH_ALEN);
 		if (hostapd_radius_acl_query(hapd, addr, query)) {
-			wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
-				   "for ACL query.");
+			wpa_printf(MSG_DEBUG,
+				   "Failed to send Access-Request for ACL query.");
 			hostapd_acl_query_free(query);
 			return HOSTAPD_ACL_REJECT;
 		}
 
 		query->auth_msg = os_memdup(msg, len);
-		if (query->auth_msg == NULL) {
-			wpa_printf(MSG_ERROR, "Failed to allocate memory for "
-				   "auth frame.");
+		if (!query->auth_msg) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to allocate memory for auth frame.");
 			hostapd_acl_query_free(query);
 			return HOSTAPD_ACL_REJECT;
 		}
@@ -392,7 +426,7 @@
 		 * Passphrase is NULL iff there is no i-th Tunnel-Password
 		 * attribute in msg.
 		 */
-		if (passphrase == NULL)
+		if (!passphrase)
 			break;
 
 		/*
@@ -464,28 +498,30 @@
 		prev = query;
 		query = query->next;
 	}
-	if (query == NULL)
+	if (!query)
 		return RADIUS_RX_UNKNOWN;
 
-	wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
-		   "message (id=%d)", query->radius_id);
+	wpa_printf(MSG_DEBUG,
+		   "Found matching Access-Request for RADIUS message (id=%d)",
+		   query->radius_id);
 
 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
-		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
-			   "correct authenticator - dropped\n");
+		wpa_printf(MSG_INFO,
+			   "Incoming RADIUS packet did not have correct authenticator - dropped");
 		return RADIUS_RX_INVALID_AUTHENTICATOR;
 	}
 
 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
 	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
-		wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
-			   "query", hdr->code);
+		wpa_printf(MSG_DEBUG,
+			   "Unknown RADIUS message code %d to ACL query",
+			   hdr->code);
 		return RADIUS_RX_UNKNOWN;
 	}
 
 	/* Insert Accept/Reject info into ACL cache */
 	cache = os_zalloc(sizeof(*cache));
-	if (cache == NULL) {
+	if (!cache) {
 		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
 		goto done;
 	}
@@ -506,8 +542,9 @@
 			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
 			    &info->acct_interim_interval) == 0 &&
 		    info->acct_interim_interval < 60) {
-			wpa_printf(MSG_DEBUG, "Ignored too small "
-				   "Acct-Interim-Interval %d for STA " MACSTR,
+			wpa_printf(MSG_DEBUG,
+				   "Ignored too small Acct-Interim-Interval %d for STA "
+				   MACSTR,
 				   info->acct_interim_interval,
 				   MAC2STR(query->addr));
 			info->acct_interim_interval = 0;
@@ -557,20 +594,43 @@
 	cache->next = hapd->acl_cache;
 	hapd->acl_cache = cache;
 
+	if (query->radius_psk) {
+		struct sta_info *sta;
+		bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+
+		sta = ap_get_sta(hapd, query->addr);
+		if (!sta || !sta->wpa_sm) {
+			wpa_printf(MSG_DEBUG,
+				   "No STA/SM entry found for the RADIUS PSK response");
+			goto done;
+		}
+#ifdef NEED_AP_MLME
+		if (success &&
+		    (ieee802_11_set_radius_info(hapd, sta, cache->accepted,
+						info) < 0 ||
+		     ap_sta_bind_vlan(hapd, sta) < 0))
+			success = false;
+#endif /* NEED_AP_MLME */
+		wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
+	} else {
 #ifdef CONFIG_DRIVER_RADIUS_ACL
-	hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
-					info->session_timeout);
+		hostapd_drv_set_radius_acl_auth(hapd, query->addr,
+						cache->accepted,
+						info->session_timeout);
 #else /* CONFIG_DRIVER_RADIUS_ACL */
 #ifdef NEED_AP_MLME
-	/* Re-send original authentication frame for 802.11 processing */
-	wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
-		   "successful RADIUS ACL query");
-	ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
+		/* Re-send original authentication frame for 802.11 processing
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "Re-sending authentication frame after successful RADIUS ACL query");
+		ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
+				NULL);
 #endif /* NEED_AP_MLME */
 #endif /* CONFIG_DRIVER_RADIUS_ACL */
+	}
 
  done:
-	if (prev == NULL)
+	if (!prev)
 		hapd->acl_queries = query->next;
 	else
 		prev->next = query->next;
@@ -646,6 +706,40 @@
 	while (psk) {
 		struct hostapd_sta_wpa_psk_short *prev = psk;
 		psk = psk->next;
-		os_free(prev);
+		bin_clear_free(prev, sizeof(*prev));
 	}
 }
+
+
+#ifndef CONFIG_NO_RADIUS
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+				int key_mgmt, const u8 *anonce,
+				const u8 *eapol, size_t eapol_len)
+{
+	struct hostapd_acl_query_data *query;
+
+	query = os_zalloc(sizeof(*query));
+	if (!query)
+		return;
+
+	query->radius_psk = true;
+	query->akm = key_mgmt;
+	os_get_reltime(&query->timestamp);
+	os_memcpy(query->addr, addr, ETH_ALEN);
+	if (anonce)
+		query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
+	if (eapol) {
+		query->eapol = os_memdup(eapol, eapol_len);
+		query->eapol_len = eapol_len;
+	}
+	if (hostapd_radius_acl_query(hapd, addr, query)) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send Access-Request for RADIUS PSK/ACL query");
+		hostapd_acl_query_free(query);
+		return;
+	}
+
+	query->next = hapd->acl_queries;
+	hapd->acl_queries = query;
+}
+#endif /* CONFIG_NO_RADIUS */
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 9410f55..22ae1a9 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -36,5 +36,8 @@
 void hostapd_acl_expire(struct hostapd_data *hapd);
 void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
 			   struct hostapd_sta_wpa_psk_short *src);
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+				int key_mgmt, const u8 *anonce,
+				const u8 *eapol, size_t eapol_len);
 
 #endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
new file mode 100644
index 0000000..dbbf9a6
--- /dev/null
+++ b/src/ap/ieee802_11_eht.c
@@ -0,0 +1,353 @@
+/*
+ * hostapd / IEEE 802.11be EHT
+ * 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.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+
+
+static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+	u8 ru;
+	u16 sz = 0;
+
+	if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+	     EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+		return 0;
+
+	ru = (ppe_thres_hdr &
+	      EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
+	while (ru) {
+		if (ru & 0x1)
+			sz++;
+		ru >>= 1;
+	}
+
+	sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
+			EHT_PPE_THRES_NSS_SHIFT));
+	sz = (sz * 6) + 9;
+	if (sz % 8)
+		sz += 8;
+	sz /= 8;
+
+	return sz;
+}
+
+
+static u8 ieee80211_eht_mcs_set_size(const u8 *he_phy_cap,
+				     const u8 *eht_phy_cap)
+{
+	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+	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)
+		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 (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;
+}
+
+
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	struct eht_capabilities *eht_cap;
+	size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return 0;
+
+	eht_cap = &mode->eht_capab[opmode];
+	if (!eht_cap->eht_supported)
+		return 0;
+
+	len += ieee80211_eht_mcs_set_size(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);
+
+	return len;
+}
+
+
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+			   enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	struct eht_capabilities *eht_cap;
+	struct ieee80211_eht_capabilities *cap;
+	size_t mcs_nss_len, ppe_thresh_len;
+	u8 *pos = eid, *length_pos;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return eid;
+
+	eht_cap = &mode->eht_capab[opmode];
+	if (!eht_cap->eht_supported)
+		return eid;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	length_pos = pos++;
+	*pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
+
+	cap = (struct ieee80211_eht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->mac_cap = host_to_le16(eht_cap->mac_cap);
+	os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
+
+	if (!is_6ghz_op_class(hapd->iconf->op_class))
+		cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
+			~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
+	if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
+		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
+			~EHT_PHYCAP_SU_BEAMFORMER;
+
+	if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
+		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
+			~EHT_PHYCAP_SU_BEAMFORMEE;
+
+	if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
+		cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
+			~EHT_PHYCAP_MU_BEAMFORMER_MASK;
+
+	pos = cap->optional;
+
+	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+						 eht_cap->phy_cap);
+	if (mcs_nss_len) {
+		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
+		pos += mcs_nss_len;
+	}
+
+	ppe_thresh_len = ieee80211_eht_ppet_size(
+				WPA_GET_LE16(&eht_cap->ppet[0]),
+				eht_cap->phy_cap);
+	if (ppe_thresh_len) {
+		os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
+		pos += ppe_thresh_len;
+	}
+
+	*length_pos = pos - (eid + 2);
+	return pos;
+}
+
+
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_config *conf = hapd->iconf;
+	struct ieee80211_eht_operation *oper;
+	u8 *pos = eid, chwidth, seg0 = 0, seg1 = 0;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 5;
+	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
+
+	oper = (struct ieee80211_eht_operation *) pos;
+	oper->oper_params = EHT_OPER_INFO_PRESENT;
+
+	if (is_6ghz_op_class(conf->op_class))
+		chwidth = op_class_to_ch_width(conf->op_class);
+	else
+		chwidth = conf->eht_oper_chwidth;
+
+	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:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
+		seg1 = seg0;
+		if (hapd->iconf->channel < seg0)
+			seg0 -= 16;
+		else
+			seg0 += 16;
+		break;
+#endif
+	case CHANWIDTH_160MHZ:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
+		seg1 = seg0;
+		if (hapd->iconf->channel < seg0)
+			seg0 -= 8;
+		else
+			seg0 += 8;
+		break;
+	case CHANWIDTH_80MHZ:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
+		break;
+	case CHANWIDTH_USE_HT:
+		if (seg0)
+			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
+		break;
+	default:
+		return eid;
+	}
+
+	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
+	oper->oper_info.ccfs1 = seg1;
+
+	return pos + 4;
+}
+
+
+static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < mcs_count; i++) {
+		ap_mcs += i * 3;
+		sta_mcs += i * 3;
+
+		for (j = 0; j < map_len; j++) {
+			if (((ap_mcs[j] >> 4) & 0xFF) == 0)
+				continue;
+
+			if ((sta_mcs[j] & 0xFF) == 0)
+				continue;
+
+			return true;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching EHT MCS found between AP TX and STA RX");
+	return false;
+}
+
+
+static bool check_valid_eht_mcs(struct hostapd_data *hapd,
+				const u8 *sta_eht_capab,
+				enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	const struct ieee80211_eht_capabilities *capab;
+	const u8 *ap_mcs, *sta_mcs;
+	u8 mcs_count = 1;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return true;
+
+	ap_mcs = mode->eht_capab[opmode].mcs;
+	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,
+				       mode->eht_capab[opmode].phy_cap) ==
+	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
+		return check_valid_eht_mcs_nss(
+			hapd, ap_mcs, sta_mcs, 1,
+			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;
+		break;
+	}
+
+	return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
+				       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
+}
+
+
+static bool ieee80211_invalid_eht_cap_size(const u8 *he_cap, const u8 *eht_cap,
+					   size_t len)
+{
+	const struct ieee80211_he_capabilities *he_capab;
+	struct ieee80211_eht_capabilities *cap;
+	const u8 *he_phy_cap;
+	size_t cap_len;
+	u16 ppe_thres_hdr;
+
+	he_capab = (const struct ieee80211_he_capabilities *) he_cap;
+	he_phy_cap = he_capab->he_phy_capab_info;
+	cap = (struct ieee80211_eht_capabilities *) eht_cap;
+	cap_len = sizeof(*cap) - sizeof(cap->optional);
+	if (len < cap_len)
+		return true;
+
+	cap_len += ieee80211_eht_mcs_set_size(he_phy_cap, cap->phy_cap);
+	if (len < cap_len)
+		return true;
+
+	ppe_thres_hdr = len > cap_len + 1 ?
+		WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
+	cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
+
+	return len < cap_len;
+}
+
+
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode,
+		       const u8 *he_capab, size_t he_capab_len,
+		       const u8 *eht_capab, size_t eht_capab_len)
+{
+	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,
+					   eht_capab_len) ||
+	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
+		sta->flags &= ~WLAN_STA_EHT;
+		os_free(sta->eht_capab);
+		sta->eht_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	os_free(sta->eht_capab);
+	sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
+	if (!sta->eht_capab) {
+		sta->eht_capab_len = 0;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_EHT;
+	sta->eht_capab_len = eht_capab_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+			   const struct ieee80211_eht_capabilities *src,
+			   struct ieee80211_eht_capabilities *dest,
+			   size_t len)
+{
+	if (!src || !dest)
+		return;
+
+	if (len > sizeof(*dest))
+		len = sizeof(*dest);
+	/* TODO: mask out unsupported features */
+
+	os_memset(dest, 0, sizeof(*dest));
+	os_memcpy(dest, src, len);
+}
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 6cd6c90..1e74c58 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -29,17 +29,19 @@
 
 	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
 		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+	/* Count the number of 1 bits in RU Index Bitmask */
 	while (ru) {
 		if (ru & 0x1)
 			sz++;
 		ru >>= 1;
 	}
 
+	/* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
+	/* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
 	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
 	sz = (sz * 6) + 7;
-	if (sz % 8)
-		sz += 8;
-	sz /= 8;
+	/* PPE Pad to count the number of needed full octets */
+	sz = (sz + 7) / 8;
 
 	return sz;
 }
@@ -64,6 +66,7 @@
 {
 	struct ieee80211_he_capabilities *cap;
 	size_t cap_len;
+	u8 ppe_thres_hdr;
 
 	cap = (struct ieee80211_he_capabilities *) buf;
 	cap_len = sizeof(*cap) - sizeof(cap->optional);
@@ -74,9 +77,11 @@
 	if (len < cap_len)
 		return 1;
 
-	cap_len += ieee80211_he_ppet_size(buf[cap_len], cap->he_phy_capab_info);
+	ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
+	cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
+					  cap->he_phy_capab_info);
 
-	return len != cap_len;
+	return len < cap_len;
 }
 
 
@@ -195,7 +200,8 @@
 	if (hapd->iface->conf->he_op.he_er_su_disable)
 		params |= HE_OPERATION_ER_SU_DISABLE;
 
-	if (hapd->iface->conf->he_op.he_bss_color_disabled)
+	if (hapd->iface->conf->he_op.he_bss_color_disabled ||
+	    hapd->cca_in_progress)
 		params |= HE_OPERATION_BSS_COLOR_DISABLED;
 	if (hapd->iface->conf->he_op.he_bss_color_partial)
 		params |= HE_OPERATION_BSS_COLOR_PARTIAL;
@@ -213,6 +219,7 @@
 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
 		u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
+		u8 control;
 
 		if (!seg0)
 			seg0 = hapd->iconf->channel;
@@ -220,16 +227,28 @@
 		params |= HE_OPERATION_6GHZ_OPER_INFO;
 
 		/* 6 GHz Operation Information field
-		 * IEEE P802.11ax/D8.0, 9.4.2.249 HE Operation element,
+		 * IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
 		 * Figure 9-788k
 		 */
 		*pos++ = hapd->iconf->channel; /* Primary Channel */
 
-		/* Control: Channel Width */
+		/* Control:
+		 *	bits 0-1: Channel Width
+		 *	bit 2: Duplicate Beacon
+		 *	bits 3-5: Regulatory Info
+		 */
+		/* Channel Width */
 		if (seg1)
-			*pos++ = 3;
+			control = 3;
 		else
-			*pos++ = center_idx_to_bw_6ghz(seg0);
+			control = center_idx_to_bw_6ghz(seg0);
+		if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
+			control |= HE_6GHZ_STANDARD_POWER_AP <<
+				HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+		else
+			control |= HE_6GHZ_INDOOR_AP <<
+				HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+		*pos++ = control;
 
 		/* Channel Center Freq Seg0/Seg1 */
 		if (hapd->iconf->he_oper_chwidth == 2) {
@@ -520,3 +539,19 @@
 	return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
 		hapd->iface->conf->he_op.he_twt_responder;
 }
+
+
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
+{
+	if (!hapd->cca_in_progress)
+		return eid;
+
+	/* BSS Color Change Announcement element */
+	*eid++ = WLAN_EID_EXTENSION;
+	*eid++ = 3;
+	*eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
+	*eid++ = hapd->cca_count; /* Color Switch Countdown */
+	*eid++ = hapd->cca_color; /* New BSS Color Information */
+
+	return eid;
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 4bff9e5..6154895 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1093,3 +1093,29 @@
 
 	return pos;
 }
+
+
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		    const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+	/* check for QoS Map support */
+	if (ext_capab_ie_len >= 5) {
+		if (ext_capab_ie[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	if (ext_capab_ie_len > 0) {
+		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+		os_free(sta->ext_capability);
+		sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+		if (sta->ext_capability) {
+			sta->ext_capability[0] = ext_capab_ie_len;
+			os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+				  ext_capab_ie_len);
+		}
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 753c883..fb5e920 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -2448,6 +2448,9 @@
 	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
 	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
 	conf.erp_domain = hapd->conf->erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+	conf.eap_skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	os_memset(&cb, 0, sizeof(cb));
 	cb.eapol_send = ieee802_1x_eapol_send;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 229edd2..e37324f 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -225,6 +225,7 @@
 	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
 	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
 	int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
+	bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
 	struct wpa_ssid_value ssid;
 	u8 channel, op_class;
 	u8 center_freq1_idx = 0, center_freq2_idx = 0;
@@ -260,10 +261,12 @@
 		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
 		if (vht)
 			bssid_info |= NEI_REP_BSSID_INFO_VHT;
-		if (he)
-			bssid_info |= NEI_REP_BSSID_INFO_HE;
 	}
 
+	if (he)
+		bssid_info |= NEI_REP_BSSID_INFO_HE;
+	if (eht)
+		bssid_info |= NEI_REP_BSSID_INFO_EHT;
 	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
 
 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index ccd1ed9..c541926 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -358,6 +358,7 @@
 	os_free(sta->vht_operation);
 	os_free(sta->he_capab);
 	os_free(sta->he_6ghz_capab);
+	os_free(sta->eht_capab);
 	hostapd_free_psk_list(sta->psk);
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
@@ -410,6 +411,7 @@
 
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(sta->sae_postponed_commit);
+	forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	os_free(sta);
@@ -1251,8 +1253,6 @@
 	for (psk = ssid->wpa_psk; psk; psk = psk->next)
 		if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
 			break;
-	if (!psk)
-		return NULL;
 	if (!psk || !psk->keyid[0])
 		return NULL;
 
@@ -1461,7 +1461,7 @@
 
 	buf[0] = '\0';
 	res = os_snprintf(buf, buflen,
-			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1481,6 +1481,7 @@
 			  (flags & WLAN_STA_HT ? "[HT]" : ""),
 			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
 			  (flags & WLAN_STA_HE ? "[HE]" : ""),
+			  (flags & WLAN_STA_EHT ? "[EHT]" : ""),
 			  (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1553,7 +1554,7 @@
 	if (hostapd_sta_add(hapd, sta->addr, 0, 0,
 			    sta->supported_rates,
 			    sta->supported_rates_len,
-			    0, NULL, NULL, NULL, 0, NULL,
+			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
 			    sta->flags, 0, 0, 0, 0)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 27e72f9..af8f171 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -42,6 +42,7 @@
 #define WLAN_STA_HE BIT(24)
 #define WLAN_STA_6GHZ BIT(25)
 #define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
+#define WLAN_STA_EHT BIT(27)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -213,6 +214,8 @@
 	struct ieee80211_he_capabilities *he_capab;
 	size_t he_capab_len;
 	struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+	struct ieee80211_eht_capabilities *eht_capab;
+	size_t eht_capab_len;
 
 	int sa_query_count; /* number of pending SA Query requests;
 			     * 0 = no SA Query in progress */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 0042ed6..23a352c 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -409,6 +409,8 @@
 	u8 dialog_token, reason;
 	const u8 *pos, *end;
 	int enabled = hapd->conf->bss_transition;
+	char *hex = NULL;
+	size_t hex_len;
 
 #ifdef CONFIG_MBO
 	if (hapd->conf->mbo_enabled)
@@ -441,6 +443,17 @@
 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
 		    pos, end - pos);
 
+	hex_len = 2 * (end - pos) + 1;
+	if (hex_len > 1) {
+		hex = os_malloc(hex_len);
+		if (hex)
+			wpa_snprintf_hex(hex, hex_len, pos, end - pos);
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		BSS_TM_QUERY MACSTR " reason=%u%s%s",
+		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+	os_free(hex);
+
 	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
 }
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index bb7ee25..ad91883 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -607,7 +607,7 @@
 	while (group) {
 		prev = group;
 		group = group->next;
-		os_free(prev);
+		bin_clear_free(prev, sizeof(*prev));
 	}
 
 	os_free(wpa_auth);
@@ -1485,6 +1485,12 @@
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
 	struct wpa_state_machine *sm = timeout_ctx;
 
+	if (sm->waiting_radius_psk) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Ignore EAPOL-Key timeout while waiting for RADIUS PSK");
+		return;
+	}
+
 	sm->pending_1_of_4_timeout = 0;
 	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
 	sm->TimeoutEvt = true;
@@ -1646,7 +1652,7 @@
 			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
 				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
-				os_free(buf);
+				bin_clear_free(buf, key_data_len);
 				return;
 			}
 			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
@@ -1667,10 +1673,10 @@
 #endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
-			os_free(buf);
+			bin_clear_free(buf, key_data_len);
 			return;
 		}
-		os_free(buf);
+		bin_clear_free(buf, key_data_len);
 	}
 
 	if (key_info & WPA_KEY_INFO_MIC) {
@@ -1823,9 +1829,9 @@
 	case WPA_DEAUTH:
 	case WPA_DISASSOC:
 		sm->DeauthenticationRequest = true;
-#ifdef CONFIG_IEEE80211R_AP
 		os_memset(sm->PMK, 0, sizeof(sm->PMK));
 		sm->pmk_len = 0;
+#ifdef CONFIG_IEEE80211R_AP
 		os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
 		sm->xxkey_len = 0;
 		os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
@@ -1853,6 +1859,14 @@
 			break;
 		}
 
+		if (sm->ptkstart_without_success > 3) {
+			wpa_printf(MSG_INFO,
+				   "WPA: Multiple EAP reauth attempts without 4-way handshake completion, disconnect "
+				   MACSTR, MAC2STR(sm->addr));
+			sm->Disconnect = true;
+			break;
+		}
+
 		if (!sm->use_ext_key_id &&
 		    sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
 			wpa_printf(MSG_INFO,
@@ -2195,6 +2209,7 @@
 	sm->PTKRequest = false;
 	sm->TimeoutEvt = false;
 	sm->alt_snonce_valid = false;
+	sm->ptkstart_without_success++;
 
 	sm->TimeoutCtr++;
 	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
@@ -3026,6 +3041,19 @@
 			break;
 	}
 
+	if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+	    wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
+	    !sm->waiting_radius_psk) {
+		wpa_printf(MSG_DEBUG, "No PSK available - ask RADIUS server");
+		wpa_auth->cb->request_radius_psk(wpa_auth->cb_ctx, sm->addr,
+						 sm->wpa_key_mgmt,
+						 sm->ANonce,
+						 sm->last_rx_eapol_key,
+						 sm->last_rx_eapol_key_len);
+		sm->waiting_radius_psk = 1;
+		return;
+	}
+
 	if (!ok) {
 		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 				"invalid MIC in msg 2/4 of 4-Way Handshake");
@@ -3279,6 +3307,7 @@
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
 			  (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
 			  NULL, 0);
+	forced_memzero(&igtk, sizeof(igtk));
 
 	if (!conf->beacon_prot)
 		return pos;
@@ -3302,6 +3331,7 @@
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
 			  (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
 			  NULL, 0);
+	forced_memzero(&bigtk, sizeof(bigtk));
 
 	return pos;
 }
@@ -3382,7 +3412,7 @@
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
 	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
-	size_t gtk_len, kde_len, wpa_ie_len;
+	size_t gtk_len, kde_len = 0, wpa_ie_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
 	int secure, gtkidx, encr = 0;
@@ -3640,7 +3670,7 @@
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
 done:
-	os_free(kde);
+	bin_clear_free(kde, kde_len);
 	os_free(wpa_ie_buf);
 	os_free(wpa_ie_buf2);
 }
@@ -3709,6 +3739,8 @@
 #ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
 #endif /* CONFIG_IEEE80211R_AP */
+
+	sm->ptkstart_without_success = 0;
 }
 
 
@@ -3783,6 +3815,11 @@
 		} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
 			SM_ENTER(WPA_PTK, PTKSTART);
 #endif /* CONFIG_SAE */
+		} else if (wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+			   wpa_auth->conf.radius_psk) {
+			wpa_printf(MSG_DEBUG,
+				   "INITPSK: No PSK yet available for STA - use RADIUS later");
+			SM_ENTER(WPA_PTK, PTKSTART);
 		} else {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 					"no PSK configured for the STA");
@@ -3861,7 +3898,7 @@
 	struct wpa_group *gsm = sm->group;
 	const u8 *kde;
 	u8 *kde_buf = NULL, *pos, hdr[2];
-	size_t kde_len;
+	size_t kde_len = 0;
 	u8 *gtk, stub_gtk[32];
 	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 
@@ -3930,7 +3967,7 @@
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, NULL, kde, kde_len, gsm->GN, 1);
 
-	os_free(kde_buf);
+	bin_clear_free(kde_buf, kde_len);
 }
 
 
@@ -5572,7 +5609,7 @@
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
-	os_free(kde);
+	bin_clear_free(kde, kde_len);
 	return 0;
 }
 
@@ -5640,7 +5677,7 @@
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
 		       rsc, NULL, kde, kde_len, gsm->GN, 1);
 
-	os_free(kde_buf);
+	bin_clear_free(kde_buf, kde_len);
 	return 0;
 }
 
@@ -5711,3 +5748,28 @@
 
 
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success)
+{
+	if (!sm->waiting_radius_psk) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore RADIUS PSK response for " MACSTR
+			   " that did not wait one",
+			   MAC2STR(sm->addr));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS PSK response for " MACSTR " (%s)",
+		   MAC2STR(sm->addr), success ? "success" : "fail");
+	sm->waiting_radius_psk = 0;
+
+	if (success) {
+		/* Try to process the EAPOL-Key msg 2/4 again */
+		sm->EAPOLKeyReceived = true;
+	} else {
+		sm->Disconnect = true;
+	}
+
+	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 8f0b5a7..348a1de 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -275,6 +275,8 @@
 	 * PTK derivation regardless of advertised capabilities.
 	 */
 	bool force_kdk_derivation;
+
+	bool radius_psk;
 };
 
 typedef enum {
@@ -322,6 +324,9 @@
 	void (*store_ptksa)(void *ctx, const u8 *addr, int cipher,
 			    u32 life_time, const struct wpa_ptk *ptk);
 	void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher);
+	void (*request_radius_psk)(void *ctx, const u8 *addr, int key_mgmt,
+				   const u8 *anonce,
+				   const u8 *eapol, size_t eapol_len);
 #ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*add_sta_ft)(void *ctx, const u8 *sta_addr);
@@ -578,4 +583,6 @@
 void wpa_auth_set_enable_eapol_large_timeout(struct wpa_authenticator *wpa_auth,
 				     u8 val);
 
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index fef1104..7a97613 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2240,6 +2240,7 @@
 		wpa_printf(MSG_DEBUG,
 			   "FT: GTK subelem encryption failed: kek_len=%d",
 			   (int) kek_len);
+		forced_memzero(keybuf, sizeof(keybuf));
 		os_free(subelem);
 		return NULL;
 	}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9e88ea3..9e8dae1 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,6 +29,7 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
+#include "ieee802_11_auth.h"
 #include "pmksa_cache_auth.h"
 #include "wpa_auth.h"
 #include "wpa_auth_glue.h"
@@ -216,6 +217,8 @@
 	wconf->force_kdk_derivation = conf->force_kdk_derivation;
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_PASN */
+
+	wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
 }
 
 
@@ -390,10 +393,14 @@
 		psk = sta->psk->psk;
 		for (pos = sta->psk; pos; pos = pos->next) {
 			if (pos->is_passphrase) {
-				pbkdf2_sha1(pos->passphrase,
-					    hapd->conf->ssid.ssid,
-					    hapd->conf->ssid.ssid_len, 4096,
-					    pos->psk, PMK_LEN);
+				if (pbkdf2_sha1(pos->passphrase,
+						hapd->conf->ssid.ssid,
+						hapd->conf->ssid.ssid_len, 4096,
+						pos->psk, PMK_LEN) != 0) {
+					wpa_printf(MSG_WARNING,
+						   "Error in pbkdf2_sha1()");
+					continue;
+				}
 				pos->is_passphrase = 0;
 			}
 			if (pos->psk == prev_psk) {
@@ -1445,6 +1452,23 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_request_radius_psk(void *ctx, const u8 *addr, int key_mgmt,
+				       const u8 *anonce,
+				       const u8 *eapol, size_t eapol_len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	wpa_printf(MSG_DEBUG, "RADIUS PSK request for " MACSTR " key_mgmt=0x%x",
+		   MAC2STR(addr), key_mgmt);
+	wpa_hexdump(MSG_DEBUG, "ANonce", anonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAPOL", eapol, eapol_len);
+	hostapd_acl_req_radius_psk(hapd, addr, key_mgmt, anonce, eapol,
+				   eapol_len);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
 int hostapd_setup_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config _conf;
@@ -1488,6 +1512,9 @@
 		.set_session_timeout = hostapd_wpa_auth_set_session_timeout,
 		.get_session_timeout = hostapd_wpa_auth_get_session_timeout,
 #endif /* CONFIG_IEEE80211R_AP */
+#ifndef CONFIG_NO_RADIUS
+		.request_radius_psk = hostapd_request_radius_psk,
+#endif /* CONFIG_NO_RADIUS */
 	};
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
@@ -1633,4 +1660,10 @@
 	hapd->l2 = NULL;
 	hostapd_wpa_unregister_ft_oui(hapd);
 #endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	forced_memzero(hapd->last_gtk, WPA_GTK_MAX_LEN);
+	forced_memzero(hapd->last_igtk, WPA_IGTK_MAX_LEN);
+	forced_memzero(hapd->last_bigtk, WPA_BIGTK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index a6dc1a5..17cb5a2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -89,6 +89,7 @@
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
 	unsigned int alt_snonce_valid:1;
+	unsigned int waiting_radius_psk:1;
 #ifdef CONFIG_IEEE80211R_AP
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
@@ -96,6 +97,8 @@
 	unsigned int is_wnmsleep:1;
 	unsigned int pmkid_set:1;
 
+	unsigned int ptkstart_without_success;
+
 #ifdef CONFIG_OCV
 	int ocv_enabled;
 #endif /* CONFIG_OCV */
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index 46d94b4..e2c4e10 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -138,7 +138,6 @@
 	switch (co) {
 	case CONFIDENTIALITY_OFFSET_30:
 		return 30;
-		break;
 	case CONFIDENTIALITY_OFFSET_50:
 		return 50;
 	default:
@@ -329,7 +328,9 @@
 				  hapd->conf->macsec_replay_protect,
 				  hapd->conf->macsec_replay_window,
 				  hapd->conf->macsec_port,
-				  hapd->conf->mka_priority, hapd->conf->iface,
+				  hapd->conf->mka_priority,
+				  hapd->conf->macsec_csindex,
+				  hapd->conf->iface,
 				  hapd->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
 	if (!res)
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 4f1c76b..aacfa33 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1069,10 +1069,11 @@
 	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
 		wpabuf_free(wps->dev.vendor_ext[i]);
 	wps_device_data_free(&wps->dev);
-	os_free(wps->network_key);
+	bin_clear_free(wps->network_key, wps->network_key_len);
 	hostapd_wps_nfc_clear(wps);
 	wpabuf_free(wps->dh_pubkey);
 	wpabuf_free(wps->dh_privkey);
+	forced_memzero(wps->psk, sizeof(wps->psk));
 	os_free(wps);
 }
 
diff --git a/src/common/brcm_vendor.h b/src/common/brcm_vendor.h
index d77b007..c1f5807 100644
--- a/src/common/brcm_vendor.h
+++ b/src/common/brcm_vendor.h
@@ -40,15 +40,15 @@
  * @BRCM_VENDOR_SCMD_SET_CONNECT_PARAMS: Set some connect parameters.
  *      Used for the case that FW handle SAE.
  *
- * @BRCM_VENDOR_SCMD_SET_START_AP_PARAMS: Set SoftAP paramters.
+ * @BRCM_VENDOR_SCMD_SET_START_AP_PARAMS: Set SoftAP parameters.
  *      Used for the case that FW handle SAE.
  *
  * @BRCM_VENDOR_SCMD_ACS: ACS command/event which is used to
  *	invoke the ACS function in device and pass selected channels to
  *	hostapd. Uses enum qca_wlan_vendor_attr_acs_offload attributes.
  *
- * @BRCM_VENDOR_SCMD_MAX: This acts as a the tail of cmds list.
- *      Make sure it located at the end of the list.
+ * @BRCM_VENDOR_SCMD_MAX: This acts as a tail of cmds list.
+ *      Make sure it is located at the end of the list.
  *
  */
 enum brcm_nl80211_vendor_subcmds {
@@ -67,7 +67,7 @@
 };
 
 /**
- * enum brcm_nl80211_vendor_events - BRCM nl80211 asynchoronous event identifiers
+ * enum brcm_nl80211_vendor_events - BRCM nl80211 asynchronous event identifiers
  *
  * @BRCM_VENDOR_EVENT_UNSPEC: Reserved value 0
  *
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 42a9302..cc26b80 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -345,12 +345,36 @@
 }
 
 
+static int dpp_parse_uri_supported_curves(struct dpp_bootstrap_info *bi,
+					  const char *txt)
+{
+	int val;
+
+	if (!txt)
+		return 0;
+
+	val = hex2num(txt[0]);
+	if (val < 0)
+		return -1;
+	bi->supported_curves = val;
+
+	val = hex2num(txt[1]);
+	if (val > 0)
+		bi->supported_curves |= val << 4;
+
+	wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x",
+		   bi->supported_curves);
+
+	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;
+	const char *version = NULL, *supported_curves = NULL;
 	struct dpp_bootstrap_info *bi;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
@@ -383,6 +407,8 @@
 			pk = pos + 2;
 		else if (pos[0] == 'V' && pos[1] == ':' && !version)
 			version = pos + 2;
+		else if (pos[0] == 'B' && pos[1] == ':' && !supported_curves)
+			supported_curves = pos + 2;
 		else
 			wpa_hexdump_ascii(MSG_DEBUG,
 					  "DPP: Ignore unrecognized URI parameter",
@@ -404,6 +430,7 @@
 	    dpp_parse_uri_mac(bi, mac) < 0 ||
 	    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_pk(bi, pk) < 0) {
 		dpp_bootstrap_info_free(bi);
 		bi = NULL;
@@ -604,6 +631,7 @@
 {
 	char macstr[ETH_ALEN * 2 + 10];
 	size_t len;
+	char supp_curves[10];
 
 	len = 4; /* "DPP:" */
 	if (bi->chan)
@@ -621,11 +649,26 @@
 #endif /* CONFIG_DPP2 */
 	len += 4 + os_strlen(bi->pk); /* K:...;; */
 
+	if (bi->supported_curves) {
+		u8 val = bi->supported_curves;
+
+		if (val & 0xf0) {
+			val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4);
+			len += os_snprintf(supp_curves, sizeof(supp_curves),
+					   "B:%02x;", val);
+		} else {
+			len += os_snprintf(supp_curves, sizeof(supp_curves),
+					   "B:%x;", val);
+		}
+	} else {
+		supp_curves[0] = '\0';
+	}
+
 	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%sK:%s;;",
+	os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
 		    bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
 		    bi->chan ? ";" : "",
 		    macstr,
@@ -633,6 +676,7 @@
 		    bi->info ? ";" : "",
 		    DPP_VERSION == 3 ? "V:3;" :
 		    (DPP_VERSION == 2 ? "V:2;" : ""),
+		    supp_curves,
 		    bi->pk);
 	return 0;
 }
@@ -658,9 +702,12 @@
 {
 	size_t nonce_len;
 	size_t json_len, clear_len;
-	struct wpabuf *clear = NULL, *msg = NULL;
+	struct wpabuf *clear = NULL, *msg = NULL, *pe = NULL;
 	u8 *wrapped;
 	size_t attr_len;
+#ifdef CONFIG_DPP3
+	u8 auth_i[DPP_MAX_HASH_LEN];
+#endif /* CONFIG_DPP3 */
 
 	wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
 
@@ -675,6 +722,18 @@
 
 	/* { E-nonce, configAttrib }ke */
 	clear_len = 4 + nonce_len + 4 + json_len;
+#ifdef CONFIG_DPP3
+	if (auth->waiting_new_key) {
+		pe = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
+		if (!pe)
+			goto fail;
+		clear_len += 4 + wpabuf_len(pe);
+
+		if (dpp_derive_auth_i(auth, auth_i) < 0)
+			goto fail;
+		clear_len += 4 + auth->curve->hash_len;
+	}
+#endif /* CONFIG_DPP3 */
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -716,6 +775,21 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_DPP3
+	if (pe) {
+		wpa_printf(MSG_DEBUG, "DPP: Pe");
+		wpabuf_put_le16(clear, DPP_ATTR_I_PROTOCOL_KEY);
+		wpabuf_put_le16(clear, wpabuf_len(pe));
+		wpabuf_put_buf(clear, pe);
+	}
+	if (auth->waiting_new_key) {
+		wpa_printf(MSG_DEBUG, "DPP: Initiator Authentication Tag");
+		wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+		wpabuf_put_le16(clear, auth->curve->hash_len);
+		wpabuf_put_data(clear, auth_i, auth->curve->hash_len);
+	}
+#endif /* CONFIG_DPP3 */
+
 	/* configAttrib */
 	wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
 	wpabuf_put_le16(clear, json_len);
@@ -748,13 +822,15 @@
 
 	wpa_hexdump_buf(MSG_DEBUG,
 			"DPP: Configuration Request frame attributes", msg);
+out:
 	wpabuf_free(clear);
+	wpabuf_free(pe);
 	return msg;
 
 fail:
-	wpabuf_free(clear);
 	wpabuf_free(msg);
-	return NULL;
+	msg = NULL;
+	goto out;
 }
 
 
@@ -815,7 +891,7 @@
 	size_t len, name_len;
 	const char *tech = "infra";
 	const char *dpp_name;
-	struct wpabuf *buf, *json;
+	struct wpabuf *buf = NULL, *json = NULL;
 	char *csr = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -840,19 +916,17 @@
 		csr = base64_encode_no_lf(wpabuf_head(auth->csr),
 					  wpabuf_len(auth->csr), &csr_len);
 		if (!csr)
-			return NULL;
+			goto fail;
 		len += 30 + csr_len;
 	}
 #endif /* CONFIG_DPP2 */
 	json = wpabuf_alloc(len);
 	if (!json)
-		return NULL;
+		goto fail;
 
 	json_start_object(json, NULL);
-	if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) {
-		wpabuf_free(json);
-		return NULL;
-	}
+	if (json_add_string_escape(json, "name", dpp_name, name_len) < 0)
+		goto fail;
 	json_value_sep(json);
 	json_add_string(json, "wi-fi_tech", tech);
 	json_value_sep(json);
@@ -877,6 +951,7 @@
 	json_end_object(json);
 
 	buf = dpp_build_conf_req(auth, wpabuf_head(json));
+fail:
 	wpabuf_free(json);
 	os_free(csr);
 
@@ -1431,7 +1506,8 @@
 	struct wpabuf *buf = NULL;
 	char *signed_conn = NULL;
 	size_t tailroom;
-	const struct dpp_curve_params *curve;
+	const struct dpp_curve_params *curve; /* C-sign-key curve */
+	const struct dpp_curve_params *nak_curve; /* netAccessKey curve */
 	struct wpabuf *dppcon = NULL;
 	size_t extra_len = 1000;
 	int incl_legacy;
@@ -1444,6 +1520,10 @@
 		goto fail;
 	}
 	curve = auth->conf->curve;
+	if (auth->new_curve && auth->new_key_received)
+		nak_curve = auth->new_curve;
+	else
+		nak_curve = auth->curve;
 
 	akm = conf->akm;
 	if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -1461,7 +1541,7 @@
 		extra_len += os_strlen(conf->group_id);
 
 	/* Connector (JSON dppCon object) */
-	dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
+	dppcon = wpabuf_alloc(extra_len + 2 * nak_curve->prime_len * 4 / 3);
 	if (!dppcon)
 		goto fail;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1491,9 +1571,31 @@
 #ifdef CONFIG_TESTING_OPTIONS
 skip_groups:
 #endif /* CONFIG_TESTING_OPTIONS */
-	if (!auth->peer_protocol_key ||
-	    dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
-			  auth->curve) < 0) {
+	if (!auth->peer_protocol_key) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No peer protocol key available to build netAccessKey JWK");
+		goto fail;
+	}
+#ifdef CONFIG_DPP3
+	if (auth->conf->net_access_key_curve &&
+	    auth->curve != auth->conf->net_access_key_curve &&
+	    !auth->new_key_received) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer protocol key curve (%s) does not match the required netAccessKey curve (%s) - %s",
+			   auth->curve->name,
+			   auth->conf->net_access_key_curve->name,
+			   auth->waiting_new_key ?
+			   "the required key not received" :
+			   "request a new key");
+		if (auth->waiting_new_key)
+			auth->waiting_new_key = false; /* failed */
+		else
+			auth->waiting_new_key = true;
+		goto fail;
+	}
+#endif /* CONFIG_DPP3 */
+	if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+			  nak_curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
 		goto fail;
 	}
@@ -1605,6 +1707,20 @@
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
 			      wpabuf_head(buf), wpabuf_len(buf));
 
+#ifdef CONFIG_DPP3
+	if (!auth->conf->net_access_key_curve) {
+		/* All netAccessKey values used in the network will have to be
+		 * from the same curve for network introduction to work, so
+		 * hardcode the first used netAccessKey curve for consecutive
+		 * operations if there was no explicit configuration of which
+		 * curve to use. */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Update Configurator to require netAccessKey curve %s based on first provisioning",
+			   nak_curve->name);
+		auth->conf->net_access_key_curve = nak_curve;
+	}
+#endif /* CONFIG_DPP3 */
+
 out:
 	os_free(signed_conn);
 	wpabuf_free(dppcon);
@@ -1732,7 +1848,7 @@
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
 		    u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req)
 {
-	struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL;
+	struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL, *pc = NULL;
 	size_t clear_len, attr_len;
 	struct wpabuf *clear = NULL, *msg = NULL;
 	u8 *wrapped;
@@ -1766,6 +1882,10 @@
 	else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta &&
 		 auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr)
 		status = DPP_STATUS_CSR_NEEDED;
+#ifdef CONFIG_DPP3
+	else if (auth->waiting_new_key)
+		status = DPP_STATUS_NEW_KEY_NEEDED;
+#endif /* CONFIG_DPP3 */
 	else
 		status = DPP_STATUS_CONFIGURE_FAILURE;
 forced_status:
@@ -1785,6 +1905,31 @@
 	if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
 	    auth->conf_sta->csrattrs)
 		clear_len += 4 + os_strlen(auth->conf_sta->csrattrs);
+#ifdef CONFIG_DPP3
+	if (status == DPP_STATUS_NEW_KEY_NEEDED) {
+		struct crypto_ec_key *new_pc;
+
+		clear_len += 6; /* Finite Cyclic Group attribute */
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Generate a new own protocol key for the curve %s",
+			   auth->conf->net_access_key_curve->name);
+		new_pc = dpp_gen_keypair(auth->conf->net_access_key_curve);
+		if (!new_pc) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to generate new Pc");
+			return NULL;
+		}
+		pc = crypto_ec_key_get_pubkey_point(new_pc, 0);
+		if (!pc) {
+			crypto_ec_key_deinit(new_pc);
+			return NULL;
+		}
+		crypto_ec_key_deinit(auth->own_protocol_key);
+		auth->own_protocol_key = new_pc;
+		auth->new_curve = auth->conf->net_access_key_curve;
+		clear_len += 4 + wpabuf_len(pc);
+	}
+#endif /* CONFIG_DPP3 */
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1862,6 +2007,27 @@
 		wpabuf_put_str(clear, auth->conf_sta->csrattrs);
 	}
 
+#ifdef CONFIG_DPP3
+	if (status == DPP_STATUS_NEW_KEY_NEEDED && auth->conf &&
+	    auth->conf->net_access_key_curve) {
+		u16 ike_group = auth->conf->net_access_key_curve->ike_group;
+
+		/* Finite Cyclic Group attribute */
+		wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
+			   ike_group);
+		wpabuf_put_le16(clear, DPP_ATTR_FINITE_CYCLIC_GROUP);
+		wpabuf_put_le16(clear, 2);
+		wpabuf_put_le16(clear, ike_group);
+
+		if (pc) {
+			wpa_printf(MSG_DEBUG, "DPP: Pc");
+			wpabuf_put_le16(clear, DPP_ATTR_R_PROTOCOL_KEY);
+			wpabuf_put_le16(clear, wpabuf_len(pc));
+			wpabuf_put_buf(clear, pc);
+		}
+	}
+#endif /* CONFIG_DPP3 */
+
 #ifdef CONFIG_TESTING_OPTIONS
 skip_config_obj:
 	if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
@@ -1912,6 +2078,7 @@
 	wpabuf_clear_free(conf2);
 	wpabuf_clear_free(env_data);
 	wpabuf_clear_free(clear);
+	wpabuf_free(pc);
 
 	return msg;
 fail:
@@ -1933,6 +2100,10 @@
 	struct json_token *root = NULL, *token;
 	enum dpp_netrole netrole;
 	struct wpabuf *cert_req = NULL;
+#ifdef CONFIG_DPP3
+	const u8 *i_proto;
+	u16 i_proto_len;
+#endif /* CONFIG_DPP3 */
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -1986,6 +2157,59 @@
 	wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
 	os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
 
+#ifdef CONFIG_DPP3
+	i_proto = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_I_PROTOCOL_KEY, &i_proto_len);
+	if (i_proto && !auth->waiting_new_key) {
+		dpp_auth_fail(auth,
+			      "Enrollee included a new protocol key even though one was not expected");
+		goto fail;
+	}
+	if (i_proto) {
+		struct crypto_ec_key *pe;
+		u8 auth_i[DPP_MAX_HASH_LEN];
+		const u8 *rx_auth_i;
+		u16 rx_auth_i_len;
+
+		wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key (new Pe)",
+			    i_proto, i_proto_len);
+
+		pe = dpp_set_pubkey_point(auth->own_protocol_key,
+					  i_proto, i_proto_len);
+		if (!pe) {
+			dpp_auth_fail(auth,
+				      "Invalid Initiator Protocol Key (Pe)");
+			goto fail;
+		}
+		dpp_debug_print_key("New Peer Protocol Key (Pe)", pe);
+		crypto_ec_key_deinit(auth->peer_protocol_key);
+		auth->peer_protocol_key = pe;
+		auth->new_key_received = true;
+		auth->waiting_new_key = false;
+
+		if (dpp_derive_auth_i(auth, auth_i) < 0)
+			goto fail;
+
+		rx_auth_i = dpp_get_attr(unwrapped, unwrapped_len,
+					 DPP_ATTR_I_AUTH_TAG, &rx_auth_i_len);
+		if (!rx_auth_i) {
+			dpp_auth_fail(auth,
+				      "Missing Initiator Authentication Tag");
+			goto fail;
+		}
+		if (rx_auth_i_len != auth->curve->hash_len ||
+		    os_memcmp(rx_auth_i, auth_i, auth->curve->hash_len) != 0) {
+			dpp_auth_fail(auth,
+				      "Mismatch in Initiator Authenticating Tag");
+			wpa_hexdump(MSG_DEBUG, "DPP: Received Auth-I",
+				    rx_auth_i, rx_auth_i_len);
+			wpa_hexdump(MSG_DEBUG, "DPP: Derived Auth-I'",
+				    auth_i, auth->curve->hash_len);
+			goto fail;
+		}
+	}
+#endif /* CONFIG_DPP3 */
+
 	config_attr = dpp_get_attr(unwrapped, unwrapped_len,
 				   DPP_ATTR_CONFIG_ATTR_OBJ,
 				   &config_attr_len);
@@ -2989,6 +3213,72 @@
 		goto fail;
 	}
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	if (status[0] == DPP_STATUS_NEW_KEY_NEEDED) {
+		const u8 *fcgroup, *r_proto;
+		u16 fcgroup_len, r_proto_len;
+		u16 group;
+		const struct dpp_curve_params *curve;
+		struct crypto_ec_key *new_pe;
+		struct crypto_ec_key *pc;
+
+		fcgroup = dpp_get_attr(unwrapped, unwrapped_len,
+				       DPP_ATTR_FINITE_CYCLIC_GROUP,
+				       &fcgroup_len);
+		if (!fcgroup || fcgroup_len != 2) {
+			dpp_auth_fail(auth,
+				      "Missing or invalid required Finite Cyclic Group attribute");
+			goto fail;
+		}
+		group = WPA_GET_LE16(fcgroup);
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Configurator requested a new protocol key from group %u",
+			   group);
+		curve = dpp_get_curve_ike_group(group);
+		if (!curve) {
+			dpp_auth_fail(auth,
+				      "Unsupported group for new protocol key");
+			goto fail;
+		}
+
+		new_pe = dpp_gen_keypair(curve);
+		if (!new_pe) {
+			dpp_auth_fail(auth,
+				      "Failed to generate a new protocol key");
+			goto fail;
+		}
+
+		crypto_ec_key_deinit(auth->own_protocol_key);
+		auth->own_protocol_key = new_pe;
+		auth->new_curve = curve;
+
+		r_proto = dpp_get_attr(unwrapped, unwrapped_len,
+				       DPP_ATTR_R_PROTOCOL_KEY,
+				       &r_proto_len);
+		if (!r_proto) {
+			dpp_auth_fail(auth,
+				      "Missing required Responder Protocol Key attribute (Pc)");
+			goto fail;
+		}
+		wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key (new Pc)",
+			    r_proto, r_proto_len);
+
+		pc = dpp_set_pubkey_point(new_pe, r_proto, r_proto_len);
+		if (!pc) {
+			dpp_auth_fail(auth, "Invalid Responder Protocol Key (Pc)");
+			goto fail;
+		}
+		dpp_debug_print_key("New Peer Protocol Key (Pc)", pc);
+
+		crypto_ec_key_deinit(auth->peer_protocol_key);
+		auth->peer_protocol_key = pc;
+
+		auth->waiting_new_key = true;
+		ret = -3;
+		goto fail;
+	}
+#endif /* CONFIG_DPP3 */
 	if (status[0] != DPP_STATUS_OK) {
 		dpp_auth_fail(auth, "Configurator rejected configuration");
 		goto fail;
@@ -3903,10 +4193,47 @@
 }
 
 
+static int dpp_parse_supported_curves_list(struct dpp_bootstrap_info *bi,
+					   char *txt)
+{
+	char *token, *context = NULL;
+	u8 curves = 0;
+
+	if (!txt)
+		return 0;
+
+	while ((token = str_token(txt, ":", &context))) {
+		if (os_strcmp(token, "P-256") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_P_256);
+		} else if (os_strcmp(token, "P-384") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_P_384);
+		} else if (os_strcmp(token, "P-521") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_P_521);
+		} else if (os_strcmp(token, "BP-256") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_256);
+		} else if (os_strcmp(token, "BP-384") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_384);
+		} else if (os_strcmp(token, "BP-512") == 0) {
+			curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_512);
+		} else {
+			wpa_printf(MSG_DEBUG, "DPP: Unsupported curve '%s'",
+				   token);
+			return -1;
+		}
+	}
+	bi->supported_curves = curves;
+
+	wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x",
+		   bi->supported_curves);
+
+	return 0;
+}
+
+
 int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
 {
 	char *mac = NULL, *info = NULL, *curve = NULL;
-	char *key = NULL;
+	char *key = NULL, *supported_curves = NULL;
 	u8 *privkey = NULL;
 	size_t privkey_len = 0;
 	int ret = -1;
@@ -3933,6 +4260,7 @@
 	info = get_param(cmd, " info=");
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
+	supported_curves = get_param(cmd, " supported_curves=");
 
 	if (key) {
 		privkey_len = os_strlen(key) / 2;
@@ -3946,6 +4274,7 @@
 	    dpp_parse_uri_chan_list(bi, bi->chan) < 0 ||
 	    dpp_parse_uri_mac(bi, mac) < 0 ||
 	    dpp_parse_uri_info(bi, info) < 0 ||
+	    dpp_parse_supported_curves_list(bi, supported_curves) < 0 ||
 	    dpp_gen_uri(bi) < 0)
 		goto fail;
 
@@ -3958,6 +4287,7 @@
 	os_free(mac);
 	os_free(info);
 	str_clear_free(key);
+	os_free(supported_curves);
 	bin_clear_free(privkey, privkey_len);
 	dpp_bootstrap_info_free(bi);
 	return ret;
@@ -4012,12 +4342,43 @@
 {
 	struct dpp_bootstrap_info *bi;
 	char pkhash[2 * SHA256_MAC_LEN + 1];
+	char supp_curves[100];
 
 	bi = dpp_bootstrap_get_id(dpp, id);
 	if (!bi)
 		return -1;
 	wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash,
 			 SHA256_MAC_LEN);
+
+	supp_curves[0] = '\0';
+	if (bi->supported_curves) {
+		int ret;
+		size_t i;
+		char *pos = supp_curves;
+		char *end = &supp_curves[sizeof(supp_curves)];
+		const char *curve[6] = { "P-256", "P-384", "P-521",
+					 "BP-256", "BP-384", "BP-512" };
+
+		ret = os_snprintf(pos, end - pos, "supp_curves=");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+
+		for (i = 0; i < ARRAY_SIZE(curve); i++) {
+			if (!(bi->supported_curves & BIT(i)))
+				continue;
+			ret = os_snprintf(pos, end - pos, "%s:", curve[i]);
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+
+		if (pos[-1] == ':')
+			pos[-1] = '\n';
+		else
+			supp_curves[0] = '\0';
+	}
+
 	return os_snprintf(reply, reply_size, "type=%s\n"
 			   "mac_addr=" MACSTR "\n"
 			   "info=%s\n"
@@ -4025,7 +4386,7 @@
 			   "use_freq=%u\n"
 			   "curve=%s\n"
 			   "pkhash=%s\n"
-			   "version=%d\n",
+			   "version=%d\n%s",
 			   dpp_bootstrap_type_txt(bi->type),
 			   MAC2STR(bi->mac_addr),
 			   bi->info ? bi->info : "",
@@ -4033,7 +4394,8 @@
 			   bi->num_freq == 1 ? bi->freq[0] : 0,
 			   bi->curve->name,
 			   pkhash,
-			   bi->version);
+			   bi->version,
+			   supp_curves);
 }
 
 
@@ -4210,12 +4572,25 @@
 
 int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
 {
-	char *curve = NULL;
+	char *curve;
 	char *key = NULL, *ppkey = NULL;
 	u8 *privkey = NULL, *pp_key = NULL;
 	size_t privkey_len = 0, pp_key_len = 0;
 	int ret = -1;
 	struct dpp_configurator *conf = NULL;
+	const struct dpp_curve_params *net_access_key_curve = NULL;
+
+	curve = get_param(cmd, " net_access_key_curve=");
+	if (curve) {
+		net_access_key_curve = dpp_get_curve_name(curve);
+		if (!net_access_key_curve) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Unsupported net_access_key_curve: %s",
+				   curve);
+			goto fail;
+		}
+		os_free(curve);
+	}
 
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
@@ -4242,6 +4617,7 @@
 	if (!conf)
 		goto fail;
 
+	conf->net_access_key_curve = net_access_key_curve;
 	conf->id = dpp_next_configurator_id(dpp);
 	dl_list_add(&dpp->configurator, &conf->list);
 	ret = conf->id;
@@ -4257,6 +4633,32 @@
 }
 
 
+int dpp_configurator_set(struct dpp_global *dpp, const char *cmd)
+{
+	unsigned int id;
+	struct dpp_configurator *conf;
+	char *curve;
+
+	id = atoi(cmd);
+	conf = dpp_configurator_get_id(dpp, id);
+	if (!conf)
+		return -1;
+
+	curve = get_param(cmd, " net_access_key_curve=");
+	if (curve) {
+		const struct dpp_curve_params *net_access_key_curve;
+
+		net_access_key_curve = dpp_get_curve_name(curve);
+		os_free(curve);
+		if (!net_access_key_curve)
+			return -1;
+		conf->net_access_key_curve = net_access_key_curve;
+	}
+
+	return 0;
+}
+
+
 static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
 {
 	struct dpp_configurator *conf, *tmp;
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 2f85ebd..fba4119 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -110,6 +110,7 @@
 	DPP_STATUS_CONFIGURE_PENDING = 11,
 	DPP_STATUS_CSR_NEEDED = 12,
 	DPP_STATUS_CSR_BAD = 13,
+	DPP_STATUS_NEW_KEY_NEEDED = 14,
 };
 
 /* DPP Reconfig Flags object - connectorKey values */
@@ -145,6 +146,15 @@
 	DPP_BOOTSTRAP_NFC_URI,
 };
 
+enum dpp_bootstrap_supported_curves {
+	DPP_BOOTSTRAP_CURVE_P_256 = 0,
+	DPP_BOOTSTRAP_CURVE_P_384 = 1,
+	DPP_BOOTSTRAP_CURVE_P_521 = 2,
+	DPP_BOOTSTRAP_CURVE_BP_256 = 3,
+	DPP_BOOTSTRAP_CURVE_BP_384 = 4,
+	DPP_BOOTSTRAP_CURVE_BP_512 = 5,
+};
+
 struct dpp_bootstrap_info {
 	struct dl_list list;
 	unsigned int id;
@@ -158,6 +168,7 @@
 	unsigned int num_freq;
 	bool channels_listed;
 	u8 version;
+	u8 supported_curves; /* enum dpp_bootstrap_supported_curves bitmap */
 	int own;
 	struct crypto_ec_key *pubkey;
 	u8 pubkey_hash[SHA256_MAC_LEN];
@@ -172,6 +183,12 @@
 
 #define PKEX_COUNTER_T_LIMIT 5
 
+enum dpp_pkex_ver {
+	PKEX_VER_AUTO,
+	PKEX_VER_ONLY_1,
+	PKEX_VER_ONLY_2,
+};
+
 struct dpp_pkex {
 	void *msg_ctx;
 	unsigned int initiator:1;
@@ -253,6 +270,7 @@
 	void *msg_ctx;
 	u8 peer_version;
 	const struct dpp_curve_params *curve;
+	const struct dpp_curve_params *new_curve;
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_bootstrap_info *own_bi;
 	struct dpp_bootstrap_info *tmp_own_bi;
@@ -353,6 +371,8 @@
 	char *trusted_eap_server_name;
 	struct wpabuf *cacert;
 	struct wpabuf *certbag;
+	bool waiting_new_key;
+	bool new_key_received;
 	void *config_resp_ctx;
 	void *gas_server_ctx;
 	bool use_config_query;
@@ -378,6 +398,7 @@
 	u8 kid_hash[SHA256_MAC_LEN];
 	char *kid;
 	const struct dpp_curve_params *curve;
+	const struct dpp_curve_params *net_access_key_curve;
 	char *connector; /* own Connector for reconfiguration */
 	struct crypto_ec_key *connector_key;
 	struct crypto_ec_key *pp_key;
@@ -512,6 +533,8 @@
 	DPP_TEST_NO_PROTOCOL_VERSION_PEER_DISC_RESP = 93,
 	DPP_TEST_INVALID_PROTOCOL_VERSION_PEER_DISC_REQ = 94,
 	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,
 };
 
 extern enum dpp_test_behavior dpp_test;
@@ -681,6 +704,7 @@
 struct dpp_bootstrap_info * dpp_bootstrap_find_chirp(struct dpp_global *dpp,
 						     const u8 *hash);
 int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
+int dpp_configurator_set(struct dpp_global *dpp, const char *cmd);
 int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
 int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
 				char *buf, size_t buflen);
@@ -698,6 +722,8 @@
 			 size_t data_len);
 int dpp_controller_start(struct dpp_global *dpp,
 			 struct dpp_controller_config *config);
+int dpp_controller_set_params(struct dpp_global *dpp,
+			      const char *configurator_params);
 void dpp_controller_stop(struct dpp_global *dpp);
 void dpp_controller_stop_for_ctx(struct dpp_global *dpp, void *cb_ctx);
 struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 4fac7de..47f56c2 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -2355,6 +2355,97 @@
 #endif /* CONFIG_DPP2 */
 
 
+#ifdef CONFIG_DPP3
+int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i)
+{
+	int ret = -1, res;
+	u8 Sx[DPP_MAX_SHARED_SECRET_LEN];
+	size_t Sx_len;
+	unsigned int hash_len;
+	const char *info = "New DPP Protocol Key";
+	const u8 *addr[3];
+	size_t len[3];
+	u8 tmp[DPP_MAX_HASH_LEN], k[DPP_MAX_HASH_LEN];
+	struct wpabuf *pcx = NULL, *pex = NULL;
+
+	hash_len = auth->curve->hash_len;
+
+	/*
+	 * Configurator: S = pc * Pe
+	 * Enrollee: S = pe * Pc
+	 * k = HKDF(bk, "New DPP Protocol Key", S.x)
+	 *   = HKDF-Expand(HKDF-Extract(bk, S.X), "New DPP Protocol Key",
+	 *                 len(new-curve-hash-out))
+	 * Auth-I = HMAC(k, E-nonce | Pc.x | Pe.x)
+	 *
+	 * auth->own_protocol_key and auth->peer_protocol_key have already been
+	 * updated to use the new keys. The new curve determines the size of
+	 * the (new) protocol keys and S.x. The other parameters (bk, hash
+	 * algorithm, k) are determined based on the initially determined curve
+	 * during the (re)authentication exchange.
+	 */
+
+	if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+		     Sx, &Sx_len) < 0)
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "DPP: S.x", Sx, Sx_len);
+
+	/* tmp = HKDF-Extract(bk, S.x) */
+	addr[0] = Sx;
+	len[0] = Sx_len;
+	res = dpp_hmac_vector(hash_len, auth->bk, hash_len, 1, addr, len, tmp);
+	if (res < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: HKDF-Extract(bk, S.x)",
+			tmp, hash_len);
+	/* k = HKDF-Expand(tmp, "New DPP Protocol Key", len(hash-output))
+	 */
+	res = dpp_hkdf_expand(hash_len, tmp, hash_len, info, k, hash_len);
+	if (res < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: k = HKDF-Expand(\"New DPP Protocol Key\")",
+			k, hash_len);
+
+	/* Auth-I = HMAC(k, E-nonce | Pc.x | Pe.x) */
+	addr[0] = auth->e_nonce;
+	len[0] = auth->curve->nonce_len;
+
+	if (auth->configurator) {
+		pcx = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
+		pex = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+						     0);
+	} else {
+		pcx = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+						     0);
+		pex = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
+	}
+	if (!pcx || !pex)
+		goto fail;
+	addr[1] = wpabuf_head(pcx);
+	len[1] = wpabuf_len(pcx) / 2;
+	addr[2] = wpabuf_head(pex);
+	len[2] = wpabuf_len(pex) / 2;
+
+	if (dpp_hmac_vector(hash_len, k, hash_len, 3, addr, len, auth_i) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: Auth-I = HMAC(k, E-nonce | Pc.x | Pe.x)",
+			auth_i, hash_len);
+	ret = 0;
+fail:
+	forced_memzero(Sx, sizeof(Sx));
+	forced_memzero(tmp, sizeof(tmp));
+	forced_memzero(k, sizeof(k));
+	wpabuf_free(pcx);
+	wpabuf_free(pex);
+	return ret;
+}
+#endif /* CONFIG_DPP3 */
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 int dpp_test_gen_invalid_key(struct wpabuf *msg,
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index 0f31ae5..10db4e8 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -136,6 +136,7 @@
 struct crypto_ec_point * dpp_decrypt_e_id(struct crypto_ec_key *ppkey,
 					  struct crypto_ec_key *a_nonce,
 					  struct crypto_ec_key *e_prime_id);
+int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i);
 char * dpp_sign_connector(struct dpp_configurator *conf,
 			  const struct wpabuf *dppcon);
 int dpp_test_gen_invalid_key(struct wpabuf *msg,
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
index 7137bc5..452c502 100644
--- a/src/common/dpp_reconfig.c
+++ b/src/common/dpp_reconfig.c
@@ -131,6 +131,7 @@
 {
 	struct wpabuf *msg;
 	size_t attr_len;
+	u8 ver = DPP_VERSION;
 
 	/* Build DPP Reconfig Authentication Request frame attributes */
 	attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
@@ -144,10 +145,25 @@
 	wpabuf_put_le16(msg, 1);
 	wpabuf_put_u8(msg, auth->transaction_id);
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_PROTOCOL_VERSION_RECONFIG_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Protocol Version");
+		goto skip_proto_ver;
+	}
+	if (dpp_test == DPP_TEST_INVALID_PROTOCOL_VERSION_RECONFIG_AUTH_REQ) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Protocol Version");
+		ver = 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	/* Protocol Version */
 	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
 	wpabuf_put_le16(msg, 1);
-	wpabuf_put_u8(msg, DPP_VERSION);
+	wpabuf_put_u8(msg, ver);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_proto_ver:
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	/* DPP Connector */
 	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index e88c6de..c83fb2d 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -89,6 +89,9 @@
 static void dpp_controller_auth_success(struct dpp_connection *conn,
 					int initiator);
 static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_DPP3
+static void dpp_tcp_build_new_key(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_DPP3 */
 static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
 static void dpp_relay_conn_timeout(void *eloop_ctx, void *timeout_ctx);
 
@@ -107,6 +110,9 @@
 	eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
 	eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
 	eloop_cancel_timeout(dpp_relay_conn_timeout, conn, NULL);
+#ifdef CONFIG_DPP3
+	eloop_cancel_timeout(dpp_tcp_build_new_key, conn, NULL);
+#endif /* CONFIG_DPP3 */
 	wpabuf_free(conn->msg);
 	wpabuf_free(conn->msg_out);
 	dpp_auth_deinit(conn->auth);
@@ -193,6 +199,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP3
+	if (auth->waiting_new_key) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+		conn->on_tcp_tx_complete_gas_done = 0;
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	if (auth->peer_version >= 2 &&
 	    auth->conf_resp_status == DPP_STATUS_OK) {
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
@@ -999,7 +1013,7 @@
 		return 0;
 	}
 
-	conn->pkex = dpp_pkex_rx_exchange_req(conn->ctrl->global, ctrl->pkex_bi,
+	conn->pkex = dpp_pkex_rx_exchange_req(conn->msg_ctx, ctrl->pkex_bi,
 					      NULL, NULL,
 					      ctrl->pkex_identifier,
 					      ctrl->pkex_code,
@@ -1440,6 +1454,21 @@
 }
 
 
+#ifdef CONFIG_DPP3
+static void dpp_tcp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	struct dpp_authentication *auth = conn->auth;
+
+	if (!auth || !auth->waiting_new_key)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+	dpp_controller_start_gas_client(conn);
+}
+#endif /* CONFIG_DPP3 */
+
+
 static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
 {
 	struct dpp_authentication *auth = conn->auth;
@@ -1460,6 +1489,14 @@
 		eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
 		return 0;
 	}
+#ifdef CONFIG_DPP3
+	if (res == -3) {
+		wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+		eloop_register_timeout(0, 0, dpp_tcp_build_new_key, conn,
+				       NULL);
+		return 0;
+	}
+#endif /* CONFIG_DPP3 */
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		return -1;
@@ -2047,6 +2084,29 @@
 }
 
 
+int dpp_controller_set_params(struct dpp_global *dpp,
+			      const char *configurator_params)
+{
+
+	if (!dpp || !dpp->controller)
+		return -1;
+
+	if (configurator_params) {
+		char *val = os_strdup(configurator_params);
+
+		if (!val)
+			return -1;
+		os_free(dpp->controller->configurator_params);
+		dpp->controller->configurator_params = val;
+	} else {
+		os_free(dpp->controller->configurator_params);
+		dpp->controller->configurator_params = NULL;
+	}
+
+	return 0;
+}
+
+
 void dpp_controller_stop(struct dpp_global *dpp)
 {
 	if (dpp) {
@@ -2183,6 +2243,9 @@
 {
 	struct dpp_connection *conn;
 
+	if (!dpp)
+		return false;
+
 	dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
 		if (conn->auth && conn->auth->conn_status_requested)
 			return true;
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index f168d4e..732124f 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -383,10 +383,11 @@
 			    int freq, int channel, int enable_edmg,
 			    u8 edmg_channel, int ht_enabled,
 			    int vht_enabled, int he_enabled,
-			    int sec_channel_offset,
+			    bool eht_enabled, int sec_channel_offset,
 			    int oper_chwidth, int center_segment0,
 			    int center_segment1, u32 vht_caps,
-			    struct he_capabilities *he_cap)
+			    struct he_capabilities *he_cap,
+			    struct eht_capabilities *eht_cap)
 {
 	if (!he_cap || !he_cap->he_supported)
 		he_enabled = 0;
@@ -397,6 +398,7 @@
 	data->ht_enabled = ht_enabled;
 	data->vht_enabled = vht_enabled;
 	data->he_enabled = he_enabled;
+	data->eht_enabled = eht_enabled;
 	data->sec_channel_offset = sec_channel_offset;
 	data->center_freq1 = freq + sec_channel_offset * 10;
 	data->center_freq2 = 0;
@@ -415,9 +417,9 @@
 				 &data->edmg);
 
 	if (is_6ghz_freq(freq)) {
-		if (!data->he_enabled) {
+		if (!data->he_enabled && !data->eht_enabled) {
 			wpa_printf(MSG_ERROR,
-				   "Can't set 6 GHz mode - HE isn't enabled");
+				   "Can't set 6 GHz mode - HE or EHT aren't enabled");
 			return -1;
 		}
 
@@ -480,7 +482,20 @@
 		return 0;
 	}
 
-	if (data->he_enabled) switch (oper_chwidth) {
+#if 0 /* FIX: Figure out how to handle CHANWIDTH_320MHZ */
+	if (data->eht_enabled) switch (oper_chwidth) {
+	case CHANWIDTH_320MHZ:
+		if (!(eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+		      EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
+			wpa_printf(MSG_ERROR,
+				   "320 MHz channel width is not supported in 5 or 6 GHz");
+			return -1;
+		}
+		break;
+	}
+#endif
+
+	if (data->he_enabled || data->eht_enabled) switch (oper_chwidth) {
 	case CHANWIDTH_USE_HT:
 		if (sec_channel_offset == 0)
 			break;
@@ -543,7 +558,8 @@
 		break;
 	}
 
-	if (data->he_enabled || data->vht_enabled) switch (oper_chwidth) {
+	if (data->eht_enabled || data->he_enabled ||
+	    data->vht_enabled) switch (oper_chwidth) {
 	case CHANWIDTH_USE_HT:
 		if (center_segment1 ||
 		    (center_segment0 != 0 &&
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 0e92aa0..d87a2ca 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -40,10 +40,11 @@
 			    int freq, int channel, int edmg, u8 edmg_channel,
 			    int ht_enabled,
 			    int vht_enabled, int he_enabled,
-			    int sec_channel_offset,
+			    bool eht_enabled, int sec_channel_offset,
 			    int oper_chwidth, int center_segment0,
 			    int center_segment1, u32 vht_caps,
-			    struct he_capabilities *he_caps);
+			    struct he_capabilities *he_caps,
+			    struct eht_capabilities *eht_cap);
 void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
 		      int disabled);
 int ieee80211ac_cap_check(u32 hw, u32 conf);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index adc6f59..44335de 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -311,6 +311,14 @@
 		elems->pasn_params = pos;
 		elems->pasn_params_len = elen;
 		break;
+	case WLAN_EID_EXT_EHT_CAPABILITIES:
+		elems->eht_capabilities = pos;
+		elems->eht_capabilities_len = elen;
+		break;
+	case WLAN_EID_EXT_EHT_OPERATION:
+		elems->eht_operation = pos;
+		elems->eht_operation_len = elen;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
@@ -1902,7 +1910,7 @@
 	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 177, 8, BW40MINUS, P2P_SUPP },
 
 	/*
-	 * IEEE P802.11ax/D8.0 Table E-4 actually talks about channel center
+	 * 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
 	 * (these center frequencies are not actual channels, which makes
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ec6556f..e21f7be 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -117,6 +117,8 @@
 	const u8 *sae_pk;
 	const u8 *s1g_capab;
 	const u8 *pasn_params;
+	const u8 *eht_capabilities;
+	const u8 *eht_operation;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -171,6 +173,8 @@
 	u8 short_ssid_list_len;
 	u8 sae_pk_len;
 	u8 pasn_params_len;
+	u8 eht_capabilities_len;
+	u8 eht_operation_len;
 
 	struct mb_ies_info mb_ies;
 	struct frag_ies_info frag_ies;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 4300ae5..c341a1d 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -479,6 +479,7 @@
 #define WLAN_EID_EXT_HE_OPERATION 36
 #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
 #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_SHORT_SSID_LIST 58
 #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
@@ -489,6 +490,11 @@
 #define WLAN_EID_EXT_REJECTED_GROUPS 92
 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
 #define WLAN_EID_EXT_PASN_PARAMS 100
+#define WLAN_EID_EXT_EHT_OPERATION 106
+#define WLAN_EID_EXT_MULTI_LINK 107
+#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
 
 /* Extended Capabilities field */
 #define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -1953,6 +1959,26 @@
 	u8 link_margin;
 } STRUCT_PACKED;
 
+/*
+ * IEEE Std 802.11ax-2021, Table 9-275a - Maximum Transmit Power
+ * Interpretation subfield encoding
+ */
+enum max_tx_pwr_interpretation {
+	LOCAL_EIRP = 0,
+	LOCAL_EIRP_PSD = 1,
+	REGULATORY_CLIENT_EIRP = 2,
+	REGULATORY_CLIENT_EIRP_PSD = 3,
+};
+
+/*
+ * IEEE Std 802.11ax-2021, Table E-13 - Maximum Transmit Power
+ * Category subfield encoding in the United States
+ */
+enum reg_6g_client_type {
+	REG_DEFAULT_CLIENT = 0,
+	REG_SUBORDINATE_CLIENT = 1,
+};
+
 #define RRM_CAPABILITIES_IE_LEN 5
 
 /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
@@ -2173,6 +2199,7 @@
 #define NEI_REP_BSSID_INFO_VHT BIT(12)
 #define NEI_REP_BSSID_INFO_FTM BIT(13)
 #define NEI_REP_BSSID_INFO_HE BIT(14)
+#define NEI_REP_BSSID_INFO_EHT BIT(21)
 
 /*
  * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
@@ -2209,7 +2236,7 @@
 	 * Operation Information subfield (5 octets). */
 } STRUCT_PACKED;
 
-/* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */
+/* IEEE Std 802.11ax-2021, Figure 9-788k - 6 GHz Operation Information field */
 struct ieee80211_he_6ghz_oper_info {
 	u8 primary_chan;
 	u8 control;
@@ -2218,15 +2245,22 @@
 	u8 min_rate;
 } STRUCT_PACKED;
 
+/* IEEE Std 802.11ax-2021, Figure 9-788l - Control field format */
 #define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK	(BIT(0) | BIT(1))
 #define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON	BIT(2)
+#define HE_6GHZ_OPER_INFO_CTRL_REG_INFO_MASK	(BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT	3
 
-/* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */
+/* IEEE Std 802.11ax-2021, 9.4.2.263 HE 6 GHz Band Capabilities element */
 struct ieee80211_he_6ghz_band_cap {
 	 /* Minimum MPDU Start Spacing B0..B2
 	  * Maximum A-MPDU Length Exponent B3..B5
-	  * Maximum MPDU Length B6..B7 */
-	le16 capab;
+	  * Maximum MPDU Length B6..B7
+	  * SM Power Save B9..B10
+	  * RD Responder B11
+	  * Rx Antenna Pattern Consistency B12
+	  * Tx Antenna Consistency B13 */
+	le16 capab; /* Capabilities Information field */
 } STRUCT_PACKED;
 
 #define HE_6GHZ_BAND_CAP_MIN_MPDU_START              (BIT(0) | BIT(1) | BIT(2))
@@ -2252,7 +2286,7 @@
 #define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS              BIT(13)
 
 /*
- * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
+ * IEEE Std 802.11ax-2021, 9.4.2.252 Spatial Reuse Parameter Set element
  */
 struct ieee80211_spatial_reuse {
 	u8 sr_ctrl; /* SR Control */
@@ -2323,6 +2357,7 @@
 #define HE_OPERATION_BSS_COLOR_PARTIAL		((u32) BIT(30))
 #define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(31))
 #define HE_OPERATION_BSS_COLOR_OFFSET		24
+#define HE_OPERATION_BSS_COLOR_MAX		64
 
 /* HE operation fields length*/
 #define HE_OPERATION_IE_MIN_LEN 6
@@ -2330,6 +2365,17 @@
 #define HE_OPERATION_COHOSTED_BSSID_INDICATOR_LEN 1
 #define HE_OPERATION_6GHZ_OPER_INFO_LEN 5
 
+/**
+ * enum he_6ghz_ap_type - Allowed Access Point types for 6 GHz Band
+ *
+ * IEEE Std 802.11ax-2021, Table E-12 (Regulatory Info subfield encoding in the
+ * United States)
+ */
+enum he_6ghz_ap_type {
+	HE_6GHZ_INDOOR_AP		= 0,
+	HE_6GHZ_STANDARD_POWER_AP	= 1,
+};
+
 /* Spatial Reuse defines */
 #define SPATIAL_REUSE_SRP_DISALLOWED		BIT(0)
 #define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED	BIT(1)
@@ -2394,6 +2440,105 @@
 #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 */
+
+/* Figure 9-1002b: EHT Operation Parameters field subfields */
+#define EHT_OPER_INFO_PRESENT                          BIT(0)
+#define EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT       BIT(1)
+
+/* Control subfield: Channel Width subfield; see Table 9-401b */
+#define EHT_OPER_CHANNEL_WIDTH_20MHZ                   0
+#define EHT_OPER_CHANNEL_WIDTH_40MHZ                   1
+#define EHT_OPER_CHANNEL_WIDTH_80MHZ                   2
+#define EHT_OPER_CHANNEL_WIDTH_160MHZ                  3
+#define EHT_OPER_CHANNEL_WIDTH_320MHZ                  4
+
+/* Figure 9-1002c: EHT Operation Information field format */
+struct ieee80211_eht_oper_info {
+	u8 control; /* B0..B2: Channel Width */
+	u8 ccfs0;
+	u8 ccfs1;
+	le16 disabled_chan_bitmap; /* 0 or 2 octets */
+} STRUCT_PACKED;
+
+/* Figure 9-1002a: EHT Operation element format */
+struct ieee80211_eht_operation {
+	u8 oper_params; /* EHT Operation Parameters: EHT_OPER_* bits */
+	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 */
+
+/* Figure 9-1002af: EHT MAC Capabilities Information field */
+#define EHT_MACCAP_EPCS_PRIO			BIT(0)
+#define EHT_MACCAP_OM_CONTROL			BIT(1)
+#define EHT_MACCAP_TRIGGERED_TXOP_MODE1		BIT(2)
+#define EHT_MACCAP_TRIGGERED_TXOP_MODE2		BIT(3)
+#define EHT_MACCAP_RESTRICTED_TWT		BIT(4)
+#define EHT_MACCAP_SCS_TRAFFIC_DESC		BIT(5)
+#define EHT_MACCAP_MAX_MPDU_LEN_MASK		(BIT(6) | BIT(7))
+#define EHT_MACCAP_MAX_MPDU_LEN_3895		0
+#define EHT_MACCAP_MAX_MPDU_LEN_7991		BIT(6)
+#define EHT_MACCAP_MAX_MPDU_LEN_11454		BIT(7)
+#define EHT_MACCAP_MAX_AMPDU_LEN_EXP_EXT	BIT(8)
+
+/* Figure 9-1002ag: EHT PHY Capabilities Information field format
+ * _IDX indicates the octet index within the field */
+#define EHT_PHY_CAPAB_LEN			9
+
+#define EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX	0
+#define EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK	((u8) BIT(1))
+
+#define EHT_PHYCAP_SU_BEAMFORMER_IDX		0
+#define EHT_PHYCAP_SU_BEAMFORMER		((u8) BIT(5))
+#define EHT_PHYCAP_SU_BEAMFORMEE_IDX		0
+#define EHT_PHYCAP_SU_BEAMFORMEE		((u8) BIT(6))
+
+#define EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX	5
+#define EHT_PHYCAP_PPE_THRESHOLD_PRESENT	((u8) BIT(3))
+
+#define EHT_PHYCAP_MU_BEAMFORMER_IDX		7
+#define EHT_PHYCAP_MU_BEAMFORMER_80MHZ		((u8) BIT(4))
+#define EHT_PHYCAP_MU_BEAMFORMER_160MHZ		((u8) BIT(5))
+#define EHT_PHYCAP_MU_BEAMFORMER_320MHZ		((u8) BIT(6))
+#define EHT_PHYCAP_MU_BEAMFORMER_MASK	(EHT_PHYCAP_MU_BEAMFORMER_80MHZ | \
+					 EHT_PHYCAP_MU_BEAMFORMER_160MHZ | \
+					 EHT_PHYCAP_MU_BEAMFORMER_320MHZ)
+
+/* Figure 9-1002ah: Supported EHT-MCS and NSS Set field format */
+#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY	4
+#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS	3
+
+#define EHT_MCS_NSS_CAPAB_LEN			9
+/*
+ * Figure 9-1002ak: EHT PPE Thresholds field format
+ * Maximum PPE threshold length: 62 octets
+ * NSS: 4 bits (maximum NSS: 16), RU index: 5 bits, each pair: 6 bits
+ * 4 + 5 + 5 * 16 * 6 = 489 bits, Padding: 7 bits
+ */
+#define EHT_PPE_THRESH_CAPAB_LEN		62
+
+/* 9.4.2.313.5: EHT PPE Thresholds field */
+#define EHT_PPE_THRES_NSS_SHIFT			0
+#define EHT_PPE_THRES_NSS_MASK			((u8) (BIT(0) | BIT(1) | \
+						       BIT(2) | BIT(3)))
+#define EHT_PPE_THRES_RU_INDEX_SHIFT		4
+#define EHT_PPE_THRES_RU_INDEX_MASK		((u16) (BIT(4) | BIT(5) | \
+							BIT(6) | BIT(7) | \
+							BIT(8)))
+
+#define EHT_NSS_MAX_STREAMS			8
+
+/* Figure 9-1002ae: EHT Capabilities element format */
+struct ieee80211_eht_capabilities {
+	/* EHT MAC Capabilities Information */
+	le16 mac_cap;
+	/* EHT PHY Capabilities Information */
+	u8 phy_cap[EHT_PHY_CAPAB_LEN];
+	/* Supported EHT-MCS And NSS Set and EHT PPE thresholds (Optional) */
+	u8 optional[EHT_MCS_NSS_CAPAB_LEN + EHT_PPE_THRESH_CAPAB_LEN];
+} STRUCT_PACKED;
+
 /* 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
@@ -2494,10 +2639,16 @@
 #define FD_CAP_PHY_INDEX_SHIFT				10
 
 /*
- * IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning
+ * IEEE Std 802.11ax-2021, 26.17.2.3.2, AP behavior for fast passive scanning
  */
 #define FD_MAX_INTERVAL_6GHZ                  20 /* TUs */
 
+/* IEEE Std 802.11ax-2021, 26.17.3.5.1: AP needs to wait and see the collision
+ * persists for at least the minimum default timeout
+ * dot11BSSColorCollisionAPPeriod (50 seconds)
+ */
+#define DOT11BSS_COLOR_COLLISION_AP_PERIOD	50
+
 /* Protected Vendor-specific QoS Management Action frame identifiers - WFA */
 #define QM_ACTION_VENDOR_TYPE 0x506f9a1a
 #define QM_ACTION_OUI_TYPE 0x1a
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 0f7d3af..d04c8d1 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1599,6 +1599,18 @@
  *	synchronous (in vendor command reply) to the request. Each TWT
  *	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
+ *	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.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
@@ -1617,6 +1629,7 @@
 	QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R		= 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,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -2390,7 +2403,10 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_DISCONNECT_IES = 58,
 
 	/* 8-bit unsigned value for ELNA bypass.
-	 * 1-Enable, 0-Disable
+	 * 0 - Disable eLNA bypass.
+	 * 1 - Enable eLNA bypass.
+	 * 2 - Reset eLNA bypass configuration, the driver should
+	 *	revert to the default configuration of eLNA bypass.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS = 59,
 
@@ -5627,6 +5643,8 @@
 	 * current channel.
 	 */
 	QCA_WLAN_VENDOR_ACS_SELECT_REASON_JAMMER_INTERFERENCE,
+	/* Represents the reason that ACS triggered by AFC */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_AFC_TRIGGER,
 };
 
 /**
@@ -5834,6 +5852,21 @@
 	 */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
 
+	/*
+	 * 16-bit attribute of bits indicating the AP power modes supported by
+	 * the channel (u16).
+	 * Note: Currently, only 3 bits are used in the attribute and each bit
+	 * corresponds to the power mode mentioned in enum
+	 * qca_wlan_vendor_external_acs_chan_power_mode and a given bit is
+	 * set if the associated mode is supported.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_SUPP_POWER_MODES
+									= 14,
+	/* Array of nested attributes for each power mode. It takes attr as
+	 * defined in enum
+	 * qca_wlan_vendor_external_acs_event_chan_power_info_attr.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR	= 15,
 	/* keep last */
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
 	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
@@ -5841,6 +5874,56 @@
 };
 
 /**
+ * qca_wlan_vendor_external_acs_chan_power_mode - Specifies the valid
+ * values that the vendor external ACS channel power attribute
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_POWER_MODE can
+ * take.
+ * @QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_LOW_POWER: Low power/Indoor mode
+ * @QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_STANDARD_POWER: Standard power mode
+ * @QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_VERY_LOW_POWER: Very low power mode
+ */
+enum qca_wlan_vendor_external_acs_chan_power_level {
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_LOW_POWER = 0,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_STANDARD_POWER = 1,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_CHAN_VERY_LOW_POWER = 2,
+};
+
+/**
+ * qca_wlan_vendor_external_acs_event_chan_power_info_attr: Represents nested
+ * attributes for power mode type and power values corresponding to that.
+ * These attributes are sent as part of
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR.
+ */
+enum qca_wlan_vendor_external_acs_event_chan_power_info_attr {
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_INVALID = 0,
+	/*
+	 * Power mode (u8) takes the values defined in enum
+	 * qca_wlan_vendor_external_acs_chan_power_mode
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_POWER_MODE
+									= 1,
+	/*
+	 * Indicates if power value is a PSD/EIRP value (flag). If flag is
+	 * present, it indicates a PSD value.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_PSD_FLAG = 2,
+	/*
+	 * Power value (u32) PSD/EIRP as indicated by
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_PSD_FLAG,
+	 * for power mode corresponding to the
+	 * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_POWER_MODE.
+	 * Units for PSD - dBm/MHz
+	 * Units for EIRP - dBm
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_POWER_VALUE
+									= 3,
+	/* keep last */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_LAST,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_MAX =
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_POWER_INFO_ATTR_LAST - 1,
+};
+
+/**
  * qca_wlan_vendor_attr_pcl: Represents attributes for
  * preferred channel list (PCL). These attributes are sent as part of
  * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and
@@ -5864,6 +5947,10 @@
 	 * bit 3 set: channel should be excluded in GO negotiation
 	 */
 	QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4,
+
+	/* Keep last */
+	QCA_WLAN_VENDOR_ATTR_PCL_LAST,
+	QCA_WLAN_VENDOR_ATTR_PCL_MAX = QCA_WLAN_VENDOR_ATTR_PCL_LAST - 1
 };
 
 /**
@@ -5926,6 +6013,10 @@
 	 * qca_wlan_vendor_attr_rropavail_info.
 	 */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14,
+	/* Flag attribute to indicate if driver supports 6 GHz AFC trigger
+	 * for External ACS
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AFC_CAPABILITY = 15,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
@@ -6529,6 +6620,12 @@
 	 * for the current operating bandwidth.
 	 */
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BANDWIDTH = 30,
+	/* Spectral FFT recapture flag attribute, to enable FFT recapture.
+	 * Recapture can only be enabled for scan period greater than 52 us.
+	 * If this attribute is enabled, re-triggers will be enabled when AGC
+	 * gain changes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_RECAPTURE = 31,
 
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
@@ -7032,7 +7129,7 @@
  *	Use XR level to benefit XR (extended reality) application to achieve
  *	latency and power by via constraint scan/roaming/adaptive PS.
  * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
- *	Use low latency level to benifit application like concurrent
+ *	Use low latency level to benefit application like concurrent
  *	downloading or video streaming via constraint scan/adaptive PS.
  * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
  *	Use ultra low latency level to benefit for gaming/voice
@@ -7356,6 +7453,14 @@
 	 * 1:support 0:not support
 	 */
 	QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30,
+	/* As per Wi-Fi Aware Specification v3.2 Service Id is the first
+	 * 48 bits of the SHA-256 hash of the Service Name.
+	 * A lower-case representation of the Service Name shall be used to
+	 * calculate the Service ID.
+	 * Array of u8: length is 6 bytes
+	 * This attribute is used and optional for ndp indication.
+	 */
+	QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_ID = 31,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
@@ -7365,9 +7470,24 @@
 
 enum qca_wlan_ndp_sub_cmd {
 	QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0,
-	/* Command to create a NAN data path interface */
+	/* Command to create a NAN data path interface.
+	 * This command was initially designed to both create and start a NAN
+	 * data path interface. However, changes to Linux 5.12 no longer allow
+	 * interface creation via vendor commands. When the driver advertises
+	 * QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI
+	 * userspace must explicitly first create the interface using
+	 * NL80211_CMD_NEW_INTERFACE before subsequently invoking this command
+	 * to start the interface.
+	 */
 	QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1,
-	/* Command to delete a NAN data path interface */
+	/* Command to delete a NAN data path interface.
+	 * This command was initially designed to both stop and delete a NAN
+	 * data path interface. However, changes to Linux 5.12 no longer allow
+	 * interface deletion via vendor commands. When the driver advertises
+	 * QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI
+	 * userspace must explicitly delete the interface using
+	 * NL80211_CMD_DEL_INTERFACE after calling this command.
+	 */
 	QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2,
 	/* Command to initiate a NAN data path session */
 	QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3,
@@ -8515,6 +8635,18 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BCAST_TWT_SUPPORT = 57,
 
+	/* 8-bit unsigned value to configure the driver/firmware to allow eMLSR
+	 * mode for IEEE 802.11be MLO capable devices. If the attribute is set
+	 * to 1, and if the firmware supports this capability too, the STA
+	 * advertises this capability to the AP over Association Request frame.
+	 * This attribute will not have any effect on legacy devices with no
+	 * IEEE 802.11be support.
+	 * 0 - Default behavior
+	 * 1 - Enable eMLSR (Enhanced Multi-link Single-Radio) mode
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_11BE_EMLSR_MODE = 58,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -8585,12 +8717,17 @@
  * peer. Refers the enum qca_wlan_vendor_attr_twt_capability. It's a synchronous
  * operation.
  *
- * @QCA_WLAN_TWT_SETUP_READY_NOTIFY: Notify userspace that the firmare is
+ * @QCA_WLAN_TWT_SETUP_READY_NOTIFY: Notify userspace that the firmware is
  * ready for a new TWT session setup after it issued a TWT teardown.
  *
  * @QCA_WLAN_TWT_SET_PARAM: Configure TWT related parameters. Required
  * parameters are obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refer
  * the enum qca_wlan_vendor_attr_twt_set_param.
+ *
+ * @QCA_WLAN_TWT_NOTIFY: Used to notify userspace about changes in TWT
+ * related information for example TWT required bit in AP capabilities etc.
+ * The reason for the notification is sent using
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS.
  */
 enum qca_wlan_twt_operation {
 	QCA_WLAN_TWT_SET = 0,
@@ -8604,6 +8741,7 @@
 	QCA_WLAN_TWT_GET_CAPABILITIES = 8,
 	QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9,
 	QCA_WLAN_TWT_SET_PARAM = 10,
+	QCA_WLAN_TWT_NOTIFY = 11,
 };
 
 /**
@@ -8620,11 +8758,17 @@
  * enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume,
  * enum qca_wlan_vendor_attr_twt_set_param, or
  * enum qca_wlan_vendor_attr_twt_stats based on the operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS: Size is u8, mandatory when
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION is set to QCA_WLAN_TWT_NOTIFY.
+ * The values used by this attribute are defined in
+ * enum qca_wlan_vendor_twt_status.
  */
 enum qca_wlan_vendor_attr_config_twt {
 	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_NOTIFY_STATUS = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST,
@@ -9100,6 +9244,10 @@
  * QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE: The driver requested to
  * terminate an existing TWT session on power save exit request from userspace.
  * Used on the TWT_TERMINATE notification from the driver/firmware.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_REQUIRED: The peer has set the TWT
+ * required bit in its capabilities.
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED: The peer has cleared
+ * the TWT required bit(1->0) in its capabilities.
  */
 enum qca_wlan_vendor_twt_status {
 	QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
@@ -9125,6 +9273,8 @@
 	QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS = 20,
 	QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS = 21,
 	QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE = 22,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_REQUIRED = 23,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_REQUIRED = 24,
 };
 
 /**
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index b78db05..27336c9 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1599,6 +1599,13 @@
 				   "%s: invalid group cipher 0x%x (%08x)",
 				   __func__, data->group_cipher,
 				   WPA_GET_BE32(pos));
+#ifdef CONFIG_NO_TKIP
+			if (RSN_SELECTOR_GET(pos) == RSN_CIPHER_SUITE_TKIP) {
+				wpa_printf(MSG_DEBUG,
+					   "%s: TKIP as group cipher not supported in CONFIG_NO_TKIP=y build",
+					   __func__);
+			}
+#endif /* CONFIG_NO_TKIP */
 			return -1;
 		}
 		pos += RSN_SELECTOR_LEN;
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index c1ce68c..779b2cf 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -487,7 +487,7 @@
 		     void (*msg_cb)(char *msg, size_t len))
 {
 	struct timeval tv;
-	struct os_reltime started_at;
+	struct os_reltime started_at, ending_at;
 	int res;
 	fd_set rfds;
 	const char *_cmd;
@@ -543,9 +543,19 @@
 	}
 	os_free(cmd_buf);
 
+	os_get_reltime(&ending_at);
+	ending_at.sec += 10;
+
 	for (;;) {
-		tv.tv_sec = 10;
-		tv.tv_usec = 0;
+		struct os_reltime diff;
+
+		os_get_reltime(&started_at);
+		if (os_reltime_before(&ending_at, &started_at))
+			return -2;
+		os_reltime_sub(&ending_at, &started_at, &diff);
+		tv.tv_sec = diff.sec;
+		tv.tv_usec = diff.usec;
+
 		FD_ZERO(&rfds);
 		FD_SET(ctrl->s, &rfds);
 		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 3d3a62a..055bf73 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -363,6 +363,9 @@
 #define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
 #define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
 
+/* BSS Transition Management Query frame received */
+#define BSS_TM_QUERY "BSS-TM-QUERY "
+
 /* BSS Transition Management Response frame received */
 #define BSS_TM_RESP "BSS-TM-RESP "
 
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index e6150b0..e4f3eb3 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1275,4 +1275,46 @@
 				struct crypto_ec_key *key,
 				enum crypto_hash_alg algo);
 
+struct crypto_rsa_key;
+
+/**
+ * crypto_rsa_key_read - Read an RSA key
+ * @file: File from which to read (PEM encoded, can be X.509v3 certificate)
+ * @private_key: Whether to read the private key instead of public key
+ * Returns: RSA key or %NULL on failure
+ */
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key);
+
+/**
+ * crypto_rsa_oaep_sha256_encrypt - RSA-OAEP-SHA-256 encryption
+ * @key: RSA key from crypto_rsa_key_read()
+ * @in: Plaintext input data
+ * Returns: Encrypted output data or %NULL on failure
+ */
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in);
+
+/**
+ * crypto_rsa_oaep_sha256_decrypt - RSA-OAEP-SHA-256 decryption
+ * @key: RSA key from crypto_rsa_key_read()
+ * @in: Encrypted input data
+ * Returns: Decrypted output data or %NULL on failure
+ */
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in);
+
+/**
+ * crypto_rsa_key_free - Free an RSA key
+ * @key: RSA key from crypto_rsa_key_read()
+ */
+void crypto_rsa_key_free(struct crypto_rsa_key *key);
+
+/**
+ * crypto_unload - Unload crypto resources
+ *
+ * This function is called just before the process exits to allow dynamic
+ * resource allocations to be freed.
+ */
+void crypto_unload(void);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 4ef1146..a7a163f 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -504,3 +504,8 @@
 	gcry_cipher_close(ctx->dec);
 	os_free(ctx);
 }
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index aad40af..d15c363 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -326,3 +326,8 @@
 void crypto_global_deinit(void)
 {
 }
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index ed30efa..fd79c1a 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -766,3 +766,8 @@
 }
 
 #endif /* CONFIG_MODEXP */
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
index 1724456..9278e27 100644
--- a/src/crypto/crypto_linux.c
+++ b/src/crypto/crypto_linux.c
@@ -1007,3 +1007,8 @@
 void crypto_global_deinit(void)
 {
 }
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
index f85d365..d745027 100644
--- a/src/crypto/crypto_nettle.c
+++ b/src/crypto/crypto_nettle.c
@@ -467,3 +467,8 @@
 {
 	bin_clear_free(ctx, sizeof(*ctx));
 }
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
index 5479194..a0dc0f5 100644
--- a/src/crypto/crypto_none.c
+++ b/src/crypto/crypto_none.c
@@ -22,3 +22,8 @@
 {
 	return 0;
 }
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 82c8576..c6e065f 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,16 +16,17 @@
 #include <openssl/dh.h>
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
-#ifdef CONFIG_OPENSSL_CMAC
-#include <openssl/cmac.h>
-#endif /* CONFIG_OPENSSL_CMAC */
+#include <openssl/pem.h>
 #ifdef CONFIG_ECC
 #include <openssl/ec.h>
 #include <openssl/x509.h>
-#include <openssl/pem.h>
 #endif /* CONFIG_ECC */
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
 #include <openssl/provider.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#else /* OpenSSL version >= 3.0 */
+#include <openssl/cmac.h>
 #endif /* OpenSSL version >= 3.0 */
 
 #include "common.h"
@@ -40,9 +41,7 @@
 #include "aes_wrap.h"
 #include "crypto.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 /* Compatibility wrappers for older versions. */
 
 static HMAC_CTX * HMAC_CTX_new(void)
@@ -121,30 +120,89 @@
 #endif /* OpenSSL version < 1.1.0 */
 
 
+#if OPENSSL_VERSION_NUMBER < 0x10101000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x30400000L)
+
+static int EC_POINT_get_affine_coordinates(const EC_GROUP *group,
+					   const EC_POINT *point, BIGNUM *x,
+					   BIGNUM *y, BN_CTX *ctx)
+{
+	return EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx);
+}
+
+
+static int EC_POINT_set_affine_coordinates(const EC_GROUP *group,
+					   EC_POINT *point, const BIGNUM *x,
+					   const BIGNUM *y, BN_CTX *ctx)
+{
+	return EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx);
+}
+
+#endif /* OpenSSL version < 1.1.1 */
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10101000L || \
+	defined(OPENSSL_IS_BORINGSSL) || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x30400000L)
+
+static int EC_POINT_set_compressed_coordinates(const EC_GROUP *group,
+					       EC_POINT *point, const BIGNUM *x,
+					       int y_bit, BN_CTX *ctx)
+{
+	return EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit,
+						       ctx);
+}
+
+
+static int EC_GROUP_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
+			      BIGNUM *b, BN_CTX *ctx)
+{
+	return EC_GROUP_get_curve_GFp(group, p, a, b, ctx);
+}
+
+#endif /* OpenSSL version < 1.1.1 */
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static OSSL_PROVIDER *openssl_default_provider = NULL;
+static OSSL_PROVIDER *openssl_legacy_provider = NULL;
+#endif /* OpenSSL version >= 3.0 */
+
 void openssl_load_legacy_provider(void)
 {
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
-	static bool loaded = false;
-	OSSL_PROVIDER *legacy;
-
-	if (loaded)
+	if (openssl_legacy_provider)
 		return;
 
-	legacy = OSSL_PROVIDER_load(NULL, "legacy");
+	openssl_legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
+	if (openssl_legacy_provider && !openssl_default_provider)
+		openssl_default_provider = OSSL_PROVIDER_load(NULL, "default");
+#endif /* OpenSSL version >= 3.0 */
+}
 
-	if (legacy) {
-		OSSL_PROVIDER_load(NULL, "default");
-		loaded = true;
+
+static void openssl_unload_legacy_provider(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (openssl_legacy_provider) {
+		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 */
 }
 
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+
 static BIGNUM * get_group5_prime(void)
 {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
-	!(defined(LIBRESSL_VERSION_NUMBER) && \
-	  LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
 	return BN_get_rfc3526_prime_1536(NULL);
 #elif !defined(OPENSSL_IS_BORINGSSL)
 	return get_rfc3526_prime_1536(NULL);
@@ -195,6 +253,8 @@
 	return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
 }
 
+#endif /* OpenSSL version < 3.0 */
+
 
 #ifdef OPENSSL_NO_SHA256
 #define NO_SHA256_WRAPPER
@@ -403,7 +463,7 @@
 	if (ctx == NULL)
 		return NULL;
 	if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
-		os_free(ctx);
+		EVP_CIPHER_CTX_free(ctx);
 		return NULL;
 	}
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
@@ -501,8 +561,52 @@
 #ifndef CONFIG_FIPS
 #ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static const EVP_CIPHER * aes_get_evp_wrap_cipher(size_t keylen)
+{
+	switch (keylen) {
+	case 16:
+		return EVP_aes_128_wrap();
+	case 24:
+		return EVP_aes_192_wrap();
+	case 32:
+		return EVP_aes_256_wrap();
+	default:
+		return NULL;
+	}
+}
+#endif /* OpenSSL version >= 3.0 */
+
+
 int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+	int ret = -1, len;
+	u8 buf[16];
+
+	if (TEST_FAIL())
+		return -1;
+
+	type = aes_get_evp_wrap_cipher(kek_len);
+	if (!type)
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+
+	if (EVP_EncryptInit_ex(ctx, type, NULL, kek, NULL) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_EncryptUpdate(ctx, cipher, &len, plain, n * 8) == 1 &&
+	    len == (n + 1) * 8 &&
+	    EVP_EncryptFinal_ex(ctx, buf, &len) == 1)
+		ret = 0;
+
+	EVP_CIPHER_CTX_free(ctx);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
 	AES_KEY actx;
 	int res;
 
@@ -513,12 +617,40 @@
 	res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
 	OPENSSL_cleanse(&actx, sizeof(actx));
 	return res <= 0 ? -1 : 0;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
 	       u8 *plain)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_CIPHER_CTX *ctx;
+	const EVP_CIPHER *type;
+	int ret = -1, len;
+	u8 buf[16];
+
+	if (TEST_FAIL())
+		return -1;
+
+	type = aes_get_evp_wrap_cipher(kek_len);
+	if (!type)
+		return -1;
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
+
+	if (EVP_DecryptInit_ex(ctx, type, NULL, kek, NULL) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_DecryptUpdate(ctx, plain, &len, cipher, (n + 1) * 8) == 1 &&
+	    len == n * 8 &&
+	    EVP_DecryptFinal_ex(ctx, buf, &len) == 1)
+		ret = 0;
+
+	EVP_CIPHER_CTX_free(ctx);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
 	AES_KEY actx;
 	int res;
 
@@ -529,6 +661,7 @@
 	res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
 	OPENSSL_cleanse(&actx, sizeof(actx));
 	return res <= 0 ? -1 : 0;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 #endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
@@ -819,9 +952,7 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	size_t publen, privlen;
@@ -870,6 +1001,57 @@
 	wpabuf_clear_free(privkey);
 	DH_free(dh);
 	return NULL;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_PARAM params[2];
+	size_t pub_len = OSSL_PARAM_UNMODIFIED;
+	size_t priv_len;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	BIGNUM *priv_bn = NULL;
+	EVP_PKEY_CTX *gctx;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+						     "modp_1536", 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	gctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+	if (!gctx ||
+	    EVP_PKEY_keygen_init(gctx) != 1 ||
+	    EVP_PKEY_CTX_set_params(gctx, params) != 1 ||
+	    EVP_PKEY_generate(gctx, &pkey) != 1 ||
+	    EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+				  &priv_bn) != 1 ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    NULL, 0, &pub_len) < 0 ||
+	    pub_len == OSSL_PARAM_UNMODIFIED ||
+	    (priv_len = BN_num_bytes(priv_bn)) == 0 ||
+	    !(pubkey = wpabuf_alloc(pub_len)) ||
+	    !(privkey = wpabuf_alloc(priv_len)) ||
+	    EVP_PKEY_get_octet_string_param(pkey,
+					    OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+					    wpabuf_put(pubkey, pub_len),
+					    pub_len, NULL) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(pubkey);
+		wpabuf_clear_free(privkey);
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+	} else {
+		BN_bn2bin(priv_bn, wpabuf_put(privkey, priv_len));
+
+		*priv = privkey;
+		*publ = pubkey;
+	}
+
+	BN_clear_free(priv_bn);
+	EVP_PKEY_CTX_free(gctx);
+	return pkey;
 #else
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
@@ -929,9 +1111,7 @@
 
 void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	DH *dh;
 
 	dh = DH_new();
@@ -962,6 +1142,39 @@
 err:
 	DH_free(dh);
 	return NULL;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = NULL;
+	OSSL_PARAM_BLD *bld;
+	OSSL_PARAM *params = NULL;
+	BIGNUM *priv_key, *pub_key;
+	EVP_PKEY_CTX *fctx;
+
+	fctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+	priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	bld = OSSL_PARAM_BLD_new();
+	if (!fctx || !priv_key || !pub_key || !bld ||
+	    OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
+					    "modp_1536", 0) != 1 ||
+	    OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY,
+				   priv_key) != 1 ||
+	    OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY,
+				   pub_key) != 1 ||
+	    !(params = OSSL_PARAM_BLD_to_param(bld)) ||
+	    EVP_PKEY_fromdata_init(fctx) != 1 ||
+	    EVP_PKEY_fromdata(fctx, &pkey, EVP_PKEY_KEYPAIR, params) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_fromdata failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+	}
+
+	BN_clear_free(priv_key);
+	BN_free(pub_key);
+	EVP_PKEY_CTX_free(fctx);
+	OSSL_PARAM_BLD_free(bld);
+	OSSL_PARAM_free(params);
+	return pkey;
 #else
 	DH *dh;
 	BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL;
@@ -1004,6 +1217,36 @@
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = ctx;
+	EVP_PKEY *peer_pub;
+	size_t len;
+	struct wpabuf *res = NULL;
+	EVP_PKEY_CTX *dctx = NULL;
+
+	peer_pub = EVP_PKEY_new();
+	if (!pkey || !peer_pub ||
+	    EVP_PKEY_copy_parameters(peer_pub, pkey) != 1 ||
+	    EVP_PKEY_set1_encoded_public_key(peer_pub, wpabuf_head(peer_public),
+					     wpabuf_len(peer_public)) != 1 ||
+	    !(dctx = EVP_PKEY_CTX_new(pkey, NULL)) ||
+	    EVP_PKEY_derive_init(dctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(dctx, peer_pub) != 1 ||
+	    EVP_PKEY_derive(dctx, NULL, &len) != 1 ||
+	    !(res = wpabuf_alloc(len)) ||
+	    EVP_PKEY_derive(dctx, wpabuf_mhead(res), &len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(res);
+		res = NULL;
+	} else {
+		wpabuf_put(res, len);
+	}
+
+	EVP_PKEY_free(peer_pub);
+	EVP_PKEY_CTX_free(dctx);
+	return res;
+#else /* OpenSSL version >= 3.0 */
 	BIGNUM *pub_key;
 	struct wpabuf *res = NULL;
 	size_t rlen;
@@ -1035,27 +1278,93 @@
 	BN_clear_free(pub_key);
 	wpabuf_clear_free(res);
 	return NULL;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 void dh5_free(void *ctx)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *pkey = ctx;
+
+	EVP_PKEY_free(pkey);
+#else /* OpenSSL version >= 3.0 */
 	DH *dh;
 	if (ctx == NULL)
 		return;
 	dh = ctx;
 	DH_free(dh);
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct crypto_hash {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_CTX *ctx;
+#else /* OpenSSL version >= 3.0 */
 	HMAC_CTX *ctx;
+#endif /* OpenSSL version >= 3.0 */
 };
 
 
 struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
 				      size_t key_len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_hash *ctx;
+	EVP_MAC *mac;
+	OSSL_PARAM params[2];
+	char *a = NULL;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		a = "MD5";
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		a = "SHA1";
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		a = "SHA256";
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!mac)
+		return NULL;
+
+	params[0] = OSSL_PARAM_construct_utf8_string("digest", a, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+	ctx->ctx = EVP_MAC_CTX_new(mac);
+	if (!ctx->ctx) {
+		EVP_MAC_free(mac);
+		os_free(ctx);
+		return NULL;
+	}
+
+	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;
+	}
+
+	EVP_MAC_free(mac);
+	return ctx;
+#else /* OpenSSL version >= 3.0 */
 	struct crypto_hash *ctx;
 	const EVP_MD *md;
 
@@ -1097,6 +1406,7 @@
 	}
 
 	return ctx;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -1104,12 +1414,49 @@
 {
 	if (ctx == NULL)
 		return;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_update(ctx->ctx, data, len);
+#else /* OpenSSL version >= 3.0 */
 	HMAC_Update(ctx->ctx, data, len);
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	size_t mdlen;
+	int res;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len) {
+		EVP_MAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return 0;
+	}
+
+	res = EVP_MAC_final(ctx->ctx, NULL, &mdlen, 0);
+	if (res != 1) {
+		EVP_MAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return -1;
+	}
+	res = EVP_MAC_final(ctx->ctx, mac, &mdlen, mdlen);
+	EVP_MAC_CTX_free(ctx->ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+#else /* OpenSSL version >= 3.0 */
 	unsigned int mdlen;
 	int res;
 
@@ -1136,9 +1483,148 @@
 	}
 
 	return -1;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+static int openssl_hmac_vector(char *digest, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	EVP_MAC *hmac;
+	OSSL_PARAM params[2];
+	EVP_MAC_CTX *ctx;
+	size_t i, mlen;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	if (!hmac)
+		return -1;
+
+	params[0] = OSSL_PARAM_construct_utf8_string("digest", digest, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	ctx = EVP_MAC_CTX_new(hmac);
+	EVP_MAC_free(hmac);
+	if (!ctx)
+		return -1;
+
+	if (EVP_MAC_init(ctx, key, key_len, params) != 1)
+		goto fail;
+
+	for (i = 0; i < num_elem; i++) {
+		if (EVP_MAC_update(ctx, addr[i], len[i]) != 1)
+			goto fail;
+	}
+
+	res = EVP_MAC_final(ctx, mac, &mlen, mdlen);
+	EVP_MAC_CTX_free(ctx);
+
+	return res == 1 ? 0 : -1;
+fail:
+	EVP_MAC_CTX_free(ctx);
+	return -1;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("MD5", key ,key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA1", key, key_len, num_elem, addr,
+				   len, mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA256", key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA384", key, key_len, num_elem, addr,
+				   len, mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector("SHA512", key, key_len, num_elem, addr,
+				   len, mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+#else /* OpenSSL version >= 3.0 */
+
 static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
 			       size_t key_len, size_t num_elem,
 			       const u8 *addr[], const size_t *len, u8 *mac,
@@ -1188,16 +1674,6 @@
 #endif /* CONFIG_FIPS */
 
 
-int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-		int iterations, u8 *buf, size_t buflen)
-{
-	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
-				   ssid_len, iterations, buflen, buf) != 1)
-		return -1;
-	return 0;
-}
-
-
 int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
 		     const u8 *addr[], const size_t *len, u8 *mac)
 {
@@ -1269,6 +1745,18 @@
 
 #endif /* CONFIG_SHA512 */
 
+#endif /* OpenSSL version >= 3.0 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+				   ssid_len, iterations, buflen, buf) != 1)
+		return -1;
+	return 0;
+}
+
 
 int crypto_get_random(void *buf, size_t len)
 {
@@ -1278,10 +1766,49 @@
 }
 
 
-#ifdef CONFIG_OPENSSL_CMAC
 int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
 		     const u8 *addr[], const size_t *len, u8 *mac)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_CTX *ctx = NULL;
+	EVP_MAC *emac;
+	int ret = -1;
+	size_t outlen, i;
+	OSSL_PARAM params[2];
+	char *cipher = NULL;
+
+	if (TEST_FAIL())
+		return -1;
+
+	emac = EVP_MAC_fetch(NULL, "CMAC", NULL);
+
+	if (key_len == 32)
+		cipher = "aes-256-cbc";
+	else if (key_len == 24)
+		cipher = "aes-192-cbc";
+	else if (key_len == 16)
+		cipher = "aes-128-cbc";
+
+	params[0] = OSSL_PARAM_construct_utf8_string("cipher", cipher, 0);
+	params[1] = OSSL_PARAM_construct_end();
+
+	if (!emac || !cipher ||
+	    !(ctx = EVP_MAC_CTX_new(emac)) ||
+	    EVP_MAC_init(ctx, key, key_len, params) != 1)
+		goto fail;
+
+	for (i = 0; i < num_elem; i++) {
+		if (!EVP_MAC_update(ctx, addr[i], len[i]))
+			goto fail;
+	}
+	if (EVP_MAC_final(ctx, mac, &outlen, 16) != 1 || outlen != 16)
+		goto fail;
+
+	ret = 0;
+fail:
+	EVP_MAC_CTX_free(ctx);
+	return ret;
+#else /* OpenSSL version >= 3.0 */
 	CMAC_CTX *ctx;
 	int ret = -1;
 	size_t outlen, i;
@@ -1296,6 +1823,9 @@
 	if (key_len == 32) {
 		if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
 			goto fail;
+	} else if (key_len == 24) {
+		if (!CMAC_Init(ctx, key, 24, EVP_aes_192_cbc(), NULL))
+			goto fail;
 	} else if (key_len == 16) {
 		if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
 			goto fail;
@@ -1313,6 +1843,7 @@
 fail:
 	CMAC_CTX_free(ctx);
 	return ret;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -1333,7 +1864,6 @@
 {
 	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
 }
-#endif /* CONFIG_OPENSSL_CMAC */
 
 
 struct crypto_bignum * crypto_bignum_init(void)
@@ -1752,7 +2282,7 @@
 	e->b = BN_new();
 	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
 	    e->order == NULL || e->a == NULL || e->b == NULL ||
-	    !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
+	    !EC_GROUP_get_curve(e->group, e->prime, e->a, e->b, e->bnctx) ||
 	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
 		crypto_ec_deinit(e);
 		e = NULL;
@@ -1847,10 +2377,10 @@
 int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
 		      struct crypto_bignum *x)
 {
-	return EC_POINT_get_affine_coordinates_GFp(e->group,
-						   (const EC_POINT *) p,
-						   (BIGNUM *) x, NULL,
-						   e->bnctx) == 1 ? 0 : -1;
+	return EC_POINT_get_affine_coordinates(e->group,
+					       (const EC_POINT *) p,
+					       (BIGNUM *) x, NULL,
+					       e->bnctx) == 1 ? 0 : -1;
 }
 
 
@@ -1868,8 +2398,8 @@
 	y_bn = BN_new();
 
 	if (x_bn && y_bn &&
-	    EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
-						x_bn, y_bn, e->bnctx)) {
+	    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);
@@ -1907,8 +2437,7 @@
 		return NULL;
 	}
 
-	if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
-						 e->bnctx)) {
+	if (!EC_POINT_set_affine_coordinates(e->group, elem, x, y, e->bnctx)) {
 		EC_POINT_clear_free(elem);
 		elem = NULL;
 	}
@@ -2009,8 +2538,8 @@
 	x = BN_new();
 	y = BN_new();
 	if (!x || !y ||
-	    EC_POINT_get_affine_coordinates_GFp(e->group, (const EC_POINT *) p,
-						x, y, e->bnctx) != 1)
+	    EC_POINT_get_affine_coordinates(e->group, (const EC_POINT *) p,
+					    x, y, e->bnctx) != 1)
 		goto fail;
 
 	x_str = BN_bn2hex(x);
@@ -2035,6 +2564,33 @@
 
 struct crypto_ecdh * crypto_ecdh_init(int group)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_ecdh *ecdh;
+	const char *name;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	name = OSSL_EC_curve_nid2name(ecdh->ec->nid);
+	if (!name)
+		goto fail;
+
+	ecdh->pkey = EVP_EC_gen(name);
+	if (!ecdh->pkey)
+		goto fail;
+
+done:
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+#else /* OpenSSL version >= 3.0 */
 	struct crypto_ecdh *ecdh;
 	EVP_PKEY *params = NULL;
 	EC_KEY *ec_params = NULL;
@@ -2089,11 +2645,32 @@
 	crypto_ecdh_deinit(ecdh);
 	ecdh = NULL;
 	goto done;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct crypto_ecdh * crypto_ecdh_init2(int group, struct crypto_ec_key *own_key)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct crypto_ecdh *ecdh;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	ecdh->pkey = EVP_PKEY_dup((EVP_PKEY *) own_key);
+	if (!ecdh->pkey)
+		goto fail;
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	return NULL;
+#else /* OpenSSL version >= 3.0 */
 	struct crypto_ecdh *ecdh;
 
 	ecdh = os_zalloc(sizeof(*ecdh));
@@ -2115,11 +2692,34 @@
 fail:
 	crypto_ecdh_deinit(ecdh);
 	return NULL;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	struct wpabuf *buf = NULL;
+	unsigned char *pub;
+	size_t len, exp_len;
+
+	len = EVP_PKEY_get1_encoded_public_key(ecdh->pkey, &pub);
+	if (len == 0)
+		return NULL;
+
+	/* Encoded using SECG SEC 1, Sec. 2.3.4 format */
+	exp_len = 1 + 2 * crypto_ec_prime_len(ecdh->ec);
+	if (len != exp_len) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL:%s: Unexpected encoded public key length %zu (expected %zu)",
+			   __func__, len, exp_len);
+		goto fail;
+	}
+	buf = wpabuf_alloc_copy(pub + 1, inc_y ? len - 1 : len / 2);
+fail:
+	OPENSSL_free(pub);
+	return buf;
+#else /* OpenSSL version >= 3.0 */
 	struct wpabuf *buf = NULL;
 	EC_KEY *eckey;
 	const EC_POINT *pubkey;
@@ -2145,10 +2745,10 @@
 	if (!x || !buf)
 		goto fail;
 
-	if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
-						x, y, ecdh->ec->bnctx) != 1) {
+	if (EC_POINT_get_affine_coordinates(ecdh->ec->group, pubkey,
+					    x, y, ecdh->ec->bnctx) != 1) {
 		wpa_printf(MSG_ERROR,
-			   "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
+			   "OpenSSL: EC_POINT_get_affine_coordinates failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
 	}
@@ -2175,12 +2775,57 @@
 	wpabuf_free(buf);
 	buf = NULL;
 	goto done;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
 struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
 					const u8 *key, size_t len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_PKEY *peerkey = EVP_PKEY_new();
+	EVP_PKEY_CTX *ctx;
+	size_t res_len;
+	struct wpabuf *res = NULL;
+	u8 *peer;
+
+	/* Encode using SECG SEC 1, Sec. 2.3.4 format */
+	peer = os_malloc(1 + len);
+	if (!peer)
+		return NULL;
+	peer[0] = inc_y ? 0x04 : 0x02;
+	os_memcpy(peer + 1, key, len);
+
+	if (!peerkey ||
+	    EVP_PKEY_copy_parameters(peerkey, ecdh->pkey) != 1 ||
+	    EVP_PKEY_set1_encoded_public_key(peerkey, peer, 1 + len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_set1_encoded_public_key failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(peerkey);
+		os_free(peer);
+		return NULL;
+	}
+	os_free(peer);
+
+	ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
+	if (!ctx ||
+	    EVP_PKEY_derive_init(ctx) != 1 ||
+	    EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
+	    EVP_PKEY_derive(ctx, NULL, &res_len) != 1 ||
+	    !(res = wpabuf_alloc(res_len)) ||
+	    EVP_PKEY_derive(ctx, wpabuf_mhead(res), &res_len) != 1) {
+		wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		wpabuf_free(res);
+		res = NULL;
+	} else {
+		wpabuf_put(res, res_len);
+	}
+
+	EVP_PKEY_free(peerkey);
+	EVP_PKEY_CTX_free(ctx);
+	return res;
+#else /* OpenSSL version >= 3.0 */
 	BIGNUM *x, *y = NULL;
 	EVP_PKEY_CTX *ctx = NULL;
 	EVP_PKEY *peerkey = NULL;
@@ -2198,19 +2843,18 @@
 		y = BN_bin2bn(key + len / 2, len / 2, NULL);
 		if (!y)
 			goto fail;
-		if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
-							 x, y,
-							 ecdh->ec->bnctx)) {
+		if (!EC_POINT_set_affine_coordinates(ecdh->ec->group, pub,
+						     x, y, ecdh->ec->bnctx)) {
 			wpa_printf(MSG_ERROR,
-				   "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+				   "OpenSSL: EC_POINT_set_affine_coordinates failed: %s",
 				   ERR_error_string(ERR_get_error(), NULL));
 			goto fail;
 		}
-	} else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
-							    pub, x, 0,
-							    ecdh->ec->bnctx)) {
+	} else if (!EC_POINT_set_compressed_coordinates(ecdh->ec->group,
+							pub, x, 0,
+							ecdh->ec->bnctx)) {
 		wpa_printf(MSG_ERROR,
-			   "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
+			   "OpenSSL: EC_POINT_set_compressed_coordinates failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
 	}
@@ -2270,6 +2914,7 @@
 	wpabuf_free(secret);
 	secret = NULL;
 	goto done;
+#endif /* OpenSSL version >= 3.0 */
 }
 
 
@@ -2370,9 +3015,9 @@
 	if (!x || !y || !point)
 		goto fail;
 
-	if (!EC_POINT_set_affine_coordinates_GFp(ec_group, point, x, y, ctx)) {
+	if (!EC_POINT_set_affine_coordinates(ec_group, point, x, y, ctx)) {
 		wpa_printf(MSG_ERROR,
-			   "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+			   "OpenSSL: EC_POINT_set_affine_coordinates failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
 		goto fail;
 	}
@@ -2777,12 +3422,53 @@
 }
 
 
-struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
-				       const u8 *data, size_t len)
+static int openssl_evp_pkey_ec_prime_len(struct crypto_ec_key *key)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char gname[50];
+	int nid;
+	EC_GROUP *group;
+	BIGNUM *prime = NULL;
+	int prime_len = -1;
+
+	if (EVP_PKEY_get_group_name((EVP_PKEY *) key, gname, sizeof(gname),
+				    NULL) != 1)
+		return -1;
+	nid = OBJ_txt2nid(gname);
+	group = EC_GROUP_new_by_curve_name(nid);
+	prime = BN_new();
+	if (!group || !prime)
+		return -1;
+	if (EC_GROUP_get_curve(group, prime, NULL, NULL, NULL) == 1)
+		prime_len = BN_num_bytes(prime);
+	EC_GROUP_free(group);
+	BN_free(prime);
+	return prime_len;
+#else
 	const EC_GROUP *group;
 	const EC_KEY *eckey;
 	BIGNUM *prime = NULL;
+	int prime_len = -1;
+
+	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	prime = BN_new();
+	if (!prime || !group ||
+	    !EC_GROUP_get_curve(group, prime, NULL, NULL, NULL))
+		goto fail;
+	prime_len = BN_num_bytes(prime);
+fail:
+	BN_free(prime);
+	return prime_len;
+#endif
+}
+
+
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+				       const u8 *data, size_t len)
+{
 	ECDSA_SIG *sig = NULL;
 	const BIGNUM *r, *s;
 	u8 *r_buf, *s_buf;
@@ -2790,20 +3476,15 @@
 	const unsigned char *p;
 	int prime_len;
 
+	prime_len = openssl_evp_pkey_ec_prime_len(key);
+	if (prime_len < 0)
+		return NULL;
+
 	buf = crypto_ec_key_sign(key, data, len);
 	if (!buf)
 		return NULL;
 
 	/* Extract (r,s) from Ecdsa-Sig-Value */
-	eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
-	if (!eckey)
-		goto fail;
-	group = EC_KEY_get0_group(eckey);
-	prime = BN_new();
-	if (!prime || !group ||
-	    !EC_GROUP_get_curve_GFp(group, prime, NULL, NULL, NULL))
-		goto fail;
-	prime_len = BN_num_bytes(prime);
 
 	p = wpabuf_head(buf);
 	sig = d2i_ECDSA_SIG(NULL, &p, wpabuf_len(buf));
@@ -2822,7 +3503,6 @@
 		goto fail;
 
 out:
-	BN_free(prime);
 	ECDSA_SIG_free(sig);
 	return buf;
 fail:
@@ -2893,6 +3573,15 @@
 
 int crypto_ec_key_group(struct crypto_ec_key *key)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	char gname[50];
+	int nid;
+
+	if (EVP_PKEY_get_group_name((EVP_PKEY *) key, gname, sizeof(gname),
+				    NULL) != 1)
+		return -1;
+	nid = OBJ_txt2nid(gname);
+#else
 	const EC_KEY *eckey;
 	const EC_GROUP *group;
 	int nid;
@@ -2904,6 +3593,7 @@
 	if (!group)
 		return -1;
 	nid = EC_GROUP_get_curve_name(group);
+#endif
 	switch (nid) {
 	case NID_X9_62_prime256v1:
 		return 19;
@@ -2932,8 +3622,13 @@
 
 int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	if (EVP_PKEY_eq((EVP_PKEY *) key1, (EVP_PKEY *) key2) != 1)
+		return -1;
+#else
 	if (EVP_PKEY_cmp((EVP_PKEY *) key1, (EVP_PKEY *) key2) != 1)
 		return -1;
+#endif
 	return 0;
 }
 
@@ -3242,3 +3937,138 @@
 }
 
 #endif /* CONFIG_ECC */
+
+
+static EVP_PKEY * crypto_rsa_key_read_public(FILE *f)
+{
+	EVP_PKEY *pkey;
+	X509 *x509;
+
+	pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+	if (pkey)
+		return pkey;
+
+	rewind(f);
+	x509 = PEM_read_X509(f, NULL, NULL, NULL);
+	if (!x509)
+		return NULL;
+
+	pkey = X509_get_pubkey(x509);
+	X509_free(x509);
+
+	if (!pkey)
+		return NULL;
+	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
+		EVP_PKEY_free(pkey);
+		return NULL;
+	}
+
+	return pkey;
+}
+
+
+struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key)
+{
+	FILE *f;
+	EVP_PKEY *pkey;
+
+	f = fopen(file, "r");
+	if (!f)
+		return NULL;
+	if (private_key)
+		pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+	else
+		pkey = crypto_rsa_key_read_public(f);
+	fclose(f);
+	return (struct crypto_rsa_key *) pkey;
+}
+
+
+#ifndef OPENSSL_NO_SHA256
+
+struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *res = NULL;
+	size_t outlen;
+
+	pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!pkctx)
+		goto fail;
+
+	if (EVP_PKEY_encrypt_init(pkctx) != 1 ||
+	    EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
+	    EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, EVP_sha256()) <= 0 ||
+	    EVP_PKEY_encrypt(pkctx, NULL, &outlen, wpabuf_head(in),
+			     wpabuf_len(in)) != 1 ||
+	    !(res = wpabuf_alloc(outlen)) ||
+	    EVP_PKEY_encrypt(pkctx, wpabuf_put(res, 0), &outlen,
+			     wpabuf_head(in), wpabuf_len(in)) != 1) {
+		wpabuf_free(res);
+		res = NULL;
+		goto fail;
+	}
+	wpabuf_put(res, outlen);
+
+fail:
+	EVP_PKEY_CTX_free(pkctx);
+	return res;
+#else
+	wpa_printf(MSG_ERROR, "%s() not supported", __func__);
+	return NULL;
+#endif
+}
+
+
+struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key,
+					       const struct wpabuf *in)
+{
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+	EVP_PKEY *pkey = (EVP_PKEY *) key;
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *res = NULL;
+	size_t outlen;
+
+	pkctx = EVP_PKEY_CTX_new(pkey, NULL);
+	if (!pkctx)
+		goto fail;
+
+	if (EVP_PKEY_decrypt_init(pkctx) != 1 ||
+	    EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
+	    EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, EVP_sha256()) <= 0 ||
+	    EVP_PKEY_decrypt(pkctx, NULL, &outlen, wpabuf_head(in),
+			     wpabuf_len(in)) != 1 ||
+	    !(res = wpabuf_alloc(outlen)) ||
+	    EVP_PKEY_decrypt(pkctx, wpabuf_put(res, 0), &outlen,
+			     wpabuf_head(in), wpabuf_len(in)) != 1) {
+		wpabuf_free(res);
+		res = NULL;
+		goto fail;
+	}
+	wpabuf_put(res, outlen);
+
+fail:
+	EVP_PKEY_CTX_free(pkctx);
+	return res;
+#else
+	wpa_printf(MSG_ERROR, "%s() not supported", __func__);
+	return NULL;
+#endif
+}
+
+#endif /* OPENSSL_NO_SHA256 */
+
+
+void crypto_rsa_key_free(struct crypto_rsa_key *key)
+{
+	EVP_PKEY_free((EVP_PKEY *) key);
+}
+
+
+void crypto_unload(void)
+{
+	openssl_unload_legacy_provider();
+}
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index 00ecf61..f47beeb 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -26,6 +26,8 @@
 #include <wolfssl/wolfcrypt/dh.h>
 #include <wolfssl/wolfcrypt/cmac.h>
 #include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/wolfcrypt/asn_public.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
 #include <wolfssl/openssl/bn.h>
 
 
@@ -85,6 +87,7 @@
 		wc_ShaUpdate(&sha, addr[i], len[i]);
 
 	wc_ShaFinal(&sha, mac);
+	wc_ShaFree(&sha);
 
 	return 0;
 }
@@ -106,6 +109,7 @@
 		wc_Sha256Update(&sha256, addr[i], len[i]);
 
 	wc_Sha256Final(&sha256, mac);
+	wc_Sha256Free(&sha256);
 
 	return 0;
 }
@@ -128,6 +132,7 @@
 		wc_Sha384Update(&sha384, addr[i], len[i]);
 
 	wc_Sha384Final(&sha384, mac);
+	wc_Sha384Free(&sha384);
 
 	return 0;
 }
@@ -150,6 +155,7 @@
 		wc_Sha512Update(&sha512, addr[i], len[i]);
 
 	wc_Sha512Final(&sha512, mac);
+	wc_Sha512Free(&sha512);
 
 	return 0;
 }
@@ -169,13 +175,16 @@
 	if (TEST_FAIL())
 		return -1;
 
-	if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
+	if (wc_HmacInit(&hmac, NULL, INVALID_DEVID) != 0 ||
+	    wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
 		return -1;
 	for (i = 0; i < num_elem; i++)
 		if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
 			return -1;
 	if (wc_HmacFinal(&hmac, mac) != 0)
 		return -1;
+	wc_HmacFree(&hmac);
+
 	return 0;
 }
 
@@ -274,9 +283,18 @@
 int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
 		int iterations, u8 *buf, size_t buflen)
 {
-	if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
-		      ssid_len, iterations, buflen, WC_SHA) != 0)
+	int ret;
+
+	ret = wc_PBKDF2(buf, (const byte *) passphrase, os_strlen(passphrase),
+			ssid, ssid_len, iterations, buflen, WC_SHA);
+	if (ret != 0) {
+		if (ret == HMAC_MIN_KEYLEN_E) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: Password is too short. Make sure your password is at least %d characters long. This is a requirement for FIPS builds.",
+				   HMAC_FIPS_MIN_KEY);
+		}
 		return -1;
+	}
 	return 0;
 }
 
@@ -409,8 +427,11 @@
 }
 
 
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
 int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
 {
+#ifdef HAVE_AES_KEYWRAP
 	int ret;
 
 	if (TEST_FAIL())
@@ -419,12 +440,16 @@
 	ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
 			    NULL);
 	return ret != (n + 1) * 8 ? -1 : 0;
+#else /* HAVE_AES_KEYWRAP */
+	return -1;
+#endif /* HAVE_AES_KEYWRAP */
 }
 
 
 int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
 	       u8 *plain)
 {
+#ifdef HAVE_AES_KEYWRAP
 	int ret;
 
 	if (TEST_FAIL())
@@ -433,7 +458,12 @@
 	ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
 			      NULL);
 	return ret != n * 8 ? -1 : 0;
+#else /* HAVE_AES_KEYWRAP */
+	return -1;
+#endif /* HAVE_AES_KEYWRAP */
 }
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
 
 
 #ifndef CONFIG_NO_RC4
@@ -670,6 +700,7 @@
 	    != 0)
 		goto done;
 
+	priv_sz = pub_sz = RFC3526_LEN;
 	if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
 				 wpabuf_mhead(pubkey), &pub_sz) != 0)
 		goto done;
@@ -803,6 +834,7 @@
 	if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
 		goto done;
 
+	priv_sz = pub_sz = prime_len;
 	if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
 	    != 0)
 		goto done;
@@ -919,7 +951,8 @@
 		goto done;
 	}
 
-	if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
+	if (wc_HmacInit(&hash->hmac, NULL, INVALID_DEVID) != 0 ||
+	    wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
 		goto done;
 
 	ret = hash;
@@ -1634,35 +1667,21 @@
 crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
 			      const struct crypto_bignum *x)
 {
-	mp_int *y2 = NULL;
-	mp_int t;
-	int calced = 0;
+	mp_int *y2;
 
 	if (TEST_FAIL())
 		return NULL;
 
-	if (mp_init(&t) != MP_OKAY)
-		return NULL;
-
+	/* y^2 = x^3 + ax + b = (x^2 + a)x + b */
 	y2 = (mp_int *) crypto_bignum_init();
-	if (!y2)
-		goto done;
-
-	if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+	if (!y2 ||
+	    mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+	    mp_addmod(y2, &e->a, &e->prime, y2) != 0 ||
 	    mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
-	    mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
-	    mp_addmod(y2, &t, &e->prime, y2) != 0 ||
-	    mp_addmod(y2, &e->b, &e->prime, y2) != 0)
-		goto done;
-
-	calced = 1;
-done:
-	if (!calced) {
-		if (y2) {
-			mp_clear(y2);
-			os_free(y2);
-		}
-		mp_clear(&t);
+	    mp_addmod(y2, &e->b, &e->prime, y2) != 0) {
+		mp_clear(y2);
+		os_free(y2);
+		y2 = NULL;
 	}
 
 	return (struct crypto_bignum *) y2;
@@ -1694,33 +1713,37 @@
 
 struct crypto_ecdh {
 	struct crypto_ec *ec;
+	WC_RNG rng;
 };
 
 struct crypto_ecdh * crypto_ecdh_init(int group)
 {
 	struct crypto_ecdh *ecdh = NULL;
-	WC_RNG rng;
 	int ret;
 
-	if (wc_InitRng(&rng) != 0)
-		goto fail;
-
 	ecdh = os_zalloc(sizeof(*ecdh));
 	if (!ecdh)
 		goto fail;
 
+	if (wc_InitRng(&ecdh->rng) != 0)
+		goto fail;
+
 	ecdh->ec = crypto_ec_init(group);
 	if (!ecdh->ec)
 		goto fail;
 
-	ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
-				 ecdh->ec->key.dp->id);
+	ret = wc_ecc_make_key_ex(&ecdh->rng, ecdh->ec->key.dp->size,
+				 &ecdh->ec->key, ecdh->ec->key.dp->id);
 	if (ret < 0)
 		goto fail;
 
-done:
-	wc_FreeRng(&rng);
+#if defined(ECC_TIMING_RESISTANT) && !defined(CONFIG_FIPS)
+	ret = wc_ecc_set_rng(&ecdh->ec->key, &ecdh->rng);
+	if (ret < 0)
+		goto fail;
+#endif /* ECC_TIMING_RESISTANT && !CONFIG_FIPS */
 
+done:
 	return ecdh;
 fail:
 	crypto_ecdh_deinit(ecdh);
@@ -1733,6 +1756,7 @@
 {
 	if (ecdh) {
 		crypto_ec_deinit(ecdh->ec);
+		wc_FreeRng(&ecdh->rng);
 		os_free(ecdh);
 	}
 }
@@ -1822,4 +1846,266 @@
 	return crypto_ec_prime_len(ecdh->ec);
 }
 
+
+struct crypto_ec_key {
+	ecc_key *eckey;
+	WC_RNG *rng; /* Needs to be initialized before use.
+		      * *NOT* initialized in crypto_ec_key_init */
+};
+
+
+static struct crypto_ec_key * crypto_ec_key_init(void)
+{
+	struct crypto_ec_key *key;
+
+	key = os_zalloc(sizeof(struct crypto_ec_key));
+	if (key) {
+#ifdef CONFIG_FIPS
+		key->eckey = os_zalloc(sizeof(ecc_key));
+#else /* CONFIG_FIPS */
+		key->eckey = wc_ecc_key_new(NULL);
+#endif /* CONFIG_FIPS */
+		/* Omit key->rng initialization because it seeds itself and thus
+		 * consumes entropy that may never be used. Lazy initialize when
+		 * necessary. */
+		if (!key->eckey) {
+			wpa_printf(MSG_ERROR,
+				   "wolfSSL: crypto_ec_key_init() failed");
+			crypto_ec_key_deinit(key);
+			key = NULL;
+		}
+#ifdef CONFIG_FIPS
+		else if (wc_ecc_init_ex(key->eckey, NULL, INVALID_DEVID) != 0) {
+			wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_init_ex failed");
+			crypto_ec_key_deinit(key);
+			key = NULL;
+		}
+#endif /* CONFIG_FIPS */
+	}
+	return key;
+}
+
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+	if (key) {
+#ifdef CONFIG_FIPS
+		os_free(key->rng);
+		os_free(key->eckey);
+#else /* CONFIG_FIPS */
+		wc_rng_free(key->rng);
+		wc_ecc_key_free(key->eckey);
+#endif /* CONFIG_FIPS */
+		os_free(key);
+	}
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+	struct crypto_ec_key *ret;
+	word32 idx = 0;
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		wpa_printf(MSG_ERROR, "wolfSSL: crypto_ec_key_init failed");
+		goto fail;
+	}
+
+	if (wc_EccPrivateKeyDecode(der, &idx, ret->eckey, (word32) der_len) !=
+	    0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPrivateKeyDecode failed");
+		goto fail;
+	}
+
+	return ret;
+fail:
+	if (ret)
+		crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+
+	if (!key || !key->eckey || !key->eckey->dp) {
+		wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
+			   __func__);
+		return -1;
+	}
+
+	switch (key->eckey->dp->id) {
+	case ECC_SECP256R1:
+		return 19;
+	case ECC_SECP384R1:
+		return 20;
+	case ECC_SECP521R1:
+		return 21;
+	case ECC_BRAINPOOLP256R1:
+		return 28;
+	case ECC_BRAINPOOLP384R1:
+		return 29;
+	case ECC_BRAINPOOLP512R1:
+		return 30;
+	}
+
+	wpa_printf(MSG_ERROR, "wolfSSL: Unsupported curve (id=%d) in EC key",
+		   key->eckey->dp->id);
+	return -1;
+}
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+	byte *der = NULL;
+	int der_len;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !key->eckey) {
+		wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
+			   __func__);
+		goto fail;
+	}
+
+	der_len = wc_EccPublicKeyDerSize(key->eckey, 1);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyDerSize failed");
+		goto fail;
+	}
+
+	der = os_malloc(der_len);
+	if (!der)
+		goto fail;
+
+	der_len = wc_EccPublicKeyToDer(key->eckey, der, der_len, 1);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyToDer failed");
+		goto fail;
+	}
+
+	ret = wpabuf_alloc_copy(der, der_len);
+	os_free(der);
+	return ret;
+
+fail:
+	os_free(der);
+	return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+	word32 idx = 0;
+	struct crypto_ec_key *ret = NULL;
+
+	ret = crypto_ec_key_init();
+	if (!ret) {
+		wpa_printf(MSG_ERROR, "wolfSSL: crypto_ec_key_init failed");
+		goto fail;
+	}
+
+	if (wc_EccPublicKeyDecode(der, &idx, ret->eckey, (word32) der_len) != 0)
+	{
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_EccPublicKeyDecode failed");
+		goto fail;
+	}
+
+	return ret;
+fail:
+	crypto_ec_key_deinit(ret);
+	return NULL;
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len)
+{
+	byte *der = NULL;
+	int der_len;
+	word32 w32_der_len;
+	struct wpabuf *ret = NULL;
+
+	if (!key || !key->eckey || !data || len == 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
+			   __func__);
+		goto fail;
+	}
+
+	if (!key->rng) {
+		/* Lazy init key->rng */
+#ifdef CONFIG_FIPS
+		key->rng = os_zalloc(sizeof(WC_RNG));
+#else /* CONFIG_FIPS */
+		key->rng = wc_rng_new(NULL, 0, NULL);
+#endif /* CONFIG_FIPS */
+		if (!key->rng) {
+			wpa_printf(MSG_ERROR, "wolfSSL: wc_rng_new failed");
+			goto fail;
+		}
+#ifdef CONFIG_FIPS
+		if (wc_InitRng(key->rng) != 0) {
+			wpa_printf(MSG_ERROR, "wolfSSL: wc_InitRng failed");
+			goto fail;
+		}
+#endif /* CONFIG_FIPS */
+	}
+
+	der_len = wc_ecc_sig_size(key->eckey);
+	if (der_len <= 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_sig_size failed");
+		goto fail;
+	}
+
+	der = os_malloc(der_len);
+	if (!der)
+		goto fail;
+
+	w32_der_len = (word32) der_len;
+	if (wc_ecc_sign_hash(data, len, der, &w32_der_len, key->rng, key->eckey)
+	    != 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_sign_hash failed");
+		goto fail;
+	}
+
+	ret = wpabuf_alloc_copy(der, der_len);
+	os_free(der);
+	if (!ret)
+		wpa_printf(MSG_ERROR, "wolfSSL: wpabuf_alloc_copy failed");
+	return ret;
+fail:
+	os_free(der);
+	return NULL;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len)
+{
+	int res = 0;
+
+	if (!key || !key->eckey || !data || len == 0 || !sig || sig_len == 0) {
+		wpa_printf(MSG_ERROR, "wolfSSL: %s: invalid input parameters",
+			   __func__);
+		return -1;
+	}
+
+	if (wc_ecc_verify_hash(sig, sig_len, data, len, &res, key->eckey) != 0)
+	{
+		wpa_printf(MSG_ERROR, "wolfSSL: wc_ecc_verify_hash failed");
+		return -1;
+	}
+
+	if (res != 1)
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: crypto_ec_key_verify_signature failed");
+
+	return res;
+}
+
 #endif /* CONFIG_ECC */
+
+
+void crypto_unload(void)
+{
+}
diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c
index 8effe2f..d2bdc95 100644
--- a/src/crypto/sha1-pbkdf2.c
+++ b/src/crypto/sha1-pbkdf2.c
@@ -50,6 +50,8 @@
 		for (j = 0; j < SHA1_MAC_LEN; j++)
 			digest[j] ^= tmp2[j];
 	}
+	forced_memzero(tmp, SHA1_MAC_LEN);
+	forced_memzero(tmp2, SHA1_MAC_LEN);
 
 	return 0;
 }
@@ -87,6 +89,7 @@
 		pos += plen;
 		left -= plen;
 	}
+	forced_memzero(digest, SHA1_MAC_LEN);
 
 	return 0;
 }
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 09fb73b..7a2ee32 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -22,7 +22,8 @@
 	TLS_CERT_CHAIN_SUCCESS,
 	TLS_CERT_CHAIN_FAILURE,
 	TLS_PEER_CERTIFICATE,
-	TLS_ALERT
+	TLS_ALERT,
+	TLS_UNSAFE_RENEGOTIATION_DISABLED,
 };
 
 /*
@@ -112,6 +113,7 @@
 #define TLS_CONN_ENABLE_TLSv1_1 BIT(15)
 #define TLS_CONN_ENABLE_TLSv1_2 BIT(16)
 #define TLS_CONN_TEAP_ANON_DH BIT(17)
+#define TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION BIT(18)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -148,8 +150,6 @@
  * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
  * passphrase is used.
  * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
- * @dh_blob: dh_file as inlined data or %NULL if not used
- * @dh_blob_len: dh_blob length
  * @engine: 1 = use engine (e.g., a smartcard) for private key operations
  * (this is OpenSSL specific for now)
  * @engine_id: engine id string (this is OpenSSL specific for now)
@@ -198,8 +198,6 @@
 	const char *private_key_passwd;
 	const char *private_key_passwd2;
 	const char *dh_file;
-	const u8 *dh_blob;
-	size_t dh_blob_len;
 
 	/* OpenSSL specific variables */
 	int engine;
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index daa01d9..e3f5b5a 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -1766,6 +1766,7 @@
 void tls_connection_set_success_data(struct tls_connection *conn,
 				     struct wpabuf *data)
 {
+	wpabuf_free(data);
 }
 
 
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 8095b43..f3e05ce 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -281,13 +281,6 @@
 		return -1;
 	}
 
-	if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
-			       params->dh_blob_len)) {
-		wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
-		tlsv1_cred_free(cred);
-		return -1;
-	}
-
 	if (tlsv1_client_set_cred(conn->client, cred) < 0) {
 		tlsv1_cred_free(cred);
 		return -1;
@@ -342,8 +335,7 @@
 		return -1;
 	}
 
-	if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
-			       params->dh_blob_len)) {
+	if (tlsv1_set_dhparams(cred, params->dh_file, NULL, 0)) {
 		wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
 		return -1;
 	}
@@ -791,6 +783,7 @@
 void tls_connection_set_success_data(struct tls_connection *conn,
 				     struct wpabuf *data)
 {
+	wpabuf_free(data);
 }
 
 
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 6d6fb0c..87f45f8 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -212,6 +212,7 @@
 void tls_connection_set_success_data(struct tls_connection *conn,
 				     struct wpabuf *data)
 {
+	wpabuf_free(data);
 }
 
 
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index ba3ef80..a1b5166 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -7,6 +7,9 @@
  */
 
 #include "includes.h"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <fcntl.h>
+#endif /* CONFIG_TESTING_OPTIONS */
 
 #ifndef CONFIG_SMARTCARD
 #ifndef OPENSSL_NO_ENGINE
@@ -24,14 +27,21 @@
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif /* OPENSSL_NO_ENGINE */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#include <openssl/decoder.h>
+#include <openssl/param_build.h>
+#else /* OpenSSL version >= 3.0 */
 #ifndef OPENSSL_NO_DSA
 #include <openssl/dsa.h>
 #endif
 #ifndef OPENSSL_NO_DH
 #include <openssl/dh.h>
 #endif
+#endif /* OpenSSL version >= 3.0 */
 
 #include "common.h"
+#include "utils/list.h"
 #include "crypto.h"
 #include "sha1.h"
 #include "sha256.h"
@@ -65,9 +75,7 @@
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* SSL_set_tlsext_status_type */
 
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
-     (defined(LIBRESSL_VERSION_NUMBER) && \
-      LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
+#if OPENSSL_VERSION_NUMBER < 0x10100000L && \
     !defined(BORINGSSL_API_VERSION)
 /*
  * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
@@ -111,17 +119,7 @@
 
 #endif
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#ifdef CONFIG_SUITEB
-static int RSA_bits(const RSA *r)
-{
-	return BN_num_bits(r->n);
-}
-#endif /* CONFIG_SUITEB */
-
-
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
 {
 	return ASN1_STRING_data((ASN1_STRING *) x);
@@ -234,12 +232,18 @@
 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;
@@ -307,6 +311,7 @@
 	struct tls_context *context = os_zalloc(sizeof(*context));
 	if (context == NULL)
 		return NULL;
+	dl_list_init(&context->sessions);
 	if (conf) {
 		context->event_cb = conf->event_cb;
 		context->cb_ctx = conf->cb_ctx;
@@ -958,21 +963,53 @@
 #endif /* OPENSSL_NO_ENGINE */
 
 
+static struct tls_session_data * get_session_data(struct tls_context *context,
+						  const struct wpabuf *buf)
+{
+	struct tls_session_data *data;
+
+	dl_list_for_each(data, &context->sessions, struct tls_session_data,
+			 list) {
+		if (data->buf == buf)
+			return data;
+	}
+
+	return NULL;
+}
+
+
 static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
 {
 	struct wpabuf *buf;
+	struct tls_context *context;
+	struct tls_session_data *found;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Remove session %p (tls_ex_idx_session=%d)", sess,
+		   tls_ex_idx_session);
 
 	if (tls_ex_idx_session < 0)
 		return;
 	buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
 	if (!buf)
 		return;
+
+	context = SSL_CTX_get_app_data(ctx);
+	SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+	found = get_session_data(context, buf);
+	if (!found) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Do not free application session data %p (sess %p)",
+			   buf, sess);
+		return;
+	}
+
+	dl_list_del(&found->list);
+	os_free(found);
 	wpa_printf(MSG_DEBUG,
 		   "OpenSSL: Free application session data %p (sess %p)",
 		   buf, sess);
 	wpabuf_free(buf);
-
-	SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
 }
 
 
@@ -1019,9 +1056,7 @@
 		}
 #endif /* OPENSSL_FIPS */
 #endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 		SSL_load_error_strings();
 		SSL_library_init();
 #ifndef OPENSSL_NO_SHA256
@@ -1098,8 +1133,19 @@
 		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
 		SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
 		SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		/* One session ticket is sufficient for EAP-TLS */
+		SSL_CTX_set_num_tickets(ssl, 1);
+#endif
 	} else {
 		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+		SSL_CTX_set_num_tickets(ssl, 0);
+#endif
 	}
 
 	if (tls_ex_idx_session < 0) {
@@ -1148,18 +1194,30 @@
 	struct tls_data *data = ssl_ctx;
 	SSL_CTX *ssl = data->ssl;
 	struct tls_context *context = SSL_CTX_get_app_data(ssl);
+	struct tls_session_data *sess_data;
+
+	if (data->tls_session_lifetime > 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions");
+		SSL_CTX_flush_sessions(ssl, 0);
+		wpa_printf(MSG_DEBUG, "OpenSSL: Flush sessions - done");
+	}
+	while ((sess_data = dl_list_first(&context->sessions,
+					  struct tls_session_data, list))) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Freeing not-flushed session data %p",
+			   sess_data->buf);
+		wpabuf_free(sess_data->buf);
+		dl_list_del(&sess_data->list);
+		os_free(sess_data);
+	}
 	if (context != tls_global)
 		os_free(context);
-	if (data->tls_session_lifetime > 0)
-		SSL_CTX_flush_sessions(ssl, 0);
 	os_free(data->ca_cert);
 	SSL_CTX_free(ssl);
 
 	tls_openssl_ref_count--;
 	if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
@@ -1561,6 +1619,63 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * By setting the environment variable SSLKEYLOGFILE to a filename keying
+ * material will be exported that you may use with Wireshark to decode any
+ * TLS flows. Please see the following for more details:
+ *
+ *	https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption
+ *
+ * Example logging sessions are (you should delete the file on each run):
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log hostapd ...
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log wpa_supplicant ...
+ *
+ *	rm -f /tmp/sslkey.log
+ *	env SSLKEYLOGFILE=/tmp/sslkey.log eapol_test ...
+ */
+static void tls_keylog_cb(const SSL *ssl, const char *line)
+{
+	int fd;
+	const char *filename;
+	struct iovec iov[2];
+
+	filename = getenv("SSLKEYLOGFILE");
+	if (!filename)
+		return;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+	if (fd < 0) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to open keylog file %s: %s",
+			   filename, strerror(errno));
+		return;
+	}
+
+	/* Assume less than _POSIX_PIPE_BUF (512) where writes are guaranteed
+	 * to be atomic for O_APPEND. */
+	iov[0].iov_base = (void *) line;
+	iov[0].iov_len = os_strlen(line);
+	iov[1].iov_base = "\n";
+	iov[1].iov_len = 1;
+
+	if (writev(fd, iov, ARRAY_SIZE(iov)) < 01) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Failed to write to keylog file %s: %s",
+			   filename, strerror(errno));
+	}
+
+	close(fd);
+}
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 struct tls_connection * tls_connection_init(void *ssl_ctx)
 {
 	struct tls_data *data = ssl_ctx;
@@ -1618,6 +1733,14 @@
 	SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
 #endif
 
+#ifdef CONFIG_TESTING_OPTIONS
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
+	/* Set the keylog file if the admin requested it. */
+	if (getenv("SSLKEYLOGFILE"))
+		SSL_CTX_set_keylog_callback(conn->ssl_ctx, tls_keylog_cb);
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	conn->ssl_in = BIO_new(BIO_s_mem());
 	if (!conn->ssl_in) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -2560,16 +2683,11 @@
 #ifdef CONFIG_SUITEB
 	if (conn->flags & TLS_CONN_SUITEB) {
 		EVP_PKEY *pk;
-		RSA *rsa;
 		int len = -1;
 
 		pk = X509_get_pubkey(err_cert);
 		if (pk) {
-			rsa = EVP_PKEY_get1_RSA(pk);
-			if (rsa) {
-				len = RSA_bits(rsa);
-				RSA_free(rsa);
-			}
+			len = EVP_PKEY_bits(pk);
 			EVP_PKEY_free(pk);
 		}
 
@@ -2960,7 +3078,6 @@
 
 
 #ifdef CONFIG_SUITEB
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
 static int suiteb_cert_cb(SSL *ssl, void *arg)
 {
 	struct tls_connection *conn = arg;
@@ -2987,7 +3104,6 @@
 		   conn->server_dh_prime_len);
 	return 0;
 }
-#endif /* OPENSSL_VERSION_NUMBER */
 #endif /* CONFIG_SUITEB */
 
 
@@ -3003,6 +3119,11 @@
 		SSL_clear_options(ssl, SSL_OP_NO_TICKET);
 #endif /* SSL_OP_NO_TICKET */
 
+#ifdef SSL_OP_LEGACY_SERVER_CONNECT
+	if (flags & TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION)
+		SSL_set_options(ssl, SSL_OP_LEGACY_SERVER_CONNECT);
+#endif /* SSL_OP_LEGACY_SERVER_CONNECT */
+
 #ifdef SSL_OP_NO_TLSv1
 	if (flags & TLS_CONN_DISABLE_TLSv1_0)
 		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
@@ -3082,7 +3203,6 @@
 	/* Start with defaults from BoringSSL */
 	SSL_set_verify_algorithm_prefs(conn->ssl, NULL, 0);
 #endif /* OPENSSL_IS_BORINGSSL */
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
 	if (flags & TLS_CONN_SUITEB_NO_ECDH) {
 		const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
 
@@ -3098,7 +3218,9 @@
 			return -1;
 		}
 	} else if (flags & TLS_CONN_SUITEB) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 		EC_KEY *ecdh;
+#endif
 		const char *ciphers =
 			"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
 		int nid[1] = { NID_secp384r1 };
@@ -3115,6 +3237,14 @@
 			return -1;
 		}
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+		if (SSL_set1_groups(ssl, nid, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B groups");
+			return -1;
+		}
+
+#else
 		if (SSL_set1_curves(ssl, nid, 1) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set Suite B curves");
@@ -3129,6 +3259,7 @@
 			return -1;
 		}
 		EC_KEY_free(ecdh);
+#endif
 	}
 	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
 #ifdef OPENSSL_IS_BORINGSSL
@@ -3153,13 +3284,6 @@
 		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
 		SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
 	}
-#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
-	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
-		wpa_printf(MSG_ERROR,
-			   "OpenSSL: Suite B RSA case not supported with this OpenSSL version");
-		return -1;
-	}
-#endif /* OPENSSL_VERSION_NUMBER */
 
 #ifdef OPENSSL_IS_BORINGSSL
 	if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
@@ -3293,14 +3417,14 @@
 		return 0;
 
 #ifdef PKCS12_FUNCS
-#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
+#ifdef LIBRESSL_VERSION_NUMBER
 	/*
 	 * Clear previously set extra chain certificates, if any, from PKCS#12
-	 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
+	 * processing in tls_parse_pkcs12() to allow LibreSSL to build a new
 	 * chain properly.
 	 */
 	SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
-#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+#endif /* LIBRESSL_VERSION_NUMBER */
 #endif /* PKCS12_FUNCS */
 
 	if (client_cert_blob &&
@@ -3493,7 +3617,7 @@
 	}
 
 	if (certs) {
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+#ifndef LIBRESSL_VERSION_NUMBER
 		if (ssl)
 			SSL_clear_chain_certs(ssl);
 		else
@@ -3542,7 +3666,7 @@
 		 * the extra certificates not to be required.
 		 */
 		res = 0;
-#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+#else /* LIBRESSL_VERSION_NUMBER */
 		SSL_CTX_clear_extra_chain_certs(data->ssl);
 		while ((cert = sk_X509_pop(certs)) != NULL) {
 			X509_NAME_oneline(X509_get_subject_name(cert), buf,
@@ -3561,7 +3685,7 @@
 			}
 		}
 		sk_X509_pop_free(certs, X509_free);
-#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+#endif /* LIBRSESSL_VERSION_NUMBER */
 	}
 
 	PKCS12_free(p12);
@@ -3862,6 +3986,7 @@
 		}
 #endif /* OPENSSL_NO_EC */
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 		if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
 					       (u8 *) private_key_blob,
 					       private_key_blob_len) == 1) {
@@ -3870,6 +3995,7 @@
 			ok = 1;
 			break;
 		}
+#endif
 
 		bio = BIO_new_mem_buf((u8 *) private_key_blob,
 				      private_key_blob_len);
@@ -3977,79 +4103,44 @@
 }
 
 
-static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
-{
-#ifdef OPENSSL_NO_DH
-	if (dh_file == NULL)
-		return 0;
-	wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
-		   "dh_file specified");
-	return -1;
-#else /* OPENSSL_NO_DH */
-	DH *dh;
-	BIO *bio;
-
-	/* TODO: add support for dh_blob */
-	if (dh_file == NULL)
-		return 0;
-	if (conn == NULL)
-		return -1;
-
-	bio = BIO_new_file(dh_file, "r");
-	if (bio == NULL) {
-		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
-			   dh_file, ERR_error_string(ERR_get_error(), NULL));
-		return -1;
-	}
-	dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-	BIO_free(bio);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#ifndef OPENSSL_NO_DH
 #ifndef OPENSSL_NO_DSA
-	while (dh == NULL) {
-		DSA *dsa;
-		wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
-			   " trying to parse as DSA params", dh_file,
-			   ERR_error_string(ERR_get_error(), NULL));
-		bio = BIO_new_file(dh_file, "r");
-		if (bio == NULL)
-			break;
-		dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
-		BIO_free(bio);
-		if (!dsa) {
-			wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
-				   "'%s': %s", dh_file,
-				   ERR_error_string(ERR_get_error(), NULL));
-			break;
-		}
+/* This is needed to replace the deprecated DSA_dup_DH() function */
+static EVP_PKEY * openssl_dsa_to_dh(EVP_PKEY *dsa)
+{
+	OSSL_PARAM_BLD *bld = NULL;
+	OSSL_PARAM *params = NULL;
+	BIGNUM *p = NULL, *q = NULL, *g = NULL;
+	EVP_PKEY_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL;
 
-		wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
-		dh = DSA_dup_DH(dsa);
-		DSA_free(dsa);
-		if (dh == NULL) {
-			wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
-				   "params into DH params");
-			break;
-		}
-		break;
-	}
-#endif /* !OPENSSL_NO_DSA */
-	if (dh == NULL) {
-		wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
-			   "'%s'", dh_file);
-		return -1;
-	}
+	if (!EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_P, &p) ||
+	    !EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_Q, &q) ||
+	    !EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_G, &g) ||
+	    !(bld = OSSL_PARAM_BLD_new()) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) ||
+	    !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+	    !(params = OSSL_PARAM_BLD_to_param(bld)) ||
+	    !(ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL)) ||
+	    EVP_PKEY_fromdata_init(ctx) != 1 ||
+	    EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEY_PARAMETERS,
+			      params) != 1)
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to convert DSA parameters to DH parameters");
 
-	if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
-		wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
-			   "%s", dh_file,
-			   ERR_error_string(ERR_get_error(), NULL));
-		DH_free(dh);
-		return -1;
-	}
-	DH_free(dh);
-	return 0;
-#endif /* OPENSSL_NO_DH */
+	EVP_PKEY_CTX_free(ctx);
+	OSSL_PARAM_free(params);
+	OSSL_PARAM_BLD_free(bld);
+	BN_free(p);
+	BN_free(q);
+	BN_free(g);
+	return pkey;
 }
-
+#endif /* !OPENSSL_NO_DSA */
+#endif /* OPENSSL_NO_DH */
+#endif /* OpenSSL version >= 3.0 */
 
 static int tls_global_dh(struct tls_data *data, const char *dh_file)
 {
@@ -4060,15 +4151,88 @@
 		   "dh_file specified");
 	return -1;
 #else /* OPENSSL_NO_DH */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	SSL_CTX *ssl_ctx = data->ssl;
+	BIO *bio;
+	OSSL_DECODER_CTX *ctx = NULL;
+	EVP_PKEY *pkey = NULL, *tmpkey = NULL;
+	bool dsa = false;
+
+	if (!ssl_ctx)
+		return -1;
+	if (!dh_file) {
+		SSL_CTX_set_dh_auto(ssl_ctx, 1);
+		return 0;
+	}
+
+	bio = BIO_new_file(dh_file, "r");
+	if (!bio) {
+		wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		return -1;
+	}
+	ctx = OSSL_DECODER_CTX_new_for_pkey(
+		&tmpkey, "PEM", NULL, NULL,
+		OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
+	if (!ctx ||
+	    OSSL_DECODER_from_bio(ctx, bio) != 1) {
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to decode domain parameters from '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		BIO_free(bio);
+		return -1;
+	}
+	BIO_free(bio);
+
+	if (!tmpkey) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load domain parameters");
+		return -1;
+	}
+
+#ifndef OPENSSL_NO_DSA
+	if (EVP_PKEY_is_a(tmpkey, "DSA")) {
+		pkey = openssl_dsa_to_dh(tmpkey);
+		EVP_PKEY_free(tmpkey);
+		if (!pkey)
+			return -1;
+		dsa = true;
+	}
+#endif /* !OPENSSL_NO_DSA */
+	if (!dsa) {
+		if (EVP_PKEY_is_a(tmpkey, "DH") ||
+		    EVP_PKEY_is_a(tmpkey, "DHX")) {
+		} else {
+			wpa_printf(MSG_INFO,
+				   "TLS: No DH parameters found in %s",
+				   dh_file);
+			EVP_PKEY_free(tmpkey);
+			return -1;
+		}
+		pkey = tmpkey;
+		tmpkey = NULL;
+	}
+
+	if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, pkey) != 1) {
+		wpa_printf(MSG_INFO,
+			   "TLS: Failed to set DH params from '%s': %s",
+			   dh_file, ERR_error_string(ERR_get_error(), NULL));
+		EVP_PKEY_free(pkey);
+		return -1;
+	}
+	return 0;
+#else /* OpenSSL version >= 3.0 */
 	SSL_CTX *ssl_ctx = data->ssl;
 	DH *dh;
 	BIO *bio;
 
-	/* TODO: add support for dh_blob */
-	if (dh_file == NULL)
-		return 0;
-	if (ssl_ctx == NULL)
+	if (!ssl_ctx)
 		return -1;
+	if (!dh_file) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
+		SSL_CTX_set_dh_auto(ssl_ctx, 1);
+#endif
+		return 0;
+	}
 
 	bio = BIO_new_file(dh_file, "r");
 	if (bio == NULL) {
@@ -4122,6 +4286,7 @@
 	}
 	DH_free(dh);
 	return 0;
+#endif /* OpenSSL version >= 3.0 */
 #endif /* OPENSSL_NO_DH */
 }
 
@@ -4152,9 +4317,7 @@
 #ifdef OPENSSL_NEED_EAP_FAST_PRF
 static int openssl_get_keyblock_size(SSL *ssl)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
 	int md_size;
@@ -4322,6 +4485,7 @@
 static struct wpabuf *
 openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
 {
+	struct tls_context *context = conn->context;
 	int res;
 	struct wpabuf *out_data;
 
@@ -4351,7 +4515,19 @@
 			wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
 				   "write");
 		else {
+			unsigned long error = ERR_peek_last_error();
+
 			tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+
+			if (context->event_cb &&
+			    ERR_GET_LIB(error) == ERR_LIB_SSL &&
+			    ERR_GET_REASON(error) ==
+			    SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED) {
+				context->event_cb(
+					context->cb_ctx,
+					TLS_UNSAFE_RENEGOTIATION_DISABLED,
+					NULL);
+			}
 			conn->failed++;
 			if (!conn->server && !conn->client_hello_generated) {
 				/* The server would not understand TLS Alert
@@ -4374,8 +4550,6 @@
 	if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
 	    os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
 	    conn->server_dh_prime_len < 3072) {
-		struct tls_context *context = conn->context;
-
 		/*
 		 * This should not be reached since earlier cert_cb should have
 		 * terminated the handshake. Keep this check here for extra
@@ -4867,6 +5041,21 @@
 
 	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 	if (!p) {
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x30400000L
+		if (SSL_version(s) == TLS1_3_VERSION && SSL_session_reused(s)) {
+			/* TLS 1.3 sends the OCSP response with the server
+			 * Certificate message. Since that Certificate message
+			 * is not sent when resuming a session, there can be no
+			 * new OCSP response. Allow this since the OCSP response
+			 * was validated when checking the initial certificate
+			 * exchange. */
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Allow no OCSP response when using TLS 1.3 and a resumed session");
+			return 1;
+		}
+#endif
+#endif
 		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
 		return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
 	}
@@ -5268,12 +5457,6 @@
 		return -1;
 	}
 
-	if (tls_connection_dh(conn, params->dh_file)) {
-		wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
-			   params->dh_file);
-		return -1;
-	}
-
 	ciphers = params->openssl_ciphers;
 #ifdef CONFIG_SUITEB
 #ifdef OPENSSL_IS_BORINGSSL
@@ -5295,22 +5478,21 @@
 	if (!params->openssl_ecdh_curves) {
 #ifndef OPENSSL_IS_BORINGSSL
 #ifndef OPENSSL_NO_EC
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
-	(OPENSSL_VERSION_NUMBER < 0x10100000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 		if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set ECDH curves to auto");
 			return -1;
 		}
-#endif /* >= 1.0.2 && < 1.1.0 */
+#endif /* < 1.1.0 */
 #endif /* OPENSSL_NO_EC */
 #endif /* OPENSSL_IS_BORINGSSL */
 	} else if (params->openssl_ecdh_curves[0]) {
-#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
+#ifdef OPENSSL_IS_BORINGSSL
 		wpa_printf(MSG_INFO,
-			"OpenSSL: ECDH configuration nnot supported");
+			"OpenSSL: ECDH configuration not supported");
 		return -1;
-#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
+#else /* !OPENSSL_IS_BORINGSSL */
 #ifndef OPENSSL_NO_EC
 		if (SSL_set1_curves_list(conn->ssl,
 					 params->openssl_ecdh_curves) != 1) {
@@ -5520,22 +5702,21 @@
 	if (!params->openssl_ecdh_curves) {
 #ifndef OPENSSL_IS_BORINGSSL
 #ifndef OPENSSL_NO_EC
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
-	(OPENSSL_VERSION_NUMBER < 0x10100000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 		if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set ECDH curves to auto");
 			return -1;
 		}
-#endif /* >= 1.0.2 && < 1.1.0 */
+#endif /* < 1.1.0 */
 #endif /* OPENSSL_NO_EC */
 #endif /* OPENSSL_IS_BORINGSSL */
 	} else if (params->openssl_ecdh_curves[0]) {
-#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
+#ifdef OPENSSL_IS_BORINGSSL
 		wpa_printf(MSG_INFO,
-			"OpenSSL: ECDH configuration nnot supported");
+			"OpenSSL: ECDH configuration not supported");
 		return -1;
-#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
+#else /* !OPENSSL_IS_BORINGSSL */
 #ifndef OPENSSL_NO_EC
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 		SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
@@ -5597,9 +5778,7 @@
 	struct tls_connection *conn = arg;
 	int ret;
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-	(defined(LIBRESSL_VERSION_NUMBER) && \
-	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	if (conn == NULL || conn->session_ticket_cb == NULL)
 		return 0;
 
@@ -5710,6 +5889,7 @@
 {
 	SSL_SESSION *sess;
 	struct wpabuf *old;
+	struct tls_session_data *sess_data = NULL;
 
 	if (tls_ex_idx_session < 0)
 		goto fail;
@@ -5718,20 +5898,35 @@
 		goto fail;
 	old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
 	if (old) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
-			   old);
-		wpabuf_free(old);
+		struct tls_session_data *found;
+
+		found = get_session_data(conn->context, old);
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Replacing old success data %p (sess %p)%s",
+			   old, sess, found ? "" : " (not freeing)");
+		if (found) {
+			dl_list_del(&found->list);
+			os_free(found);
+			wpabuf_free(old);
+		}
 	}
-	if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+
+	sess_data = os_zalloc(sizeof(*sess_data));
+	if (!sess_data ||
+	    SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
 		goto fail;
 
-	wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
+	sess_data->buf = data;
+	dl_list_add(&conn->context->sessions, &sess_data->list);
+	wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p (sess %p)",
+		   data, sess);
 	conn->success_data = 1;
 	return;
 
 fail:
 	wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
 	wpabuf_free(data);
+	os_free(sess_data);
 }
 
 
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
index 97bf605..b570bea 100644
--- a/src/crypto/tls_openssl_ocsp.c
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -644,13 +644,12 @@
 		   buf);
 
 	ctx = X509_STORE_CTX_new();
-	if (!ctx ||
-	    !X509_STORE_CTX_init(ctx, store, signer, untrusted) ||
-	    !X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER)) {
+	if (!ctx || !X509_STORE_CTX_init(ctx, store, signer, untrusted))
 		goto fail;
-	}
+	X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER);
 	ret = X509_verify_cert(ctx);
 	chain = X509_STORE_CTX_get1_chain(ctx);
+	X509_STORE_CTX_cleanup(ctx);
 	if (ret <= 0) {
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Could not validate OCSP signer certificate");
@@ -663,6 +662,7 @@
 	}
 
 	if (!signer_trusted) {
+		X509_check_purpose(signer, -1, 0);
 		if ((X509_get_extension_flags(signer) & EXFLAG_XKUSAGE) &&
 		    (X509_get_extended_key_usage(signer) & XKU_OCSP_SIGN)) {
 			wpa_printf(MSG_DEBUG,
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index cf482bf..b4f1bbe 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -26,6 +26,10 @@
 #include <wolfssl/wolfcrypt/aes.h>
 #endif
 
+#ifdef CONFIG_FIPS
+#include <wolfssl/wolfcrypt/fips_test.h>
+#endif /* CONFIG_FIPS */
+
 #if !defined(CONFIG_FIPS) &&                             \
     (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) ||   \
      defined(EAP_SERVER_FAST))
@@ -58,6 +62,7 @@
 	void *cb_ctx;
 	int cert_in_cb;
 	char *ocsp_stapling_response;
+	unsigned int tls_session_lifetime;
 };
 
 static struct tls_context *tls_global = NULL;
@@ -94,6 +99,7 @@
 	WOLFSSL_X509 *peer_cert;
 	WOLFSSL_X509 *peer_issuer;
 	WOLFSSL_X509 *peer_issuer_issuer;
+	char *peer_subject; /* peer subject info for authenticated peer */
 };
 
 
@@ -190,6 +196,33 @@
 }
 
 
+#if defined(CONFIG_FIPS) && defined(HAVE_FIPS)
+static void wcFipsCb(int ok, int err, const char *hash)
+{
+	wpa_printf(MSG_INFO,
+		   "wolfFIPS: wolfCrypt Fips error callback, ok = %d, err = %d",
+		   ok, err);
+	wpa_printf(MSG_INFO, "wolfFIPS: message = %s", wc_GetErrorString(err));
+	wpa_printf(MSG_INFO, "wolfFIPS: hash = %s", hash);
+	if (err == IN_CORE_FIPS_E) {
+		wpa_printf(MSG_ERROR,
+			   "wolfFIPS: In core integrity hash check failure, copy above hash");
+		wpa_printf(MSG_ERROR, "wolfFIPS: into verifyCore[] in fips_test.c and rebuild");
+	}
+}
+#endif /* CONFIG_FIPS && HAVE_FIPS */
+
+
+#ifdef DEBUG_WOLFSSL
+static void wolfSSL_logging_cb(const int log_level,
+			       const char * const log_message)
+{
+	(void) log_level;
+	wpa_printf(MSG_DEBUG, "wolfSSL log:%s", log_message);
+}
+#endif /* DEBUG_WOLFSSL */
+
+
 void * tls_init(const struct tls_config *conf)
 {
 	WOLFSSL_CTX *ssl_ctx;
@@ -197,6 +230,7 @@
 	const char *ciphers;
 
 #ifdef DEBUG_WOLFSSL
+	wolfSSL_SetLoggingCb(wolfSSL_logging_cb);
 	wolfSSL_Debugging_ON();
 #endif /* DEBUG_WOLFSSL */
 
@@ -209,7 +243,9 @@
 
 		if (wolfSSL_Init() < 0)
 			return NULL;
-		/* wolfSSL_Debugging_ON(); */
+#if defined(CONFIG_FIPS) && defined(HAVE_FIPS)
+		wolfCrypt_SetCb_fips(wcFipsCb);
+#endif /* CONFIG_FIPS && HAVE_FIPS */
 	}
 
 	tls_ref_count++;
@@ -227,17 +263,21 @@
 	}
 	wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
 	wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
+	context->tls_session_lifetime = conf->tls_session_lifetime;
 	wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
 
 	if (conf->tls_session_lifetime > 0) {
+		wolfSSL_CTX_set_session_id_context(ssl_ctx,
+						   (const unsigned char *)
+						   "hostapd", 7);
 		wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
 		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
-						   SSL_SESS_CACHE_SERVER);
+						   WOLFSSL_SESS_CACHE_SERVER);
 		wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
 		wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
 	} else {
 		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
-						   SSL_SESS_CACHE_CLIENT);
+						   WOLFSSL_SESS_CACHE_OFF);
 	}
 
 	if (conf && conf->openssl_ciphers)
@@ -336,6 +376,7 @@
 	os_free(conn->alt_subject_match);
 	os_free(conn->suffix_match);
 	os_free(conn->domain_match);
+	os_free(conn->peer_subject);
 
 	/* self */
 	os_free(conn);
@@ -369,10 +410,13 @@
 	wolfSSL_set_quiet_shutdown(conn->ssl, 1);
 	wolfSSL_shutdown(conn->ssl);
 
-	session = wolfSSL_get_session(conn->ssl);
-	if (wolfSSL_clear(conn->ssl) != 1)
+	session = wolfSSL_get1_session(conn->ssl);
+	if (wolfSSL_clear(conn->ssl) != 1) {
+		wolfSSL_SESSION_free(session);
 		return -1;
+	}
 	wolfSSL_set_session(conn->ssl, session);
+	wolfSSL_SESSION_free(session);
 
 	return 0;
 }
@@ -420,44 +464,6 @@
 }
 
 
-static int tls_connection_dh(struct tls_connection *conn, const char *dh_file,
-			     const u8 *dh_blob, size_t blob_len)
-{
-	if (!dh_file && !dh_blob)
-		return 0;
-
-	wolfSSL_set_accept_state(conn->ssl);
-
-	if (dh_blob) {
-		if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len,
-					    SSL_FILETYPE_ASN1) < 0) {
-			wpa_printf(MSG_INFO, "SSL: use DH DER blob failed");
-			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "SSL: use DH blob OK");
-		return 0;
-	}
-
-	if (dh_file) {
-		wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file);
-		if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
-					  SSL_FILETYPE_PEM) < 0) {
-			wpa_printf(MSG_INFO, "SSL: use DH PEM file failed");
-			if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
-						  SSL_FILETYPE_ASN1) < 0) {
-				wpa_printf(MSG_INFO,
-					   "SSL: use DH DER file failed");
-				return -1;
-			}
-		}
-		wpa_printf(MSG_DEBUG, "SSL: use DH file OK");
-		return 0;
-	}
-
-	return 0;
-}
-
-
 static int tls_connection_client_cert(struct tls_connection *conn,
 				      const char *client_cert,
 				      const u8 *client_cert_blob,
@@ -472,7 +478,13 @@
 			    SSL_FILETYPE_ASN1) != SSL_SUCCESS) {
 			wpa_printf(MSG_INFO,
 				   "SSL: use client cert DER blob failed");
-			return -1;
+			if (wolfSSL_use_certificate_chain_buffer_format(
+				    conn->ssl, client_cert_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use client cert PEM blob failed");
+				return -1;
+			}
 		}
 		wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
 		return 0;
@@ -534,23 +546,35 @@
 	if (private_key_blob) {
 		if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
 						  private_key_blob, blob_len,
-						  SSL_FILETYPE_ASN1) <= 0) {
+						  SSL_FILETYPE_ASN1) !=
+		    SSL_SUCCESS) {
 			wpa_printf(MSG_INFO,
 				   "SSL: use private DER blob failed");
+			if (wolfSSL_use_PrivateKey_buffer(
+				    conn->ssl,
+				    private_key_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use private PEM blob failed");
+			} else {
+				ok = 1;
+			}
 		} else {
-			wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
 			ok = 1;
 		}
+		if (ok)
+			wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
 	}
 
 	if (!ok && private_key) {
 		if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
-						SSL_FILETYPE_PEM) <= 0) {
+						SSL_FILETYPE_PEM) !=
+		    SSL_SUCCESS) {
 			wpa_printf(MSG_INFO,
 				   "SSL: use private key PEM file failed");
 			if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
-							SSL_FILETYPE_ASN1) <= 0)
-			{
+							SSL_FILETYPE_ASN1) !=
+			    SSL_SUCCESS) {
 				wpa_printf(MSG_INFO,
 					   "SSL: use private key DER file failed");
 			} else {
@@ -721,8 +745,7 @@
 		WOLFSSL_X509_NAME_ENTRY *e;
 		WOLFSSL_ASN1_STRING *cn;
 
-		i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME,
-						       i);
+		i = wolfSSL_X509_NAME_get_index_by_NID(name, NID_commonName, i);
 		if (i == -1)
 			break;
 		e = wolfSSL_X509_NAME_get_entry(name, i);
@@ -1134,6 +1157,11 @@
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
 
+	if (depth == 0 && preverify_ok) {
+		os_free(conn->peer_subject);
+		conn->peer_subject = os_strdup(buf);
+	}
+
 	return preverify_ok;
 }
 
@@ -1194,8 +1222,14 @@
 		if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
 						   SSL_FILETYPE_ASN1) !=
 		    SSL_SUCCESS) {
-			wpa_printf(MSG_INFO, "SSL: failed to load CA blob");
-			return -1;
+			wpa_printf(MSG_INFO, "SSL: failed to load DER CA blob");
+			if (wolfSSL_CTX_load_verify_buffer(
+				    ctx, ca_cert_blob, blob_len,
+				    SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: failed to load PEM CA blob");
+				return -1;
+			}
 		}
 		wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
 		return 0;
@@ -1238,10 +1272,8 @@
 static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
 {
 #ifdef HAVE_SESSION_TICKET
-#if 0
 	if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
 		wolfSSL_UseSessionTicket(ssl);
-#endif
 #endif /* HAVE_SESSION_TICKET */
 
 	if (flags & TLS_CONN_DISABLE_TLSv1_0)
@@ -1250,6 +1282,8 @@
 		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
 	if (flags & TLS_CONN_DISABLE_TLSv1_2)
 		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+	if (flags & TLS_CONN_DISABLE_TLSv1_3)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
 }
 
 
@@ -1289,12 +1323,6 @@
 		return -1;
 	}
 
-	if (tls_connection_dh(conn, params->dh_file, params->dh_blob,
-			      params->dh_blob_len) < 0) {
-		wpa_printf(MSG_INFO, "Error setting DH");
-		return -1;
-	}
-
 	if (params->openssl_ciphers &&
 	    wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
 		wpa_printf(MSG_INFO,
@@ -1311,7 +1339,8 @@
 					    WOLFSSL_CSR_OCSP_USE_NONCE) !=
 		    SSL_SUCCESS)
 			return -1;
-		wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+		if (wolfSSL_EnableOCSPStapling(conn->ssl) != SSL_SUCCESS)
+			return -1;
 	}
 #endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
 #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
@@ -1320,7 +1349,8 @@
 					      WOLFSSL_CSR2_OCSP_MULTI, 0) !=
 		    SSL_SUCCESS)
 			return -1;
-		wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+		if (wolfSSL_EnableOCSPStapling(conn->ssl) != SSL_SUCCESS)
+			return -1;
 	}
 #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
 #if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
@@ -1427,25 +1457,10 @@
 }
 
 
-static int tls_global_dh(void *ssl_ctx, const char *dh_file,
-			 const u8 *dh_blob, size_t blob_len)
+static int tls_global_dh(void *ssl_ctx, const char *dh_file)
 {
 	WOLFSSL_CTX *ctx = ssl_ctx;
 
-	if (!dh_file && !dh_blob)
-		return 0;
-
-	if (dh_blob) {
-		if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len,
-						SSL_FILETYPE_ASN1) < 0) {
-			wpa_printf(MSG_INFO,
-				   "SSL: global use DH DER blob failed");
-			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK");
-		return 0;
-	}
-
 	if (dh_file) {
 		if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
 		    0) {
@@ -1532,8 +1547,7 @@
 		return -1;
 	}
 
-	if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob,
-			  params->dh_blob_len) < 0) {
+	if (tls_global_dh(tls_ctx, params->dh_file) < 0) {
 		wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
 			   params->dh_file);
 		return -1;
@@ -1590,6 +1604,9 @@
 			      int verify_peer, unsigned int flags,
 			      const u8 *session_ctx, size_t session_ctx_len)
 {
+	static int counter = 0;
+	struct tls_context *context;
+
 	if (!conn)
 		return -1;
 
@@ -1607,6 +1624,22 @@
 
 	wolfSSL_set_accept_state(conn->ssl);
 
+	context = wolfSSL_CTX_get_ex_data((WOLFSSL_CTX *) ssl_ctx, 0);
+	if (context && context->tls_session_lifetime == 0) {
+		/*
+		 * Set session id context to a unique value to make sure
+		 * session resumption cannot be used either through session
+		 * caching or TLS ticket extension.
+		 */
+		counter++;
+		wolfSSL_set_session_id_context(conn->ssl,
+					       (const unsigned char *) &counter,
+					       sizeof(counter));
+	} else {
+		wolfSSL_set_session_id_context(conn->ssl, session_ctx,
+					       session_ctx_len);
+	}
+
 	/* TODO: do we need to fake a session like OpenSSL does here? */
 
 	return 0;
@@ -1997,11 +2030,21 @@
 			      const char *label, const u8 *context,
 			      size_t context_len, u8 *out, size_t out_len)
 {
-	if (context)
+	if (!conn)
 		return -1;
-	if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+#if LIBWOLFSSL_VERSION_HEX >= 0x04007000
+	if (wolfSSL_export_keying_material(conn->ssl, out, out_len,
+					   label, os_strlen(label),
+					   context, context_len,
+					   context != NULL) != WOLFSSL_SUCCESS)
 		return -1;
 	return 0;
+#else
+	if (context ||
+	    wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+		return -1;
+#endif
+	return 0;
 }
 
 
@@ -2046,9 +2089,15 @@
 			       _out, skip + out_len);
 		ret = 0;
 	} else {
+#ifdef CONFIG_FIPS
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Can't use sha1_md5 in FIPS build");
+		ret = -1;
+#else /* CONFIG_FIPS */
 		ret = tls_prf_sha1_md5(master_key, master_key_len,
 				       "key expansion", seed, sizeof(seed),
 				       _out, skip + out_len);
+#endif /* CONFIG_FIPS */
 	}
 
 	forced_memzero(master_key, master_key_len);
@@ -2160,6 +2209,39 @@
 }
 
 
+int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
+{
+	size_t len;
+	int reused;
+
+	reused = wolfSSL_session_reused(conn->ssl);
+	if ((wolfSSL_is_server(conn->ssl) && !reused) ||
+	    (!wolfSSL_is_server(conn->ssl) && reused))
+		len = wolfSSL_get_peer_finished(conn->ssl, buf, max_len);
+	else
+		len = wolfSSL_get_finished(conn->ssl, buf, max_len);
+
+	if (len == 0 || len > max_len)
+		return -1;
+
+	return len;
+}
+
+
+u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
+{
+	return (u16) wolfSSL_get_current_cipher_suite(conn->ssl);
+}
+
+
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+	if (conn)
+		return conn->peer_subject;
+	return NULL;
+}
+
+
 void tls_connection_set_success_data(struct tls_connection *conn,
 				     struct wpabuf *data)
 {
@@ -2206,3 +2288,11 @@
 		return NULL;
 	return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
 }
+
+
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+	if (conn)
+		return wolfSSL_get_certificate(conn->ssl) != NULL;
+	return false;
+}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 8b92e12..46cee44 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -200,6 +200,15 @@
 	u16 he_6ghz_capa;
 };
 
+/* struct eht_capabilities - IEEE 802.11be EHT capabilities */
+struct eht_capabilities {
+	bool eht_supported;
+	u16 mac_cap;
+	u8 phy_cap[EHT_PHY_CAPAB_LEN];
+	u8 mcs[EHT_MCS_NSS_CAPAB_LEN];
+	u8 ppet[EHT_PPE_THRESH_CAPAB_LEN];
+};
+
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
 #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
 
@@ -298,6 +307,11 @@
 	 * for IEEE 802.11ay EDMG configuration.
 	 */
 	struct ieee80211_edmg_config edmg;
+
+	/**
+	 * eht_capab - EHT (IEEE 802.11be) capabilities
+	 */
+	struct eht_capabilities eht_capab[IEEE80211_MODE_NUM];
 };
 
 
@@ -338,6 +352,7 @@
  * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
  * of TSF of the BSS specified by %tsf_bssid.
  * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
+ * @beacon_newer: Whether the Beacon frame data is known to be newer
  * @ie_len: length of the following IE field in octets
  * @beacon_ie_len: length of the following Beacon IE field in octets
  *
@@ -370,6 +385,7 @@
 	int snr;
 	u64 parent_tsf;
 	u8 tsf_bssid[ETH_ALEN];
+	bool beacon_newer;
 	size_t ie_len;
 	size_t beacon_ie_len;
 	/* Followed by ie_len + beacon_ie_len octets of IE data */
@@ -782,6 +798,16 @@
 	 * for IEEE 802.11ay EDMG configuration.
 	 */
 	struct ieee80211_edmg_config edmg;
+
+	/**
+	 * radar_background - Whether radar/CAC background is requested
+	 */
+	bool radar_background;
+
+	/**
+	 * eht_enabled - Whether EHT is enabled
+	 */
+	bool eht_enabled;
 };
 
 /**
@@ -1036,7 +1062,7 @@
 	 *
 	 * If the driver needs to do special configuration for WPS association,
 	 * this variable provides more information on what type of association
-	 * is being requested. Most drivers should not need ot use this.
+	 * is being requested. Most drivers should not need to use this.
 	 */
 	enum wps_mode wps;
 
@@ -1781,7 +1807,7 @@
 	WPA_IF_P2P_GROUP,
 
 	/**
-	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to identify the
 	 * abstracted P2P Device function in the driver
 	 */
 	WPA_IF_P2P_DEVICE,
@@ -2033,6 +2059,10 @@
 #define WPA_DRIVER_FLAGS2_OCV			0x0000000000000080ULL
 /** Driver expects user space implementation of SME in AP mode */
 #define WPA_DRIVER_FLAGS2_AP_SME		0x0000000000000100ULL
+/** Driver handles SA Query procedures in AP mode */
+#define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP	0x0000000000000200ULL
+/** Driver supports background radar/CAC detection */
+#define WPA_DRIVER_RADAR_BACKGROUND		0x0000000000000400ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2200,6 +2230,8 @@
 	const struct ieee80211_he_capabilities *he_capab;
 	size_t he_capab_len;
 	const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+	const struct ieee80211_eht_capabilities *eht_capab;
+	size_t eht_capab_len;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	u32 flags_mask; /* unset bits in flags */
 #ifdef CONFIG_MESH
@@ -2403,6 +2435,27 @@
 	u16 counter_offset_presp[2];
 };
 
+/**
+ * struct cca_settings - Settings for color switch command
+ * @cca_count: Count in Beacon frames (TBTT) to perform the switch
+ * @cca_color: The new color that we are switching to
+ * @beacon_cca: Beacon/Probe Response/(Re)Association Response frame info for
+ * color switch period
+ * @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info
+ * @counter_offset_beacon: Offset to the count field in Beacon frame tail
+ * @counter_offset_presp: Offset to the count field in Probe Response frame
+ */
+struct cca_settings {
+	u8 cca_count;
+	u8 cca_color;
+
+	struct beacon_data beacon_cca;
+	struct beacon_data beacon_after;
+
+	u16 counter_offset_beacon;
+	u16 counter_offset_presp;
+};
+
 /* TDLS peer capabilities for send_tdls_mgmt() */
 enum tdls_peer_capability {
 	TDLS_PEER_HT = BIT(0),
@@ -2467,6 +2520,9 @@
 
 	/* Indicates whether EDMG is enabled */
 	int edmg_enabled;
+
+	/* Indicates whether EHT is enabled */
+	bool eht_enabled;
 };
 
 struct wpa_bss_trans_info {
@@ -2608,7 +2664,7 @@
 	 * some drivers may expect them in different order than wpa_supplicant
 	 * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
 	 * will trigger Michael MIC errors. This can be fixed by changing the
-	 * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
+	 * order of MIC keys by swapping the bytes 16..23 and 24..31 of the key
 	 * in driver_*.c set_key() implementation, see driver_ndis.c for an
 	 * example on how this can be done.
 	 */
@@ -3989,6 +4045,17 @@
 	int (*switch_channel)(void *priv, struct csa_settings *settings);
 
 	/**
+	 * switch_color - Announce color switch and migrate the BSS to the
+	 * given color
+	 * @priv: Private driver interface data
+	 * @settings: Settings for CCA period and new color
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to move the BSS to its new color.
+	 */
+	int (*switch_color)(void *priv, struct cca_settings *settings);
+
+	/**
 	 * add_tx_ts - Add traffic stream
 	 * @priv: Private driver interface data
 	 * @tsid: Traffic stream ID
@@ -4617,7 +4684,7 @@
 	 * This event must be delivered when a Michael MIC error is detected by
 	 * the local driver. Additional data for event processing is
 	 * provided with union wpa_event_data::michael_mic_failure. This
-	 * information is used to request new encyption key and to initiate
+	 * information is used to request new encryption key and to initiate
 	 * TKIP countermeasures if needed.
 	 */
 	EVENT_MICHAEL_MIC_FAILURE,
@@ -5147,6 +5214,26 @@
 	 * non-zero wait time and that has not been explicitly cancelled.
 	 */
 	EVENT_TX_WAIT_EXPIRE,
+
+	/**
+	  * EVENT_BSS_COLOR_COLLISION - Notification of a BSS color collision
+	  */
+	EVENT_BSS_COLOR_COLLISION,
+
+	/**
+	 * EVENT_CCA_STARTED_NOTIFY - Notification that CCA has started
+	 */
+	EVENT_CCA_STARTED_NOTIFY,
+
+	/**
+	 * EVENT_CCA_ABORTED_NOTIFY - Notification that CCA has aborted
+	 */
+	EVENT_CCA_ABORTED_NOTIFY,
+
+	/**
+	 * EVENT_CCA_NOTIFY - Notification that CCA has completed
+	 */
+	EVENT_CCA_NOTIFY,
 };
 
 
@@ -6039,6 +6126,13 @@
 	struct unprot_beacon {
 		const u8 *sa;
 	} unprot_beacon;
+
+	/**
+	 * struct bss_color_collision - Data for EVENT_BSS_COLOR_COLLISION
+	 */
+	struct bss_color_collision {
+		u64 bitmap;
+	} bss_color_collision;
 };
 
 /**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 9b4166d..cb66dfa 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -17,7 +17,6 @@
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "l2_packet/l2_packet.h"
-#include "p2p/p2p.h"
 
 #include "common.h"
 #ifndef _BYTE_ORDER
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 741521c..8db7861 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -91,6 +91,10 @@
 	E2S(UPDATE_DH);
 	E2S(UNPROT_BEACON);
 	E2S(TX_WAIT_EXPIRE);
+	E2S(BSS_COLOR_COLLISION);
+	E2S(CCA_STARTED_NOTIFY);
+	E2S(CCA_ABORTED_NOTIFY);
+	E2S(CCA_NOTIFY);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 3dba13c..b609bbf 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -77,6 +77,9 @@
 
 	u8 encoding_sa;
 	bool encoding_sa_set;
+
+	u64 cipher_suite;
+	bool cipher_suite_set;
 };
 
 
@@ -460,8 +463,14 @@
  */
 static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
 {
+	struct macsec_drv_data *drv = priv;
+
 	wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
-	return 0;
+
+	drv->cipher_suite_set = true;
+	drv->cipher_suite = cs;
+
+	return try_commit(drv);
 }
 
 
@@ -1063,7 +1072,8 @@
 }
 
 
-static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci,
+				    u64 cs)
 {
 	struct rtnl_link *needle;
 	void *match;
@@ -1074,6 +1084,8 @@
 
 	rtnl_link_set_link(needle, parent);
 	rtnl_link_macsec_set_sci(needle, sci);
+	if (cs)
+		rtnl_link_macsec_set_cipher_suite(needle, cs);
 
 	match = nl_cache_find(cache, (struct nl_object *) needle);
 	rtnl_link_put(needle);
@@ -1098,6 +1110,7 @@
 	char *ifname;
 	u64 sci;
 	int err;
+	u64 cs = 0;
 
 	wpa_printf(MSG_DEBUG, DRV_PREFIX
 		   "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
@@ -1122,6 +1135,12 @@
 
 	drv->created_link = true;
 
+	if (drv->cipher_suite_set) {
+		cs = drv->cipher_suite;
+		drv->cipher_suite_set = false;
+		rtnl_link_macsec_set_cipher_suite(link, cs);
+	}
+
 	err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
 	if (err == -NLE_BUSY) {
 		wpa_printf(MSG_INFO,
@@ -1137,7 +1156,7 @@
 	rtnl_link_put(link);
 
 	nl_cache_refill(drv->sk, drv->link_cache);
-	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
+	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci, cs);
 	if (!link) {
 		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
 		return -1;
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index 54964f3..eccaf63 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -861,7 +861,7 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
+	wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
 
 	return -1;
 }
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a2c4842..5892fc1 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -38,9 +38,9 @@
 #include "radiotap_iter.h"
 #include "rfkill.h"
 #include "driver_nl80211.h"
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 #include "common/brcm_vendor.h"
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 #ifndef NETLINK_CAP_ACK
 #define NETLINK_CAP_ACK 10
@@ -2943,6 +2943,8 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
 		   drv->ifindex);
+	bss->beacon_set = 0;
+	bss->freq = 0;
 	nl80211_put_wiphy_data_ap(bss);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
 	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
@@ -3191,7 +3193,7 @@
 	return num_suites;
 }
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
 				      struct drv_acs_params *params)
 {
@@ -3241,7 +3243,7 @@
 	nlmsg_free(msg);
 	return ret;
 }
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 #ifdef CONFIG_DRIVER_NL80211_BRCM
 static int wpa_cross_akm_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
@@ -3292,7 +3294,7 @@
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 static int key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
 				  const u8 *key, size_t key_len)
 {
@@ -3320,7 +3322,8 @@
 
 	return ret;
 }
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 
 static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
 			   const u8 *key, size_t key_len,
@@ -3409,12 +3412,13 @@
 	if (key_flag & KEY_FLAG_PMK) {
 		if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)
 			return nl80211_set_pmk(drv, key, key_len, addr);
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 		if (drv->vendor_set_pmk) {
 			wpa_printf(MSG_INFO, "nl80211: key_mgmt_set_key with key_len %lu", (unsigned long) key_len);
 			return key_mgmt_set_key(drv, key, key_len);
 		}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 		/* The driver does not have any offload mechanism for PMK, so
 		 * there is no need to configure this key. */
 		return 0;
@@ -4768,10 +4772,24 @@
 	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
 		goto fail;
 
-	if (drv->device_ap_sme &&
-	    (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) &&
-	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
-		goto fail;
+	if (drv->device_ap_sme) {
+		u32 flags = 0;
+
+		if (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) {
+			/* Add the previously used flag attribute to support
+			 * older kernel versions and the newer flag bit for
+			 * newer kernels. */
+			if (nla_put_flag(msg,
+					 NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+				goto fail;
+			flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT;
+		}
+
+		flags |= NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT;
+
+		if (nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags))
+			goto fail;
+	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
 		   params->pairwise_ciphers);
@@ -5005,15 +5023,19 @@
 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
 		return -ENOBUFS;
 
+	wpa_printf(MSG_DEBUG, "  * eht_enabled=%d", freq->eht_enabled);
 	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
 	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
 	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+	wpa_printf(MSG_DEBUG, "  * radar_background=%d",
+		   freq->radar_background);
 
 	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
 	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
 		hw_mode == HOSTAPD_MODE_IEEE80211B;
 
-	if (freq->vht_enabled || (freq->he_enabled && !is_24ghz)) {
+	if (freq->vht_enabled ||
+	    ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
 		enum nl80211_chan_width cw;
 
 		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
@@ -5085,6 +5107,9 @@
 				NL80211_CHAN_NO_HT))
 			return -ENOBUFS;
 	}
+	if (freq->radar_background)
+		nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND);
+
 	return 0;
 }
 
@@ -5097,9 +5122,10 @@
 	int ret;
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
-		   freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
-		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
+		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+		   freq->center_freq1, freq->center_freq2);
 
 	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
 			      NL80211_CMD_SET_WIPHY);
@@ -5248,6 +5274,14 @@
 				goto fail;
 		}
 
+		if (params->eht_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * eht_capab",
+				    params->eht_capab, params->eht_capab_len);
+			if (nla_put(msg, NL80211_ATTR_EHT_CAPABILITY,
+				    params->eht_capab_len, params->eht_capab))
+				goto fail;
+		}
+
 		if (params->ext_capab) {
 			wpa_hexdump(MSG_DEBUG, "  * ext_capab",
 				    params->ext_capab, params->ext_capab_len);
@@ -8298,6 +8332,11 @@
 		if (save_cookie)
 			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
 
+		if (!wait) {
+			 /* There is no need to store this cookie since there
+			  * is no wait that could be canceled later.  */
+			goto fail;
+		}
 		if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) {
 			wpa_printf(MSG_DEBUG,
 				   "nl80211: Drop oldest pending send frame cookie 0x%llx",
@@ -8428,7 +8467,8 @@
 	u64 cookie;
 
 	/* Cancel the last pending TX cookie */
-	nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
+	if (drv->send_frame_cookie != (u64) -1)
+		nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
 
 	/*
 	 * Cancel the other pending TX cookies, if any. This is needed since
@@ -8620,7 +8660,6 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 	wpa_driver_nl80211_del_beacon(bss);
-	bss->beacon_set = 0;
 
 	/*
 	 * If the P2P GO interface was dynamically added, then it is
@@ -8640,7 +8679,6 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 	wpa_driver_nl80211_del_beacon(bss);
-	bss->beacon_set = 0;
 	return 0;
 }
 
@@ -10078,6 +10116,87 @@
 }
 
 
+#ifdef CONFIG_IEEE80211AX
+static int nl80211_switch_color(void *priv, struct cca_settings *settings)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *beacon_cca;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Color change request (cca_count=%u color=%d)",
+		   settings->cca_count, settings->cca_color);
+
+	if (drv->nlmode != NL80211_IFTYPE_AP)
+		return -EOPNOTSUPP;
+
+	if (!settings->beacon_cca.tail)
+		return -EINVAL;
+
+	if (settings->beacon_cca.tail_len <= settings->counter_offset_beacon ||
+	    settings->beacon_cca.tail[settings->counter_offset_beacon] !=
+	    settings->cca_count)
+		return -EINVAL;
+
+	if (settings->beacon_cca.probe_resp &&
+	    (settings->beacon_cca.probe_resp_len <=
+	     settings->counter_offset_presp ||
+	     settings->beacon_cca.probe_resp[settings->counter_offset_presp] !=
+	     settings->cca_count))
+		return -EINVAL;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_COLOR_CHANGE_REQUEST);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COUNT,
+		       settings->cca_count) ||
+	    nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COLOR,
+		       settings->cca_color))
+		goto error;
+
+	/* beacon_after params */
+	ret = set_beacon_data(msg, &settings->beacon_after);
+	if (ret)
+		goto error;
+
+	/* beacon_csa params */
+	beacon_cca = nla_nest_start(msg, NL80211_ATTR_COLOR_CHANGE_ELEMS);
+	if (!beacon_cca) {
+		ret = -ENOBUFS;
+		goto error;
+	}
+
+	ret = set_beacon_data(msg, &settings->beacon_cca);
+	if (ret)
+		goto error;
+
+	if (nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_BEACON,
+			settings->counter_offset_beacon) ||
+	    (settings->beacon_cca.probe_resp &&
+	     nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_PRESP,
+			 settings->counter_offset_presp))) {
+		ret = -ENOBUFS;
+		goto error;
+	}
+
+	nla_nest_end(msg, beacon_cca);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: switch_color failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+
+error:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Could not build color switch request");
+	return ret;
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
 static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
 			  u8 user_priority, u16 admitted_time)
 {
@@ -10547,17 +10666,17 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int new_addr = addr != NULL;
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	struct nl_msg *msg;
 	struct nlattr *params;
 	int ret;
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 	wpa_printf(MSG_DEBUG, "Enter: %s", __FUNCTION__);
 
 	if (TEST_FAIL())
 		return -1;
 	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 		if (!addr ) {
 			addr = drv->global->p2p_perm_addr;
 		}
@@ -10584,7 +10703,7 @@
 		return ret;
 #else
 		return -ENOTSUP;
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 	}
 	if (!addr)
 		addr = drv->perm_addr;
@@ -11233,6 +11352,8 @@
 	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
 	    (params->vht_enabled &&
 	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+	    (params->eht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED)) ||
 	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
 			params->ch_width) ||
 	    add_acs_ch_list(msg, params->freq_list) ||
@@ -11245,9 +11366,10 @@
 	nla_nest_end(msg, data);
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d EDMG: %d",
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d",
 		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
-		   params->vht_enabled, params->ch_width, params->edmg_enabled);
+		   params->vht_enabled, params->eht_enabled, params->ch_width,
+		   params->edmg_enabled);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
@@ -11889,20 +12011,22 @@
 
 static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
 {
-#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM)
+#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM) \
+	|| defined(CONFIG_DRIVER_NL80211_SYNA)
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM \
+	  || defined(CONFIG_DRIVER_NL80211_SYNA) */
 
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	if (drv->qca_do_acs)
 		return nl80211_qca_do_acs(drv, params);
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (drv->brcm_do_acs)
 		return wpa_driver_do_broadcom_acs(drv, params);
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 	return -1;
 }
@@ -12395,6 +12519,9 @@
 	.get_survey = wpa_driver_nl80211_get_survey,
 	.status = wpa_driver_nl80211_status,
 	.switch_channel = nl80211_switch_channel,
+#ifdef CONFIG_IEEE80211AX
+	.switch_color = nl80211_switch_color,
+#endif /* CONFIG_IEEE80211AX */
 #ifdef ANDROID_P2P
 	.set_noa = wpa_driver_set_p2p_noa,
 	.get_noa = wpa_driver_get_p2p_noa,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index dcf482a..8ad7184 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -228,9 +228,9 @@
 	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
 	 */
 	int last_scan_cmd;
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	unsigned int vendor_set_pmk:1; /* for legacy set_pmk method before NL80211_CMD_SET_PMK */
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	bool roam_indication_done;
 	u8 *pending_roam_data;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 392b0c6..75df36c 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -664,6 +664,10 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
 		capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_RADAR_BACKGROUND))
+		capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
 }
 
 
@@ -919,8 +923,15 @@
 	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
 			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
 
-	if (tb[NL80211_ATTR_DEVICE_AP_SME])
+	if (tb[NL80211_ATTR_DEVICE_AP_SME]) {
+		u32 ap_sme_features_flags =
+			nla_get_u32(tb[NL80211_ATTR_DEVICE_AP_SME]);
+
+		if (ap_sme_features_flags & NL80211_AP_SME_SA_QUERY_OFFLOAD)
+			capa->flags2 |= WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP;
+
 		info->device_ap_sme = 1;
+	}
 
 	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
 	wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
@@ -1020,7 +1031,7 @@
 					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 			} else if (vinfo->vendor_id == OUI_BRCM) {
 				switch (vinfo->subcmd) {
 				case BRCM_VENDOR_SCMD_ACS:
@@ -1038,7 +1049,7 @@
 				default:
 					break;
 				}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 			}
 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
 				   vinfo->vendor_id, vinfo->subcmd);
@@ -1335,7 +1346,7 @@
 	drv->has_capability = 1;
 	drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
 
-	/* Fallback to hardcoded defaults if the driver does nott advertize any
+	/* Fallback to hardcoded defaults if the driver does not advertise any
 	 * AKM capabilities. */
 	if (!drv->has_driver_key_mgmt) {
 		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
@@ -1772,12 +1783,14 @@
 }
 
 
-static void phy_info_iftype_copy(struct he_capabilities *he_capab,
+static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
 				 enum ieee80211_op_mode opmode,
 				 struct nlattr **tb, struct nlattr **tb_flags)
 {
 	enum nl80211_iftype iftype;
 	size_t len;
+	struct he_capabilities *he_capab = &mode->he_capab[opmode];
+	struct eht_capabilities *eht_capab = &mode->eht_capab[opmode];
 
 	switch (opmode) {
 	case IEEE80211_MODE_INFRA:
@@ -1847,6 +1860,47 @@
 		capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
 		he_capab->he_6ghz_capa = le_to_host16(capa);
 	}
+
+	if (!tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] ||
+	    !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY])
+		return;
+
+	eht_capab->eht_supported = true;
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] &&
+	    nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]) >= 2) {
+		    const u8 *pos;
+
+		    pos = nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]);
+		    eht_capab->mac_cap = WPA_GET_LE16(pos);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]);
+		if (len > sizeof(eht_capab->phy_cap))
+			len = sizeof(eht_capab->phy_cap);
+		os_memcpy(eht_capab->phy_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]);
+		if (len > sizeof(eht_capab->mcs))
+			len = sizeof(eht_capab->mcs);
+		os_memcpy(eht_capab->mcs,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]);
+		if (len > sizeof(eht_capab->ppet))
+			len = sizeof(eht_capab->ppet);
+		os_memcpy(&eht_capab->ppet,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
+			  len);
+	}
 }
 
 
@@ -1868,7 +1922,7 @@
 		return NL_STOP;
 
 	for (i = 0; i < IEEE80211_MODE_NUM; i++)
-		phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags);
+		phy_info_iftype_copy(mode, i, tb, tb_flags);
 
 	return NL_OK;
 }
@@ -2430,7 +2484,7 @@
 
 	for (i = 0; i < num_modes; i++) {
 		struct hostapd_hw_modes *mode = &modes[i];
-		char str[200];
+		char str[1000];
 		char *pos = str;
 		char *end = pos + sizeof(str);
 		int j, res;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 177c31d..5c103a4 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -171,6 +171,13 @@
 	C2S(NL80211_CMD_UNPROT_BEACON)
 	C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS)
 	C2S(NL80211_CMD_SET_SAR_SPECS)
+	C2S(NL80211_CMD_OBSS_COLOR_COLLISION)
+	C2S(NL80211_CMD_COLOR_CHANGE_REQUEST)
+	C2S(NL80211_CMD_COLOR_CHANGE_STARTED)
+	C2S(NL80211_CMD_COLOR_CHANGE_ABORTED)
+	C2S(NL80211_CMD_COLOR_CHANGE_COMPLETED)
+	C2S(NL80211_CMD_SET_FILS_AAD)
+	C2S(NL80211_CMD_ASSOC_COMEBACK)
 	C2S(__NL80211_CMD_AFTER_LAST)
 	}
 #undef C2S
@@ -2410,7 +2417,7 @@
 }
 
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 
 static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
 				       const u8 *data, size_t len)
@@ -2488,7 +2495,7 @@
 	}
 }
 
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 
 static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
@@ -2535,11 +2542,11 @@
 	case OUI_QCA:
 		nl80211_vendor_event_qca(drv, subcmd, data, len);
 		break;
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	case OUI_BRCM:
 		nl80211_vendor_event_brcm(drv, subcmd, data, len);
 		break;
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
 		break;
@@ -2822,7 +2829,8 @@
 				   nla_len(tb[NL80211_ATTR_FRAME]));
 		break;
 	default:
-		wpa_printf(MSG_INFO, "nl80211: Unxpected ethertype 0x%04x from "
+		wpa_printf(MSG_INFO,
+			   "nl80211: Unexpected ethertype 0x%04x from "
 			   MACSTR " over control port",
 			   ethertype, MAC2STR(src_addr));
 		break;
@@ -2871,9 +2879,12 @@
 		}
 	}
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: TX frame wait expired for cookie 0x%llx%s",
+		   "nl80211: TX frame wait expired for cookie 0x%llx%s%s",
 		   (long long unsigned int) cookie,
-		   match ? " (match)" : "");
+		   match ? " (match)" : "",
+		   drv->send_frame_cookie == cookie ? " (match-saved)" : "");
+	if (drv->send_frame_cookie == cookie)
+		drv->send_frame_cookie = (u64) -1;
 	if (!match)
 		return;
 
@@ -2887,6 +2898,69 @@
 }
 
 
+static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
+				   struct nlattr *mac, struct nlattr *timeout)
+{
+	if (!mac || !timeout)
+		return;
+	wpa_printf(MSG_DEBUG, "nl80211: Association comeback requested by "
+		   MACSTR " (timeout: %u ms)",
+		   MAC2STR((u8 *) nla_data(mac)), nla_get_u32(timeout));
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+
+static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+					 struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	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_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
 static void do_process_drv_event(struct i802_bss *bss, int cmd,
 				 struct nlattr **tb)
 {
@@ -3136,6 +3210,24 @@
 	case NL80211_CMD_FRAME_WAIT_CANCEL:
 		nl80211_frame_wait_cancel(drv, tb[NL80211_ATTR_COOKIE]);
 		break;
+	case NL80211_CMD_ASSOC_COMEBACK:
+		nl80211_assoc_comeback(drv, tb[NL80211_ATTR_MAC],
+				       tb[NL80211_ATTR_TIMEOUT]);
+		break;
+#ifdef CONFIG_IEEE80211AX
+	case NL80211_CMD_OBSS_COLOR_COLLISION:
+		nl80211_obss_color_collision(drv, tb);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_STARTED:
+		nl80211_color_change_announcement_started(drv);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_ABORTED:
+		nl80211_color_change_announcement_aborted(drv);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+		nl80211_color_change_announcement_completed(drv);
+		break;
+#endif /* CONFIG_IEEE80211AX */
 	default:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
 			"(cmd=%d)", cmd);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 1316084..b82e5af 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -713,6 +713,7 @@
 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
 		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
 		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_BEACON_TSF] = { .type = NLA_U64 },
 		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
 		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
 		[NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
@@ -774,8 +775,10 @@
 		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
 	if (bss[NL80211_BSS_BEACON_TSF]) {
 		u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
-		if (tsf > r->tsf)
+		if (tsf > r->tsf) {
 			r->tsf = tsf;
+			r->beacon_newer = true;
+		}
 	}
 	if (bss[NL80211_BSS_SEEN_MS_AGO])
 		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index a03d4a0..0186099 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,6 +30,10 @@
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
 endif
 
+ifdef CONFIG_DRIVER_NL80211_SYNA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_SYNA
+endif
+
 ifdef CONFIG_DRIVER_MACSEC_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
 DRV_OBJS += ../src/drivers/driver_macsec_qca.o
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 10eab6a..8c58456 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -44,6 +44,9 @@
 ifdef CONFIG_DRIVER_NL80211_BRCM
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
 endif
+ifdef CONFIG_DRIVER_NL80211_SYNA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_SYNA
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c
index 93673a3..4d4ec81 100644
--- a/src/drivers/ndis_events.c
+++ b/src/drivers/ndis_events.c
@@ -372,8 +372,9 @@
 				  L"MSNdis_NotifyAdapterRemoval") == 0) {
 			ndis_events_adapter_removal(events);
 		} else {
-			wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
-				   "'%S'", vtClass.bstrVal);
+			wpa_printf(MSG_DEBUG,
+				   "Unexpected event - __CLASS: '%S'",
+				   vtClass.bstrVal);
 		}
 
 		VariantClear(&vtClass);
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index f962c06..0568a79 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -11,7 +11,7 @@
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
  * Copyright 2015-2017	Intel Deutschland GmbH
- * Copyright (C) 2018-2020 Intel Corporation
+ * Copyright (C) 2018-2022 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -301,6 +301,29 @@
  */
 
 /**
+ * DOC: FILS shared key crypto offload
+ *
+ * This feature is applicable to drivers running in AP mode.
+ *
+ * FILS shared key crypto offload can be advertised by drivers by setting
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD flag. The drivers that support
+ * FILS shared key crypto offload should be able to encrypt and decrypt
+ * association frames for FILS shared key authentication as per IEEE 802.11ai.
+ * With this capability, for FILS key derivation, drivers depend on userspace.
+ *
+ * After FILS key derivation, userspace shares the FILS AAD details with the
+ * driver and the driver stores the same to use in decryption of association
+ * request and in encryption of association response. The below parameters
+ * should be given to the driver in %NL80211_CMD_SET_FILS_AAD.
+ *	%NL80211_ATTR_MAC - STA MAC address, used for storing FILS AAD per STA
+ *	%NL80211_ATTR_FILS_KEK - Used for encryption or decryption
+ *	%NL80211_ATTR_FILS_NONCES - Used for encryption or decryption
+ *			(STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * Once the association is done, the driver cleans the FILS AAD data.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -337,7 +360,10 @@
  * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
  *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
  *	userspace to request deletion of a virtual interface, then requires
- *	attribute %NL80211_ATTR_IFINDEX.
+ *	attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are
+ *	enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
+ *	and if this command is used for the transmitting interface, then all
+ *	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.
@@ -1185,6 +1211,32 @@
  *	passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
  *	specify the wiphy index to be applied to.
  *
+ * @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever
+ *	mac80211/drv detects a bss color collision.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that
+ *	userspace wants to change the BSS color.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has
+ *	started
+ *
+ * @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has
+ *	been aborted
+ *
+ * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
+ *	has completed
+ *
+ * @NL80211_CMD_SET_FILS_AAD: Set FILS AAD data to the driver using -
+ *	&NL80211_ATTR_MAC - for STA MAC address
+ *	&NL80211_ATTR_FILS_KEK - for KEK
+ *	&NL80211_ATTR_FILS_NONCES - for FILS Nonces
+ *		(STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * @NL80211_CMD_ASSOC_COMEBACK: notification about an association
+ *      temporal rejection with comeback. The event includes %NL80211_ATTR_MAC
+ *      to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
+ *      specify the timeout value.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1417,6 +1469,18 @@
 
 	NL80211_CMD_SET_SAR_SPECS,
 
+	NL80211_CMD_OBSS_COLOR_COLLISION,
+
+	NL80211_CMD_COLOR_CHANGE_REQUEST,
+
+	NL80211_CMD_COLOR_CHANGE_STARTED,
+	NL80211_CMD_COLOR_CHANGE_ABORTED,
+	NL80211_CMD_COLOR_CHANGE_COMPLETED,
+
+	NL80211_CMD_SET_FILS_AAD,
+
+	NL80211_CMD_ASSOC_COMEBACK,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2413,7 +2477,9 @@
  *	space supports external authentication. This attribute shall be used
  *	with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
  *	may offload authentication processing to user space if this capability
- *	is indicated in the respective requests from the user space.
+ *	is indicated in the respective requests from the user space. (This flag
+ *	attribute deprecated for %NL80211_CMD_START_AP, use
+ *	%NL80211_ATTR_AP_SETTINGS_FLAGS)
  *
  * @NL80211_ATTR_NSS: Station's New/updated  RX_NSS value notified using this
  *	u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
@@ -2560,6 +2626,43 @@
  *	disassoc events to indicate that an immediate reconnect to the AP
  *	is desired.
  *
+ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+ *	%NL80211_CMD_OBSS_COLOR_COLLISION event.
+ *
+ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+ *	until the color switch event.
+ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+ *	switching to
+ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+ *	information for the time while performing a color switch.
+ *
+ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
+ *	advertisements (MBSSID) parameters in AP mode.
+ *	Kernel uses this attribute to indicate the driver's support for MBSSID
+ *	and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
+ *	Userspace should use this attribute to configure per interface MBSSID
+ *	parameters.
+ *	See &enum nl80211_mbssid_config_attributes for details.
+ *
+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
+ *	Mandatory parameter for the transmitting interface to enable MBSSID.
+ *	Optional for the non-transmitting interfaces.
+ *
+ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain
+ *	available for radar/CAC detection on some hw. This chain can't be used
+ *	to transmit or receive frames and it is bounded to a running wdev.
+ *	Background radar/CAC detection allows to avoid the CAC downtime
+ *	switching on a different channel during CAC detection on the selected
+ *	radar channel.
+ *
+ * @NL80211_ATTR_AP_SETTINGS_FLAGS: u32 attribute contains ap settings flags,
+ *	enumerated in &enum nl80211_ap_settings_flags. This attribute shall be
+ *	used with %NL80211_CMD_START_AP request.
+ *
+ * @NL80211_ATTR_EHT_CAPABILITY: EHT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION). Can be set
+ *	only if %NL80211_STA_FLAG_WME is set.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3057,6 +3160,21 @@
 
 	NL80211_ATTR_DISABLE_HE,
 
+	NL80211_ATTR_OBSS_COLOR_BITMAP,
+
+	NL80211_ATTR_COLOR_CHANGE_COUNT,
+	NL80211_ATTR_COLOR_CHANGE_COLOR,
+	NL80211_ATTR_COLOR_CHANGE_ELEMS,
+
+	NL80211_ATTR_MBSSID_CONFIG,
+	NL80211_ATTR_MBSSID_ELEMS,
+
+	NL80211_ATTR_RADAR_BACKGROUND,
+
+	NL80211_ATTR_AP_SETTINGS_FLAGS,
+
+	NL80211_ATTR_EHT_CAPABILITY,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3112,6 +3230,8 @@
 #define NL80211_HE_MAX_CAPABILITY_LEN           54
 #define NL80211_MAX_NR_CIPHER_SUITES		5
 #define NL80211_MAX_NR_AKM_SUITES		2
+#define NL80211_EHT_MIN_CAPABILITY_LEN          13
+#define NL80211_EHT_MAX_CAPABILITY_LEN          51
 
 #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME	10
 
@@ -3139,7 +3259,7 @@
  *	and therefore can't be created in the normal ways, use the
  *	%NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
  *	commands to create and destroy one
- * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ * @NL80211_IFTYPE_OCB: Outside Context of a BSS
  *	This mode corresponds to the MIB variable dot11OCBActivated=true
  * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
@@ -3281,6 +3401,56 @@
 };
 
 /**
+ * enum nl80211_eht_gi - EHT guard interval
+ * @NL80211_RATE_INFO_EHT_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_EHT_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_EHT_GI_3_2: 3.2 usec
+ */
+enum nl80211_eht_gi {
+	NL80211_RATE_INFO_EHT_GI_0_8,
+	NL80211_RATE_INFO_EHT_GI_1_6,
+	NL80211_RATE_INFO_EHT_GI_3_2,
+};
+
+/**
+ * enum nl80211_eht_ru_alloc - EHT RU allocation values
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52P26: 52+26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106P26: 106+26 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484P242: 484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484: 996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242: 996+484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996: 2x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484: 2x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996: 3x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484: 3x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_4x996: 4x996-tone RU allocation
+ */
+enum nl80211_eht_ru_alloc {
+	NL80211_RATE_INFO_EHT_RU_ALLOC_26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_52,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_52P26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_106,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_106P26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_484P242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_2x996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_3x996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_4x996,
+};
+
+/**
  * enum nl80211_rate_info - bitrate information
  *
  * These attribute types are used with %NL80211_STA_INFO_TXRATE
@@ -3319,6 +3489,13 @@
  * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
  * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
  *	non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+ * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+ * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+ * @NL80211_RATE_INFO_EHT_NSS: EHT NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_EHT_GI: EHT guard interval identifier
+ *	(u8, see &enum nl80211_eht_gi)
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
+ *	non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -3340,6 +3517,11 @@
 	NL80211_RATE_INFO_HE_GI,
 	NL80211_RATE_INFO_HE_DCM,
 	NL80211_RATE_INFO_HE_RU_ALLOC,
+	NL80211_RATE_INFO_320_MHZ_WIDTH,
+	NL80211_RATE_INFO_EHT_MCS,
+	NL80211_RATE_INFO_EHT_NSS,
+	NL80211_RATE_INFO_EHT_GI,
+	NL80211_RATE_INFO_EHT_RU_ALLOC,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -3650,11 +3832,20 @@
  *     capabilities IE
  * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
  *     defined in HE capabilities IE
- * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently
- *     defined
  * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
  *	given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
+ *	advertised on this band/for this iftype (binary)
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: EHT PHY capabilities as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: EHT supported NSS/MCS as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
+ *	defined in EHT capabilities element
  * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
+ * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
  */
 enum nl80211_band_iftype_attr {
 	__NL80211_BAND_IFTYPE_ATTR_INVALID,
@@ -3665,6 +3856,11 @@
 	NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
 	NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
 	NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
+	NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
 
 	/* keep last */
 	__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -3809,6 +4005,10 @@
  *	on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed
  *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_320MHZ: any 320 MHz channel using this channel
+ *	as the primary or any of the secondary channels isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
+ *	in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -3845,6 +4045,8 @@
 	NL80211_FREQUENCY_ATTR_4MHZ,
 	NL80211_FREQUENCY_ATTR_8MHZ,
 	NL80211_FREQUENCY_ATTR_16MHZ,
+	NL80211_FREQUENCY_ATTR_NO_320MHZ,
+	NL80211_FREQUENCY_ATTR_NO_EHT,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4043,6 +4245,7 @@
  * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
  * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
  * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
  */
 enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_OFDM		= 1<<0,
@@ -4061,6 +4264,7 @@
 	NL80211_RRF_NO_80MHZ		= 1<<15,
 	NL80211_RRF_NO_160MHZ		= 1<<16,
 	NL80211_RRF_NO_HE		= 1<<17,
+	NL80211_RRF_NO_320MHZ		= 1<<18,
 };
 
 #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
@@ -4558,6 +4762,8 @@
  * @NL80211_CHAN_WIDTH_4: 4 MHz OFDM channel
  * @NL80211_CHAN_WIDTH_8: 8 MHz OFDM channel
  * @NL80211_CHAN_WIDTH_16: 16 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_320: 320 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
  */
 enum nl80211_chan_width {
 	NL80211_CHAN_WIDTH_20_NOHT,
@@ -4573,6 +4779,7 @@
 	NL80211_CHAN_WIDTH_4,
 	NL80211_CHAN_WIDTH_8,
 	NL80211_CHAN_WIDTH_16,
+	NL80211_CHAN_WIDTH_320,
 };
 
 /**
@@ -4887,6 +5094,7 @@
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
  * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
  * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
  * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
  *	since newer kernel versions may support more bands
  */
@@ -4896,6 +5104,7 @@
 	NL80211_BAND_60GHZ,
 	NL80211_BAND_6GHZ,
 	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
 
 	NUM_NL80211_BANDS,
 };
@@ -5462,7 +5671,7 @@
  *	=> allows 8 of AP/GO that can have BI gcd >= min gcd
  *
  *	numbers = [ #{STA} <= 2 ], channels = 2, max = 2
- *	=> allows two STAs on different channels
+ *	=> allows two STAs on the same or on different channels
  *
  *	numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
  *	=> allows a STA plus three P2P interfaces
@@ -5507,7 +5716,7 @@
  * @NL80211_PLINK_ESTAB: mesh peer link is established
  * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
  * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
- *	plink are discarded
+ *	plink are discarded, except for authentication frames
  * @NUM_NL80211_PLINK_STATES: number of peer link states
  * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
  */
@@ -5644,13 +5853,15 @@
 	NL80211_TDLS_DISABLE_LINK,
 };
 
-/*
+/**
  * enum nl80211_ap_sme_features - device-integrated AP features
- * Reserved for future use, no bits are defined in
- * NL80211_ATTR_DEVICE_AP_SME yet.
-enum nl80211_ap_sme_features {
-};
+ * @NL80211_AP_SME_SA_QUERY_OFFLOAD: SA Query procedures offloaded to driver
+ *	when user space indicates support for SA Query procedures offload during
+ *	"start ap" with %NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT.
  */
+enum nl80211_ap_sme_features {
+	NL80211_AP_SME_SA_QUERY_OFFLOAD		= 1 << 0,
+};
 
 /**
  * enum nl80211_feature_flags - device/driver features
@@ -5950,6 +6161,17 @@
  *      frame protection for all management frames exchanged during the
  *      negotiation and range measurement procedure.
  *
+ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+ *	detection and change announcemnts.
+ *
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
+ *	FILS encryption and decryption for (Re)Association Request and Response
+ *	frames. Userspace has to share FILS AAD details to the driver by using
+ *	@NL80211_CMD_SET_FILS_AAD.
+ *
+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
+ *	detection.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -6014,6 +6236,9 @@
 	NL80211_EXT_FEATURE_SECURE_LTF,
 	NL80211_EXT_FEATURE_SECURE_RTT,
 	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+	NL80211_EXT_FEATURE_BSS_COLOR,
+	NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
+	NL80211_EXT_FEATURE_RADAR_BACKGROUND,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -6912,6 +7137,9 @@
  * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
  *	valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
  *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
+ *	responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
+ *	or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
  *
  * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
  * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
@@ -6931,6 +7159,7 @@
 	NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
 	NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
 	NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
+	NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
 
 	/* keep last */
 	NUM_NL80211_PMSR_FTM_REQ_ATTR,
@@ -7299,4 +7528,76 @@
 	NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
 };
 
+/**
+ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
+ * multi-BSSID advertisements (EMA) in AP mode.
+ * Kernel uses some of these attributes to advertise driver's support for
+ * MBSSID and EMA.
+ * Remaining attributes should be used by the userspace to configure the
+ * features.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
+ *	the maximum number of MBSSID interfaces supported by the driver.
+ *	Driver should indicate MBSSID support by setting
+ *	wiphy->mbssid_max_interfaces to a value more than or equal to 2.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
+ *	to advertise the maximum profile periodicity supported by the driver
+ *	if EMA is enabled. Driver should indicate EMA support to the userspace
+ *	by setting wiphy->ema_max_profile_periodicity to
+ *	a non-zero value.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
+ *	this BSS (u8) in the multiple BSSID set.
+ *	Value must be set to 0 for the transmitting interface and non-zero for
+ *	all non-transmitting interfaces. The userspace will be responsible
+ *	for using unique indices for the interfaces.
+ *	Range: 0 to wiphy->mbssid_max_interfaces-1.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
+ *	a non-transmitted profile which provides the interface index (u32) of
+ *	the transmitted profile. The value must match one of the interface
+ *	indices advertised by the kernel. Optional if the interface being set up
+ *	is the transmitting one, however, if provided then the value must match
+ *	the interface index of the same.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
+ *	Setting this flag is permitted only if the driver advertises EMA support
+ *	by setting wiphy->ema_max_profile_periodicity to non-zero.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
+ */
+enum nl80211_mbssid_config_attributes {
+	__NL80211_MBSSID_CONFIG_ATTR_INVALID,
+
+	NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
+	NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
+	NL80211_MBSSID_CONFIG_ATTR_INDEX,
+	NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+	NL80211_MBSSID_CONFIG_ATTR_EMA,
+
+	/* keep last */
+	__NL80211_MBSSID_CONFIG_ATTR_LAST,
+	NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_ap_settings_flags - AP settings flags
+ *
+ * @NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT: AP supports external
+ *	authentication.
+ * @NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT: Userspace supports SA Query
+ *	procedures offload to driver. If driver advertises
+ *	%NL80211_AP_SME_SA_QUERY_OFFLOAD in AP SME features, userspace shall
+ *	ignore SA Query procedures and validations when this flag is set by
+ *	userspace.
+ */
+enum nl80211_ap_settings_flags {
+	NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT	= 1 << 0,
+	NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT	= 1 << 1,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index 70999c4..3346ec5 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -72,7 +72,7 @@
 	EAP_TYPE_MD5 = 4, /* RFC 3748 */
 	EAP_TYPE_OTP = 5 /* RFC 3748 */,
 	EAP_TYPE_GTC = 6, /* RFC 3748 */
-	EAP_TYPE_TLS = 13 /* RFC 2716 */,
+	EAP_TYPE_TLS = 13 /* RFC 5216 */,
 	EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
 	EAP_TYPE_SIM = 18 /* RFC 4186 */,
 	EAP_TYPE_TTLS = 21 /* RFC 5281 */,
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 5fd370f..269d719 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1684,6 +1684,7 @@
 	struct wpabuf *resp;
 	const u8 *identity;
 	size_t identity_len;
+	struct wpabuf *privacy_identity = NULL;
 
 	if (config == NULL) {
 		wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
@@ -1706,6 +1707,30 @@
 		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 &&
+		   config->identity_len > 0) {
+		const u8 *pos = config->identity;
+		const u8 *end = config->identity + config->identity_len;
+
+		privacy_identity = wpabuf_alloc(9 + config->identity_len);
+		if (!privacy_identity)
+			return NULL;
+
+		/* Include method prefix */
+		if (*pos == '0' || *pos == '1' || *pos == '6')
+			wpabuf_put_u8(privacy_identity, *pos);
+		wpabuf_put_str(privacy_identity, "anonymous");
+
+		/* Include realm */
+		while (pos < end && *pos != '@')
+			pos++;
+		wpabuf_put_data(privacy_identity, pos, end - pos);
+
+		identity = wpabuf_head(privacy_identity);
+		identity_len = wpabuf_len(privacy_identity);
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP: using IMSI privacy anonymous identity",
+				  identity, identity_len);
 	} else {
 		identity = config->identity;
 		identity_len = config->identity_len;
@@ -1742,6 +1767,7 @@
 		return NULL;
 
 	wpabuf_put_data(resp, identity, identity_len);
+	wpabuf_free(privacy_identity);
 
 	return resp;
 }
@@ -2157,6 +2183,11 @@
 			eap_notify_status(sm, "remote TLS alert",
 					  data->alert.description);
 		break;
+	case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+		wpa_printf(MSG_INFO,
+			   "TLS handshake failed due to the server not supporting safe renegotiation (RFC 5746); phase1 parameter allow_unsafe_renegotiation=1 can be used to work around this");
+		eap_notify_status(sm, "unsafe server renegotiation", "failure");
+		break;
 	}
 
 	os_free(hash_hex);
@@ -2785,6 +2816,63 @@
 	return config->identity;
 }
 
+static const u8 * strnchr(const u8 *str, size_t len, u8 needle) {
+	const u8 *cur = str;
+
+	if (NULL == str) return NULL;
+	if (0 >= len) return NULL;
+
+	while (cur < str + len) {
+		if (*cur == needle)
+			return cur;
+		cur++;
+	}
+	return NULL;
+}
+
+const u8 * eap_get_config_realm(struct eap_sm *sm, size_t *len) {
+	struct eap_peer_config *config = eap_get_config(sm);
+	const u8 *realm = NULL;
+	size_t realm_len = 0;
+	const u8 *identity = NULL;
+	size_t identity_len = 0;
+
+	if (!config)
+		return NULL;
+
+	/* Look for the realm of the permanent identity */
+	identity = eap_get_config_identity(sm, &identity_len);
+	realm = strnchr(identity, identity_len, '@');
+	if (NULL != realm) {
+		wpa_printf(MSG_DEBUG, "Get the realm from identity.");
+		*len = identity_len - (realm - identity);
+		return realm;
+	}
+
+	/* Look for the realm of the anonymous identity. */
+	identity = config->anonymous_identity;
+	identity_len = config->anonymous_identity_len;
+	realm = strnchr(identity, identity_len, '@');
+	if (NULL != realm) {
+		wpa_printf(MSG_DEBUG, "Get the realm from anonymous identity.");
+		*len = identity_len - (realm - identity);
+		return realm;
+	}
+
+	/* Look for the realm of the real identity. */
+	identity = config->imsi_identity;
+	identity_len = config->imsi_identity_len;
+	realm = strnchr(identity, identity_len, '@');
+	if (NULL != realm) {
+		wpa_printf(MSG_DEBUG, "Get the realm from IMSI identity.");
+		*len = identity_len - (realm - identity);
+		return realm;
+	}
+	wpa_printf(MSG_DEBUG, "No realm information in identities.");
+	*len = 0;
+	return NULL;
+}
+
 
 static int eap_get_ext_password(struct eap_sm *sm,
 				struct eap_peer_config *config)
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index ee7010d..e721efe 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/base64.h"
 #include "pcsc_funcs.h"
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
@@ -58,6 +59,7 @@
 	u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
 	size_t last_kdf_count;
 	int error_code;
+	struct crypto_rsa_key *imsi_privacy_key;
 };
 
 
@@ -101,6 +103,25 @@
 
 	data->eap_method = EAP_TYPE_AKA;
 
+	if (config && config->imsi_privacy_key) {
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		data->imsi_privacy_key = crypto_rsa_key_read(
+			config->imsi_privacy_key, false);
+		if (!data->imsi_privacy_key) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-AKA: Failed to read/parse IMSI privacy key %s",
+				   config->imsi_privacy_key);
+			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");
+		os_free(data);
+		return NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+	}
+
 	/* Zero is a valid error code, so we need to initialize */
 	data->error_code = NO_EAP_METHOD_ERROR;
 
@@ -160,6 +181,9 @@
 		wpabuf_free(data->id_msgs);
 		os_free(data->network_name);
 		eap_aka_clear_keys(data, 0);
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		crypto_rsa_key_free(data->imsi_privacy_key);
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 		os_free(data);
 	}
 }
@@ -385,33 +409,16 @@
 		size_t identity_len = 0;
 		const u8 *realm = NULL;
 		size_t realm_len = 0;
-		struct eap_peer_config *config = eap_get_config(sm);
 
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
 				  attr->next_pseudonym,
 				  attr->next_pseudonym_len);
 		os_free(data->pseudonym);
-		/* Look for the realm of the permanent identity */
-		identity = eap_get_config_identity(sm, &identity_len);
-		if (identity) {
-			for (realm = identity, realm_len = identity_len;
-			     realm_len > 0; realm_len--, realm++) {
-				if (*realm == '@')
-					break;
-			}
-		}
-		// If no realm from the permanent identity, look for the
-		// realm of the anonymous identity.
-		if (realm_len == 0 && config && config->anonymous_identity
-		    && config->anonymous_identity_len > 0) {
-			for (realm = config->anonymous_identity,
-			    realm_len = config->anonymous_identity_len;
-			    realm_len > 0; realm_len--, realm++) {
-				if (*realm == '@')
-					break;
-			}
-		}
+
+		/* Get realm from identities to decorate pseudonym. */
+		realm = eap_get_config_realm(sm, &realm_len);
+
 		data->pseudonym = os_malloc(attr->next_pseudonym_len +
 					    realm_len);
 		if (data->pseudonym == NULL) {
@@ -629,6 +636,47 @@
 }
 
 
+#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)
+{
+	struct wpabuf *imsi_buf, *enc;
+	char *b64;
+	size_t b64_len;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypt permanent identity",
+			  identity, identity_len);
+
+	imsi_buf = wpabuf_alloc_copy(identity, identity_len);
+	if (!imsi_buf)
+		return NULL;
+	enc = crypto_rsa_oaep_sha256_encrypt(imsi_privacy_key, imsi_buf);
+	wpabuf_free(imsi_buf);
+	if (!enc)
+		return NULL;
+
+	b64 = base64_encode_no_lf(wpabuf_head(enc), wpabuf_len(enc), &b64_len);
+	wpabuf_free(enc);
+	if (!b64)
+		return NULL;
+
+	enc = wpabuf_alloc(1 + b64_len);
+	if (!enc) {
+		os_free(b64);
+		return NULL;
+	}
+	wpabuf_put_u8(enc, '\0');
+	wpabuf_put_data(enc, b64, b64_len);
+	os_free(b64);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypted permanent identity",
+			  wpabuf_head(enc), wpabuf_len(enc));
+
+	return enc;
+}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+
 static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
 						 struct eap_aka_data *data,
 						 u8 id,
@@ -637,6 +685,7 @@
 	const u8 *identity = NULL;
 	size_t identity_len = 0;
 	struct eap_sim_msg *msg;
+	struct wpabuf *enc_identity = NULL;
 
 	data->reauth = 0;
 	if (id_req == ANY_ID && data->reauth_id) {
@@ -661,6 +710,22 @@
 				ids &= ~CLEAR_PSEUDONYM;
 			eap_aka_clear_identities(sm, data, ids);
 		}
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		if (identity && data->imsi_privacy_key) {
+			enc_identity = eap_aka_encrypt_identity(
+				data->imsi_privacy_key,
+				identity, identity_len);
+			if (!enc_identity) {
+				wpa_printf(MSG_INFO,
+					   "EAP-AKA: Failed to encrypt permanent identity");
+				return eap_aka_client_error(
+					data, id,
+					EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+			}
+			identity = wpabuf_head(enc_identity);
+			identity_len = wpabuf_len(enc_identity);
+		}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 	}
 	if (id_req != NO_ID_REQ)
 		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
@@ -675,6 +740,7 @@
 		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
 				identity, identity_len);
 	}
+	wpabuf_free(enc_identity);
 
 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 3238f74..eaf514b 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -104,24 +104,6 @@
 	char *private_key_passwd;
 
 	/**
-	 * dh_file - File path to DH/DSA parameters file (in PEM format)
-	 *
-	 * This is an optional configuration file for setting parameters for an
-	 * ephemeral DH key exchange. In most cases, the default RSA
-	 * authentication does not use this configuration. However, it is
-	 * possible setup RSA to use ephemeral DH key exchange. In addition,
-	 * ciphers with DSA keys always use ephemeral DH keys. This can be used
-	 * to achieve forward secrecy. If the file is in DSA parameters format,
-	 * it will be automatically converted into DH params. Full path to the
-	 * file should be used since working directory may change when
-	 * wpa_supplicant is run in the background.
-	 *
-	 * Alternatively, a named configuration blob can be used by setting
-	 * this to blob://blob_name.
-	 */
-	char *dh_file;
-
-	/**
 	 * subject_match - Constraint for server certificate subject
 	 *
 	 * This substring is matched against the subject of the authentication
@@ -336,6 +318,16 @@
 	size_t imsi_identity_len;
 
 	/**
+	 * imsi_privacy_key - IMSI privacy key (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.
+	 */
+	char *imsi_privacy_key;
+
+	/**
 	 * machine_identity - EAP Identity for machine credential
 	 *
 	 * This field is used to set the machine identity or NAI for cases where
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index f43891e..652b67e 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -392,6 +392,7 @@
 const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
 const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_realm(struct eap_sm *sm, size_t *len);
 void eap_clear_config_otp(struct eap_sm *sm);
 const char * eap_get_config_phase1(struct eap_sm *sm);
 const char * eap_get_config_phase2(struct eap_sm *sm);
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index de423e8..954c585 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -9,7 +9,9 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/base64.h"
 #include "pcsc_funcs.h"
+#include "crypto/crypto.h"
 #include "crypto/milenage.h"
 #include "crypto/random.h"
 #include "eap_peer/eap_i.h"
@@ -49,6 +51,7 @@
 	int result_ind, use_result_ind;
 	int use_pseudonym;
 	int error_code;
+	struct crypto_rsa_key *imsi_privacy_key;
 };
 
 
@@ -98,6 +101,25 @@
 		return NULL;
 	}
 
+	if (config && config->imsi_privacy_key) {
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		data->imsi_privacy_key = crypto_rsa_key_read(
+			config->imsi_privacy_key, false);
+		if (!data->imsi_privacy_key) {
+			wpa_printf(MSG_ERROR,
+				   "EAP-SIM: Failed to read/parse IMSI privacy key %s",
+				   config->imsi_privacy_key);
+			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");
+		os_free(data);
+		return NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+	}
+
 	/* Zero is a valid error code, so we need to initialize */
 	data->error_code = NO_EAP_METHOD_ERROR;
 
@@ -162,6 +184,9 @@
 		os_free(data->reauth_id);
 		os_free(data->last_eap_identity);
 		eap_sim_clear_keys(data, 0);
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		crypto_rsa_key_free(data->imsi_privacy_key);
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 		os_free(data);
 	}
 }
@@ -407,33 +432,16 @@
 		size_t identity_len = 0;
 		const u8 *realm = NULL;
 		size_t realm_len = 0;
-		struct eap_peer_config *config = eap_get_config(sm);
 
 		wpa_hexdump_ascii(MSG_DEBUG,
 				  "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
 				  attr->next_pseudonym,
 				  attr->next_pseudonym_len);
 		os_free(data->pseudonym);
-		/* Look for the realm of the permanent identity */
-		identity = eap_get_config_identity(sm, &identity_len);
-		if (identity) {
-			for (realm = identity, realm_len = identity_len;
-			     realm_len > 0; realm_len--, realm++) {
-				if (*realm == '@')
-					break;
-			}
-		}
-		// If no realm from the permanent identity, look for the
-		// realm of the anonymous identity.
-		if (realm_len == 0 && config && config->anonymous_identity
-		    && config->anonymous_identity_len > 0) {
-			for (realm = config->anonymous_identity,
-			    realm_len = config->anonymous_identity_len;
-			    realm_len > 0; realm_len--, realm++) {
-				if (*realm == '@')
-					break;
-			}
-		}
+
+		/* Get realm from identities to decorate pseudonym. */
+		realm = eap_get_config_realm(sm, &realm_len);
+
 		data->pseudonym = os_malloc(attr->next_pseudonym_len +
 					    realm_len);
 		if (data->pseudonym == NULL) {
@@ -493,6 +501,47 @@
 }
 
 
+#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)
+{
+	struct wpabuf *imsi_buf, *enc;
+	char *b64;
+	size_t b64_len;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypt permanent identity",
+			  identity, identity_len);
+
+	imsi_buf = wpabuf_alloc_copy(identity, identity_len);
+	if (!imsi_buf)
+		return NULL;
+	enc = crypto_rsa_oaep_sha256_encrypt(imsi_privacy_key, imsi_buf);
+	wpabuf_free(imsi_buf);
+	if (!enc)
+		return NULL;
+
+	b64 = base64_encode_no_lf(wpabuf_head(enc), wpabuf_len(enc), &b64_len);
+	wpabuf_free(enc);
+	if (!b64)
+		return NULL;
+
+	enc = wpabuf_alloc(1 + b64_len);
+	if (!enc) {
+		os_free(b64);
+		return NULL;
+	}
+	wpabuf_put_u8(enc, '\0');
+	wpabuf_put_data(enc, b64, b64_len);
+	os_free(b64);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypted permanent identity",
+			  wpabuf_head(enc), wpabuf_len(enc));
+
+	return enc;
+}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+
 static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
 					      struct eap_sim_data *data, u8 id,
 					      enum eap_sim_id_req id_req)
@@ -501,6 +550,7 @@
 	size_t identity_len = 0;
 	struct eap_sim_msg *msg;
 	struct wpabuf *resp;
+	struct wpabuf *enc_identity = NULL;
 
 	data->reauth = 0;
 	if (id_req == ANY_ID && data->reauth_id) {
@@ -525,6 +575,22 @@
 				ids &= ~CLEAR_PSEUDONYM;
 			eap_sim_clear_identities(sm, data, ids);
 		}
+#ifdef CRYPTO_RSA_OAEP_SHA256
+		if (identity && data->imsi_privacy_key) {
+			enc_identity = eap_sim_encrypt_identity(
+				data->imsi_privacy_key,
+				identity, identity_len);
+			if (!enc_identity) {
+				wpa_printf(MSG_INFO,
+					   "EAP-SIM: Failed to encrypt permanent identity");
+				return eap_sim_client_error(
+					data, id,
+					EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+			}
+			identity = wpabuf_head(enc_identity);
+			identity_len = wpabuf_len(enc_identity);
+		}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 	}
 	if (id_req != NO_ID_REQ)
 		eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
@@ -538,6 +604,7 @@
 		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
 				identity, identity_len);
 	}
+	wpabuf_free(enc_identity);
 	if (!data->reauth) {
 		wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
 			    data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index 0d479f1..4167e99 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,5 +1,5 @@
 /*
- * EAP peer method: EAP-TLS (RFC 2716)
+ * EAP peer method: EAP-TLS (RFC 5216, RFC 9190)
  * Copyright (c) 2004-2008, 2012-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
@@ -26,6 +26,7 @@
 	void *ssl_ctx;
 	u8 eap_type;
 	struct wpabuf *pending_resp;
+	bool prot_success_received;
 };
 
 
@@ -302,15 +303,20 @@
 		return NULL;
 	}
 
-	/* draft-ietf-emu-eap-tls13-13 Section 2.5 */
+	/* RFC 9190 Section 2.5 */
 	if (res == 2 && data->ssl.tls_v13 && wpabuf_len(resp) == 1 &&
 	    *wpabuf_head_u8(resp) == 0) {
-		wpa_printf(MSG_DEBUG, "EAP-TLS: ACKing Commitment Message");
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: ACKing protected success indication (appl data 0x00)");
 		eap_peer_tls_reset_output(&data->ssl);
 		res = 1;
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_UNCOND_SUCC;
+		data->prot_success_received = true;
 	}
 
-	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
+	if (tls_connection_established(data->ssl_ctx, data->ssl.conn) &&
+	    (!data->ssl.tls_v13 || data->prot_success_received))
 		eap_tls_success(sm, data, ret);
 
 	if (res == 1) {
@@ -335,6 +341,7 @@
 
 	wpabuf_free(data->pending_resp);
 	data->pending_resp = NULL;
+	data->prot_success_received = false;
 }
 
 
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 1aaca36..3050456 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -102,6 +102,10 @@
 		params->flags |= TLS_CONN_SUITEB_NO_ECDH;
 	if (os_strstr(txt, "tls_suiteb_no_ecdh=0"))
 		params->flags &= ~TLS_CONN_SUITEB_NO_ECDH;
+	if (os_strstr(txt, "allow_unsafe_renegotiation=1"))
+		params->flags |= TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION;
+	if (os_strstr(txt, "allow_unsafe_renegotiation=0"))
+		params->flags &= ~TLS_CONN_ALLOW_UNSAFE_RENEGOTIATION;
 }
 
 
@@ -113,7 +117,6 @@
 	params->client_cert = config->client_cert;
 	params->private_key = config->private_key;
 	params->private_key_passwd = config->private_key_passwd;
-	params->dh_file = config->dh_file;
 	params->subject_match = config->subject_match;
 	params->altsubject_match = config->altsubject_match;
 	params->check_cert_subject = config->check_cert_subject;
@@ -192,18 +195,20 @@
 		 * TLS v1.3 changes, so disable this by default for now. */
 		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
 	}
+#ifndef EAP_TLSV1_3
 	if (data->eap_type == EAP_TYPE_TLS ||
 	    data->eap_type == EAP_UNAUTH_TLS_TYPE ||
 	    data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) {
 		/* While the current EAP-TLS implementation is more or less
-		 * complete for TLS v1.3, there has been no interoperability
-		 * testing with other implementations, so disable for by default
-		 * for now until there has been chance to confirm that no
-		 * significant interoperability issues show up with TLS version
-		 * update.
+		 * complete for TLS v1.3, there has been only minimal
+		 * interoperability testing with other implementations, so
+		 * disable it by default for now until there has been chance to
+		 * confirm that no significant interoperability issues show up
+		 * with TLS version update.
 		 */
 		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
 	}
+#endif /* EAP_TLSV1_3 */
 	if (phase2 && sm->use_machine_cred) {
 		wpa_printf(MSG_DEBUG, "TLS: using machine config options");
 		eap_tls_params_from_conf2m(params, config);
@@ -228,9 +233,7 @@
 			       &params->client_cert_blob_len) ||
 	    eap_tls_check_blob(sm, &params->private_key,
 			       &params->private_key_blob,
-			       &params->private_key_blob_len) ||
-	    eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
-			       &params->dh_blob_len)) {
+			       &params->private_key_blob_len)) {
 		wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
 		return -1;
 	}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index c401915..c8e2de0 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -1473,11 +1473,11 @@
 		goto start;
 	}
 
-	/* draft-ietf-emu-eap-tls13-13 Section 2.5 */
+	/* RFC 9190 Section 2.5 */
 	if (data->ssl.tls_v13 && wpabuf_len(in_decrypted) == 1 &&
 	    *wpabuf_head_u8(in_decrypted) == 0) {
 		wpa_printf(MSG_DEBUG,
-			   "EAP-TTLS: ACKing EAP-TLS Commitment Message");
+			   "EAP-TLS: ACKing protected success indication (appl data 0x00)");
 		eap_peer_tls_reset_output(&data->ssl);
 		wpabuf_free(in_decrypted);
 		return 1;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 61032cc..2894cfb 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -124,6 +124,9 @@
 	 * callback context.
 	 */
 	void *eap_sim_db_priv;
+
+	struct crypto_rsa_key *imsi_privacy_key;
+
 	bool backend_auth;
 	int eap_server;
 
@@ -258,6 +261,10 @@
 
 	unsigned int max_auth_rounds;
 	unsigned int max_auth_rounds_short;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	bool skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 struct eap_session_data {
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index e9bf030..5fb19e9 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/base64.h"
 #include "crypto/sha256.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
@@ -737,11 +738,8 @@
 			  sm->identity, sm->identity_len);
 
 	username = sim_get_username(sm->identity, sm->identity_len);
-	if (username == NULL) {
-		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
-		eap_aka_state(data, NOTIFICATION);
-		return;
-	}
+	if (!username)
+		goto fail;
 
 	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
 		os_free(username);
@@ -785,16 +783,87 @@
 			   username);
 		os_strlcpy(data->permanent, username, sizeof(data->permanent));
 		os_free(username);
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	} else if (sm->identity_len > 1 && sm->identity[0] == '\0') {
+		char *enc_id, *pos, *end;
+		size_t enc_id_len;
+		u8 *decoded_id;
+		size_t decoded_id_len;
+		struct wpabuf *enc, *dec;
+		u8 *new_id;
+
+		os_free(username);
+		if (!sm->cfg->imsi_privacy_key) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-AKA: Received encrypted identity, but no IMSI privacy key configured to decrypt it");
+			goto fail;
+		}
+
+		enc_id = (char *) &sm->identity[1];
+		end = (char *) &sm->identity[sm->identity_len];
+		for (pos = enc_id; pos < end; pos++) {
+			if (*pos == ',')
+				break;
+		}
+		enc_id_len = pos - enc_id;
+
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-AKA: Encrypted permanent identity",
+				  enc_id, enc_id_len);
+		decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len);
+		if (!decoded_id) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-AKA: Could not base64 decode encrypted identity");
+			goto fail;
+		}
+		wpa_hexdump(MSG_DEBUG,
+			    "EAP-AKA: Decoded encrypted permanent identity",
+			    decoded_id, decoded_id_len);
+		enc = wpabuf_alloc_copy(decoded_id, decoded_id_len);
+		os_free(decoded_id);
+		if (!enc)
+			goto fail;
+		dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key,
+						     enc);
+		wpabuf_free(enc);
+		if (!dec) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-AKA: Failed to decrypt encrypted identity");
+			goto fail;
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Decrypted permanent identity",
+				  wpabuf_head(dec), wpabuf_len(dec));
+		username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec));
+		if (!username) {
+			wpabuf_free(dec);
+			goto fail;
+		}
+		new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec));
+		if (!new_id) {
+			wpabuf_free(dec);
+			goto fail;
+		}
+		os_free(sm->identity);
+		sm->identity = new_id;
+		sm->identity_len = wpabuf_len(dec);
+		wpabuf_free(dec);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
 			   username);
 		os_free(username);
-		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
-		eap_aka_state(data, NOTIFICATION);
+		goto fail;
 		return;
 	}
 
 	eap_aka_fullauth(sm, data);
+	return;
+
+fail:
+	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+	eap_aka_state(data, NOTIFICATION);
 }
 
 
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index eac3245..5440670 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -276,6 +276,7 @@
 
 	if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
+		wpabuf_free(msg);
 		eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 		return eap_eke_build_failure(data, id);
 	}
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index f526e8b..998d0e8 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -56,6 +56,10 @@
 };
 
 
+static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
+				int vendor, enum eap_type eap_type);
+
+
 static const char * eap_peap_state_txt(int state)
 {
 	switch (state) {
@@ -558,10 +562,24 @@
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, "
 				   "starting Phase2");
 			eap_peap_state(data, PHASE2_START);
+			if (data->ssl.tls_v13 && data->ssl.tls_out &&
+			    wpabuf_len(data->ssl.tls_out) == 0) {
+				/* This can happen with TLS 1.3 when a new
+				 * session ticket is not generated and the
+				 * Finished message from the peer terminates
+				 * Phase 1. */
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: No pending data to send - move directly to Phase 2 ID query");
+				eap_peap_state(data, PHASE2_ID);
+				eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
+						     EAP_TYPE_IDENTITY);
+				goto phase2_id;
+			}
 		}
 		break;
 	case PHASE2_ID:
 	case PHASE2_METHOD:
+	phase2_id:
 		wpabuf_free(data->ssl.tls_out);
 		data->ssl.tls_out_pos = 0;
 		data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id);
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 8a68289..1bcf26c 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_sim_common.h"
@@ -512,6 +514,73 @@
 			   username);
 		os_strlcpy(data->permanent, username, sizeof(data->permanent));
 		os_free(username);
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	} else if (sm->identity_len > 1 && sm->identity[0] == '\0') {
+		char *enc_id, *pos, *end;
+		size_t enc_id_len;
+		u8 *decoded_id;
+		size_t decoded_id_len;
+		struct wpabuf *enc, *dec;
+		u8 *new_id;
+
+		os_free(username);
+		if (!sm->cfg->imsi_privacy_key) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-SIM: Received encrypted identity, but no IMSI privacy key configured to decrypt it");
+			goto failed;
+		}
+
+		enc_id = (char *) &sm->identity[1];
+		end = (char *) &sm->identity[sm->identity_len];
+		for (pos = enc_id; pos < end; pos++) {
+			if (*pos == ',')
+				break;
+		}
+		enc_id_len = pos - enc_id;
+
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-SIM: Encrypted permanent identity",
+				  enc_id, enc_id_len);
+		decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len);
+		if (!decoded_id) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-SIM: Could not base64 decode encrypted identity");
+			goto failed;
+		}
+		wpa_hexdump(MSG_DEBUG,
+			    "EAP-SIM: Decoded encrypted permanent identity",
+			    decoded_id, decoded_id_len);
+		enc = wpabuf_alloc_copy(decoded_id, decoded_id_len);
+		os_free(decoded_id);
+		if (!enc)
+			goto failed;
+		dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key,
+						     enc);
+		wpabuf_free(enc);
+		if (!dec) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-SIM: Failed to decrypt encrypted identity");
+			goto failed;
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Decrypted permanent identity",
+				  wpabuf_head(dec), wpabuf_len(dec));
+		username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec));
+		if (!username) {
+			wpabuf_free(dec);
+			goto failed;
+		}
+		new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec));
+		if (!new_id) {
+			wpabuf_free(dec);
+			goto failed;
+		}
+		os_free(sm->identity);
+		sm->identity = new_id;
+		sm->identity_len = wpabuf_len(dec);
+		wpabuf_free(dec);
+		os_strlcpy(data->permanent, username, sizeof(data->permanent));
+		os_free(username);
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
 			   username);
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 00a496f..443c293 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -1,5 +1,5 @@
 /*
- * hostapd / EAP-TLS (RFC 2716)
+ * hostapd / EAP-TLS (RFC 5216, RFC 9190)
  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
@@ -306,6 +306,14 @@
 
 	wpa_printf(MSG_DEBUG,
 		   "EAP-TLS: Resuming previous session");
+
+	if (data->ssl.tls_v13 && data->ssl.tls_out) {
+		wpa_hexdump_buf(MSG_DEBUG,
+				"EAP-TLS: Additional data to be sent for TLS 1.3",
+				data->ssl.tls_out);
+		return;
+	}
+
 	eap_tls_state(data, SUCCESS);
 	tls_connection_set_success_data_resumed(data->ssl.conn);
 	/* TODO: Cache serial number with session and update EAP user
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index a9b53b1..717af2e 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -94,6 +94,11 @@
 		if (data->tls_out_limit > 100)
 			data->tls_out_limit -= 100;
 	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	data->skip_prot_success = sm->cfg->skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	return 0;
 }
 
@@ -367,14 +372,14 @@
 			sm->cfg->ssl_ctx, data->conn);
 
 	/*
-	 * https://tools.ietf.org/html/draft-ietf-emu-eap-tls13#section-2.5
+	 * RFC 9190 Section 2.5
 	 *
 	 * We need to signal the other end that TLS negotiation is done. We
 	 * can't send a zero-length application data message, so we send
 	 * application data which is one byte of zero.
 	 *
 	 * Note this is only done for when there is no application data to be
-	 * sent. So this is done always for EAP-TLS but notibly not for PEAP
+	 * sent. So this is done always for EAP-TLS but notably not for PEAP
 	 * even on resumption.
 	 */
 	if (data->tls_v13 &&
@@ -390,8 +395,15 @@
 				break;
 			/* fallthrough */
 		case EAP_TYPE_TLS:
+#ifdef CONFIG_TESTING_OPTIONS
+			if (data->skip_prot_success) {
+				wpa_printf(MSG_INFO,
+					   "TESTING: Do not send protected success indication");
+				break;
+			}
+#endif /* CONFIG_TESTING_OPTIONS */
 			wpa_printf(MSG_DEBUG,
-				   "EAP-TLS: Send Commitment Message");
+				   "EAP-TLS: Send protected success indication (appl data 0x00)");
 
 			plain = wpabuf_alloc(1);
 			if (!plain)
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index b0723a1..ad28c79 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -55,6 +55,8 @@
 	 * tls_v13 - Whether TLS v1.3 or newer is used
 	 */
 	int tls_v13;
+
+	bool skip_prot_success; /* testing behavior only for TLS v1.3 */
 };
 
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 5fe89c6..61b7039 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -23,6 +23,7 @@
 	size_t eap_req_id_text_len;
 	int erp_send_reauth_start;
 	char *erp_domain; /* a copy of this will be allocated */
+	bool eap_skip_prot_success;
 
 	/* Opaque context pointer to owner data for callback functions */
 	void *ctx;
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 7d21f68..a203606 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -508,7 +508,7 @@
 		return;
 	}
 
-	for (i = 0, j = 0; i < P2P_MAX_REG_CLASSES; i++) {
+	for (i = 0, j = 0; i < src->reg_classes; i++) {
 		if (is_6ghz_op_class(src->reg_class[i].reg_class))
 			continue;
 		os_memcpy(&dst->reg_class[j], &src->reg_class[i],
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index cf41d8d..2bf3e8e 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -20,7 +20,7 @@
 #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
 #define STATE_MACHINE_DEBUG_PREFIX "CP"
 
-static u64 default_cs_id = CS_ID_GCM_AES_128;
+static u64 cs_id[] = { CS_ID_GCM_AES_128, CS_ID_GCM_AES_256 };
 
 /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
 enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
@@ -210,7 +210,6 @@
 	sm->replay_protect = sm->kay->macsec_replay_protect;
 	sm->validate_frames = sm->kay->macsec_validate;
 
-	/* NOTE: now no other than default cipher suite (AES-GCM-128) */
 	sm->current_cipher_suite = sm->cipher_suite;
 	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
 
@@ -473,8 +472,8 @@
 	sm->orx = false;
 	sm->otx = false;
 
-	sm->current_cipher_suite = default_cs_id;
-	sm->cipher_suite = default_cs_id;
+	sm->current_cipher_suite = cs_id[kay->macsec_csindex];
+	sm->cipher_suite = cs_id[kay->macsec_csindex];
 	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
 	sm->confidentiality_offset = sm->cipher_offset;
 	sm->transmit_delay = MKA_LIFE_TIME;
@@ -491,6 +490,7 @@
 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
 	secy_cp_control_confidentiality_offset(sm->kay,
 					       sm->confidentiality_offset);
+	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
 
 	SM_STEP_RUN(CP);
 
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index 657de93..a1f8ae9 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -221,8 +221,16 @@
 
 	wpa_printf(MSG_DEBUG, "\tKey Number............: %d",
 		   be_to_host32(body->kn));
-	/* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
-	wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24);
+	if (body_len == 28) {
+		wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:",
+			    body->sak, 24);
+	} else if (body_len > CS_ID_LEN - sizeof(body->kn)) {
+		wpa_hexdump(MSG_DEBUG, "\tMACsec Cipher Suite...:",
+			    body->sak, CS_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:",
+			    body->sak + CS_ID_LEN,
+			    body_len - CS_ID_LEN - sizeof(body->kn));
+	}
 }
 
 
@@ -3456,7 +3464,8 @@
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
-		    u16 port, u8 priority, const char *ifname, const u8 *addr)
+		    u16 port, u8 priority, u32 macsec_csindex,
+		    const char *ifname, const u8 *addr)
 {
 	struct ieee802_1x_kay *kay;
 
@@ -3493,7 +3502,7 @@
 	kay->dist_time = 0;
 
 	kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
-	kay->macsec_csindex = DEFAULT_CS_INDEX;
+	kay->macsec_csindex = macsec_csindex;
 	kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
 	kay->mka_version = MKA_VERSION_ID;
 
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 1d3c2ac..11cf7b7 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -240,7 +240,8 @@
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
 		    bool macsec_replay_protect, u32 macsec_replay_window,
-		    u16 port, u8 priority, const char *ifname, const u8 *addr);
+		    u16 port, u8 priority, u32 macsec_csindex,
+		    const char *ifname, const u8 *addr);
 void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
 
 struct ieee802_1x_mka_participant *
diff --git a/src/radius/radius.c b/src/radius/radius.c
index be16e27..a642280 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -159,7 +159,8 @@
 
 
 struct radius_attr_type {
-	u8 type;
+	u16 type; /* 0..255 for basic types;
+		   * (241 << 8) | <ext-type> for extended types */
 	char *name;
 	enum {
 		RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
@@ -260,11 +261,31 @@
 	  RADIUS_ATTR_HEXDUMP },
 	{ RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
 	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_EXT_TYPE_1, "Extended-Type-1", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_TYPE_2, "Extended-Type-2", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_TYPE_3, "Extended-Type-3", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_TYPE_4, "Extended-Type-4", RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_LONG_EXT_TYPE_1, "Long-Extended-Type-1",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_LONG_EXT_TYPE_2, "Long-Extended-Type-2",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1, "Extended-Vendor-Specific-1",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2, "Extended-Vendor-Specific-2",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3, "Extended-Vendor-Specific-3",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4, "Extended-Vendor-Specific-4",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5, "Extended-Vendor-Specific-5",
+	  RADIUS_ATTR_UNDIST },
+	{ RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6, "Extended-Vendor-Specific-6",
+	  RADIUS_ATTR_UNDIST },
 };
 #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
-static const struct radius_attr_type *radius_get_attr_type(u8 type)
+static const struct radius_attr_type * radius_get_attr_type(u16 type)
 {
 	size_t i;
 
@@ -277,23 +298,60 @@
 }
 
 
+static bool radius_is_long_ext_type(u8 type)
+{
+	return type == RADIUS_ATTR_LONG_EXT_TYPE_1 ||
+		type == RADIUS_ATTR_LONG_EXT_TYPE_2;
+}
+
+
+static bool radius_is_ext_type(u8 type)
+{
+	return type >= RADIUS_ATTR_EXT_TYPE_1 &&
+		type <= RADIUS_ATTR_LONG_EXT_TYPE_2;
+}
+
+
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
+	struct radius_attr_hdr_ext *ext = NULL;
 	const struct radius_attr_type *attr;
 	int len;
 	unsigned char *pos;
 	char buf[1000];
 
-	attr = radius_get_attr_type(hdr->type);
-
-	wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
-		   hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
-
-	if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
+	if (hdr->length < sizeof(struct radius_attr_hdr))
 		return;
 
-	len = hdr->length - sizeof(struct radius_attr_hdr);
-	pos = (unsigned char *) (hdr + 1);
+	if (radius_is_ext_type(hdr->type)) {
+		if (hdr->length < 4) {
+			wpa_printf(MSG_INFO,
+				   "   Invalid attribute %d (too short for extended type)",
+				hdr->type);
+			return;
+		}
+
+		ext = (struct radius_attr_hdr_ext *) hdr;
+	}
+
+	if (ext) {
+		attr = radius_get_attr_type((ext->type << 8) | ext->ext_type);
+		wpa_printf(MSG_INFO, "   Attribute %d.%d (%s) length=%d",
+			   ext->type, ext->ext_type,
+			   attr ? attr->name : "?Unknown?", ext->length);
+		pos = (unsigned char *) (ext + 1);
+		len = ext->length - sizeof(struct radius_attr_hdr_ext);
+	} else {
+		attr = radius_get_attr_type(hdr->type);
+		wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+			   hdr->type, attr ? attr->name : "?Unknown?",
+			   hdr->length);
+		pos = (unsigned char *) (hdr + 1);
+		len = hdr->length - sizeof(struct radius_attr_hdr);
+	}
+
+	if (!attr)
+		return;
 
 	switch (attr->data_type) {
 	case RADIUS_ATTR_TEXT:
@@ -627,22 +685,54 @@
 }
 
 
-struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
-					    const u8 *data, size_t data_len)
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type,
+					     const u8 *data, size_t data_len)
 {
-	size_t buf_needed;
-	struct radius_attr_hdr *attr;
+	size_t buf_needed, max_len;
+	struct radius_attr_hdr *attr = NULL;
+	struct radius_attr_hdr_ext *ext;
+	u8 ext_type = 0;
 
 	if (TEST_FAIL())
 		return NULL;
 
-	if (data_len > RADIUS_MAX_ATTR_LEN) {
-		wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
-		       (unsigned long) data_len);
-		return NULL;
+	if (type > 255) {
+		if (!radius_is_ext_type(type >> 8)) {
+			wpa_printf(MSG_ERROR,
+				   "%s: Undefined extended type %d.%d",
+				   __func__, type >> 8, type & 0xff);
+			return NULL;
+		}
+		ext_type = type & 0xff;
+		type >>= 8;
+	} else if (radius_is_ext_type(type)) {
+		wpa_printf(MSG_ERROR, "%s: Unexpected extended type use for %d",
+			   __func__, type);
 	}
 
-	buf_needed = sizeof(*attr) + data_len;
+	if (radius_is_long_ext_type(type)) {
+		size_t hdr_len = sizeof(struct radius_attr_hdr_ext) + 1;
+		size_t plen = 255 - hdr_len;
+		size_t num;
+
+		max_len = 4096;
+		num = (data_len + plen - 1) / plen;
+		if (num == 0)
+			num = 1;
+		buf_needed = num * hdr_len + data_len;
+	} else if (radius_is_ext_type(type)) {
+		max_len = RADIUS_MAX_EXT_ATTR_LEN;
+		buf_needed = sizeof(struct radius_attr_hdr_ext) + data_len;
+	} else {
+		max_len = RADIUS_MAX_ATTR_LEN;
+		buf_needed = sizeof(*attr) + data_len;
+	}
+	if (data_len > max_len) {
+		wpa_printf(MSG_ERROR,
+			   "%s: too long attribute (%zu > %zu bytes)",
+			   __func__, data_len, max_len);
+		return NULL;
+	}
 
 	if (wpabuf_tailroom(msg->buf) < buf_needed) {
 		/* allocate more space for message buffer */
@@ -651,13 +741,44 @@
 		msg->hdr = wpabuf_mhead(msg->buf);
 	}
 
-	attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
-	attr->type = type;
-	attr->length = sizeof(*attr) + data_len;
-	wpabuf_put_data(msg->buf, data, data_len);
+	if (radius_is_long_ext_type(type)) {
+		size_t plen = 255 - sizeof(struct radius_attr_hdr_ext) - 1;
+		size_t alen;
 
-	if (radius_msg_add_attr_to_array(msg, attr))
-		return NULL;
+		do {
+			alen = data_len > plen ? plen : data_len;
+			ext = wpabuf_put(msg->buf,
+					 sizeof(struct radius_attr_hdr_ext));
+			if (!attr)
+				attr = (struct radius_attr_hdr *) ext;
+			ext->type = type;
+			ext->length = sizeof(*ext) + 1 + alen;
+			ext->ext_type = ext_type;
+			wpabuf_put_u8(msg->buf, data_len > alen ? 0x80 : 0);
+			wpabuf_put_data(msg->buf, data, data_len);
+			data += alen;
+			data_len -= alen;
+			if (radius_msg_add_attr_to_array(
+				    msg, (struct radius_attr_hdr *) ext))
+				return NULL;
+		} while (data_len > 0);
+	} else if (radius_is_ext_type(type)) {
+		ext = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr_ext));
+		attr = (struct radius_attr_hdr *) ext;
+		ext->type = type;
+		ext->length = sizeof(*ext) + data_len;
+		ext->ext_type = ext_type;
+		wpabuf_put_data(msg->buf, data, data_len);
+		if (radius_msg_add_attr_to_array(msg, attr))
+			return NULL;
+	} else {
+		attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
+		attr->type = type;
+		attr->length = sizeof(*attr) + data_len;
+		wpabuf_put_data(msg->buf, data, data_len);
+		if (radius_msg_add_attr_to_array(msg, attr))
+			return NULL;
+	}
 
 	return attr;
 }
@@ -1285,6 +1406,28 @@
 }
 
 
+int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id,
+			  u8 vendor_type, const u8 *data, size_t len)
+{
+	struct radius_attr_hdr *attr;
+	u8 *buf, *pos;
+	size_t alen;
+
+	alen = 4 + 1 + len;
+	buf = os_malloc(alen);
+	if (!buf)
+		return 0;
+	pos = buf;
+	WPA_PUT_BE32(pos, vendor_id);
+	pos += 4;
+	*pos++ = vendor_type;
+	os_memcpy(pos, data, len);
+	attr = radius_msg_add_attr(msg, type, buf, alen);
+	os_free(buf);
+	return attr != NULL;
+}
+
+
 int radius_user_password_hide(struct radius_msg *msg,
 			      const u8 *data, size_t data_len,
 			      const u8 *secret, size_t secret_len,
diff --git a/src/radius/radius.h b/src/radius/radius.h
index fb81481..177c64a 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014-2022, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -46,7 +46,15 @@
 	/* followed by length-2 octets of attribute value */
 } STRUCT_PACKED;
 
+struct radius_attr_hdr_ext {
+	u8 type;
+	u8 length; /* including this header */
+	u8 ext_type;
+	/* followed by length-3 octets of attribute value */
+} STRUCT_PACKED;
+
 #define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+#define RADIUS_MAX_EXT_ATTR_LEN (255 - sizeof(struct radius_attr_hdr_ext))
 
 enum { RADIUS_ATTR_USER_NAME = 1,
        RADIUS_ATTR_USER_PASSWORD = 2,
@@ -113,6 +121,18 @@
        RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
        RADIUS_ATTR_WLAN_AKM_SUITE = 188,
        RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
+       RADIUS_ATTR_EXT_TYPE_1 = 241,
+       RADIUS_ATTR_EXT_TYPE_2 = 242,
+       RADIUS_ATTR_EXT_TYPE_3 = 243,
+       RADIUS_ATTR_EXT_TYPE_4 = 244,
+       RADIUS_ATTR_LONG_EXT_TYPE_1 = 245,
+       RADIUS_ATTR_LONG_EXT_TYPE_2 = 246,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_1 = (241 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_2 = (242 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_3 = (243 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_4 = (244 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5 = (245 << 8) | 26,
+       RADIUS_ATTR_EXT_VENDOR_SPECIFIC_6 = (246 << 8) | 26,
 };
 
 
@@ -188,6 +208,13 @@
        RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
 };
 
+/* FreeRADIUS vendor-specific attributes */
+#define RADIUS_VENDOR_ID_FREERADIUS 11344
+/* Extended-Vendor-Specific-5 (245.26; long extended header) */
+enum {
+	RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE = 1,
+	RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG = 2,
+};
 
 /* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */
 #define RADIUS_VENDOR_ID_WFA 40808
@@ -257,7 +284,7 @@
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
 			      size_t secret_len,
 			      int require_message_authenticator);
-struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u16 type,
 					     const u8 *data, size_t data_len);
 struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
 int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
@@ -284,6 +311,8 @@
 			     const u8 *recv_key, size_t recv_key_len);
 int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
 		       size_t len);
+int radius_msg_add_ext_vs(struct radius_msg *msg, u16 type, u32 vendor_id,
+			  u8 vendor_type, const u8 *data, size_t len);
 int radius_user_password_hide(struct radius_msg *msg,
 			      const u8 *data, size_t data_len,
 			      const u8 *secret, size_t secret_len,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5ff11bd..00b7e84 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2024,6 +2024,13 @@
 	u8 *rbuf, *key_mic;
 	size_t kde_len = 0;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->disable_eapol_g2_tx) {
+		wpa_printf(MSG_INFO, "TEST: Disable sending EAPOL-Key 2/2");
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #ifdef CONFIG_OCV
 	if (wpa_sm_ocv_enabled(sm))
 		kde_len = OCV_OCI_KDE_LEN;
@@ -3381,6 +3388,9 @@
 	case WPA_PARAM_OCI_FREQ_FILS_ASSOC:
 		sm->oci_freq_override_fils_assoc = value;
 		break;
+	case WPA_PARAM_DISABLE_EAPOL_G2_TX:
+		sm->disable_eapol_g2_tx = value;
+		break;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_DPP2
 	case WPA_PARAM_DPP_PFS:
@@ -3897,7 +3907,7 @@
 	pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true);
 }
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 void wpa_sm_install_pmk(struct wpa_sm *sm)
 {
 	/* In case the driver wants to handle re-assocs, pass it down the PMK. */
@@ -3936,7 +3946,8 @@
 			"WPA: Updated KCK and KEK after FT reassoc");
 	}
 }
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 
 #ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 3d5e883..00fa0bc 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -118,6 +118,7 @@
 	WPA_PARAM_OCI_FREQ_EAPOL_G2,
 	WPA_PARAM_OCI_FREQ_FT_ASSOC,
 	WPA_PARAM_OCI_FREQ_FILS_ASSOC,
+	WPA_PARAM_DISABLE_EAPOL_G2_TX,
 };
 
 struct rsn_supp_config {
@@ -211,10 +212,10 @@
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 void wpa_sm_install_pmk(struct wpa_sm *sm);
 void wpa_sm_notify_brcm_ft_reassoc(struct wpa_sm *sm, const u8 *bssid);
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
 void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
@@ -454,9 +455,10 @@
 				 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);
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#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 */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 #ifdef CONFIG_PASN
 
 int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
@@ -496,12 +498,12 @@
 	return 0;
 }
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 static inline int wpa_ft_is_ft_protocol(struct wpa_sm *sm)
 {
 	return 0;
 }
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 static inline void wpa_reset_ft_completed(struct wpa_sm *sm)
 {
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 2b172b7..9d3b538 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -743,7 +743,7 @@
 	return sm->ft_completed;
 }
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 int wpa_ft_is_ft_protocol(struct wpa_sm *sm)
 {
 	if (sm == NULL)
@@ -754,7 +754,7 @@
 
 	return sm->ft_protocol;
 }
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 void wpa_reset_ft_completed(struct wpa_sm *sm)
 {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 6cdce32..579616f 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -185,6 +185,7 @@
 	unsigned int oci_freq_override_eapol_g2;
 	unsigned int oci_freq_override_ft_assoc;
 	unsigned int oci_freq_override_fils_assoc;
+	unsigned int disable_eapol_g2_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_FILS
diff --git a/src/utils/common.c b/src/utils/common.c
index 2c12751..6acfcbd 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -13,7 +13,7 @@
 #include "common.h"
 
 
-static int hex2num(char c)
+int hex2num(char c)
 {
 	if (c >= '0' && c <= '9')
 		return c - '0';
diff --git a/src/utils/common.h b/src/utils/common.h
index 45f72bb..435a9a8 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -477,6 +477,7 @@
 int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
 int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2num(char c);
 int hex2byte(const char *hex);
 int hexstr2bin(const char *hex, u8 *buf, size_t len);
 void inc_byte_array(u8 *counter, size_t len);
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
index d9fc925..23e9ecd 100644
--- a/src/utils/http-utils.h
+++ b/src/utils/http-utils.h
@@ -33,6 +33,7 @@
 	size_t num_othername;
 	struct http_logo *logo;
 	size_t num_logo;
+	const char *url;
 };
 
 int soap_init_client(struct http_ctx *ctx, const char *address,
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index e62fbf9..30b07f2 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -64,6 +64,7 @@
 	X509 *peer_issuer_issuer;
 
 	const char *last_err;
+	const char *url;
 };
 
 
@@ -871,6 +872,7 @@
 		       X509 *cert, GENERAL_NAMES **names)
 {
 	os_memset(hcert, 0, sizeof(*hcert));
+	hcert->url = ctx->url ? ctx->url : ctx->svc_address;
 
 	*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 	if (*names)
@@ -1617,23 +1619,23 @@
 		       const char *fname, const char *ca_fname)
 {
 	CURL *curl;
-	FILE *f;
+	FILE *f = NULL;
 	CURLcode res;
 	long http = 0;
+	int ret = -1;
 
 	ctx->last_err = NULL;
+	ctx->url = url;
 
 	wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
 		   url, fname, ca_fname);
 	curl = curl_easy_init();
 	if (curl == NULL)
-		return -1;
+		goto fail;
 
 	f = fopen(fname, "wb");
-	if (f == NULL) {
-		curl_easy_cleanup(curl);
-		return -1;
-	}
+	if (!f)
+		goto fail;
 
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	if (ca_fname) {
@@ -1655,9 +1657,7 @@
 			ctx->last_err = curl_easy_strerror(res);
 		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
 			   ctx->last_err);
-		curl_easy_cleanup(curl);
-		fclose(f);
-		return -1;
+		goto fail;
 	}
 
 	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
@@ -1665,15 +1665,19 @@
 	if (http != 200) {
 		ctx->last_err = "HTTP download failed";
 		wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
-		curl_easy_cleanup(curl);
-		fclose(f);
-		return -1;
+		goto fail;
 	}
 
-	curl_easy_cleanup(curl);
-	fclose(f);
+	ret = 0;
 
-	return 0;
+fail:
+	ctx->url = NULL;
+	if (curl)
+		curl_easy_cleanup(curl);
+	if (f)
+		fclose(f);
+
+	return ret;
 }
 
 
@@ -1686,16 +1690,17 @@
 {
 	long http = 0;
 	CURLcode res;
-	char *ret;
+	char *ret = NULL;
 	CURL *curl;
 	struct curl_slist *curl_hdr = NULL;
 
 	ctx->last_err = NULL;
+	ctx->url = url;
 	wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
 	curl = setup_curl_post(ctx, url, ca_fname, username, password,
 			       client_cert, client_key);
 	if (curl == NULL)
-		return NULL;
+		goto fail;
 
 	if (content_type) {
 		char ct[200];
@@ -1715,8 +1720,7 @@
 			ctx->last_err = curl_easy_strerror(res);
 		wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
 			   ctx->last_err);
-		free_curl_buf(ctx);
-		return NULL;
+		goto fail;
 	}
 
 	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
@@ -1724,12 +1728,11 @@
 	if (http != 200) {
 		ctx->last_err = "HTTP POST failed";
 		wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
-		free_curl_buf(ctx);
-		return NULL;
+		goto fail;
 	}
 
 	if (ctx->curl_buf == NULL)
-		return NULL;
+		goto fail;
 
 	ret = ctx->curl_buf;
 	if (resp_len)
@@ -1739,6 +1742,9 @@
 
 	wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
 
+fail:
+	free_curl_buf(ctx);
+	ctx->url = NULL;
 	return ret;
 }
 
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index 0d088bb..db11387 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -301,7 +301,6 @@
         "src/common/sae_pk.c",
         "src/common/wpa_common.c",
         "src/crypto/aes-ctr.c",
-        "src/crypto/aes-omac1.c",
         "src/crypto/aes-siv.c",
         "src/crypto/crypto_openssl.c",
         "src/crypto/dh_groups.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index cdf8943..d1436e2 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -119,7 +119,6 @@
 
 ifdef CONFIG_FIPS
 CONFIG_NO_RANDOM_POOL=
-CONFIG_OPENSSL_CMAC=y
 endif
 
 OBJS = config.c
@@ -515,6 +514,9 @@
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_TLSV1_3
+L_CFLAGS += -DEAP_TLSV1_3
+endif
 endif
 
 ifdef CONFIG_EAP_UNAUTH_TLS
@@ -775,6 +777,7 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_ECC=y
 NEED_DRAGONFLY=y
+MS_FUNCS=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -945,6 +948,9 @@
 ifdef CONFIG_IEEE80211AX
 OBJS += src/ap/ieee802_11_he.c
 endif
+ifdef CONFIG_IEEE80211BE
+OBJS += src/ap/ieee802_11_eht.c
+endif
 ifdef CONFIG_WNM_AP
 L_CFLAGS += -DCONFIG_WNM_AP
 OBJS += src/ap/wnm_ap.c
@@ -967,6 +973,10 @@
 ifdef CONFIG_IEEE80211AC
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
+ifdef CONFIG_IEEE80211BE
+CONFIG_IEEE80211AX=y
+L_CFLAGS += -DCONFIG_IEEE80211BE
+endif
 ifdef CONFIG_IEEE80211AX
 L_CFLAGS += -DCONFIG_IEEE80211AX
 endif
@@ -1099,6 +1109,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
+L_CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 ifdef TLS_FUNCS
 L_CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += src/crypto/tls_openssl.c
@@ -1303,9 +1314,7 @@
 AESOBJS += src/crypto/aes-encblock.c
 endif
 NEED_AES_ENC=y
-ifdef CONFIG_OPENSSL_CMAC
-L_CFLAGS += -DCONFIG_OPENSSL_CMAC
-else
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-omac1.c
 endif
 ifdef NEED_AES_WRAP
@@ -1399,6 +1408,17 @@
 endif
 endif
 
+ifdef CONFIG_SAE
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
 SHA256OBJS = # none by default
 L_CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 18ffbcb..1c6911b 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1,24 +1,29 @@
 BINALL=wpa_supplicant wpa_cli
 
-ifndef CONFIG_NO_WPA_PASSPHRASE
-BINALL += wpa_passphrase
-endif
-
 ALL = $(BINALL)
 ALL += systemd/wpa_supplicant.service
 ALL += systemd/wpa_supplicant@.service
 ALL += systemd/wpa_supplicant-nl80211@.service
 ALL += systemd/wpa_supplicant-wired@.service
 ALL += dbus/fi.w1.wpa_supplicant1.service
-ifdef CONFIG_BUILD_WPA_CLIENT_SO
-ALL += libwpa_client.so
-endif
 
 EXTRA_TARGETS=dynamic_eap_methods
 
 CONFIG_FILE=.config
 include ../src/build.rules
 
+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
+_all: libwpa_client.so
+endif
+
+ifndef CONFIG_NO_WPA_PASSPHRASE
+# add the dependency this way to allow CONFIG_NO_WPA_PASSPHRASE
+# being set in the config which is read by build.rules
+_all: wpa_passphrase
+endif
+
 ifdef LIBS
 # If LIBS is set with some global build system defaults, clone those for
 # LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well.
@@ -68,6 +73,9 @@
 
 install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
 	$(MAKE) -C ../src install
+ifndef CONFIG_NO_WPA_PASSPHRASE
+	install -D wpa_passphrase $(DESTDIR)/$(BINDIR)/wpa_passphrase
+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
@@ -79,7 +87,6 @@
 
 ifdef CONFIG_FIPS
 CONFIG_NO_RANDOM_POOL=
-CONFIG_OPENSSL_CMAC=y
 endif
 
 OBJS = config.o
@@ -484,6 +491,9 @@
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_TLSV1_3
+CFLAGS += -DEAP_TLSV1_3
+endif
 endif
 
 ifdef CONFIG_EAP_UNAUTH_TLS
@@ -750,6 +760,7 @@
 CONFIG_IEEE8021X_EAPOL=y
 NEED_ECC=y
 NEED_DRAGONFLY=y
+MS_FUNCS=y
 endif
 
 ifdef CONFIG_EAP_EKE
@@ -939,6 +950,9 @@
 ifdef CONFIG_IEEE80211AX
 OBJS += ../src/ap/ieee802_11_he.o
 endif
+ifdef CONFIG_IEEE80211BE
+OBJS += ../src/ap/ieee802_11_eht.o
+endif
 ifdef CONFIG_WNM_AP
 CFLAGS += -DCONFIG_WNM_AP
 OBJS += ../src/ap/wnm_ap.o
@@ -961,6 +975,10 @@
 ifdef CONFIG_IEEE80211AC
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
+ifdef CONFIG_IEEE80211BE
+CONFIG_IEEE80211AX=y
+CFLAGS += -DCONFIG_IEEE80211BE
+endif
 ifdef CONFIG_IEEE80211AX
 CFLAGS += -DCONFIG_IEEE80211AX
 endif
@@ -1107,6 +1125,7 @@
 endif
 
 ifeq ($(CONFIG_TLS), openssl)
+CFLAGS += -DCRYPTO_RSA_OAEP_SHA256
 ifdef TLS_FUNCS
 CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += ../src/crypto/tls_openssl.o
@@ -1358,9 +1377,7 @@
 AESOBJS += ../src/crypto/aes-encblock.o
 endif
 NEED_AES_ENC=y
-ifdef CONFIG_OPENSSL_CMAC
-CFLAGS += -DCONFIG_OPENSSL_CMAC
-else
+ifneq ($(CONFIG_TLS), openssl)
 ifneq ($(CONFIG_TLS), linux)
 ifneq ($(CONFIG_TLS), wolfssl)
 AESOBJS += ../src/crypto/aes-omac1.o
@@ -1474,6 +1491,17 @@
 endif
 endif
 
+ifdef CONFIG_SAE
+ifdef NEED_SHA384
+# Need to add HMAC-SHA384 KDF as well, if SHA384 was enabled.
+NEED_HMAC_SHA384_KDF=y
+endif
+ifdef NEED_SHA512
+# Need to add HMAC-SHA512 KDF as well, if SHA512 was enabled.
+NEED_HMAC_SHA512_KDF=y
+endif
+endif
+
 SHA256OBJS = # none by default
 CFLAGS += -DCONFIG_SHA256
 ifneq ($(CONFIG_TLS), openssl)
@@ -2076,3 +2104,4 @@
 	rm -f libwpa_client.a
 	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 b076621..a099a85 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -168,6 +168,12 @@
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #	format
 #
+# imsi_privacy_key: IMSI privacy key (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.
+#
 # domain_suffix_match: Constraint for server domain name
 #	If set, this FQDN is used as a suffix match requirement for the AAA
 #	server certificate in SubjectAltName dNSName element(s). If a
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
index b884f67..e902cc8 100644
--- a/wpa_supplicant/README-WPS
+++ b/wpa_supplicant/README-WPS
@@ -24,8 +24,8 @@
 environments that require secure network access without chance for
 allowing outsiders to gain access during the setup phase.
 
-WPS uses following terms to describe the entities participating in the
-network setup:
+WPS uses the following terms to describe the entities participating
+in the network setup:
 - access point: the WLAN access point
 - Registrar: a device that control a network and can authorize
   addition of new devices); this may be either in the AP ("internal
@@ -55,22 +55,22 @@
 
 WPS is an optional component that needs to be enabled in
 wpa_supplicant build configuration (.config). Here is an example
-configuration that includes WPS support and Linux nl80211 -based
+configuration that includes WPS support and Linux nl80211-based
 driver interface:
 
 CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
 
 If you want to enable WPS external registrar (ER) functionality, you
-will also need to add following line:
+will also need to add the following line:
 
 CONFIG_WPS_ER=y
 
-Following parameter can be used to enable support for NFC config method:
+The following parameter can be used to enable support for NFC config
+method:
 
 CONFIG_WPS_NFC=y
 
-
 WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
 the device. This is configured in the runtime configuration for
 wpa_supplicant (if not set, UUID will be generated based on local MAC
@@ -91,7 +91,6 @@
 update_config=1
 
 
-
 External operations
 -------------------
 
@@ -118,7 +117,6 @@
 the client will be enrolled with credentials needed to connect to the
 AP to access the network.
 
-
 If the client device does not have a display that could show the
 random PIN, a hardcoded PIN that is printed on a label can be
 used. wpa_supplicant is notified this with a control interface
@@ -135,7 +133,6 @@
 
 wpa_cli wps_pin any 12345670 300
 
-
 If a random PIN is needed for a user interface, "wpa_cli wps_pin get"
 can be used to generate a new PIN without starting WPS negotiation.
 This random PIN can then be passed as an argument to another wps_pin
@@ -154,7 +151,6 @@
 negotiation which will generate a new WPA PSK in the same way as the
 PIN method described above.
 
-
 If the client wants to operate in the Registrar role to learn the
 current AP configuration and optionally, to configure an AP,
 wpa_supplicant is notified over the control interface, e.g., with
@@ -218,7 +214,8 @@
 processing the credential attributes and updating wpa_supplicant
 configuration based on them.
 
-Following control interface messages are sent out for external programs:
+The following control interface messages are sent out for external
+programs:
 
 WPS-CRED-RECEIVED  <hexdump of Credential attribute(s)>
 For example:
@@ -236,7 +233,7 @@
 Separate wpa_supplicant process can be started for WPS ER
 operations. A special "none" driver can be used in such a case to
 indicate that no local network interface is actually controlled. For
-example, following command could be used to start the ER:
+example, the following command could be used to start the ER:
 
 wpa_supplicant -Dnone -c er.conf -ieth0
 
@@ -245,7 +242,6 @@
 ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
 device_name=WPS External Registrar
 
-
 wpa_cli commands for ER functionality:
 
 wps_er_start [IP address]
@@ -275,7 +271,6 @@
 <auth> must be one of the following: OPEN WPAPSK WPA2PSK
 <encr> must be one of the following: NONE WEP TKIP CCMP
 
-
 wps_er_pbc <Enrollee UUID|MAC address>
 - accept an Enrollee PBC using External Registrar
 
@@ -285,7 +280,6 @@
 - if the MAC address of the enrollee is known, it should be configured
   to allow the AP to advertise list of authorized enrollees
 
-
 WPS ER events:
 
 WPS_EVENT_ER_AP_ADD
diff --git a/wpa_supplicant/aidl/sta_network.cpp b/wpa_supplicant/aidl/sta_network.cpp
index fe4a760..61c71a1 100644
--- a/wpa_supplicant/aidl/sta_network.cpp
+++ b/wpa_supplicant/aidl/sta_network.cpp
@@ -2142,6 +2142,7 @@
 		return ndk::ScopedAStatus::ok();
 	}
 
+	new_entry->external = true;
 	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, new_entry);
 
 	return ndk::ScopedAStatus::ok();
@@ -2160,6 +2161,7 @@
 	if (key_mgmt_mask & WPA_KEY_MGMT_OWE) {
 		// Do not allow to connect to Open network when OWE is selected
 		wpa_ssid->owe_only = 1;
+		wpa_ssid->owe_ptk_workaround = 1;
 	}
 	wpa_ssid->key_mgmt = key_mgmt_mask;
 	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
@@ -2488,6 +2490,26 @@
 			key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE;
 		}
 #endif
+#ifdef CONFIG_FILS
+		if ((key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA256) &&
+		    (capa.key_mgmt_iftype[WPA_IF_STATION] &
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_FILS_SHA256;
+		}
+
+		if ((key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA384) &&
+		    (capa.key_mgmt_iftype[WPA_IF_STATION] &
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_FILS_SHA384;
+		}
+#endif
+#ifdef CONFIG_SUITEB192
+		if ((key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) &&
+		    (capa.key_mgmt_iftype[WPA_IF_STATION] &
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384)) {
+			key_mgmt_mask |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+		}
+#endif
 #endif
 	}
 
@@ -2512,6 +2534,20 @@
 		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_SAE;
 	}
 #endif
+#ifdef CONFIG_FILS
+	if (key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA256) {
+		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_FILS_SHA256;
+	}
+
+	if (key_mgmt_mask & WPA_KEY_MGMT_FILS_SHA384) {
+		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_FILS_SHA384;
+	}
+#endif
+#ifdef CONFIG_SUITEB192
+	if (key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		key_mgmt_mask &= ~WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+	}
+#endif
 #endif
 }
 
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index ee6f809..799790b 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -303,12 +303,38 @@
 		wpa_printf(MSG_DEBUG, "%s,NULL wpa_s for wlan0", __FUNCTION__);
 		return createStatus(SupplicantStatusCode::FAILURE_IFACE_UNKNOWN);
 	}
-	if (wpas_p2p_add_p2pdev_interface(
-		wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+
+	const u8 *if_addr = NULL;
+	char force_name[100] = {'\0'};
+	wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
+	if (wpa_s->conf->p2p_device_random_mac_addr == 2 &&
+		!is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr))
+		if_addr = wpa_s->conf->p2p_device_persistent_mac_addr;
+
+	int ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, iface_params.ifname, if_addr, NULL,
+		force_name, wpa_s->pending_interface_addr, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
+		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
+	}
+
+	os_strlcpy(wpa_s->pending_interface_name, iface_params.ifname,
+		sizeof(wpa_s->pending_interface_name));
+	iface_params.p2p_mgmt = 1;
+	iface_params.driver_param = wpa_s->conf->driver_param;
+	iface_params.ctrl_interface = NULL;
+
+	struct wpa_supplicant *p2pdev_wpa_s = wpa_supplicant_add_iface(
+		wpa_s->global, &iface_params, wpa_s);
+
+	if (!p2pdev_wpa_s) {
 		wpa_printf(MSG_INFO,
 			"Failed to enable P2P Device");
 		return createStatus(SupplicantStatusCode::FAILURE_UNKNOWN);
 	}
+	p2pdev_wpa_s->p2pdev = p2pdev_wpa_s;
+	wpa_s->pending_interface_name[0] = '\0';
+
 	return ndk::ScopedAStatus::ok();
 }
 
@@ -321,6 +347,13 @@
 	if (name.empty()) {
 		return {nullptr, createStatus(SupplicantStatusCode::FAILURE_ARGS_INVALID)};
 	}
+	if (strncmp(name.c_str(), P2P_MGMT_DEVICE_PREFIX, strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
+		struct wpa_supplicant* wpa_s = wpa_supplicant_get_iface(wpa_global_, name.c_str());
+		if (wpa_s) {
+			wpa_printf(MSG_DEBUG, "Remove existing p2p dev interface");
+			wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0);
+		}
+	}
 	// Try to get the wpa_supplicant record for this iface, return
 	// the iface object with the appropriate status code if it exists.
 	ndk::ScopedAStatus status;
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 0559822..94f0f83 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -175,6 +175,10 @@
 	hostapd_set_oper_centr_freq_seg0_idx(
 		conf, conf->channel + conf->secondary_channel * 2);
 	hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
+	ieee80211_freq_to_channel_ext(ssid->frequency, 0,
+		   conf->vht_oper_chwidth,
+		   &conf->op_class,
+		   &conf->channel);
 }
 
 
@@ -1028,6 +1032,7 @@
 	hapd_iface->extended_capa = wpa_s->extended_capa;
 	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
 	hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
+	hapd_iface->drv_max_acl_mac_addrs = wpa_s->drv_max_acl_mac_addrs;
 
 	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 	if (conf == NULL) {
@@ -1105,6 +1110,11 @@
 		hapd_iface->bss[i]->ext_eapol_frame_io =
 			wpa_s->ext_eapol_frame_io;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_WNM_AP
+		if (ssid->mode == WPAS_MODE_AP)
+			hapd_iface->bss[i]->conf->bss_transition = 1;
+#endif /* CONFIG_WNM_AP */
 	}
 
 	os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
@@ -1564,6 +1574,183 @@
 	return pos - buf;
 }
 
+
+#ifdef CONFIG_WNM_AP
+
+int ap_ctrl_iface_disassoc_imminent(struct wpa_supplicant *wpa_s,
+				    const char *buf)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_disassoc_imminent(hapd, buf);
+}
+
+
+int ap_ctrl_iface_ess_disassoc(struct wpa_supplicant *wpa_s, const char *buf)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_ess_disassoc(hapd, buf);
+}
+
+
+int ap_ctrl_iface_bss_tm_req(struct wpa_supplicant *wpa_s, const char *buf)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+	return hostapd_ctrl_iface_bss_tm_req(hapd, buf);
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+int ap_ctrl_iface_acl_add_mac(struct wpa_supplicant *wpa_s,
+			      enum macaddr_acl acl_type,
+			      const char *buf)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	hapd->conf->macaddr_acl = acl_type;
+
+	if (acl_type == ACCEPT_UNLESS_DENIED)
+		return hostapd_ctrl_iface_acl_add_mac(&hapd->conf->deny_mac,
+						      &hapd->conf->num_deny_mac,
+						      buf);
+	if (acl_type == DENY_UNLESS_ACCEPTED)
+		return hostapd_ctrl_iface_acl_add_mac(
+			&hapd->conf->accept_mac,
+			&hapd->conf->num_accept_mac, buf);
+
+	return -1;
+}
+
+
+int ap_ctrl_iface_acl_del_mac(struct wpa_supplicant *wpa_s,
+			      enum macaddr_acl acl_type,
+			      const char *buf)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	hapd->conf->macaddr_acl = acl_type;
+
+	if (acl_type == ACCEPT_UNLESS_DENIED)
+		return hostapd_ctrl_iface_acl_del_mac(&hapd->conf->deny_mac,
+						      &hapd->conf->num_deny_mac,
+						      buf);
+	if (acl_type == DENY_UNLESS_ACCEPTED)
+		return hostapd_ctrl_iface_acl_del_mac(
+			&hapd->conf->accept_mac, &hapd->conf->num_accept_mac,
+			buf);
+
+	return -1;
+}
+
+
+int ap_ctrl_iface_acl_show_mac(struct wpa_supplicant *wpa_s,
+			       enum macaddr_acl acl_type, char *buf,
+			       size_t buflen)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	if (acl_type == ACCEPT_UNLESS_DENIED)
+		return hostapd_ctrl_iface_acl_show_mac(hapd->conf->deny_mac,
+						       hapd->conf->num_deny_mac,
+						       buf, buflen);
+	if (acl_type == DENY_UNLESS_ACCEPTED)
+		return hostapd_ctrl_iface_acl_show_mac(
+			hapd->conf->accept_mac,	hapd->conf->num_accept_mac,
+			buf, buflen);
+
+	return -1;
+}
+
+
+void ap_ctrl_iface_acl_clear_list(struct wpa_supplicant *wpa_s,
+				  enum macaddr_acl acl_type)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return;
+
+	hapd->conf->macaddr_acl = acl_type;
+
+	if (acl_type == ACCEPT_UNLESS_DENIED)
+		hostapd_ctrl_iface_acl_clear_list(&hapd->conf->deny_mac,
+						  &hapd->conf->num_deny_mac);
+	else if (acl_type == DENY_UNLESS_ACCEPTED)
+		hostapd_ctrl_iface_acl_clear_list(&hapd->conf->accept_mac,
+						  &hapd->conf->num_accept_mac);
+}
+
+
+int ap_ctrl_iface_disassoc_deny_mac(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	return hostapd_disassoc_deny_mac(hapd);
+}
+
+
+int ap_ctrl_iface_disassoc_accept_mac(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	return hostapd_disassoc_accept_mac(hapd);
+}
+
+
+int ap_ctrl_iface_set_acl(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else
+		return -1;
+
+	return hostapd_set_acl(hapd);
+}
+
 #endif /* CONFIG_CTRL_IFACE */
 
 
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 7bc1b78..ccd3e7b 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -10,6 +10,8 @@
 #ifndef AP_H
 #define AP_H
 
+enum macaddr_acl;
+
 int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 			     struct wpa_ssid *ssid);
 void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
@@ -38,6 +40,22 @@
 				   const char *txtaddr);
 int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 				 size_t buflen, int verbose);
+int ap_ctrl_iface_disassoc_imminent(struct wpa_supplicant *wpa_s,
+				    const char *buf);
+int ap_ctrl_iface_ess_disassoc(struct wpa_supplicant *wpa_s, const char *buf);
+int ap_ctrl_iface_bss_tm_req(struct wpa_supplicant *wpa_s, const char *buf);
+int ap_ctrl_iface_acl_add_mac(struct wpa_supplicant *wpa_s,
+			      enum macaddr_acl acl_type, const char *buf);
+int ap_ctrl_iface_acl_del_mac(struct wpa_supplicant *wpa_s,
+			      enum macaddr_acl acl_type, const char *buf);
+int ap_ctrl_iface_acl_show_mac(struct wpa_supplicant *wpa_s,
+			       enum macaddr_acl acl_type, char *buf,
+			       size_t buflen);
+void ap_ctrl_iface_acl_clear_list(struct wpa_supplicant *wpa_s,
+				  enum macaddr_acl acl_type);
+int ap_ctrl_iface_disassoc_deny_mac(struct wpa_supplicant *wpa_s);
+int ap_ctrl_iface_disassoc_accept_mac(struct wpa_supplicant *wpa_s);
+int ap_ctrl_iface_set_acl(struct wpa_supplicant *wpa_s);
 void ap_tx_status(void *ctx, const u8 *addr,
 		  const u8 *buf, size_t len, int ack);
 void ap_eapol_tx_status(void *ctx, const u8 *dst,
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index e13783c..429c6e7 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -291,6 +291,7 @@
 	dst->noise = src->noise;
 	dst->level = src->level;
 	dst->tsf = src->tsf;
+	dst->beacon_newer = src->beacon_newer;
 	dst->est_throughput = src->est_throughput;
 	dst->snr = src->snr;
 
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 4078b9b..146aaee 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -108,6 +108,8 @@
 	int level;
 	/** Timestamp of last Beacon/Probe Response frame */
 	u64 tsf;
+	/** Whether the Beacon frame data is known to be newer */
+	bool beacon_newer;
 	/** Time of the last update (i.e., Beacon or Probe Response RX) */
 	struct os_reltime last_update;
 	/** Estimated throughput in kbps */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index f344e1d..c8844bb 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2451,7 +2451,6 @@
 	{ STRe(client_cert, cert.client_cert) },
 	{ STRe(private_key, cert.private_key) },
 	{ STR_KEYe(private_key_passwd, cert.private_key_passwd) },
-	{ STRe(dh_file, cert.dh_file) },
 	{ STRe(subject_match, cert.subject_match) },
 	{ STRe(check_cert_subject, cert.check_cert_subject) },
 	{ STRe(altsubject_match, cert.altsubject_match) },
@@ -2462,7 +2461,6 @@
 	{ STRe(client_cert2, phase2_cert.client_cert) },
 	{ STRe(private_key2, phase2_cert.private_key) },
 	{ STR_KEYe(private_key2_passwd, phase2_cert.private_key_passwd) },
-	{ STRe(dh_file2, phase2_cert.dh_file) },
 	{ STRe(subject_match2, phase2_cert.subject_match) },
 	{ STRe(check_cert_subject2, phase2_cert.check_cert_subject) },
 	{ STRe(altsubject_match2, phase2_cert.altsubject_match) },
@@ -2490,7 +2488,6 @@
 	{ STRe(machine_private_key, machine_cert.private_key) },
 	{ STR_KEYe(machine_private_key_passwd,
 		   machine_cert.private_key_passwd) },
-	{ STRe(machine_dh_file, machine_cert.dh_file) },
 	{ STRe(machine_subject_match, machine_cert.subject_match) },
 	{ STRe(machine_check_cert_subject, machine_cert.check_cert_subject) },
 	{ STRe(machine_altsubject_match, machine_cert.altsubject_match) },
@@ -2506,6 +2503,7 @@
 	{ INTe(machine_ocsp, machine_cert.ocsp) },
 	{ INT(eapol_flags) },
 	{ INTe(sim_num, sim_num) },
+	{ STRe(imsi_privacy_key, imsi_privacy_key) },
 	{ STRe(openssl_ciphers, openssl_ciphers) },
 	{ INTe(erp, erp) },
 #endif /* IEEE8021X_EAPOL */
@@ -2612,6 +2610,7 @@
 	{ INT(macsec_replay_window) },
 	{ INT_RANGE(macsec_port, 1, 65534) },
 	{ INT_RANGE(mka_priority, 0, 255) },
+	{ INT_RANGE(macsec_csindex, 0, 1) },
 	{ FUNC_KEY(mka_cak) },
 	{ FUNC_KEY(mka_ckn) },
 #endif /* CONFIG_MACSEC */
@@ -2753,7 +2752,6 @@
 	os_free(cert->client_cert);
 	os_free(cert->private_key);
 	str_clear_free(cert->private_key_passwd);
-	os_free(cert->dh_file);
 	os_free(cert->subject_match);
 	os_free(cert->check_cert_subject);
 	os_free(cert->altsubject_match);
@@ -2773,6 +2771,7 @@
 	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->machine_identity);
 	bin_clear_free(eap->password, eap->password_len);
 	bin_clear_free(eap->machine_password, eap->machine_password_len);
@@ -2876,6 +2875,7 @@
 		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);
 }
 
@@ -3155,6 +3155,26 @@
 }
 
 
+static const char *removed_fields[] = {
+	"dh_file",
+	"dh_file2",
+	"machine_dh_file",
+	NULL
+};
+
+static bool removed_field(const char *field)
+{
+	int i;
+
+	for (i = 0; removed_fields[i]; i++) {
+		if (os_strcmp(field, removed_fields[i]) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+
 /**
  * wpa_config_set - Set a variable in network configuration
  * @ssid: Pointer to network configuration data
@@ -3203,6 +3223,12 @@
 		break;
 	}
 	if (i == NUM_SSID_FIELDS) {
+		if (removed_field(var)) {
+			wpa_printf(MSG_INFO,
+				   "Line %d: Ignore removed configuration field '%s'",
+				   line, var);
+			return ret;
+		}
 		if (line) {
 			wpa_printf(MSG_ERROR, "Line %d: unknown network field "
 				   "'%s'.", line, var);
@@ -3400,8 +3426,11 @@
 void wpa_config_update_psk(struct wpa_ssid *ssid)
 {
 #ifndef CONFIG_NO_PBKDF2
-	pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
-		    ssid->psk, PMK_LEN);
+	if (pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
+			ssid->psk, PMK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "Error in pbkdf2_sha1()");
+		return;
+	}
 	wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
 			ssid->psk, PMK_LEN);
 	ssid->psk_set = 1;
@@ -3882,6 +3911,12 @@
 		return 0;
 	}
 
+	if (os_strcmp(var, "imsi_privacy_key") == 0) {
+		os_free(cred->imsi_privacy_key);
+		cred->imsi_privacy_key = val;
+		return 0;
+	}
+
 	if (line) {
 		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
 			   line, var);
@@ -4032,6 +4067,9 @@
 	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, "milenage") == 0) {
 		if (!(cred->milenage))
 			return NULL;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 696fb38..77d6ab5 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -182,6 +182,16 @@
 	char *milenage;
 
 	/**
+	 * imsi_privacy_key - IMSI privacy key (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.
+	 */
+	char *imsi_privacy_key;
+
+	/**
 	 * engine - Use an engine for private key operations
 	 */
 	int engine;
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 778da45..978cc6a 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -297,8 +297,8 @@
 	struct wpa_ssid *ssid, *tail, *head;
 	struct wpa_cred *cred, *cred_tail, *cred_head;
 	struct wpa_config *config;
-	int id = 0;
-	int cred_id = 0;
+	static int id = 0;
+	static int cred_id = 0;
 
 	if (name == NULL)
 		return NULL;
@@ -699,7 +699,6 @@
 	STR(client_cert);
 	STR(private_key);
 	STR(private_key_passwd);
-	STR(dh_file);
 	STR(subject_match);
 	STR(check_cert_subject);
 	STR(altsubject_match);
@@ -710,7 +709,6 @@
 	STR(client_cert2);
 	STR(private_key2);
 	STR(private_key2_passwd);
-	STR(dh_file2);
 	STR(subject_match2);
 	STR(check_cert_subject2);
 	STR(altsubject_match2);
@@ -721,7 +719,6 @@
 	STR(machine_client_cert);
 	STR(machine_private_key);
 	STR(machine_private_key_passwd);
-	STR(machine_dh_file);
 	STR(machine_subject_match);
 	STR(machine_check_cert_subject);
 	STR(machine_altsubject_match);
@@ -810,6 +807,7 @@
 	INT(macsec_replay_window);
 	INT(macsec_port);
 	INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
+	INT(macsec_csindex);
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	INT(update_identifier);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index dd8b1d9..4d3d114 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -911,6 +911,13 @@
 	int mka_priority;
 
 	/**
+	 * macsec_csindex - Cipher suite index for MACsec
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_csindex;
+
+	/**
 	 * mka_ckn - MKA pre-shared CKN
 	 */
 #define MACSEC_CKN_MAX_LEN 32
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 1b7f96e..b27c6cf 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -905,7 +905,6 @@
 	STR(client_cert);
 	STR(private_key);
 	STR(private_key_passwd);
-	STR(dh_file);
 	STR(subject_match);
 	STR(check_cert_subject);
 	STR(altsubject_match);
@@ -914,7 +913,6 @@
 	STR(client_cert2);
 	STR(private_key2);
 	STR(private_key2_passwd);
-	STR(dh_file2);
 	STR(subject_match2);
 	STR(check_cert_subject2);
 	STR(altsubject_match2);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index dc6c772..e8a8118 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -674,6 +674,9 @@
 	} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
 		os_free(wpa_s->dpp_configurator_params);
 		wpa_s->dpp_configurator_params = os_strdup(value);
+#ifdef CONFIG_DPP2
+		dpp_controller_set_params(wpa_s->dpp, value);
+#endif /* CONFIG_DPP2 */
 	} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
 		wpa_s->dpp_init_max_tries = atoi(value);
 	} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
@@ -836,6 +839,11 @@
 		wpa_s->disable_scs_support = !!atoi(value);
 	} else if (os_strcasecmp(cmd, "disable_mscs_support") == 0) {
 		wpa_s->disable_mscs_support = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "disable_eapol_g2_tx") == 0) {
+		wpa_s->disable_eapol_g2_tx = !!atoi(value);
+		/* Populate value to wpa_sm if already associated. */
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX,
+				 wpa_s->disable_eapol_g2_tx);
 #ifdef CONFIG_DPP
 	} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
 		os_free(wpa_s->dpp_config_obj_override);
@@ -2306,11 +2314,12 @@
 
 		if (wpa_s->connection_set &&
 		    (wpa_s->connection_ht || wpa_s->connection_vht ||
-		     wpa_s->connection_he)) {
+		     wpa_s->connection_he || wpa_s->connection_eht)) {
 			ret = os_snprintf(pos, end - pos,
 					  "wifi_generation=%u\n",
-					  wpa_s->connection_he ? 6 :
-					  (wpa_s->connection_vht ? 5 : 4));
+					  wpa_s->connection_eht ? 7 :
+					  (wpa_s->connection_he ? 6 :
+					   (wpa_s->connection_vht ? 5 : 4)));
 			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
@@ -8539,6 +8548,7 @@
 	wpa_s->oci_freq_override_ft_assoc = 0;
 	wpa_s->oci_freq_override_fils_assoc = 0;
 	wpa_s->oci_freq_override_wnm_sleep = 0;
+	wpa_s->disable_eapol_g2_tx = 0;
 #ifdef CONFIG_DPP
 	os_free(wpa_s->dpp_config_obj_override);
 	wpa_s->dpp_config_obj_override = NULL;
@@ -8592,6 +8602,8 @@
 
 	if (wpa_s->mac_addr_changed && wpa_s->conf->mac_addr == 0)
 		wpas_restore_permanent_mac_addr(wpa_s);
+
+	wpa_s->conf->ignore_old_scan_res = 0;
 }
 
 
@@ -9984,8 +9996,9 @@
 	int flow_id = 0;
 	bool protection = false;
 	u8 twt_channel = 0;
-	u8 control = BIT(4); /* Control field (IEEE P802.11ax/D8.0 Figure
-			      * 9-687): B4 = TWT Information Frame Disabled */
+	u8 control = BIT(4); /* Control field (IEEE Std 802.11ax-2021,
+			      * Figure 9-687 - Control field format):
+			      * B4 = TWT Information Frame Disabled */
 	const char *tok_s;
 
 	tok_s = os_strstr(cmd, " dialog=");
@@ -10687,7 +10700,7 @@
 #endif /* CONFIG_FILS */
 
 
-static int wpas_ctrl_cmd_debug_level(const char *cmd)
+int wpas_ctrl_cmd_debug_level(const char *cmd)
 {
 	if (os_strcmp(cmd, "PING") == 0 ||
 	    os_strncmp(cmd, "BSS ", 4) == 0 ||
@@ -12019,6 +12032,58 @@
 	} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
 		if (wpas_ap_update_beacon(wpa_s))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
+		if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
+			if (ap_ctrl_iface_acl_add_mac(wpa_s,
+						      DENY_UNLESS_ACCEPTED,
+						      buf + 19) ||
+			    ap_ctrl_iface_set_acl(wpa_s))
+				reply_len = -1;
+		} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
+			if (ap_ctrl_iface_acl_del_mac(wpa_s,
+						      DENY_UNLESS_ACCEPTED,
+						      buf + 19) ||
+			    ap_ctrl_iface_set_acl(wpa_s) ||
+			    ap_ctrl_iface_disassoc_accept_mac(wpa_s))
+				reply_len = -1;
+		} else if (os_strcmp(buf + 11, "SHOW") == 0) {
+			reply_len = ap_ctrl_iface_acl_show_mac(
+				wpa_s, DENY_UNLESS_ACCEPTED,
+				reply, reply_size);
+		} else if (os_strcmp(buf + 11, "CLEAR") == 0) {
+			ap_ctrl_iface_acl_clear_list(wpa_s,
+						     DENY_UNLESS_ACCEPTED);
+			if (ap_ctrl_iface_set_acl(wpa_s) ||
+			    ap_ctrl_iface_disassoc_accept_mac(wpa_s))
+				reply_len = -1;
+		} else {
+			reply_len = -1;
+		}
+	} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
+		if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
+			if (ap_ctrl_iface_acl_add_mac(wpa_s,
+						      ACCEPT_UNLESS_DENIED,
+						      buf + 17) ||
+			    ap_ctrl_iface_set_acl(wpa_s) ||
+			    ap_ctrl_iface_disassoc_deny_mac(wpa_s))
+				reply_len = -1;
+		} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
+			if (ap_ctrl_iface_acl_del_mac(wpa_s,
+						      ACCEPT_UNLESS_DENIED,
+						      buf + 17) ||
+			    ap_ctrl_iface_set_acl(wpa_s))
+				reply_len = -1;
+		} else if (os_strcmp(buf + 9, "SHOW") == 0) {
+			reply_len = ap_ctrl_iface_acl_show_mac(
+				wpa_s, ACCEPT_UNLESS_DENIED, reply, reply_size);
+		} else if (os_strcmp(buf + 9, "CLEAR") == 0) {
+			ap_ctrl_iface_acl_clear_list(wpa_s,
+						     ACCEPT_UNLESS_DENIED);
+			if (ap_ctrl_iface_set_acl(wpa_s))
+				reply_len = -1;
+		} else {
+			reply_len = -1;
+		}
 #endif /* CONFIG_AP */
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
 		wpas_notify_suspend(wpa_s->global);
@@ -12114,6 +12179,17 @@
 		if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18))
 			reply_len = -1;
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_WNM_AP
+	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+		if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
+		if (ap_ctrl_iface_ess_disassoc(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+		if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11))
+			reply_len = -1;
+#endif /* CONFIG_WNM_AP */
 	} else if (os_strcmp(buf, "FLUSH") == 0) {
 		wpa_supplicant_ctrl_iface_flush(wpa_s);
 	} else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
@@ -12312,6 +12388,9 @@
 			if (os_snprintf_error(reply_size, reply_len))
 				reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SET ", 21) == 0) {
+		if (dpp_configurator_set(wpa_s->dpp, buf + 20) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
 		if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0)
 			reply_len = -1;
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index dfbd25a..9842ea1 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -122,6 +122,8 @@
 
 void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
 
+int wpas_ctrl_cmd_debug_level(const char *cmd);
+
 #else /* CONFIG_CTRL_IFACE */
 
 static inline struct ctrl_iface_priv *
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1cbf7fa..1178f40 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -337,6 +337,9 @@
 		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/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 639573d..2052873 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -178,6 +178,9 @@
 		else
 			reply_len = 2;
 	} else {
+		sockaddr_print(wpas_ctrl_cmd_debug_level(buf),
+			       "Control interface recv command from:",
+			       &from, fromlen);
 		reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
 							      &reply_len);
 		reply = reply_buf;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 959a68b..0b1002b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1121,7 +1121,7 @@
 	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
-	const char *capabilities[13];
+	const char *capabilities[14];
 	size_t num_items = 0;
 	struct wpa_global *global = user_data;
 	struct wpa_supplicant *wpa_s;
@@ -1177,6 +1177,9 @@
 #endif /* CONFIG_SUITEB192 */
 	if (ext_key_id_supported)
 		capabilities[num_items++] = "extended_key_id";
+#ifndef CONFIG_WEP
+	capabilities[num_items++] = "wep_disabled";
+#endif /* !CONFIG_WEP */
 
 	return wpas_dbus_simple_array_property_getter(iter,
 						      DBUS_TYPE_STRING,
@@ -3951,7 +3954,7 @@
 	const char *auth_mode;
 	char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX];
 
-	if (wpa_s->wpa_state != WPA_COMPLETED) {
+	if (wpa_s->wpa_state <= WPA_SCANNING) {
 		auth_mode = "INACTIVE";
 	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 6208e9e..95dc4e3 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -101,6 +101,9 @@
 
 # EAP-TLS
 CONFIG_EAP_TLS=y
+# Enable EAP-TLSv1.3 support by default (currently disabled unless explicitly
+# enabled in network configuration)
+#CONFIG_EAP_TLSV1_3=y
 
 # EAL-PEAP
 CONFIG_EAP_PEAP=y
@@ -203,6 +206,9 @@
 # Support VHT overrides (disable VHT, mask MCS rates, etc.)
 #CONFIG_VHT_OVERRIDES=y
 
+# Support HE overrides
+#CONFIG_HE_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
@@ -248,6 +254,9 @@
 # Simultaneous Authentication of Equals (SAE), WPA3-Personal
 CONFIG_SAE=y
 
+# SAE Public Key, WPA3-Personal
+#CONFIG_SAE_PK=y
+
 # Disable scan result processing (ap_scan=1) to save code size by about 1 kB.
 # This can be used if ap_scan=1 mode is never enabled.
 #CONFIG_NO_SCAN_PROCESSING=y
@@ -474,6 +483,16 @@
 # IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
 CONFIG_IEEE80211AC=y
 
+# IEEE 802.11ax HE support (mainly for AP mode)
+CONFIG_IEEE80211AX=y
+
+# IEEE 802.11be EHT support (mainly for AP mode)
+# CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE.
+# Note: This is experimental and work in progress. The definitions are still
+# subject to change and this should not be expected to interoperate with the
+# final IEEE 802.11be version.
+#CONFIG_IEEE80211BE=y
+
 # Wireless Network Management (IEEE Std 802.11v-2011)
 # Note: This is experimental and not complete implementation.
 #CONFIG_WNM=y
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index d570cfe..b6dbc98 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -1148,6 +1148,7 @@
 		return;
 	}
 
+	wpa_s->dpp_pkex_wait_auth_req = false;
 	wpa_s->dpp_gas_client = 0;
 	wpa_s->dpp_gas_server = 0;
 	wpa_s->dpp_auth_ok_on_ack = 0;
@@ -1661,6 +1662,21 @@
 #endif /* CONFIG_DPP2 */
 
 
+#ifdef CONFIG_DPP3
+static void wpas_dpp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !auth->waiting_new_key)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+	wpas_dpp_start_gas_client(wpa_s);
+}
+#endif /* CONFIG_DPP3 */
+
+
 static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
 				 enum gas_query_result result,
 				 const struct wpabuf *adv_proto,
@@ -1714,6 +1730,14 @@
 		return;
 	}
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	if (res == -3) {
+		wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+		eloop_register_timeout(0, 0, wpas_dpp_build_new_key, wpa_s,
+				       NULL);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		goto fail;
@@ -2748,14 +2772,8 @@
 #endif /* CONFIG_DPP2 */
 
 
-enum wpas_dpp_pkex_ver {
-	PKEX_VER_AUTO,
-	PKEX_VER_ONLY_1,
-	PKEX_VER_ONLY_2,
-};
-
 static int wpas_dpp_pkex_init(struct wpa_supplicant *wpa_s,
-			      enum wpas_dpp_pkex_ver ver,
+			      enum dpp_pkex_ver ver,
 			      const struct hostapd_ip_addr *ipaddr,
 			      int tcp_port)
 {
@@ -2908,6 +2926,17 @@
 	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
 		   MAC2STR(src));
 
+	if (wpa_s->dpp_pkex_ver == PKEX_VER_ONLY_1 && v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv2 Exchange Request when configured to be PKEX v1 only");
+		return;
+	}
+	if (wpa_s->dpp_pkex_ver == PKEX_VER_ONLY_2 && !v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv1 Exchange Request when configured to be PKEX v2 only");
+		return;
+	}
+
 	/* TODO: Support multiple PKEX codes by iterating over all the enabled
 	 * values here */
 
@@ -2935,6 +2964,7 @@
 		return;
 	}
 
+	wpa_s->dpp_pkex_wait_auth_req = false;
 	msg = wpa_s->dpp_pkex->exchange_resp;
 	wait_time = wpa_s->max_remain_on_chan;
 	if (wait_time > 2000)
@@ -3051,6 +3081,7 @@
 	wpabuf_free(msg);
 
 	wpas_dpp_pkex_finish(wpa_s, src, freq);
+	wpa_s->dpp_pkex_wait_auth_req = true;
 }
 
 
@@ -3332,11 +3363,13 @@
 		return NULL;
 	}
 
+	auth->conf_resp = resp;
 	if (!resp) {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
 		wpas_notify_dpp_configuration_failure(wpa_s);
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
 	}
-	auth->conf_resp = resp;
 	return resp;
 }
 
@@ -3367,6 +3400,14 @@
 	}
 #endif /* CONFIG_DPP2 */
 
+#ifdef CONFIG_DPP3
+	if (auth->waiting_new_key && ok) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+		wpabuf_free(resp);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
 		   ok);
 	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
@@ -3493,6 +3534,8 @@
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Starting network introduction protocol to derive PMKSA for "
 		   MACSTR, MAC2STR(bss->bssid));
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 
 	len = 5 + 4 + os_strlen(ssid->dpp_connector);
 #ifdef CONFIG_DPP2
@@ -3614,6 +3657,11 @@
 {
 	struct dpp_bootstrap_info *own_bi;
 	const char *pos, *end;
+#ifdef CONFIG_DPP3
+	enum dpp_pkex_ver ver = PKEX_VER_AUTO;
+#else /* CONFIG_DPP3 */
+	enum dpp_pkex_ver ver = PKEX_VER_ONLY_1;
+#endif /* CONFIG_DPP3 */
 	int tcp_port = DPP_TCP_PORT;
 	struct hostapd_ip_addr *ipaddr = NULL;
 #ifdef CONFIG_DPP2
@@ -3679,27 +3727,22 @@
 	if (!wpa_s->dpp_pkex_code)
 		return -1;
 
+	pos = os_strstr(cmd, " ver=");
+	if (pos) {
+		int v;
+
+		pos += 5;
+		v = atoi(pos);
+		if (v == 1)
+			ver = PKEX_VER_ONLY_1;
+		else if (v == 2)
+			ver = PKEX_VER_ONLY_2;
+		else
+			return -1;
+	}
+	wpa_s->dpp_pkex_ver = ver;
+
 	if (os_strstr(cmd, " init=1")) {
-#ifdef CONFIG_DPP3
-		enum wpas_dpp_pkex_ver ver = PKEX_VER_AUTO;
-#else /* CONFIG_DPP3 */
-		enum wpas_dpp_pkex_ver ver = PKEX_VER_ONLY_1;
-#endif /* CONFIG_DPP3 */
-
-		pos = os_strstr(cmd, " ver=");
-		if (pos) {
-			int v;
-
-			pos += 5;
-			v = atoi(pos);
-			if (v == 1)
-				ver = PKEX_VER_ONLY_1;
-			else if (v == 2)
-				ver = PKEX_VER_ONLY_2;
-			else
-				return -1;
-		}
-
 		if (wpas_dpp_pkex_init(wpa_s, ver, ipaddr, tcp_port) < 0)
 			return -1;
 	} else {
@@ -3751,12 +3794,13 @@
 
 void wpas_dpp_stop(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->dpp_auth || wpa_s->dpp_pkex)
+	if (wpa_s->dpp_auth || wpa_s->dpp_pkex || wpa_s->dpp_pkex_wait_auth_req)
 		offchannel_send_action_done(wpa_s);
 	dpp_auth_deinit(wpa_s->dpp_auth);
 	wpa_s->dpp_auth = NULL;
 	dpp_pkex_free(wpa_s->dpp_pkex);
 	wpa_s->dpp_pkex = NULL;
+	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);
 }
@@ -3822,6 +3866,9 @@
 	dpp_free_reconfig_id(wpa_s->dpp_reconfig_id);
 	wpa_s->dpp_reconfig_id = NULL;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	eloop_cancel_timeout(wpas_dpp_build_new_key, wpa_s, NULL);
+#endif /* CONFIG_DPP3 */
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
 	wpas_dpp_stop(wpa_s);
@@ -4236,6 +4283,7 @@
 
 	wpas_dpp_chirp_stop(wpa_s);
 	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_netrole = DPP_NETROLE_STA;
 	wpa_s->dpp_qr_mutual = 0;
 	wpa_s->dpp_chirp_bi = bi;
 	wpa_s->dpp_presence_announcement = dpp_build_presence_announcement(bi);
@@ -4320,6 +4368,7 @@
 	}
 	wpas_dpp_chirp_stop(wpa_s);
 	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	wpa_s->dpp_netrole = DPP_NETROLE_STA;
 	wpa_s->dpp_qr_mutual = 0;
 	wpa_s->dpp_reconfig_ssid = ssid;
 	wpa_s->dpp_reconfig_ssid_id = ssid->id;
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index e256ac5..f806895 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "utils/ext_password.h"
 #include "common/version.h"
+#include "crypto/crypto.h"
 #include "crypto/tls.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
@@ -732,16 +733,27 @@
 	case EAP_TYPE_IDENTITY: return "Identity";
 	case EAP_TYPE_NOTIFICATION: return "Notification";
 	case EAP_TYPE_NAK: return "Nak";
-	case EAP_TYPE_TLS: return "TLS";
-	case EAP_TYPE_TTLS: return "TTLS";
-	case EAP_TYPE_PEAP: return "PEAP";
-	case EAP_TYPE_SIM: return "SIM";
-	case EAP_TYPE_GTC: return "GTC";
+
 	case EAP_TYPE_MD5: return "MD5";
 	case EAP_TYPE_OTP: return "OTP";
+	case EAP_TYPE_GTC: return "GTC";
+	case EAP_TYPE_TLS: return "TLS";
+	case EAP_TYPE_LEAP: return "LEAP";
+	case EAP_TYPE_SIM: return "SIM";
+	case EAP_TYPE_TTLS: return "TTLS";
+	case EAP_TYPE_AKA: return "AKA";
+	case EAP_TYPE_PEAP: return "PEAP";
+	case EAP_TYPE_MSCHAPV2: return "MSCHAPv2";
 	case EAP_TYPE_FAST: return "FAST";
-	case EAP_TYPE_SAKE: return "SAKE";
+	case EAP_TYPE_PAX: return "PAX";
 	case EAP_TYPE_PSK: return "PSK";
+	case EAP_TYPE_SAKE: return "SAKE";
+	case EAP_TYPE_IKEV2: return "IKEv2";
+	case EAP_TYPE_AKA_PRIME: return "AKA-PRIME";
+	case EAP_TYPE_GPSK: return "GPSK";
+	case EAP_TYPE_PWD: return "PWD";
+	case EAP_TYPE_EKE: return "EKE";
+	case EAP_TYPE_TEAP: return "TEAP";
 	default: return "Unknown";
 	}
 }
@@ -761,20 +773,20 @@
 	msg = e->last_recv_radius;
 
 	eap = radius_msg_get_eap(msg);
-	if (eap == NULL) {
-		/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
+	if (!eap) {
+		/* RFC 3579, Chap. 2.6.3:
 		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
 		 * attribute */
-		wpa_printf(MSG_DEBUG, "could not extract "
-			       "EAP-Message from RADIUS message");
+		wpa_printf(MSG_DEBUG,
+			   "could not extract EAP-Message from RADIUS message");
 		wpabuf_free(e->last_eap_radius);
 		e->last_eap_radius = NULL;
 		return;
 	}
 
 	if (wpabuf_len(eap) < sizeof(*hdr)) {
-		wpa_printf(MSG_DEBUG, "too short EAP packet "
-			       "received from authentication server");
+		wpa_printf(MSG_DEBUG,
+			   "too short EAP packet received from authentication server");
 		wpabuf_free(eap);
 		return;
 	}
@@ -810,11 +822,11 @@
 		wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
 		break;
 	}
-	wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
-		       "id=%d len=%d) from RADIUS server: %s",
-		      hdr->code, hdr->identifier, ntohs(hdr->length), buf);
-
-	/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
+	buf[sizeof(buf) - 1] = '\0';
+	wpa_printf(MSG_DEBUG,
+		   "decapsulated EAP packet (code=%d id=%d len=%d) from RADIUS server: %s",
+		   hdr->code, hdr->identifier, be_to_host16(hdr->length),
+		   buf);
 
 	wpabuf_free(e->last_eap_radius);
 	e->last_eap_radius = eap;
@@ -847,7 +859,7 @@
 
 	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
 				      shared_secret_len);
-	if (keys && keys->send == NULL && keys->recv == NULL) {
+	if (keys && !keys->send && !keys->recv) {
 		os_free(keys);
 		keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
 						 shared_secret_len);
@@ -908,20 +920,19 @@
 	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
 				0) < 0 &&
 	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "Allowing RADIUS "
-			      "Access-Reject without Message-Authenticator "
-			      "since it does not include EAP-Message\n");
+		wpa_printf(MSG_DEBUG,
+			   "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
 	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
 				     req, 1)) {
-		printf("Incoming RADIUS packet did not have correct "
-		       "Message-Authenticator - dropped\n");
-		return RADIUS_RX_UNKNOWN;
+		wpa_printf(MSG_INFO,
+			   "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
 	}
 
 	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
 	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
 	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
-		printf("Unknown RADIUS message code\n");
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
 		return RADIUS_RX_UNKNOWN;
 	}
 
@@ -1549,6 +1560,7 @@
 	else
 		printf("SUCCESS\n");
 
+	crypto_unload();
 	os_program_deinit();
 
 	return ret;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 0003d1f..7234de4 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -149,19 +149,22 @@
 }
 
 
-static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
+static struct wpa_bss *
+wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
-	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 
 	if (!bss) {
 		wpa_supplicant_update_scan_results(wpa_s);
 
 		/* Get the BSS from the new scan results */
-		bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 	}
 
 	if (bss)
 		wpa_s->current_bss = bss;
+
+	return bss;
 }
 
 
@@ -173,7 +176,7 @@
 	int res;
 
 	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
-		wpa_supplicant_update_current_bss(wpa_s);
+		wpa_supplicant_update_current_bss(wpa_s, wpa_s->bssid);
 
 		if (wpa_s->current_ssid->ssid_len == 0)
 			return 0; /* current profile still in use */
@@ -250,7 +253,7 @@
 	old_ssid = wpa_s->current_ssid;
 	wpa_s->current_ssid = ssid;
 
-	wpa_supplicant_update_current_bss(wpa_s);
+	wpa_supplicant_update_current_bss(wpa_s, wpa_s->bssid);
 
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 	wpa_supplicant_initiate_eapol(wpa_s);
@@ -567,6 +570,7 @@
 #ifdef CONFIG_WEP
 	int wep_ok;
 #endif /* CONFIG_WEP */
+	bool is_6ghz_bss = is_6ghz_freq(bss->freq);
 
 	ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
 	if (ret >= 0)
@@ -581,6 +585,13 @@
 #endif /* CONFIG_WEP */
 
 	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (is_6ghz_bss && !rsn_ie) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - 6 GHz BSS without RSNE");
+		return 0;
+	}
+
 	while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) {
 		proto_match++;
 
@@ -595,6 +606,16 @@
 		if (!ie.has_group)
 			ie.group_cipher = wpa_default_rsn_cipher(bss->freq);
 
+		if (is_6ghz_bss) {
+			/* WEP and TKIP are not allowed on 6 GHz */
+			ie.pairwise_cipher &= ~(WPA_CIPHER_WEP40 |
+						WPA_CIPHER_WEP104 |
+						WPA_CIPHER_TKIP);
+			ie.group_cipher &= ~(WPA_CIPHER_WEP40 |
+					     WPA_CIPHER_WEP104 |
+					     WPA_CIPHER_TKIP);
+		}
+
 #ifdef CONFIG_WEP
 		if (wep_ok &&
 		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
@@ -636,6 +657,21 @@
 			break;
 		}
 
+		if (is_6ghz_bss) {
+			/* 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");
+				break;
+			}
+
+			/* WPA PSK is not allowed on the 6 GHz band */
+			ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK |
+					 WPA_KEY_MGMT_FT_PSK |
+					 WPA_KEY_MGMT_PSK_SHA256);
+		}
+
 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
 			if (debug_print)
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -666,6 +702,13 @@
 		return 1;
 	}
 
+	if (is_6ghz_bss) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - 6 GHz BSS without matching RSNE");
+		return 0;
+	}
+
 	if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED &&
 	    (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) {
 		if (debug_print)
@@ -1317,7 +1360,10 @@
 	}
 
 #ifdef CONFIG_SAE
-	if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
+	/* 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) &&
 #ifdef CONFIG_DRIVER_NL80211_BRCM
 	    !(wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt)) &&
@@ -1412,7 +1458,7 @@
 	if (wpa_s->ignore_assoc_disallow)
 		goto skip_assoc_disallow;
 #endif /* CONFIG_TESTING_OPTIONS */
-	assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+	assoc_disallow = wpas_mbo_check_assoc_disallow(bss);
 	if (assoc_disallow && assoc_disallow[1] >= 1) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG,
@@ -2961,6 +3007,8 @@
 				 BAND_2_4_GHZ);
 			wpa_s->connection_he = req_elems.he_capabilities &&
 				resp_elems.he_capabilities;
+			wpa_s->connection_eht = req_elems.eht_capabilities &&
+				resp_elems.eht_capabilities;
 
 			int max_nss_rx_req = get_max_nss_capability(&req_elems, 1);
 			int max_nss_rx_resp = get_max_nss_capability(&resp_elems, 1);
@@ -3181,7 +3229,7 @@
 		p += len;
 	}
 #endif /* CONFIG_SME */
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (((wpa_s->key_mgmt == WPA_KEY_MGMT_FT_PSK) ||
 		(wpa_s->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) ||
 		(wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE) ||
@@ -3189,7 +3237,7 @@
 		wpa_ft_is_completed(wpa_s->wpa)) {
 		return 0;
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 	/* Process FT when SME is in the driver */
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
@@ -3352,9 +3400,9 @@
 #if defined(CONFIG_FILS) || defined(CONFIG_MBO)
 	struct wpa_bss *bss;
 #endif /* CONFIG_FILS || CONFIG_MBO */
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	struct wpa_ie_data ie;
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface) {
@@ -3372,7 +3420,7 @@
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 	wpa_s->own_reconnect_req = 0;
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (!(wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0)) {
 		struct wpa_ft_ies parse;
 		/* Check for FT reassociation is done by the driver */
@@ -3391,9 +3439,29 @@
 		}
 #endif  /* CONFIG_IEEE80211R */
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 	ft_completed = wpa_ft_is_completed(wpa_s->wpa);
+
+	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
+		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,
+			MAC2STR(bssid));
+		if (!wpa_supplicant_update_current_bss(wpa_s, bssid)) {
+			wpa_printf(MSG_ERROR,
+				   "Can't find target AP's information!");
+			return;
+		}
+		wpa_supplicant_assoc_update_ie(wpa_s);
+	}
+
 	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 		return;
 	/*
@@ -3404,14 +3472,7 @@
 	if (!ft_completed)
 		ft_completed = wpa_fils_is_completed(wpa_s->wpa);
 
-	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
-		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
-		wpa_supplicant_deauthenticate(
-			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-		return;
-	}
-
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#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)) {
@@ -3419,7 +3480,8 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "Pass the PMK to the driver");
 		wpa_sm_install_pmk(wpa_s->wpa);
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
 	if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
 		if (os_reltime_initialized(&wpa_s->session_start)) {
@@ -3556,7 +3618,7 @@
 		 */
 		eapol_sm_notify_portValid(wpa_s->eapol, true);
 	}
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (ft_completed && wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
 		if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
 			wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID, key_mgmt: 0x%0x",
@@ -3569,7 +3631,8 @@
 		wpa_s->assoc_freq = data->assoc_info.freq;
 		wpa_sm_notify_brcm_ft_reassoc(wpa_s->wpa, bssid);
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+
 	wpa_s->last_eapol_matches_bssid = 0;
 
 #ifdef CONFIG_TESTING_OPTIONS
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index ca380b9..9a459c2 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1065,6 +1065,12 @@
 			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)
+			goto fail;
+	}
+
 	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
 	if (!only_add)
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 51a8a02..9229eb5 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -12,6 +12,7 @@
 #endif /* __linux__ */
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "fst/fst.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -403,6 +404,7 @@
 #endif /* CONFIG_MATCH_IFACE */
 	os_free(params.pid_file);
 
+	crypto_unload();
 	os_program_deinit();
 
 	return exitcode;
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 8ac73ef..31d0fce 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -65,14 +65,18 @@
 }
 
 
-const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
+static const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss,
+					enum mbo_attr_id attr, bool beacon)
 {
 	const u8 *mbo, *end;
 
 	if (!bss)
 		return NULL;
 
-	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+	if (beacon)
+		mbo = wpa_bss_get_vendor_ie_beacon(bss, MBO_IE_VENDOR_TYPE);
+	else
+		mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
 	if (!mbo)
 		return NULL;
 
@@ -83,6 +87,19 @@
 }
 
 
+const u8 * wpas_mbo_check_assoc_disallow(struct wpa_bss *bss)
+{
+	const u8 *assoc_disallow;
+
+	assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW,
+					       bss->beacon_newer);
+	if (assoc_disallow && assoc_disallow[1] >= 1)
+		return assoc_disallow;
+
+	return NULL;
+}
+
+
 void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			struct wpa_ssid *ssid)
 {
@@ -92,8 +109,8 @@
 	wpa_s->disable_mbo_oce = 0;
 	if (!bss)
 		return;
-	mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND);
-	oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND);
+	mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND, false);
+	oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND, false);
 	if (!mbo && !oce)
 		return;
 	if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON))
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index d6b8a1a..b67396d 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -225,12 +225,13 @@
 		    ifmsh->conf->ieee80211n,
 		    ifmsh->conf->ieee80211ac,
 		    ifmsh->conf->ieee80211ax,
+		    false,
 		    ifmsh->conf->secondary_channel,
 		    hostapd_get_oper_chwidth(ifmsh->conf),
 		    hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
 		    hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
 		    ifmsh->conf->vht_capab,
-		    he_capab)) {
+		    he_capab, NULL)) {
 		wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
 		wpa_supplicant_mesh_deinit(wpa_s, true);
 		return -1;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index c52e88f..8e31824 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -420,6 +420,10 @@
 {
 	if (wpa_s->next_ssid == ssid)
 		wpa_s->next_ssid = NULL;
+	if (wpa_s->last_ssid == ssid)
+		wpa_s->last_ssid = NULL;
+	if (wpa_s->current_ssid == ssid)
+		wpa_s->current_ssid = NULL;
 	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 &&
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index f1d033b..3c395e9 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -8899,7 +8899,7 @@
 				hapd->conf->ssid.wpa_psk = psk->next;
 			rem = psk;
 			psk = psk->next;
-			os_free(rem);
+			bin_clear_free(rem, sizeof(*rem));
 		} else {
 			prev = psk;
 			psk = psk->next;
@@ -9730,9 +9730,12 @@
 		goto out;
 	}
 
-	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"P2P CSA: CSA to a different band is not supported");
+	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode &&
+	    (wpa_s->ap_iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A ||
+	     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);
 		ret = -1;
 		goto out;
 	}
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index baf4c26..dc21b6a 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -1000,6 +1000,7 @@
 	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)) {
@@ -1079,9 +1080,14 @@
 	pasn->group = group;
 	pasn->freq = freq;
 
-	if (wpa_s->conf->force_kdk_derivation ||
-	    (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF &&
-	     ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+	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;
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 31b5532..3ae99da 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -13,6 +13,7 @@
 #include <assert.h>
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eloop.h"
@@ -365,6 +366,7 @@
 
 	eloop_destroy();
 
+	crypto_unload();
 	os_program_deinit();
 
 	return ret;
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index cf107eb..4457b6c 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -501,7 +501,7 @@
 
 
 static int * wpas_add_channels(const struct oper_class_map *op,
-			       struct hostapd_hw_modes *mode, int active,
+			       struct hostapd_hw_modes *mode,
 			       const u8 *channels, const u8 size)
 {
 	int *freqs, *next_freq;
@@ -532,7 +532,7 @@
 		enum chan_allowed res = verify_channel(mode, op->op_class, chan,
 						       op->bw);
 
-		if (res == NOT_ALLOWED || (res == NO_IR && active))
+		if (res == NOT_ALLOWED)
 			continue;
 
 		if (wpas_add_channel(op->op_class, chan, num_primary_channels,
@@ -554,7 +554,7 @@
 
 
 static int * wpas_op_class_freqs(const struct oper_class_map *op,
-				 struct hostapd_hw_modes *mode, int active)
+				 struct hostapd_hw_modes *mode)
 {
 	u8 channels_80mhz_5ghz[] = { 42, 58, 106, 122, 138, 155, 171 };
 	u8 channels_160mhz_5ghz[] = { 50, 114, 163 };
@@ -581,11 +581,11 @@
 			ARRAY_SIZE(channels_160mhz_5ghz);
 	}
 
-	return wpas_add_channels(op, mode, active, channels, num_chan);
+	return wpas_add_channels(op, mode, channels, num_chan);
 }
 
 
-static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s, int active,
+static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s,
 				       const char *country, const u8 *subelems,
 				       size_t len)
 {
@@ -633,7 +633,7 @@
 		 * by a corresponding AP Channel Report element as specified in
 		 * IEEE Std 802.11-2016, 11.11.9.1.
 		 */
-		new_freqs = wpas_add_channels(op, mode, active, pos, left);
+		new_freqs = wpas_add_channels(op, mode, pos, left);
 		if (new_freqs)
 			int_array_concat(&freqs, new_freqs);
 
@@ -648,7 +648,7 @@
 
 
 static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s,
-				       u8 op_class, u8 chan, int active,
+				       u8 op_class, u8 chan,
 				       const u8 *subelems, size_t len)
 {
 	int *freqs = NULL, *ext_freqs = NULL;
@@ -678,7 +678,7 @@
 
 	switch (chan) {
 	case 0:
-		freqs = wpas_op_class_freqs(op, mode, active);
+		freqs = wpas_op_class_freqs(op, mode);
 		if (!freqs)
 			return NULL;
 		break;
@@ -686,14 +686,13 @@
 		/* freqs will be added from AP channel subelements */
 		break;
 	default:
-		freqs = wpas_add_channels(op, mode, active, &chan, 1);
+		freqs = wpas_add_channels(op, mode, &chan, 1);
 		if (!freqs)
 			return NULL;
 		break;
 	}
 
-	ext_freqs = wpas_channel_report_freqs(wpa_s, active, country, subelems,
-					      len);
+	ext_freqs = wpas_channel_report_freqs(wpa_s, country, subelems, len);
 	if (ext_freqs) {
 		int_array_concat(&freqs, ext_freqs);
 		os_free(ext_freqs);
@@ -1220,10 +1219,9 @@
 		goto out;
 	}
 
-	params->freqs = wpas_beacon_request_freqs(
-		wpa_s, req->oper_class, req->channel,
-		req->mode == BEACON_REPORT_MODE_ACTIVE,
-		req->variable, len - sizeof(*req));
+	params->freqs = wpas_beacon_request_freqs(wpa_s, req->oper_class,
+						  req->channel, req->variable,
+						  len - sizeof(*req));
 	if (!params->freqs) {
 		wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
 		reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 2112ecd..a683eac 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -2181,20 +2181,33 @@
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *r = scan_res->res[i];
 		u8 *pos;
+		const u8 *ssid_ie, *ssid = NULL;
+		size_t ssid_len = 0;
+
+		ssid_ie = wpa_scan_get_ie(r, WLAN_EID_SSID);
+		if (ssid_ie) {
+			ssid = ssid_ie + 2;
+			ssid_len = ssid_ie[1];
+		}
+
 		if (r->flags & WPA_SCAN_LEVEL_DBM) {
 			int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
 
-			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-				   "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
-				   MAC2STR(r->bssid), r->freq, r->qual,
+			wpa_printf(MSG_EXCESSIVE, MACSTR
+				   " ssid=%s freq=%d qual=%d noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
+				   MAC2STR(r->bssid),
+				   wpa_ssid_txt(ssid, ssid_len),
+				   r->freq, r->qual,
 				   r->noise, noise_valid ? "" : "~", r->level,
 				   r->snr, r->snr >= GREAT_SNR ? "*" : "",
 				   r->flags,
 				   r->age, r->est_throughput);
 		} else {
-			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-				   "noise=%d level=%d flags=0x%x age=%u est=%u",
-				   MAC2STR(r->bssid), r->freq, r->qual,
+			wpa_printf(MSG_EXCESSIVE, MACSTR
+				   " ssid=%s freq=%d qual=%d noise=%d level=%d flags=0x%x age=%u est=%u",
+				   MAC2STR(r->bssid),
+				   wpa_ssid_txt(ssid, ssid_len),
+				   r->freq, r->qual,
 				   r->noise, r->level, r->flags, r->age,
 				   r->est_throughput);
 		}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 0b02f75..cc55fa6 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1366,7 +1366,7 @@
 			" auth_type=%u auth_transaction=%u status_code=%u",
 			MAC2STR(bssid), WLAN_AUTH_SAE,
 			auth_transaction, status_code);
-		return -1;
+		return -2;
 	}
 
 	if (auth_transaction == 1) {
@@ -1517,7 +1517,10 @@
 		if (res < 0) {
 			/* Notify failure to the driver */
 			sme_send_external_auth_status(
-				wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE);
+				wpa_s,
+				res == -2 ?
+				le_to_host16(header->u.auth.status_code) :
+				WLAN_STATUS_UNSPECIFIED_FAILURE);
 			return;
 		}
 		if (res != 1)
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 033589f..0e2315d 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1418,11 +1418,11 @@
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
-	"dh_file", "subject_match", "altsubject_match",
+	"subject_match", "altsubject_match",
 	"check_cert_subject",
 	"domain_suffix_match", "domain_match", "ca_cert2", "ca_path2",
 	"client_cert2", "private_key2", "private_key2_passwd",
-	"dh_file2", "subject_match2", "altsubject_match2",
+	"subject_match2", "altsubject_match2",
 	"check_cert_subject2",
 	"domain_suffix_match2", "domain_match2", "phase1", "phase2",
 	"pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id",
@@ -2046,6 +2046,20 @@
 	return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
 }
 
+
+static int wpa_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv);
+}
+
 #endif /* CONFIG_AP */
 
 
@@ -2894,6 +2908,31 @@
 #endif /* CONFIG_WNM */
 
 
+#ifdef CONFIG_WNM_AP
+
+static int wpa_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DISASSOC_IMMINENT", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "ESS_DISASSOC", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "BSS_TM_REQ", 1, argc, argv);
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
 static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	if (argc == 0)
@@ -3599,6 +3638,10 @@
 	{ "update_beacon", wpa_cli_cmd_update_beacon, NULL,
 	  cli_cmd_flag_none,
 	  "= update Beacon frame contents"},
+	{ "accept_acl", wpa_cli_cmd_accept_macacl, NULL, cli_cmd_flag_none,
+	  "=Add/Delete/Show/Clear allow MAC ACL" },
+	{ "deny_acl", wpa_cli_cmd_deny_macacl, NULL, cli_cmd_flag_none,
+	  "=Add/Delete/Show/Clear deny MAC ACL" },
 #endif /* CONFIG_AP */
 	{ "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
 	  "= notification of suspend/hibernate" },
@@ -3845,6 +3888,14 @@
 	  " [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]"
 	  " = Send BSS Transition Management Query" },
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_WNM_AP
+	{ "disassoc_imminent", wpa_cli_cmd_disassoc_imminent, NULL, cli_cmd_flag_none,
+	  "= send Disassociation Imminent notification" },
+	{ "ess_disassoc", wpa_cli_cmd_ess_disassoc, NULL, cli_cmd_flag_none,
+	  "= send ESS Dissassociation Imminent notification" },
+	{ "bss_tm_req", wpa_cli_cmd_bss_tm_req, NULL, cli_cmd_flag_none,
+	  "= send BSS Transition Management Request" },
+#endif /* CONFIG_WNM_AP */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
 	{ "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index 538997e..d9c07e6 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -58,7 +58,11 @@
 		return 1;
 	}
 
-	pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
+	if (pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32)
+	    != 0) {
+		fprintf(stderr, "Error in pbkdf2_sha1()\n");
+		return 1;
+	}
 
 	printf("network={\n");
 	printf("\tssid=\"%s\"\n", ssid);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 99af85e..4503ae9 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1715,6 +1715,8 @@
 			 wpa_s->oci_freq_override_ft_assoc);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC,
 			 wpa_s->oci_freq_override_fils_assoc);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DISABLE_EAPOL_G2_TX,
+			 wpa_s->disable_eapol_g2_tx);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	/* Extended Key ID is only supported in infrastructure BSS so far */
@@ -1786,9 +1788,15 @@
 		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 		    ssid->passphrase && !sae_only) {
 			u8 psk[PMK_LEN];
-		        pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
-				    4096, psk, PMK_LEN);
-		        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from 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);
 			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
 			psk_set = 1;
@@ -1822,8 +1830,14 @@
 #ifndef CONFIG_NO_PBKDF2
 			if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
 			{
-				pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
-					    4096, psk, PMK_LEN);
+				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)",
@@ -1893,7 +1907,9 @@
 
 #ifdef CONFIG_DRIVER_NL80211_BRCM
 	if ((wpa_s->key_mgmt & WPA_KEY_MGMT_CROSS_AKM_ROAM) &&
-	    IS_CROSS_AKM_ROAM_KEY_MGMT(ssid->key_mgmt)) {
+		IS_CROSS_AKM_ROAM_KEY_MGMT(ssid->key_mgmt) &&
+		(wpa_s->group_cipher == WPA_CIPHER_CCMP) &&
+		(wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_PSK;
 		wpa_dbg(wpa_s, MSG_INFO,
 			"WPA: Updating to KEY_MGMT SAE+PSK for seamless roaming");
@@ -2247,6 +2263,7 @@
 	else
 		rand_style = ssid->mac_addr;
 
+	wpa_s->eapol_failed = 0;
 	wpa_s->multi_ap_ie = 0;
 	wmm_ac_clear_saved_tspecs(wpa_s);
 	wpa_s->reassoc_same_bss = 0;
@@ -2779,9 +2796,11 @@
 				    freq->channel, ssid->enable_edmg,
 				    ssid->edmg_channel, freq->ht_enabled,
 				    vht_freq.vht_enabled, freq->he_enabled,
+				    false,
 				    freq->sec_channel_offset,
 				    chwidth, seg0, seg1, vht_caps,
-				    &mode->he_capab[ieee80211_mode]) != 0)
+				    &mode->he_capab[ieee80211_mode],
+				    NULL) != 0)
 		return;
 
 	*freq = vht_freq;
@@ -4280,7 +4299,7 @@
  */
 int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id)
 {
-	struct wpa_ssid *ssid;
+	struct wpa_ssid *ssid, *prev = wpa_s->current_ssid;
 	int was_disabled;
 
 	ssid = wpa_config_get_network(wpa_s->conf, id);
@@ -4288,10 +4307,7 @@
 		return -1;
 	wpas_notify_network_removed(wpa_s, ssid);
 
-	if (wpa_s->last_ssid == ssid)
-		wpa_s->last_ssid = NULL;
-
-	if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) {
+	if (ssid == prev || !prev) {
 #ifdef CONFIG_SME
 		wpa_s->sme.prev_bssid_set = 0;
 #endif /* CONFIG_SME */
@@ -4302,7 +4318,7 @@
 		eapol_sm_invalidate_cached_session(wpa_s->eapol);
 	}
 
-	if (ssid == wpa_s->current_ssid) {
+	if (ssid == prev) {
 		wpa_sm_set_config(wpa_s->wpa, NULL);
 		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 
@@ -4365,8 +4381,6 @@
 
 		id = ssid->id;
 		ssid = ssid->next;
-		if (wpa_s->last_ssid == remove_ssid)
-			wpa_s->last_ssid = NULL;
 		wpas_notify_network_removed(wpa_s, remove_ssid);
 		wpa_config_remove_network(wpa_s->conf, id);
 	}
@@ -6747,6 +6761,7 @@
 		wpa_s->drv_flags2 = capa.flags2;
 		wpa_s->drv_enc = capa.enc;
 		wpa_s->drv_rrm_flags = capa.rrm_flags;
+		wpa_s->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
 		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
@@ -7162,11 +7177,15 @@
 		return NULL;
 	}
 
-	/* Notify the control interfaces about new networks for non p2p mgmt
-	 * ifaces. */
-	if (iface->p2p_mgmt == 0) {
-		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+	/* Notify the control interfaces about new networks */
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (iface->p2p_mgmt == 0) {
 			wpas_notify_network_added(wpa_s, ssid);
+		} else if (ssid->ssid_len > P2P_WILDCARD_SSID_LEN
+				&& os_strncmp((const char *) ssid->ssid,
+					P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) {
+			wpas_notify_persistent_group_added(wpa_s, ssid);
+		}
 	}
 
 	wpa_s->next = global->ifaces;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 9376b03..fd54fef 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1234,14 +1234,6 @@
 #	to blob://<blob name>.
 # private_key_passwd: Password for private key file (if left out, this will be
 #	asked through control interface)
-# dh_file: File path to DH/DSA parameters file (in PEM format)
-#	This is an optional configuration file for setting parameters for an
-#	ephemeral DH key exchange. In most cases, the default RSA
-#	authentication does not use this configuration. However, it is possible
-#	setup RSA to use ephemeral DH key exchange. In addition, ciphers with
-#	DSA keys always use ephemeral DH keys. This can be used to achieve
-#	forward secrecy. If the file is in DSA parameters format, it will be
-#	automatically converted into DH params.
 # subject_match: Substring to be matched against the subject of the
 #	authentication server certificate. If this string is set, the server
 #	certificate is only accepted if it contains this string in the subject.
@@ -1378,6 +1370,11 @@
 # tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default)
 # tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in
 #	particular when using Suite B with RSA keys of >= 3K (3072) bits
+# allow_unsafe_renegotiation=1 - allow connection with a TLS server that does
+#	not support safe renegotiation (RFC 5746); please note that this
+#	workaround should be only when having to authenticate with an old
+#	authentication server that cannot be updated to use secure TLS
+#	implementation.
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 1d2c3e2..3adb819 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -14,6 +14,7 @@
 #include "common/defs.h"
 #include "common/sae.h"
 #include "common/wpa_ctrl.h"
+#include "common/dpp.h"
 #include "crypto/sha384.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "wps/wps_defs.h"
@@ -929,6 +930,7 @@
 	u64 drv_flags2;
 	unsigned int drv_enc;
 	unsigned int drv_rrm_flags;
+	unsigned int drv_max_acl_mac_addrs;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -965,6 +967,7 @@
 	struct os_reltime pending_eapol_rx_time;
 	u8 pending_eapol_rx_src[ETH_ALEN];
 	unsigned int last_eapol_matches_bssid:1;
+	unsigned int eapol_failed:1;
 	unsigned int eap_expected_failure:1;
 	unsigned int reattach:1; /* reassociation to the same BSS requested */
 	unsigned int mac_addr_changed:1;
@@ -976,6 +979,7 @@
 	unsigned int connection_ht:1;
 	unsigned int connection_vht:1;
 	unsigned int connection_he:1;
+	unsigned int connection_eht:1;
 	unsigned int connection_max_nss_rx:4;
 	unsigned int connection_max_nss_tx:4;
 	unsigned int connection_channel_bandwidth:5;
@@ -1368,6 +1372,7 @@
 	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 */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1483,6 +1488,7 @@
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
 	char *dpp_pkex_identifier;
+	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
 	char *dpp_configurator_params;
 	struct os_reltime dpp_last_init;
@@ -1495,6 +1501,7 @@
 	u8 dpp_last_ssid[SSID_MAX_LEN];
 	size_t dpp_last_ssid_len;
 	bool dpp_conf_backup_received;
+	bool dpp_pkex_wait_auth_req;
 #ifdef CONFIG_DPP2
 	struct dpp_pfs *dpp_pfs;
 	int dpp_pfs_fallback;
@@ -1707,7 +1714,7 @@
 int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
 		int add_oce_capa);
 const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
-const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
+const u8 * wpas_mbo_check_assoc_disallow(struct wpa_bss *bss);
 void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			struct wpa_ssid *ssid);
 const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 008ec08..c2bd45f 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -298,29 +298,37 @@
 		EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
 
 	if (result != EAPOL_SUPP_RESULT_SUCCESS) {
+		int timeout = 2;
 		/*
 		 * Make sure we do not get stuck here waiting for long EAPOL
 		 * timeout if the AP does not disconnect in case of
 		 * authentication failure.
 		 */
-		wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+		if (wpa_s->eapol_failed) {
+			wpa_printf(MSG_DEBUG,
+				   "EAPOL authentication failed again and AP did not disconnect us");
+			timeout = 0;
+		}
+		wpa_s->eapol_failed = 1;
+		wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
 	} else {
+		wpa_s->eapol_failed = 0;
 		ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
 	}
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM                                                            
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (result != EAPOL_SUPP_RESULT_SUCCESS)                          
 #else                                                                     
 	if (result != EAPOL_SUPP_RESULT_SUCCESS ||                        
 		!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))  
-#endif /* CONFIG_DRIVER_NL80211_BRCM */                                                      
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 		return;                                                   
 
-#ifdef CONFIG_DRIVER_NL80211_BRCM
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 	if (wpa_ft_is_ft_protocol(wpa_s->wpa)) {
 		return;
 	}
-#endif /* CONFIG_DRIVER_NL80211_BRCM */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
 
 	if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
 		return;
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index defd0f2..c3ef93b 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -103,7 +103,6 @@
 	switch (co) {
 	case CONFIDENTIALITY_OFFSET_30:
 		return 30;
-		break;
 	case CONFIDENTIALITY_OFFSET_50:
 		return 50;
 	default:
@@ -241,8 +240,8 @@
 
 	res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_replay_protect,
 				  ssid->macsec_replay_window, ssid->macsec_port,
-				  ssid->mka_priority, wpa_s->ifname,
-				  wpa_s->own_addr);
+				  ssid->mka_priority, ssid->macsec_csindex,
+				  wpa_s->ifname, wpa_s->own_addr);
 	/* ieee802_1x_kay_init() frees kay_ctx on failure */
 	if (res == NULL)
 		return -1;
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 1ba360e..7428f02 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -356,8 +356,6 @@
 		/* Remove the duplicated older network entry. */
 		wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
 		wpas_notify_network_removed(wpa_s, ssid);
-		if (wpa_s->current_ssid == ssid)
-			wpa_s->current_ssid = NULL;
 		wpa_config_remove_network(wpa_s->conf, ssid->id);
 	}
 }